Wallet improvements

This commit is contained in:
Peter
2019-09-25 00:57:46 +04:00
parent ee9815cff8
commit c64c16b8ee
39 changed files with 4202 additions and 3733 deletions

View File

@@ -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);
});
}