Request verification

This commit is contained in:
Isaac 2024-05-15 21:09:50 +04:00
parent 95ffacf97c
commit 4ac12021d5
12 changed files with 169 additions and 14 deletions

View File

@ -742,7 +742,7 @@ private final class NotificationServiceHandler {
Logger.shared.logToConsole = loggingSettings.logToConsole
Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: false)
let networkArguments = NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), externalRequestVerificationStream: .never(), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: false)
let isLockedMessage: String?
if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {

View File

@ -174,7 +174,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
if let accountCache = accountCache {
account = .single(accountCache)
} else {
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: false), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), externalRequestVerificationStream: .never(), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: false), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|> mapToSignal { account -> Signal<Account?, NoError> in
if let account = account {
switch account {

View File

@ -99,6 +99,8 @@
- (void)removeChangeListener:(id<MTContextChangeListener> _Nonnull)changeListener;
- (void)setDiscoverBackupAddressListSignal:(MTSignal * _Nonnull)signal;
- (void)setExternalRequestVerification:(MTSignal * _Nonnull (^ _Nonnull)(NSString * _Nonnull))externalRequestVerification;
- (MTSignal * _Nullable)performExternalRequestVerificationWithNonce:(NSString * _Nonnull)nonce;
- (NSTimeInterval)globalTime;
- (NSTimeInterval)globalTimeDifference;

View File

@ -1,5 +1,3 @@
#import <Foundation/Foundation.h>
@interface MTRequestContext : NSObject

View File

@ -1,7 +1,18 @@
#import <Foundation/Foundation.h>
@protocol MTDisposable;
@interface MTRequestPendingVerificationData : NSObject
@property (nonatomic, strong, readonly) NSString *nonce;
@property (nonatomic, strong) NSString *secret;
@property (nonatomic) bool isResolved;
@property (nonatomic, strong) id<MTDisposable> disposable;
- (instancetype)initWithNonce:(NSString *)nonce;
@end
@interface MTRequestErrorContext : NSObject
@property (nonatomic) CFAbsoluteTime minimalExecuteTime;
@ -13,4 +24,6 @@
@property (nonatomic) bool waitingForTokenExport;
@property (nonatomic, strong) id waitingForRequestToComplete;
@property (nonatomic, strong) MTRequestPendingVerificationData *pendingVerificationData;
@end

View File

@ -181,6 +181,7 @@ static MTDatacenterAuthInfoMapKeyStruct parseAuthInfoMapKeyInteger(NSNumber *key
NSMutableArray<MTWeakContextChangeListener *> *_changeListeners;
MTSignal *_discoverBackupAddressListSignal;
MTSignal * _Nonnull (^ _Nullable _externalRequestVerification)(NSString * _Nonnull);
NSMutableDictionary *_discoverDatacenterAddressActions;
NSMutableDictionary<NSNumber *, MTDatacenterAuthAction *> *_datacenterAuthActions;
@ -526,6 +527,25 @@ static void copyKeychainDictionaryKey(NSString * _Nonnull group, NSString * _Non
} synchronous:true];
}
- (void)setExternalRequestVerification:(MTSignal * _Nonnull (^ _Nonnull)(NSString * _Nonnull))externalRequestVerification {
[[MTContext contextQueue] dispatchOnQueue:^ {
_externalRequestVerification = externalRequestVerification;
} synchronous:true];
}
- (MTSignal * _Nullable)performExternalRequestVerificationWithNonce:(NSString * _Nonnull)nonce {
__block MTSignal * _Nonnull (^ _Nullable externalRequestVerification)(NSString * _Nonnull);
[[MTContext contextQueue] dispatchOnQueue:^ {
externalRequestVerification = _externalRequestVerification;
} synchronous:true];
if (externalRequestVerification != nil) {
return externalRequestVerification(nonce);
} else {
return [MTSignal single:nil];
}
}
- (NSTimeInterval)globalTime
{
return [[NSDate date] timeIntervalSince1970] + [self globalTimeDifference];

View File

@ -1,5 +1,17 @@
#import <MtProtoKit/MTRequestErrorContext.h>
@implementation MTRequestPendingVerificationData
- (instancetype)initWithNonce:(NSString *)nonce {
self = [super init];
if (self != nil) {
_nonce = nonce;
}
return self;
}
@end
@implementation MTRequestErrorContext
@end

View File

@ -17,6 +17,7 @@
#import <MtProtoKit/MTDropResponseContext.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTDatacenterAuthInfo.h>
#import <MtProtoKit/MTSignal.h>
#import "MTBuffer.h"
#import "MTInternalMessageParser.h"
@ -24,6 +25,26 @@
#import <MtProtoKit/MTRpcError.h>
#import "MTDropRpcResultMessage.h"
@interface MTRequestVerificationData : NSObject
@property (nonatomic, strong, readonly) NSString *nonce;
@property (nonatomic, strong, readonly) NSString *secret;
@end
@implementation MTRequestVerificationData
- (instancetype)initWithNonce:(NSString *)nonce secret:(NSString *)secret {
self = [super init];
if (self != nil) {
_nonce = nonce;
_secret = secret;
}
return self;
}
@end
@interface MTRequestMessageService ()
{
MTContext *_context;
@ -381,8 +402,8 @@
}
}
- (NSData *)decorateRequestData:(MTRequest *)request initializeApi:(bool)initializeApi unresolvedDependencyOnRequestInternalId:(__autoreleasing id *)unresolvedDependencyOnRequestInternalId decoratedDebugDescription:(__autoreleasing NSString **)decoratedDebugDescription
{
- (NSData *)decorateRequestData:(MTRequest *)request initializeApi:(bool)initializeApi requestVerificationData:(MTRequestVerificationData *)requestVerificationData unresolvedDependencyOnRequestInternalId:(__autoreleasing id *)unresolvedDependencyOnRequestInternalId decoratedDebugDescription:(__autoreleasing NSString **)decoratedDebugDescription
{
NSData *currentData = request.payload;
NSString *debugDescription = @"";
@ -397,8 +418,6 @@
// invokeWithLayer
[buffer appendInt32:(int32_t)0xda9b0d0d];
[buffer appendInt32:(int32_t)[_serialization currentLayer]];
//initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
int32_t flags = 0;
if (_apiEnvironment.socksProxySettings.secret != nil) {
@ -482,6 +501,19 @@
}
}
if (requestVerificationData != nil) {
MTBuffer *buffer = [[MTBuffer alloc] init];
[buffer appendInt32:(int32_t)0xdae54f8];
[buffer appendTLString:requestVerificationData.nonce];
[buffer appendTLString:requestVerificationData.secret];
[buffer appendBytes:currentData.bytes length:currentData.length];
currentData = buffer.data;
debugDescription = [debugDescription stringByAppendingFormat:@", apnsSecret(%@, %@)", requestVerificationData.nonce, requestVerificationData.secret];
}
if (decoratedDebugDescription != nil) {
*decoratedDebugDescription = debugDescription;
}
@ -511,6 +543,11 @@
if (request.errorContext.waitingForTokenExport) {
continue;
}
if (request.errorContext.pendingVerificationData != nil) {
if (!request.errorContext.pendingVerificationData.isResolved) {
continue;
}
}
bool foundDependency = false;
for (MTRequest *anotherRequest in _requests) {
@ -542,7 +579,16 @@
messageSeqNo = request.requestContext.messageSeqNo;
}
NSData *decoratedRequestData = [self decorateRequestData:request initializeApi:requestsWillInitializeApi unresolvedDependencyOnRequestInternalId:&autoreleasingUnresolvedDependencyOnRequestInternalId decoratedDebugDescription:&decoratedDebugDescription];
MTRequestVerificationData *requestVerificationData = nil;
if (request.errorContext != nil) {
if (request.errorContext.pendingVerificationData != nil) {
if (request.errorContext.pendingVerificationData.isResolved) {
requestVerificationData = [[MTRequestVerificationData alloc] initWithNonce:request.errorContext.pendingVerificationData.nonce secret:request.errorContext.pendingVerificationData.secret];
}
}
}
NSData *decoratedRequestData = [self decorateRequestData:request initializeApi:requestsWillInitializeApi requestVerificationData:requestVerificationData unresolvedDependencyOnRequestInternalId:&autoreleasingUnresolvedDependencyOnRequestInternalId decoratedDebugDescription:&decoratedDebugDescription];
MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:decoratedRequestData metadata:request.metadata additionalDebugDescription:decoratedDebugDescription shortMetadata:request.shortMetadata messageId:messageId messageSeqNo:messageSeqNo];
outgoingMessage.needsQuickAck = request.acknowledgementReceived != nil;
@ -875,6 +921,34 @@
[_context updateAuthInfoForDatacenterWithId:mtProto.datacenterId authInfo:authInfo selector:authInfoSelector];
}];
restartRequest = true;
} else if (rpcError.errorCode == 400 && [rpcError.errorDescription rangeOfString:@"APNS_VERIFY_CHECK_"].location != NSNotFound) {
if (request.errorContext == nil) {
request.errorContext = [[MTRequestErrorContext alloc] init];
}
NSString *nonce = [rpcError.errorDescription substringFromIndex:[@"APNS_VERIFY_CHECK_" length]];
request.errorContext.pendingVerificationData = [[MTRequestPendingVerificationData alloc] initWithNonce:nonce];
__weak MTRequestMessageService *weakSelf = self;
MTQueue *queue = _queue;
id requestId = request.internalId;
request.errorContext.pendingVerificationData.disposable = [[_context performExternalRequestVerificationWithNonce:nonce] startWithNext:^(id result) {
[queue dispatchOnQueue:^{
__strong MTRequestMessageService *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
for (MTRequest *request in strongSelf->_requests) {
if (request.internalId == requestId) {
request.errorContext.pendingVerificationData.secret = result;
request.errorContext.pendingVerificationData.isResolved = true;
}
}
[strongSelf->_mtProto requestTransportTransaction];
}];
}];
restartRequest = true;
} else if (rpcError.errorCode == 406) {
if (_didReceiveSoftAuthResetError) {

View File

@ -434,13 +434,14 @@ public struct NetworkInitializationArguments {
public let voipMaxLayer: Int32
public let voipVersions: [CallSessionManagerImplementationVersion]
public let appData: Signal<Data?, NoError>
public let externalRequestVerificationStream: Signal<[String: String], NoError>
public let autolockDeadine: Signal<Int32?, NoError>
public let encryptionProvider: EncryptionProvider
public let deviceModelName:String?
public let useBetaFeatures: Bool
public let isICloudEnabled: Bool
public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, voipVersions: [CallSessionManagerImplementationVersion], appData: Signal<Data?, NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider, deviceModelName: String?, useBetaFeatures: Bool, isICloudEnabled: Bool) {
public init(apiId: Int32, apiHash: String, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, voipVersions: [CallSessionManagerImplementationVersion], appData: Signal<Data?, NoError>, externalRequestVerificationStream: Signal<[String: String], NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider, deviceModelName: String?, useBetaFeatures: Bool, isICloudEnabled: Bool) {
self.apiId = apiId
self.apiHash = apiHash
self.languagesCategory = languagesCategory
@ -448,6 +449,7 @@ public struct NetworkInitializationArguments {
self.voipMaxLayer = voipMaxLayer
self.voipVersions = voipVersions
self.appData = appData
self.externalRequestVerificationStream = externalRequestVerificationStream
self.autolockDeadine = autolockDeadine
self.encryptionProvider = encryptionProvider
self.deviceModelName = deviceModelName
@ -573,6 +575,25 @@ func initializedNetwork(accountId: AccountRecordId, arguments: NetworkInitializa
if !supplementary {
context.setDiscoverBackupAddressListSignal(MTBackupAddressSignals.fetchBackupIps(testingEnvironment, currentContext: context, additionalSource: wrappedAdditionalSource, phoneNumber: phoneNumber, mainDatacenterId: datacenterId))
let externalRequestVerificationStream = arguments.externalRequestVerificationStream
context.setExternalRequestVerification({ nonce in
return MTSignal(generator: { subscriber in
let disposable = (externalRequestVerificationStream
|> map { dict -> String? in
return dict[nonce]
}
|> filter { $0 != nil }
|> take(1)
|> timeout(15.0, queue: .mainQueue(), alternate: .single(nil))).start(next: { secret in
subscriber?.putNext(secret)
subscriber?.putCompletion()
})
return MTBlockDisposable(block: {
disposable.dispose()
})
})
})
}
/*#if DEBUG

View File

@ -385,6 +385,7 @@ public class ShareRootControllerImpl {
voipMaxLayer: 0,
voipVersions: [],
appData: .single(nil),
externalRequestVerificationStream: .never(),
autolockDeadine: .single(nil),
encryptionProvider: OpenSSLEncryptionProvider(),
deviceModelName: nil,

View File

@ -274,6 +274,15 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
}
private let firebaseSecretStream = Promise<[String: String]>([:])
private var firebaseRequestVerificationSecrets: [String: String] = [:] {
didSet {
if self.firebaseRequestVerificationSecrets != oldValue {
self.firebaseRequestVerificationSecretStream.set(.single(self.firebaseRequestVerificationSecrets))
}
}
}
private let firebaseRequestVerificationSecretStream = Promise<[String: String]>([:])
private var urlSessions: [URLSession] = []
private func urlSession(identifier: String) -> URLSession {
if let existingSession = self.urlSessions.first(where: { $0.configuration.identifier == identifier }) {
@ -491,7 +500,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
Logger.shared.log("data", "can't deserialize")
}
return data
}, autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: buildConfig.isICloudEnabled)
}, externalRequestVerificationStream: self.firebaseRequestVerificationSecretStream.get(), autolockDeadine: autolockDeadine, encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: !buildConfig.isAppStoreBuild, isICloudEnabled: buildConfig.isICloudEnabled)
guard let appGroupUrl = maybeAppGroupUrl else {
self.mainWindow?.presentNative(UIAlertController(title: nil, message: "Error 2", preferredStyle: .alert))
@ -1895,6 +1904,11 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
firebaseSecrets[receipt] = secret
self.firebaseSecrets = firebaseSecrets
}
if let nonce = firebaseDict["verify_nonce"] as? String, let secret = firebaseDict["verify_secret"] as? String {
var firebaseRequestVerificationSecrets = self.firebaseRequestVerificationSecrets
firebaseRequestVerificationSecrets[nonce] = secret
self.firebaseRequestVerificationSecrets = firebaseRequestVerificationSecrets
}
completionHandler(.newData)
return

View File

@ -140,7 +140,7 @@ public final class NotificationViewControllerImpl {
return nil
})
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, notificationController: nil, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: self.initializationData.useBetaFeatures, isICloudEnabled: false), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil)
sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, sharedContainerPath: self.initializationData.appGroupPath, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, notificationController: nil, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, apiHash: self.initializationData.apiHash, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(self.initializationData.bundleData), externalRequestVerificationStream: .never(), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider(), deviceModelName: nil, useBetaFeatures: self.initializationData.useBetaFeatures, isICloudEnabled: false), hasInAppPurchases: false, rootPath: rootPath, legacyBasePath: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), firebaseSecretStream: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }, appDelegate: nil)
presentationDataPromise.set(sharedAccountContext!.presentationData)
}