diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/include/olm/pk.h b/include/olm/pk.h index 4278fca..c46baa0 100644 --- a/include/olm/pk.h +++ b/include/olm/pk.h @@ -114,7 +114,7 @@ size_t olm_clear_pk_decryption( /** Get the number of bytes required to store an olm private key */ -size_t olm_pk_private_key_length(); +size_t olm_pk_private_key_length(void); /** DEPRECATED: Use olm_pk_private_key_length() */ diff --git a/xcode/OLMKit.xcodeproj/project.pbxproj b/xcode/OLMKit.xcodeproj/project.pbxproj index ded14f1..7ea3d5b 100644 --- a/xcode/OLMKit.xcodeproj/project.pbxproj +++ b/xcode/OLMKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3244277D2175EF700023EDF1 /* OLMKitPkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3244277C2175EF700023EDF1 /* OLMKitPkTests.m */; }; 3274F6021D9A633A005282E4 /* OLMKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3274F5F81D9A633A005282E4 /* OLMKit.framework */; }; 3274F6071D9A633A005282E4 /* OLMKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3274F6061D9A633A005282E4 /* OLMKitTests.m */; }; 3274F6131D9A698E005282E4 /* OLMKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 3274F6121D9A698E005282E4 /* OLMKit.h */; }; @@ -27,6 +28,7 @@ /* Begin PBXFileReference section */ 1B226B371526F2782C9D6372 /* Pods-OLMKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OLMKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-OLMKit/Pods-OLMKit.release.xcconfig"; sourceTree = ""; }; + 3244277C2175EF700023EDF1 /* OLMKitPkTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OLMKitPkTests.m; sourceTree = ""; }; 3274F5F81D9A633A005282E4 /* OLMKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OLMKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3274F5FC1D9A633A005282E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3274F6011D9A633A005282E4 /* OLMKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OLMKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -105,6 +107,7 @@ 3274F6051D9A633A005282E4 /* OLMKitTests */ = { isa = PBXGroup; children = ( + 3244277C2175EF700023EDF1 /* OLMKitPkTests.m */, 3274F6061D9A633A005282E4 /* OLMKitTests.m */, 32A151301DABDD4300400192 /* OLMKitGroupTests.m */, 3274F6081D9A633A005282E4 /* Info.plist */, @@ -279,6 +282,7 @@ buildActionMask = 2147483647; files = ( 3274F6071D9A633A005282E4 /* OLMKitTests.m in Sources */, + 3244277D2175EF700023EDF1 /* OLMKitPkTests.m in Sources */, 32A151311DABDD4300400192 /* OLMKitGroupTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/xcode/OLMKit/OLMAccount.m b/xcode/OLMKit/OLMAccount.m index 058b389..9e48c2d 100644 --- a/xcode/OLMKit/OLMAccount.m +++ b/xcode/OLMKit/OLMAccount.m @@ -193,6 +193,7 @@ } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_account(_account, key.bytes, key.length, pickle.mutableBytes, pickle.length); + [pickle resetBytesInRange:NSMakeRange(0, pickle.length)]; if (result == olm_error()) { const char *olm_error = olm_account_last_error(_account); NSString *errorString = [NSString stringWithUTF8String:olm_error]; @@ -219,6 +220,7 @@ return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; + [pickled resetBytesInRange:NSMakeRange(0, pickled.length)]; return pickleString; } diff --git a/xcode/OLMKit/OLMInboundGroupSession.m b/xcode/OLMKit/OLMInboundGroupSession.m index 68750ec..9e57741 100644 --- a/xcode/OLMKit/OLMInboundGroupSession.m +++ b/xcode/OLMKit/OLMInboundGroupSession.m @@ -227,6 +227,7 @@ } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_inbound_group_session(session, key.bytes, key.length, pickle.mutableBytes, pickle.length); + [pickle resetBytesInRange:NSMakeRange(0, pickle.length)]; if (result == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; @@ -253,6 +254,7 @@ return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; + [pickled resetBytesInRange:NSMakeRange(0, pickled.length)]; return pickleString; } diff --git a/xcode/OLMKit/OLMKit.h b/xcode/OLMKit/OLMKit.h index 455d11b..6f79399 100644 --- a/xcode/OLMKit/OLMKit.h +++ b/xcode/OLMKit/OLMKit.h @@ -26,6 +26,8 @@ #import #import #import +#import +#import @interface OLMKit : NSObject diff --git a/xcode/OLMKit/OLMOutboundGroupSession.m b/xcode/OLMKit/OLMOutboundGroupSession.m index a3421fd..a0a7cc6 100644 --- a/xcode/OLMKit/OLMOutboundGroupSession.m +++ b/xcode/OLMKit/OLMOutboundGroupSession.m @@ -148,6 +148,7 @@ } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_outbound_group_session(session, key.bytes, key.length, pickle.mutableBytes, pickle.length); + [pickle resetBytesInRange:NSMakeRange(0, pickle.length)]; if (result == olm_error()) { const char *olm_error = olm_outbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; @@ -174,6 +175,7 @@ return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; + [pickled resetBytesInRange:NSMakeRange(0, pickled.length)]; return pickleString; } diff --git a/xcode/OLMKit/OLMPKEncryption.h b/xcode/OLMKit/OLMPKEncryption.h new file mode 100644 index 0000000..a55d5bc --- /dev/null +++ b/xcode/OLMKit/OLMPKEncryption.h @@ -0,0 +1,42 @@ +/* + Copyright 2018 New Vector 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.0OLMPKEncryption + 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 + +#import "OLMPkMessage.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OLMPkEncryption : NSObject + +/** + Set the recipient's public key for encrypting to. + + @param recipientKey the recipient's public key. + */ +- (void)setRecipientKey:(NSString*)recipientKey; + +/** + Encrypt a plaintext for the recipient. + + @param message the message to encrypt. + @param error the error if any. + @return the encrypted message. + */ +- (OLMPkMessage *)encryptMessage:(NSString*)message error:(NSError* _Nullable *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xcode/OLMKit/OLMPKEncryption.m b/xcode/OLMKit/OLMPKEncryption.m new file mode 100644 index 0000000..c2e3d04 --- /dev/null +++ b/xcode/OLMKit/OLMPKEncryption.m @@ -0,0 +1,111 @@ +/* + Copyright 2018 New Vector 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 "OLMPkEncryption.h" + +#include "olm/olm.h" +#include "olm/pk.h" +#include "OLMUtility.h" + +@interface OLMPkEncryption () +{ + OlmPkEncryption *session; +} +@end + +@implementation OLMPkEncryption + +- (void)dealloc { + olm_clear_pk_encryption(session); + free(session); +} + + +- (instancetype)init { + self = [super init]; + if (self) { + session = (OlmPkEncryption *)malloc(olm_pk_encryption_size()); + olm_pk_encryption(session); + } + return self; +} + +- (void)setRecipientKey:(NSString*)recipientKey { + NSData *recipientKeyData = [recipientKey dataUsingEncoding:NSUTF8StringEncoding]; + olm_pk_encryption_set_recipient_key(session, recipientKeyData.bytes, recipientKeyData.length); +} + +- (OLMPkMessage *)encryptMessage:(NSString *)message error:(NSError *__autoreleasing _Nullable *)error { + NSData *plaintextData = [message dataUsingEncoding:NSUTF8StringEncoding]; + + size_t randomLength = olm_pk_encrypt_random_length(session); + NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; + if (!random) { + return nil; + } + + size_t ciphertextLength = olm_pk_ciphertext_length(session, plaintextData.length); + NSMutableData *ciphertext = [NSMutableData dataWithLength:ciphertextLength]; + if (!ciphertext) { + return nil; + } + + size_t macLength = olm_pk_mac_length(session); + NSMutableData *macData = [NSMutableData dataWithLength:macLength]; + if (!ciphertext) { + return nil; + } + + size_t ephemeralKeyLength = olm_pk_key_length(); + NSMutableData *ephemeralKeyData = [NSMutableData dataWithLength:ephemeralKeyLength]; + if (!ciphertext) { + return nil; + } + + size_t result = olm_pk_encrypt(session, + plaintextData.bytes, plaintextData.length, + ciphertext.mutableBytes, ciphertext.length, + macData.mutableBytes, macLength, + ephemeralKeyData.mutableBytes, ephemeralKeyLength, + random.mutableBytes, randomLength); + if (result == olm_error()) { + const char *olm_error = olm_pk_encryption_last_error(session); + + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + NSLog(@"[OLMPkEncryption] encryptMessage: 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; + } + + OLMPkMessage *encryptedMessage = [[OLMPkMessage alloc] + initWithCiphertext:[[NSString alloc] initWithData:ciphertext encoding:NSUTF8StringEncoding] + mac:[[NSString alloc] initWithData:macData encoding:NSUTF8StringEncoding] + ephemeralKey:[[NSString alloc] initWithData:ephemeralKeyData encoding:NSUTF8StringEncoding]]; + + + return encryptedMessage; +} + +@end diff --git a/xcode/OLMKit/OLMPkDecryption.h b/xcode/OLMKit/OLMPkDecryption.h new file mode 100644 index 0000000..8715a99 --- /dev/null +++ b/xcode/OLMKit/OLMPkDecryption.h @@ -0,0 +1,64 @@ +/* + Copyright 2018 New Vector 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 + +#import "OLMSerializable.h" +#import "OLMPkMessage.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OLMPkDecryption : NSObject + +/** + Initialise the key from the private part of a key as returned by `privateKey`. + + Note that the pubkey is a base64 encoded string, but the private key is + an unencoded byte array. + + @param privateKey the private key part. + @param error the error if any. + @return the associated public key. + */ +- (NSString *)setPrivateKey:(NSData*)privateKey error:(NSError* _Nullable *)error; + +/** + Generate a new key to use for decrypting messages. + + @param error the error if any. + @return the public part of the generated key. + */ +- (NSString *)generateKey:(NSError* _Nullable *)error; + +/** + Get the private key. + + @return the private key; + */ +- (NSData *)privateKey; + +/** + Decrypt a ciphertext. + + @param message the cipher message to decrypt. + @param error the error if any. + @return the decrypted message. + */ +- (NSString *)decryptMessage:(OLMPkMessage*)message error:(NSError* _Nullable *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xcode/OLMKit/OLMPkDecryption.m b/xcode/OLMKit/OLMPkDecryption.m new file mode 100644 index 0000000..75fe5f2 --- /dev/null +++ b/xcode/OLMKit/OLMPkDecryption.m @@ -0,0 +1,295 @@ +/* + Copyright 2018 New Vector 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 "OLMPkDecryption.h" + +#include "olm/olm.h" +#include "olm/pk.h" +#include "OLMUtility.h" + +@interface OLMPkDecryption () +{ + OlmPkDecryption *session; +} +@end + +@implementation OLMPkDecryption + +- (void)dealloc { + olm_clear_pk_decryption(session); + free(session); +} + +- (instancetype)init { + self = [super init]; + if (self) { + session = (OlmPkDecryption *)malloc(olm_pk_decryption_size()); + olm_pk_decryption(session); + } + return self; +} + +- (NSString *)setPrivateKey:(NSData *)privateKey error:(NSError *__autoreleasing _Nullable *)error { + size_t publicKeyLength = olm_pk_key_length(); + NSMutableData *publicKeyData = [NSMutableData dataWithLength:publicKeyLength]; + if (!publicKeyData) { + return nil; + } + + size_t result = olm_pk_key_from_private(session, + publicKeyData.mutableBytes, publicKeyLength, + (void*)privateKey.bytes, privateKey.length); + if (result == olm_error()) { + const char *olm_error = olm_pk_decryption_last_error(session); + NSLog(@"[OLMPkDecryption] setPrivateKey: olm_pk_key_from_private error: %s", olm_error); + + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + if (error && olm_error && errorString) { + *error = [NSError errorWithDomain:OLMErrorDomain + code:0 + userInfo:@{ + NSLocalizedDescriptionKey: errorString, + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_pk_key_from_private error: %@", errorString] + }]; + } + return nil; + } + + NSString *publicKey = [[NSString alloc] initWithData:publicKeyData encoding:NSUTF8StringEncoding]; + return publicKey; +} + +- (NSString *)generateKey:(NSError *__autoreleasing _Nullable *)error { + size_t randomLength = olm_pk_private_key_length(); + NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; + if (!random) { + return nil; + } + + size_t publicKeyLength = olm_pk_key_length(); + NSMutableData *publicKeyData = [NSMutableData dataWithLength:publicKeyLength]; + if (!publicKeyData) { + return nil; + } + + size_t result = olm_pk_key_from_private(session, + publicKeyData.mutableBytes, publicKeyData.length, + random.mutableBytes, randomLength); + [random resetBytesInRange:NSMakeRange(0, randomLength)]; + if (result == olm_error()) { + const char *olm_error = olm_pk_decryption_last_error(session); + NSLog(@"[OLMPkDecryption] generateKey: olm_pk_key_from_private error: %s", olm_error); + + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + if (error && olm_error && errorString) { + *error = [NSError errorWithDomain:OLMErrorDomain + code:0 + userInfo:@{ + NSLocalizedDescriptionKey: errorString, + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_pk_key_from_private error: %@", errorString] + }]; + } + return nil; + } + + NSString *publicKey = [[NSString alloc] initWithData:publicKeyData encoding:NSUTF8StringEncoding]; + return publicKey; +} + +- (NSData *)privateKey { + size_t privateKeyLength = olm_pk_private_key_length(); + NSMutableData *privateKeyData = [NSMutableData dataWithLength:privateKeyLength]; + if (!privateKeyData) { + return nil; + } + + size_t result = olm_pk_get_private_key(session, + privateKeyData.mutableBytes, privateKeyLength); + if (result == olm_error()) { + const char *olm_error = olm_pk_decryption_last_error(session); + NSLog(@"[OLMPkDecryption] privateKey: olm_pk_get_private_key error: %s", olm_error); + return nil; + } + + NSData *privateKey = [privateKeyData copy]; + [privateKeyData resetBytesInRange:NSMakeRange(0, privateKeyData.length)]; + + return privateKey; +} + +-(NSString *)decryptMessage:(OLMPkMessage *)message error:(NSError *__autoreleasing _Nullable *)error { + NSData *messageData = [message.ciphertext dataUsingEncoding:NSUTF8StringEncoding]; + NSData *macData = [message.mac dataUsingEncoding:NSUTF8StringEncoding]; + NSData *ephemeralKeyData = [message.ephemeralKey dataUsingEncoding:NSUTF8StringEncoding]; + if (!messageData || !macData || !ephemeralKeyData) { + return nil; + } + + NSMutableData *mutMessage = messageData.mutableCopy; + size_t maxPlaintextLength = olm_pk_max_plaintext_length(session, mutMessage.length); + if (maxPlaintextLength == olm_error()) { + const char *olm_error = olm_pk_decryption_last_error(session); + + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + NSLog(@"[OLMPkDecryption] decryptMessage: olm_pk_max_plaintext_length error: %@", errorString); + + if (error && olm_error && errorString) { + *error = [NSError errorWithDomain:OLMErrorDomain + code:0 + userInfo:@{ + NSLocalizedDescriptionKey: errorString, + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_pk_max_plaintext_length error: %@", errorString] + }]; + } + + return nil; + } + + mutMessage = messageData.mutableCopy; + NSMutableData *plaintextData = [NSMutableData dataWithLength:maxPlaintextLength]; + size_t plaintextLength = olm_pk_decrypt(session, + ephemeralKeyData.bytes, ephemeralKeyData.length, + macData.bytes, macData.length, + mutMessage.mutableBytes, mutMessage.length, + plaintextData.mutableBytes, plaintextData.length); + if (plaintextLength == olm_error()) { + const char *olm_error = olm_pk_decryption_last_error(session); + + NSString *errorString = [NSString stringWithUTF8String:olm_error]; + NSLog(@"[OLMPkDecryption] decryptMessage: olm_pk_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 *__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; + } + + size_t ephemeralLength = olm_pk_key_length(); + NSMutableData *ephemeralBuffer = [NSMutableData dataWithLength:ephemeralLength]; + + NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; + size_t result = olm_unpickle_pk_decryption(session, + key.bytes, key.length, + pickle.mutableBytes, pickle.length, + ephemeralBuffer.mutableBytes, ephemeralLength); + [pickle resetBytesInRange:NSMakeRange(0, pickle.length)]; + if (result == olm_error()) { + const char *olm_error = olm_pk_decryption_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_pk_decryption_length(session); + NSMutableData *pickled = [NSMutableData dataWithLength:length]; + + size_t result = olm_pickle_pk_decryption(session, + key.bytes, key.length, + pickled.mutableBytes, pickled.length); + if (result == olm_error()) { + const char *olm_error = olm_pk_decryption_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]; + [pickled resetBytesInRange:NSMakeRange(0, pickled.length)]; + + 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/OLMPkMessage.h b/xcode/OLMKit/OLMPkMessage.h new file mode 100644 index 0000000..1559fca --- /dev/null +++ b/xcode/OLMKit/OLMPkMessage.h @@ -0,0 +1,31 @@ +/* + Copyright 2018 New Vector 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface OLMPkMessage : NSObject + +@property (nonatomic, copy, readonly) NSString *ciphertext; +@property (nonatomic, copy, readonly,) NSString *mac; +@property (nonatomic, copy, readonly) NSString *ephemeralKey; + +- (instancetype) initWithCiphertext:(NSString*)ciphertext mac:(NSString*)mac ephemeralKey:(NSString*)ephemeralKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xcode/OLMKit/OLMPkMessage.m b/xcode/OLMKit/OLMPkMessage.m new file mode 100644 index 0000000..0f24512 --- /dev/null +++ b/xcode/OLMKit/OLMPkMessage.m @@ -0,0 +1,32 @@ +/* + Copyright 2018 New Vector 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 "OLMPkMessage.h" + +@implementation OLMPkMessage + +- (instancetype)initWithCiphertext:(NSString *)ciphertext mac:(NSString *)mac ephemeralKey:(NSString *)ephemeralKey { + self = [super init]; + if (!self) { + return nil; + } + _ciphertext = [ciphertext copy]; + _mac = [mac copy]; + _ephemeralKey = [ephemeralKey copy]; + return self; +} + +@end diff --git a/xcode/OLMKit/OLMSession.m b/xcode/OLMKit/OLMSession.m index 8c29113..fc58a08 100644 --- a/xcode/OLMKit/OLMSession.m +++ b/xcode/OLMKit/OLMSession.m @@ -309,6 +309,7 @@ } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_session(_session, key.bytes, key.length, pickle.mutableBytes, pickle.length); + [pickle resetBytesInRange:NSMakeRange(0, pickle.length)]; if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; @@ -335,6 +336,7 @@ return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; + [pickled resetBytesInRange:NSMakeRange(0, pickled.length)]; return pickleString; } diff --git a/xcode/OLMKitTests/OLMKitPkTests.m b/xcode/OLMKitTests/OLMKitPkTests.m new file mode 100644 index 0000000..04d2e30 --- /dev/null +++ b/xcode/OLMKitTests/OLMKitPkTests.m @@ -0,0 +1,107 @@ +/* + Copyright 2018 New Vector 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 +#import + +/** + Tests are inspired from js tests. + */ +@interface OLMKitPkTests : XCTestCase { + OLMPkEncryption *encryption; + OLMPkDecryption *decryption; +} + +@end + +@implementation OLMKitPkTests + +- (void)setUp { + encryption = [OLMPkEncryption new]; + decryption = [OLMPkDecryption new]; +} + +- (void)tearDown { + encryption = nil; + decryption = nil; +} + +- (void)testImportExportKeys { + UInt8 alicePrivateBytes[] = { + 0x77, 0x07, 0x6D, 0x0A, 0x73, 0x18, 0xA5, 0x7D, + 0x3C, 0x16, 0xC1, 0x72, 0x51, 0xB2, 0x66, 0x45, + 0xDF, 0x4C, 0x2F, 0x87, 0xEB, 0xC0, 0x99, 0x2A, + 0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A + }; + + NSData *alicePrivate = [NSData dataWithBytes:alicePrivateBytes length:sizeof(alicePrivateBytes)]; + + NSError *error; + NSString *alicePublic = [decryption setPrivateKey:alicePrivate error:&error]; + XCTAssertNil(error); + XCTAssertEqualObjects(alicePublic, @"hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo"); + + NSData *alicePrivateOut = decryption.privateKey; + XCTAssertNil(error); + XCTAssertEqualObjects(alicePrivateOut, alicePrivate); +} + +- (void)testEncryptAndDecrypt { + + NSString *pubKey = [decryption generateKey:nil]; + NSLog(@"Ephemeral Key: %@", pubKey); + XCTAssertNotNil(pubKey); + + NSString *TEST_TEXT = @"têst1"; + NSError *error; + [encryption setRecipientKey:pubKey]; + OLMPkMessage *message = [encryption encryptMessage:TEST_TEXT error:&error]; + NSLog(@"message: %@ %@ %@", message.ciphertext, message.mac, message.ephemeralKey); + XCTAssertNil(error); + XCTAssertNotNil(message); + XCTAssertNotNil(message.ciphertext); + XCTAssertNotNil(message.mac); + XCTAssertNotNil(message.ephemeralKey); + + NSString *decrypted = [decryption decryptMessage:message error:&error]; + XCTAssertNil(error); + XCTAssertEqualObjects(decrypted, TEST_TEXT); + + TEST_TEXT = @"hot beverage: ☕"; + [encryption setRecipientKey:pubKey]; + message = [encryption encryptMessage:TEST_TEXT error:&error]; + decrypted = [decryption decryptMessage:message error:&error]; + XCTAssertEqualObjects(decrypted, TEST_TEXT); +} + +- (void)testOLMPkDecryptionSerialization { + NSString *TEST_TEXT = @"têst1"; + NSString *pubKey = [decryption generateKey:nil]; + [encryption setRecipientKey:pubKey]; + OLMPkMessage *encrypted = [encryption encryptMessage:TEST_TEXT error:nil]; + + + NSData *pickle = [NSKeyedArchiver archivedDataWithRootObject:decryption]; + decryption = nil; + + OLMPkDecryption *newDecryption = [NSKeyedUnarchiver unarchiveObjectWithData:pickle]; + + NSError *error; + NSString *decrypted = [newDecryption decryptMessage:encrypted error:&error]; + XCTAssertEqualObjects(decrypted, TEST_TEXT); +} + +@end