Skip to content

Commit

Permalink
Self-signed homeserver: When creating a new MXRestClient, trust alrea…
Browse files Browse the repository at this point in the history
…dy trusted certificate. This fixes e2e on self-signed hs. (element-hq/element-ios#816)
  • Loading branch information
manuroe committed Feb 17, 2017
1 parent 1e82eed commit 43686ab
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 1 deletion.
4 changes: 4 additions & 0 deletions MatrixSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
322A51C71D9BBD3C00C8536D /* MXOlmDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */; };
322A51C81D9BBD3C00C8536D /* MXOlmDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51C61D9BBD3C00C8536D /* MXOlmDevice.m */; };
322A51D81D9E846800C8536D /* MXCryptoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51D71D9E846800C8536D /* MXCryptoTests.m */; };
32322A481E57264E005DD155 /* MXSelfSignedHomeserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32322A471E57264E005DD155 /* MXSelfSignedHomeserverTests.m */; };
3233606F1A403A0D0071A488 /* MXFileStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3233606D1A403A0D0071A488 /* MXFileStore.h */; };
323360701A403A0D0071A488 /* MXFileStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3233606E1A403A0D0071A488 /* MXFileStore.m */; };
323B2ACF1BCD3EF000B11F34 /* MXCoreDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 323B2ACB1BCD3EF000B11F34 /* MXCoreDataStore.h */; };
Expand Down Expand Up @@ -273,6 +274,7 @@
322A51C51D9BBD3C00C8536D /* MXOlmDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXOlmDevice.h; sourceTree = "<group>"; };
322A51C61D9BBD3C00C8536D /* MXOlmDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXOlmDevice.m; sourceTree = "<group>"; };
322A51D71D9E846800C8536D /* MXCryptoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCryptoTests.m; sourceTree = "<group>"; };
32322A471E57264E005DD155 /* MXSelfSignedHomeserverTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXSelfSignedHomeserverTests.m; sourceTree = "<group>"; };
3233606D1A403A0D0071A488 /* MXFileStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXFileStore.h; sourceTree = "<group>"; };
3233606E1A403A0D0071A488 /* MXFileStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXFileStore.m; sourceTree = "<group>"; };
323B2ACB1BCD3EF000B11F34 /* MXCoreDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCoreDataStore.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -860,6 +862,7 @@
32C6F93919DD814400EA4E9C /* MatrixSDKTests */ = {
isa = PBXGroup;
children = (
32322A471E57264E005DD155 /* MXSelfSignedHomeserverTests.m */,
32E226A81D081CE200E6CA54 /* MXPeekingRoomTests.m */,
323EF7461C7CB4C7000DC98C /* MXEventTimelineTests.m */,
326D1EF41BFC79300030947B /* MXPushRuleTests.m */,
Expand Down Expand Up @@ -1352,6 +1355,7 @@
3265CB3B1A151C3800E24B2F /* MXRoomStateTests.m in Sources */,
326D1EF51BFC79300030947B /* MXPushRuleTests.m in Sources */,
324BE45B1E3FA7A8008D99D4 /* MXMegolmExportEncryptionTest.m in Sources */,
32322A481E57264E005DD155 /* MXSelfSignedHomeserverTests.m in Sources */,
325653831A2E14ED00CC0423 /* MXStoreTests.m in Sources */,
3295719A1B024D2B00ABB3BA /* MXMockCallStackCall.m in Sources */,
3281E8A019E2CC1200976E1A /* MXHTTPClientTests.m in Sources */,
Expand Down
25 changes: 24 additions & 1 deletion MatrixSDK/MXRestClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,30 @@ -(id)initWithCredentials:(MXCredentials*)inCredentials andOnUnrecognizedCertific

httpClient = [[MXHTTPClient alloc] initWithBaseURL:homeserver
accessToken:credentials.accessToken
andOnUnrecognizedCertificateBlock:onUnrecognizedCertBlock];
andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {

// Check whether the provided certificate is the already trusted by the user.
if (inCredentials.allowedCertificate && [inCredentials.allowedCertificate isEqualToData:certificate])
{
return YES;
}

// Check whether the user has already ignored this certificate change.
if (inCredentials.ignoredCertificate && [inCredentials.ignoredCertificate isEqualToData:certificate])
{
return NO;
}

// Let the app ask the end user to verify it
if (onUnrecognizedCertBlock)
{
return onUnrecognizedCertBlock(certificate);
}
else
{
return NO;
}
}];

// By default, use the same address for the identity server
self.identityServer = homeserver;
Expand Down
168 changes: 168 additions & 0 deletions MatrixSDKTests/MXSelfSignedHomeserverTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
Copyright 2017 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 <XCTest/XCTest.h>

#import "MatrixSDKTestsData.h"

#import "MXSession.h"

@interface MXSelfSignedHomeserverTests : XCTestCase
{
MatrixSDKTestsData *matrixSDKTestsData;
}

@end

@implementation MXSelfSignedHomeserverTests

- (void)setUp
{
[super setUp];

matrixSDKTestsData = [[MatrixSDKTestsData alloc] init];
}

- (void)tearDown
{
[super tearDown];
}

- (void)testRegiter
{
XCTestExpectation *expectation = [self expectationWithDescription:@"asyncTest"];

__block BOOL certificateCheckAsked = NO;
[matrixSDKTestsData getHttpsBobCredentials:^{

XCTAssert(certificateCheckAsked, @"We must have been asked to check the certificate");
XCTAssertNotNil(matrixSDKTestsData.bobCredentials);

XCTAssertNotNil(matrixSDKTestsData.bobCredentials.accessToken);
XCTAssertNotNil(matrixSDKTestsData.bobCredentials.allowedCertificate);
XCTAssertNil(matrixSDKTestsData.bobCredentials.ignoredCertificate);

[expectation fulfill];

} onUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
certificateCheckAsked = YES;
return YES;
}];

[self waitForExpectationsWithTimeout:10 handler:nil];
}

