Compare commits

...

2 Commits

Author SHA1 Message Date
Michael Kirk
ab57c9cf5c update testing dependencies
// FREEBIE
2017-01-23 09:15:37 -05:00
Michael Kirk
55630ef624 Prevent session corruption by using a consistent queue.
Assert that session state manipulation happens on a consistent queue.
Hopefully this will sidestep any issues related to corrupting session
state.

// FREEBIE
2017-01-22 18:07:51 -05:00
5 changed files with 91 additions and 40 deletions

View File

@ -1,3 +1,5 @@
language: objective-c
osx_image: xcode8
script: bin/ci

View File

@ -46,6 +46,28 @@
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
}
- (void)testBasicSessionCipherDispatchQueue {
SessionRecord *aliceSessionRecord = [SessionRecord new];
SessionRecord *bobSessionRecord = [SessionRecord new];
XCTestExpectation *expectation = [self expectationWithDescription:@"session cipher completed"];
dispatch_queue_t sessionCipherDispatchQueue = dispatch_queue_create("session cipher queue", DISPATCH_QUEUE_SERIAL);
[SessionCipher setSessionCipherDispatchQueue:sessionCipherDispatchQueue];
dispatch_async(sessionCipherDispatchQueue, ^{
[self sessionInitialization:aliceSessionRecord.sessionState bobSessionState:bobSessionRecord.sessionState];
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:5.0 handler:^(NSError * _Nullable error) {
if (error) {
XCTFail(@"Expectation failed with error: %@", error);
}
}];
}
-(void)sessionInitialization:(SessionState*)aliceSessionState bobSessionState:(SessionState*)bobSessionState{
ECKeyPair *aliceIdentityKeyPair = [Curve25519 generateKeyPair];

View File

@ -19,6 +19,16 @@
@interface SessionCipher : NSObject
/**
* To keep Session state synchronized, encryption and decryption must happen on the same (serial) dispatch queue. If no
* queue is specified, the main queue will be used by default. We only assert that this invariant is held. Dispatching
* to this thread is the responsibility of the caller.
*
* @param dispatchQueue serial dispatch queue on which all encryption/decryption must be dispatched.
*/
+ (void)setSessionCipherDispatchQueue:(dispatch_queue_t)dispatchQueue;
+ (dispatch_queue_t)getSessionCipherDispatchQueue;
- (instancetype)initWithAxolotlStore:(id<AxolotlStore>)sessionStore recipientId:(NSString*)recipientId deviceId:(int)deviceId;
- (instancetype)initWithSessionStore:(id<SessionStore>)sessionStore preKeyStore:(id<PreKeyStore>)preKeyStore signedPreKeyStore:(id<SignedPreKeyStore>)signedPreKeyStore identityKeyStore:(id<IdentityKeyStore>)identityKeyStore recipientId:(NSString*)recipientId deviceId:(int)deviceId;
@ -30,4 +40,4 @@
- (int)remoteRegistrationId;
- (int)sessionVersion;
@end
@end

View File

@ -29,6 +29,11 @@
#import <HKDFKit/HKDFKit.h>
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(major, minor) \
([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = major, .minorVersion = minor, .patchVersion = 0}])
static dispatch_queue_t _sessionCipherDispatchQueue;
@interface SessionCipher ()
@property NSString* recipientId;
@ -77,8 +82,31 @@
return self;
}
#pragma mark - dispatch queue
+ (dispatch_queue_t)getSessionCipherDispatchQueue;
{
if (_sessionCipherDispatchQueue) {
return _sessionCipherDispatchQueue;
} else {
return dispatch_get_main_queue();
}
}
+ (void)setSessionCipherDispatchQueue:(dispatch_queue_t)dispatchQueue
{
_sessionCipherDispatchQueue = dispatchQueue;
}
- (void)assertOnSessionCipherDispatchQueue
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(10, 0)) {
dispatch_assert_queue([[self class] getSessionCipherDispatchQueue]);
} // else, skip assert as it's a development convenience.
}
- (id<CipherMessage>)encryptMessage:(NSData*)paddedMessage{
[self assertOnSessionCipherDispatchQueue];
SessionRecord *sessionRecord = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId];
SessionState *session = sessionRecord.sessionState;
ChainKey *chainKey = session.senderChainKey;
@ -118,6 +146,7 @@
}
- (NSData*)decrypt:(id<CipherMessage>)whisperMessage{
[self assertOnSessionCipherDispatchQueue];
if ([whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) {
return [self decryptPreKeyWhisperMessage:(PreKeyWhisperMessage*)whisperMessage];
} else{
@ -126,6 +155,7 @@
}
- (NSData*)decryptPreKeyWhisperMessage:(PreKeyWhisperMessage*)preKeyWhisperMessage{
[self assertOnSessionCipherDispatchQueue];
SessionRecord *sessionRecord = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId];
int unsignedPreKeyId = [self.sessionBuilder processPrekeyWhisperMessage:preKeyWhisperMessage withSession:sessionRecord];
NSData *plaintext = [self decryptWithSessionRecord:sessionRecord whisperMessage:preKeyWhisperMessage.message];
@ -140,6 +170,7 @@
}
- (NSData*)decryptWhisperMessage:(WhisperMessage*)message{
[self assertOnSessionCipherDispatchQueue];
if (![self.sessionStore containsSession:self.recipientId deviceId:self.deviceId]) {
@throw [NSException exceptionWithName:NoSessionException reason:[NSString stringWithFormat:@"No session for: %@, %d", self.recipientId, self.deviceId] userInfo:nil];
}
@ -154,6 +185,7 @@
-(NSData*)decryptWithSessionRecord:(SessionRecord*)sessionRecord whisperMessage:(WhisperMessage*)message{
[self assertOnSessionCipherDispatchQueue];
SessionState *sessionState = [sessionRecord sessionState];
NSArray *previousStates = [sessionRecord previousSessionStates];
NSMutableArray *exceptions = [NSMutableArray array];
@ -182,6 +214,7 @@
}
-(NSData*)decryptWithSessionState:(SessionState*)sessionState whisperMessage:(WhisperMessage*)message{
[self assertOnSessionCipherDispatchQueue];
if (![sessionState hasSenderChain]) {
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Uninitialized session!" userInfo:nil];
}
@ -206,6 +239,7 @@
}
- (ChainKey*)getOrCreateChainKeys:(SessionState*)sessionState theirEphemeral:(NSData*)theirEphemeral{
[self assertOnSessionCipherDispatchQueue];
@try {
if ([sessionState hasReceiverChain:theirEphemeral]) {
return [sessionState receiverChainKey:theirEphemeral];
@ -230,7 +264,7 @@
}
- (MessageKeys*)getOrCreateMessageKeysForSession:(SessionState*)sessionState theirEphemeral:(NSData*)theirEphemeral chainKey:(ChainKey*)chainKey counter:(int)counter{
[self assertOnSessionCipherDispatchQueue];
if (chainKey.index > counter) {
if ([sessionState hasMessageKeys:theirEphemeral counter:counter]) {
return [sessionState removeMessageKeys:theirEphemeral counter:counter];
@ -268,6 +302,7 @@
- (int)remoteRegistrationId{
[self assertOnSessionCipherDispatchQueue];
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:_deviceId];
if (!record) {
@ -278,6 +313,7 @@
}
- (int)sessionVersion{
[self assertOnSessionCipherDispatchQueue];
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:_deviceId];
if (!record) {
@ -287,4 +323,4 @@
return record.sessionState.version;
}
@end
@end

View File

@ -35,38 +35,33 @@ GEM
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.1)
cocoapods-downloader (1.1.2)
cocoapods-downloader (1.1.3)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.1.1)
cocoapods-trunk (1.1.2)
nap (>= 0.8, < 2.0)
netrc (= 0.7.8)
cocoapods-try (1.1.0)
colored (1.2)
commander (4.4.1)
commander (4.4.3)
highline (~> 1.7.2)
credentials_manager (0.16.2)
colored
commander (>= 4.3.5)
highline (>= 1.7.1)
security
domain_name (0.5.20161129)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.1.1)
dotenv (2.1.2)
escape (0.0.4)
excon (0.54.0)
faraday (0.10.0)
faraday (0.11.0)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.10.1)
faraday_middleware (0.11.0.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.0.1)
addressable (~> 2)
fastlane (2.0.1)
fastlane (2.9.0)
activesupport (< 5)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@ -80,7 +75,7 @@ GEM
faraday_middleware (~> 0.9)
fastimage (>= 1.6)
gh_inspector (>= 1.0.1, < 2.0.0)
google-api-client (~> 0.9.1)
google-api-client (~> 0.9.2)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
mini_magick (~> 4.5.1)
@ -88,20 +83,19 @@ GEM
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 1.2.0, < 2.0.0)
rubyzip (>= 1.1.0, < 2.0.0)
security (= 0.1.3)
slack-notifier (>= 1.3, < 2.0.0)
terminal-notifier (>= 1.6.2, < 2.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
word_wrap (~> 1.0.0)
xcode-install (~> 2.0.0)
xcodeproj (>= 0.20, < 2.0.0)
xcpretty (>= 0.2.4, < 1.0.0)
xcpretty-travis-formatter (>= 0.0.3)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.0.2)
google-api-client (0.9.20)
gh_inspector (1.0.3)
google-api-client (0.9.23)
addressable (~> 2.3)
googleauth (~> 0.5)
httpclient (~> 2.7)
@ -124,7 +118,7 @@ GEM
httpclient (2.8.3)
hurley (0.2)
i18n (0.7.0)
json (1.8.3)
json (1.8.6)
jwt (1.5.6)
little-plugger (1.1.4)
logging (2.1.0)
@ -136,7 +130,7 @@ GEM
mime-types-data (3.2016.0521)
mini_magick (4.5.1)
minitest (5.10.1)
molinillo (0.5.4)
molinillo (0.5.5)
multi_json (1.12.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
@ -145,7 +139,7 @@ GEM
netrc (0.7.8)
os (0.9.6)
plist (3.2.0)
public_suffix (2.0.4)
public_suffix (2.0.5)
representable (2.3.0)
uber (~> 0.0.7)
retriable (2.1.0)
@ -158,16 +152,6 @@ GEM
jwt (~> 1.5)
multi_json (~> 1.10)
slack-notifier (1.5.1)
spaceship (0.39.0)
babosa (= 1.0.2)
colored
credentials_manager (>= 0.16.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
fastimage (>= 1.6)
multi_xml (~> 0.5)
plist (>= 3.1.0, < 4.0.0)
terminal-notifier (1.7.1)
terminal-table (1.7.3)
unicode-display_width (~> 1.1.1)
@ -178,17 +162,14 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (1.1.2)
unicode-display_width (1.1.3)
word_wrap (1.0.0)
xcode-install (2.0.9)
claide (>= 0.9.1, < 1.1.0)
spaceship (>= 0.25.1, < 1.0.0)
xcodeproj (1.4.1)
xcodeproj (1.4.2)
CFPropertyList (~> 2.3.3)
activesupport (>= 3)
claide (>= 1.0.1, < 2.0)
colored (~> 1.2)
nanaimo (~> 0.2.0)
nanaimo (~> 0.2.3)
xcpretty (0.2.4)
rouge (~> 1.8)
xcpretty-travis-formatter (0.0.4)