Skip to content

Commit

Permalink
Move IV argument to private Crypto constructor.
Browse files Browse the repository at this point in the history
When decrypting, IV is being extracted from the beginning of the
message's data blob. When encrypting, the IV should be random, and
there's no point in taking it as an argument except for testing.

I've added and end-to-end test that sends and receives encrypted
messages. I've also run it with ably/ably-js as sender and receiver
and it works.
  • Loading branch information
tcard committed Feb 29, 2016
1 parent ea77eef commit 8992f71
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 15 deletions.
1 change: 1 addition & 0 deletions ably-ios/ARTCrypto+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@interface ARTCipherParams ()

@property (nonatomic, weak) ARTLog *logger;
- (instancetype)initWithAlgorithm:(NSString *)algorithm key:(NSData *)key keyLength:(NSUInteger)keyLength iv:(NSData *)iv;

@end

Expand Down
5 changes: 2 additions & 3 deletions ably-ios/ARTCrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@
@property (readonly, getter=getMode) NSString *mode;

- (instancetype)init UNAVAILABLE_ATTRIBUTE;
- (instancetype)initWithAlgorithm:(NSString *)algorithm key:(NSData *)key keyLength:(NSUInteger)keyLenght iv:(NSData *)iv;
- (instancetype)initWithAlgorithm:(NSString *)algorithm key:(NSData *)key keyLength:(NSUInteger)keyLength;

@end

@interface ARTCrypto : NSObject

+ (ARTCipherParams *)getDefaultParams;
+ (ARTCipherParams *)getDefaultParamsWithKey:(NSData *)key;
+ (ARTCipherParams *)getDefaultParamsWithKey:(NSData *)key iv:(NSData *)iv;
+ (ARTCipherParams *)getDefaultParams:(NSData *)key;

@end
16 changes: 10 additions & 6 deletions ably-ios/ARTCrypto.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ + (instancetype)cbcCipherWithParams:(ARTCipherParams *)cipherParams;

@implementation ARTCipherParams

- (instancetype)initWithAlgorithm:(NSString *)algorithm key:(NSData *)key keyLength:(NSUInteger)keyLenght iv:(NSData *)iv {
- (instancetype)initWithAlgorithm:(NSString *)algorithm key:(NSData *)key keyLength:(NSUInteger)keyLength {
return [self initWithAlgorithm:algorithm key:key keyLength:keyLength iv:[ARTCrypto generateSecureRandomData:keyLength]];
}

- (instancetype)initWithAlgorithm:(NSString *)algorithm key:(NSData *)key keyLength:(NSUInteger)keyLength iv:(NSData *)iv {
self = [super init];
if (self) {
_algorithm = algorithm;
_key = key;
_keyLength = keyLenght;
_keyLength = keyLength;
_iv = iv;
}
return self;
Expand Down Expand Up @@ -306,18 +310,18 @@ + (ARTCipherParams *)getDefaultParams {
if (nil == key) {
return nil;
}
return [self getDefaultParamsWithKey:key];
return [self getDefaultParams:key];
}

+ (ARTCipherParams *)getDefaultParamsWithKey:(NSData *)key {
+ (ARTCipherParams *)getDefaultParams:(NSData *)key {
NSData *ivData = [self generateSecureRandomData:[self defaultBlockLength]];
if (nil == ivData) {
return nil;
}
return [self getDefaultParamsWithKey:key iv:ivData];
return [self getDefaultParams:key iv:ivData];
}

+ (ARTCipherParams *)getDefaultParamsWithKey:(NSData *)key iv:(NSData *)iv {
+ (ARTCipherParams *)getDefaultParams:(NSData *)key iv:(NSData *)iv {
return [[ARTCipherParams alloc] initWithAlgorithm:[self defaultAlgorithm] key:key keyLength:[self defaultBlockLength] iv:iv];
}

Expand Down
1 change: 1 addition & 0 deletions ably-ios/Ably.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ framework module Ably {
header "ARTURLSessionServerTrust.h"
header "ARTWebSocketTransport+Private.h"
header "ARTClientOptions+Private.h"
header "ARTCrypto+Private.h"
}
}
2 changes: 1 addition & 1 deletion ably-iosTests/ARTRealtimeCryptoTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#import "ARTRealtime.h"
#import "ARTRealtimeChannel.h"
#import "ARTTestUtil.h"
#import "ARTCrypto.h"
#import "ARTCrypto+Private.h"
#import "ARTDataQuery.h"
#import "ARTPaginatedResult.h"
#import "ARTChannelOptions.h"
Expand Down
2 changes: 1 addition & 1 deletion ably-iosTests/ARTRestCryptoTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#import "ARTPresenceMessage.h"
#import "ARTRest.h"
#import "ARTTestUtil.h"
#import "ARTCrypto.h"
#import "ARTCrypto+Private.h"
#import "ARTLog.h"
#import "ARTRestChannel.h"
#import "ARTChannelOptions.h"
Expand Down
2 changes: 1 addition & 1 deletion ably.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
EB1AE0CE1C5C3A4900D62250 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1AE0CD1C5C3A4900D62250 /* Utilities.swift */; };
EB20F8D71C653F2300EF3978 /* ARTPresence+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB20F8D61C653F1E00EF3978 /* ARTPresence+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB503C881C7E4A090053AF00 /* ARTClientOptions+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB503C871C7E4A090053AF00 /* ARTClientOptions+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB5E058D1C77027600A48B39 /* ARTCrypto+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E058C1C77027600A48B39 /* ARTCrypto+Private.h */; };
EB5E058D1C77027600A48B39 /* ARTCrypto+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E058C1C77027600A48B39 /* ARTCrypto+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB7913A81C6E54C3000ABF9B /* Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7913A71C6E54C3000ABF9B /* Crypto.swift */; };
EB82F8511C59D29B00661917 /* ARTDataEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = EB3239461C59AB2C00892664 /* ARTDataEncoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB82F8521C59D30500661917 /* ARTDataEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = EB3239421C59AB0400892664 /* ARTDataEncoder.m */; };
Expand Down
4 changes: 3 additions & 1 deletion ablySpec/Crypto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import Nimble
import Quick
import SwiftyJSON