- (void)testTrustedCertificate
{
XCTestExpectation *expectation = [self expectationWithDescription:@"asyncTest"];

[matrixSDKTestsData getHttpsBobCredentials:^{

MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:matrixSDKTestsData.bobCredentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {

XCTFail(@"We have already accepted the certificate. We should not be asked again");
return NO;
}];

// Check the instance is usable
XCTAssert(mxRestClient);
[mxRestClient createRoom:nil visibility:0 roomAlias:nil topic:nil success:^(MXCreateRoomResponse *response) {

[expectation fulfill];

} failure:^(NSError *error) {
XCTFail(@"Cannot set up intial test conditions - error: %@", error);
[expectation fulfill];
}];

} onUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
return YES;
}];

[self waitForExpectationsWithTimeout:10 handler:nil];
}

// Create a room and post a message to it
- (void)testRoomAndMessages
{
[matrixSDKTestsData doHttpsMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) {

[mxSession createRoom:@"A room" visibility:0 roomAlias:nil topic:nil success:^(MXRoom *room) {

XCTAssertNotNil(room);

[room.liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {

XCTAssertEqual(direction, MXTimelineDirectionForwards);
XCTAssert([event.description containsString:@"Hello"]);

[expectation fulfill];
}];

[room sendTextMessage:@"Hello" success:nil failure:^(NSError *error) {
XCTFail(@"Cannot set up intial test conditions - error: %@", error);
[expectation fulfill];
}];

} failure:^(NSError *error) {
XCTFail(@"Cannot set up intial test conditions - error: %@", error);
[expectation fulfill];
}];
}];
}

// Create an e2e encrypted room and post a message to it
- (void)testE2ERoomAndMessages
{
[MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES;

[matrixSDKTestsData doHttpsMXSessionTestWithBob:self readyToTest:^(MXSession *mxSession, XCTestExpectation *expectation) {

[MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO;

[mxSession createRoom:@"A room" visibility:0 roomAlias:nil topic:nil success:^(MXRoom *room) {

XCTAssertNotNil(room);

[room enableEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm success:^{

[room.liveTimeline listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {

XCTAssertEqual(direction, MXTimelineDirectionForwards);
XCTAssert(event.clearEvent);
XCTAssert([event.clearEvent.description containsString:@"Hello"]);

[expectation fulfill];
}];

[room sendTextMessage:@"Hello" success:nil failure:^(NSError *error) {
XCTFail(@"Cannot set up intial test conditions - error: %@", error);
[expectation fulfill];
}];

} failure:^(NSError *error) {
XCTFail(@"Cannot set up intial test conditions - error: %@", error);
[expectation fulfill];
}];

} failure:^(NSError *error) {
XCTFail(@"Cannot set up intial test conditions - error: %@", error);
[expectation fulfill];
}];
}];
}

@end
11 changes: 11 additions & 0 deletions MatrixSDKTests/MatrixSDKTestsData.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

// The URL of your test home server
FOUNDATION_EXPORT NSString * const kMXTestsHomeServerURL;
FOUNDATION_EXPORT NSString * const kMXTestsHomeServerHttpsURL;

// Alice has a displayname and an avatar
FOUNDATION_EXPORT NSString * const kMXTestsAliceDisplayName;
Expand Down Expand Up @@ -107,6 +108,16 @@ FOUNDATION_EXPORT NSString * const kMXTestsAliceAvatarURL;
readyToTest:(void (^)(MXSession *bobSession, MXRestClient *aliceRestClient, NSString* roomId, XCTestExpectation *expectation))readyToTest;


#pragma mark - HTTPS mxBob
- (void)getHttpsBobCredentials:(void (^)())success;
- (void)getHttpsBobCredentials:(void (^)())success onUnrecognizedCertificateBlock:(MXHTTPClientOnUnrecognizedCertificate)onUnrecognizedCertBlock;

- (void)doHttpsMXRestClientTestWithBob:(XCTestCase*)testCase
readyToTest:(void (^)(MXRestClient *bobRestClient, XCTestExpectation *expectation))readyToTest;
- (void)doHttpsMXSessionTestWithBob:(XCTestCase*)testCase
readyToTest:(void (^)(MXSession *mxSession, XCTestExpectation *expectation))readyToTest;


#pragma mark - tools

- (void)relogUserSession:(MXSession*)session withPassword:(NSString*)password onComplete:(void (^)(MXSession *newSession))onComplete;
Expand Down
95 changes: 95 additions & 0 deletions MatrixSDKTests/MatrixSDKTestsData.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Here, we use one of the home servers launched by the ./demo/start.sh script
*/
NSString *const kMXTestsHomeServerURL = @"http://localhost:8080";
NSString *const kMXTestsHomeServerHttpsURL = @"https://localhost:8481";

NSString * const kMXTestsAliceDisplayName = @"mxAlice";
NSString * const kMXTestsAliceAvatarURL = @"mxc://matrix.org/kciiXusgZFKuNLIfLqmmttIQ";
Expand Down Expand Up @@ -594,6 +595,100 @@ - (void)doMXSessionTestWithBobAndAliceInARoom:(XCTestCase*)testCase
}


#pragma mark - HTTPS mxBob
- (void)getHttpsBobCredentials:(void (^)())success
{
[self getHttpsBobCredentials:success onUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
return YES;
}];
}

