From 719eb543a8d08c4f536ea7933ffb3af0a8553e87 Mon Sep 17 00:00:00 2001 From: Chris Ballinger Date: Fri, 8 Apr 2016 17:24:41 -0700 Subject: Xcode, podspec, wrapper --- xcode/OLMKit/Info.plist | 26 +++++++++ xcode/OLMKit/OLMAccount.h | 37 ++++++++++++ xcode/OLMKit/OLMAccount.m | 116 ++++++++++++++++++++++++++++++++++++++ xcode/OLMKit/OLMAccount_Private.h | 15 +++++ xcode/OLMKit/OLMKit.h | 23 ++++++++ xcode/OLMKit/OLMMessage.h | 24 ++++++++ xcode/OLMKit/OLMMessage.m | 13 +++++ xcode/OLMKit/OLMSerializable.h | 19 +++++++ xcode/OLMKit/OLMSession.h | 29 ++++++++++ xcode/OLMKit/OLMSession.m | 30 ++++++++++ xcode/OLMKit/OLMUtility.h | 15 +++++ xcode/OLMKit/OLMUtility.m | 28 +++++++++ 12 files changed, 375 insertions(+) create mode 100644 xcode/OLMKit/Info.plist create mode 100644 xcode/OLMKit/OLMAccount.h create mode 100644 xcode/OLMKit/OLMAccount.m create mode 100644 xcode/OLMKit/OLMAccount_Private.h create mode 100644 xcode/OLMKit/OLMKit.h create mode 100644 xcode/OLMKit/OLMMessage.h create mode 100644 xcode/OLMKit/OLMMessage.m create mode 100644 xcode/OLMKit/OLMSerializable.h create mode 100644 xcode/OLMKit/OLMSession.h create mode 100644 xcode/OLMKit/OLMSession.m create mode 100644 xcode/OLMKit/OLMUtility.h create mode 100644 xcode/OLMKit/OLMUtility.m (limited to 'xcode/OLMKit') diff --git a/xcode/OLMKit/Info.plist b/xcode/OLMKit/Info.plist new file mode 100644 index 0000000..d3de8ee --- /dev/null +++ b/xcode/OLMKit/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/xcode/OLMKit/OLMAccount.h b/xcode/OLMKit/OLMAccount.h new file mode 100644 index 0000000..cfa7129 --- /dev/null +++ b/xcode/OLMKit/OLMAccount.h @@ -0,0 +1,37 @@ +// +// OLMAccount.h +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import +#import "OLMSerializable.h" + +@interface OLMAccount : NSObject + +/** Creates new account */ +- (instancetype) initNewAccount; + +/** public identity keys. base64 encoded in "curve25519" and "ed25519" keys */ +- (NSDictionary*) identityKeys; + +/** signs message with ed25519 key for account */ +- (NSData*) signMessage:(NSData*)messageData; + +/** Public parts of the unpublished one time keys for the account */ +- (NSDictionary*) oneTimeKeys; + +/** Marks the current set of one time keys as being published. */ +- (void) markKeysAsPublished; + +/** The largest number of one time keys this account can store. */ +- (NSUInteger) maxOneTimeKeys; + +/** Generates a number of new one time keys. If the total number of keys stored + * by this account exceeds -maxOneTimeKeys then the old keys are + * discarded. */ +- (void) generateOneTimeKeys:(NSUInteger)numberOfKeys; + +@end diff --git a/xcode/OLMKit/OLMAccount.m b/xcode/OLMKit/OLMAccount.m new file mode 100644 index 0000000..58dd4ad --- /dev/null +++ b/xcode/OLMKit/OLMAccount.m @@ -0,0 +1,116 @@ +// +// OLMAccount.m +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import "OLMAccount.h" +#import "OLMAccount_Private.h" +#import "OLMUtility.h" + +@import Security; + +@implementation OLMAccount + +- (void) dealloc { + olm_clear_account(_account); + free(_account); +} + +- (BOOL) initializeAccountMemory { + size_t accountSize = olm_account_size(); + _account = malloc(accountSize); + NSParameterAssert(_account != nil); + if (!_account) { + return NO; + } + _account = olm_account(_account); + NSParameterAssert(_account != nil); + if (!_account) { + return NO; + } + return YES; +} + +- (instancetype) initNewAccount { + self = [super init]; + if (!self) { + return nil; + } + BOOL success = [self initializeAccountMemory]; + if (!success) { + return nil; + } + size_t randomLength = olm_create_account_random_length(_account); + size_t accountResult = olm_create_account(_account, (void*)[OLMUtility randomBytesOfLength:randomLength].bytes, randomLength); + if (accountResult == olm_error()) { + const char *error = olm_account_last_error(_account); + NSLog(@"error creating account: %s", error); + return nil; + } + return self; +} + +- (size_t) maxOneTimeKeys { + return olm_account_max_number_of_one_time_keys(_account); +} + + +/** public identity keys */ +- (NSDictionary*) identityKeys { + size_t identityKeysLength = olm_account_identity_keys_length(_account); + uint8_t *identityKeysBytes = malloc(identityKeysLength); + if (!identityKeysBytes) { + return nil; + } + size_t result = olm_account_identity_keys(_account, identityKeysBytes, identityKeysLength); + if (result == olm_error()) { + const char *error = olm_account_last_error(_account); + NSLog(@"error getting id keys: %s", error); + free(identityKeysBytes); + return nil; + } + NSData *idKeyData = [NSData dataWithBytesNoCopy:identityKeysBytes length:identityKeysLength freeWhenDone:YES]; + NSError *error = nil; + NSDictionary *keysDictionary = [NSJSONSerialization JSONObjectWithData:idKeyData options:0 error:&error]; + if (error) { + NSLog(@"Could not decode JSON: %@", error.localizedDescription); + } + return keysDictionary; +} + +- (NSDictionary*) oneTimeKeys { + size_t otkLength = olm_account_one_time_keys_length(_account); + uint8_t *otkBytes = malloc(otkLength); + if (!otkBytes) { + return nil; + } + size_t result = olm_account_one_time_keys(_account, otkBytes, otkLength); + if (result == olm_error()) { + const char *error = olm_account_last_error(_account); + NSLog(@"error getting id keys: %s", error); + free(otkBytes); + } + NSData *otk = [NSData dataWithBytesNoCopy:otkBytes length:otkLength freeWhenDone:YES]; + NSError *error = nil; + NSDictionary *keysDictionary = [NSJSONSerialization JSONObjectWithData:otk options:0 error:&error]; + if (error) { + NSLog(@"Could not decode JSON: %@", error.localizedDescription); + } + return keysDictionary; +} + + +- (void) generateOneTimeKeys:(NSUInteger)numberOfKeys { + size_t randomLength = olm_account_generate_one_time_keys_random_length(_account, numberOfKeys); + size_t result = olm_account_generate_one_time_keys(_account, numberOfKeys, (void*)[OLMUtility randomBytesOfLength:randomLength].bytes, randomLength); + if (result == olm_error()) { + const char *error = olm_account_last_error(_account); + NSLog(@"error generating keys: %s", error); + } +} + + +@end diff --git a/xcode/OLMKit/OLMAccount_Private.h b/xcode/OLMKit/OLMAccount_Private.h new file mode 100644 index 0000000..4eb3e2b --- /dev/null +++ b/xcode/OLMKit/OLMAccount_Private.h @@ -0,0 +1,15 @@ +// +// OLMAccount_Private.h +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +@import olm; + +@interface OLMAccount() + +@property (nonatomic) OlmAccount *account; + +@end \ No newline at end of file diff --git a/xcode/OLMKit/OLMKit.h b/xcode/OLMKit/OLMKit.h new file mode 100644 index 0000000..745af43 --- /dev/null +++ b/xcode/OLMKit/OLMKit.h @@ -0,0 +1,23 @@ +// +// OLMKit.h +// OLMKit +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import + +//! Project version number for OLMKit. +FOUNDATION_EXPORT double OLMKitVersionNumber; + +//! Project version string for OLMKit. +FOUNDATION_EXPORT const unsigned char OLMKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + +#import "OLMAccount.h" +#import "OLMSession.h" +#import "OLMMessage.h" +#import "OLMUtility.h" \ No newline at end of file diff --git a/xcode/OLMKit/OLMMessage.h b/xcode/OLMKit/OLMMessage.h new file mode 100644 index 0000000..2b747fb --- /dev/null +++ b/xcode/OLMKit/OLMMessage.h @@ -0,0 +1,24 @@ +// +// OLMMessage.h +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import + +typedef NS_ENUM(NSUInteger, OLMMessageType) { + OLMMessageTypeUnknown, + OLMMessageTypePreKey, + OLMMessageTypeMessage +}; + +@interface OLMMessage : NSObject + +@property (nonatomic, readonly, nonnull) NSString *message; +@property (readonly) OLMMessageType type; + +- (nonnull instancetype) initWithMessage:(nonnull NSString*)message type:(OLMMessageType)type; + +@end diff --git a/xcode/OLMKit/OLMMessage.m b/xcode/OLMKit/OLMMessage.m new file mode 100644 index 0000000..ce732ec --- /dev/null +++ b/xcode/OLMKit/OLMMessage.m @@ -0,0 +1,13 @@ +// +// OLMMessage.m +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import "OLMMessage.h" + +@implementation OLMMessage + +@end diff --git a/xcode/OLMKit/OLMSerializable.h b/xcode/OLMKit/OLMSerializable.h new file mode 100644 index 0000000..afacdaa --- /dev/null +++ b/xcode/OLMKit/OLMSerializable.h @@ -0,0 +1,19 @@ +// +// OLMSerializable.h +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import + +@protocol OLMSerializable + +/** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ +- (instancetype) initWithSerializedData:(NSData*)serializedData key:(NSData*)key error:(NSError**)error; + +/** Serializes and encrypts object data */ +- (NSData*) serializeDataWithKey:(NSData*)key; + +@end diff --git a/xcode/OLMKit/OLMSession.h b/xcode/OLMKit/OLMSession.h new file mode 100644 index 0000000..196900f --- /dev/null +++ b/xcode/OLMKit/OLMSession.h @@ -0,0 +1,29 @@ +// +// OLMSession.h +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import +#import "OLMSerializable.h" +#import "OLMAccount.h" + +@interface OLMSession : NSObject + +- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey theirOneTimeKey:(NSData*)theirOneTimeKey; + +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSData*)oneTimeKeyMessage; + +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData*)oneTimeKeyMessage; + +- (NSData*) sessionIdentifier; + +- (BOOL) matchesInboundSession:(NSData*)oneTimeKeyMessage; + +- (BOOL) matchesInboundSessionFrom:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData *)oneTimeKeyMessage; + +- (void) removeOneTimeKeys; + +@end diff --git a/xcode/OLMKit/OLMSession.m b/xcode/OLMKit/OLMSession.m new file mode 100644 index 0000000..24a8b36 --- /dev/null +++ b/xcode/OLMKit/OLMSession.m @@ -0,0 +1,30 @@ +// +// OLMSession.m +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import "OLMSession.h" +@import olm; + +@interface OLMSession() +@property (nonatomic) OlmSession *session; +@end + +@implementation OLMSession + +- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey theirOneTimeKey:(NSData*)theirOneTimeKey { + +} + +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSData*)oneTimeKeyMessage { + +} + +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData*)oneTimeKeyMessage { + +} + +@end diff --git a/xcode/OLMKit/OLMUtility.h b/xcode/OLMKit/OLMUtility.h new file mode 100644 index 0000000..0de9725 --- /dev/null +++ b/xcode/OLMKit/OLMUtility.h @@ -0,0 +1,15 @@ +// +// OLMUtility.h +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import + +@interface OLMUtility : NSObject + ++ (NSData*) randomBytesOfLength:(NSUInteger)length; + +@end diff --git a/xcode/OLMKit/OLMUtility.m b/xcode/OLMKit/OLMUtility.m new file mode 100644 index 0000000..0148932 --- /dev/null +++ b/xcode/OLMKit/OLMUtility.m @@ -0,0 +1,28 @@ +// +// OLMUtility.m +// olm +// +// Created by Chris Ballinger on 4/8/16. +// +// + +#import "OLMUtility.h" + +@implementation OLMUtility + ++ (NSData*) randomBytesOfLength:(NSUInteger)length { + uint8_t *randomBytes = malloc(length * sizeof(uint8_t)); + NSParameterAssert(randomBytes != NULL); + if (!randomBytes) { + return nil; + } + int result = SecRandomCopyBytes(kSecRandomDefault, length, randomBytes); + if (result != 0) { + free(randomBytes); + return nil; + } + NSData *data = [NSData dataWithBytesNoCopy:randomBytes length:length freeWhenDone:YES]; + return data; +} + +@end -- cgit v1.2.3 From f505113fb7a6d61015ad8050b3fb4e26df029150 Mon Sep 17 00:00:00 2001 From: Chris Ballinger Date: Sat, 9 Apr 2016 14:00:30 -0700 Subject: Initial test passing --- xcode/OLMKit/OLMAccount.m | 6 +- xcode/OLMKit/OLMMessage.h | 16 +++-- xcode/OLMKit/OLMMessage.m | 11 ++++ xcode/OLMKit/OLMSession.h | 23 +++++-- xcode/OLMKit/OLMSession.m | 165 ++++++++++++++++++++++++++++++++++++++++++++-- xcode/OLMKit/OLMUtility.h | 2 +- xcode/OLMKit/OLMUtility.m | 13 ++-- 7 files changed, 206 insertions(+), 30 deletions(-) (limited to 'xcode/OLMKit') diff --git a/xcode/OLMKit/OLMAccount.m b/xcode/OLMKit/OLMAccount.m index 58dd4ad..d56b6b4 100644 --- a/xcode/OLMKit/OLMAccount.m +++ b/xcode/OLMKit/OLMAccount.m @@ -44,7 +44,8 @@ return nil; } size_t randomLength = olm_create_account_random_length(_account); - size_t accountResult = olm_create_account(_account, (void*)[OLMUtility randomBytesOfLength:randomLength].bytes, randomLength); + NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; + size_t accountResult = olm_create_account(_account, random.mutableBytes, random.length); if (accountResult == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error creating account: %s", error); @@ -105,7 +106,8 @@ - (void) generateOneTimeKeys:(NSUInteger)numberOfKeys { size_t randomLength = olm_account_generate_one_time_keys_random_length(_account, numberOfKeys); - size_t result = olm_account_generate_one_time_keys(_account, numberOfKeys, (void*)[OLMUtility randomBytesOfLength:randomLength].bytes, randomLength); + NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; + size_t result = olm_account_generate_one_time_keys(_account, numberOfKeys, random.mutableBytes, random.length); if (result == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error generating keys: %s", error); diff --git a/xcode/OLMKit/OLMMessage.h b/xcode/OLMKit/OLMMessage.h index 2b747fb..97c748f 100644 --- a/xcode/OLMKit/OLMMessage.h +++ b/xcode/OLMKit/OLMMessage.h @@ -8,17 +8,21 @@ #import -typedef NS_ENUM(NSUInteger, OLMMessageType) { - OLMMessageTypeUnknown, - OLMMessageTypePreKey, - OLMMessageTypeMessage +/* + from olm.hh + static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0; + static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1; + */ +typedef NS_ENUM(NSInteger, OLMMessageType) { + OLMMessageTypePreKey = 0, + OLMMessageTypeMessage = 1 }; @interface OLMMessage : NSObject -@property (nonatomic, readonly, nonnull) NSString *message; +@property (nonatomic, copy, readonly, nonnull) NSString *ciphertext; @property (readonly) OLMMessageType type; -- (nonnull instancetype) initWithMessage:(nonnull NSString*)message type:(OLMMessageType)type; +- (nullable instancetype) initWithCiphertext:(nonnull NSString*)ciphertext type:(OLMMessageType)type; @end diff --git a/xcode/OLMKit/OLMMessage.m b/xcode/OLMKit/OLMMessage.m index ce732ec..d0cfb41 100644 --- a/xcode/OLMKit/OLMMessage.m +++ b/xcode/OLMKit/OLMMessage.m @@ -10,4 +10,15 @@ @implementation OLMMessage +- (nullable instancetype) initWithCiphertext:(nonnull NSString*)ciphertext type:(OLMMessageType)type { + NSParameterAssert(ciphertext != nil); + self = [super init]; + if (!self) { + return nil; + } + _ciphertext = [ciphertext copy]; + _type = type; + return self; +} + @end diff --git a/xcode/OLMKit/OLMSession.h b/xcode/OLMKit/OLMSession.h index 196900f..1a075e4 100644 --- a/xcode/OLMKit/OLMSession.h +++ b/xcode/OLMKit/OLMSession.h @@ -9,21 +9,30 @@ #import #import "OLMSerializable.h" #import "OLMAccount.h" +#import "OLMMessage.h" @interface OLMSession : NSObject -- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey theirOneTimeKey:(NSData*)theirOneTimeKey; +@property (nonatomic, strong) OLMAccount *account; -- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSData*)oneTimeKeyMessage; +- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey; -- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData*)oneTimeKeyMessage; +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSString*)oneTimeKeyMessage; -- (NSData*) sessionIdentifier; +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString*)oneTimeKeyMessage; -- (BOOL) matchesInboundSession:(NSData*)oneTimeKeyMessage; +- (NSString*) sessionIdentifier; -- (BOOL) matchesInboundSessionFrom:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData *)oneTimeKeyMessage; +- (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage; -- (void) removeOneTimeKeys; +- (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage; + +- (BOOL) removeOneTimeKeys; + +/** UTF-8 plaintext -> base64 ciphertext */ +- (OLMMessage*) encryptMessage:(NSString*)message; + +/** base64 ciphertext -> UTF-8 plaintext */ +- (NSString*) decryptMessage:(OLMMessage*)message; @end diff --git a/xcode/OLMKit/OLMSession.m b/xcode/OLMKit/OLMSession.m index 24a8b36..fa7cb62 100644 --- a/xcode/OLMKit/OLMSession.m +++ b/xcode/OLMKit/OLMSession.m @@ -7,6 +7,8 @@ // #import "OLMSession.h" +#import "OLMUtility.h" +#import "OLMAccount_Private.h" @import olm; @interface OLMSession() @@ -15,16 +17,167 @@ @implementation OLMSession -- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey theirOneTimeKey:(NSData*)theirOneTimeKey { - +- (void) dealloc { + olm_clear_session(_session); + free(_session); } -- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSData*)oneTimeKeyMessage { - +- (BOOL) initializeSessionMemory { + size_t size = olm_session_size(); + _session = malloc(size); + NSParameterAssert(_session != nil); + if (!_session) { + return NO; + } + _session = olm_session(_session); + NSParameterAssert(_session != nil); + if (!_session) { + return NO; + } + return YES; } -- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData*)oneTimeKeyMessage { - +- (instancetype) initWithAccount:(OLMAccount*)account { + self = [super init]; + if (!self) { + return nil; + } + BOOL success = [self initializeSessionMemory]; + if (!success) { + return nil; + } + _account = account; + return self; +} + +- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey { + self = [self initWithAccount:account]; + if (!self) { + return nil; + } + NSMutableData *random = [OLMUtility randomBytesOfLength:olm_create_outbound_session_random_length(_session)]; + NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding]; + NSData *otKey = [theirOneTimeKey dataUsingEncoding:NSUTF8StringEncoding]; + size_t result = olm_create_outbound_session(_session, account.account, idKey.bytes, idKey.length, otKey.bytes, otKey.length, random.mutableBytes, random.length); + if (result == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_create_outbound_session error: %s", error); + return nil; + } + return self; +} + +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSString*)oneTimeKeyMessage { + self = [self initWithAccount:account]; + if (!self) { + return nil; + } + BOOL success = [self initializeSessionMemory]; + if (!success) { + return nil; + } + NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; + size_t result = olm_create_inbound_session(_session, account.account, otk.mutableBytes, oneTimeKeyMessage.length); + if (result == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_create_inbound_session error: %s", error); + return nil; + } + return self; +} + +- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString*)oneTimeKeyMessage { + self = [self initWithAccount:account]; + if (!self) { + return nil; + } + BOOL success = [self initializeSessionMemory]; + if (!success) { + return nil; + } + NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding]; + NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; + size_t result = olm_create_inbound_session_from(_session, account.account, idKey.bytes, idKey.length, otk.mutableBytes, otk.length); + if (result == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_create_inbound_session_from error: %s", error); + return nil; + } + return self; +} + +- (NSString*) sessionIdentifier { + size_t length = olm_session_id_length(_session); + NSMutableData *idData = [NSMutableData dataWithLength:length]; + if (!idData) { + return nil; + } + size_t result = olm_session_id(_session, idData.mutableBytes, idData.length); + if (result == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_session_id error: %s", error); + return nil; + } + NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding]; + return idString; +} + +- (OLMMessage*) encryptMessage:(NSString*)message { + size_t messageType = olm_encrypt_message_type(_session); + size_t randomLength = olm_encrypt_random_length(_session); + NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; + NSData *plaintextData = [message dataUsingEncoding:NSUTF8StringEncoding]; + size_t ciphertextLength = olm_encrypt_message_length(_session, plaintextData.length); + NSMutableData *ciphertext = [NSMutableData dataWithLength:ciphertextLength]; + if (!ciphertext) { + return nil; + } + size_t result = olm_encrypt(_session, plaintextData.bytes, plaintextData.length, random.mutableBytes, random.length, ciphertext.mutableBytes, ciphertext.length); + if (result == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_encrypt error: %s", error); + return nil; + } + NSString *ciphertextString = [[NSString alloc] initWithData:ciphertext encoding:NSUTF8StringEncoding]; + OLMMessage *encryptedMessage = [[OLMMessage alloc] initWithCiphertext:ciphertextString type:messageType]; + return encryptedMessage; +} + +- (BOOL) removeOneTimeKeys { + size_t result = olm_remove_one_time_keys(_account.account, _session); + if (result == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_remove_one_time_keys error: %s", error); + return NO; + } + return YES; +} + +- (NSString*) decryptMessage:(OLMMessage*)message { + NSParameterAssert(message != nil); + NSData *messageData = [message.ciphertext dataUsingEncoding:NSUTF8StringEncoding]; + if (!messageData) { + return nil; + } + NSMutableData *mutMessage = messageData.mutableCopy; + size_t maxPlaintextLength = olm_decrypt_max_plaintext_length(_session, message.type, mutMessage.mutableBytes, mutMessage.length); + if (maxPlaintextLength == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_decrypt_max_plaintext_length error: %s", error); + return nil; + } + // message buffer is destroyed by olm_decrypt_max_plaintext_length + mutMessage = messageData.mutableCopy; + NSMutableData *plaintextData = [NSMutableData dataWithLength:maxPlaintextLength]; + size_t plaintextLength = olm_decrypt(_session, message.type, mutMessage.mutableBytes, mutMessage.length, plaintextData.mutableBytes, plaintextData.length); + if (plaintextLength == olm_error()) { + const char *error = olm_session_last_error(_session); + NSAssert(NO, @"olm_decrypt error: %s", error); + return nil; + } + plaintextData.length = plaintextLength; + NSString *plaintext = [[NSString alloc] initWithData:plaintextData encoding:NSUTF8StringEncoding]; + return plaintext; } @end diff --git a/xcode/OLMKit/OLMUtility.h b/xcode/OLMKit/OLMUtility.h index 0de9725..8acbf40 100644 --- a/xcode/OLMKit/OLMUtility.h +++ b/xcode/OLMKit/OLMUtility.h @@ -10,6 +10,6 @@ @interface OLMUtility : NSObject -+ (NSData*) randomBytesOfLength:(NSUInteger)length; ++ (NSMutableData*) randomBytesOfLength:(NSUInteger)length; @end diff --git a/xcode/OLMKit/OLMUtility.m b/xcode/OLMKit/OLMUtility.m index 0148932..5dbe644 100644 --- a/xcode/OLMKit/OLMUtility.m +++ b/xcode/OLMKit/OLMUtility.m @@ -10,19 +10,16 @@ @implementation OLMUtility -+ (NSData*) randomBytesOfLength:(NSUInteger)length { - uint8_t *randomBytes = malloc(length * sizeof(uint8_t)); - NSParameterAssert(randomBytes != NULL); - if (!randomBytes) { ++ (NSMutableData*) randomBytesOfLength:(NSUInteger)length { + NSMutableData *randomData = [NSMutableData dataWithLength:length]; + if (!randomData) { return nil; } - int result = SecRandomCopyBytes(kSecRandomDefault, length, randomBytes); + int result = SecRandomCopyBytes(kSecRandomDefault, randomData.length, randomData.mutableBytes); if (result != 0) { - free(randomBytes); return nil; } - NSData *data = [NSData dataWithBytesNoCopy:randomBytes length:length freeWhenDone:YES]; - return data; + return randomData; } @end -- cgit v1.2.3 From daab2a58af947cddd67fe9f30dd3a9fc327650c0 Mon Sep 17 00:00:00 2001 From: Chris Ballinger Date: Wed, 13 Apr 2016 16:53:47 -0700 Subject: OLMAccount and OLMSession serialization --- xcode/OLMKit/OLMAccount.h | 8 ++- xcode/OLMKit/OLMAccount.m | 113 +++++++++++++++++++++++++++++++++- xcode/OLMKit/OLMSerializable.h | 6 +- xcode/OLMKit/OLMSession.h | 6 +- xcode/OLMKit/OLMSession.m | 124 +++++++++++++++++++++++++++++++------- xcode/OLMKit/OLMSession_Private.h | 16 +++++ 6 files changed, 239 insertions(+), 34 deletions(-) create mode 100644 xcode/OLMKit/OLMSession_Private.h (limited to 'xcode/OLMKit') diff --git a/xcode/OLMKit/OLMAccount.h b/xcode/OLMKit/OLMAccount.h index cfa7129..a2923f9 100644 --- a/xcode/OLMKit/OLMAccount.h +++ b/xcode/OLMKit/OLMAccount.h @@ -9,7 +9,9 @@ #import #import "OLMSerializable.h" -@interface OLMAccount : NSObject +@class OLMSession; + +@interface OLMAccount : NSObject /** Creates new account */ - (instancetype) initNewAccount; @@ -18,11 +20,13 @@ - (NSDictionary*) identityKeys; /** signs message with ed25519 key for account */ -- (NSData*) signMessage:(NSData*)messageData; +- (NSString*) signMessage:(NSData*)messageData; /** Public parts of the unpublished one time keys for the account */ - (NSDictionary*) oneTimeKeys; +- (BOOL) removeOneTimeKeysForSession:(OLMSession*)session; + /** Marks the current set of one time keys as being published. */ - (void) markKeysAsPublished; diff --git a/xcode/OLMKit/OLMAccount.m b/xcode/OLMKit/OLMAccount.m index d56b6b4..4561a37 100644 --- a/xcode/OLMKit/OLMAccount.m +++ b/xcode/OLMKit/OLMAccount.m @@ -8,6 +8,8 @@ #import "OLMAccount.h" #import "OLMAccount_Private.h" +#import "OLMSession.h" +#import "OLMSession_Private.h" #import "OLMUtility.h" @import Security; @@ -34,7 +36,7 @@ return YES; } -- (instancetype) initNewAccount { +- (instancetype) init { self = [super init]; if (!self) { return nil; @@ -43,6 +45,14 @@ if (!success) { return nil; } + return self; +} + +- (instancetype) initNewAccount { + self = [self init]; + if (!self) { + return nil; + } size_t randomLength = olm_create_account_random_length(_account); NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; size_t accountResult = olm_create_account(_account, random.mutableBytes, random.length); @@ -114,5 +124,106 @@ } } +- (BOOL) removeOneTimeKeysForSession:(OLMSession *)session { + NSParameterAssert(session != nil); + if (!session) { + return nil; + } + size_t result = olm_remove_one_time_keys(self.account, session.session); + if (result == olm_error()) { + const char *error = olm_session_last_error(session.session); + NSAssert(NO, @"olm_remove_one_time_keys error: %s", error); + return NO; + } + return YES; +} + +#pragma mark OLMSerializable + +/** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ +- (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error { + self = [self init]; + if (!self) { + return nil; + } + NSParameterAssert(key.length > 0); + NSParameterAssert(serializedData.length > 0); + if (key.length == 0 || serializedData.length == 0) { + if (error) { + *error = [NSError errorWithDomain:@"org.matrix.olm" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}]; + } + return nil; + } + NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; + size_t result = olm_unpickle_account(_account, key.bytes, key.length, pickle.mutableBytes, pickle.length); + if (result == olm_error()) { + const char *olm_error = olm_account_last_error(_account); + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + if (error && errorString) { + *error = [NSError errorWithDomain:@"org.matrix.olm" code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; + } + return nil; + } + return self; +} + +/** Serializes and encrypts object data, outputs base64 blob */ +- (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error { + NSParameterAssert(key.length > 0); + size_t length = olm_pickle_account_length(_account); + NSMutableData *pickled = [NSMutableData dataWithLength:length]; + size_t result = olm_pickle_account(_account, key.bytes, key.length, pickled.mutableBytes, pickled.length); + if (result == olm_error()) { + const char *olm_error = olm_account_last_error(_account); + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + if (error && errorString) { + *error = [NSError errorWithDomain:@"org.matrix.olm" code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; + } + return nil; + } + NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; + return pickleString; +} + +#pragma mark NSSecureCoding + ++ (BOOL) supportsSecureCoding { + return YES; +} + +#pragma mark NSCoding + +- (id)initWithCoder:(NSCoder *)decoder { + NSString *version = [decoder decodeObjectOfClass:[NSString class] forKey:@"version"]; + + NSError *error = nil; + + if ([version isEqualToString:@"1"]) { + NSString *pickle = [decoder decodeObjectOfClass:[NSString class] forKey:@"pickle"]; + NSData *key = [decoder decodeObjectOfClass:[NSData class] forKey:@"key"]; + + self = [self initWithSerializedData:pickle key:key error:&error]; + } + + NSParameterAssert(error == nil); + NSParameterAssert(self != nil); + if (!self) { + return nil; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + NSData *key = [OLMUtility randomBytesOfLength:32]; + NSError *error = nil; + NSString *pickle = [self serializeDataWithKey:key error:&error]; + NSParameterAssert(pickle.length > 0 && error == nil); + + [encoder encodeObject:pickle forKey:@"pickle"]; + [encoder encodeObject:key forKey:@"key"]; + [encoder encodeObject:@"1" forKey:@"version"]; +} + @end diff --git a/xcode/OLMKit/OLMSerializable.h b/xcode/OLMKit/OLMSerializable.h index afacdaa..b4b115a 100644 --- a/xcode/OLMKit/OLMSerializable.h +++ b/xcode/OLMKit/OLMSerializable.h @@ -11,9 +11,9 @@ @protocol OLMSerializable /** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ -- (instancetype) initWithSerializedData:(NSData*)serializedData key:(NSData*)key error:(NSError**)error; +- (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error; -/** Serializes and encrypts object data */ -- (NSData*) serializeDataWithKey:(NSData*)key; +/** Serializes and encrypts object data, outputs base64 blob */ +- (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error; @end diff --git a/xcode/OLMKit/OLMSession.h b/xcode/OLMKit/OLMSession.h index 1a075e4..c209564 100644 --- a/xcode/OLMKit/OLMSession.h +++ b/xcode/OLMKit/OLMSession.h @@ -11,9 +11,7 @@ #import "OLMAccount.h" #import "OLMMessage.h" -@interface OLMSession : NSObject - -@property (nonatomic, strong) OLMAccount *account; +@interface OLMSession : NSObject - (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey; @@ -27,8 +25,6 @@ - (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage; -- (BOOL) removeOneTimeKeys; - /** UTF-8 plaintext -> base64 ciphertext */ - (OLMMessage*) encryptMessage:(NSString*)message; diff --git a/xcode/OLMKit/OLMSession.m b/xcode/OLMKit/OLMSession.m index fa7cb62..119079f 100644 --- a/xcode/OLMKit/OLMSession.m +++ b/xcode/OLMKit/OLMSession.m @@ -9,12 +9,9 @@ #import "OLMSession.h" #import "OLMUtility.h" #import "OLMAccount_Private.h" +#import "OLMSession_Private.h" @import olm; -@interface OLMSession() -@property (nonatomic) OlmSession *session; -@end - @implementation OLMSession - (void) dealloc { @@ -37,7 +34,7 @@ return YES; } -- (instancetype) initWithAccount:(OLMAccount*)account { +- (instancetype) init { self = [super init]; if (!self) { return nil; @@ -46,6 +43,18 @@ if (!success) { return nil; } + return self; +} + +- (instancetype) initWithAccount:(OLMAccount*)account { + self = [self init]; + if (!self) { + return nil; + } + NSParameterAssert(account != nil && account.account != NULL); + if (account == nil || account.account == NULL) { + return nil; + } _account = account; return self; } @@ -72,10 +81,6 @@ if (!self) { return nil; } - BOOL success = [self initializeSessionMemory]; - if (!success) { - return nil; - } NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; size_t result = olm_create_inbound_session(_session, account.account, otk.mutableBytes, oneTimeKeyMessage.length); if (result == olm_error()) { @@ -91,10 +96,6 @@ if (!self) { return nil; } - BOOL success = [self initializeSessionMemory]; - if (!success) { - return nil; - } NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding]; NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; size_t result = olm_create_inbound_session_from(_session, account.account, idKey.bytes, idKey.length, otk.mutableBytes, otk.length); @@ -143,16 +144,6 @@ return encryptedMessage; } -- (BOOL) removeOneTimeKeys { - size_t result = olm_remove_one_time_keys(_account.account, _session); - if (result == olm_error()) { - const char *error = olm_session_last_error(_session); - NSAssert(NO, @"olm_remove_one_time_keys error: %s", error); - return NO; - } - return YES; -} - - (NSString*) decryptMessage:(OLMMessage*)message { NSParameterAssert(message != nil); NSData *messageData = [message.ciphertext dataUsingEncoding:NSUTF8StringEncoding]; @@ -180,4 +171,91 @@ return plaintext; } +#pragma mark OLMSerializable + +/** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ +- (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error { + self = [self init]; + if (!self) { + return nil; + } + NSParameterAssert(key.length > 0); + NSParameterAssert(serializedData.length > 0); + if (key.length == 0 || serializedData.length == 0) { + if (error) { + *error = [NSError errorWithDomain:@"org.matrix.olm" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}]; + } + return nil; + } + NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; + size_t result = olm_unpickle_session(_session, key.bytes, key.length, pickle.mutableBytes, pickle.length); + if (result == olm_error()) { + const char *olm_error = olm_session_last_error(_session); + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + if (error && errorString) { + *error = [NSError errorWithDomain:@"org.matrix.olm" code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; + } + return nil; + } + return self; +} + +/** Serializes and encrypts object data, outputs base64 blob */ +- (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error { + NSParameterAssert(key.length > 0); + size_t length = olm_pickle_session_length(_session); + NSMutableData *pickled = [NSMutableData dataWithLength:length]; + size_t result = olm_pickle_session(_session, key.bytes, key.length, pickled.mutableBytes, pickled.length); + if (result == olm_error()) { + const char *olm_error = olm_session_last_error(_session); + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + if (error && errorString) { + *error = [NSError errorWithDomain:@"org.matrix.olm" code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; + } + return nil; + } + NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; + return pickleString; +} + +#pragma mark NSSecureCoding + ++ (BOOL) supportsSecureCoding { + return YES; +} + +#pragma mark NSCoding + +- (id)initWithCoder:(NSCoder *)decoder { + NSString *version = [decoder decodeObjectOfClass:[NSString class] forKey:@"version"]; + + NSError *error = nil; + + if ([version isEqualToString:@"1"]) { + NSString *pickle = [decoder decodeObjectOfClass:[NSString class] forKey:@"pickle"]; + NSData *key = [decoder decodeObjectOfClass:[NSData class] forKey:@"key"]; + + self = [self initWithSerializedData:pickle key:key error:&error]; + } + + NSParameterAssert(error == nil); + NSParameterAssert(self != nil); + if (!self) { + return nil; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder { + NSData *key = [OLMUtility randomBytesOfLength:32]; + NSError *error = nil; + NSString *pickle = [self serializeDataWithKey:key error:&error]; + NSParameterAssert(pickle.length > 0 && error == nil); + + [encoder encodeObject:pickle forKey:@"pickle"]; + [encoder encodeObject:key forKey:@"key"]; + [encoder encodeObject:@"1" forKey:@"version"]; +} + @end diff --git a/xcode/OLMKit/OLMSession_Private.h b/xcode/OLMKit/OLMSession_Private.h new file mode 100644 index 0000000..d906b14 --- /dev/null +++ b/xcode/OLMKit/OLMSession_Private.h @@ -0,0 +1,16 @@ +// +// OLMSession_Private.h +// olm +// +// Created by Chris Ballinger on 4/13/16. +// +// + +@import olm; + +@interface OLMSession() + +@property (nonatomic) OlmSession *session; +@property (nonatomic, strong) OLMAccount *account; + +@end \ No newline at end of file -- cgit v1.2.3