mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
56433f5e11
2
BUCK
2
BUCK
@ -335,6 +335,7 @@ apple_binary(
|
||||
deps = [
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
"//submodules/MtProtoKit:MtProtoKit#shared",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
@ -381,6 +382,7 @@ apple_binary(
|
||||
"//submodules/Postbox:Postbox#shared",
|
||||
"//submodules/TelegramCore:TelegramCore#shared",
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
7
Makefile
7
Makefile
@ -3,7 +3,7 @@
|
||||
include Utils.makefile
|
||||
|
||||
BUCK_OPTIONS=\
|
||||
--config custom.appVersion="5.12" \
|
||||
--config custom.appVersion="5.12.1" \
|
||||
--config custom.developmentCodeSignIdentity="${DEVELOPMENT_CODE_SIGN_IDENTITY}" \
|
||||
--config custom.distributionCodeSignIdentity="${DISTRIBUTION_CODE_SIGN_IDENTITY}" \
|
||||
--config custom.developmentTeam="${DEVELOPMENT_TEAM}" \
|
||||
@ -343,6 +343,11 @@ build_openssl: check_env
|
||||
//submodules/openssl:openssl#iphoneos-arm64 \
|
||||
--verbose 7 ${BUCK_OPTIONS} ${BUCK_THREADS_OPTIONS} ${BUCK_DEBUG_OPTIONS}
|
||||
|
||||
build_libphonenumber: check_env
|
||||
$(BUCK) build \
|
||||
//submodules/libphonenumber:libphonenumber#iphoneos-arm64 \
|
||||
${BUCK_OPTIONS} ${BUCK_THREADS_OPTIONS} ${BUCK_DEBUG_OPTIONS}
|
||||
|
||||
build_ton: check_env
|
||||
$(BUCK) build \
|
||||
//submodules/ton:ton#iphoneos-arm64 \
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.12</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUILD_NUMBER}</string>
|
||||
<key>NSExtension</key>
|
||||
|
@ -1,10 +1,7 @@
|
||||
#import "FetchImage.h"
|
||||
|
||||
#ifdef BUCK
|
||||
#import <MTProtoKit/MTProtoKit.h>
|
||||
#else
|
||||
#import <MTProtoKitDynamic/MTProtoKitDynamic.h>
|
||||
#endif
|
||||
#import <OpenSSLEncryptionProvider/OpenSSLEncryptionProvider.h>
|
||||
|
||||
#import "Serialization.h"
|
||||
|
||||
@ -87,7 +84,7 @@ dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _
|
||||
apiEnvironment = [apiEnvironment withUpdatedSocksProxySettings:[[MTSocksProxySettings alloc] initWithIp:proxyConnection.host port:(uint16_t)proxyConnection.port username:proxyConnection.username password:proxyConnection.password secret:proxyConnection.secret]];
|
||||
}
|
||||
|
||||
MTContext *context = [[MTContext alloc] initWithSerialization:serialization apiEnvironment:apiEnvironment isTestingEnvironment:account.isTestingEnvironment useTempAuthKeys:false];
|
||||
MTContext *context = [[MTContext alloc] initWithSerialization:serialization encryptionProvider:[[OpenSSLEncryptionProvider alloc] init] apiEnvironment:apiEnvironment isTestingEnvironment:account.isTestingEnvironment useTempAuthKeys:false];
|
||||
|
||||
NSDictionary *seedAddressList = @{};
|
||||
|
||||
@ -135,7 +132,7 @@ dispatch_block_t fetchImage(BuildConfig *buildConfig, AccountProxyConnection * _
|
||||
[context updateAuthInfoForDatacenterWithId:[datacenterId intValue] authInfo:[[MTDatacenterAuthInfo alloc] initWithAuthKey:info.masterKey.data authKeyId:info.masterKey.keyId saltSet:@[] authKeyAttributes:@{} mainTempAuthKey:nil mediaTempAuthKey:nil]];
|
||||
}
|
||||
|
||||
MTProto * mtProto = [[MTProto alloc] initWithContext:context datacenterId:datacenterId usageCalculationInfo:nil];
|
||||
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
mtProto.useTempAuthKeys = context.useTempAuthKeys;
|
||||
mtProto.checkForProxyConnectionIssues = false;
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.12</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUILD_NUMBER}</string>
|
||||
<key>NSExtension</key>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.12</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUILD_NUMBER}</string>
|
||||
<key>NSExtension</key>
|
||||
|
@ -5,6 +5,7 @@ import Postbox
|
||||
import SwiftSignalKit
|
||||
import BuildConfig
|
||||
import Contacts
|
||||
import OpenSSLEncryptionProvider
|
||||
|
||||
private var accountCache: Account?
|
||||
|
||||
@ -97,7 +98,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
||||
|
||||
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil))), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|
||||
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil)), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|
||||
|> mapToSignal { account -> Signal<Account?, NoError> in
|
||||
if let account = account {
|
||||
switch account {
|
||||
|
@ -185,7 +185,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(PRODUCT_BUNDLE_SHORT_VERSION)</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
@ -30,8 +30,6 @@ build_wallet: check_env
|
||||
$(BUCK) build \
|
||||
//Wallet:AppPackage#iphoneos-arm64,iphoneos-armv7 \
|
||||
//Wallet:Wallet#dwarf-and-dsym,iphoneos-arm64,iphoneos-armv7 \
|
||||
//submodules/MtProtoKit:MtProtoKit#dwarf-and-dsym,shared,iphoneos-arm64,iphoneos-armv7 \
|
||||
//submodules/MtProtoKit:MtProtoKit#shared,iphoneos-arm64,iphoneos-armv7 \
|
||||
//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#dwarf-and-dsym,shared,iphoneos-arm64,iphoneos-armv7 \
|
||||
//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared,iphoneos-arm64,iphoneos-armv7 \
|
||||
//submodules/AsyncDisplayKit:AsyncDisplayKit#dwarf-and-dsym,shared,iphoneos-arm64,iphoneos-armv7 \
|
||||
|
@ -13,7 +13,6 @@ load("//Config:buck_rule_macros.bzl",
|
||||
)
|
||||
|
||||
framework_dependencies = [
|
||||
"//submodules/MtProtoKit:MtProtoKit",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
|
@ -5,7 +5,6 @@ import BuildConfig
|
||||
import WalletUI
|
||||
import WalletCore
|
||||
import AVFoundation
|
||||
import MtProtoKit
|
||||
|
||||
private func encodeText(_ string: String, _ key: Int) -> String {
|
||||
var result = ""
|
||||
@ -366,6 +365,7 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
|
||||
|
||||
self.storage = WalletStorageInterfaceImpl(path: basePath + "/data")
|
||||
self.window = window
|
||||
|
||||
self.tonInstance = TonInstance(
|
||||
basePath: basePath + "/keys",
|
||||
config: config,
|
||||
@ -428,6 +428,8 @@ private final class WalletContextImpl: NSObject, WalletContext, UIImagePickerCon
|
||||
self.presentationData = WalletPresentationData(
|
||||
theme: WalletTheme(
|
||||
info: WalletInfoTheme(
|
||||
buttonBackgroundColor: accentColor,
|
||||
buttonTextColor: .white,
|
||||
incomingFundsTitleColor: UIColor(rgb: 0x00b12c),
|
||||
outgoingFundsTitleColor: UIColor(rgb: 0xff3b30)
|
||||
), transaction: WalletTransactionTheme(
|
||||
@ -547,259 +549,96 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
), backgroundDetailsMode: nil
|
||||
)
|
||||
|
||||
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
|
||||
print("Starting with \(documentsPath)")
|
||||
|
||||
let config =
|
||||
"""
|
||||
{
|
||||
"liteservers": [
|
||||
{
|
||||
"ip": 1137658550,
|
||||
"port": 4924,
|
||||
"id": {
|
||||
"@type": "pub.ed25519",
|
||||
"key": "peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M="
|
||||
}
|
||||
}
|
||||
],
|
||||
"validator": {
|
||||
"@type": "validator.config.global",
|
||||
"zero_state": {
|
||||
"workchain": -1,
|
||||
"shard": -9223372036854775808,
|
||||
"seqno": 0,
|
||||
"root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=",
|
||||
"file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo="
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let walletContext = WalletContextImpl(basePath: documentsPath, config: config, blockchainName: "testnet", navigationBarTheme: navigationBarTheme, window: mainWindow)
|
||||
self.walletContext = walletContext
|
||||
|
||||
let _ = (combineLatest(queue: .mainQueue(),
|
||||
walletContext.storage.getWalletRecords(),
|
||||
walletContext.keychain.encryptionPublicKey()
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { records, publicKey in
|
||||
if let record = records.first {
|
||||
if let publicKey = publicKey {
|
||||
print("publicKey = \(publicKey.base64EncodedString())")
|
||||
if record.info.encryptedSecret.publicKey == publicKey {
|
||||
if record.exportCompleted {
|
||||
let _ = (walletAddress(publicKey: record.info.publicKey, tonInstance: walletContext.tonInstance)
|
||||
|> deliverOnMainQueue).start(next: { address in
|
||||
let infoScreen = WalletInfoScreen(context: walletContext, walletInfo: record.info, address: address, enableDebugActions: false)
|
||||
|
||||
navigationController.setViewControllers([infoScreen], animated: false)
|
||||
})
|
||||
} else {
|
||||
let createdScreen = WalletSplashScreen(context: walletContext, mode: .created(record.info, nil), walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([createdScreen], animated: false)
|
||||
}
|
||||
} else {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: .secureStorageReset(.changed), walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
}
|
||||
} else {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: WalletSplashMode.secureStorageReset(.notAvailable), walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
}
|
||||
} else {
|
||||
if publicKey != nil {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: .intro, walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
} else {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: .secureStorageNotAvailable, walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
}
|
||||
}
|
||||
})
|
||||
mainWindow.viewController = navigationController
|
||||
|
||||
self.window?.makeKeyAndVisible()
|
||||
|
||||
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
|
||||
#if DEBUG
|
||||
print("Starting with \(documentsPath)")
|
||||
#endif
|
||||
|
||||
let updatedConfigSignal: Signal<String, NoError> = Signal { subscriber in
|
||||
let downloadTask = URLSession.shared.downloadTask(with: URL(string: "https://test.ton.org/ton-lite-client-test1.config.json")!, completionHandler: { location, _, error in
|
||||
if let location = location, let data = try? Data(contentsOf: location), let string = String(data: data, encoding: .utf8) {
|
||||
subscriber.putNext(string)
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
})
|
||||
downloadTask.resume()
|
||||
|
||||
return ActionDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
let updatedConfig = Promise<String>()
|
||||
updatedConfig.set(updatedConfigSignal)
|
||||
|
||||
let configPath = documentsPath + "/config"
|
||||
var initialConfig: Signal<String, NoError> = .complete()
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: configPath)), let string = String(data: data, encoding: .utf8) {
|
||||
initialConfig = .single(string)
|
||||
} else {
|
||||
initialConfig = updatedConfig.get() |> take(1)
|
||||
}
|
||||
|
||||
let _ = (initialConfig
|
||||
|> deliverOnMainQueue).start(next: { initialConfig in
|
||||
let walletContext = WalletContextImpl(basePath: documentsPath, config: initialConfig, blockchainName: "testnet", navigationBarTheme: navigationBarTheme, window: mainWindow)
|
||||
self.walletContext = walletContext
|
||||
|
||||
let _ = (updatedConfig.get()
|
||||
|> deliverOnMainQueue).start(next: { config in
|
||||
if config != initialConfig {
|
||||
let _ = walletContext.tonInstance.updateConfig(config: config, blockchainName: "testnet").start()
|
||||
}
|
||||
})
|
||||
|
||||
let _ = (combineLatest(queue: .mainQueue(),
|
||||
walletContext.storage.getWalletRecords(),
|
||||
walletContext.keychain.encryptionPublicKey()
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { records, publicKey in
|
||||
if let record = records.first {
|
||||
if let publicKey = publicKey {
|
||||
print("publicKey = \(publicKey.base64EncodedString())")
|
||||
if record.info.encryptedSecret.publicKey == publicKey {
|
||||
if record.exportCompleted {
|
||||
let _ = (walletAddress(publicKey: record.info.publicKey, tonInstance: walletContext.tonInstance)
|
||||
|> deliverOnMainQueue).start(next: { address in
|
||||
let infoScreen = WalletInfoScreen(context: walletContext, walletInfo: record.info, address: address, enableDebugActions: false)
|
||||
|
||||
navigationController.setViewControllers([infoScreen], animated: false)
|
||||
})
|
||||
} else {
|
||||
let createdScreen = WalletSplashScreen(context: walletContext, mode: .created(record.info, nil), walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([createdScreen], animated: false)
|
||||
}
|
||||
} else {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: .secureStorageReset(.changed), walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
}
|
||||
} else {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: WalletSplashMode.secureStorageReset(.notAvailable), walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
}
|
||||
} else {
|
||||
if publicKey != nil {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: .intro, walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
} else {
|
||||
let splashScreen = WalletSplashScreen(context: walletContext, mode: .secureStorageNotAvailable, walletCreatedPreloadState: nil)
|
||||
|
||||
navigationController.setViewControllers([splashScreen], animated: false)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private final class Serialization: NSObject, MTSerialization {
|
||||
func currentLayer() -> UInt {
|
||||
return 106
|
||||
}
|
||||
|
||||
func parseMessage(_ data: Data!) -> Any! {
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportAuthorization(_ datacenterId: Int32, data: AutoreleasingUnsafeMutablePointer<NSData?>!) -> MTExportAuthorizationResponseParser! {
|
||||
return nil
|
||||
}
|
||||
|
||||
func importAuthorization(_ authId: Int32, bytes: Data!) -> Data! {
|
||||
return Data()
|
||||
}
|
||||
|
||||
func requestDatacenterAddress(with data: AutoreleasingUnsafeMutablePointer<NSData?>!) -> MTRequestDatacenterAddressListParser! {
|
||||
return { _ in
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func requestNoop(_ data: AutoreleasingUnsafeMutablePointer<NSData?>!) -> MTRequestNoopParser! {
|
||||
return { _ in
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class Keychain: NSObject, MTKeychain {
|
||||
let get: (String) -> Data?
|
||||
let set: (String, Data) -> Void
|
||||
let remove: (String) -> Void
|
||||
|
||||
init(get: @escaping (String) -> Data?, set: @escaping (String, Data) -> Void, remove: @escaping (String) -> Void) {
|
||||
self.get = get
|
||||
self.set = set
|
||||
self.remove = remove
|
||||
}
|
||||
|
||||
func setObject(_ object: Any!, forKey aKey: String!, group: String!) {
|
||||
if let object = object {
|
||||
let data = NSKeyedArchiver.archivedData(withRootObject: object)
|
||||
self.set(group + ":" + aKey, data)
|
||||
} else {
|
||||
self.remove(group + ":" + aKey)
|
||||
}
|
||||
}
|
||||
|
||||
func object(forKey aKey: String!, group: String!) -> Any! {
|
||||
if let data = self.get(group + ":" + aKey) {
|
||||
return NSKeyedUnarchiver.unarchiveObject(with: data as Data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeObject(forKey aKey: String!, group: String!) {
|
||||
self.remove(group + ":" + aKey)
|
||||
}
|
||||
|
||||
func dropGroup(_ group: String!) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private final class TonProxyImpl: TonNetworkProxy {
|
||||
private let context: MTContext
|
||||
private let mtProto: MTProto
|
||||
private let requestService: MTRequestMessageService
|
||||
|
||||
init() {
|
||||
let serialization = Serialization()
|
||||
|
||||
var apiEnvironment = MTApiEnvironment()
|
||||
|
||||
apiEnvironment.apiId = 8
|
||||
apiEnvironment.langPack = "ios"
|
||||
apiEnvironment.layer = serialization.currentLayer() as NSNumber
|
||||
apiEnvironment.disableUpdates = true
|
||||
apiEnvironment = apiEnvironment.withUpdatedLangPackCode("en")
|
||||
|
||||
self.context = MTContext(serialization: serialization, apiEnvironment: apiEnvironment, isTestingEnvironment: false, useTempAuthKeys: false)
|
||||
|
||||
let seedAddressList: [Int: [String]]
|
||||
|
||||
seedAddressList = [
|
||||
1: ["149.154.175.50", "2001:b28:f23d:f001::a"],
|
||||
2: ["149.154.167.50", "2001:67c:4e8:f002::a"],
|
||||
3: ["149.154.175.100", "2001:b28:f23d:f003::a"],
|
||||
4: ["149.154.167.91", "2001:67c:4e8:f004::a"],
|
||||
5: ["149.154.171.5", "2001:b28:f23f:f005::a"]
|
||||
]
|
||||
|
||||
for (id, ips) in seedAddressList {
|
||||
self.context.setSeedAddressSetForDatacenterWithId(id, seedAddressSet: MTDatacenterAddressSet(addressList: ips.map { MTDatacenterAddress(ip: $0, port: 443, preferForMedia: false, restrictToTcp: false, cdn: false, preferForProxy: false, secret: nil)! }))
|
||||
}
|
||||
|
||||
let keychainDict = Atomic<[String: Data]>(value: [:])
|
||||
self.context.keychain = Keychain(get: { key in
|
||||
return keychainDict.with { dict -> Data? in
|
||||
return dict[key]
|
||||
}
|
||||
}, set: { key, value in
|
||||
let _ = keychainDict.modify { dict in
|
||||
var dict = dict
|
||||
dict[key] = value
|
||||
return dict
|
||||
}
|
||||
}, remove: { key in
|
||||
let _ = keychainDict.modify { dict in
|
||||
var dict = dict
|
||||
dict.removeValue(forKey: key)
|
||||
return dict
|
||||
}
|
||||
})
|
||||
|
||||
let mtProto = MTProto(context: self.context, datacenterId: 2, usageCalculationInfo: nil)!
|
||||
mtProto.useTempAuthKeys = self.context.useTempAuthKeys
|
||||
mtProto.checkForProxyConnectionIssues = false
|
||||
|
||||
self.mtProto = mtProto
|
||||
|
||||
self.requestService = MTRequestMessageService(context: context)!
|
||||
mtProto.add(self.requestService)
|
||||
|
||||
self.mtProto.resume()
|
||||
}
|
||||
|
||||
func request(data: Data, timeout: Double, completion: @escaping (TonNetworkProxyResult) -> Void) -> Disposable {
|
||||
let request = MTRequest()
|
||||
let outputStream = MTOutputStream()
|
||||
|
||||
//wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse;
|
||||
outputStream.write(Int32(bitPattern: 0xe2c9d33e as UInt32))
|
||||
outputStream.writeBytes(data)
|
||||
|
||||
request.setPayload(outputStream.currentBytes(), metadata: "wallet.sendLiteRequest", shortMetadata: "wallet.sendLiteRequest", responseParser: { response in
|
||||
guard let response = response else {
|
||||
return nil
|
||||
}
|
||||
let inputStream = MTInputStream(data: response)!
|
||||
//wallet.liteResponse#764386d7 response:bytes = wallet.LiteResponse;
|
||||
let signature = inputStream.readInt32()
|
||||
if (signature != 0x764386d7 as Int32) {
|
||||
return nil
|
||||
}
|
||||
return inputStream.readBytes()
|
||||
})
|
||||
|
||||
request.dependsOnPasswordEntry = false
|
||||
request.shouldContinueExecutionWithErrorContext = { _ in
|
||||
return true
|
||||
};
|
||||
|
||||
request.completed = { response, _, error in
|
||||
if let response = response as? Data {
|
||||
completion(.reponse(response))
|
||||
} else {
|
||||
completion(.error(error?.errorDescription ?? "UNKNOWN ERROR"))
|
||||
}
|
||||
}
|
||||
|
||||
let requestId = request.internalId
|
||||
|
||||
self.requestService.add(request)
|
||||
|
||||
return ActionDisposable { [weak self] in
|
||||
self?.requestService.removeRequest(byInternalId: requestId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.12</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUILD_NUMBER}</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.12</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUILD_NUMBER}</string>
|
||||
<key>NSExtension</key>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.12</string>
|
||||
<string>5.12.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${BUILD_NUMBER}</string>
|
||||
<key>NSExtension</key>
|
||||
|
@ -528,6 +528,7 @@ public protocol AccountContext: class {
|
||||
var wallpaperUploadManager: WallpaperUploadManager? { get }
|
||||
var watchManager: WatchManager? { get }
|
||||
var hasWallets: Signal<Bool, NoError> { get }
|
||||
var hasWalletAccess: Signal<Bool, NoError> { get }
|
||||
|
||||
var currentLimitsConfiguration: Atomic<LimitsConfiguration> { get }
|
||||
|
||||
|
@ -22,7 +22,7 @@ static_library(
|
||||
"Sources/*.h",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/MtProtoKit:MtProtoKit#shared",
|
||||
"//submodules/PKCS:PKCS",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -10,11 +10,17 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef BUCK
|
||||
#import <MtProtoKit/MtProtoKit.h>
|
||||
#else
|
||||
#import <MtProtoKitDynamic/MtProtoKitDynamic.h>
|
||||
#endif
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#import <PKCS/PKCS.h>
|
||||
|
||||
static NSData *sha1(NSData *data) {
|
||||
uint8_t digest[20];
|
||||
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
|
||||
|
||||
return [[NSData alloc] initWithBytes:digest length:20];
|
||||
}
|
||||
|
||||
static NSString *telegramApplicationSecretKey = @"telegramApplicationSecretKey_v3";
|
||||
|
||||
@ -376,7 +382,7 @@ API_AVAILABLE(ios(10))
|
||||
_dataDict[@"name"] = signature.subjectName;
|
||||
}
|
||||
if (signature.data != nil) {
|
||||
_dataDict[@"data"] = [MTSha1(signature.data) base64EncodedStringWithOptions:0];
|
||||
_dataDict[@"data"] = [sha1(signature.data) base64EncodedStringWithOptions:0];
|
||||
_dataDict[@"data1"] = [signature.data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import Foundation
|
||||
import CloudKit
|
||||
import MtProtoKit
|
||||
import SwiftSignalKit
|
||||
import EncryptionProvider
|
||||
|
||||
private enum FetchError {
|
||||
case generic
|
||||
@ -119,9 +120,12 @@ public protocol CloudDataContext {
|
||||
@available(iOS 10.0, *)
|
||||
public final class CloudDataContextImpl: CloudDataContext {
|
||||
private let queue = Queue()
|
||||
private let encryptionProvider: EncryptionProvider
|
||||
private let impl: QueueLocalObject<CloudDataContextObject>
|
||||
|
||||
public init() {
|
||||
public init(encryptionProvider: EncryptionProvider) {
|
||||
self.encryptionProvider = encryptionProvider
|
||||
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return CloudDataContextObject(queue: queue)
|
||||
@ -129,6 +133,7 @@ public final class CloudDataContextImpl: CloudDataContext {
|
||||
}
|
||||
|
||||
public func get(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError> {
|
||||
let encryptionProvider = self.encryptionProvider
|
||||
return phoneNumber
|
||||
|> take(1)
|
||||
|> mapToSignal { phoneNumber -> Signal<MTBackupDatacenterData, NoError> in
|
||||
@ -140,7 +145,7 @@ public final class CloudDataContextImpl: CloudDataContext {
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.get(prefix: prefix).start(next: { data in
|
||||
if let data = data, let datacenterData = MTIPDataDecode(data, phoneNumber ?? "") {
|
||||
if let data = data, let datacenterData = MTIPDataDecode(encryptionProvider, data, phoneNumber ?? "") {
|
||||
subscriber.putNext(datacenterData)
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
|
@ -139,19 +139,22 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
super.touchesMoved(touches, with: event)
|
||||
|
||||
if let touch = touches.first {
|
||||
/*if #available(iOS 9.0, *) {
|
||||
if #available(iOS 9.0, *) {
|
||||
let maxForce: CGFloat = max(2.5, min(3.0, touch.maximumPossibleForce))
|
||||
let progress = touch.force / maxForce
|
||||
self.currentProgress = progress
|
||||
if self.isValidated {
|
||||
self.activationProgress?(progress, .update)
|
||||
}
|
||||
if touch.force >= maxForce {
|
||||
if !self.isValidated {
|
||||
self.isValidated = true
|
||||
}
|
||||
|
||||
switch self.state {
|
||||
case .possible:
|
||||
self.delayTimer?.invalidate()
|
||||
self.animator?.invalidate()
|
||||
self.activated?(self)
|
||||
if let view = self.view?.superview {
|
||||
if let window = view.window {
|
||||
cancelOtherGestures(gesture: self, view: window)
|
||||
}
|
||||
cancelParentGestures(view: view)
|
||||
}
|
||||
self.state = .began
|
||||
@ -159,7 +162,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
break
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
self.externalUpdated?(self.view, touch.location(in: self.view))
|
||||
}
|
||||
|
@ -990,9 +990,6 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
if topItemIndexAndMinY.0 == 0 {
|
||||
var offsetValue: CGFloat = -(topItemIndexAndMinY.1 - self.insets.top)
|
||||
if self.areAllItemsOnScreen() {
|
||||
offsetValue = 0.0
|
||||
}
|
||||
offset = .known(offsetValue)
|
||||
} else if topItemIndexAndMinY.0 == -1 {
|
||||
offset = .none
|
||||
|
19
submodules/EncryptionProvider/BUCK
Normal file
19
submodules/EncryptionProvider/BUCK
Normal file
@ -0,0 +1,19 @@
|
||||
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||
|
||||
static_library(
|
||||
name = "EncryptionProvider",
|
||||
srcs = glob([
|
||||
"Sources/**/*.m",
|
||||
]),
|
||||
headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
exported_headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
deps = [
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
],
|
||||
)
|
60
submodules/EncryptionProvider/Sources/EncryptionProvider.h
Normal file
60
submodules/EncryptionProvider/Sources/EncryptionProvider.h
Normal file
@ -0,0 +1,60 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol MTBignum <NSObject>
|
||||
|
||||
@end
|
||||
|
||||
@protocol MTRsaPublicKey <NSObject>
|
||||
|
||||
@end
|
||||
|
||||
@protocol MTBignumContext <NSObject>
|
||||
|
||||
- (id<MTBignum>)create;
|
||||
- (id<MTBignum>)clone:(id<MTBignum>)other;
|
||||
|
||||
- (void)setConstantTime:(id<MTBignum>)other;
|
||||
|
||||
- (void)assignWordTo:(id<MTBignum>)bignum value:(unsigned long)value;
|
||||
- (void)assignHexTo:(id<MTBignum>)bignum value:(NSString *)value;
|
||||
- (void)assignBinTo:(id<MTBignum>)bignum value:(NSData *)value;
|
||||
- (void)assignOneTo:(id<MTBignum>)bignum;
|
||||
- (void)assignZeroTo:(id<MTBignum>)bignum;
|
||||
|
||||
- (bool)isOne:(id<MTBignum>)bignum;
|
||||
- (bool)isZero:(id<MTBignum>)bignum;
|
||||
- (NSData *)getBin:(id<MTBignum>)bignum;
|
||||
- (int)isPrime:(id<MTBignum>)bignum numberOfChecks:(int)numberOfChecks;
|
||||
|
||||
- (int)compare:(id<MTBignum>)a with:(id<MTBignum>)b;
|
||||
|
||||
- (bool)modAddInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod;
|
||||
- (bool)modSubInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod;
|
||||
- (bool)modMulInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod;
|
||||
- (bool)modExpInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod;
|
||||
- (bool)addInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b;
|
||||
- (bool)subInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b;
|
||||
- (bool)mulInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b;
|
||||
- (bool)expInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b;
|
||||
- (bool)modInverseInto:(id<MTBignum>)result a:(id<MTBignum>)a mod:(id<MTBignum>)mod;
|
||||
- (unsigned long)modWord:(id<MTBignum>)a mod:(unsigned long)mod;
|
||||
- (bool)rightShift1Bit:(id<MTBignum>)result a:(id<MTBignum>)a;
|
||||
|
||||
- (id<MTBignum>)rsaGetE:(id<MTRsaPublicKey>)publicKey;
|
||||
- (id<MTBignum>)rsaGetN:(id<MTRsaPublicKey>)publicKey;
|
||||
|
||||
@end
|
||||
|
||||
@protocol EncryptionProvider <NSObject>
|
||||
|
||||
- (id<MTBignumContext>)createBignumContext;
|
||||
|
||||
- (NSData * _Nullable)rsaEncryptWithPublicKey:(NSString *)publicKey data:(NSData *)data;
|
||||
- (NSData * _Nullable)rsaEncryptPKCS1OAEPWithPublicKey:(NSString *)publicKey data:(NSData *)data;
|
||||
- (id<MTRsaPublicKey>)parseRSAPublicKey:(NSString *)publicKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -7,8 +7,6 @@ framework(
|
||||
"MtProtoKit/*.m",
|
||||
"thirdparty/AFNetworking/*.m",
|
||||
"thirdparty/AsyncSocket/*.m",
|
||||
"TON/*.m",
|
||||
"TON/*.mm",
|
||||
]),
|
||||
headers = merge_maps([
|
||||
glob_map(glob([
|
||||
@ -16,7 +14,6 @@ framework(
|
||||
"MtProtoKit/*.h",
|
||||
"thirdparty/AFNetworking/*.h",
|
||||
"thirdparty/AsyncSocket/*.h",
|
||||
"TON/*.h",
|
||||
])),
|
||||
]),
|
||||
exported_headers = [
|
||||
@ -78,13 +75,10 @@ framework(
|
||||
"MTProxyConnectivity.h",
|
||||
"MTGzip.h",
|
||||
"MTDatacenterVerificationData.h",
|
||||
"MTPKCS.h",
|
||||
"TON/TON.h",
|
||||
],
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
"//submodules/openssl:openssl",
|
||||
"//submodules/ton:ton",
|
||||
"//submodules/EncryptionProvider:EncryptionProvider",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -10,3 +10,4 @@
|
||||
|
||||
void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
|
||||
void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
|
||||
void MyAesCbcDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
|
||||
|
@ -161,6 +161,13 @@ void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void
|
||||
}
|
||||
}
|
||||
|
||||
void MyAesCbcDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
|
||||
int outLength = 0;
|
||||
CCCryptorStatus status = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, key, keyLength, iv, inBytes, length, outBytes, length, &outLength);
|
||||
assert(status == kCCSuccess);
|
||||
assert(outLength == length);
|
||||
}
|
||||
|
||||
static void ctr128_inc(unsigned char *counter)
|
||||
{
|
||||
uint32_t n = 16, c = 1;
|
||||
|
@ -75,6 +75,8 @@ static NSData *base64_decode(NSString *str) {
|
||||
@[@"www.google.com", @"dns.google.com"],
|
||||
];
|
||||
|
||||
id<EncryptionProvider> encryptionProvider = currentContext.encryptionProvider;
|
||||
|
||||
NSMutableArray *signals = [[NSMutableArray alloc] init];
|
||||
for (NSArray *hostAndHostname in hosts) {
|
||||
NSString *host = hostAndHostname[0];
|
||||
@ -117,7 +119,7 @@ static NSData *base64_decode(NSString *str) {
|
||||
NSData *result = base64_decode(finalString);
|
||||
NSMutableData *finalData = [[NSMutableData alloc] initWithData:result];
|
||||
[finalData setLength:256];
|
||||
MTBackupDatacenterData *datacenterData = MTIPDataDecode(finalData, phoneNumber);
|
||||
MTBackupDatacenterData *datacenterData = MTIPDataDecode(encryptionProvider, finalData, phoneNumber);
|
||||
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveGoogle"]) {
|
||||
return [MTSignal single:datacenterData];
|
||||
}
|
||||
@ -154,6 +156,8 @@ static NSString *makeRandomPadding() {
|
||||
}
|
||||
|
||||
+ (MTSignal *)fetchBackupIpsResolveCloudflare:(bool)isTesting phoneNumber:(NSString *)phoneNumber currentContext:(MTContext *)currentContext addressOverride:(NSString *)addressOverride {
|
||||
id<EncryptionProvider> encryptionProvider = currentContext.encryptionProvider;
|
||||
|
||||
NSArray *hosts = @[
|
||||
@[@"mozilla.cloudflare-dns.com", @""],
|
||||
];
|
||||
@ -201,7 +205,7 @@ static NSString *makeRandomPadding() {
|
||||
NSData *result = base64_decode(finalString);
|
||||
NSMutableData *finalData = [[NSMutableData alloc] initWithData:result];
|
||||
[finalData setLength:256];
|
||||
MTBackupDatacenterData *datacenterData = MTIPDataDecode(finalData, phoneNumber);
|
||||
MTBackupDatacenterData *datacenterData = MTIPDataDecode(encryptionProvider, finalData, phoneNumber);
|
||||
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveCloudflare"]) {
|
||||
return [MTSignal single:datacenterData];
|
||||
}
|
||||
@ -234,13 +238,13 @@ static NSString *makeRandomPadding() {
|
||||
apiEnvironment.disableUpdates = true;
|
||||
apiEnvironment.langPack = currentContext.apiEnvironment.langPack;
|
||||
|
||||
MTContext *context = [[MTContext alloc] initWithSerialization:currentContext.serialization apiEnvironment:apiEnvironment isTestingEnvironment:currentContext.isTestingEnvironment useTempAuthKeys:address.datacenterId != 0 ? currentContext.useTempAuthKeys : false];
|
||||
MTContext *context = [[MTContext alloc] initWithSerialization:currentContext.serialization encryptionProvider:currentContext.encryptionProvider apiEnvironment:apiEnvironment isTestingEnvironment:currentContext.isTestingEnvironment useTempAuthKeys:address.datacenterId != 0 ? currentContext.useTempAuthKeys : false];
|
||||
|
||||
if (address.datacenterId != 0) {
|
||||
//context.keychain = currentContext.keychain;
|
||||
}
|
||||
|
||||
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:address.datacenterId usageCalculationInfo:nil];
|
||||
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:address.datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
if (address.datacenterId != 0) {
|
||||
mtProto.useTempAuthKeys = currentContext.useTempAuthKeys;
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
# import <MtProtoKit/MTDatacenterAuthInfo.h>
|
||||
#endif
|
||||
|
||||
#import <EncryptionProvider/EncryptionProvider.h>
|
||||
|
||||
@class MTDatacenterAddress;
|
||||
@class MTDatacenterAddressSet;
|
||||
@protocol MTSerialization;
|
||||
@ -47,11 +49,12 @@
|
||||
@property (nonatomic, strong) id<MTKeychain> keychain;
|
||||
|
||||
@property (nonatomic, strong, readonly) id<MTSerialization> serialization;
|
||||
@property (nonatomic, strong) id<EncryptionProvider> encryptionProvider;
|
||||
@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment;
|
||||
@property (nonatomic, readonly) bool isTestingEnvironment;
|
||||
@property (nonatomic, readonly) bool useTempAuthKeys;
|
||||
|
||||
- (instancetype)initWithSerialization:(id<MTSerialization>)serialization apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys;
|
||||
- (instancetype)initWithSerialization:(id<MTSerialization>)serialization encryptionProvider:(id<EncryptionProvider>)encryptionProvider apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys;
|
||||
|
||||
- (void)performBatchUpdates:(void (^)())block;
|
||||
|
||||
@ -98,6 +101,7 @@
|
||||
- (void)publicKeysForDatacenterWithIdRequired:(NSInteger)datacenterId;
|
||||
|
||||
- (void)removeAllAuthTokens;
|
||||
- (void)removeTokenForDatacenterWithId:(NSInteger)datacenterId;
|
||||
- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId;
|
||||
- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken;
|
||||
|
||||
|
@ -181,12 +181,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSerialization:(id<MTSerialization>)serialization apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys
|
||||
- (instancetype)initWithSerialization:(id<MTSerialization>)serialization encryptionProvider:(id<EncryptionProvider>)encryptionProvider apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSAssert(serialization != nil, @"serialization should not be nil");
|
||||
NSAssert(apiEnvironment != nil, @"apiEnvironment should not be nil");
|
||||
#endif
|
||||
NSAssert(encryptionProvider != nil, @"encryptionProvider should not be nil");
|
||||
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
@ -194,6 +193,7 @@
|
||||
arc4random_buf(&_uniqueId, sizeof(_uniqueId));
|
||||
|
||||
_serialization = serialization;
|
||||
_encryptionProvider = encryptionProvider;
|
||||
_apiEnvironment = apiEnvironment;
|
||||
_isTestingEnvironment = isTestingEnvironment;
|
||||
_useTempAuthKeys = useTempAuthKeys;
|
||||
@ -345,8 +345,9 @@
|
||||
}];
|
||||
|
||||
NSDictionary *datacenterAuthInfoById = [keychain objectForKey:@"datacenterAuthInfoById" group:@"persistent"];
|
||||
if (datacenterAuthInfoById != nil)
|
||||
if (datacenterAuthInfoById != nil) {
|
||||
_datacenterAuthInfoById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAuthInfoById];
|
||||
}
|
||||
|
||||
NSDictionary *datacenterPublicKeysById = [keychain objectForKey:@"datacenterPublicKeysById" group:@"ephemeral"];
|
||||
if (datacenterPublicKeysById != nil) {
|
||||
@ -571,13 +572,17 @@
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^
|
||||
{
|
||||
if (authInfo != nil && datacenterId != 0)
|
||||
if (datacenterId != 0)
|
||||
{
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTContext#%x: auth info updated for %d]", (int)self, datacenterId);
|
||||
MTLog(@"[MTContext#%x: auth info updated for %d to %@]", (int)self, datacenterId, authInfo);
|
||||
}
|
||||
|
||||
_datacenterAuthInfoById[@(datacenterId)] = authInfo;
|
||||
if (authInfo != nil) {
|
||||
_datacenterAuthInfoById[@(datacenterId)] = authInfo;
|
||||
} else {
|
||||
[_datacenterAuthInfoById removeObjectForKey:@(datacenterId)];
|
||||
}
|
||||
[_keychain setObject:_datacenterAuthInfoById forKey:@"datacenterAuthInfoById" group:@"persistent"];
|
||||
|
||||
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
||||
@ -919,6 +924,21 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)removeTokenForDatacenterWithId:(NSInteger)datacenterId
|
||||
{
|
||||
[[MTContext contextQueue] dispatchOnQueue:^{
|
||||
[_authTokenById removeObjectForKey:@(datacenterId)];
|
||||
[_keychain setObject:_authTokenById forKey:@"authTokenById" group:@"persistent"];
|
||||
|
||||
MTDatacenterTransferAuthAction *action = _datacenterTransferAuthActions[@(datacenterId)];
|
||||
if (action != nil) {
|
||||
action.delegate = nil;
|
||||
[action cancel];
|
||||
[_datacenterTransferAuthActions removeObjectForKey:@(datacenterId)];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId
|
||||
{
|
||||
__block id result = nil;
|
||||
|
@ -79,7 +79,7 @@
|
||||
if (alreadyCompleted) {
|
||||
[self complete];
|
||||
} else {
|
||||
_authMtProto = [[MTProto alloc] initWithContext:context datacenterId:_datacenterId usageCalculationInfo:nil];
|
||||
_authMtProto = [[MTProto alloc] initWithContext:context datacenterId:_datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_authMtProto.cdn = isCdn;
|
||||
_authMtProto.useUnauthorizedMode = true;
|
||||
if (_tempAuth) {
|
||||
|
@ -129,6 +129,8 @@ typedef enum {
|
||||
|
||||
@interface MTDatacenterAuthMessageService ()
|
||||
{
|
||||
id<EncryptionProvider> _encryptionProvider;
|
||||
|
||||
bool _tempAuth;
|
||||
MTSessionInfo *_sessionInfo;
|
||||
|
||||
@ -161,6 +163,7 @@ typedef enum {
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
_encryptionProvider = context.encryptionProvider;
|
||||
_tempAuth = tempAuth;
|
||||
_sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:context];
|
||||
}
|
||||
@ -571,7 +574,7 @@ typedef enum {
|
||||
|
||||
NSData *innerDataGA = dhInnerData.gA;
|
||||
NSData *innerDataDhPrime = dhInnerData.dhPrime;
|
||||
if (!MTCheckIsSafeGAOrB(innerDataGA, innerDataDhPrime))
|
||||
if (!MTCheckIsSafeGAOrB(_encryptionProvider, innerDataGA, innerDataDhPrime))
|
||||
{
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g_a]", self);
|
||||
@ -581,7 +584,7 @@ typedef enum {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MTCheckMod(innerDataDhPrime, (unsigned int)innerDataG, mtProto.context.keychain))
|
||||
if (!MTCheckMod(_encryptionProvider, innerDataDhPrime, (unsigned int)innerDataG, mtProto.context.keychain))
|
||||
{
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g (2)]", self);
|
||||
@ -591,7 +594,7 @@ typedef enum {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MTCheckIsSafePrime(innerDataDhPrime, mtProto.context.keychain))
|
||||
if (!MTCheckIsSafePrime(_encryptionProvider, innerDataDhPrime, mtProto.context.keychain))
|
||||
{
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH prime]", self);
|
||||
@ -609,9 +612,9 @@ typedef enum {
|
||||
tmpG = (int32_t)OSSwapInt32(tmpG);
|
||||
NSData *g = [[NSData alloc] initWithBytes:&tmpG length:4];
|
||||
|
||||
NSData *g_b = MTExp(g, b, innerDataDhPrime);
|
||||
NSData *g_b = MTExp(_encryptionProvider, g, b, innerDataDhPrime);
|
||||
|
||||
NSData *authKey = MTExp(innerDataGA, b, innerDataDhPrime);
|
||||
NSData *authKey = MTExp(_encryptionProvider, innerDataGA, b, innerDataDhPrime);
|
||||
|
||||
NSData *authKeyHash = MTSha1(authKey);
|
||||
|
||||
|
@ -84,7 +84,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
_sourceDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:sourceDatacenterId usageCalculationInfo:nil];
|
||||
_sourceDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:sourceDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_sourceDatacenterMtProto.useTempAuthKeys = context.useTempAuthKeys;
|
||||
|
||||
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
|
||||
@ -122,7 +122,8 @@
|
||||
_sourceDatacenterMtProto = nil;
|
||||
|
||||
MTContext *context = _context;
|
||||
_destinationDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:_destinationDatacenterId usageCalculationInfo:nil];
|
||||
_destinationDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:_destinationDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_destinationDatacenterMtProto.canResetAuthData = true;
|
||||
_destinationDatacenterMtProto.useTempAuthKeys = context.useTempAuthKeys;
|
||||
|
||||
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
|
||||
|
@ -92,7 +92,7 @@
|
||||
{
|
||||
if ([context authInfoForDatacenterWithId:_targetDatacenterId] != nil)
|
||||
{
|
||||
_mtProto = [[MTProto alloc] initWithContext:context datacenterId:_targetDatacenterId usageCalculationInfo:nil];
|
||||
_mtProto = [[MTProto alloc] initWithContext:context datacenterId:_targetDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
|
||||
_mtProto.useTempAuthKeys = useTempAuthKeys;
|
||||
_requestService = [[MTRequestMessageService alloc] initWithContext:_context];
|
||||
_requestService.forceBackgroundRequests = true;
|
||||
@ -128,6 +128,9 @@
|
||||
{
|
||||
if (_context != context || !_awaitingAddresSetUpdate)
|
||||
return;
|
||||
if (authInfo == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_targetDatacenterId != 0 && _targetDatacenterId == datacenterId)
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
#ifndef MTEncryption_H
|
||||
#define MTEncryption_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <EncryptionProvider/EncryptionProvider.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -29,22 +29,22 @@ void MTAesDecryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *k
|
||||
NSData *MTAesEncrypt(NSData *data, NSData *key, NSData *iv);
|
||||
NSData *MTAesDecrypt(NSData *data, NSData *key, NSData *iv);
|
||||
NSData *MTRsaEncrypt(NSString *publicKey, NSData *data);
|
||||
NSData *MTExp(NSData *base, NSData *exp, NSData *modulus);
|
||||
NSData *MTModSub(NSData *a, NSData *b, NSData *modulus);
|
||||
NSData *MTModMul(NSData *a, NSData *b, NSData *modulus);
|
||||
NSData *MTMul(NSData *a, NSData *b);
|
||||
NSData *MTAdd(NSData *a, NSData *b);
|
||||
NSData *MTExp(id<EncryptionProvider> provider, NSData *base, NSData *exp, NSData *modulus);
|
||||
NSData *MTModSub(id<EncryptionProvider> provider, NSData *a, NSData *b, NSData *modulus);
|
||||
NSData *MTModMul(id<EncryptionProvider> provider, NSData *a, NSData *b, NSData *modulus);
|
||||
NSData *MTMul(id<EncryptionProvider> provider, NSData *a, NSData *b);
|
||||
NSData *MTAdd(id<EncryptionProvider> provider, NSData *a, NSData *b);
|
||||
bool MTFactorize(uint64_t what, uint64_t *resA, uint64_t *resB);
|
||||
bool MTIsZero(NSData *value);
|
||||
bool MTIsZero(id<EncryptionProvider> provider, NSData *value);
|
||||
|
||||
NSData *MTAesCtrDecrypt(NSData *data, NSData *key, NSData *iv);
|
||||
|
||||
@protocol MTKeychain;
|
||||
bool MTCheckIsSafeG(unsigned int g);
|
||||
bool MTCheckIsSafeB(NSData *b, NSData *p);
|
||||
bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain);
|
||||
bool MTCheckIsSafeGAOrB(NSData *gAOrB, NSData *p);
|
||||
bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain);
|
||||
bool MTCheckIsSafeB(id<EncryptionProvider> provider, NSData *b, NSData *p);
|
||||
bool MTCheckIsSafePrime(id<EncryptionProvider> provider, NSData *numberBytes, id<MTKeychain> keychain);
|
||||
bool MTCheckIsSafeGAOrB(id<EncryptionProvider> provider, NSData *gAOrB, NSData *p);
|
||||
bool MTCheckMod(id<EncryptionProvider> provider, NSData *numberBytes, unsigned int g, id<MTKeychain> keychain);
|
||||
|
||||
@interface MTAesCtr : NSObject
|
||||
|
||||
@ -59,9 +59,9 @@ bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain);
|
||||
|
||||
@end
|
||||
|
||||
uint64_t MTRsaFingerprint(NSString *key);
|
||||
uint64_t MTRsaFingerprint(id<EncryptionProvider> provider, NSString *key);
|
||||
|
||||
NSData *MTRsaEncryptPKCS1OAEP(NSString *key, NSData *data);
|
||||
NSData *MTRsaEncryptPKCS1OAEP(id<EncryptionProvider> provider, NSString *key, NSData *data);
|
||||
|
||||
@interface MTBackupDatacenterAddress : NSObject
|
||||
|
||||
@ -80,7 +80,7 @@ NSData *MTRsaEncryptPKCS1OAEP(NSString *key, NSData *data);
|
||||
|
||||
@end
|
||||
|
||||
MTBackupDatacenterData *MTIPDataDecode(NSData *data, NSString *phoneNumber);
|
||||
MTBackupDatacenterData *MTIPDataDecode(id<EncryptionProvider> provider, NSData *data, NSString *phoneNumber);
|
||||
|
||||
NSData * _Nullable MTPBKDF2(NSData * _Nonnull data, NSData * _Nonnull salt, int rounds);
|
||||
|
||||
|
@ -8,23 +8,13 @@
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#import <EncryptionProvider/EncryptionProvider.h>
|
||||
|
||||
#import "MTAes.h"
|
||||
#import "MTRsa.h"
|
||||
|
||||
#import "MTBuffer.h"
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
# include <openssl/pem.h>
|
||||
#else
|
||||
# include <openssl/pem.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/aes.h>
|
||||
|
||||
#import "MTBufferReader.h"
|
||||
|
||||
NSData *MTSha1(NSData *data)
|
||||
@ -340,167 +330,124 @@ NSData *MTRsaEncrypt(NSString *publicKey, NSData *data)
|
||||
#endif
|
||||
}
|
||||
|
||||
NSData *MTExp(NSData *base, NSData *exp, NSData *modulus)
|
||||
NSData *MTExp(id<EncryptionProvider> provider, NSData *base, NSData *exp, NSData *modulus)
|
||||
{
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnBase = BN_bin2bn(base.bytes, (int)base.length, NULL);
|
||||
BN_set_flags(bnBase, BN_FLG_CONSTTIME);
|
||||
|
||||
BIGNUM *bnExp = BN_bin2bn(exp.bytes, (int)exp.length, NULL);
|
||||
BN_set_flags(bnExp, BN_FLG_CONSTTIME);
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
BIGNUM *bnModulus = BN_bin2bn(modulus.bytes, (int)modulus.length, NULL);
|
||||
BN_set_flags(bnModulus, BN_FLG_CONSTTIME);
|
||||
id<MTBignum> bnBase = [context create];
|
||||
[context assignBinTo:bnBase value:base];
|
||||
[context setConstantTime:bnBase];
|
||||
|
||||
BIGNUM *bnRes = BN_new();
|
||||
BN_set_flags(bnModulus, BN_FLG_CONSTTIME);
|
||||
id<MTBignum> bnExp = [context create];
|
||||
[context assignBinTo:bnExp value:exp];
|
||||
[context setConstantTime:bnExp];
|
||||
|
||||
BN_mod_exp(bnRes, bnBase, bnExp, bnModulus, ctx);
|
||||
id<MTBignum> bnModulus = [context create];
|
||||
[context assignBinTo:bnModulus value:modulus];
|
||||
[context setConstantTime:bnModulus];
|
||||
|
||||
unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
|
||||
int resLen = BN_bn2bin(bnRes, res);
|
||||
id<MTBignum> bnRes = [context create];
|
||||
[context setConstantTime:bnRes];
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
BN_free(bnBase);
|
||||
BN_free(bnExp);
|
||||
BN_free(bnModulus);
|
||||
BN_free(bnRes);
|
||||
[context modExpInto:bnRes a:bnBase b:bnExp mod:bnModulus];
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
|
||||
free(res);
|
||||
NSData *result = [context getBin:bnRes];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NSData *MTModSub(NSData *a, NSData *b, NSData *modulus) {
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
|
||||
NSData *MTModSub(id<EncryptionProvider> provider, NSData *a, NSData *b, NSData *modulus) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
|
||||
id<MTBignum> bnA = [context create];
|
||||
[context assignBinTo:bnA value:a];
|
||||
|
||||
BIGNUM *bnModulus = BN_bin2bn(modulus.bytes, (int)modulus.length, NULL);
|
||||
id<MTBignum> bnB = [context create];
|
||||
[context assignBinTo:bnB value:b];
|
||||
|
||||
BIGNUM *bnRes = BN_new();
|
||||
id<MTBignum> bnModulus = [context create];
|
||||
[context assignBinTo:bnModulus value:modulus];
|
||||
|
||||
BN_mod_sub(bnRes, bnA, bnB, bnModulus, ctx);
|
||||
id<MTBignum> bnRes = [context create];
|
||||
|
||||
unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
|
||||
int resLen = BN_bn2bin(bnRes, res);
|
||||
[context modSubInto:bnRes a:bnA b:bnB mod:bnModulus];
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
BN_free(bnA);
|
||||
BN_free(bnB);
|
||||
BN_free(bnModulus);
|
||||
BN_free(bnRes);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
|
||||
free(res);
|
||||
|
||||
return result;
|
||||
return [context getBin:bnRes];
|
||||
}
|
||||
|
||||
NSData *MTModMul(NSData *a, NSData *b, NSData *modulus) {
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
|
||||
NSData *MTModMul(id<EncryptionProvider> provider, NSData *a, NSData *b, NSData *modulus) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
|
||||
id<MTBignum> bnA = [context create];
|
||||
[context assignBinTo:bnA value:a];
|
||||
|
||||
BIGNUM *bnModulus = BN_bin2bn(modulus.bytes, (int)modulus.length, NULL);
|
||||
id<MTBignum> bnB = [context create];
|
||||
[context assignBinTo:bnB value:b];
|
||||
|
||||
BIGNUM *bnRes = BN_new();
|
||||
id<MTBignum> bnModulus = [context create];
|
||||
[context assignBinTo:bnModulus value:modulus];
|
||||
|
||||
BN_mod_mul(bnRes, bnA, bnB, bnModulus, ctx);
|
||||
id<MTBignum> bnRes = [context create];
|
||||
|
||||
unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
|
||||
int resLen = BN_bn2bin(bnRes, res);
|
||||
[context modMulInto:bnRes a:bnA b:bnB mod:bnModulus];
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
BN_free(bnA);
|
||||
BN_free(bnB);
|
||||
BN_free(bnModulus);
|
||||
BN_free(bnRes);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
|
||||
free(res);
|
||||
|
||||
return result;
|
||||
return [context getBin:bnRes];
|
||||
}
|
||||
|
||||
NSData *MTMul(NSData *a, NSData *b) {
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
|
||||
NSData *MTMul(id<EncryptionProvider> provider, NSData *a, NSData *b) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
|
||||
id<MTBignum> bnA = [context create];
|
||||
[context assignBinTo:bnA value:a];
|
||||
|
||||
BIGNUM *bnRes = BN_new();
|
||||
id<MTBignum> bnB = [context create];
|
||||
[context assignBinTo:bnB value:b];
|
||||
|
||||
BN_mul(bnRes, bnA, bnB, ctx);
|
||||
id<MTBignum> bnRes = [context create];
|
||||
|
||||
unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
|
||||
int resLen = BN_bn2bin(bnRes, res);
|
||||
[context mulInto:bnRes a:bnA b:bnB];
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
BN_free(bnA);
|
||||
BN_free(bnB);
|
||||
BN_free(bnRes);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
|
||||
free(res);
|
||||
|
||||
return result;
|
||||
return [context getBin:bnRes];
|
||||
}
|
||||
|
||||
NSData *MTAdd(NSData *a, NSData *b) {
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
|
||||
NSData *MTAdd(id<EncryptionProvider> provider, NSData *a, NSData *b) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
|
||||
id<MTBignum> bnA = [context create];
|
||||
[context assignBinTo:bnA value:a];
|
||||
|
||||
BIGNUM *bnRes = BN_new();
|
||||
id<MTBignum> bnB = [context create];
|
||||
[context assignBinTo:bnB value:b];
|
||||
|
||||
BN_add(bnRes, bnA, bnB);
|
||||
id<MTBignum> bnRes = [context create];
|
||||
|
||||
unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
|
||||
int resLen = BN_bn2bin(bnRes, res);
|
||||
[context addInto:bnRes a:bnA b:bnB];
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
BN_free(bnA);
|
||||
BN_free(bnB);
|
||||
BN_free(bnRes);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
|
||||
free(res);
|
||||
|
||||
return result;
|
||||
return [context getBin:bnRes];
|
||||
}
|
||||
|
||||
bool MTIsZero(NSData *value) {
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnValue = BN_bin2bn(value.bytes, (int)value.length, NULL);
|
||||
bool MTIsZero(id<EncryptionProvider> provider, NSData *value) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
bool isZero = BN_is_zero(bnValue);
|
||||
id<MTBignum> bnA = [context create];
|
||||
[context assignBinTo:bnA value:value];
|
||||
|
||||
BN_free(bnValue);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return isZero;
|
||||
return [context isZero:bnA];
|
||||
}
|
||||
|
||||
bool MTCheckIsSafeB(NSData *b, NSData *p) {
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
|
||||
BIGNUM *bnP = BN_bin2bn(p.bytes, (int)p.length, NULL);
|
||||
BIGNUM *bnZero = BN_new();
|
||||
BN_zero(bnZero);
|
||||
bool MTCheckIsSafeB(id<EncryptionProvider> provider, NSData *b, NSData *p) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
bool result = BN_cmp(bnB, bnZero) == 1 && BN_cmp(bnB, bnP) == -1;
|
||||
id<MTBignum> bnB = [context create];
|
||||
[context assignBinTo:bnB value:b];
|
||||
|
||||
BN_free(bnB);
|
||||
BN_free(bnP);
|
||||
BN_free(bnZero);
|
||||
BN_CTX_free(ctx);
|
||||
id<MTBignum> bnP = [context create];
|
||||
[context assignBinTo:bnP value:p];
|
||||
|
||||
return result;
|
||||
id<MTBignum> bnZero = [context create];
|
||||
[context assignZeroTo:bnZero];
|
||||
|
||||
return [context compare:bnB with:bnZero] == 1 && [context compare:bnB with:bnP] == -1;
|
||||
}
|
||||
|
||||
static inline uint64_t mygcd(uint64_t a, uint64_t b)
|
||||
@ -616,27 +563,20 @@ static NSString *hexStringFromData(NSData *data)
|
||||
return string;
|
||||
}
|
||||
|
||||
bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain)
|
||||
bool MTCheckIsSafePrime(id<EncryptionProvider> provider, NSData *numberBytes, id<MTKeychain> keychain)
|
||||
{
|
||||
NSString *primeKey = [[NSString alloc] initWithFormat:@"isPrimeSafe_%@", hexStringFromData(numberBytes)];
|
||||
|
||||
NSNumber *nCachedResult = [keychain objectForKey:primeKey group:@"primes"];
|
||||
if (nCachedResult != nil)
|
||||
if (nCachedResult != nil) {
|
||||
return [nCachedResult boolValue];
|
||||
}
|
||||
|
||||
if (numberBytes.length != 256)
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[[NSNumber alloc] initWithBool:false] forKey:primeKey];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
if (numberBytes.length != 256) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(((uint8_t *)numberBytes.bytes)[0] & (1 << 7)))
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[[NSNumber alloc] initWithBool:false] forKey:primeKey];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
if (!(((uint8_t *)numberBytes.bytes)[0] & (1 << 7))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -665,114 +605,108 @@ bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain)
|
||||
0xb9, 0x2f, 0xcc, 0x5b
|
||||
};
|
||||
|
||||
if (memcmp(goodPrime0, numberBytes.bytes, 256) == 0)
|
||||
if (memcmp(goodPrime0, numberBytes.bytes, 256) == 0) {
|
||||
return true;
|
||||
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnNumber = BN_bin2bn(numberBytes.bytes, (int)numberBytes.length, NULL);
|
||||
|
||||
int result = BN_is_prime_ex(bnNumber, 30, ctx, NULL);
|
||||
|
||||
if (result == 1)
|
||||
{
|
||||
BIGNUM *bnNumberMinus1 = BN_new();
|
||||
BN_sub(bnNumberMinus1, bnNumber, BN_value_one());
|
||||
BIGNUM *bnNumberMinus1DivBy2 = BN_new();
|
||||
BN_rshift1(bnNumberMinus1DivBy2, bnNumberMinus1);
|
||||
|
||||
result = BN_is_prime_ex(bnNumberMinus1DivBy2, 30, ctx, NULL);
|
||||
|
||||
BN_free(bnNumberMinus1);
|
||||
BN_free(bnNumberMinus1DivBy2);
|
||||
}
|
||||
|
||||
BN_free(bnNumber);
|
||||
BN_CTX_free(ctx);
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
id<MTBignum> bnNumber = [context create];
|
||||
[context assignBinTo:bnNumber value:numberBytes];
|
||||
|
||||
int result = [context isPrime:bnNumber numberOfChecks:30];
|
||||
|
||||
if (result == 1) {
|
||||
id<MTBignum> bnNumberOne = [context create];
|
||||
[context assignOneTo:bnNumberOne];
|
||||
|
||||
id<MTBignum> bnNumberMinusOne = [context create];
|
||||
[context subInto:bnNumberMinusOne a:bnNumber b:bnNumberOne];
|
||||
|
||||
id<MTBignum> bnNumberMinusOneDivByTwo = [context create];
|
||||
[context rightShift1Bit:bnNumberMinusOneDivByTwo a:bnNumberMinusOne];
|
||||
|
||||
int result = [context isPrime:bnNumberMinusOneDivByTwo numberOfChecks:30];
|
||||
}
|
||||
|
||||
[keychain setObject:@(result == 1) forKey:primeKey group:@"primes"];
|
||||
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
bool MTCheckIsSafeGAOrB(NSData *gAOrB, NSData *p)
|
||||
{
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnNumber = BN_bin2bn(gAOrB.bytes, (int)gAOrB.length, NULL);
|
||||
BIGNUM *bnP = BN_bin2bn(p.bytes, (int)p.length, NULL);
|
||||
bool MTCheckIsSafeGAOrB(id<EncryptionProvider> provider, NSData *gAOrB, NSData *p) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
id<MTBignum> bnNumber = [context create];
|
||||
[context assignBinTo:bnNumber value:gAOrB];
|
||||
|
||||
id<MTBignum> bnP = [context create];
|
||||
[context assignBinTo:bnP value:p];
|
||||
|
||||
id<MTBignum> bnOne = [context create];
|
||||
[context assignOneTo:bnOne];
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (BN_cmp(bnNumber, BN_value_one()) == 1)
|
||||
{
|
||||
BIGNUM *pMinus1 = BN_new();
|
||||
BN_sub(pMinus1, bnP, BN_value_one());
|
||||
if ([context compare:bnNumber with:bnOne] == 1) {
|
||||
id<MTBignum> bnPMinusOne = [context create];
|
||||
[context subInto:bnPMinusOne a:bnP b:bnOne];
|
||||
|
||||
if (BN_cmp(bnNumber, pMinus1) == -1)
|
||||
{
|
||||
if ([context compare:bnNumber with:bnPMinusOne] == -1) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
BN_free(pMinus1);
|
||||
}
|
||||
|
||||
BN_free(bnNumber);
|
||||
BN_free(bnP);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain)
|
||||
bool MTCheckMod(id<EncryptionProvider> provider, NSData *numberBytes, unsigned int g, id<MTKeychain> keychain)
|
||||
{
|
||||
NSString *modKey = [[NSString alloc] initWithFormat:@"isPrimeModSafe_%@_%d", hexStringFromData(numberBytes), g];
|
||||
NSNumber *nCachedResult = [keychain objectForKey:modKey group:@"primes"];
|
||||
if (nCachedResult != nil)
|
||||
if (nCachedResult != nil) {
|
||||
return [nCachedResult boolValue];
|
||||
}
|
||||
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BIGNUM *bnNumber = BN_bin2bn(numberBytes.bytes, (int)numberBytes.length, NULL);
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
id<MTBignum> bnNumber = [context create];
|
||||
[context assignBinTo:bnNumber value:numberBytes];
|
||||
|
||||
bool result = false;
|
||||
|
||||
switch (g)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
BN_ULONG modResult = BN_mod_word(bnNumber, 8);
|
||||
switch (g) {
|
||||
case 2: {
|
||||
unsigned long modResult = [context modWord:bnNumber mod:8];
|
||||
result = modResult == 7;
|
||||
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
BN_ULONG modResult = BN_mod_word(bnNumber, 3);
|
||||
case 3: {
|
||||
unsigned long modResult = [context modWord:bnNumber mod:3];
|
||||
result = modResult == 2;
|
||||
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
case 4: {
|
||||
result = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
BN_ULONG modResult = BN_mod_word(bnNumber, 5);
|
||||
case 5: {
|
||||
unsigned long modResult = [context modWord:bnNumber mod:5];
|
||||
result = modResult == 1 || modResult == 4;
|
||||
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
BN_ULONG modResult = BN_mod_word(bnNumber, 24);
|
||||
case 6: {
|
||||
unsigned long modResult = [context modWord:bnNumber mod:24];
|
||||
result = modResult == 19 || modResult == 23;
|
||||
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
BN_ULONG modResult = BN_mod_word(bnNumber, 7);
|
||||
case 7: {
|
||||
unsigned long modResult = [context modWord:bnNumber mod:7];
|
||||
result = modResult == 3 || modResult == 5 || modResult == 6;
|
||||
|
||||
break;
|
||||
@ -781,9 +715,6 @@ bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain)
|
||||
break;
|
||||
}
|
||||
|
||||
BN_free(bnNumber);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
[keychain setObject:@(result) forKey:modKey group:@"primes"];
|
||||
|
||||
return result;
|
||||
@ -796,27 +727,21 @@ NSData *MTAesCtrDecrypt(NSData *data, NSData *key, NSData *iv) {
|
||||
return outData;
|
||||
}
|
||||
|
||||
uint64_t MTRsaFingerprint(NSString *key) {
|
||||
BIO *keyBio = BIO_new(BIO_s_mem());
|
||||
uint64_t MTRsaFingerprint(id<EncryptionProvider> provider, NSString *key) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
id<MTRsaPublicKey> rsaKey = [provider parseRSAPublicKey:key];
|
||||
if (rsaKey == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
|
||||
BIO_write(keyBio, keyData.bytes, (int)keyData.length);
|
||||
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
|
||||
id<MTBignum> rsaKeyN = [context rsaGetN:rsaKey];
|
||||
id<MTBignum> rsaKeyE = [context rsaGetE:rsaKey];
|
||||
|
||||
BIGNUM *rsaKeyN = RSA_get0_n(rsaKey);
|
||||
BIGNUM *rsaKeyE = RSA_get0_e(rsaKey);
|
||||
|
||||
int nBytes = BN_num_bytes(rsaKeyN);
|
||||
int eBytes = BN_num_bytes(rsaKeyE);
|
||||
NSData *nData = [context getBin:rsaKeyN];
|
||||
NSData *eData = [context getBin:rsaKeyE];
|
||||
|
||||
MTBuffer *buffer = [[MTBuffer alloc] init];
|
||||
|
||||
NSMutableData *nData = [[NSMutableData alloc] initWithLength:nBytes];
|
||||
BN_bn2bin(rsaKeyN, nData.mutableBytes);
|
||||
[buffer appendTLBytes:nData];
|
||||
|
||||
NSMutableData *eData = [[NSMutableData alloc] initWithLength:eBytes];
|
||||
BN_bn2bin(rsaKeyE, eData.mutableBytes);
|
||||
[buffer appendTLBytes:eData];
|
||||
|
||||
NSData *sha1Data = MTSha1(buffer.data);
|
||||
@ -831,40 +756,15 @@ uint64_t MTRsaFingerprint(NSString *key) {
|
||||
(((uint64_t) sha1Buffer[14]) << 16) |
|
||||
(((uint64_t) sha1Buffer[13]) << 8) |
|
||||
((uint64_t) sha1Buffer[12]);
|
||||
RSA_free(rsaKey);
|
||||
BIO_free(keyBio);
|
||||
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
NSData *MTRsaEncryptPKCS1OAEP(NSString *key, NSData *data) {
|
||||
BIO *keyBio = BIO_new(BIO_s_mem());
|
||||
|
||||
NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
|
||||
BIO_write(keyBio, keyData.bytes, (int)keyData.length);
|
||||
RSA *rsaKey = PEM_read_bio_RSA_PUBKEY(keyBio, NULL, NULL, NULL);
|
||||
if (rsaKey == nil) {
|
||||
BIO_free(keyBio);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [[NSMutableData alloc] initWithLength:data.length + 2048];
|
||||
|
||||
int encryptedLength = RSA_public_encrypt((int)data.length, data.bytes, outData.mutableBytes, rsaKey, RSA_PKCS1_OAEP_PADDING);
|
||||
RSA_free(rsaKey);
|
||||
BIO_free(keyBio);
|
||||
|
||||
if (encryptedLength < 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
assert(encryptedLength <= outData.length);
|
||||
[outData setLength:encryptedLength];
|
||||
|
||||
return outData;
|
||||
NSData *MTRsaEncryptPKCS1OAEP(id<EncryptionProvider> provider, NSString *key, NSData *data) {
|
||||
return [provider rsaEncryptPKCS1OAEPWithPublicKey:key data:data];
|
||||
}
|
||||
|
||||
static NSData *decrypt_TL_data(unsigned char buffer[256]) {
|
||||
static NSData *decrypt_TL_data(id<EncryptionProvider> provider, unsigned char buffer[256]) {
|
||||
NSString *keyString = @"-----BEGIN RSA PUBLIC KEY-----\n"
|
||||
"MIIBCgKCAQEAyr+18Rex2ohtVy8sroGPBwXD3DOoKCSpjDqYoXgCqB7ioln4eDCF\n"
|
||||
"fOBUlfXUEvM/fnKCpF46VkAftlb4VuPDeQSS/ZxZYEGqHaywlroVnXHIjgqoxiAd\n"
|
||||
@ -873,66 +773,73 @@ static NSData *decrypt_TL_data(unsigned char buffer[256]) {
|
||||
"fDK/NWcvGqa0w/nriMD6mDjKOryamw0OP9QuYgMN0C9xMW9y8SmP4h92OAWodTYg\n"
|
||||
"Y1hZCxdv6cs5UnW9+PWvS+WIbkh+GaWYxwIDAQAB\n"
|
||||
"-----END RSA PUBLIC KEY-----";
|
||||
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
BIO *keyBio = BIO_new(BIO_s_mem());
|
||||
BIO_write(keyBio, keyData.bytes, (int)keyData.length);
|
||||
|
||||
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
|
||||
id<MTRsaPublicKey> rsaKey = [provider parseRSAPublicKey:keyString];
|
||||
if (rsaKey == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
BIGNUM *x = BN_new();
|
||||
BIGNUM *y = BN_new();
|
||||
BN_CTX *bnContext = BN_CTX_new();
|
||||
uint8_t *bytes = buffer;
|
||||
BN_bin2bn(bytes, 256, x);
|
||||
|
||||
BIGNUM *rsaKeyN = RSA_get0_n(rsaKey);
|
||||
BIGNUM *rsaKeyE = RSA_get0_e(rsaKey);
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
|
||||
id<MTBignum> x = [context create];
|
||||
id<MTBignum> y = [context create];
|
||||
|
||||
[context assignBinTo:x value:[NSData dataWithBytesNoCopy:buffer length:256 freeWhenDone:false]];
|
||||
|
||||
id<MTBignum> rsaKeyN = [context rsaGetN:rsaKey];
|
||||
id<MTBignum> rsaKeyE = [context rsaGetE:rsaKey];
|
||||
|
||||
NSData *result = nil;
|
||||
if (BN_mod_exp(y, x, rsaKeyE, rsaKeyN, bnContext) == 1) {
|
||||
unsigned l = 256 - BN_num_bytes(y);
|
||||
|
||||
if ([context modExpInto:y a:x b:rsaKeyE mod:rsaKeyN]) {
|
||||
NSData *yBytes = [context getBin:y];
|
||||
unsigned l = 256 - (unsigned)yBytes.length;
|
||||
memset(bytes, 0, l);
|
||||
if (BN_bn2bin(y, bytes + l) == 256 - l) {
|
||||
AES_KEY aeskey;
|
||||
unsigned char iv[16];
|
||||
memcpy(iv, bytes + 16, 16);
|
||||
AES_set_decrypt_key(bytes, 256, &aeskey);
|
||||
AES_cbc_encrypt(bytes + 32, bytes + 32, 256 - 32, &aeskey, iv, AES_DECRYPT);
|
||||
|
||||
[yBytes getBytes:bytes + l length:256 - l];
|
||||
|
||||
NSMutableData *iv = [[NSMutableData alloc] initWithLength:16];
|
||||
memcpy(iv.mutableBytes, bytes + 16, 16);
|
||||
|
||||
NSData *encryptedBytes = [[NSData alloc] initWithBytes:bytes + 32 length:256 - 32];
|
||||
|
||||
NSData *keyBytes = [[NSData alloc] initWithBytes:bytes length:32];
|
||||
|
||||
NSMutableData *decryptedBytes = [[NSMutableData alloc] initWithLength:encryptedBytes.length];
|
||||
MyAesCbcDecrypt(encryptedBytes.bytes, encryptedBytes.length, decryptedBytes.mutableBytes, keyBytes.bytes, keyBytes.length, iv.mutableBytes);
|
||||
|
||||
if (decryptedBytes == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *sha256Bytes = MTSha256([decryptedBytes subdataWithRange:NSMakeRange(0, 256 - 32 - 16)]);
|
||||
|
||||
unsigned char sha256_out[32];
|
||||
[sha256Bytes getBytes:sha256_out length:32];
|
||||
|
||||
NSData *sha256Part = [sha256Bytes subdataWithRange:NSMakeRange(0, 16)];
|
||||
NSData *testSha256 = [decryptedBytes subdataWithRange:NSMakeRange(decryptedBytes.length - 16, 16)];
|
||||
|
||||
if ([sha256Part isEqualToData:testSha256]) {
|
||||
memcpy(bytes + 32, decryptedBytes.bytes, 256 - 32);
|
||||
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||
unsigned char sha256_out[32];
|
||||
unsigned olen = 0;
|
||||
EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
|
||||
EVP_DigestUpdate(ctx, bytes + 32, 256 - 32 - 16);
|
||||
EVP_DigestFinal_ex(ctx, sha256_out, &olen);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
if (olen == 32) {
|
||||
if (memcmp(bytes + 256 - 16, sha256_out, 16) == 0) {
|
||||
unsigned data_len = *(unsigned *) (bytes + 32);
|
||||
if (data_len && data_len <= 256 - 32 - 16 && !(data_len & 3)) {
|
||||
result = [NSData dataWithBytes:buffer + 32 + 4 length:data_len];
|
||||
} else {
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"TL data length field invalid - %d", data_len);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"RSA signature check FAILED (SHA256 mismatch)");
|
||||
}
|
||||
unsigned data_len = *(unsigned *) (bytes + 32);
|
||||
if (data_len && data_len <= 256 - 32 - 16 && !(data_len & 3)) {
|
||||
result = [NSData dataWithBytes:buffer + 32 + 4 length:data_len];
|
||||
} else {
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"TL data length field invalid - %d", data_len);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"RSA signature check FAILED (SHA256 mismatch)");
|
||||
}
|
||||
}
|
||||
}
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
RSA_free(rsaKey);
|
||||
BIO_free(keyBio);
|
||||
BN_CTX_free(bnContext);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -965,13 +872,13 @@ static NSData *decrypt_TL_data(unsigned char buffer[256]) {
|
||||
|
||||
@end
|
||||
|
||||
MTBackupDatacenterData *MTIPDataDecode(NSData *data, NSString *phoneNumber) {
|
||||
MTBackupDatacenterData *MTIPDataDecode(id<EncryptionProvider> provider, NSData *data, NSString *phoneNumber) {
|
||||
if (data.length < 256) {
|
||||
return nil;
|
||||
}
|
||||
unsigned char buffer[256];
|
||||
memcpy(buffer, data.bytes, 256);
|
||||
NSData *result = decrypt_TL_data(buffer);
|
||||
NSData *result = decrypt_TL_data(provider, buffer);
|
||||
|
||||
if (result != nil) {
|
||||
MTBufferReader *reader = [[MTBufferReader alloc] initWithData:result];
|
||||
|
@ -45,10 +45,11 @@
|
||||
@property (nonatomic) bool enforceMedia;
|
||||
@property (nonatomic) bool cdn;
|
||||
@property (nonatomic) bool checkForProxyConnectionIssues;
|
||||
@property (nonatomic) bool canResetAuthData;
|
||||
@property (nonatomic) id requiredAuthToken;
|
||||
@property (nonatomic) NSInteger authTokenMasterDatacenterId;
|
||||
|
||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo requiredAuthToken:(id)requiredAuthToken authTokenMasterDatacenterId:(NSInteger)authTokenMasterDatacenterId;
|
||||
|
||||
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
|
||||
|
||||
|
@ -138,7 +138,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
return queue;
|
||||
}
|
||||
|
||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo
|
||||
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo requiredAuthToken:(id)requiredAuthToken authTokenMasterDatacenterId:(NSInteger)authTokenMasterDatacenterId
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NSAssert(context != nil, @"context should not be nil");
|
||||
@ -153,6 +153,8 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
|
||||
_datacenterId = datacenterId;
|
||||
_usageCalculationInfo = usageCalculationInfo;
|
||||
_apiEnvironment = context.apiEnvironment;
|
||||
_requiredAuthToken = requiredAuthToken;
|
||||
_authTokenMasterDatacenterId = authTokenMasterDatacenterId;
|
||||
|
||||
[_context addChangeListener:self];
|
||||
|
||||
@ -2183,6 +2185,23 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
[_context tempAuthKeyForDatacenterWithIdRequired:_datacenterId keyType:tempAuthKeyType];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
if (_requiredAuthToken != nil && _authTokenMasterDatacenterId != _datacenterId) {
|
||||
_authInfo = nil;
|
||||
[_context removeTokenForDatacenterWithId:_datacenterId];
|
||||
[_context performBatchUpdates:^{
|
||||
[_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil];
|
||||
[_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:false];
|
||||
}];
|
||||
_mtState |= MTProtoStateAwaitingDatacenterAuthorization;
|
||||
} else if (_canResetAuthData) {
|
||||
_authInfo = nil;
|
||||
[_context performBatchUpdates:^{
|
||||
[_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:nil];
|
||||
[_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:false];
|
||||
}];
|
||||
_mtState |= MTProtoStateAwaitingDatacenterAuthorization;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2691,15 +2710,16 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
{
|
||||
[[MTProto managerQueue] dispatchOnQueue:^
|
||||
{
|
||||
if (!_useUnauthorizedMode && context == _context && datacenterId == _datacenterId && authInfo != nil)
|
||||
if (!_useUnauthorizedMode && context == _context && datacenterId == _datacenterId)
|
||||
{
|
||||
_authInfo = authInfo;
|
||||
|
||||
bool wasSuspended = _mtState & (MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterTempAuthKey);
|
||||
|
||||
if (_mtState & MTProtoStateAwaitingDatacenterAuthorization)
|
||||
{
|
||||
[self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterAuthorization)];
|
||||
if (authInfo != nil) {
|
||||
if (_mtState & MTProtoStateAwaitingDatacenterAuthorization) {
|
||||
[self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterAuthorization)];
|
||||
}
|
||||
}
|
||||
|
||||
/*if (_transportScheme != nil && _useTempAuthKeys) {
|
||||
@ -2732,11 +2752,15 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
|
||||
[self checkTempAuthKeyBinding:_transportScheme.address];
|
||||
}*/
|
||||
|
||||
if ((_mtState & (MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterTempAuthKey)) == 0) {
|
||||
if (wasSuspended) {
|
||||
[self resetTransport];
|
||||
[self requestTransportTransaction];
|
||||
if (authInfo != nil) {
|
||||
if ((_mtState & (MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterTempAuthKey)) == 0) {
|
||||
if (wasSuspended) {
|
||||
[self resetTransport];
|
||||
[self requestTransportTransaction];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[self resetTransport];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
@ -23,97 +23,87 @@
|
||||
#import "MTSignal.h"
|
||||
#import "MTDNS.h"
|
||||
|
||||
#import <openssl/bn.h>
|
||||
#import <EncryptionProvider/EncryptionProvider.h>
|
||||
|
||||
static BIGNUM *get_y2(BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) {
|
||||
static id<MTBignum> get_y2(id<MTBignum> x, id<MTBignum> mod, id<MTBignumContext> context) {
|
||||
// returns y^2 = x^3 + 486662 * x^2 + x
|
||||
BIGNUM *y = BN_dup(x);
|
||||
id<MTBignum> y = [context clone:x];
|
||||
assert(y != NULL);
|
||||
BIGNUM *coef = BN_new();
|
||||
BN_set_word(coef, 486662);
|
||||
BN_mod_add(y, y, coef, mod, big_num_context);
|
||||
BN_mod_mul(y, y, x, mod, big_num_context);
|
||||
BN_one(coef);
|
||||
BN_mod_add(y, y, coef, mod, big_num_context);
|
||||
BN_mod_mul(y, y, x, mod, big_num_context);
|
||||
BN_clear_free(coef);
|
||||
id<MTBignum> coef = [context create];
|
||||
[context assignWordTo:coef value:486662];
|
||||
[context modAddInto:y a:y b:coef mod:mod];
|
||||
[context modMulInto:y a:y b:x mod:mod];
|
||||
[context assignOneTo:coef];
|
||||
[context modAddInto:y a:y b:coef mod:mod];
|
||||
[context modMulInto:y a:y b:x mod:mod];
|
||||
return y;
|
||||
}
|
||||
|
||||
static BIGNUM *get_double_x(BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) {
|
||||
static id<MTBignum> get_double_x(id<MTBignum> x, id<MTBignum> mod, id<MTBignumContext> context) {
|
||||
// returns x_2 =(x^2 - 1)^2/(4*y^2)
|
||||
BIGNUM *denominator = get_y2(x, mod, big_num_context);
|
||||
id<MTBignum> denominator = get_y2(x, mod, context);
|
||||
assert(denominator != NULL);
|
||||
BIGNUM *coef = BN_new();
|
||||
BN_set_word(coef, 4);
|
||||
BN_mod_mul(denominator, denominator, coef, mod, big_num_context);
|
||||
id<MTBignum> coef = [context create];
|
||||
[context assignWordTo:coef value:4];
|
||||
[context modMulInto:denominator a:denominator b:coef mod:mod];
|
||||
|
||||
BIGNUM *numerator = BN_new();
|
||||
id<MTBignum> numerator = [context create];
|
||||
assert(numerator != NULL);
|
||||
BN_mod_mul(numerator, x, x, mod, big_num_context);
|
||||
BN_one(coef);
|
||||
BN_mod_sub(numerator, numerator, coef, mod, big_num_context);
|
||||
BN_mod_mul(numerator, numerator, numerator, mod, big_num_context);
|
||||
[context modMulInto:numerator a:x b:x mod:mod];
|
||||
[context assignOneTo:coef];
|
||||
[context modSubInto:numerator a:numerator b:coef mod:mod];
|
||||
[context modMulInto:numerator a:numerator b:numerator mod:mod];
|
||||
|
||||
BN_mod_inverse(denominator, denominator, mod, big_num_context);
|
||||
BN_mod_mul(numerator, numerator, denominator, mod, big_num_context);
|
||||
[context modInverseInto:denominator a:denominator mod:mod];
|
||||
[context modMulInto:numerator a:numerator b:denominator mod:mod];
|
||||
|
||||
BN_clear_free(coef);
|
||||
BN_clear_free(denominator);
|
||||
return numerator;
|
||||
}
|
||||
|
||||
static void generate_public_key(unsigned char key[32]) {
|
||||
BIGNUM *mod = NULL;
|
||||
BN_hex2bn(&mod, "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed");
|
||||
BIGNUM *pow = NULL;
|
||||
BN_hex2bn(&pow, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6");
|
||||
BN_CTX *big_num_context = BN_CTX_new();
|
||||
assert(big_num_context != NULL);
|
||||
static void generate_public_key(unsigned char key[32], id<EncryptionProvider> provider) {
|
||||
id<MTBignumContext> context = [provider createBignumContext];
|
||||
assert(context != NULL);
|
||||
id<MTBignum> mod = [context create];
|
||||
[context assignHexTo:mod value:@"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"];
|
||||
id<MTBignum> pow = [context create];
|
||||
[context assignHexTo:pow value:@"3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6"];
|
||||
|
||||
BIGNUM *x = BN_new();
|
||||
id<MTBignum> x = [context create];
|
||||
while (1) {
|
||||
int randomResult = SecRandomCopyBytes(kSecRandomDefault, 32, key);
|
||||
assert(randomResult == errSecSuccess);
|
||||
|
||||
key[31] &= 127;
|
||||
BN_bin2bn(key, 32, x);
|
||||
assert(x != NULL);
|
||||
BN_mod_mul(x, x, x, mod, big_num_context);
|
||||
[context assignBinTo:x value:[NSData dataWithBytesNoCopy:key length:32 freeWhenDone:false]];
|
||||
|
||||
BIGNUM *y = get_y2(x, mod, big_num_context);
|
||||
[context modMulInto:x a:x b:x mod:mod];
|
||||
|
||||
BIGNUM *r = BN_new();
|
||||
BN_mod_exp(r, y, pow, mod, big_num_context);
|
||||
BN_clear_free(y);
|
||||
if (BN_is_one(r)) {
|
||||
BN_clear_free(r);
|
||||
id<MTBignum> y = get_y2(x, mod, context);
|
||||
|
||||
id<MTBignum> r = [context create];
|
||||
[context modExpInto:r a:y b:pow mod:mod];
|
||||
if ([context isOne:r]) {
|
||||
break;
|
||||
}
|
||||
BN_clear_free(r);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
BIGNUM *x2 = get_double_x(x, mod, big_num_context);
|
||||
BN_clear_free(x);
|
||||
id<MTBignum> x2 = get_double_x(x, mod, context);
|
||||
x = x2;
|
||||
}
|
||||
|
||||
int num_size = BN_num_bytes(x);
|
||||
NSData *xBytes = [context getBin:x];
|
||||
|
||||
int num_size = (int)[xBytes length];
|
||||
assert(num_size <= 32);
|
||||
memset(key, '\0', 32 - num_size);
|
||||
BN_bn2bin(x, key + (32 - num_size));
|
||||
[xBytes getBytes:key + (32 - num_size) length:[xBytes length]];
|
||||
for (i = 0; i < 16; i++) {
|
||||
unsigned char t = key[i];
|
||||
key[i] = key[31 - i];
|
||||
key[31 - i] = t;
|
||||
}
|
||||
|
||||
BN_clear_free(x);
|
||||
BN_CTX_free(big_num_context);
|
||||
BN_clear_free(pow);
|
||||
BN_clear_free(mod);
|
||||
}
|
||||
|
||||
@interface MTTcpConnectionData : NSObject
|
||||
@ -265,7 +255,9 @@ struct ctr_state {
|
||||
@end
|
||||
|
||||
@interface MTTcpConnection () <GCDAsyncSocketDelegate>
|
||||
{
|
||||
{
|
||||
id<EncryptionProvider> _encryptionProvider;
|
||||
|
||||
GCDAsyncSocket *_socket;
|
||||
bool _closed;
|
||||
|
||||
@ -341,6 +333,8 @@ struct ctr_state {
|
||||
{
|
||||
_internalId = [[MTInternalId(MTTcpConnection) alloc] init];
|
||||
|
||||
_encryptionProvider = context.encryptionProvider;
|
||||
|
||||
_scheme = scheme;
|
||||
|
||||
_interface = interface;
|
||||
@ -590,7 +584,7 @@ struct ctr_state {
|
||||
[helloData appendBytes:s6 length:117];
|
||||
|
||||
uint8_t r2[32];
|
||||
generate_public_key(r2);
|
||||
generate_public_key(r2, strongSelf->_encryptionProvider);
|
||||
|
||||
[helloData appendBytes:r2 length:32];
|
||||
|
||||
|
@ -64,7 +64,7 @@
|
||||
MTPayloadData payloadData;
|
||||
NSData *data = [MTDiscoverConnectionSignals payloadData:&payloadData context:context address:address];
|
||||
|
||||
MTContext *proxyContext = [[MTContext alloc] initWithSerialization:context.serialization apiEnvironment:[[context apiEnvironment] withUpdatedSocksProxySettings:settings] isTestingEnvironment:context.isTestingEnvironment useTempAuthKeys:context.useTempAuthKeys];
|
||||
MTContext *proxyContext = [[MTContext alloc] initWithSerialization:context.serialization encryptionProvider:context.encryptionProvider apiEnvironment:[[context apiEnvironment] withUpdatedSocksProxySettings:settings] isTestingEnvironment:context.isTestingEnvironment useTempAuthKeys:context.useTempAuthKeys];
|
||||
|
||||
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:proxyContext datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpConnection class] address:address media:false] interface:nil usageCalculationInfo:nil];
|
||||
__weak MTTcpConnection *weakConnection = connection;
|
||||
|
21
submodules/OpenSSLEncryptionProvider/BUCK
Normal file
21
submodules/OpenSSLEncryptionProvider/BUCK
Normal file
@ -0,0 +1,21 @@
|
||||
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||
|
||||
static_library(
|
||||
name = "OpenSSLEncryptionProvider",
|
||||
srcs = glob([
|
||||
"Sources/**/*.m",
|
||||
]),
|
||||
headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
exported_headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/EncryptionProvider:EncryptionProvider",
|
||||
"//submodules/openssl:openssl",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
],
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
#import <EncryptionProvider/EncryptionProvider.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OpenSSLEncryptionProvider : NSObject <EncryptionProvider>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -0,0 +1,375 @@
|
||||
#import "OpenSSLEncryptionProvider.h"
|
||||
|
||||
#import <openssl/bn.h>
|
||||
#import <openssl/rsa.h>
|
||||
#import <openssl/pem.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MTBignumImpl : NSObject <MTBignum, NSCopying> {
|
||||
@public
|
||||
BIGNUM *_value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTBignumImpl
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_value = BN_new();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithValue:(BIGNUM *)value {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_value = value;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
BN_clear_free(_value);
|
||||
}
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone * _Nullable)__unused zone {
|
||||
return [[MTBignumImpl alloc] initWithValue:BN_dup(_value)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MTRsaPublicKeyImpl : NSObject <MTRsaPublicKey> {
|
||||
@public
|
||||
RSA *_value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTRsaPublicKeyImpl
|
||||
|
||||
- (instancetype)initWithValue:(RSA *)value {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_value = value;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
RSA_free(_value);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MTBignumContextImpl : NSObject <MTBignumContext> {
|
||||
BN_CTX *_context;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MTBignumContextImpl
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_context = BN_CTX_new();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
BN_CTX_free(_context);
|
||||
}
|
||||
|
||||
- (id<MTBignum>)create {
|
||||
return [[MTBignumImpl alloc] init];
|
||||
}
|
||||
|
||||
- (id<MTBignum>)clone:(id<MTBignum>)other {
|
||||
assert([other isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *otherImpl = other;
|
||||
return [otherImpl copy];
|
||||
}
|
||||
|
||||
- (void)setConstantTime:(id<MTBignum>)other {
|
||||
assert([other isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *otherImpl = other;
|
||||
BN_set_flags(otherImpl->_value, BN_FLG_CONSTTIME);
|
||||
}
|
||||
|
||||
- (void)assignWordTo:(id<MTBignum>)bignum value:(unsigned long)value {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
BN_set_word(bignumImpl->_value, value);
|
||||
}
|
||||
|
||||
- (void)assignHexTo:(id<MTBignum>)bignum value:(NSString *)value {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
BN_hex2bn(&bignumImpl->_value, [value UTF8String]);
|
||||
}
|
||||
|
||||
- (void)assignBinTo:(id<MTBignum>)bignum value:(NSData *)value {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
BN_bin2bn(value.bytes, value.length, bignumImpl->_value);
|
||||
}
|
||||
|
||||
- (void)assignOneTo:(id<MTBignum>)bignum {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
BN_one(bignumImpl->_value);
|
||||
}
|
||||
|
||||
- (void)assignZeroTo:(id<MTBignum>)bignum {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
BN_zero(bignumImpl->_value);
|
||||
}
|
||||
|
||||
- (bool)isOne:(id<MTBignum>)bignum {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
return BN_is_one(bignumImpl->_value);
|
||||
}
|
||||
|
||||
- (bool)isZero:(id<MTBignum>)bignum {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
return BN_is_zero(bignumImpl->_value);
|
||||
}
|
||||
|
||||
- (NSData *)getBin:(id<MTBignum>)bignum {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
int numBytes = BN_num_bytes(bignumImpl->_value);
|
||||
NSMutableData *data = [[NSMutableData alloc] initWithLength:numBytes];
|
||||
BN_bn2bin(bignumImpl->_value, data.mutableBytes);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
- (int)isPrime:(id<MTBignum>)bignum numberOfChecks:(int)numberOfChecks {
|
||||
assert([bignum isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *bignumImpl = bignum;
|
||||
|
||||
return BN_is_prime_ex(bignumImpl->_value, numberOfChecks, _context, NULL);
|
||||
}
|
||||
|
||||
- (int)compare:(id<MTBignum>)a with:(id<MTBignum>)b {
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
|
||||
return BN_cmp(aImpl->_value, bImpl->_value);
|
||||
}
|
||||
|
||||
- (bool)modAddInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([mod isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
MTBignumImpl *modImpl = mod;
|
||||
|
||||
return BN_mod_add(resultImpl->_value, aImpl->_value, bImpl->_value, modImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (bool)modSubInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([mod isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
MTBignumImpl *modImpl = mod;
|
||||
|
||||
return BN_mod_sub(resultImpl->_value, aImpl->_value, bImpl->_value, modImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (bool)modMulInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([mod isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
MTBignumImpl *modImpl = mod;
|
||||
|
||||
return BN_mod_mul(resultImpl->_value, aImpl->_value, bImpl->_value, modImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (bool)modExpInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b mod:(id<MTBignum>)mod {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([mod isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
MTBignumImpl *modImpl = mod;
|
||||
|
||||
return BN_mod_exp(resultImpl->_value, aImpl->_value, bImpl->_value, modImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (bool)addInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
|
||||
return BN_add(resultImpl->_value, aImpl->_value, bImpl->_value) != 0;
|
||||
}
|
||||
|
||||
- (bool)subInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
|
||||
return BN_sub(resultImpl->_value, aImpl->_value, bImpl->_value) != 0;
|
||||
}
|
||||
|
||||
- (bool)mulInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
|
||||
return BN_mul(resultImpl->_value, aImpl->_value, bImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (bool)expInto:(id<MTBignum>)result a:(id<MTBignum>)a b:(id<MTBignum>)b {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([b isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *bImpl = b;
|
||||
|
||||
return BN_exp(resultImpl->_value, aImpl->_value, bImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (bool)modInverseInto:(id<MTBignum>)result a:(id<MTBignum>)a mod:(id<MTBignum>)mod {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([mod isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
MTBignumImpl *modImpl = mod;
|
||||
|
||||
return BN_mod_inverse(resultImpl->_value, aImpl->_value, modImpl->_value, _context) != 0;
|
||||
}
|
||||
|
||||
- (unsigned long)modWord:(id<MTBignum>)a mod:(unsigned long)mod {
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *aImpl = a;
|
||||
|
||||
return BN_mod_word(aImpl->_value, mod);
|
||||
}
|
||||
|
||||
- (bool)rightShift1Bit:(id<MTBignum>)result a:(id<MTBignum>)a {
|
||||
assert([result isKindOfClass:[MTBignumImpl class]]);
|
||||
assert([a isKindOfClass:[MTBignumImpl class]]);
|
||||
MTBignumImpl *resultImpl = result;
|
||||
MTBignumImpl *aImpl = a;
|
||||
return BN_rshift1(resultImpl->_value, aImpl->_value);
|
||||
}
|
||||
|
||||
- (id<MTBignum>)rsaGetE:(id<MTRsaPublicKey>)publicKey {
|
||||
assert([publicKey isKindOfClass:[MTRsaPublicKeyImpl class]]);
|
||||
MTRsaPublicKeyImpl *publicKeyImpl = publicKey;
|
||||
return [[MTBignumImpl alloc] initWithValue:BN_dup(RSA_get0_e(publicKeyImpl->_value))];
|
||||
}
|
||||
|
||||
- (id<MTBignum>)rsaGetN:(id<MTRsaPublicKey>)publicKey {
|
||||
assert([publicKey isKindOfClass:[MTRsaPublicKeyImpl class]]);
|
||||
MTRsaPublicKeyImpl *publicKeyImpl = publicKey;
|
||||
return [[MTBignumImpl alloc] initWithValue:BN_dup(RSA_get0_n(publicKeyImpl->_value))];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation OpenSSLEncryptionProvider
|
||||
|
||||
- (id<MTBignumContext>)createBignumContext {
|
||||
return [[MTBignumContextImpl alloc] init];
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)rsaEncryptWithPublicKey:(NSString *)publicKey data:(NSData *)data {
|
||||
MTRsaPublicKeyImpl *rsaKey = [self parseRSAPublicKey:publicKey];
|
||||
if (rsaKey == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
MTBignumContextImpl *context = [[MTBignumContextImpl alloc] init];
|
||||
|
||||
MTBignumImpl *a = [context create];
|
||||
[context assignBinTo:a value:data];
|
||||
|
||||
MTBignumImpl *r = [context create];
|
||||
[context modExpInto:r a:a b:[context rsaGetE:rsaKey] mod:[context rsaGetN:rsaKey]];
|
||||
|
||||
return [context getBin:r];
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)rsaEncryptPKCS1OAEPWithPublicKey:(NSString *)publicKey data:(NSData *)data {
|
||||
MTRsaPublicKeyImpl *rsaKey = [self parseRSAPublicKey:publicKey];
|
||||
if (rsaKey == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [[NSMutableData alloc] initWithLength:data.length + 2048];
|
||||
|
||||
int encryptedLength = RSA_public_encrypt((int)data.length, data.bytes, outData.mutableBytes, rsaKey->_value, RSA_PKCS1_OAEP_PADDING);
|
||||
|
||||
if (encryptedLength < 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
assert(encryptedLength <= outData.length);
|
||||
[outData setLength:encryptedLength];
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (id<MTRsaPublicKey>)parseRSAPublicKey:(NSString *)publicKey {
|
||||
BIO *keyBio = BIO_new(BIO_s_mem());
|
||||
NSData *keyData = [publicKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (keyData == nil) {
|
||||
return nil;
|
||||
}
|
||||
BIO_write(keyBio, keyData.bytes, (int)keyData.length);
|
||||
RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
|
||||
BIO_free(keyBio);
|
||||
if (rsaKey == nil) {
|
||||
return nil;
|
||||
}
|
||||
return [[MTRsaPublicKeyImpl alloc] initWithValue:rsaKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -35,7 +35,7 @@ static bool readCGFloat(NSString *string, int *position, CGFloat *result) {
|
||||
int length = (int)string.length;
|
||||
while (*position < length) {
|
||||
unichar c = [string characterAtIndex:*position];
|
||||
*position++;
|
||||
(*position)++;
|
||||
|
||||
if (c == '.') {
|
||||
if (seenDot) {
|
||||
@ -316,7 +316,7 @@ static bool ProxyWindowIsLight = true;
|
||||
dismissBlock();
|
||||
}];
|
||||
|
||||
if (_icon == nil) {
|
||||
if (_isShield) {
|
||||
dispatchAfter(0.15, dispatch_get_main_queue(), ^{
|
||||
[_spinner setSucceed];
|
||||
});
|
||||
|
20
submodules/PKCS/BUCK
Normal file
20
submodules/PKCS/BUCK
Normal file
@ -0,0 +1,20 @@
|
||||
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||
|
||||
static_library(
|
||||
name = "PKCS",
|
||||
srcs = glob([
|
||||
"Sources/**/*.m",
|
||||
]),
|
||||
headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
exported_headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/openssl:openssl",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
],
|
||||
)
|
@ -1169,14 +1169,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
|
||||
|
||||
let hasWallet = contextValue.get()
|
||||
|> mapToSignal { context in
|
||||
return context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> map { view -> Bool in
|
||||
if #available(iOSApplicationExtension 10.3, iOS 10.3, *) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return context.hasWalletAccess
|
||||
}
|
||||
|
||||
let hasPassport = ValuePromise<Bool>(false)
|
||||
|
@ -24,7 +24,8 @@ framework(
|
||||
"//submodules/MtProtoKit:MtProtoKit#shared",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/Postbox:Postbox#shared",
|
||||
"//submodules/CloudData:CloudData",
|
||||
"//submodules/CloudData:CloudData",
|
||||
"//submodules/EncryptionProvider:EncryptionProvider",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -15,7 +15,7 @@ import Foundation
|
||||
#endif
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
import EncryptionProvider
|
||||
|
||||
public protocol AccountState: PostboxCoding {
|
||||
func equalsTo(_ other: AccountState) -> Bool
|
||||
@ -571,7 +571,7 @@ func sha512Digest(_ data : Data) -> Data {
|
||||
}
|
||||
}
|
||||
|
||||
func passwordUpdateKDF(password: String, derivation: TwoStepPasswordDerivation) -> (Data, TwoStepPasswordDerivation)? {
|
||||
func passwordUpdateKDF(encryptionProvider: EncryptionProvider, password: String, derivation: TwoStepPasswordDerivation) -> (Data, TwoStepPasswordDerivation)? {
|
||||
guard let passwordData = password.data(using: .utf8, allowLossyConversion: true) else {
|
||||
return nil
|
||||
}
|
||||
@ -609,7 +609,7 @@ func passwordUpdateKDF(password: String, derivation: TwoStepPasswordDerivation)
|
||||
|
||||
let x = sha256Digest(nextSalt2 + pbkdfResult + nextSalt2)
|
||||
|
||||
let gx = MTExp(g, x, p)!
|
||||
let gx = MTExp(encryptionProvider, g, x, p)!
|
||||
|
||||
return (gx, .sha256_sha256_PBKDF2_HMAC_sha512_sha256_srp(salt1: nextSalt1, salt2: nextSalt2, iterations: iterations, g: gValue, p: p))
|
||||
}
|
||||
@ -653,7 +653,7 @@ private func paddedXor(_ a: Data, _ b: Data) -> Data {
|
||||
return a
|
||||
}
|
||||
|
||||
func passwordKDF(password: String, derivation: TwoStepPasswordDerivation, srpSessionData: TwoStepSRPSessionData) -> PasswordKDFResult? {
|
||||
func passwordKDF(encryptionProvider: EncryptionProvider, password: String, derivation: TwoStepPasswordDerivation, srpSessionData: TwoStepSRPSessionData) -> PasswordKDFResult? {
|
||||
guard let passwordData = password.data(using: .utf8, allowLossyConversion: true) else {
|
||||
return nil
|
||||
}
|
||||
@ -679,15 +679,15 @@ func passwordKDF(password: String, derivation: TwoStepPasswordDerivation, srpSes
|
||||
})
|
||||
}
|
||||
|
||||
if !MTCheckIsSafeB(srpSessionData.B, p) {
|
||||
if !MTCheckIsSafeB(encryptionProvider, srpSessionData.B, p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let B = paddedToLength(what: srpSessionData.B, to: p)
|
||||
let A = paddedToLength(what: MTExp(g, a, p)!, to: p)
|
||||
let A = paddedToLength(what: MTExp(encryptionProvider, g, a, p)!, to: p)
|
||||
let u = sha256Digest(A + B)
|
||||
|
||||
if MTIsZero(u) {
|
||||
if MTIsZero(encryptionProvider, u) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -699,18 +699,18 @@ func passwordKDF(password: String, derivation: TwoStepPasswordDerivation, srpSes
|
||||
|
||||
let x = sha256Digest(salt2 + pbkdfResult + salt2)
|
||||
|
||||
let gx = MTExp(g, x, p)!
|
||||
let gx = MTExp(encryptionProvider, g, x, p)!
|
||||
|
||||
let k = sha256Digest(p + paddedToLength(what: g, to: p))
|
||||
|
||||
let s1 = MTModSub(B, MTModMul(k, gx, p)!, p)!
|
||||
let s1 = MTModSub(encryptionProvider, B, MTModMul(encryptionProvider, k, gx, p)!, p)!
|
||||
|
||||
if !MTCheckIsSafeGAOrB(s1, p) {
|
||||
if !MTCheckIsSafeGAOrB(encryptionProvider, s1, p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let s2 = MTAdd(a, MTMul(u, x)!)!
|
||||
let S = MTExp(s1, s2, p)!
|
||||
let s2 = MTAdd(encryptionProvider, a, MTMul(encryptionProvider, u, x)!)!
|
||||
let S = MTExp(encryptionProvider, s1, s2, p)!
|
||||
let K = sha256Digest(paddedToLength(what: S, to: p))
|
||||
let m1 = paddedXor(sha256Digest(p), sha256Digest(paddedToLength(what: g, to: p)))
|
||||
let m2 = sha256Digest(salt1)
|
||||
@ -788,7 +788,7 @@ func verifyPassword(_ account: UnauthorizedAccount, password: String) -> Signal<
|
||||
return .fail(MTRpcError(errorCode: 400, errorDescription: "INTERNAL_NO_PASSWORD"))
|
||||
}
|
||||
|
||||
let kdfResult = passwordKDF(password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData)
|
||||
let kdfResult = passwordKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData)
|
||||
|
||||
if let kdfResult = kdfResult {
|
||||
return account.network.request(Api.functions.auth.checkPassword(password: .inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1))), automaticFloodWait: false)
|
||||
|
@ -2093,7 +2093,7 @@ private func recordPeerActivityTimestamp(peerId: PeerId, timestamp: Int32, into
|
||||
}
|
||||
}
|
||||
|
||||
func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountPeerId: PeerId, mediaBox: MediaBox, transaction: Transaction, auxiliaryMethods: AccountAuxiliaryMethods, finalState: AccountFinalState) -> AccountReplayedFinalState? {
|
||||
func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountPeerId: PeerId, mediaBox: MediaBox, encryptionProvider: EncryptionProvider, transaction: Transaction, auxiliaryMethods: AccountAuxiliaryMethods, finalState: AccountFinalState) -> AccountReplayedFinalState? {
|
||||
let verified = verifyTransaction(transaction, finalState: finalState.state)
|
||||
if !verified {
|
||||
Logger.shared.log("State", "failed to verify final state")
|
||||
@ -2551,7 +2551,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
}
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: presences)
|
||||
case let .UpdateSecretChat(chat, _):
|
||||
updateSecretChat(accountPeerId: accountPeerId, transaction: transaction, chat: chat, requestData: nil)
|
||||
updateSecretChat(encryptionProvider: encryptionProvider, accountPeerId: accountPeerId, transaction: transaction, chat: chat, requestData: nil)
|
||||
case let .AddSecretMessages(messages):
|
||||
for message in messages {
|
||||
let peerId = message.peerId
|
||||
@ -2869,7 +2869,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
||||
inner: while true {
|
||||
let keychain = (transaction.getPeerChatState(peerId) as? SecretChatState)?.keychain
|
||||
if processSecretChatIncomingEncryptedOperations(transaction: transaction, peerId: peerId) {
|
||||
let processResult = processSecretChatIncomingDecryptedOperations(mediaBox: mediaBox, transaction: transaction, peerId: peerId)
|
||||
let processResult = processSecretChatIncomingDecryptedOperations(encryptionProvider: encryptionProvider, mediaBox: mediaBox, transaction: transaction, peerId: peerId)
|
||||
if !processResult.addedMessages.isEmpty {
|
||||
let currentInclusion = transaction.getPeerChatListInclusion(peerId)
|
||||
if let groupId = currentInclusion.groupId, groupId == Namespaces.PeerGroup.archive {
|
||||
|
@ -439,7 +439,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
return postbox.transaction { transaction -> (difference: Api.updates.Difference?, finalStatte: AccountReplayedFinalState?, skipBecauseOfError: Bool) in
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let replayedState = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
let replayedState = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
@ -554,7 +554,7 @@ public final class AccountStateManager {
|
||||
|
||||
return postbox.transaction { transaction -> AccountReplayedFinalState? in
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
@ -756,10 +756,11 @@ public final class AccountStateManager {
|
||||
let accountManager = self.accountManager
|
||||
let postbox = self.postbox
|
||||
let mediaBox = self.postbox.mediaBox
|
||||
let network = self.network
|
||||
let auxiliaryMethods = self.auxiliaryMethods
|
||||
let signal = self.postbox.transaction { transaction -> AccountReplayedFinalState? in
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
let result = replayFinalState(accountManager: accountManager, postbox: postbox, accountPeerId: accountPeerId, mediaBox: mediaBox, encryptionProvider: network.encryptionProvider, transaction: transaction, auxiliaryMethods: auxiliaryMethods, finalState: finalState)
|
||||
let deltaTime = CFAbsoluteTimeGetCurrent() - startTime
|
||||
if deltaTime > 1.0 {
|
||||
Logger.shared.log("State", "replayFinalState took \(deltaTime)s")
|
||||
|
@ -505,10 +505,10 @@ private final class CallSessionManagerContext {
|
||||
switch context.state {
|
||||
case let .requested(_, accessHash, a, gA, config, _):
|
||||
let p = config.p.makeData()
|
||||
if !MTCheckIsSafeGAOrB(gA, p) {
|
||||
if !MTCheckIsSafeGAOrB(self.network.encryptionProvider, gA, p) {
|
||||
self.drop(internalId: internalId, reason: .disconnect)
|
||||
}
|
||||
var key = MTExp(gB.makeData(), a, p)!
|
||||
var key = MTExp(self.network.encryptionProvider, gB.makeData(), a, p)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
@ -654,7 +654,7 @@ private final class CallSessionManagerContext {
|
||||
}
|
||||
|
||||
private func makeSessionEncryptionKey(config: SecretChatEncryptionConfig, gAHash: Data, b: Data, gA: Data) -> (key: Data, keyId: Int64, keyVisualHash: Data)? {
|
||||
var key = MTExp(gA, b, config.p.makeData())!
|
||||
var key = MTExp(self.network.encryptionProvider, gA, b, config.p.makeData())!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
@ -831,9 +831,9 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal
|
||||
|
||||
let bData = b
|
||||
|
||||
let gb = MTExp(g, bData, p)!
|
||||
let gb = MTExp(network.encryptionProvider, g, bData, p)!
|
||||
|
||||
if !MTCheckIsSafeGAOrB(gb, p) {
|
||||
if !MTCheckIsSafeGAOrB(network.encryptionProvider, gb, p) {
|
||||
return .single(.failed)
|
||||
}
|
||||
|
||||
@ -893,8 +893,8 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
|
||||
let g = Data(bytes: &gValue, count: 4)
|
||||
let p = config.p.makeData()
|
||||
|
||||
let ga = MTExp(g, a, p)!
|
||||
if !MTCheckIsSafeGAOrB(ga, p) {
|
||||
let ga = MTExp(network.encryptionProvider, g, a, p)!
|
||||
if !MTCheckIsSafeGAOrB(network.encryptionProvider, ga, p) {
|
||||
return .single(.failed(.generic))
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ public func updateChannelOwnership(account: Account, accountStateManager: Accoun
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<Api.InputCheckPasswordSRP, ChannelOwnershipTransferError> in
|
||||
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||
guard let kdfResult = passwordKDF(password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
guard let kdfResult = passwordKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single(.inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1)))
|
||||
|
@ -34,9 +34,9 @@ public func createSecretChat(account: Account, peerId: PeerId) -> Signal<PeerId,
|
||||
let p = config.p.makeData()
|
||||
|
||||
let aData = a.makeData()
|
||||
let ga = MTExp(g, aData, p)!
|
||||
let ga = MTExp(account.network.encryptionProvider, g, aData, p)!
|
||||
|
||||
if !MTCheckIsSafeGAOrB(ga, p) {
|
||||
if !MTCheckIsSafeGAOrB(account.network.encryptionProvider, ga, p) {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ public func createSecretChat(account: Account, peerId: PeerId) -> Signal<PeerId,
|
||||
}
|
||||
|> mapToSignal { result -> Signal<PeerId, CreateSecretChatError> in
|
||||
return account.postbox.transaction { transaction -> PeerId in
|
||||
updateSecretChat(accountPeerId: account.peerId, transaction: transaction, chat: result, requestData: SecretChatRequestData(g: config.g, p: config.p, a: a))
|
||||
updateSecretChat(encryptionProvider: account.network.encryptionProvider, accountPeerId: account.peerId, transaction: transaction, chat: result, requestData: SecretChatRequestData(g: config.g, p: config.p, a: a))
|
||||
|
||||
return result.peerId
|
||||
} |> mapError { _ -> CreateSecretChatError in return .generic }
|
||||
|
@ -48,14 +48,17 @@ class Download: NSObject, MTRequestMessageServiceDelegate {
|
||||
self.isCdn = isCdn
|
||||
self.context = context
|
||||
|
||||
self.mtProto = MTProto(context: self.context, datacenterId: datacenterId, usageCalculationInfo: usageInfo)
|
||||
var requiredAuthToken: Any?
|
||||
var authTokenMasterDatacenterId: Int = 0
|
||||
if !isCdn && datacenterId != masterDatacenterId {
|
||||
authTokenMasterDatacenterId = masterDatacenterId
|
||||
requiredAuthToken = Int(datacenterId) as NSNumber
|
||||
}
|
||||
|
||||
self.mtProto = MTProto(context: self.context, datacenterId: datacenterId, usageCalculationInfo: usageInfo, requiredAuthToken: requiredAuthToken, authTokenMasterDatacenterId: authTokenMasterDatacenterId)
|
||||
self.mtProto.cdn = isCdn
|
||||
self.mtProto.useTempAuthKeys = self.context.useTempAuthKeys && !isCdn
|
||||
self.mtProto.media = isMedia
|
||||
if !isCdn && datacenterId != masterDatacenterId {
|
||||
self.mtProto.authTokenMasterDatacenterId = masterDatacenterId
|
||||
self.mtProto.requiredAuthToken = Int(datacenterId) as NSNumber
|
||||
}
|
||||
self.requestService = MTRequestMessageService(context: self.context)
|
||||
self.requestService.forceBackgroundRequests = true
|
||||
|
||||
|
@ -333,7 +333,7 @@ public func grantSecureIdAccess(network: Network, peerId: PeerId, publicKey: Str
|
||||
guard let (encryptedCredentialsData, decryptedCredentialsHash) = encryptedCredentialsData(data: credentialsData, secretData: credentialsSecretData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
guard let encryptedSecretData = MTRsaEncryptPKCS1OAEP(publicKey, credentialsSecretData) else {
|
||||
guard let encryptedSecretData = MTRsaEncryptPKCS1OAEP(network.encryptionProvider, publicKey, credentialsSecretData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId:
|
||||
|> mapToSignal { config -> Signal<Void, NoError> in
|
||||
let p = config.p.makeData()
|
||||
|
||||
if !MTCheckIsSafeGAOrB(gA.makeData(), p) {
|
||||
if !MTCheckIsSafeGAOrB(network.encryptionProvider, gA.makeData(), p) {
|
||||
return postbox.transaction { transaction -> Void in
|
||||
let removed = transaction.operationLogRemoveEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: tagLocalIndex)
|
||||
assert(removed)
|
||||
@ -219,9 +219,9 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId:
|
||||
|
||||
let bData = b.makeData()
|
||||
|
||||
let gb = MTExp(g, bData, p)!
|
||||
let gb = MTExp(network.encryptionProvider, g, bData, p)!
|
||||
|
||||
var key = MTExp(gA.makeData(), bData, p)!
|
||||
var key = MTExp(network.encryptionProvider, gA.makeData(), bData, p)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
@ -293,7 +293,7 @@ private func pfsRequestKey(postbox: Postbox, network: Network, peerId: PeerId, l
|
||||
let p = config.p.makeData()
|
||||
|
||||
let aData = a.makeData()
|
||||
let ga = MTExp(g, aData, p)!
|
||||
let ga = MTExp(network.encryptionProvider, g, aData, p)!
|
||||
|
||||
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
if let state = transaction.getPeerChatState(peerId) as? SecretChatState {
|
||||
@ -321,9 +321,9 @@ private func pfsAcceptKey(postbox: Postbox, network: Network, peerId: PeerId, la
|
||||
|
||||
let bData = b.makeData()
|
||||
|
||||
let gb = MTExp(g, bData, p)!
|
||||
let gb = MTExp(network.encryptionProvider, g, bData, p)!
|
||||
|
||||
var key = MTExp(gA.makeData(), bData, p)!
|
||||
var key = MTExp(network.encryptionProvider, gA.makeData(), bData, p)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
|
@ -16,6 +16,8 @@ import Foundation
|
||||
import CloudData
|
||||
#endif
|
||||
|
||||
import EncryptionProvider
|
||||
|
||||
public enum ConnectionStatus: Equatable {
|
||||
case waitingForNetwork
|
||||
case connecting(proxyAddress: String?, proxyHasConnectionIssues: Bool)
|
||||
@ -407,19 +409,20 @@ public struct NetworkInitializationArguments {
|
||||
public let appVersion: String
|
||||
public let voipMaxLayer: Int32
|
||||
public let appData: Signal<Data?, NoError>
|
||||
public let encryptionProvider: EncryptionProvider
|
||||
|
||||
public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal<Data?, NoError>) {
|
||||
public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal<Data?, NoError>, encryptionProvider: EncryptionProvider) {
|
||||
self.apiId = apiId
|
||||
self.languagesCategory = languagesCategory
|
||||
self.appVersion = appVersion
|
||||
self.voipMaxLayer = voipMaxLayer
|
||||
self.appData = appData
|
||||
self.encryptionProvider = encryptionProvider
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private let cloudDataContext = makeCloudDataContext()
|
||||
#endif
|
||||
private let cloudDataContext = Atomic<CloudDataContext?>(value: nil)
|
||||
|
||||
func initializedNetwork(arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?) -> Signal<Network, NoError> {
|
||||
return Signal { subscriber in
|
||||
let queue = Queue()
|
||||
@ -460,7 +463,7 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
|
||||
}
|
||||
}
|
||||
|
||||
let context = MTContext(serialization: serialization, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: false)!
|
||||
let context = MTContext(serialization: serialization, encryptionProvider: arguments.encryptionProvider, apiEnvironment: apiEnvironment, isTestingEnvironment: testingEnvironment, useTempAuthKeys: false)!
|
||||
|
||||
let seedAddressList: [Int: [String]]
|
||||
|
||||
@ -487,7 +490,15 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
|
||||
var wrappedAdditionalSource: MTSignal?
|
||||
#if os(iOS)
|
||||
if #available(iOS 10.0, *) {
|
||||
if let cloudDataContext = cloudDataContext {
|
||||
var cloudDataContextValue: CloudDataContext?
|
||||
if let value = cloudDataContext.with({ $0 }) {
|
||||
cloudDataContextValue = value
|
||||
} else {
|
||||
cloudDataContextValue = makeCloudDataContext(encryptionProvider: arguments.encryptionProvider)
|
||||
let _ = cloudDataContext.swap(cloudDataContextValue)
|
||||
}
|
||||
|
||||
if let cloudDataContext = cloudDataContextValue {
|
||||
wrappedAdditionalSource = MTSignal(generator: { subscriber in
|
||||
let disposable = cloudDataContext.get(phoneNumber: .single(phoneNumber)).start(next: { value in
|
||||
subscriber?.putNext(value)
|
||||
@ -504,10 +515,10 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
|
||||
context.setDiscoverBackupAddressListSignal(MTBackupAddressSignals.fetchBackupIps(testingEnvironment, currentContext: context, additionalSource: wrappedAdditionalSource, phoneNumber: phoneNumber))
|
||||
|
||||
#if DEBUG
|
||||
//let _ = MTBackupAddressSignals.fetchBackupIps(testingEnvironment, currentContext: context, additionalSource: wrappedAdditionalSource, phoneNumber: phoneNumber).start(next: nil)
|
||||
let _ = MTBackupAddressSignals.fetchBackupIps(testingEnvironment, currentContext: context, additionalSource: wrappedAdditionalSource, phoneNumber: phoneNumber).start(next: nil)
|
||||
#endif
|
||||
|
||||
let mtProto = MTProto(context: context, datacenterId: datacenterId, usageCalculationInfo: usageCalculationInfo(basePath: basePath, category: nil))!
|
||||
let mtProto = MTProto(context: context, datacenterId: datacenterId, usageCalculationInfo: usageCalculationInfo(basePath: basePath, category: nil), requiredAuthToken: nil, authTokenMasterDatacenterId: 0)!
|
||||
mtProto.useTempAuthKeys = context.useTempAuthKeys
|
||||
mtProto.checkForProxyConnectionIssues = true
|
||||
|
||||
@ -537,7 +548,7 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
|
||||
mtProto.delegate = connectionStatusDelegate
|
||||
mtProto.add(requestService)
|
||||
|
||||
let network = Network(queue: queue, datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath, appDataDisposable: appDataDisposable)
|
||||
let network = Network(queue: queue, datacenterId: datacenterId, context: context, mtProto: mtProto, requestService: requestService, connectionStatusDelegate: connectionStatusDelegate, _connectionStatus: connectionStatus, basePath: basePath, appDataDisposable: appDataDisposable, encryptionProvider: arguments.encryptionProvider)
|
||||
appDataUpdatedImpl = { [weak network] data in
|
||||
guard let data = data else {
|
||||
return
|
||||
@ -653,6 +664,8 @@ public enum NetworkRequestResult<T> {
|
||||
}
|
||||
|
||||
public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
public let encryptionProvider: EncryptionProvider
|
||||
|
||||
private let queue: Queue
|
||||
public let datacenterId: Int
|
||||
public let context: MTContext
|
||||
@ -703,7 +716,9 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
return "Network context: \(self.context)"
|
||||
}
|
||||
|
||||
fileprivate init(queue: Queue, datacenterId: Int, context: MTContext, mtProto: MTProto, requestService: MTRequestMessageService, connectionStatusDelegate: MTProtoConnectionStatusDelegate, _connectionStatus: Promise<ConnectionStatus>, basePath: String, appDataDisposable: Disposable) {
|
||||
fileprivate init(queue: Queue, datacenterId: Int, context: MTContext, mtProto: MTProto, requestService: MTRequestMessageService, connectionStatusDelegate: MTProtoConnectionStatusDelegate, _connectionStatus: Promise<ConnectionStatus>, basePath: String, appDataDisposable: Disposable, encryptionProvider: EncryptionProvider) {
|
||||
self.encryptionProvider = encryptionProvider
|
||||
|
||||
self.queue = queue
|
||||
self.datacenterId = datacenterId
|
||||
self.context = context
|
||||
@ -740,7 +755,7 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
|
||||
if id == Int(dcId) {
|
||||
let dict = NSMutableDictionary()
|
||||
dict["key"] = publicKey
|
||||
dict["fingerprint"] = MTRsaFingerprint(publicKey)
|
||||
dict["fingerprint"] = MTRsaFingerprint(encryptionProvider, publicKey)
|
||||
array.add(dict)
|
||||
}
|
||||
}
|
||||
@ -1050,9 +1065,9 @@ class Keychain: NSObject, MTKeychain {
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
func makeCloudDataContext() -> CloudDataContext? {
|
||||
func makeCloudDataContext(encryptionProvider: EncryptionProvider) -> CloudDataContext? {
|
||||
if #available(iOS 10.0, *) {
|
||||
return CloudDataContextImpl()
|
||||
return CloudDataContextImpl(encryptionProvider: encryptionProvider)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import Foundation
|
||||
import TelegramApi
|
||||
#endif
|
||||
|
||||
import EncryptionProvider
|
||||
|
||||
private enum MessageParsingError: Error {
|
||||
case contentParsingError
|
||||
case unsupportedLayer
|
||||
@ -64,7 +66,7 @@ struct SecretChatOperationProcessResult {
|
||||
let addedMessages: [StoreMessage]
|
||||
}
|
||||
|
||||
func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, transaction: Transaction, peerId: PeerId) -> SecretChatOperationProcessResult {
|
||||
func processSecretChatIncomingDecryptedOperations(encryptionProvider: EncryptionProvider, mediaBox: MediaBox, transaction: Transaction, peerId: PeerId) -> SecretChatOperationProcessResult {
|
||||
if let state = transaction.getPeerChatState(peerId) as? SecretChatState, let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
|
||||
var removeTagLocalIndices: [Int32] = []
|
||||
var updatedState = state
|
||||
@ -257,7 +259,7 @@ func processSecretChatIncomingDecryptedOperations(mediaBox: MediaBox, transactio
|
||||
updatedPeer = updatedPeer.withUpdatedMessageAutoremoveTimeout(timeout == 0 ? nil : timeout)
|
||||
updatedState = updatedState.withUpdatedMessageAutoremoveTimeout(timeout == 0 ? nil : timeout)
|
||||
case let .rekeyAction(action):
|
||||
updatedState = secretChatAdvanceRekeySessionIfNeeded(transaction: transaction, peerId: peerId, state: updatedState, action: action)
|
||||
updatedState = secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: encryptionProvider, transaction: transaction, peerId: peerId, state: updatedState, action: action)
|
||||
case let .deleteMessages(globallyUniqueIds):
|
||||
var messageIds: [MessageId] = []
|
||||
for id in globallyUniqueIds {
|
||||
|
@ -286,40 +286,40 @@ public func deleteSecureIdValues(network: Network, keys: Set<SecureIdValueKey>)
|
||||
|
||||
public func dropSecureId(network: Network, currentPassword: String) -> Signal<Void, AuthorizationPasswordVerificationError> {
|
||||
return twoStepAuthData(network)
|
||||
|> mapError { _ -> AuthorizationPasswordVerificationError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<Void, AuthorizationPasswordVerificationError> in
|
||||
let checkPassword: Api.InputCheckPasswordSRP
|
||||
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||
let kdfResult = passwordKDF(password: currentPassword, derivation: currentPasswordDerivation, srpSessionData: srpSessionData)
|
||||
if let kdfResult = kdfResult {
|
||||
checkPassword = .inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1))
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|> mapError { _ -> AuthorizationPasswordVerificationError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<Void, AuthorizationPasswordVerificationError> in
|
||||
let checkPassword: Api.InputCheckPasswordSRP
|
||||
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||
let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: currentPassword, derivation: currentPasswordDerivation, srpSessionData: srpSessionData)
|
||||
if let kdfResult = kdfResult {
|
||||
checkPassword = .inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1))
|
||||
} else {
|
||||
checkPassword = .inputCheckPasswordEmpty
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
let settings = network.request(Api.functions.account.getPasswordSettings(password: checkPassword), automaticFloodWait: false)
|
||||
|> mapError { error in
|
||||
return AuthorizationPasswordVerificationError.generic
|
||||
}
|
||||
|
||||
return settings
|
||||
|> mapToSignal { value -> Signal<Void, AuthorizationPasswordVerificationError> in
|
||||
switch value {
|
||||
case .passwordSettings:
|
||||
var flags: Int32 = 0
|
||||
flags |= (1 << 2)
|
||||
return network.request(Api.functions.account.updatePasswordSettings(password: .inputCheckPasswordEmpty, newSettings: .passwordInputSettings(flags: flags, newAlgo: nil, newPasswordHash: nil, hint: nil, email: nil, newSecureSettings: nil)), automaticFloodWait: false)
|
||||
|> map { _ in }
|
||||
|> mapError { _ in
|
||||
return AuthorizationPasswordVerificationError.generic
|
||||
}
|
||||
} else {
|
||||
checkPassword = .inputCheckPasswordEmpty
|
||||
}
|
||||
|
||||
let settings = network.request(Api.functions.account.getPasswordSettings(password: checkPassword), automaticFloodWait: false)
|
||||
|> mapError { error in
|
||||
return AuthorizationPasswordVerificationError.generic
|
||||
}
|
||||
|
||||
return settings
|
||||
|> mapToSignal { value -> Signal<Void, AuthorizationPasswordVerificationError> in
|
||||
switch value {
|
||||
case .passwordSettings:
|
||||
var flags: Int32 = 0
|
||||
flags |= (1 << 2)
|
||||
return network.request(Api.functions.account.updatePasswordSettings(password: .inputCheckPasswordEmpty, newSettings: .passwordInputSettings(flags: flags, newAlgo: nil, newPasswordHash: nil, hint: nil, email: nil, newSecureSettings: nil)), automaticFloodWait: false)
|
||||
|> map { _ in }
|
||||
|> mapError { _ in
|
||||
return AuthorizationPasswordVerificationError.generic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,12 +50,12 @@ func validatedEncryptionConfig(postbox: Postbox, network: Network) -> Signal<Sec
|
||||
return .complete()
|
||||
}
|
||||
|
||||
if !MTCheckMod(p.makeData(), UInt32(g), network.context.keychain) {
|
||||
if !MTCheckMod(network.encryptionProvider, p.makeData(), UInt32(g), network.context.keychain) {
|
||||
Logger.shared.log("SecretChatEncryptionConfig", "Invalid p or g")
|
||||
return .complete()
|
||||
}
|
||||
|
||||
if !MTCheckIsSafePrime(p.makeData(), network.context.keychain) {
|
||||
if !MTCheckIsSafePrime(network.encryptionProvider, p.makeData(), network.context.keychain) {
|
||||
Logger.shared.log("SecretChatEncryptionConfig", "Invalid p")
|
||||
return .never()
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import Foundation
|
||||
#endif
|
||||
#endif
|
||||
|
||||
import EncryptionProvider
|
||||
|
||||
private let keyUseCountThreshold: Int32 = 100
|
||||
|
||||
func secretChatInitiateRekeySessionIfNeeded(transaction: Transaction, peerId: PeerId, state: SecretChatState) -> SecretChatState {
|
||||
@ -36,105 +38,105 @@ func secretChatInitiateRekeySessionIfNeeded(transaction: Transaction, peerId: Pe
|
||||
return state
|
||||
}
|
||||
|
||||
func secretChatAdvanceRekeySessionIfNeeded(transaction: Transaction, peerId: PeerId, state: SecretChatState, action: SecretChatRekeyServiceAction) -> SecretChatState {
|
||||
func secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: EncryptionProvider, transaction: Transaction, peerId: PeerId, state: SecretChatState, action: SecretChatRekeyServiceAction) -> SecretChatState {
|
||||
switch state.embeddedState {
|
||||
case let .sequenceBasedLayer(sequenceState):
|
||||
switch action {
|
||||
case let .pfsAbortSession(rekeySessionId):
|
||||
if let rekeySession = sequenceState.rekeyState, rekeySession.id == rekeySessionId {
|
||||
return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(nil)))
|
||||
}
|
||||
case let .pfsAcceptKey(rekeySessionId, gB, remoteKeyFingerprint):
|
||||
if let rekeySession = sequenceState.rekeyState, rekeySession.id == rekeySessionId {
|
||||
switch rekeySession.data {
|
||||
case let .requested(a, config):
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let p = config.p.makeData()
|
||||
|
||||
let aData = a.makeData()
|
||||
if !MTCheckIsSafeGAOrB(gB.makeData(), p) {
|
||||
return state.withUpdatedEmbeddedState(.terminated)
|
||||
}
|
||||
|
||||
var key = MTExp(gB.makeData(), aData, p)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
} else {
|
||||
while key.count < 256 {
|
||||
key.insert(0, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
let keyHash = MTSha1(key)!
|
||||
|
||||
var keyFingerprint: Int64 = 0
|
||||
keyHash.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
memcpy(&keyFingerprint, bytes.advanced(by: keyHash.count - 8), 8)
|
||||
}
|
||||
|
||||
assert(remoteKeyFingerprint == keyFingerprint)
|
||||
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsCommitKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, keyFingerprint: keyFingerprint), mutable: true, delivered: false))
|
||||
|
||||
let keyValidityOperationIndex = transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing)
|
||||
let keyValidityOperationCanonicalIndex = sequenceState.canonicalOutgoingOperationIndex(keyValidityOperationIndex)
|
||||
|
||||
return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(nil))).withUpdatedKeychain(state.keychain.withUpdatedKey(fingerprint: keyFingerprint, { _ in
|
||||
return SecretChatKey(fingerprint: keyFingerprint, key: MemoryBuffer(data: key), validity: .sequenceBasedIndexRange(fromCanonicalIndex: keyValidityOperationCanonicalIndex), useCount: 0)
|
||||
}))
|
||||
default:
|
||||
assertionFailure()
|
||||
break
|
||||
}
|
||||
}
|
||||
case let .pfsCommitKey(rekeySessionId, keyFingerprint):
|
||||
if let rekeySession = sequenceState.rekeyState, rekeySession.id == rekeySessionId {
|
||||
if case let .accepted(key, localKeyFingerprint) = rekeySession.data, keyFingerprint == localKeyFingerprint {
|
||||
let keyValidityOperationIndex = transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing)
|
||||
let keyValidityOperationCanonicalIndex = sequenceState.canonicalOutgoingOperationIndex(keyValidityOperationIndex)
|
||||
|
||||
let updatedState = state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(nil))).withUpdatedKeychain(state.keychain.withUpdatedKey(fingerprint: keyFingerprint, { _ in
|
||||
return SecretChatKey(fingerprint: keyFingerprint, key: key, validity: .sequenceBasedIndexRange(fromCanonicalIndex: keyValidityOperationCanonicalIndex), useCount: 0)
|
||||
}))
|
||||
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .noop(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64()), mutable: true, delivered: false))
|
||||
|
||||
return updatedState
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
case let .pfsRequestKey(rekeySessionId, gA):
|
||||
var acceptSession = true
|
||||
if let rekeySession = sequenceState.rekeyState {
|
||||
switch rekeySession.data {
|
||||
case .requesting, .requested:
|
||||
if rekeySessionId < rekeySession.id {
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAbortSession(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id), mutable: true, delivered: false))
|
||||
} else {
|
||||
acceptSession = false
|
||||
}
|
||||
case .accepting, .accepted:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if acceptSession {
|
||||
let bBytes = malloc(256)!
|
||||
let _ = SecRandomCopyBytes(nil, 256, bBytes.assumingMemoryBound(to: UInt8.self))
|
||||
let b = MemoryBuffer(memory: bBytes, capacity: 256, length: 256, freeWhenDone: true)
|
||||
|
||||
let rekeySession = SecretChatRekeySessionState(id: rekeySessionId, data: .accepting)
|
||||
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAcceptKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, gA: gA, b: b), mutable: true, delivered: false))
|
||||
return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(rekeySession)))
|
||||
}
|
||||
case let .sequenceBasedLayer(sequenceState):
|
||||
switch action {
|
||||
case let .pfsAbortSession(rekeySessionId):
|
||||
if let rekeySession = sequenceState.rekeyState, rekeySession.id == rekeySessionId {
|
||||
return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(nil)))
|
||||
}
|
||||
default:
|
||||
break
|
||||
case let .pfsAcceptKey(rekeySessionId, gB, remoteKeyFingerprint):
|
||||
if let rekeySession = sequenceState.rekeyState, rekeySession.id == rekeySessionId {
|
||||
switch rekeySession.data {
|
||||
case let .requested(a, config):
|
||||
var gValue: Int32 = config.g.byteSwapped
|
||||
let p = config.p.makeData()
|
||||
|
||||
let aData = a.makeData()
|
||||
if !MTCheckIsSafeGAOrB(encryptionProvider, gB.makeData(), p) {
|
||||
return state.withUpdatedEmbeddedState(.terminated)
|
||||
}
|
||||
|
||||
var key = MTExp(encryptionProvider, gB.makeData(), aData, p)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
} else {
|
||||
while key.count < 256 {
|
||||
key.insert(0, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
let keyHash = MTSha1(key)!
|
||||
|
||||
var keyFingerprint: Int64 = 0
|
||||
keyHash.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
|
||||
memcpy(&keyFingerprint, bytes.advanced(by: keyHash.count - 8), 8)
|
||||
}
|
||||
|
||||
assert(remoteKeyFingerprint == keyFingerprint)
|
||||
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsCommitKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, keyFingerprint: keyFingerprint), mutable: true, delivered: false))
|
||||
|
||||
let keyValidityOperationIndex = transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing)
|
||||
let keyValidityOperationCanonicalIndex = sequenceState.canonicalOutgoingOperationIndex(keyValidityOperationIndex)
|
||||
|
||||
return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(nil))).withUpdatedKeychain(state.keychain.withUpdatedKey(fingerprint: keyFingerprint, { _ in
|
||||
return SecretChatKey(fingerprint: keyFingerprint, key: MemoryBuffer(data: key), validity: .sequenceBasedIndexRange(fromCanonicalIndex: keyValidityOperationCanonicalIndex), useCount: 0)
|
||||
}))
|
||||
default:
|
||||
assertionFailure()
|
||||
break
|
||||
}
|
||||
}
|
||||
case let .pfsCommitKey(rekeySessionId, keyFingerprint):
|
||||
if let rekeySession = sequenceState.rekeyState, rekeySession.id == rekeySessionId {
|
||||
if case let .accepted(key, localKeyFingerprint) = rekeySession.data, keyFingerprint == localKeyFingerprint {
|
||||
let keyValidityOperationIndex = transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing)
|
||||
let keyValidityOperationCanonicalIndex = sequenceState.canonicalOutgoingOperationIndex(keyValidityOperationIndex)
|
||||
|
||||
let updatedState = state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(nil))).withUpdatedKeychain(state.keychain.withUpdatedKey(fingerprint: keyFingerprint, { _ in
|
||||
return SecretChatKey(fingerprint: keyFingerprint, key: key, validity: .sequenceBasedIndexRange(fromCanonicalIndex: keyValidityOperationCanonicalIndex), useCount: 0)
|
||||
}))
|
||||
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .noop(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64()), mutable: true, delivered: false))
|
||||
|
||||
return updatedState
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
case let .pfsRequestKey(rekeySessionId, gA):
|
||||
var acceptSession = true
|
||||
if let rekeySession = sequenceState.rekeyState {
|
||||
switch rekeySession.data {
|
||||
case .requesting, .requested:
|
||||
if rekeySessionId < rekeySession.id {
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAbortSession(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id), mutable: true, delivered: false))
|
||||
} else {
|
||||
acceptSession = false
|
||||
}
|
||||
case .accepting, .accepted:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if acceptSession {
|
||||
let bBytes = malloc(256)!
|
||||
let _ = SecRandomCopyBytes(nil, 256, bBytes.assumingMemoryBound(to: UInt8.self))
|
||||
let b = MemoryBuffer(memory: bBytes, capacity: 256, length: 256, freeWhenDone: true)
|
||||
|
||||
let rekeySession = SecretChatRekeySessionState(id: rekeySessionId, data: .accepting)
|
||||
|
||||
transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAcceptKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, gA: gA, b: b), mutable: true, delivered: false))
|
||||
return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(rekeySession)))
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public func requestTwoStepVerifiationSettings(network: Network, password: String
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
guard let kdfResult = passwordKDF(password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
guard let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ public func updateTwoStepVerificationPassword(network: Network, currentPassword:
|
||||
|> mapToSignal { authData, secureSecret -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> in
|
||||
let checkPassword: Api.InputCheckPasswordSRP
|
||||
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||
if let kdfResult = passwordKDF(password: currentPassword ?? "", derivation: currentPasswordDerivation, srpSessionData: srpSessionData) {
|
||||
if let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: currentPassword ?? "", derivation: currentPasswordDerivation, srpSessionData: srpSessionData) {
|
||||
checkPassword = .inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1))
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
@ -179,7 +179,7 @@ public func updateTwoStepVerificationPassword(network: Network, currentPassword:
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
|
||||
guard let (updatedPasswordHash, updatedPasswordDerivation) = passwordUpdateKDF(password: password, derivation: authData.nextPasswordDerivation) else {
|
||||
guard let (updatedPasswordHash, updatedPasswordDerivation) = passwordUpdateKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: authData.nextPasswordDerivation) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
@ -251,7 +251,7 @@ func updateTwoStepVerificationSecureSecret(network: Network, password: String, s
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
guard let kdfResult = passwordKDF(password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
guard let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ public func updateTwoStepVerificationEmail(network: Network, currentPassword: St
|
||||
|> mapToSignal { authData -> Signal<UpdateTwoStepVerificationPasswordResult, UpdateTwoStepVerificationPasswordError> in
|
||||
let checkPassword: Api.InputCheckPasswordSRP
|
||||
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||
guard let kdfResult = passwordKDF(password: currentPassword, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
guard let kdfResult = passwordKDF(encryptionProvider: network.encryptionProvider, password: currentPassword, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
checkPassword = .inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1))
|
||||
@ -415,7 +415,7 @@ public func requestTemporaryTwoStepPasswordToken(account: Account, password: Str
|
||||
guard let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData else {
|
||||
return .fail(MTRpcError(errorCode: 400, errorDescription: "NO_PASSWORD"))
|
||||
}
|
||||
guard let kdfResult = passwordKDF(password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
guard let kdfResult = passwordKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
return .fail(MTRpcError(errorCode: 400, errorDescription: "KDF_ERROR"))
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ struct SecretChatRequestData {
|
||||
let a: MemoryBuffer
|
||||
}
|
||||
|
||||
func updateSecretChat(accountPeerId: PeerId, transaction: Transaction, chat: Api.EncryptedChat, requestData: SecretChatRequestData?) {
|
||||
func updateSecretChat(encryptionProvider: EncryptionProvider, accountPeerId: PeerId, transaction: Transaction, chat: Api.EncryptedChat, requestData: SecretChatRequestData?) {
|
||||
let currentPeer = transaction.getPeer(chat.peerId) as? TelegramSecretChat
|
||||
let currentState = transaction.getPeerChatState(chat.peerId) as? SecretChatState
|
||||
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.secretChatSettings) as? SecretChatSettings ?? SecretChatSettings.defaultSettings
|
||||
@ -33,14 +33,14 @@ func updateSecretChat(accountPeerId: PeerId, transaction: Transaction, chat: Api
|
||||
let pData = p.makeData()
|
||||
let aData = a.makeData()
|
||||
|
||||
if !MTCheckIsSafeGAOrB(gAOrB.makeData(), pData) {
|
||||
if !MTCheckIsSafeGAOrB(encryptionProvider, gAOrB.makeData(), pData) {
|
||||
var updatedState = currentState
|
||||
updatedState = updatedState.withUpdatedEmbeddedState(.terminated)
|
||||
transaction.setPeerChatState(chat.peerId, state: updatedState)
|
||||
return
|
||||
}
|
||||
|
||||
var key = MTExp(gAOrB.makeData(), aData, pData)!
|
||||
var key = MTExp(encryptionProvider, gAOrB.makeData(), aData, pData)!
|
||||
|
||||
if key.count > 256 {
|
||||
key.count = 256
|
||||
|
@ -190,6 +190,7 @@ framework(
|
||||
"//submodules/Markdown:Markdown",
|
||||
"//submodules/SearchPeerMembers:SearchPeerMembers",
|
||||
"//submodules/WidgetItems:WidgetItems",
|
||||
"//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -10,6 +10,7 @@ import AccountContext
|
||||
import LiveLocationManager
|
||||
import TemporaryCachedPeerDataManager
|
||||
import WalletCore
|
||||
import WalletUI
|
||||
|
||||
private final class DeviceSpecificContactImportContext {
|
||||
let disposable = MetaDisposable()
|
||||
@ -139,6 +140,22 @@ public final class AccountContextImpl: AccountContext {
|
||||
}
|
||||
}
|
||||
|
||||
public var hasWalletAccess: Signal<Bool, NoError> {
|
||||
return self.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
|> map { view -> Bool in
|
||||
guard let appConfiguration = view.values[PreferencesKeys.appConfiguration] as? AppConfiguration else {
|
||||
return false
|
||||
}
|
||||
let walletConfiguration = WalletConfiguration.with(appConfiguration: appConfiguration)
|
||||
if walletConfiguration.config != nil && walletConfiguration.blockchainName != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
public init(sharedContext: SharedAccountContextImpl, account: Account, tonContext: StoredTonContext?, limitsConfiguration: LimitsConfiguration) {
|
||||
self.sharedContextImpl = sharedContext
|
||||
self.account = account
|
||||
|
@ -28,6 +28,7 @@ import WalletUI
|
||||
import UrlHandling
|
||||
import WalletUrl
|
||||
import WalletCore
|
||||
import OpenSSLEncryptionProvider
|
||||
|
||||
private let handleVoipNotifications = false
|
||||
|
||||
@ -387,7 +388,7 @@ final class SharedApplicationContext {
|
||||
Logger.shared.log("data", "can't deserialize")
|
||||
}
|
||||
return data
|
||||
})
|
||||
}, encryptionProvider: OpenSSLEncryptionProvider())
|
||||
|
||||
guard let appGroupUrl = maybeAppGroupUrl else {
|
||||
UIAlertView(title: nil, message: "Error 2", delegate: nil, cancelButtonTitle: "OK").show()
|
||||
|
@ -188,7 +188,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private let startingBot = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
private let unblockingPeer = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
private let searching = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
private let searchResult = Promise<(SearchMessagesResult, SearchMessagesState)?>()
|
||||
private let searchResult = Promise<(SearchMessagesResult, SearchMessagesState, SearchMessagesLocation)?>()
|
||||
private let loadingMessage = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
private var preloadHistoryPeerId: PeerId?
|
||||
@ -3235,9 +3235,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = (strongSelf.searchResult.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] searchResult in
|
||||
if let strongSelf = self, let searchResult = searchResult?.0 {
|
||||
let controller = ChatSearchResultsController(context: strongSelf.context, searchQuery: searchData.query, messages: searchResult.messages, navigateToMessageIndex: { index in
|
||||
if let strongSelf = self, let (searchResult, searchState, searchLocation) = searchResult {
|
||||
|
||||
let controller = ChatSearchResultsController(context: strongSelf.context, location: searchLocation, searchQuery: searchData.query, searchResult: searchResult, searchState: searchState, navigateToMessageIndex: { index in
|
||||
strongSelf.interfaceInteraction?.navigateMessageSearch(.index(index))
|
||||
}, resultsUpdated: { results, state in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let updatedValue: (SearchMessagesResult, SearchMessagesState, SearchMessagesLocation)? = (results, state, searchLocation)
|
||||
strongSelf.searchResult.set(.single(updatedValue))
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { current in
|
||||
if let data = current.search {
|
||||
let messageIndices = results.messages.map({ $0.index }).sorted()
|
||||
var currentIndex = messageIndices.last
|
||||
if let previousResultId = data.resultsState?.currentId {
|
||||
for index in messageIndices {
|
||||
if index.id >= previousResultId {
|
||||
currentIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return current.updatedSearch(data.withUpdatedResultsState(ChatSearchResultsState(messageIndices: messageIndices, currentId: currentIndex?.id, state: state, totalCount: results.totalCount, completed: results.completed)))
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
})
|
||||
})
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
if case let .inline(navigationController) = strongSelf.presentationInterfaceState.mode {
|
||||
@ -4966,15 +4990,37 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var items: [ActionSheetItem] = []
|
||||
|
||||
if self.presentationInterfaceState.isScheduledMessages {
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak actionSheet] in
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ScheduledMessages_ClearAllConfirmation, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
beginClear(.scheduledMessages)
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.scheduledMessages)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
}))
|
||||
} else if canRemoveGlobally {
|
||||
items.append(DeleteChatPeerActionSheetItem(context: self.context, peer: mainPeer, chatPeer: chatPeer, action: .clearHistory, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak actionSheet] in
|
||||
beginClear(.forEveryone)
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForEveryone(mainPeer.compactDisplayTitle).0, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forEveryone)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteForCurrentUser, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
@ -4982,9 +5028,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}))
|
||||
} else {
|
||||
items.append(ActionSheetTextItem(title: text))
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak actionSheet] in
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ClearAll, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
beginClear(.forLocalPeer)
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: {
|
||||
beginClear(.forLocalPeer)
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
}))
|
||||
}
|
||||
|
||||
@ -6294,7 +6351,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
let search = searchMessages(account: self.context.account, location: searchState.location, query: searchState.query, state: nil, limit: limit)
|
||||
|> delay(0.2, queue: Queue.mainQueue())
|
||||
self.searchResult.set(search |> map(Optional.init))
|
||||
self.searchResult.set(search
|
||||
|> map { (result, state) -> (SearchMessagesResult, SearchMessagesState, SearchMessagesLocation)? in
|
||||
return (result, state, searchState.location)
|
||||
})
|
||||
|
||||
searchDisposable.set((search
|
||||
|> deliverOnMainQueue).start(next: { [weak self] results, updatedState in
|
||||
|
@ -105,7 +105,10 @@ private func chatListSearchContainerPreparedTransition(from fromEntries: [ChatLi
|
||||
class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private let messages: [Message]
|
||||
private let location: SearchMessagesLocation
|
||||
private let searchQuery: String
|
||||
private var searchResult: SearchMessagesResult
|
||||
private var searchState: SearchMessagesState
|
||||
|
||||
private var interaction: ChatListNodeInteraction?
|
||||
|
||||
@ -114,14 +117,23 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
private var enqueuedTransitions: [(ChatListSearchContainerTransition, Bool)] = []
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
var resultsUpdated: ((SearchMessagesResult, SearchMessagesState) -> Void)?
|
||||
var resultSelected: ((Int) -> Void)?
|
||||
|
||||
private let presentationDataPromise: Promise<ChatListPresentationData>
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
init(context: AccountContext, searchQuery: String, messages: [Message]) {
|
||||
private var isLoadingMore = false
|
||||
private let loadMoreDisposable = MetaDisposable()
|
||||
|
||||
private let previousEntries = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
|
||||
init(context: AccountContext, location: SearchMessagesLocation, searchQuery: String, searchResult: SearchMessagesResult, searchState: SearchMessagesState) {
|
||||
self.context = context
|
||||
self.messages = messages
|
||||
self.location = location
|
||||
self.searchQuery = searchQuery
|
||||
self.searchResult = searchResult
|
||||
self.searchState = searchState
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations))
|
||||
|
||||
@ -138,7 +150,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
|> map { presentationData -> [ChatListSearchEntry] in
|
||||
var entries: [ChatListSearchEntry] = []
|
||||
|
||||
for message in messages {
|
||||
for message in searchResult.messages {
|
||||
var peer = RenderedPeer(message: message)
|
||||
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
|
||||
if let channelPeer = message.peers[migrationReference.peerId] {
|
||||
@ -156,8 +168,8 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
}, togglePeerSelected: { _ in
|
||||
}, messageSelected: { [weak self] peer, message, _ in
|
||||
if let strongSelf = self {
|
||||
if let index = strongSelf.messages.firstIndex(where: { $0.index == message.index }) {
|
||||
strongSelf.resultSelected?(strongSelf.messages.count - index - 1)
|
||||
if let index = strongSelf.searchResult.messages.firstIndex(where: { $0.index == message.index }) {
|
||||
strongSelf.resultSelected?(strongSelf.searchResult.messages.count - index - 1)
|
||||
}
|
||||
strongSelf.listNode.clearHighlightAnimated(true)
|
||||
}
|
||||
@ -175,17 +187,86 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
interaction.searchTextHighightState = searchQuery
|
||||
self.interaction = interaction
|
||||
|
||||
let previousEntries = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
self.disposable.set((signal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] entries in
|
||||
if let strongSelf = self {
|
||||
let previousEntries = previousEntries.swap(entries)
|
||||
let previousEntries = strongSelf.previousEntries.swap(entries)
|
||||
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, context: context, interaction: interaction)
|
||||
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
}))
|
||||
|
||||
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard case let .known(value) = offset, value < 100.0 else {
|
||||
return
|
||||
}
|
||||
if strongSelf.searchResult.completed {
|
||||
return
|
||||
}
|
||||
if strongSelf.isLoadingMore {
|
||||
return
|
||||
}
|
||||
strongSelf.loadMore()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
self.loadMoreDisposable.dispose()
|
||||
}
|
||||
|
||||
private func loadMore() {
|
||||
self.isLoadingMore = true
|
||||
|
||||
self.loadMoreDisposable.set((searchMessages(account: self.context.account, location: self.location, query: self.searchQuery, state: self.searchState)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] (updatedResult, updatedState) in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let interaction = strongSelf.interaction else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isLoadingMore = false
|
||||
strongSelf.searchResult = updatedResult
|
||||
strongSelf.searchState = updatedState
|
||||
strongSelf.resultsUpdated?(updatedResult, updatedState)
|
||||
|
||||
let context = strongSelf.context
|
||||
|
||||
let signal = strongSelf.presentationDataPromise.get()
|
||||
|> map { presentationData -> [ChatListSearchEntry] in
|
||||
var entries: [ChatListSearchEntry] = []
|
||||
|
||||
for message in updatedResult.messages {
|
||||
var peer = RenderedPeer(message: message)
|
||||
if let group = message.peers[message.id.peerId] as? TelegramGroup, let migrationReference = group.migrationReference {
|
||||
if let channelPeer = message.peers[migrationReference.peerId] {
|
||||
peer = RenderedPeer(peer: channelPeer)
|
||||
}
|
||||
}
|
||||
entries.append(.message(message, peer, nil, presentationData))
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
strongSelf.disposable.set((signal
|
||||
|> deliverOnMainQueue).start(next: { entries in
|
||||
if let strongSelf = self {
|
||||
let previousEntries = strongSelf.previousEntries.swap(entries)
|
||||
|
||||
let firstTime = previousEntries == nil
|
||||
let transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: entries, context: context, interaction: interaction)
|
||||
strongSelf.enqueueTransition(transition, firstTime: firstTime)
|
||||
}
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
|
@ -15,19 +15,25 @@ final class ChatSearchResultsController: ViewController {
|
||||
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private let location: SearchMessagesLocation
|
||||
private let searchQuery: String
|
||||
private let messages: [Message]
|
||||
private let searchResult: SearchMessagesResult
|
||||
private let searchState: SearchMessagesState
|
||||
|
||||
private let navigateToMessageIndex: (Int) -> Void
|
||||
private let resultsUpdated: (SearchMessagesResult, SearchMessagesState) -> Void
|
||||
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, searchQuery: String, messages: [Message], navigateToMessageIndex: @escaping (Int) -> Void) {
|
||||
init(context: AccountContext, location: SearchMessagesLocation, searchQuery: String, searchResult: SearchMessagesResult, searchState: SearchMessagesState, navigateToMessageIndex: @escaping (Int) -> Void, resultsUpdated: @escaping (SearchMessagesResult, SearchMessagesState) -> Void) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.location = location
|
||||
self.searchQuery = searchQuery
|
||||
self.messages = messages
|
||||
self.navigateToMessageIndex = navigateToMessageIndex
|
||||
self.resultsUpdated = resultsUpdated
|
||||
self.searchResult = searchResult
|
||||
self.searchState = searchState
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings))
|
||||
|
||||
@ -57,11 +63,14 @@ final class ChatSearchResultsController: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = ChatSearchResultsControllerNode(context: self.context, searchQuery: self.searchQuery, messages: self.messages)
|
||||
self.displayNode = ChatSearchResultsControllerNode(context: self.context, location: self.location, searchQuery: self.searchQuery, searchResult: self.searchResult, searchState: self.searchState)
|
||||
self.controllerNode.resultSelected = { [weak self] messageIndex in
|
||||
self?.navigateToMessageIndex(messageIndex)
|
||||
self?.dismiss()
|
||||
}
|
||||
self.controllerNode.resultsUpdated = { [weak self] result, state in
|
||||
self?.resultsUpdated(result, state)
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
|
@ -13,6 +13,7 @@ import StickerResources
|
||||
import PhotoResources
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import OpenSSLEncryptionProvider
|
||||
|
||||
private enum NotificationContentAuthorizationError {
|
||||
case unauthorized
|
||||
@ -143,7 +144,7 @@ public final class NotificationViewControllerImpl {
|
||||
f(false)
|
||||
})
|
||||
|
||||
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData)), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import LegacyUI
|
||||
import PeerInfoUI
|
||||
import ShareItems
|
||||
import SettingsUI
|
||||
import OpenSSLEncryptionProvider
|
||||
|
||||
private let inForeground = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
|
||||
@ -171,7 +172,7 @@ public class ShareRootControllerImpl {
|
||||
})
|
||||
semaphore.wait()
|
||||
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData)), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
let sharedContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in })
|
||||
internalContext = InternalContext(sharedContext: sharedContext)
|
||||
globalInternalContext = internalContext
|
||||
}
|
||||
|
24
submodules/TonBinding/BUCK
Normal file
24
submodules/TonBinding/BUCK
Normal file
@ -0,0 +1,24 @@
|
||||
load("//Config:buck_rule_macros.bzl", "static_library")
|
||||
|
||||
static_library(
|
||||
name = "TonBinding",
|
||||
srcs = glob([
|
||||
"Sources/**/*.m",
|
||||
"Sources/**/*.mm",
|
||||
]),
|
||||
headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
exported_headers = glob([
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SSignalKit:SSignalKit",
|
||||
"//submodules/openssl:openssl",
|
||||
"//submodules/ton:ton",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
],
|
||||
)
|
@ -1,9 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SSignalKit/SSignalKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class MTSignal;
|
||||
|
||||
@interface TONError : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSString *text;
|
||||
@ -88,15 +87,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests;
|
||||
|
||||
- (MTSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword;
|
||||
- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey;
|
||||
- (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress;
|
||||
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSData *)textMessage forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId;
|
||||
- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword;
|
||||
- (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList;
|
||||
- (MTSignal *)deleteKey:(TONKey *)key;
|
||||
- (MTSignal *)deleteAllKeys;
|
||||
- (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash;
|
||||
- (SSignal *)updateConfig:(NSString *)config blockchainName:(NSString *)blockchainName;
|
||||
|
||||
- (SSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword;
|
||||
- (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey;
|
||||
- (SSignal *)getAccountStateWithAddress:(NSString *)accountAddress;
|
||||
- (SSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSData *)textMessage forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId;
|
||||
- (SSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword;
|
||||
- (SSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList;
|
||||
- (SSignal *)deleteKey:(TONKey *)key;
|
||||
- (SSignal *)deleteAllKeys;
|
||||
- (SSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash;
|
||||
|
||||
- (NSData *)encrypt:(NSData *)decryptedData secret:(NSData *)data;
|
||||
- (NSData * __nullable)decrypt:(NSData *)encryptedData secret:(NSData *)data;
|
@ -1,9 +1,6 @@
|
||||
#import "TON.h"
|
||||
|
||||
#import "MTLogging.h"
|
||||
#import "tonlib/Client.h"
|
||||
#import "MTQueue.h"
|
||||
#import "MTSignal.h"
|
||||
|
||||
static td::SecureString makeSecureString(NSData * _Nonnull data) {
|
||||
if (data == nil || data.length == 0) {
|
||||
@ -226,9 +223,10 @@ typedef enum {
|
||||
uint64_t _nextRequestId;
|
||||
NSLock *_requestHandlersLock;
|
||||
NSMutableDictionary<NSNumber *, TONRequestHandler *> *_requestHandlers;
|
||||
MTPipe *_initializedStatus;
|
||||
SPipe *_initializedStatus;
|
||||
NSMutableSet *_sendGramRandomIds;
|
||||
MTQueue *_queue;
|
||||
SQueue *_queue;
|
||||
bool _enableExternalRequests;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -247,13 +245,14 @@ typedef enum {
|
||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = [MTQueue mainQueue];
|
||||
_queue = [SQueue mainQueue];
|
||||
_requestHandlersLock = [[NSLock alloc] init];
|
||||
_requestHandlers = [[NSMutableDictionary alloc] init];
|
||||
_initializedStatus = [[MTPipe alloc] initWithReplay:true];
|
||||
_initializedStatus = [[SPipe alloc] initWithReplay:true];
|
||||
_initializedStatus.sink(@(TONInitializationStatusInitializing));
|
||||
_nextRequestId = 1;
|
||||
_sendGramRandomIds = [[NSMutableSet alloc] init];
|
||||
_enableExternalRequests = enableExternalRequests;
|
||||
|
||||
_client = std::make_shared<tonlib::Client>();
|
||||
|
||||
@ -306,7 +305,7 @@ typedef enum {
|
||||
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:keystoreDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
||||
|
||||
MTPipe *initializedStatus = _initializedStatus;
|
||||
SPipe *initializedStatus = _initializedStatus;
|
||||
[[self requestInitWithConfigString:config blockchainName:blockchainName keystoreDirectory:keystoreDirectory enableExternalRequests:enableExternalRequests] startWithNext:nil error:^(id error) {
|
||||
NSString *errorText = @"Unknown error";
|
||||
if ([error isKindOfClass:[TONError class]]) {
|
||||
@ -354,8 +353,8 @@ typedef enum {
|
||||
return readSecureString(value->bytes_);
|
||||
}
|
||||
|
||||
- (MTSignal *)requestInitWithConfigString:(NSString *)configString blockchainName:(NSString *)blockchainName keystoreDirectory:(NSString *)keystoreDirectory enableExternalRequests:(bool)enableExternalRequests {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)requestInitWithConfigString:(NSString *)configString blockchainName:(NSString *)blockchainName keystoreDirectory:(NSString *)keystoreDirectory enableExternalRequests:(bool)enableExternalRequests {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
@ -381,13 +380,42 @@ typedef enum {
|
||||
));
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)updateConfig:(NSString *)config blockchainName:(NSString *)blockchainName {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
_requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr<tonlib_api::Object> &object) {
|
||||
if (object->get_id() == tonlib_api::error::ID) {
|
||||
auto error = tonlib_api::move_object_as<tonlib_api::error>(object);
|
||||
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
|
||||
} else {
|
||||
[subscriber putCompletion];
|
||||
}
|
||||
}];
|
||||
|
||||
auto query = make_object<tonlib_api::options_setConfig>(
|
||||
make_object<tonlib_api::config>(
|
||||
config.UTF8String,
|
||||
blockchainName.UTF8String,
|
||||
_enableExternalRequests,
|
||||
false
|
||||
)
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (SSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
@ -417,17 +445,17 @@ typedef enum {
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
NSData *publicKeyData = [publicKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (publicKeyData == nil) {
|
||||
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in getWalletAccountAddressWithPublicKey"]];
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{}];
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{}];
|
||||
}
|
||||
|
||||
uint64_t requestId = _nextRequestId;
|
||||
@ -453,13 +481,13 @@ typedef enum {
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)getAccountStateWithAddress:(NSString *)accountAddress {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
@ -491,34 +519,34 @@ typedef enum {
|
||||
auto query = make_object<tonlib_api::generic_getAccountState>(make_object<tonlib_api::accountAddress>(accountAddress.UTF8String));
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSData *)textMessage forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSData *)textMessage forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
if ([_sendGramRandomIds containsObject:@(randomId)]) {
|
||||
[_sendGramRandomIds addObject:@(randomId)];
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}
|
||||
|
||||
NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (publicKeyData == nil) {
|
||||
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in sendGramsFromKey"]];
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{}];
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{}];
|
||||
}
|
||||
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
__weak TON *weakSelf = self;
|
||||
MTQueue *queue = _queue;
|
||||
SQueue *queue = _queue;
|
||||
_requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr<tonlib_api::Object> &object) {
|
||||
if (object->get_id() == tonlib_api::error::ID) {
|
||||
[queue dispatchOnQueue:^{
|
||||
[queue dispatch:^{
|
||||
__strong TON *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[_sendGramRandomIds removeObject:@(randomId)];
|
||||
@ -553,17 +581,17 @@ typedef enum {
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (publicKeyData == nil) {
|
||||
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in exportKey"]];
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{}];
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{}];
|
||||
}
|
||||
|
||||
uint64_t requestId = _nextRequestId;
|
||||
@ -601,13 +629,13 @@ typedef enum {
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
@ -642,17 +670,17 @@ typedef enum {
|
||||
make_object<tonlib_api::exportedKey>(std::move(wordVector)));
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)deleteKey:(TONKey *)key {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)deleteKey:(TONKey *)key {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (publicKeyData == nil) {
|
||||
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in deleteKey"]];
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{}];
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{}];
|
||||
}
|
||||
|
||||
uint64_t requestId = _nextRequestId;
|
||||
@ -675,13 +703,13 @@ typedef enum {
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)deleteAllKeys {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)deleteAllKeys {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
@ -697,17 +725,17 @@ typedef enum {
|
||||
auto query = make_object<tonlib_api::deleteAllKeys>();
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
- (SSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash {
|
||||
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
|
||||
NSData *addressData = [address dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (addressData == nil) {
|
||||
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in getTransactionListWithAddress"]];
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{}];
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{}];
|
||||
}
|
||||
|
||||
uint64_t requestId = _nextRequestId;
|
||||
@ -750,9 +778,9 @@ typedef enum {
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
return [[SBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
|
||||
}
|
||||
|
||||
@end
|
@ -6,8 +6,8 @@ static_library(
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
deps = [
|
||||
"//submodules/MtProtoKit:MtProtoKit#shared",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/TonBinding:TonBinding",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -4,7 +4,7 @@ import SwiftSignalKitMac
|
||||
import MtProtoKitMac
|
||||
#else
|
||||
import SwiftSignalKit
|
||||
import MtProtoKit
|
||||
import TonBinding
|
||||
#endif
|
||||
|
||||
public struct TonKeychainEncryptedData: Codable, Equatable {
|
||||
@ -102,6 +102,24 @@ public final class TonInstance {
|
||||
})
|
||||
}
|
||||
|
||||
public func updateConfig(config: String, blockchainName: String) -> Signal<Never, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.updateConfig(config, blockchainName: blockchainName).start(next: nil, error: { _ in
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
cancel?.dispose()
|
||||
})
|
||||
}
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func exportKey(key: TONKey, localPassword: Data) -> Signal<[String], NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
|
@ -162,7 +162,8 @@ public final class WalletSplashScreen: ViewController {
|
||||
|> restart
|
||||
|> take(1)
|
||||
|
||||
strongSelf.actionDisposable.set(check.start(error: { _ in
|
||||
strongSelf.actionDisposable.set((check
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user