- (void)getHttpsBobCredentials:(void (^)())success onUnrecognizedCertificateBlock:(MXHTTPClientOnUnrecognizedCertificate)onUnrecognizedCertBlock
{
if (self.bobCredentials)
{
// Credentials are already here, they are ready
success();
}
else
{
// Use a different Bob each time so that tests are independent
NSString *bobUniqueUser = [NSString stringWithFormat:@"%@-%@", MXTESTS_BOB, [[NSUUID UUID] UUIDString]];

MXRestClient *mxRestClient = [[MXRestClient alloc] initWithHomeServer:kMXTestsHomeServerHttpsURL
andOnUnrecognizedCertificateBlock:onUnrecognizedCertBlock];

// First, try register the user
[mxRestClient registerWithLoginType:kMXLoginFlowTypeDummy username:bobUniqueUser password:MXTESTS_BOB_PWD success:^(MXCredentials *credentials) {

_bobCredentials = credentials;
success();

} failure:^(NSError *error) {
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && [mxError.errcode isEqualToString:@"M_USER_IN_USE"])
{
// The user already exists. This error is normal.
// Log Bob in to get his keys
[mxRestClient loginWithLoginType:kMXLoginFlowTypeDummy username:bobUniqueUser password:MXTESTS_BOB_PWD success:^(MXCredentials *credentials) {

_bobCredentials = credentials;
success();

} failure:^(NSError *error) {
NSAssert(NO, @"Cannot log mxBOB in");
}];
}
else
{
NSAssert(NO, @"Cannot create mxBOB account. Make sure the homeserver at %@ is running", mxRestClient.homeserver);
}
}];
}
}

- (void)doHttpsMXRestClientTestWithBob:(XCTestCase*)testCase
readyToTest:(void (^)(MXRestClient *bobRestClient, XCTestExpectation *expectation))readyToTest
{
XCTestExpectation *expectation;
if (testCase)
{
expectation = [testCase expectationWithDescription:@"asyncTest"];
}

[self getHttpsBobCredentials:^{

MXRestClient *restClient = [[MXRestClient alloc] initWithCredentials:self.bobCredentials
andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) {
return YES;
}];

readyToTest(restClient, expectation);
}];

if (testCase)
{
[testCase waitForExpectationsWithTimeout:10 handler:nil];
}
}

- (void)doHttpsMXSessionTestWithBob:(XCTestCase*)testCase
readyToTest:(void (^)(MXSession *mxSession, XCTestExpectation *expectation))readyToTest
{
[self doHttpsMXRestClientTestWithBob:testCase readyToTest:^(MXRestClient *bobRestClient, XCTestExpectation *expectation) {
MXSession *mxSession = [[MXSession alloc] initWithMatrixRestClient:bobRestClient];

[mxSession start:^{

readyToTest(mxSession, expectation);

} failure:^(NSError *error) {
NSAssert(NO, @"Cannot set up intial test conditions - error: %@", error);
}];
}];
}


#pragma mark - tools

- (void)relogUserSession:(MXSession*)session withPassword:(NSString*)password onComplete:(void (^)(MXSession *newSession))onComplete
Expand Down

0 comments on commit 43686ab

Please sign in to comment.