Initial test passing

This commit is contained in:
Chris Ballinger 2016-04-09 14:00:30 -07:00
parent 719eb543a8
commit f505113fb7
8 changed files with 227 additions and 34 deletions

View file

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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -10,6 +10,6 @@
@interface OLMUtility : NSObject
+ (NSData*) randomBytesOfLength:(NSUInteger)length;
+ (NSMutableData*) randomBytesOfLength:(NSUInteger)length;
@end

View file

@ -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

View file

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