diff options
author | Chris Ballinger <chrisballinger@gmail.com> | 2016-04-09 14:00:30 -0700 |
---|---|---|
committer | Chris Ballinger <chrisballinger@gmail.com> | 2016-04-09 14:00:30 -0700 |
commit | f505113fb7a6d61015ad8050b3fb4e26df029150 (patch) | |
tree | 0bdaba8f07189f3adc828c2895ce7429d9b3cb3e | |
parent | 719eb543a8d08c4f536ea7933ffb3af0a8553e87 (diff) |
Initial test passing
-rw-r--r-- | xcode/OLMKit/OLMAccount.m | 6 | ||||
-rw-r--r-- | xcode/OLMKit/OLMMessage.h | 16 | ||||
-rw-r--r-- | xcode/OLMKit/OLMMessage.m | 11 | ||||
-rw-r--r-- | xcode/OLMKit/OLMSession.h | 23 | ||||
-rw-r--r-- | xcode/OLMKit/OLMSession.m | 165 | ||||
-rw-r--r-- | xcode/OLMKit/OLMUtility.h | 2 | ||||
-rw-r--r-- | xcode/OLMKit/OLMUtility.m | 13 | ||||
-rw-r--r-- | xcode/OLMKitTests/OLMKitTests.m | 25 |
8 files changed, 227 insertions, 34 deletions
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 <Foundation/Foundation.h> -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 <Foundation/Foundation.h> #import "OLMSerializable.h" #import "OLMAccount.h" +#import "OLMMessage.h" @interface OLMSession : NSObject <OLMSerializable> -- (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 diff --git a/xcode/OLMKitTests/OLMKitTests.m b/xcode/OLMKitTests/OLMKitTests.m index 944d11c..7075057 100644 --- a/xcode/OLMKitTests/OLMKitTests.m +++ b/xcode/OLMKitTests/OLMKitTests.m @@ -31,10 +31,27 @@ OLMAccount *alice = [[OLMAccount alloc] initNewAccount]; OLMAccount *bob = [[OLMAccount alloc] initNewAccount]; [bob generateOneTimeKeys:5]; - NSDictionary *identityKeys = bob.identityKeys; - NSDictionary *oneTimeKeys = bob.oneTimeKeys; - NSParameterAssert(identityKeys != nil); - NSParameterAssert(oneTimeKeys != nil); + NSDictionary *bobIdKeys = bob.identityKeys; + NSString *bobIdKey = bobIdKeys[@"curve25519"]; + NSDictionary *bobOneTimeKeys = bob.oneTimeKeys; + NSParameterAssert(bobIdKey != nil); + NSParameterAssert(bobOneTimeKeys != nil); + __block NSString *bobOneTimeKey = nil; + NSDictionary *bobOtkCurve25519 = bobOneTimeKeys[@"curve25519"]; + [bobOtkCurve25519 enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + bobOneTimeKey = obj; + }]; + XCTAssert([bobOneTimeKey isKindOfClass:[NSString class]]); + + OLMSession *aliceSession = [[OLMSession alloc] initOutboundSessionWithAccount:alice theirIdentityKey:bobIdKey theirOneTimeKey:bobOneTimeKey]; + NSString *message = @"Hello!"; + OLMMessage *aliceToBobMsg = [aliceSession encryptMessage:message]; + + OLMSession *bobSession = [[OLMSession alloc] initInboundSessionWithAccount:bob oneTimeKeyMessage:aliceToBobMsg.ciphertext]; + NSString *plaintext = [bobSession decryptMessage:aliceToBobMsg]; + XCTAssertEqualObjects(message, plaintext); + BOOL success = [bobSession removeOneTimeKeys]; + XCTAssertTrue(success); } |