aboutsummaryrefslogtreecommitdiff
path: root/xcode/OLMKit
diff options
context:
space:
mode:
authorRichard van der Hoff <github@rvanderhoff.org.uk>2016-12-22 14:43:01 +0000
committerGitHub <noreply@github.com>2016-12-22 14:43:01 +0000
commit90b36130532d2d8040b39e310d8d0d5ee22aea0a (patch)
tree26eabdea9bc04077b1621deb34b5d0cbe669dedd /xcode/OLMKit
parentfb91b1f18269c5b681c972dc4448858db0966c3a (diff)
parent46ad79517ec8e005bd2d1de767d3cd59ec038fe2 (diff)
Merge pull request #36 from matrix-org/manuroe/olmkit
OLMKit
Diffstat (limited to 'xcode/OLMKit')
-rw-r--r--xcode/OLMKit/Info.plist26
-rw-r--r--xcode/OLMKit/OLMAccount.h51
-rw-r--r--xcode/OLMKit/OLMAccount.m265
-rw-r--r--xcode/OLMKit/OLMAccount_Private.h25
-rw-r--r--xcode/OLMKit/OLMInboundGroupSession.h30
-rw-r--r--xcode/OLMKit/OLMInboundGroupSession.m244
-rw-r--r--xcode/OLMKit/OLMKit.h31
-rw-r--r--xcode/OLMKit/OLMKit.m29
-rw-r--r--xcode/OLMKit/OLMMessage.h38
-rw-r--r--xcode/OLMKit/OLMMessage.m34
-rw-r--r--xcode/OLMKit/OLMOutboundGroupSession.h32
-rw-r--r--xcode/OLMKit/OLMOutboundGroupSession.m220
-rw-r--r--xcode/OLMKit/OLMSerializable.h29
-rw-r--r--xcode/OLMKit/OLMSession.h44
-rw-r--r--xcode/OLMKit/OLMSession.m381
-rw-r--r--xcode/OLMKit/OLMSession_Private.h26
-rw-r--r--xcode/OLMKit/OLMUtility.h49
-rw-r--r--xcode/OLMKit/OLMUtility.m121
18 files changed, 1675 insertions, 0 deletions
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 @@
+<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</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>
diff --git a/xcode/OLMKit/OLMAccount.h b/xcode/OLMKit/OLMAccount.h
new file mode 100644
index 0000000..c8d65cd
--- /dev/null
+++ b/xcode/OLMKit/OLMAccount.h
@@ -0,0 +1,51 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "OLMSerializable.h"
+
+@class OLMSession;
+
+@interface OLMAccount : NSObject <OLMSerializable, NSSecureCoding>
+
+/** Creates new account */
+- (instancetype) initNewAccount;
+
+/** public identity keys. base64 encoded in "curve25519" and "ed25519" keys */
+- (NSDictionary*) identityKeys;
+
+/** signs message with ed25519 key for account */
+- (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) markOneTimeKeysAsPublished;
+
+/** 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..af1e308
--- /dev/null
+++ b/xcode/OLMKit/OLMAccount.m
@@ -0,0 +1,265 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMAccount.h"
+#import "OLMAccount_Private.h"
+#import "OLMSession.h"
+#import "OLMSession_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) init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+ BOOL success = [self initializeAccountMemory];
+ 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);
+ [random resetBytesInRange:NSMakeRange(0, random.length)];
+ if (accountResult == olm_error()) {
+ const char *error = olm_account_last_error(_account);
+ NSLog(@"error creating account: %s", error);
+ return nil;
+ }
+ return self;
+}
+
+- (NSUInteger) 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;
+}
+
+- (NSString *)signMessage:(NSData *)messageData {
+ size_t signatureLength = olm_account_signature_length(_account);
+ uint8_t *signatureBytes = malloc(signatureLength);
+ if (!signatureBytes) {
+ return nil;
+ }
+
+ size_t result = olm_account_sign(_account, messageData.bytes, messageData.length, signatureBytes, signatureLength);
+ if (result == olm_error()) {
+ const char *error = olm_account_last_error(_account);
+ NSLog(@"error signing message: %s", error);
+ free(signatureBytes);
+ return nil;
+ }
+
+ NSData *signatureData = [NSData dataWithBytesNoCopy:signatureBytes length:signatureLength freeWhenDone:YES];
+ return [[NSString alloc] initWithData:signatureData encoding:NSUTF8StringEncoding];
+}
+
+- (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);
+ NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength];
+ size_t result = olm_account_generate_one_time_keys(_account, numberOfKeys, random.mutableBytes, random.length);
+ [random resetBytesInRange:NSMakeRange(0, random.length)];
+ if (result == olm_error()) {
+ const char *error = olm_account_last_error(_account);
+ NSLog(@"error generating keys: %s", error);
+ }
+}
+
+- (BOOL) removeOneTimeKeysForSession:(OLMSession *)session {
+ NSParameterAssert(session != nil);
+ if (!session) {
+ return NO;
+ }
+ size_t result = olm_remove_one_time_keys(self.account, session.session);
+ if (result == olm_error()) {
+ const char *error = olm_account_last_error(_account);
+ NSLog(@"olm_remove_one_time_keys error: %s", error);
+ return NO;
+ }
+ return YES;
+}
+
+- (void)markOneTimeKeysAsPublished
+{
+ olm_account_mark_keys_as_published(self.account);
+}
+
+#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:OLMErrorDomain 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:OLMErrorDomain 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:OLMErrorDomain 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/OLMAccount_Private.h b/xcode/OLMKit/OLMAccount_Private.h
new file mode 100644
index 0000000..313ab71
--- /dev/null
+++ b/xcode/OLMKit/OLMAccount_Private.h
@@ -0,0 +1,25 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include "olm/olm.h"
+
+@interface OLMAccount()
+
+@property (nonatomic) OlmAccount *account;
+
+@end
diff --git a/xcode/OLMKit/OLMInboundGroupSession.h b/xcode/OLMKit/OLMInboundGroupSession.h
new file mode 100644
index 0000000..ede68e3
--- /dev/null
+++ b/xcode/OLMKit/OLMInboundGroupSession.h
@@ -0,0 +1,30 @@
+/*
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "OLMSerializable.h"
+
+@interface OLMInboundGroupSession : NSObject <OLMSerializable, NSSecureCoding>
+
+- (instancetype) initInboundGroupSessionWithSessionKey:(NSString*)sessionKey error:(NSError**)error;
+
+- (NSString*)sessionIdentifier;
+
+/** base64 ciphertext -> UTF-8 plaintext */
+- (NSString*)decryptMessage:(NSString*)message messageIndex:(NSUInteger*)messageIndex error:(NSError**)error;
+
+@end
diff --git a/xcode/OLMKit/OLMInboundGroupSession.m b/xcode/OLMKit/OLMInboundGroupSession.m
new file mode 100644
index 0000000..6ef51c3
--- /dev/null
+++ b/xcode/OLMKit/OLMInboundGroupSession.m
@@ -0,0 +1,244 @@
+/*
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMInboundGroupSession.h"
+
+#import "OLMUtility.h"
+#include "olm/olm.h"
+
+@interface OLMInboundGroupSession ()
+{
+ OlmInboundGroupSession *session;
+}
+@end
+
+
+@implementation OLMInboundGroupSession
+
+- (void)dealloc {
+ olm_clear_inbound_group_session(session);
+ free(session);
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self)
+ {
+ session = malloc(olm_inbound_group_session_size());
+ if (session) {
+ session = olm_inbound_group_session(session);
+ }
+
+ if (!session) {
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (instancetype)initInboundGroupSessionWithSessionKey:(NSString *)sessionKey error:(NSError**)error {
+ self = [self init];
+ if (self) {
+ NSData *sessionKeyData = [sessionKey dataUsingEncoding:NSUTF8StringEncoding];
+ size_t result = olm_init_inbound_group_session(session, sessionKeyData.bytes, sessionKeyData.length);
+ if (result == olm_error()) {
+ const char *olm_error = olm_inbound_group_session_last_error(session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_init_inbound_group_session error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_init_inbound_group_session error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (NSString *)sessionIdentifier {
+ size_t length = olm_inbound_group_session_id_length(session);
+ NSMutableData *idData = [NSMutableData dataWithLength:length];
+ if (!idData) {
+ return nil;
+ }
+ size_t result = olm_inbound_group_session_id(session, idData.mutableBytes, idData.length);
+ if (result == olm_error()) {
+ const char *error = olm_inbound_group_session_last_error(session);
+ NSLog(@"olm_inbound_group_session_id error: %s", error);
+ return nil;
+ }
+ NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding];
+ return idString;
+}
+
+- (NSString *)decryptMessage:(NSString *)message messageIndex:(NSUInteger*)messageIndex error:(NSError**)error
+{
+ NSParameterAssert(message != nil);
+ NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];
+ if (!messageData) {
+ return nil;
+ }
+ NSMutableData *mutMessage = messageData.mutableCopy;
+ size_t maxPlaintextLength = olm_group_decrypt_max_plaintext_length(session, mutMessage.mutableBytes, mutMessage.length);
+ if (maxPlaintextLength == olm_error()) {
+ const char *olm_error = olm_inbound_group_session_last_error(session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_group_decrypt_max_plaintext_length error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_group_decrypt_max_plaintext_length error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ // message buffer is destroyed by olm_group_decrypt_max_plaintext_length
+ mutMessage = messageData.mutableCopy;
+ NSMutableData *plaintextData = [NSMutableData dataWithLength:maxPlaintextLength];
+
+ uint32_t message_index;
+ size_t plaintextLength = olm_group_decrypt(session, mutMessage.mutableBytes, mutMessage.length, plaintextData.mutableBytes, plaintextData.length, &message_index);
+ if (plaintextLength == olm_error()) {
+ const char *olm_error = olm_inbound_group_session_last_error(session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_group_decrypt error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_group_decrypt error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ plaintextData.length = plaintextLength;
+ NSString *plaintext = [[NSString alloc] initWithData:plaintextData encoding:NSUTF8StringEncoding];
+ [plaintextData resetBytesInRange:NSMakeRange(0, plaintextData.length)];
+
+ if (messageIndex)
+ {
+ *messageIndex = message_index;
+ }
+
+ 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 *__autoreleasing *)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:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}];
+ }
+ return nil;
+ }
+ NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy;
+ size_t result = olm_unpickle_inbound_group_session(session, key.bytes, key.length, pickle.mutableBytes, pickle.length);
+ if (result == olm_error()) {
+ const char *olm_error = olm_inbound_group_session_last_error(session);
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ if (error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain 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_inbound_group_session_length(session);
+ NSMutableData *pickled = [NSMutableData dataWithLength:length];
+ size_t result = olm_pickle_inbound_group_session(session, key.bytes, key.length, pickled.mutableBytes, pickled.length);
+ if (result == olm_error()) {
+ const char *olm_error = olm_inbound_group_session_last_error(session);
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ if (error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain 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/OLMKit.h b/xcode/OLMKit/OLMKit.h
new file mode 100644
index 0000000..34db111
--- /dev/null
+++ b/xcode/OLMKit/OLMKit.h
@@ -0,0 +1,31 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <UIKit/UIKit.h>
+
+//! Project version string for OLMKit, the same as libolm.
+NSString *OLMKitVersionString();
+
+// In this header, you should import all the public headers of your framework using statements like #import <OLMKit/PublicHeader.h>
+
+#import <OLMKit/OLMAccount.h>
+#import <OLMKit/OLMSession.h>
+#import <OLMKit/OLMMessage.h>
+#import <OLMKit/OLMUtility.h>
+#import <OLMKit/OLMInboundGroupSession.h>
+#import <OLMKit/OLMOutboundGroupSession.h>
diff --git a/xcode/OLMKit/OLMKit.m b/xcode/OLMKit/OLMKit.m
new file mode 100644
index 0000000..e7bfd25
--- /dev/null
+++ b/xcode/OLMKit/OLMKit.m
@@ -0,0 +1,29 @@
+/*
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMKit.h"
+
+#include "olm/olm.h"
+
+NSString *OLMKitVersionString()
+{
+ uint8_t major, minor, patch;
+
+ olm_get_library_version(&major, &minor, &patch);
+
+ return [NSString stringWithFormat:@"%tu.%tu.%tu", major, minor, patch];
+}
diff --git a/xcode/OLMKit/OLMMessage.h b/xcode/OLMKit/OLMMessage.h
new file mode 100644
index 0000000..b6e8c8f
--- /dev/null
+++ b/xcode/OLMKit/OLMMessage.h
@@ -0,0 +1,38 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+/*
+ 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, copy, readonly, nonnull) NSString *ciphertext;
+@property (readonly) OLMMessageType type;
+
+- (nullable instancetype) initWithCiphertext:(nonnull NSString*)ciphertext type:(OLMMessageType)type;
+
+@end
diff --git a/xcode/OLMKit/OLMMessage.m b/xcode/OLMKit/OLMMessage.m
new file mode 100644
index 0000000..949f834
--- /dev/null
+++ b/xcode/OLMKit/OLMMessage.m
@@ -0,0 +1,34 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMMessage.h"
+
+@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/OLMOutboundGroupSession.h b/xcode/OLMKit/OLMOutboundGroupSession.h
new file mode 100644
index 0000000..c979b61
--- /dev/null
+++ b/xcode/OLMKit/OLMOutboundGroupSession.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "OLMSerializable.h"
+
+@interface OLMOutboundGroupSession : NSObject <OLMSerializable, NSSecureCoding>
+
+- (instancetype) initOutboundGroupSession;
+
+- (NSString*)sessionIdentifier;
+- (NSUInteger)messageIndex;
+- (NSString*)sessionKey;
+
+/** UTF-8 plaintext -> base64 ciphertext */
+- (NSString*)encryptMessage:(NSString*)message error:(NSError**)error;
+
+@end
diff --git a/xcode/OLMKit/OLMOutboundGroupSession.m b/xcode/OLMKit/OLMOutboundGroupSession.m
new file mode 100644
index 0000000..a3421fd
--- /dev/null
+++ b/xcode/OLMKit/OLMOutboundGroupSession.m
@@ -0,0 +1,220 @@
+/*
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMOutboundGroupSession.h"
+
+#import "OLMUtility.h"
+#include "olm/olm.h"
+
+@interface OLMOutboundGroupSession ()
+{
+ OlmOutboundGroupSession *session;
+}
+@end
+
+@implementation OLMOutboundGroupSession
+
+- (void)dealloc {
+ olm_clear_outbound_group_session(session);
+ free(session);
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self)
+ {
+ session = malloc(olm_outbound_group_session_size());
+ if (session) {
+ session = olm_outbound_group_session(session);
+ }
+
+ if (!session) {
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (instancetype)initOutboundGroupSession {
+ self = [self init];
+ if (self) {
+ NSMutableData *random = [OLMUtility randomBytesOfLength:olm_init_outbound_group_session_random_length(session)];
+
+ size_t result = olm_init_outbound_group_session(session, random.mutableBytes, random.length);
+ [random resetBytesInRange:NSMakeRange(0, random.length)];
+ if (result == olm_error()) {
+ const char *error = olm_outbound_group_session_last_error(session);
+ NSLog(@"olm_init_outbound_group_session error: %s", error);
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (NSString *)sessionIdentifier {
+ size_t length = olm_outbound_group_session_id_length(session);
+ NSMutableData *idData = [NSMutableData dataWithLength:length];
+ if (!idData) {
+ return nil;
+ }
+ size_t result = olm_outbound_group_session_id(session, idData.mutableBytes, idData.length);
+ if (result == olm_error()) {
+ const char *error = olm_outbound_group_session_last_error(session);
+ NSLog(@"olm_outbound_group_session_id error: %s", error);
+ return nil;
+ }
+ NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding];
+ return idString;
+}
+
+- (NSUInteger)messageIndex {
+ return olm_outbound_group_session_message_index(session);
+}
+
+- (NSString *)sessionKey {
+ size_t length = olm_outbound_group_session_key_length(session);
+ NSMutableData *sessionKeyData = [NSMutableData dataWithLength:length];
+ if (!sessionKeyData) {
+ return nil;
+ }
+ size_t result = olm_outbound_group_session_key(session, sessionKeyData.mutableBytes, sessionKeyData.length);
+ if (result == olm_error()) {
+ const char *error = olm_outbound_group_session_last_error(session);
+ NSLog(@"olm_outbound_group_session_key error: %s", error);
+ return nil;
+ }
+ NSString *sessionKey = [[NSString alloc] initWithData:sessionKeyData encoding:NSUTF8StringEncoding];
+ [sessionKeyData resetBytesInRange:NSMakeRange(0, sessionKeyData.length)];
+ return sessionKey;
+}
+
+- (NSString *)encryptMessage:(NSString *)message error:(NSError**)error {
+ NSData *plaintextData = [message dataUsingEncoding:NSUTF8StringEncoding];
+ size_t ciphertextLength = olm_group_encrypt_message_length(session, plaintextData.length);
+ NSMutableData *ciphertext = [NSMutableData dataWithLength:ciphertextLength];
+ if (!ciphertext) {
+ return nil;
+ }
+ size_t result = olm_group_encrypt(session, plaintextData.bytes, plaintextData.length, ciphertext.mutableBytes, ciphertext.length);
+ if (result == olm_error()) {
+ const char *olm_error = olm_outbound_group_session_last_error(session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_group_encrypt error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_group_encrypt error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ return [[NSString alloc] initWithData:ciphertext encoding:NSUTF8StringEncoding];
+}
+
+#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 *__autoreleasing *)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:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}];
+ }
+ return nil;
+ }
+ NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy;
+ size_t result = olm_unpickle_outbound_group_session(session, key.bytes, key.length, pickle.mutableBytes, pickle.length);
+ if (result == olm_error()) {
+ const char *olm_error = olm_outbound_group_session_last_error(session);
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ if (error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain 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_outbound_group_session_length(session);
+ NSMutableData *pickled = [NSMutableData dataWithLength:length];
+ size_t result = olm_pickle_outbound_group_session(session, key.bytes, key.length, pickled.mutableBytes, pickled.length);
+ if (result == olm_error()) {
+ const char *olm_error = olm_outbound_group_session_last_error(session);
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ if (error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain 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
new file mode 100644
index 0000000..e929903
--- /dev/null
+++ b/xcode/OLMKit/OLMSerializable.h
@@ -0,0 +1,29 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+@protocol OLMSerializable <NSObject>
+
+/** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */
+- (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error;
+
+/** 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
new file mode 100644
index 0000000..0446f98
--- /dev/null
+++ b/xcode/OLMKit/OLMSession.h
@@ -0,0 +1,44 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "OLMSerializable.h"
+#import "OLMAccount.h"
+#import "OLMMessage.h"
+
+@interface OLMSession : NSObject <OLMSerializable, NSSecureCoding>
+
+- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey error:(NSError**)error;
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error;
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error;
+
+- (NSString*) sessionIdentifier;
+
+- (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage;
+
+- (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage;
+
+/** UTF-8 plaintext -> base64 ciphertext */
+- (OLMMessage*) encryptMessage:(NSString*)message error:(NSError**)error;
+
+/** base64 ciphertext -> UTF-8 plaintext */
+- (NSString*) decryptMessage:(OLMMessage*)message error:(NSError**)error;
+
+@end
diff --git a/xcode/OLMKit/OLMSession.m b/xcode/OLMKit/OLMSession.m
new file mode 100644
index 0000000..8c29113
--- /dev/null
+++ b/xcode/OLMKit/OLMSession.m
@@ -0,0 +1,381 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMSession.h"
+#import "OLMUtility.h"
+#import "OLMAccount_Private.h"
+#import "OLMSession_Private.h"
+#include "olm/olm.h"
+
+@implementation OLMSession
+
+- (void) dealloc {
+ olm_clear_session(_session);
+ free(_session);
+}
+
+- (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) init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+ BOOL success = [self initializeSessionMemory];
+ 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;
+}
+
+- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey error:(NSError**)error {
+ 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);
+ [random resetBytesInRange:NSMakeRange(0, random.length)];
+ if (result == olm_error()) {
+ const char *olm_error = olm_session_last_error(_session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_create_outbound_session error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_create_outbound_session error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ return self;
+}
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error {
+ self = [self initWithAccount:account];
+ if (!self) {
+ 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 *olm_error = olm_session_last_error(_session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_create_inbound_session error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_create_inbound_session error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ return self;
+}
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error {
+ self = [self initWithAccount:account];
+ if (!self) {
+ 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 *olm_error = olm_session_last_error(_session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_create_inbound_session_from error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_create_inbound_session_from error: %@", errorString]
+ }];
+ }
+
+ 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);
+ NSLog(@"olm_session_id error: %s", error);
+ return nil;
+ }
+ NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding];
+ return idString;
+}
+
+- (BOOL)matchesInboundSession:(NSString *)oneTimeKeyMessage {
+ NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]];
+
+ size_t result = olm_matches_inbound_session(_session, otk.mutableBytes, otk.length);
+ if (result == 1) {
+ return YES;
+ }
+ else {
+ if (result == olm_error()) {
+ const char *error = olm_session_last_error(_session);
+ NSLog(@"olm_matches_inbound_session error: %s", error);
+ }
+ return NO;
+ }
+}
+
+- (BOOL)matchesInboundSessionFrom:(NSString *)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage {
+ NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding];
+ NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]];
+
+ size_t result = olm_matches_inbound_session_from(_session,
+ idKey.bytes, idKey.length,
+ otk.mutableBytes, otk.length);
+ if (result == 1) {
+ return YES;
+ }
+ else {
+ if (result == olm_error()) {
+ const char *error = olm_session_last_error(_session);
+ NSLog(@"olm_matches_inbound_session error: %s", error);
+ }
+ return NO;
+ }
+}
+
+- (OLMMessage*) encryptMessage:(NSString*)message error:(NSError**)error {
+ 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);
+ [random resetBytesInRange:NSMakeRange(0, random.length)];
+ if (result == olm_error()) {
+ const char *olm_error = olm_session_last_error(_session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_encrypt error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_encrypt error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ NSString *ciphertextString = [[NSString alloc] initWithData:ciphertext encoding:NSUTF8StringEncoding];
+ OLMMessage *encryptedMessage = [[OLMMessage alloc] initWithCiphertext:ciphertextString type:messageType];
+ return encryptedMessage;
+}
+
+- (NSString*) decryptMessage:(OLMMessage*)message error:(NSError**)error {
+ 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 *olm_error = olm_session_last_error(_session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_decrypt_max_plaintext_length error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_decrypt_max_plaintext_length error: %@", errorString]
+ }];
+ }
+
+ 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 *olm_error = olm_session_last_error(_session);
+
+ NSString *errorString = [NSString stringWithUTF8String:olm_error];
+ NSLog(@"olm_decrypt error: %@", errorString);
+
+ if (error && olm_error && errorString) {
+ *error = [NSError errorWithDomain:OLMErrorDomain
+ code:0
+ userInfo:@{
+ NSLocalizedDescriptionKey: errorString,
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_decrypt error: %@", errorString]
+ }];
+ }
+
+ return nil;
+ }
+ plaintextData.length = plaintextLength;
+ NSString *plaintext = [[NSString alloc] initWithData:plaintextData encoding:NSUTF8StringEncoding];
+ [plaintextData resetBytesInRange:NSMakeRange(0, plaintextData.length)];
+ 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:OLMErrorDomain 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:OLMErrorDomain 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:OLMErrorDomain 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..28ba5e1
--- /dev/null
+++ b/xcode/OLMKit/OLMSession_Private.h
@@ -0,0 +1,26 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include "olm/olm.h"
+
+@interface OLMSession()
+
+@property (nonatomic) OlmSession *session;
+@property (nonatomic, strong) OLMAccount *account;
+
+@end
diff --git a/xcode/OLMKit/OLMUtility.h b/xcode/OLMKit/OLMUtility.h
new file mode 100644
index 0000000..22e9724
--- /dev/null
+++ b/xcode/OLMKit/OLMUtility.h
@@ -0,0 +1,49 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+FOUNDATION_EXPORT NSString *const OLMErrorDomain;
+
+@interface OLMUtility : NSObject
+
+/**
+ Calculate the SHA-256 hash of the input and encodes it as base64.
+
+ @param message the message to hash.
+ @return the base64-encoded hash value.
+ */
+- (NSString*)sha256:(NSData*)message;
+
+/**
+ Verify an ed25519 signature.
+
+ @param signature the base64-encoded signature to be checked.
+ @param key the ed25519 key.
+ @param message the message which was signed.
+ @param the result error if there is a problem with the verification.
+ If the key was too small then the message will be "OLM.INVALID_BASE64".
+ If the signature was invalid then the message will be "OLM.BAD_MESSAGE_MAC".
+
+ @return YES if valid.
+ */
+- (BOOL)verifyEd25519Signature:(NSString*)signature key:(NSString*)key message:(NSData*)message error:(NSError**)error;
+
++ (NSMutableData*) randomBytesOfLength:(NSUInteger)length;
+
+@end
diff --git a/xcode/OLMKit/OLMUtility.m b/xcode/OLMKit/OLMUtility.m
new file mode 100644
index 0000000..936785a
--- /dev/null
+++ b/xcode/OLMKit/OLMUtility.m
@@ -0,0 +1,121 @@
+/*
+ Copyright 2016 Chris Ballinger
+ Copyright 2016 OpenMarket Ltd
+ Copyright 2016 Vector Creations Ltd
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#import "OLMUtility.h"
+
+#include "olm/olm.h"
+
+NSString *const OLMErrorDomain = @"org.matrix.olm";
+
+@interface OLMUtility()
+
+@property (nonatomic) OlmUtility *utility;
+
+@end
+
+@implementation OLMUtility
+
+- (void) dealloc {
+ olm_clear_utility(_utility);
+ free(_utility);
+}
+
+- (BOOL) initializeUtilityMemory {
+ size_t utilitySize = olm_utility_size();
+ _utility = malloc(utilitySize);
+ NSParameterAssert(_utility != nil);
+ if (!_utility) {
+ return NO;
+ }
+ _utility = olm_utility(_utility);
+ NSParameterAssert(_utility != nil);
+ if (!_utility) {
+ return NO;
+ }
+ return YES;
+}
+
+- (instancetype) init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+ BOOL success = [self initializeUtilityMemory];
+ if (!success) {
+ return nil;
+ }
+ return self;
+}
+
+- (NSString *)sha256:(NSData *)message {
+ size_t length = olm_sha256_length(_utility);
+
+ NSMutableData *shaData = [NSMutableData dataWithLength:length];
+ if (!shaData) {
+ return nil;
+ }
+
+ size_t result = olm_sha256(_utility, message.bytes, message.length, shaData.mutableBytes, shaData.length);
+ if (result == olm_error()) {
+ const char *error = olm_utility_last_error(_utility);
+ NSLog(@"olm_sha256 error: %s", error);
+ return nil;
+ }
+
+ NSString *sha = [[NSString alloc] initWithData:shaData encoding:NSUTF8StringEncoding];
+ return sha;
+}
+
+- (BOOL)verifyEd25519Signature:(NSString*)signature key:(NSString*)key message:(NSData*)message error:(NSError**)error {
+
+ NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
+ NSData *signatureData = [signature dataUsingEncoding:NSUTF8StringEncoding];
+
+ size_t result = olm_ed25519_verify(_utility,
+ keyData.bytes, keyData.length,
+ message.bytes, message.length,
+ (void*)signatureData.bytes, signatureData.length
+ );
+
+ if (result == olm_error()) {
+ if (error) {
+ NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: [NSString stringWithUTF8String:olm_utility_last_error(_utility)]};
+
+ // @TODO
+ *error = [[NSError alloc] initWithDomain:@"OLMKitErrorDomain" code:0 userInfo:userInfo];
+ }
+ return NO;
+ }
+ else {
+ return YES;
+ }
+}
+
++ (NSMutableData*) randomBytesOfLength:(NSUInteger)length {
+ NSMutableData *randomData = [NSMutableData dataWithLength:length];
+ if (!randomData) {
+ return nil;
+ }
+ int result = SecRandomCopyBytes(kSecRandomDefault, randomData.length, randomData.mutableBytes);
+ if (result != 0) {
+ return nil;
+ }
+ return randomData;
+}
+
+@end