import Ably.Private

class Crypto : QuickSpec {
override func spec() {
describe("Crypto") {
for cryptoTest in CryptoTest.all {
context("with fixtures from \(cryptoTest).json") {
let (key, iv, items) = AblyTests.loadCryptoTestData(cryptoTest)
let decoder = ARTDataEncoder.init(cipherParams: nil, error: nil)
let cipherParams = ARTCipherParams.init(algorithm: "aes", key: key, keyLength: UInt(key.length), iv: iv)
let cipherParams = ARTCipherParams(algorithm: "aes", key: key, keyLength: UInt(key.length), iv: iv)

func extractMessage(fixture: AblyTests.CryptoTestItem.TestMessage) -> ARTMessage {
let msg = ARTMessage(name: fixture.name, data: fixture.data)
Expand Down
92 changes: 90 additions & 2 deletions ablySpec/RealtimeClientChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Quick
import Nimble
import Aspects

import Ably.Private

class RealtimeClientChannel: QuickSpec {
override func spec() {
describe("Channel") {
Expand Down Expand Up @@ -1085,8 +1087,8 @@ class RealtimeClientChannel: QuickSpec {
let (keyData, ivData, messages) = AblyTests.loadCryptoTestData(cryptoTest)
let testMessage = messages[0]

let cipherParams = ARTCrypto.defaultParamsWithKey(keyData, iv: ivData)
let channelOptions = ARTChannelOptions(encrypted: cipherParams)
let cipherParams = ARTCipherParams(algorithm: "aes", key: keyData, keyLength: UInt(keyData.length), iv: ivData)
let channelOptions = ARTChannelOptions(encrypted: true, cipherParams: cipherParams)
let channel = client.channels.get("test", options: channelOptions)

let transport = client.transport as! TestProxyTransport
Expand Down Expand Up @@ -1493,6 +1495,92 @@ class RealtimeClientChannel: QuickSpec {
}
}
}

context("crypto") {
it("if configured for encryption, channels encrypt and decrypt messages' data") {
let options = AblyTests.commonAppSetup()
options.autoConnect = false

let clientSender = ARTRealtime(options: options)
clientSender.setTransportClass(TestProxyTransport.self)
defer { clientSender.close() }
clientSender.connect()

let clientReceiver = ARTRealtime(options: options)
clientReceiver.setTransportClass(TestProxyTransport.self)
defer { clientReceiver.close() }
clientReceiver.connect()

let cipherParams = ARTCrypto.getDefaultParams()
let sender = clientSender.channels.get("test", options: ARTChannelOptions(encrypted: true, cipherParams: cipherParams))
let receiver = clientReceiver.channels.get("test", options: ARTChannelOptions(encrypted: true, cipherParams: cipherParams))

var received = [ARTMessage]()

waitUntil(timeout: testTimeout) { done in
receiver.attach { _ in
receiver.subscribe { message in
receiver.unsubscribe()
received.append(message)
done()
}

sender.publish("first", data: "first data")
}
}
if received.count != 1 {
fail("should have received one message")
return
}

waitUntil(timeout: testTimeout) { done in
receiver.detach { _ in
sender.publish("second", data: "second data") { _ in done() }
}
}
if receiver.state != .Detached {
fail("receiver should be detached")
return
}

waitUntil(timeout: testTimeout) { done in
receiver.attach { _ in
receiver.subscribe { message in
received.append(message)
done()
}
sender.publish("third", data: "third data")
}
}
if received.count != 2 {
fail("should've received two messages")
return
}

expect(received[0].name).to(equal("first"))
expect(received[0].data as? NSString).to(equal("first data"))
expect(received[1].name).to(equal("third"))
expect(received[1].data as? NSString).to(equal("third data"))

let senderTransport = clientSender.transport as! TestProxyTransport
let senderMessages = senderTransport.protocolMessagesSent.filter({ $0.action == .Message })
for protocolMessage in senderMessages {
for message in protocolMessage.messages! {
expect(message.data! as? String).toNot(equal("\(message.name!) data"))
expect(message.encoding).to(equal("utf-8/cipher+aes-256-cbc/base64"))
}
}

let receiverTransport = clientReceiver.transport as! TestProxyTransport
let receiverMessages = receiverTransport.protocolMessagesReceived.filter({ $0.action == .Message })
for protocolMessage in receiverMessages {
for message in protocolMessage.messages! {
expect(message.data! as? String).toNot(equal("\(message.name!) data"))
expect(message.encoding).to(equal("utf-8/cipher+aes-256-cbc/base64"))
}
}
}
}
}
}
}

0 comments on commit 8992f71

Please sign in to comment.