mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Wallet improvements
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
#import <MtProtoKitDynamic/MtProtoKitDynamic.h>
|
||||
#endif
|
||||
|
||||
static NSString *telegramApplicationSecretKey = @"telegramApplicationSecretKey_v3";
|
||||
|
||||
static uint32_t funcSwap32(uint32_t input)
|
||||
{
|
||||
return OSSwapBigToHostInt32(input);
|
||||
@@ -238,7 +240,7 @@ API_AVAILABLE(ios(10))
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)encrypt:(NSData * _Nonnull)data;
|
||||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data;
|
||||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data cancelled:(bool *)cancelled;
|
||||
|
||||
@end
|
||||
|
||||
@@ -258,6 +260,11 @@ API_AVAILABLE(ios(10))
|
||||
CFRelease(_publicKey);
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)getPublicKey {
|
||||
NSData *result = CFBridgingRelease(SecKeyCopyExternalRepresentation(_publicKey, nil));
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)encrypt:(NSData * _Nonnull)data {
|
||||
if (data.length % 16 != 0) {
|
||||
return nil;
|
||||
@@ -274,12 +281,17 @@ API_AVAILABLE(ios(10))
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data {
|
||||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data cancelled:(bool *)cancelled {
|
||||
CFErrorRef error = NULL;
|
||||
NSData *plainText = (NSData *)CFBridgingRelease(SecKeyCreateDecryptedData(_privateKey, kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM, (__bridge CFDataRef)data, &error));
|
||||
|
||||
if (!plainText) {
|
||||
__unused NSError *err = CFBridgingRelease(error);
|
||||
if (err.code == -2) {
|
||||
if (cancelled) {
|
||||
*cancelled = true;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -431,22 +443,29 @@ API_AVAILABLE(ios(10))
|
||||
return bundleSeedID;
|
||||
}
|
||||
|
||||
+ (LocalPrivateKey * _Nullable)getLocalPrivateKey:(NSString * _Nonnull)baseAppBundleId API_AVAILABLE(ios(10)) {
|
||||
+ (NSString * _Nonnull)applicationSecretTag:(bool)isCheckKey {
|
||||
if (isCheckKey) {
|
||||
return [[telegramApplicationSecretKey stringByAppendingString:@"_check"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
} else {
|
||||
return [telegramApplicationSecretKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
+ (LocalPrivateKey * _Nullable)getApplicationSecretKey:(NSString * _Nonnull)baseAppBundleId isCheckKey:(bool)isCheckKey API_AVAILABLE(ios(10)) {
|
||||
NSString *bundleSeedId = [self bundleSeedId];
|
||||
if (bundleSeedId == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *applicationTag = [self applicationSecretTag:isCheckKey];
|
||||
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
|
||||
|
||||
NSData *applicationTag = [@"telegramLocalKey" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSDictionary *query = @{
|
||||
(id)kSecClass: (id)kSecClassKey,
|
||||
(id)kSecAttrApplicationTag: applicationTag,
|
||||
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
|
||||
(id)kSecAttrAccessGroup: (id)accessGroup,
|
||||
(id)kSecReturnRef: @YES,
|
||||
(id)kSecReturnRef: @YES
|
||||
};
|
||||
SecKeyRef privateKey = NULL;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);
|
||||
@@ -474,13 +493,13 @@ API_AVAILABLE(ios(10))
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (bool)removeLocalPrivateKey:(NSString * _Nonnull)baseAppBundleId API_AVAILABLE(ios(10)) {
|
||||
+ (bool)removeApplicationSecretKey:(NSString * _Nonnull)baseAppBundleId isCheckKey:(bool)isCheckKey API_AVAILABLE(ios(10)) {
|
||||
NSString *bundleSeedId = [self bundleSeedId];
|
||||
if (bundleSeedId == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *applicationTag = [@"telegramLocalKey" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *applicationTag = [self applicationSecretTag:isCheckKey];
|
||||
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
|
||||
|
||||
NSDictionary *query = @{
|
||||
@@ -496,142 +515,21 @@ API_AVAILABLE(ios(10))
|
||||
return true;
|
||||
}
|
||||
|
||||
+ (LocalPrivateKey * _Nullable)addLocalPrivateKey:(NSString * _Nonnull)baseAppBundleId API_AVAILABLE(ios(10)) {
|
||||
+ (LocalPrivateKey * _Nullable)addApplicationSecretKey:(NSString * _Nonnull)baseAppBundleId isCheckKey:(bool)isCheckKey API_AVAILABLE(ios(10)) {
|
||||
NSString *bundleSeedId = [self bundleSeedId];
|
||||
if (bundleSeedId == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *applicationTag = [@"telegramLocalKey" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *applicationTag = [self applicationSecretTag:isCheckKey];
|
||||
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
|
||||
|
||||
SecAccessControlRef access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysThisDeviceOnly, kSecAccessControlPrivateKeyUsage, NULL);
|
||||
NSDictionary *attributes = @{
|
||||
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
|
||||
(id)kSecAttrKeySizeInBits: @256,
|
||||
(id)kSecAttrTokenID: (id)kSecAttrTokenIDSecureEnclave,
|
||||
(id)kSecPrivateKeyAttrs: @{
|
||||
(id)kSecAttrIsPermanent: @YES,
|
||||
(id)kSecAttrApplicationTag: applicationTag,
|
||||
(id)kSecAttrAccessControl: (__bridge id)access,
|
||||
(id)kSecAttrAccessGroup: (id)accessGroup,
|
||||
},
|
||||
};
|
||||
|
||||
CFErrorRef error = NULL;
|
||||
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);
|
||||
if (!privateKey) {
|
||||
if (access) {
|
||||
CFRelease(access);
|
||||
}
|
||||
|
||||
__unused NSError *err = CFBridgingRelease(error);
|
||||
return nil;
|
||||
SecAccessControlRef access;
|
||||
if (isCheckKey) {
|
||||
access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlPrivateKeyUsage, NULL);
|
||||
} else {
|
||||
access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence | kSecAccessControlPrivateKeyUsage, NULL);
|
||||
}
|
||||
|
||||
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
|
||||
if (!publicKey) {
|
||||
if (privateKey) {
|
||||
CFRelease(privateKey);
|
||||
}
|
||||
if (access) {
|
||||
CFRelease(access);
|
||||
}
|
||||
|
||||
__unused NSError *err = CFBridgingRelease(error);
|
||||
return nil;
|
||||
}
|
||||
|
||||
LocalPrivateKey *result = [[LocalPrivateKey alloc] initWithPrivateKey:privateKey publicKey:publicKey];
|
||||
|
||||
if (publicKey) {
|
||||
CFRelease(publicKey);
|
||||
}
|
||||
if (privateKey) {
|
||||
CFRelease(privateKey);
|
||||
}
|
||||
if (access) {
|
||||
CFRelease(access);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (LocalPrivateKey * _Nullable)getApplicationSecretKey:(NSString * _Nonnull)baseAppBundleId API_AVAILABLE(ios(10)) {
|
||||
NSString *bundleSeedId = [self bundleSeedId];
|
||||
if (bundleSeedId == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
|
||||
|
||||
NSData *applicationTag = [@"telegramApplicationSecretKey" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSDictionary *query = @{
|
||||
(id)kSecClass: (id)kSecClassKey,
|
||||
(id)kSecAttrApplicationTag: applicationTag,
|
||||
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
|
||||
(id)kSecAttrAccessGroup: (id)accessGroup,
|
||||
(id)kSecReturnRef: @YES,
|
||||
};
|
||||
SecKeyRef privateKey = NULL;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&privateKey);
|
||||
if (status != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
|
||||
if (!publicKey) {
|
||||
if (privateKey) {
|
||||
CFRelease(privateKey);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
LocalPrivateKey *result = [[LocalPrivateKey alloc] initWithPrivateKey:privateKey publicKey:publicKey];
|
||||
|
||||
if (publicKey) {
|
||||
CFRelease(publicKey);
|
||||
}
|
||||
if (privateKey) {
|
||||
CFRelease(privateKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (bool)removeApplicationSecretKey:(NSString * _Nonnull)baseAppBundleId API_AVAILABLE(ios(10)) {
|
||||
NSString *bundleSeedId = [self bundleSeedId];
|
||||
if (bundleSeedId == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *applicationTag = [@"telegramApplicationSecretKey" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
|
||||
|
||||
NSDictionary *query = @{
|
||||
(id)kSecClass: (id)kSecClassKey,
|
||||
(id)kSecAttrApplicationTag: applicationTag,
|
||||
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
|
||||
(id)kSecAttrAccessGroup: (id)accessGroup
|
||||
};
|
||||
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
|
||||
if (status != errSecSuccess) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+ (LocalPrivateKey * _Nullable)addApplicationSecretKey:(NSString * _Nonnull)baseAppBundleId API_AVAILABLE(ios(10)) {
|
||||
NSString *bundleSeedId = [self bundleSeedId];
|
||||
if (bundleSeedId == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *applicationTag = [@"telegramApplicationSecretKey" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *accessGroup = [bundleSeedId stringByAppendingFormat:@".%@", baseAppBundleId];
|
||||
|
||||
SecAccessControlRef access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysThisDeviceOnly, kSecAccessControlUserPresence | kSecAccessControlPrivateKeyUsage, NULL);
|
||||
NSDictionary *attributes = @{
|
||||
(id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
|
||||
(id)kSecAttrKeySizeInBits: @256,
|
||||
@@ -738,24 +636,67 @@ API_AVAILABLE(ios(10))
|
||||
return [[DeviceSpecificEncryptionParameters alloc] initWithKey:key salt:salt];
|
||||
}
|
||||
|
||||
+ (void)encryptApplicationSecret:(NSData * _Nonnull)secret baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
LocalPrivateKey *privateKey = [self getApplicationSecretKey:baseAppBundleId];
|
||||
if (privateKey == nil) {
|
||||
privateKey = [self addApplicationSecretKey:baseAppBundleId];
|
||||
+ (dispatch_queue_t)encryptionQueue {
|
||||
static dispatch_queue_t instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = dispatch_queue_create("encryptionQueue", 0);
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (void)getHardwareEncryptionAvailableWithBaseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion {
|
||||
dispatch_async([self encryptionQueue], ^{
|
||||
LocalPrivateKey *checkKey = [self getApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
if (checkKey != nil) {
|
||||
NSData *sampleData = [checkKey encrypt:[NSData data]];
|
||||
if (sampleData == nil) {
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
} else {
|
||||
NSData *decryptedData = [checkKey decrypt:sampleData cancelled: nil];
|
||||
if (decryptedData == nil) {
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
}
|
||||
|
||||
LocalPrivateKey *privateKey = [self getApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
if (privateKey == nil) {
|
||||
completion(nil);
|
||||
return;
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
privateKey = [self addApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
privateKey = [self addApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
}
|
||||
NSData *result = [privateKey encrypt:secret];
|
||||
completion(result);
|
||||
completion([privateKey getPublicKey]);
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)decryptApplicationSecret:(NSData * _Nonnull)secret baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
LocalPrivateKey *privateKey = [self getApplicationSecretKey:baseAppBundleId];
|
||||
+ (void)encryptApplicationSecret:(NSData * _Nonnull)secret baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable, NSData * _Nullable))completion {
|
||||
dispatch_async([self encryptionQueue], ^{
|
||||
LocalPrivateKey *privateKey = [self getApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
if (privateKey == nil) {
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
[self removeApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
privateKey = [self addApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
privateKey = [self addApplicationSecretKey:baseAppBundleId isCheckKey:true];
|
||||
}
|
||||
if (privateKey == nil) {
|
||||
completion(nil, nil);
|
||||
return;
|
||||
}
|
||||
NSData *result = [privateKey encrypt:secret];
|
||||
completion(result, [privateKey getPublicKey]);
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)decryptApplicationSecret:(NSData * _Nonnull)secret publicKey:(NSData * _Nonnull)publicKey baseAppBundleId:(NSString * _Nonnull)baseAppBundleId completion:(void (^)(NSData * _Nullable))completion {
|
||||
dispatch_async([self encryptionQueue], ^{
|
||||
LocalPrivateKey *privateKey = [self getApplicationSecretKey:baseAppBundleId isCheckKey:false];
|
||||
if (privateKey == nil) {
|
||||
completion(nil);
|
||||
return;
|
||||
@@ -764,7 +705,16 @@ API_AVAILABLE(ios(10))
|
||||
completion(nil);
|
||||
return;
|
||||
}
|
||||
NSData *result = [privateKey decrypt:secret];
|
||||
NSData *currentPublicKey = [privateKey getPublicKey];
|
||||
if (currentPublicKey == nil) {
|
||||
completion(nil);
|
||||
return;
|
||||
}
|
||||
if (![publicKey isEqualToData:currentPublicKey]) {
|
||||
completion(nil);
|
||||
return;
|
||||
}
|
||||
NSData *result = [privateKey decrypt:secret cancelled:nil];
|
||||
completion(result);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user