Unverified Commit 2784e495 authored by manuroe's avatar manuroe Committed by GitHub
Browse files

Merge pull request #70 from matrix-org/manuroe/objc_pk

OLMKit: Add objc wrappers for pk encryption/decryption
parents 2cace25f cc9a97f0
File deleted
......@@ -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()
*/
......
......@@ -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 = "<group>"; };
3244277C2175EF700023EDF1 /* OLMKitPkTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OLMKitPkTests.m; sourceTree = "<group>"; };
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 = "<group>"; };
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;
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -26,6 +26,8 @@
#import <OLMKit/OLMUtility.h>
#import <OLMKit/OLMInboundGroupSession.h>
#import <OLMKit/OLMOutboundGroupSession.h>
#import <OLMKit/OLMPkEncryption.h>
#import <OLMKit/OLMPkDecryption.h>
@interface OLMKit : NSObject
......
......@@ -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;
}
......
/*
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 <Foundation/Foundation.h>
#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
/*
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
/*
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 <Foundation/Foundation.h>
#import "OLMSerializable.h"
#import "OLMPkMessage.h"
NS_ASSUME_NONNULL_BEGIN
@interface OLMPkDecryption : NSObject <OLMSerializable, NSSecureCoding>
/**
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
/*
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;
}