Initial test passing
This commit is contained in:
parent
719eb543a8
commit
f505113fb7
8 changed files with 227 additions and 34 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
|
||||
@interface OLMUtility : NSObject
|
||||
|
||||
+ (NSData*) randomBytesOfLength:(NSUInteger)length;
|
||||
+ (NSMutableData*) randomBytesOfLength:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue