Updated keychain

This commit is contained in:
Peter 2015-07-27 15:01:26 +03:00
parent 469b3931e6
commit 6252ca75f7
14 changed files with 794 additions and 378 deletions

10
MTFileBasedKeychain.h Normal file
View File

@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
#import <MTProtoKit/MTKeychain.h>
@interface MTFileBasedKeychain : NSObject <MTKeychain>
+ (instancetype)unencryptedKeychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath;
+ (instancetype)keychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath;
@end

362
MTFileBasedKeychain.m Normal file
View File

@ -0,0 +1,362 @@
#import "MTFileBasedKeychain.h"
#import <MTProtoKit/MTLogging.h>
#import <pthread.h>
#define TG_SYNCHRONIZED_DEFINE(lock) pthread_mutex_t _TG_SYNCHRONIZED_##lock
#define TG_SYNCHRONIZED_INIT(lock) pthread_mutex_init(&_TG_SYNCHRONIZED_##lock, NULL)
#define TG_SYNCHRONIZED_BEGIN(lock) pthread_mutex_lock(&_TG_SYNCHRONIZED_##lock);
#define TG_SYNCHRONIZED_END(lock) pthread_mutex_unlock(&_TG_SYNCHRONIZED_##lock);
#import <CommonCrypto/CommonCrypto.h>
#import <MTProtoKit/MTEncryption.h>
static TG_SYNCHRONIZED_DEFINE(_keychains) = PTHREAD_MUTEX_INITIALIZER;
static NSMutableDictionary *keychains()
{
static NSMutableDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
TG_SYNCHRONIZED_INIT(_keychains);
dict = [[NSMutableDictionary alloc] init];
});
return dict;
}
@interface MTFileBasedKeychain ()
{
NSString *_name;
bool _encrypted;
NSData *_aesKey;
NSData *_aesIv;
NSString *_documentsPath;
TG_SYNCHRONIZED_DEFINE(_dictByGroup);
NSMutableDictionary *_dictByGroup;
}
@end
@implementation MTFileBasedKeychain
+ (instancetype)unencryptedKeychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath
{
if (name == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_keychains);
MTFileBasedKeychain *keychain = [keychains() objectForKey:name];
if (keychain == nil)
{
keychain = [[MTFileBasedKeychain alloc] initWithName:name documentsPath:documentsPath encrypted:false];
[keychains() setObject:keychain forKey:name];
}
TG_SYNCHRONIZED_END(_keychains);
return keychain;
}
+ (instancetype)keychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath
{
if (name == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_keychains);
MTFileBasedKeychain *keychain = [keychains() objectForKey:name];
if (keychain == nil)
{
keychain = [[MTFileBasedKeychain alloc] initWithName:name documentsPath:documentsPath encrypted:true];
[keychains() setObject:keychain forKey:name];
}
TG_SYNCHRONIZED_END(_keychains);
return keychain;
}
- (instancetype)initWithName:(NSString *)name documentsPath:(NSString *)documentsPath encrypted:(bool)encrypted
{
self = [super init];
if (self != nil)
{
TG_SYNCHRONIZED_INIT(_dictByGroup);
_dictByGroup = [[NSMutableDictionary alloc] init];
_name = name;
_documentsPath = documentsPath;
_encrypted = encrypted;
if (name != nil)
{
if (_encrypted)
{
NSMutableDictionary *keychainReadQuery = [[NSMutableDictionary alloc] initWithDictionary:@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
#endif
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
}];
bool upgradeEncryption = false;
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainReadQuery, (CFTypeRef *)&keyData) == noErr && keyData != NULL)
{
NSData *data = (__bridge_transfer NSData *)keyData;
if (data.length == 64)
{
_aesKey = [data subdataWithRange:NSMakeRange(0, 32)];
_aesIv = [data subdataWithRange:NSMakeRange(32, 32)];
}
}
else
{
keychainReadQuery[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainReadQuery, (CFTypeRef *)&keyData) == noErr && keyData != NULL)
{
NSData *data = (__bridge_transfer NSData *)keyData;
if (data.length == 64)
{
upgradeEncryption = true;
_aesKey = [data subdataWithRange:NSMakeRange(0, 32)];
_aesIv = [data subdataWithRange:NSMakeRange(32, 32)];
}
}
}
bool storeKey = upgradeEncryption || _aesKey == nil || _aesIv == nil;
if (_aesKey == nil || _aesIv == nil)
{
uint8_t buf[32];
SecRandomCopyBytes(kSecRandomDefault, 32, buf);
_aesKey = [[NSData alloc] initWithBytes:buf length:32];
SecRandomCopyBytes(kSecRandomDefault, 32, buf);
_aesIv = [[NSData alloc] initWithBytes:buf length:32];
}
NSMutableData *newKeyData = [[NSMutableData alloc] init];
[newKeyData appendData:_aesKey];
[newKeyData appendData:_aesIv];
if (storeKey)
{
SecItemDelete((__bridge CFDictionaryRef)@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: upgradeEncryption ? (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly
#endif
});
SecItemAdd((__bridge CFDictionaryRef)@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
#endif
(__bridge id)kSecValueData: newKeyData
}, NULL);
}
}
}
}
return self;
}
- (NSString *)filePathForName:(NSString *)name group:(NSString *)group
{
static NSString *dataDirectory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
#if TARGET_OS_IPHONE
dataDirectory = [_documentsPath stringByAppendingPathComponent:@"mtkeychain"];
#elif TARGET_OS_MAC
NSString *applicationSupportPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
NSString *applicationName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
dataDirectory = [[applicationSupportPath stringByAppendingPathComponent:applicationName] stringByAppendingPathComponent:@"mtkeychain"];
#else
# error "Unsupported OS"
#endif
__autoreleasing NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:dataDirectory withIntermediateDirectories:true attributes:nil error:&error];
if (error != nil)
MTLog(@"[MTKeychain error creating keychain directory: %@]", error);
});
return [dataDirectory stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%@_%@.bin", name, group]];
}
- (void)_loadKeychainIfNeeded:(NSString *)group
{
if (_dictByGroup[group] == nil)
{
if (_name != nil)
{
NSData *data = [[NSData alloc] initWithContentsOfFile:[self filePathForName:_name group:group]];
if (data != nil && data.length >= 4)
{
uint32_t length = 0;
[data getBytes:&length range:NSMakeRange(0, 4)];
uint32_t paddedLength = length;
while (paddedLength % 16 != 0)
{
paddedLength++;
}
if (data.length == 4 + paddedLength || data.length == 4 + paddedLength + 4)
{
NSMutableData *decryptedData = [[NSMutableData alloc] init];
[decryptedData appendData:[data subdataWithRange:NSMakeRange(4, paddedLength)]];
if (_encrypted)
MTAesDecryptInplace(decryptedData, _aesKey, _aesIv);
[decryptedData setLength:length];
bool hashVerified = true;
if (data.length == 4 + paddedLength + 4)
{
int32_t hash = 0;
[data getBytes:&hash range:NSMakeRange(4 + paddedLength, 4)];
int32_t decryptedHash = MTMurMurHash32(decryptedData.bytes, (int)decryptedData.length);
if (hash != decryptedHash)
{
MTLog(@"[MTKeychain invalid decrypted hash]");
hashVerified = false;
}
}
if (hashVerified)
{
@try
{
id object = [NSKeyedUnarchiver unarchiveObjectWithData:decryptedData];
if ([object respondsToSelector:@selector(objectForKey:)] && [object respondsToSelector:@selector(setObject:forKey:)])
_dictByGroup[group] = object;
else
MTLog(@"[MTKeychain invalid root object %@]", object);
}
@catch (NSException *e)
{
MTLog(@"[MTKeychain error parsing keychain: %@]", e);
}
}
}
else
MTLog(@"[MTKeychain error loading keychain: expected data length %d, got %d]", 4 + (int)paddedLength, (int)data.length);
}
}
if (_dictByGroup[group] == nil)
_dictByGroup[group] = [[NSMutableDictionary alloc] init];
}
}
- (void)_storeKeychain:(NSString *)group
{
if (_dictByGroup[group] != nil && _name != nil)
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_dictByGroup[group]];
if (data != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] initWithData:data];
int32_t hash = MTMurMurHash32(encryptedData.bytes, (int)encryptedData.length);
while (encryptedData.length % 16 != 0)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[encryptedData appendBytes:&random length:1];
}
if (_encrypted)
MTAesEncryptInplace(encryptedData, _aesKey, _aesIv);
uint32_t length = (uint32_t)data.length;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&length length:4];
[encryptedData appendBytes:&hash length:4];
NSString *filePath = [self filePathForName:_name group:group];
if (![encryptedData writeToFile:filePath atomically:true])
MTLog(@"[MTKeychain error writing keychain to file]");
else
{
#if TARGET_OS_IPHONE
__autoreleasing NSError *error = nil;
[[NSURL fileURLWithPath:filePath] setResourceValue:[NSNumber numberWithBool:true] forKey:NSURLIsExcludedFromBackupKey error:&error];
if (error != nil)
MTLog(@"[MTKeychain error setting \"exclude from backup\" flag]");
#endif
}
}
else
MTLog(@"[MTKeychain error serializing keychain]");
}
}
- (void)setObject:(id)object forKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (object == nil || aKey == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
_dictByGroup[group][aKey] = object;
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (id)objectForKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (aKey == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
id result = _dictByGroup[group][aKey];
TG_SYNCHRONIZED_END(_dictByGroup);
return result;
}
- (void)removeObjectForKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (aKey == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
[_dictByGroup[group] removeObjectForKey:aKey];
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (void)dropGroup:(NSString *)group
{
if (group == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
_dictByGroup[group] = [[NSMutableDictionary alloc] init];
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
@end

View File

@ -14,7 +14,7 @@
@protocol MTSerialization;
@class MTContext;
@class MTTransportScheme;
@class MTKeychain;
@protocol MTKeychain;
@class MTSessionInfo;
@class MTApiEnvironment;
@ -38,7 +38,7 @@
@interface MTContext : NSObject
@property (nonatomic, strong) MTKeychain *keychain;
@property (nonatomic, strong) id<MTKeychain> keychain;
@property (nonatomic, strong, readonly) id<MTSerialization> serialization;
@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment;

View File

@ -194,7 +194,7 @@
[[MTContext contextQueue] dispatchOnQueue:block];
}
- (void)setKeychain:(MTKeychain *)keychain
- (void)setKeychain:(id<MTKeychain>)keychain
{
[[MTContext contextQueue] dispatchOnQueue:^
{

View File

@ -32,11 +32,11 @@ NSData *MTRsaEncrypt(NSString *publicKey, NSData *data);
NSData *MTExp(NSData *base, NSData *exp, NSData *modulus);
bool MTFactorize(uint64_t what, uint64_t *resA, uint64_t *resB);
@class MTKeychain;
@protocol MTKeychain;
bool MTCheckIsSafeG(unsigned int g);
bool MTCheckIsSafePrime(NSData *numberBytes, MTKeychain *keychain);
bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain);
bool MTCheckIsSafeGAOrB(NSData *gAOrB, NSData *p);
bool MTCheckMod(NSData *numberBytes, unsigned int g, MTKeychain *keychain);
bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain);
#ifdef __cplusplus
}

View File

@ -401,7 +401,7 @@ static NSString *hexStringFromData(NSData *data)
return string;
}
bool MTCheckIsSafePrime(NSData *numberBytes, MTKeychain *keychain)
bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain)
{
NSString *primeKey = [[NSString alloc] initWithFormat:@"isPrimeSafe_%@", hexStringFromData(numberBytes)];
@ -507,7 +507,7 @@ bool MTCheckIsSafeGAOrB(NSData *gAOrB, NSData *p)
return result;
}
bool MTCheckMod(NSData *numberBytes, unsigned int g, MTKeychain *keychain)
bool MTCheckMod(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"];

View File

@ -8,14 +8,11 @@
#import <Foundation/Foundation.h>
@interface MTKeychain : NSObject
@protocol MTKeychain <NSObject>
+ (instancetype)unencryptedKeychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath;
+ (instancetype)keychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath;
- (void)setObject:(id)object forKey:(id<NSCopying>)aKey group:(NSString *)group;
- (id)objectForKey:(id<NSCopying>)aKey group:(NSString *)group;
- (void)removeObjectForKey:(id<NSCopying>)aKey group:(NSString *)group;
- (void)setObject:(id)object forKey:(NSString *)aKey group:(NSString *)group;
- (id)objectForKey:(NSString *)aKey group:(NSString *)group;
- (void)removeObjectForKey:(NSString *)aKey group:(NSString *)group;
- (void)dropGroup:(NSString *)group;

View File

@ -7,363 +7,3 @@
*/
#import <MTProtoKit/MTKeychain.h>
#import <MTProtoKit/MTLogging.h>
#import <pthread.h>
#define TG_SYNCHRONIZED_DEFINE(lock) pthread_mutex_t _TG_SYNCHRONIZED_##lock
#define TG_SYNCHRONIZED_INIT(lock) pthread_mutex_init(&_TG_SYNCHRONIZED_##lock, NULL)
#define TG_SYNCHRONIZED_BEGIN(lock) pthread_mutex_lock(&_TG_SYNCHRONIZED_##lock);
#define TG_SYNCHRONIZED_END(lock) pthread_mutex_unlock(&_TG_SYNCHRONIZED_##lock);
#import <CommonCrypto/CommonCrypto.h>
#import <MTProtoKit/MTEncryption.h>
static TG_SYNCHRONIZED_DEFINE(_keychains) = PTHREAD_MUTEX_INITIALIZER;
static NSMutableDictionary *keychains()
{
static NSMutableDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
TG_SYNCHRONIZED_INIT(_keychains);
dict = [[NSMutableDictionary alloc] init];
});
return dict;
}
@interface MTKeychain ()
{
NSString *_name;
bool _encrypted;
NSData *_aesKey;
NSData *_aesIv;
NSString *_documentsPath;
TG_SYNCHRONIZED_DEFINE(_dictByGroup);
NSMutableDictionary *_dictByGroup;
}
@end
@implementation MTKeychain
+ (instancetype)unencryptedKeychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath
{
if (name == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_keychains);
MTKeychain *keychain = [keychains() objectForKey:name];
if (keychain == nil)
{
keychain = [[MTKeychain alloc] initWithName:name documentsPath:documentsPath encrypted:false];
[keychains() setObject:keychain forKey:name];
}
TG_SYNCHRONIZED_END(_keychains);
return keychain;
}
+ (instancetype)keychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath
{
if (name == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_keychains);
MTKeychain *keychain = [keychains() objectForKey:name];
if (keychain == nil)
{
keychain = [[MTKeychain alloc] initWithName:name documentsPath:documentsPath encrypted:true];
[keychains() setObject:keychain forKey:name];
}
TG_SYNCHRONIZED_END(_keychains);
return keychain;
}
- (instancetype)initWithName:(NSString *)name documentsPath:(NSString *)documentsPath encrypted:(bool)encrypted
{
self = [super init];
if (self != nil)
{
TG_SYNCHRONIZED_INIT(_dictByGroup);
_dictByGroup = [[NSMutableDictionary alloc] init];
_name = name;
_documentsPath = documentsPath;
_encrypted = encrypted;
if (name != nil)
{
if (_encrypted)
{
NSMutableDictionary *keychainReadQuery = [[NSMutableDictionary alloc] initWithDictionary:@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
#endif
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
}];
bool upgradeEncryption = false;
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainReadQuery, (CFTypeRef *)&keyData) == noErr && keyData != NULL)
{
NSData *data = (__bridge_transfer NSData *)keyData;
if (data.length == 64)
{
_aesKey = [data subdataWithRange:NSMakeRange(0, 32)];
_aesIv = [data subdataWithRange:NSMakeRange(32, 32)];
}
}
else
{
keychainReadQuery[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainReadQuery, (CFTypeRef *)&keyData) == noErr && keyData != NULL)
{
NSData *data = (__bridge_transfer NSData *)keyData;
if (data.length == 64)
{
upgradeEncryption = true;
_aesKey = [data subdataWithRange:NSMakeRange(0, 32)];
_aesIv = [data subdataWithRange:NSMakeRange(32, 32)];
}
}
}
bool storeKey = upgradeEncryption || _aesKey == nil || _aesIv == nil;
if (_aesKey == nil || _aesIv == nil)
{
uint8_t buf[32];
SecRandomCopyBytes(kSecRandomDefault, 32, buf);
_aesKey = [[NSData alloc] initWithBytes:buf length:32];
SecRandomCopyBytes(kSecRandomDefault, 32, buf);
_aesIv = [[NSData alloc] initWithBytes:buf length:32];
}
NSMutableData *newKeyData = [[NSMutableData alloc] init];
[newKeyData appendData:_aesKey];
[newKeyData appendData:_aesIv];
if (storeKey)
{
SecItemDelete((__bridge CFDictionaryRef)@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: upgradeEncryption ? (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly
#endif
});
SecItemAdd((__bridge CFDictionaryRef)@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
#endif
(__bridge id)kSecValueData: newKeyData
}, NULL);
}
}
}
}
return self;
}
- (NSString *)filePathForName:(NSString *)name group:(NSString *)group
{
static NSString *dataDirectory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
#if TARGET_OS_IPHONE
dataDirectory = [_documentsPath stringByAppendingPathComponent:@"mtkeychain"];
#elif TARGET_OS_MAC
NSString *applicationSupportPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
NSString *applicationName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
dataDirectory = [[applicationSupportPath stringByAppendingPathComponent:applicationName] stringByAppendingPathComponent:@"mtkeychain"];
#else
# error "Unsupported OS"
#endif
__autoreleasing NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:dataDirectory withIntermediateDirectories:true attributes:nil error:&error];
if (error != nil)
MTLog(@"[MTKeychain error creating keychain directory: %@]", error);
});
return [dataDirectory stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%@_%@.bin", name, group]];
}
- (void)_loadKeychainIfNeeded:(NSString *)group
{
if (_dictByGroup[group] == nil)
{
if (_name != nil)
{
NSData *data = [[NSData alloc] initWithContentsOfFile:[self filePathForName:_name group:group]];
if (data != nil && data.length >= 4)
{
uint32_t length = 0;
[data getBytes:&length range:NSMakeRange(0, 4)];
uint32_t paddedLength = length;
while (paddedLength % 16 != 0)
{
paddedLength++;
}
if (data.length == 4 + paddedLength || data.length == 4 + paddedLength + 4)
{
NSMutableData *decryptedData = [[NSMutableData alloc] init];
[decryptedData appendData:[data subdataWithRange:NSMakeRange(4, paddedLength)]];
if (_encrypted)
MTAesDecryptInplace(decryptedData, _aesKey, _aesIv);
[decryptedData setLength:length];
bool hashVerified = true;
if (data.length == 4 + paddedLength + 4)
{
int32_t hash = 0;
[data getBytes:&hash range:NSMakeRange(4 + paddedLength, 4)];
int32_t decryptedHash = MTMurMurHash32(decryptedData.bytes, (int)decryptedData.length);
if (hash != decryptedHash)
{
MTLog(@"[MTKeychain invalid decrypted hash]");
hashVerified = false;
}
}
if (hashVerified)
{
@try
{
id object = [NSKeyedUnarchiver unarchiveObjectWithData:decryptedData];
if ([object respondsToSelector:@selector(objectForKey:)] && [object respondsToSelector:@selector(setObject:forKey:)])
_dictByGroup[group] = object;
else
MTLog(@"[MTKeychain invalid root object %@]", object);
}
@catch (NSException *e)
{
MTLog(@"[MTKeychain error parsing keychain: %@]", e);
}
}
}
else
MTLog(@"[MTKeychain error loading keychain: expected data length %d, got %d]", 4 + (int)paddedLength, (int)data.length);
}
}
if (_dictByGroup[group] == nil)
_dictByGroup[group] = [[NSMutableDictionary alloc] init];
}
}
- (void)_storeKeychain:(NSString *)group
{
if (_dictByGroup[group] != nil && _name != nil)
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_dictByGroup[group]];
if (data != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] initWithData:data];
int32_t hash = MTMurMurHash32(encryptedData.bytes, (int)encryptedData.length);
while (encryptedData.length % 16 != 0)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[encryptedData appendBytes:&random length:1];
}
if (_encrypted)
MTAesEncryptInplace(encryptedData, _aesKey, _aesIv);
uint32_t length = (uint32_t)data.length;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&length length:4];
[encryptedData appendBytes:&hash length:4];
NSString *filePath = [self filePathForName:_name group:group];
if (![encryptedData writeToFile:filePath atomically:true])
MTLog(@"[MTKeychain error writing keychain to file]");
else
{
#if TARGET_OS_IPHONE
__autoreleasing NSError *error = nil;
[[NSURL fileURLWithPath:filePath] setResourceValue:[NSNumber numberWithBool:true] forKey:NSURLIsExcludedFromBackupKey error:&error];
if (error != nil)
MTLog(@"[MTKeychain error setting \"exclude from backup\" flag]");
#endif
}
}
else
MTLog(@"[MTKeychain error serializing keychain]");
}
}
- (void)setObject:(id)object forKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (object == nil || aKey == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
_dictByGroup[group][aKey] = object;
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (id)objectForKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (aKey == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
id result = _dictByGroup[group][aKey];
TG_SYNCHRONIZED_END(_dictByGroup);
return result;
}
- (void)removeObjectForKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (aKey == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
[_dictByGroup[group] removeObjectForKey:aKey];
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (void)dropGroup:(NSString *)group
{
if (group == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
_dictByGroup[group] = [[NSMutableDictionary alloc] init];
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
@end

View File

@ -21,3 +21,10 @@ FOUNDATION_EXPORT const unsigned char MtProtoKitVersionString[];
#import <MTProtoKit/MTProto.h>
#import <MTProtoKit/MTRequestMessageService.h>
#import <MTProtoKit/MTRequest.h>
#import <MTProtoKit/MTKeychain.h>
#import <MTProtoKit/MTSerialization.h>
#import <MTProtoKit/MTDatacenterAddress.h>
#import <MTProtoKit/MTDatacenterAddressSet.h>
#import <MTProtoKit/MTApiEnvironment.h>
#import <MTProtoKit/MTEncryption.h>
#import <MTProtoKit/MTIncomingMessage.h>

View File

@ -12,6 +12,8 @@
D0580AC31B0F3E9C00E8235B /* MTDiscoverConnectionSignals.m in Sources */ = {isa = PBXBuildFile; fileRef = D0580AC11B0F3E9C00E8235B /* MTDiscoverConnectionSignals.m */; };
D079AB9C1AF39B8000076F59 /* MtProtoKitMac.h in Headers */ = {isa = PBXBuildFile; fileRef = D079AB9B1AF39B8000076F59 /* MtProtoKitMac.h */; settings = {ATTRIBUTES = (Public, ); }; };
D079ABB01AF39BA400076F59 /* MTProtoKit.h in Headers */ = {isa = PBXBuildFile; fileRef = D063A31D18B158AE00C65116 /* MTProtoKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
D09A59581B582EFF00FC3724 /* MTFileBasedKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = D09A59561B582EFF00FC3724 /* MTFileBasedKeychain.h */; settings = {ATTRIBUTES = (Public, ); }; };
D09A59591B582EFF00FC3724 /* MTFileBasedKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = D09A59571B582EFF00FC3724 /* MTFileBasedKeychain.m */; };
D0CB05FC1ADC4483005E298F /* MtProtoKit.h in Headers */ = {isa = PBXBuildFile; fileRef = D0CB05FB1ADC4483005E298F /* MtProtoKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0CB06101ADC44B7005E298F /* MTTime.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DBD23418B2DA1E00631ADC /* MTTime.m */; };
D0CB06111ADC44B7005E298F /* MTTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = D05A84D818AFE81C007F1076 /* MTTimer.m */; };
@ -119,7 +121,7 @@
D0D1A03D1ADD983C007D9ED6 /* MTDestroySessionResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A00B1ADD983C007D9ED6 /* MTDestroySessionResponseMessage.m */; };
D0D1A03E1ADD983C007D9ED6 /* MTDropRpcResultMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A00C1ADD983C007D9ED6 /* MTDropRpcResultMessage.h */; };
D0D1A03F1ADD983C007D9ED6 /* MTDropRpcResultMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A00D1ADD983C007D9ED6 /* MTDropRpcResultMessage.m */; };
D0D1A0401ADD983C007D9ED6 /* MTExportedAuthorizationData.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A00E1ADD983C007D9ED6 /* MTExportedAuthorizationData.h */; };
D0D1A0401ADD983C007D9ED6 /* MTExportedAuthorizationData.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A00E1ADD983C007D9ED6 /* MTExportedAuthorizationData.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0D1A0411ADD983C007D9ED6 /* MTExportedAuthorizationData.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A00F1ADD983C007D9ED6 /* MTExportedAuthorizationData.m */; };
D0D1A0421ADD983C007D9ED6 /* MTFutureSaltsMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A0101ADD983C007D9ED6 /* MTFutureSaltsMessage.h */; };
D0D1A0431ADD983C007D9ED6 /* MTFutureSaltsMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A0111ADD983C007D9ED6 /* MTFutureSaltsMessage.m */; };
@ -149,7 +151,7 @@
D0D1A05B1ADD983C007D9ED6 /* MTPongMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A0291ADD983C007D9ED6 /* MTPongMessage.m */; };
D0D1A05C1ADD983C007D9ED6 /* MTResPqMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A02A1ADD983C007D9ED6 /* MTResPqMessage.h */; };
D0D1A05D1ADD983C007D9ED6 /* MTResPqMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A02B1ADD983C007D9ED6 /* MTResPqMessage.m */; };
D0D1A05E1ADD983C007D9ED6 /* MTRpcError.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A02C1ADD983C007D9ED6 /* MTRpcError.h */; };
D0D1A05E1ADD983C007D9ED6 /* MTRpcError.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A02C1ADD983C007D9ED6 /* MTRpcError.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0D1A05F1ADD983C007D9ED6 /* MTRpcError.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A02D1ADD983C007D9ED6 /* MTRpcError.m */; };
D0D1A0601ADD983C007D9ED6 /* MTRpcResultMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A02E1ADD983C007D9ED6 /* MTRpcResultMessage.h */; };
D0D1A0611ADD983C007D9ED6 /* MTRpcResultMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A02F1ADD983C007D9ED6 /* MTRpcResultMessage.m */; };
@ -159,11 +161,24 @@
D0D1A0651ADD983C007D9ED6 /* MTServerDhParamsMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A0331ADD983C007D9ED6 /* MTServerDhParamsMessage.m */; };
D0D1A0661ADD983C007D9ED6 /* MTSetClientDhParamsResponseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A0341ADD983C007D9ED6 /* MTSetClientDhParamsResponseMessage.h */; };
D0D1A0671ADD983C007D9ED6 /* MTSetClientDhParamsResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A0351ADD983C007D9ED6 /* MTSetClientDhParamsResponseMessage.m */; };
D0D1A06B1ADD987A007D9ED6 /* MTDatacenterAddressListData.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A0691ADD987A007D9ED6 /* MTDatacenterAddressListData.h */; };
D0D1A06B1ADD987A007D9ED6 /* MTDatacenterAddressListData.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D1A0691ADD987A007D9ED6 /* MTDatacenterAddressListData.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0D1A06C1ADD987A007D9ED6 /* MTDatacenterAddressListData.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D1A06A1ADD987A007D9ED6 /* MTDatacenterAddressListData.m */; };
D0D1A0721ADDE2FC007D9ED6 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D1A0711ADDE2FC007D9ED6 /* libz.dylib */; };
D0D225101B4D817B0085E26D /* MtProtoKitDynamic.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D2250F1B4D817B0085E26D /* MtProtoKitDynamic.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0D225161B4D817B0085E26D /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D2250B1B4D817B0085E26D /* MtProtoKitDynamic.framework */; };
D0D2251D1B4D817B0085E26D /* MtProtoKitDynamicTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D2251C1B4D817B0085E26D /* MtProtoKitDynamicTests.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
D0D225171B4D817B0085E26D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05A830A18AFB3F9007F1076 /* Project object */;
proxyType = 1;
remoteGlobalIDString = D0D2250A1B4D817B0085E26D;
remoteInfo = MtProtoKitDynamic;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
93DBD22F18B2D72800631ADC /* MTRequestErrorContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MTRequestErrorContext.h; path = MtProtoKit/MTRequestErrorContext.h; sourceTree = "<group>"; };
93DBD23018B2D72800631ADC /* MTRequestErrorContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MTRequestErrorContext.m; path = MtProtoKit/MTRequestErrorContext.m; sourceTree = "<group>"; };
@ -367,6 +382,8 @@
D079AB9B1AF39B8000076F59 /* MtProtoKitMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MtProtoKitMac.h; sourceTree = "<group>"; };
D079ABA71AF39B8000076F59 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D079ABA81AF39B8000076F59 /* MtProtoKitMacTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MtProtoKitMacTests.m; sourceTree = "<group>"; };
D09A59561B582EFF00FC3724 /* MTFileBasedKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTFileBasedKeychain.h; sourceTree = "<group>"; };
D09A59571B582EFF00FC3724 /* MTFileBasedKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTFileBasedKeychain.m; sourceTree = "<group>"; };
D0A44E4D18B24A6000B64FC6 /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libcrypto.a; sourceTree = "<group>"; };
D0CB05F71ADC4483005E298F /* MtProtoKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MtProtoKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0CB05FA1ADC4483005E298F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -426,6 +443,12 @@
D0D1A0691ADD987A007D9ED6 /* MTDatacenterAddressListData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MTDatacenterAddressListData.h; path = MTProtoKit/MTDatacenterAddressListData.h; sourceTree = "<group>"; };
D0D1A06A1ADD987A007D9ED6 /* MTDatacenterAddressListData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MTDatacenterAddressListData.m; path = MTProtoKit/MTDatacenterAddressListData.m; sourceTree = "<group>"; };
D0D1A0711ADDE2FC007D9ED6 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
D0D2250B1B4D817B0085E26D /* MtProtoKitDynamic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MtProtoKitDynamic.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D0D2250E1B4D817B0085E26D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D0D2250F1B4D817B0085E26D /* MtProtoKitDynamic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MtProtoKitDynamic.h; sourceTree = "<group>"; };
D0D225151B4D817B0085E26D /* MtProtoKitDynamicTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MtProtoKitDynamicTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D0D2251B1B4D817B0085E26D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D0D2251C1B4D817B0085E26D /* MtProtoKitDynamicTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MtProtoKitDynamicTests.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -446,6 +469,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225071B4D817B0085E26D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225121B4D817B0085E26D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D0D225161B4D817B0085E26D /* MtProtoKitDynamic.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -564,6 +602,8 @@
D0CB06051ADC4483005E298F /* MtProtoKitTests */,
D079AB981AF39B8000076F59 /* MtProtoKitMac */,
D079ABA51AF39B8000076F59 /* MtProtoKitMacTests */,
D0D2250C1B4D817B0085E26D /* MtProtoKitDynamic */,
D0D225191B4D817B0085E26D /* MtProtoKitDynamicTests */,
D05A831618AFB3F9007F1076 /* Frameworks */,
D05A831518AFB3F9007F1076 /* Products */,
);
@ -574,6 +614,8 @@
children = (
D0CB05F71ADC4483005E298F /* MtProtoKit.framework */,
D079AB971AF39B8000076F59 /* MtProtoKitMac.framework */,
D0D2250B1B4D817B0085E26D /* MtProtoKitDynamic.framework */,
D0D225151B4D817B0085E26D /* MtProtoKitDynamicTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -695,6 +737,8 @@
children = (
D05A83C018AFB75B007F1076 /* MTKeychain.h */,
D05A83A318AFB75B007F1076 /* MTKeychain.m */,
D09A59561B582EFF00FC3724 /* MTFileBasedKeychain.h */,
D09A59571B582EFF00FC3724 /* MTFileBasedKeychain.m */,
);
name = Keychain;
sourceTree = "<group>";
@ -1065,6 +1109,40 @@
name = Messages;
sourceTree = "<group>";
};
D0D2250C1B4D817B0085E26D /* MtProtoKitDynamic */ = {
isa = PBXGroup;
children = (
D0D2250F1B4D817B0085E26D /* MtProtoKitDynamic.h */,
D0D2250D1B4D817B0085E26D /* Supporting Files */,
);
path = MtProtoKitDynamic;
sourceTree = "<group>";
};
D0D2250D1B4D817B0085E26D /* Supporting Files */ = {
isa = PBXGroup;
children = (
D0D2250E1B4D817B0085E26D /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
D0D225191B4D817B0085E26D /* MtProtoKitDynamicTests */ = {
isa = PBXGroup;
children = (
D0D2251C1B4D817B0085E26D /* MtProtoKitDynamicTests.m */,
D0D2251A1B4D817B0085E26D /* Supporting Files */,
);
path = MtProtoKitDynamicTests;
sourceTree = "<group>";
};
D0D2251A1B4D817B0085E26D /* Supporting Files */ = {
isa = PBXGroup;
children = (
D0D2251B1B4D817B0085E26D /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -1132,6 +1210,7 @@
D0D1A05E1ADD983C007D9ED6 /* MTRpcError.h in Headers */,
D0CB063A1ADC4591005E298F /* MTTimeFixContext.h in Headers */,
D0CB065D1ADC45CE005E298F /* MTTcpConnectionBehaviour.h in Headers */,
D09A59581B582EFF00FC3724 /* MTFileBasedKeychain.h in Headers */,
D0CB06311ADC4583005E298F /* MTDatacenterAddressSet.h in Headers */,
D0D1A03C1ADD983C007D9ED6 /* MTDestroySessionResponseMessage.h in Headers */,
D0CB061E1ADC4541005E298F /* MTOutputStream.h in Headers */,
@ -1159,6 +1238,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225081B4D817B0085E26D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
D0D225101B4D817B0085E26D /* MtProtoKitDynamic.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
@ -1198,6 +1285,42 @@
productReference = D0CB05F71ADC4483005E298F /* MtProtoKit.framework */;
productType = "com.apple.product-type.framework";
};
D0D2250A1B4D817B0085E26D /* MtProtoKitDynamic */ = {
isa = PBXNativeTarget;
buildConfigurationList = D0D225221B4D817B0085E26D /* Build configuration list for PBXNativeTarget "MtProtoKitDynamic" */;
buildPhases = (
D0D225061B4D817B0085E26D /* Sources */,
D0D225071B4D817B0085E26D /* Frameworks */,
D0D225081B4D817B0085E26D /* Headers */,
D0D225091B4D817B0085E26D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = MtProtoKitDynamic;
productName = MtProtoKitDynamic;
productReference = D0D2250B1B4D817B0085E26D /* MtProtoKitDynamic.framework */;
productType = "com.apple.product-type.framework";
};
D0D225141B4D817B0085E26D /* MtProtoKitDynamicTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = D0D225231B4D817B0085E26D /* Build configuration list for PBXNativeTarget "MtProtoKitDynamicTests" */;
buildPhases = (
D0D225111B4D817B0085E26D /* Sources */,
D0D225121B4D817B0085E26D /* Frameworks */,
D0D225131B4D817B0085E26D /* Resources */,
);
buildRules = (
);
dependencies = (
D0D225181B4D817B0085E26D /* PBXTargetDependency */,
);
name = MtProtoKitDynamicTests;
productName = MtProtoKitDynamicTests;
productReference = D0D225151B4D817B0085E26D /* MtProtoKitDynamicTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -1213,6 +1336,12 @@
D0CB05F61ADC4483005E298F = {
CreatedOnToolsVersion = 6.3;
};
D0D2250A1B4D817B0085E26D = {
CreatedOnToolsVersion = 6.4;
};
D0D225141B4D817B0085E26D = {
CreatedOnToolsVersion = 6.4;
};
};
};
buildConfigurationList = D05A830D18AFB3F9007F1076 /* Build configuration list for PBXProject "MtProtoKit" */;
@ -1229,6 +1358,8 @@
targets = (
D0CB05F61ADC4483005E298F /* MtProtoKit */,
D079AB961AF39B8000076F59 /* MtProtoKitMac */,
D0D2250A1B4D817B0085E26D /* MtProtoKitDynamic */,
D0D225141B4D817B0085E26D /* MtProtoKitDynamicTests */,
);
};
/* End PBXProject section */
@ -1248,6 +1379,20 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225091B4D817B0085E26D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225131B4D817B0085E26D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -1282,6 +1427,7 @@
D0D1A0511ADD983C007D9ED6 /* MTMsgsAckMessage.m in Sources */,
D0CB066F1ADC49FF005E298F /* AFHTTPRequestOperation.m in Sources */,
D0D1A06C1ADD987A007D9ED6 /* MTDatacenterAddressListData.m in Sources */,
D09A59591B582EFF00FC3724 /* MTFileBasedKeychain.m in Sources */,
D0CB06681ADC45DA005E298F /* MTHttpWorker.m in Sources */,
D0CB06561ADC45BA005E298F /* MTApiEnvironment.m in Sources */,
D0D1A05F1ADD983C007D9ED6 /* MTRpcError.m in Sources */,
@ -1340,8 +1486,31 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225061B4D817B0085E26D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D0D225111B4D817B0085E26D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D0D2251D1B4D817B0085E26D /* MtProtoKitDynamicTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
D0D225181B4D817B0085E26D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D0D2250A1B4D817B0085E26D /* MtProtoKitDynamic */;
targetProxy = D0D225171B4D817B0085E26D /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
D05A833518AFB3F9007F1076 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@ -1591,6 +1760,110 @@
};
name = Release;
};
D0D2251E1B4D817B0085E26D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
D0D2251F1B4D817B0085E26D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
D0D225201B4D817B0085E26D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = MtProtoKitDynamicTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
D0D225211B4D817B0085E26D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = MtProtoKitDynamicTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -1621,6 +1894,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D0D225221B4D817B0085E26D /* Build configuration list for PBXNativeTarget "MtProtoKitDynamic" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D0D2251E1B4D817B0085E26D /* Debug */,
D0D2251F1B4D817B0085E26D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D0D225231B4D817B0085E26D /* Build configuration list for PBXNativeTarget "MtProtoKitDynamicTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D0D225201B4D817B0085E26D /* Debug */,
D0D225211B4D817B0085E26D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = D05A830A18AFB3F9007F1076 /* Project object */;

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.telegram.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,19 @@
//
// MtProtoKitDynamic.h
// MtProtoKitDynamic
//
// Created by Peter on 08/07/15.
// Copyright (c) 2015 Telegram. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for MtProtoKitDynamic.
FOUNDATION_EXPORT double MtProtoKitDynamicVersionNumber;
//! Project version string for MtProtoKitDynamic.
FOUNDATION_EXPORT const unsigned char MtProtoKitDynamicVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <MtProtoKitDynamic/PublicHeader.h>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.telegram.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,40 @@
//
// MtProtoKitDynamicTests.m
// MtProtoKitDynamicTests
//
// Created by Peter on 08/07/15.
// Copyright (c) 2015 Telegram. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@interface MtProtoKitDynamicTests : XCTestCase
@end
@implementation MtProtoKitDynamicTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, @"Pass");
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
@end