Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06c654a757 | ||
|
|
36601e4bbe | ||
|
|
9413e47751 | ||
|
|
c40a9fb072 | ||
|
|
18a7031342 | ||
|
|
43aa55e6be | ||
|
|
89c447b636 | ||
|
|
170acafbc8 | ||
|
|
99d700a87c | ||
|
|
582ec1a96c | ||
|
|
8f8d565412 | ||
|
|
0516a98a55 | ||
|
|
f0632b87bb | ||
|
|
97ae15634e | ||
|
|
34073098e9 | ||
|
|
61ce278894 | ||
|
|
852d43fe8d | ||
|
|
debc5cb311 | ||
|
|
6a1c548be0 | ||
|
|
97bc4ea101 | ||
|
|
ddcdbd04da | ||
|
|
905c5d0e0c | ||
|
|
fb8ca529ae | ||
|
|
4a4899ea14 | ||
|
|
1c5425eb26 | ||
|
|
8d4451262d | ||
|
|
832cb97c95 | ||
|
|
9bdcbaade2 | ||
|
|
d1739b2669 | ||
|
|
3fe60a30fe | ||
|
|
41c6a84b46 | ||
|
|
aa4b0a7bfa | ||
|
|
f852496e31 | ||
|
|
dedb0f987a | ||
|
|
5ca2eb05d2 | ||
|
|
d5c81762a0 | ||
|
|
604eb1c19c | ||
|
|
6f418ac522 | ||
|
|
28467b6819 | ||
|
|
66b38e23b4 | ||
|
|
7b2c34bb61 | ||
|
|
a87f2fc9c1 | ||
|
|
3d799beccf | ||
|
|
5c312e5aea | ||
|
|
5d20c55fff | ||
|
|
1865a2e4ac | ||
|
|
e5a8061c1a | ||
|
|
5a85385d42 | ||
|
|
344d881d69 | ||
|
|
6b2961c2b0 | ||
|
|
03034a254b | ||
|
|
0d068ef410 | ||
|
|
0fe029fd43 | ||
|
|
198b4ecd04 | ||
|
|
f4568fa993 | ||
|
|
6d8c7264c7 | ||
|
|
f43a65b456 | ||
|
|
0ad7159d80 | ||
|
|
fe5d823418 | ||
|
|
311b09b093 | ||
|
|
9a69cc64f1 | ||
|
|
446fbdaa57 | ||
|
|
26a411e8cb | ||
|
|
e192a45285 | ||
|
|
8da4a1a428 | ||
|
|
95dea2286f | ||
|
|
0a4750f750 | ||
|
|
a0f79cd1b6 | ||
|
|
e48fc86529 | ||
|
|
3fe2ece82d | ||
|
|
49b5a5b3a1 | ||
|
|
49920d7aed | ||
|
|
d9d548684e | ||
|
|
8b00d65570 | ||
|
|
62d53ced37 | ||
|
|
7b2af26220 | ||
|
|
33d36f511e | ||
|
|
2e509b6b79 | ||
|
|
5bf753d86e | ||
|
|
fc05f34c6d | ||
|
|
2fceaa564b | ||
|
|
7c23621c4f | ||
|
|
1b2a3c6cdf | ||
|
|
9861f01ad8 | ||
|
|
4d164c1f14 | ||
|
|
2b11096d44 | ||
|
|
d403482bd9 | ||
|
|
a0337ac262 | ||
|
|
65c8677a12 | ||
|
|
c48b72054f | ||
|
|
c058f3bdf4 | ||
|
|
bc73419109 | ||
|
|
50c201fba0 | ||
|
|
8cb667e70e | ||
|
|
0c9dc8cc0f | ||
|
|
569f40fc21 | ||
|
|
1fbdd114af | ||
|
|
557c8e4070 | ||
|
|
6499f89c32 | ||
|
|
998788736d | ||
|
|
a943ecad27 | ||
|
|
54d5f90558 | ||
|
|
76ea24f81f | ||
|
|
b523c0b82e | ||
|
|
ce1e6456d9 | ||
|
|
6844c755d6 | ||
|
|
150384058e | ||
|
|
1a46e0c9d6 | ||
|
|
51c30e935c | ||
|
|
f2129709f1 | ||
|
|
9b846f1525 | ||
|
|
cdde8ddc1e | ||
|
|
4222bef1ea | ||
|
|
6dd55895b5 | ||
|
|
c995e00daf | ||
|
|
c1925e77fc | ||
|
|
6e9d5e8c3d | ||
|
|
4549ec4a18 | ||
|
|
7179db4653 | ||
|
|
ae6eda2b0c | ||
|
|
28afe5c1db | ||
|
|
729a8d47f6 | ||
|
|
3b7e71ddaf | ||
|
|
81047679f6 | ||
|
|
8fa5ea8d22 | ||
|
|
f8f44d143b | ||
|
|
2c9d4a2deb | ||
|
|
9179d4e326 | ||
|
|
f3268d0465 | ||
|
|
9bf4423f35 | ||
|
|
a4cbfbcbef | ||
|
|
d0bd4d56c0 | ||
|
|
aaeda00372 | ||
|
|
f289a7744e | ||
|
|
23e2ad495b | ||
|
|
9b4810bd3a | ||
|
|
bce663486a | ||
|
|
19c33e916b | ||
|
|
139bde3773 | ||
|
|
1c9b907317 | ||
|
|
cbf2f47d59 | ||
|
|
1fe2e238d3 | ||
|
|
af1946846a | ||
|
|
498b567d21 | ||
|
|
f8272f3ea1 | ||
|
|
3b04acd9b3 | ||
|
|
dcf226b659 | ||
|
|
cf3671e35e | ||
|
|
34ef60c07f | ||
|
|
9fdcaa60f0 | ||
|
|
8d92bc7144 | ||
|
|
19b5a67b2c | ||
|
|
3323ddb12b | ||
|
|
dc038ad497 | ||
|
|
23a5d1b67a | ||
|
|
a3c843cc8a | ||
|
|
bb770b6a1c | ||
|
|
945c04f9e7 | ||
|
|
9f55b0044e | ||
|
|
968941c84f | ||
|
|
c420c5b5ad | ||
|
|
ce297c233d | ||
|
|
8b723e58f0 | ||
|
|
f70cfcf0f4 | ||
|
|
919d541d6b | ||
|
|
ca733001b8 | ||
|
|
714f5ebe19 | ||
|
|
6cc64d588d | ||
|
|
66090bb109 | ||
|
|
9ef20523cd | ||
|
|
9a3b654ba9 | ||
|
|
0acacd2012 | ||
|
|
ffc922b8f1 | ||
|
|
9c646e49a6 | ||
|
|
d6cd482d9b |
15
.clang-format
Normal file
15
.clang-format
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
BasedOnStyle: WebKit
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
ColumnLimit: 120
|
||||
IndentCaseLabels: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerBindsToType: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
test_output
|
||||
|
||||
11
.travis.yml
11
.travis.yml
@ -1,8 +1,5 @@
|
||||
before_install:
|
||||
- pod install
|
||||
|
||||
language: objective-c
|
||||
xcode_sdk: iphonesimulator8.1
|
||||
install: true
|
||||
xcode_workspace: AxolotlKit.xcworkspace
|
||||
xcode_scheme: AxolotlKit
|
||||
|
||||
osx_image: xcode8.3
|
||||
|
||||
script: bin/ci
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
//
|
||||
// AxolotlInMemoryStore.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 17/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
|
||||
@interface AxolotlInMemoryStore : NSObject <AxolotlStore>
|
||||
|
||||
@end
|
||||
@ -1,180 +0,0 @@
|
||||
//
|
||||
// AxolotlInMemoryStore.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 17/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
|
||||
@interface AxolotlInMemoryStore ()
|
||||
|
||||
@property NSMutableDictionary *sessionRecords;
|
||||
|
||||
// Signed PreKey Store
|
||||
|
||||
@property NSMutableDictionary *preKeyStore;
|
||||
@property NSMutableDictionary *signedPreKeyStore;
|
||||
|
||||
@property NSMutableDictionary *trustedKeys;
|
||||
|
||||
@property ECKeyPair *_identityKeyPair;
|
||||
@property int _localRegistrationId;
|
||||
|
||||
@end
|
||||
|
||||
@implementation AxolotlInMemoryStore
|
||||
|
||||
|
||||
# pragma mark General
|
||||
|
||||
- (instancetype)init{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
self._identityKeyPair = [Curve25519 generateKeyPair];
|
||||
self._localRegistrationId = arc4random() % 16380;
|
||||
|
||||
_preKeyStore = [NSMutableDictionary dictionary];
|
||||
_signedPreKeyStore = [NSMutableDictionary dictionary];
|
||||
_trustedKeys = [NSMutableDictionary dictionary];
|
||||
_sessionRecords = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
# pragma mark Signed PreKey Store
|
||||
|
||||
- (SignedPreKeyRecord *)loadSignedPrekey:(int)signedPreKeyId{
|
||||
if (![[self.signedPreKeyStore allKeys] containsObject:[NSNumber numberWithInt:signedPreKeyId]]) {
|
||||
@throw [NSException exceptionWithName:InvalidKeyIdException reason:@"No such signedprekeyrecord" userInfo:nil];
|
||||
}
|
||||
|
||||
return [self.signedPreKeyStore objectForKey:[NSNumber numberWithInt:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray *)loadSignedPreKeys{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (SignedPreKeyRecord *signedPrekey in [self.signedPreKeyStore allValues]) {
|
||||
[results addObject:signedPrekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord{
|
||||
[self.signedPreKeyStore setObject:signedPreKeyRecord forKey:[NSNumber numberWithInteger:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId{
|
||||
if ([[self.signedPreKeyStore allKeys] containsObject:[NSNumber numberWithInteger:signedPreKeyId]]) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)removeSignedPreKey:(int)signedPrekeyId{
|
||||
[self.signedPreKeyStore removeObjectForKey:[NSNumber numberWithInteger:signedPrekeyId]];
|
||||
}
|
||||
|
||||
# pragma mark PreKey Store
|
||||
|
||||
- (PreKeyRecord *)loadPreKey:(int)preKeyId{
|
||||
if (![[self.preKeyStore allKeys] containsObject:[NSNumber numberWithInt:preKeyId]]) {
|
||||
@throw [NSException exceptionWithName:InvalidKeyIdException reason:@"No such signedprekeyrecord" userInfo:nil];
|
||||
}
|
||||
|
||||
return [self.preKeyStore objectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray *)loadPreKeys{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (PreKeyRecord *prekey in [self.preKeyStore allValues]) {
|
||||
[results addObject:prekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record{
|
||||
[self.preKeyStore setObject:record forKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (BOOL)containsPreKey:(int)preKeyId{
|
||||
if ([[self.preKeyStore allKeys] containsObject:[NSNumber numberWithInteger:preKeyId]]) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)removePreKey:(int)preKeyId{
|
||||
[self.preKeyStore removeObjectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
# pragma mark IdentityKeyStore
|
||||
|
||||
- (ECKeyPair *)identityKeyPair{
|
||||
return __identityKeyPair;
|
||||
}
|
||||
|
||||
- (int)localRegistrationId{
|
||||
return __localRegistrationId;
|
||||
}
|
||||
|
||||
- (void)saveRemoteIdentity:(NSData *)identityKey recipientId:(NSString*)recipientId{
|
||||
[self.trustedKeys setObject:identityKey forKey:recipientId];
|
||||
}
|
||||
|
||||
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey recipientId:(NSString*)recipientId{
|
||||
NSData *data = [self.trustedKeys objectForKey:recipientId];
|
||||
|
||||
if (data) {
|
||||
return [data isEqualToData:identityKey];
|
||||
}
|
||||
|
||||
return YES; // Trust on first use
|
||||
}
|
||||
|
||||
# pragma mark Session Store
|
||||
|
||||
-(SessionRecord*)loadSession:(NSString*)contactIdentifier deviceId:(int)deviceId{
|
||||
SessionRecord *sessionRecord = [[self deviceSessionRecordsForContactIdentifier:contactIdentifier] objectForKey:[NSNumber numberWithInteger:deviceId]];
|
||||
|
||||
if (!sessionRecord) {
|
||||
sessionRecord = [SessionRecord new];
|
||||
}
|
||||
|
||||
return sessionRecord;
|
||||
}
|
||||
|
||||
- (NSArray*)subDevicesSessions:(NSString*)contactIdentifier{
|
||||
return [[self deviceSessionRecordsForContactIdentifier:contactIdentifier] allKeys];
|
||||
}
|
||||
|
||||
- (NSDictionary*)deviceSessionRecordsForContactIdentifier:(NSString*)contactIdentifier{
|
||||
return [self.sessionRecords objectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
- (void)storeSession:(NSString*)contactIdentifier deviceId:(int)deviceId session:(SessionRecord *)session{
|
||||
NSAssert(session, @"Session can't be nil");
|
||||
[self.sessionRecords setObject:@{[NSNumber numberWithInt:deviceId]:session} forKey:contactIdentifier];
|
||||
}
|
||||
|
||||
- (BOOL)containsSession:(NSString*)contactIdentifier deviceId:(int)deviceId{
|
||||
|
||||
if ([[self.sessionRecords objectForKey:contactIdentifier] objectForKey:[NSNumber numberWithInt:deviceId]]){
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
//
|
||||
// AxolotlKit_Tests.m
|
||||
// AxolotlKit Tests
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface AxolotlKit_Tests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation AxolotlKit_Tests
|
||||
|
||||
@end
|
||||
@ -1,145 +0,0 @@
|
||||
//
|
||||
// SessionBuilder.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "SessionBuilder.h"
|
||||
#import "SessionCipher.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
|
||||
#import <25519/Ed25519.h>
|
||||
|
||||
|
||||
@interface PreKeyWhisperMessage ()
|
||||
@property (nonatomic, readwrite) NSData *identityKey;
|
||||
@property (nonatomic, readwrite) NSData *baseKey;
|
||||
@end
|
||||
|
||||
@interface SessionBuilderTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation SessionBuilderTests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing session initialization with a basic PrekeyWhisperMessage
|
||||
*/
|
||||
|
||||
- (void)testBasicPreKey {
|
||||
|
||||
NSString *BOB_RECIPIENT_ID = @"+3828923892";
|
||||
NSString *ALICE_RECIPIENT_ID = @"alice@gmail.com";
|
||||
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
SessionBuilder *aliceSessionBuilder = [[SessionBuilder alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
ECKeyPair *bobPreKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature = [Ed25519 sign:bobSignedPreKeyPair.publicKey withKeyPair:bobStore.identityKeyPair];
|
||||
|
||||
PreKeyBundle *bobPreKey = [[PreKeyBundle alloc]initWithRegistrationId:bobStore.localRegistrationId
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair.publicKey
|
||||
signedPreKeyPublic:bobSignedPreKeyPair.publicKey
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature
|
||||
identityKey:bobStore.identityKeyPair.publicKey];
|
||||
|
||||
[aliceSessionBuilder processPrekeyBundle:bobPreKey];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
|
||||
NSString *originalMessage = @"Freedom is the right to tell people what they do not want to hear.";
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
WhisperMessage *outgoingMessage = [aliceSessionCipher encryptMessage:[originalMessage dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
XCTAssert([outgoingMessage isKindOfClass:[PreKeyWhisperMessage class]], @"Message should be PreKey type");
|
||||
|
||||
PreKeyWhisperMessage *incomingMessage = (PreKeyWhisperMessage*)outgoingMessage;
|
||||
[bobStore storePreKey:31337 preKeyRecord:[[PreKeyRecord alloc] initWithId:bobPreKey.preKeyId keyPair:bobPreKeyPair]];
|
||||
[bobStore storeSignedPreKey:22 signedPreKeyRecord:[[SignedPreKeyRecord alloc] initWithId:22 keyPair:bobSignedPreKeyPair signature:bobSignedPreKeySignature generatedAt:[NSDate date]]];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:ALICE_RECIPIENT_ID deviceId:1];
|
||||
[bobSessionCipher decrypt:incomingMessage];
|
||||
|
||||
XCTAssert([bobStore containsSession:ALICE_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1].sessionState.aliceBaseKey != nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the case where an attacker would send a new PreKeyWhisperMessage with another IdentityKey
|
||||
*/
|
||||
|
||||
- (void)testBasicPreKeyMITM {
|
||||
|
||||
NSString *BOB_RECIPIENT_ID = @"+3828923892";
|
||||
NSString *ALICE_RECIPIENT_ID = @"alice@gmail.com";
|
||||
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
SessionBuilder *aliceSessionBuilder = [[SessionBuilder alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
ECKeyPair *bobPreKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature = [Ed25519 sign:bobSignedPreKeyPair.publicKey withKeyPair:bobStore.identityKeyPair];
|
||||
|
||||
PreKeyBundle *bobPreKey = [[PreKeyBundle alloc]initWithRegistrationId:bobStore.localRegistrationId
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair.publicKey
|
||||
signedPreKeyPublic:bobSignedPreKeyPair.publicKey
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature
|
||||
identityKey:bobStore.identityKeyPair.publicKey];
|
||||
|
||||
[aliceSessionBuilder processPrekeyBundle:bobPreKey];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
|
||||
NSString *originalMessage = @"Freedom is the right to tell people what they do not want to hear.";
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
WhisperMessage *outgoingMessage = [aliceSessionCipher encryptMessage:[originalMessage dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
XCTAssert([outgoingMessage isKindOfClass:[PreKeyWhisperMessage class]], @"Message should be PreKey type");
|
||||
|
||||
PreKeyWhisperMessage *incomingMessage = (PreKeyWhisperMessage*)outgoingMessage;
|
||||
[bobStore storePreKey:31337 preKeyRecord:[[PreKeyRecord alloc] initWithId:bobPreKey.preKeyId keyPair:bobPreKeyPair]];
|
||||
[bobStore storeSignedPreKey:22 signedPreKeyRecord:[[SignedPreKeyRecord alloc] initWithId:22 keyPair:bobSignedPreKeyPair signature:bobSignedPreKeySignature generatedAt:[NSDate date]]];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:ALICE_RECIPIENT_ID deviceId:1];
|
||||
[bobSessionCipher decrypt:incomingMessage];
|
||||
|
||||
XCTAssert([bobStore containsSession:ALICE_RECIPIENT_ID deviceId:1]);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1].sessionState.version == 3);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1].sessionState.aliceBaseKey != nil);
|
||||
|
||||
incomingMessage.identityKey = [Curve25519 generateKeyPair].publicKey;
|
||||
incomingMessage.baseKey = [Curve25519 generateKeyPair].publicKey;
|
||||
XCTAssertThrowsSpecificNamed([bobSessionCipher decrypt:incomingMessage], NSException, UntrustedIdentityKeyException);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@ -1,92 +0,0 @@
|
||||
//
|
||||
// SessionCipherTest.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 30/09/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "RatchetingSession.h"
|
||||
#import "SessionBuilder.h"
|
||||
#import "SessionCipher.h"
|
||||
#import "Chainkey.h"
|
||||
|
||||
#import "SessionState.h"
|
||||
|
||||
@interface SessionCipherTest : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation SessionCipherTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testBasicSession{
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
|
||||
[self sessionInitialization:aliceSessionRecord.sessionState bobSessionState:bobSessionRecord.sessionState];
|
||||
|
||||
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
|
||||
}
|
||||
|
||||
-(void)sessionInitialization:(SessionState*)aliceSessionState bobSessionState:(SessionState*)bobSessionState{
|
||||
|
||||
ECKeyPair *aliceIdentityKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *aliceBaseKey = [Curve25519 generateKeyPair];
|
||||
|
||||
ECKeyPair *bobIdentityKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobBaseKey = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobOneTimePK = [Curve25519 generateKeyPair];
|
||||
|
||||
AliceAxolotlParameters *aliceParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKeyPair theirIdentityKey:[bobIdentityKeyPair publicKey] ourBaseKey:aliceBaseKey theirSignedPreKey:[bobBaseKey publicKey] theirOneTimePreKey:[bobOneTimePK publicKey] theirRatchetKey:[bobBaseKey publicKey]];
|
||||
|
||||
BobAxolotlParameters *bobParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKeyPair theirIdentityKey:[aliceIdentityKeyPair publicKey] ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:bobOneTimePK theirBaseKey:[aliceBaseKey publicKey]];
|
||||
|
||||
[RatchetingSession initializeSession:bobSessionState sessionVersion:3 BobParameters:bobParams];
|
||||
|
||||
[RatchetingSession initializeSession:aliceSessionState sessionVersion:3 AliceParameters:aliceParams];
|
||||
|
||||
|
||||
XCTAssert([aliceSessionState.remoteIdentityKey isEqualToData:bobSessionState.localIdentityKey]);
|
||||
}
|
||||
|
||||
- (void)runInteractionWithAliceRecord:(SessionRecord*)aliceSessionRecord bobRecord:(SessionRecord*)bobSessionRecord {
|
||||
|
||||
NSString *aliceIdentifier = @"+3728378173821";
|
||||
NSString *bobIdentifier = @"bob@gmail.com";
|
||||
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord];
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:bobIdentifier deviceId:1];
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *alicePlainText = [@"This is a plaintext message!" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
WhisperMessage *cipherText = [aliceSessionCipher encryptMessage:alicePlainText];
|
||||
|
||||
NSData *bobPlaintext = [bobSessionCipher decrypt:cipherText];
|
||||
|
||||
XCTAssert([bobPlaintext isEqualToData:alicePlainText]);
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,15 +0,0 @@
|
||||
//
|
||||
// ECKeyPair+ECKeyPairTesting.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Curve25519.h"
|
||||
|
||||
@interface ECKeyPair (ECKeyPairTesting)
|
||||
|
||||
+(ECKeyPair*)keyPairWithPrivateKey:(NSData*)privateKey publicKey:(NSData*)publicKey;
|
||||
|
||||
@end
|
||||
@ -1,30 +0,0 @@
|
||||
//
|
||||
// ECKeyPair+ECKeyPairTesting.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
|
||||
@implementation ECKeyPair (testing)
|
||||
|
||||
+(ECKeyPair*)keyPairWithPrivateKey:(NSData*)privateKey publicKey:(NSData*)publicKey{
|
||||
if (([publicKey length] == 33)) {
|
||||
publicKey = [publicKey removeKeyType];
|
||||
}
|
||||
|
||||
if ([privateKey length] != ECCKeyLength && [publicKey length] != ECCKeyLength) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Public or Private key is not required size" userInfo:@{@"PrivateKey":privateKey, @"Public Key":publicKey}];
|
||||
}
|
||||
|
||||
ECKeyPair *keyPair = [ECKeyPair new];
|
||||
memcpy(keyPair->publicKey, [publicKey bytes], ECCKeyLength);
|
||||
memcpy(keyPair->privateKey, [privateKey bytes], ECCKeyLength);
|
||||
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,54 +0,0 @@
|
||||
//
|
||||
// Serialization.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
|
||||
#import "WhisperMessage.h"
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface WhisperMessageSerialization : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation WhisperMessageSerialization
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testWhisperMessage {
|
||||
ECKeyPair *keyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *fakeMacKey = [Curve25519 generateKeyPair];
|
||||
int counter = 0;
|
||||
NSData *cipherText = [@"I'm not really ciphertext" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
ECKeyPair *senderIdentityKey = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *receiverIdentityKey = [Curve25519 generateKeyPair];
|
||||
|
||||
WhisperMessage *message = [[WhisperMessage alloc] initWithVersion:3 macKey:fakeMacKey.publicKey senderRatchetKey:keyPair.publicKey counter:counter previousCounter:0 cipherText:cipherText senderIdentityKey:senderIdentityKey.publicKey receiverIdentityKey:receiverIdentityKey.publicKey];
|
||||
|
||||
WhisperMessage *deserializedMessage = [[WhisperMessage alloc] initWithData:message.serialized];
|
||||
|
||||
|
||||
XCTAssert([[message.serialized subdataWithRange:NSMakeRange(0, message.serialized.length-8)] isEqualToData:[deserializedMessage.serialized subdataWithRange:NSMakeRange(0, deserializedMessage.serialized.length-8)]]);
|
||||
|
||||
[message verifyMacWithVersion:3 senderIdentityKey:senderIdentityKey.publicKey receiverIdentityKey:receiverIdentityKey.publicKey macKey:fakeMacKey.publicKey];
|
||||
[deserializedMessage verifyMacWithVersion:3 senderIdentityKey:senderIdentityKey.publicKey receiverIdentityKey:receiverIdentityKey.publicKey macKey:fakeMacKey.publicKey];
|
||||
|
||||
XCTAssert([message.cipherText isEqualToData:deserializedMessage.cipherText]);
|
||||
XCTAssert(message.version == deserializedMessage.version);
|
||||
XCTAssert([message.serialized isEqualToData:deserializedMessage.serialized]);
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,19 +1,27 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "AxolotlKit"
|
||||
s.version = "0.6.2"
|
||||
s.summary = "AxolotlKit is a Free implementation of the Axolotl protocol in Objective-C"
|
||||
s.homepage = "https://github.com/WhisperSystems/AxolotlKit"
|
||||
s.license = "GPLv2"
|
||||
s.license = { :type => "GPLv2", :file => "LICENSE" }
|
||||
s.author = { "Frederic Jacobs" => "github@fredericjacobs.com" }
|
||||
s.social_media_url = "http://twitter.com/FredericJacobs"
|
||||
s.source = { :git => "https://github.com/WhisperSystems/AxolotlKit.git", :tag => "#{s.version}" }
|
||||
s.source_files = "AxolotlKit/Classes/*.{h,m}", "AxolotlKit/Classes/**/*.{h,m}"
|
||||
s.public_header_files = "AxolotlKit/Classes/*.{h}", "AxolotlKit/Classes/**/*.{h}"
|
||||
s.ios.deployment_target = "6.0"
|
||||
s.name = "AxolotlKit"
|
||||
s.version = "0.9.0"
|
||||
s.summary = "AxolotlKit is a Free implementation of the Axolotl protocol in Objective-C"
|
||||
s.homepage = "https://github.com/WhisperSystems/AxolotlKit"
|
||||
s.license = "GPLv2"
|
||||
s.license = { :type => "GPLv2", :file => "LICENSE" }
|
||||
s.author = { "Frederic Jacobs" => "github@fredericjacobs.com" }
|
||||
s.social_media_url = "http://twitter.com/FredericJacobs"
|
||||
s.source = { :git => "https://github.com/WhisperSystems/AxolotlKit.git", :tag => "#{s.version}" }
|
||||
s.source_files = "AxolotlKit/Classes/**/*.{h,m,swift}", "AxolotlKit/Private/*.{h,m,swift}"
|
||||
s.public_header_files = "AxolotlKit/Classes/**/*.{h}"
|
||||
s.prefix_header_file = "AxolotlKit/SPKPrefix.h"
|
||||
s.ios.deployment_target = "10.0"
|
||||
s.osx.deployment_target = "10.8"
|
||||
s.requires_arc = true
|
||||
s.dependency '25519', '~> 1.8'
|
||||
s.dependency 'HKDFKit', '~> 0.0.3'
|
||||
s.dependency 'ProtocolBuffers', '~> 1.9.7'
|
||||
s.requires_arc = true
|
||||
|
||||
s.dependency 'Curve25519Kit', '~> 2.1.0'
|
||||
s.dependency 'HKDFKit', '~> 0.0.3'
|
||||
s.dependency 'CocoaLumberjack'
|
||||
s.dependency 'SwiftProtobuf'
|
||||
s.dependency 'SignalCoreKit'
|
||||
|
||||
s.test_spec 'Tests' do |test_spec|
|
||||
test_spec.source_files = 'AxolotlKitTests/**/*.{h,m,swift}'
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,8 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
30BDB6D632952E18074FD70B /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AD9F87ECA041E727CC457415 /* libPods.a */; };
|
||||
719C4E40588B7AD1F1DD22FC /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AD9F87ECA041E727CC457415 /* libPods.a */; };
|
||||
57470C183587C149ABC5FAB1 /* Pods_AxolotlKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B975DDFFBFB50034D581DD9 /* Pods_AxolotlKitTests.framework */; };
|
||||
B619AF1419FC148D00E33198 /* ProtobuffsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B619AF1319FC148D00E33198 /* ProtobuffsTests.m */; };
|
||||
B61E841E19D17C33004FC382 /* HKDFTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B61E841C19D1780D004FC382 /* HKDFTest.m */; };
|
||||
B62EFBF01A9140190072ADD3 /* PreKeyBundleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B62EFBEF1A9140190072ADD3 /* PreKeyBundleTests.m */; };
|
||||
@ -31,32 +30,8 @@
|
||||
B6B3E64419FD5C8E0035422D /* ECKeyPair+ECKeyPairTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B3E64219FD5BC70035422D /* ECKeyPair+ECKeyPairTesting.m */; };
|
||||
B6BB8C3519F7E773008A5895 /* SessionBuilderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B6BB8C3419F7E773008A5895 /* SessionBuilderTests.m */; };
|
||||
B6D41A2319DB4F8E00EE9BAA /* SessionCipherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B6D41A2219DB4F8E00EE9BAA /* SessionCipherTest.m */; };
|
||||
B6E07F7A19FE49010091446D /* PreKeyWhisperMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F3919FE49010091446D /* PreKeyWhisperMessage.m */; };
|
||||
B6E07F7B19FE49010091446D /* WhisperMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F3B19FE49010091446D /* WhisperMessage.m */; };
|
||||
B6E07F7C19FE49010091446D /* AES-CBC.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F3F19FE49010091446D /* AES-CBC.m */; };
|
||||
B6E07F7D19FE49010091446D /* PreKeyBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F4419FE49010091446D /* PreKeyBundle.m */; };
|
||||
B6E07F7E19FE49010091446D /* PreKeyRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F4619FE49010091446D /* PreKeyRecord.m */; };
|
||||
B6E07F7F19FE49010091446D /* SignedPrekeyRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F4819FE49010091446D /* SignedPrekeyRecord.m */; };
|
||||
B6E07F8019FE49010091446D /* WhisperTextProtocol.pb.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F4B19FE49010091446D /* WhisperTextProtocol.pb.m */; };
|
||||
B6E07F8119FE49010091446D /* WhisperTextProtocol.proto in Resources */ = {isa = PBXBuildFile; fileRef = B6E07F4C19FE49010091446D /* WhisperTextProtocol.proto */; };
|
||||
B6E07F8219FE49010091446D /* AliceAxolotlParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F4F19FE49010091446D /* AliceAxolotlParameters.m */; };
|
||||
B6E07F8319FE49010091446D /* BobAxolotlParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5219FE49010091446D /* BobAxolotlParameters.m */; };
|
||||
B6E07F8419FE49010091446D /* ChainAndIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5519FE49010091446D /* ChainAndIndex.m */; };
|
||||
B6E07F8519FE49010091446D /* ChainKey.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5719FE49010091446D /* ChainKey.m */; };
|
||||
B6E07F8619FE49010091446D /* MessageKeys.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5919FE49010091446D /* MessageKeys.m */; };
|
||||
B6E07F8719FE49010091446D /* RatchetingSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5B19FE49010091446D /* RatchetingSession.m */; };
|
||||
B6E07F8819FE49010091446D /* ReceivingChain.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5D19FE49010091446D /* ReceivingChain.m */; };
|
||||
B6E07F8919FE49010091446D /* RKCK.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F5F19FE49010091446D /* RKCK.m */; };
|
||||
B6E07F8A19FE49010091446D /* RootKey.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6119FE49010091446D /* RootKey.m */; };
|
||||
B6E07F8B19FE49010091446D /* SendingChain.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6319FE49010091446D /* SendingChain.m */; };
|
||||
B6E07F8C19FE49010091446D /* TSDerivedSecrets.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6519FE49010091446D /* TSDerivedSecrets.m */; };
|
||||
B6E07F8D19FE49010091446D /* SessionCipher.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6719FE49010091446D /* SessionCipher.m */; };
|
||||
B6E07F8E19FE49010091446D /* SessionBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6A19FE49010091446D /* SessionBuilder.m */; };
|
||||
B6E07F8F19FE49010091446D /* SessionRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6C19FE49010091446D /* SessionRecord.m */; };
|
||||
B6E07F9019FE49010091446D /* SessionState.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F6E19FE49010091446D /* SessionState.m */; };
|
||||
B6E07F9119FE49010091446D /* NSData+keyVersionByte.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F7719FE49010091446D /* NSData+keyVersionByte.m */; };
|
||||
B6E07F9219FE49010091446D /* SerializationUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E07F7919FE49010091446D /* SerializationUtilities.m */; };
|
||||
B6E37AE019CF107C00E619CA /* ECCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E37ADF19CF107C00E619CA /* ECCTests.m */; };
|
||||
D75CF4C560CF9758A0E0550F /* Pods_AxolotlKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF81D400C79761E2EBFB1BEA /* Pods_AxolotlKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -70,7 +45,10 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
AD9F87ECA041E727CC457415 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1B975DDFFBFB50034D581DD9 /* Pods_AxolotlKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AxolotlKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
273B59DE0B6958AC58F56CBC /* Pods-AxolotlKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AxolotlKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AxolotlKit/Pods-AxolotlKit.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
535B95F3C3486538E9B66E8B /* Pods-AxolotlKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AxolotlKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AxolotlKitTests/Pods-AxolotlKitTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
A301BD7DC87662E4F12CE89C /* Pods-AxolotlKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AxolotlKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-AxolotlKit/Pods-AxolotlKit.release.xcconfig"; sourceTree = "<group>"; };
|
||||
B619AF1319FC148D00E33198 /* ProtobuffsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtobuffsTests.m; sourceTree = "<group>"; };
|
||||
B61E841C19D1780D004FC382 /* HKDFTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKDFTest.m; sourceTree = "<group>"; };
|
||||
B62EFBEF1A9140190072ADD3 /* PreKeyBundleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreKeyBundleTests.m; sourceTree = "<group>"; };
|
||||
@ -97,71 +75,11 @@
|
||||
B6B3E63B19FD3D8A0035422D /* WhisperMessageSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WhisperMessageSerialization.m; sourceTree = "<group>"; };
|
||||
B6B3E64119FD5BC70035422D /* ECKeyPair+ECKeyPairTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ECKeyPair+ECKeyPairTesting.h"; path = "Util/ECKeyPair+ECKeyPairTesting.h"; sourceTree = "<group>"; };
|
||||
B6B3E64219FD5BC70035422D /* ECKeyPair+ECKeyPairTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "ECKeyPair+ECKeyPairTesting.m"; path = "Util/ECKeyPair+ECKeyPairTesting.m"; sourceTree = "<group>"; };
|
||||
B6B98F7D197D838A00B16B5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B6BB8C3419F7E773008A5895 /* SessionBuilderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionBuilderTests.m; sourceTree = "<group>"; };
|
||||
B6D41A2219DB4F8E00EE9BAA /* SessionCipherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionCipherTest.m; sourceTree = "<group>"; };
|
||||
B6E07F3519FE49010091446D /* AxolotlExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AxolotlExceptions.h; sourceTree = "<group>"; };
|
||||
B6E07F3719FE49010091446D /* CipherMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CipherMessage.h; sourceTree = "<group>"; };
|
||||
B6E07F3819FE49010091446D /* PreKeyWhisperMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreKeyWhisperMessage.h; sourceTree = "<group>"; };
|
||||
B6E07F3919FE49010091446D /* PreKeyWhisperMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreKeyWhisperMessage.m; sourceTree = "<group>"; };
|
||||
B6E07F3A19FE49010091446D /* WhisperMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WhisperMessage.h; sourceTree = "<group>"; };
|
||||
B6E07F3B19FE49010091446D /* WhisperMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WhisperMessage.m; sourceTree = "<group>"; };
|
||||
B6E07F3C19FE49010091446D /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
|
||||
B6E07F3E19FE49010091446D /* AES-CBC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AES-CBC.h"; sourceTree = "<group>"; };
|
||||
B6E07F3F19FE49010091446D /* AES-CBC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AES-CBC.m"; sourceTree = "<group>"; };
|
||||
B6E07F4319FE49010091446D /* PreKeyBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreKeyBundle.h; sourceTree = "<group>"; };
|
||||
B6E07F4419FE49010091446D /* PreKeyBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreKeyBundle.m; sourceTree = "<group>"; };
|
||||
B6E07F4519FE49010091446D /* PreKeyRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreKeyRecord.h; sourceTree = "<group>"; };
|
||||
B6E07F4619FE49010091446D /* PreKeyRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreKeyRecord.m; sourceTree = "<group>"; };
|
||||
B6E07F4719FE49010091446D /* SignedPrekeyRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignedPrekeyRecord.h; sourceTree = "<group>"; };
|
||||
B6E07F4819FE49010091446D /* SignedPrekeyRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignedPrekeyRecord.m; sourceTree = "<group>"; };
|
||||
B6E07F4A19FE49010091446D /* WhisperTextProtocol.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WhisperTextProtocol.pb.h; sourceTree = "<group>"; };
|
||||
B6E07F4B19FE49010091446D /* WhisperTextProtocol.pb.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WhisperTextProtocol.pb.m; sourceTree = "<group>"; };
|
||||
B6E07F4C19FE49010091446D /* WhisperTextProtocol.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WhisperTextProtocol.proto; sourceTree = "<group>"; };
|
||||
B6E07F4E19FE49010091446D /* AliceAxolotlParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AliceAxolotlParameters.h; sourceTree = "<group>"; };
|
||||
B6E07F4F19FE49010091446D /* AliceAxolotlParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AliceAxolotlParameters.m; sourceTree = "<group>"; };
|
||||
B6E07F5019FE49010091446D /* AxolotlParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AxolotlParameters.h; sourceTree = "<group>"; };
|
||||
B6E07F5119FE49010091446D /* BobAxolotlParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BobAxolotlParameters.h; sourceTree = "<group>"; };
|
||||
B6E07F5219FE49010091446D /* BobAxolotlParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BobAxolotlParameters.m; sourceTree = "<group>"; };
|
||||
B6E07F5319FE49010091446D /* Chain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chain.h; sourceTree = "<group>"; };
|
||||
B6E07F5419FE49010091446D /* ChainAndIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChainAndIndex.h; sourceTree = "<group>"; };
|
||||
B6E07F5519FE49010091446D /* ChainAndIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChainAndIndex.m; sourceTree = "<group>"; };
|
||||
B6E07F5619FE49010091446D /* ChainKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChainKey.h; sourceTree = "<group>"; };
|
||||
B6E07F5719FE49010091446D /* ChainKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChainKey.m; sourceTree = "<group>"; };
|
||||
B6E07F5819FE49010091446D /* MessageKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageKeys.h; sourceTree = "<group>"; };
|
||||
B6E07F5919FE49010091446D /* MessageKeys.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessageKeys.m; sourceTree = "<group>"; };
|
||||
B6E07F5A19FE49010091446D /* RatchetingSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RatchetingSession.h; sourceTree = "<group>"; };
|
||||
B6E07F5B19FE49010091446D /* RatchetingSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RatchetingSession.m; sourceTree = "<group>"; };
|
||||
B6E07F5C19FE49010091446D /* ReceivingChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReceivingChain.h; sourceTree = "<group>"; };
|
||||
B6E07F5D19FE49010091446D /* ReceivingChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReceivingChain.m; sourceTree = "<group>"; };
|
||||
B6E07F5E19FE49010091446D /* RKCK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKCK.h; sourceTree = "<group>"; };
|
||||
B6E07F5F19FE49010091446D /* RKCK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKCK.m; sourceTree = "<group>"; };
|
||||
B6E07F6019FE49010091446D /* RootKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootKey.h; sourceTree = "<group>"; };
|
||||
B6E07F6119FE49010091446D /* RootKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootKey.m; sourceTree = "<group>"; };
|
||||
B6E07F6219FE49010091446D /* SendingChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendingChain.h; sourceTree = "<group>"; };
|
||||
B6E07F6319FE49010091446D /* SendingChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SendingChain.m; sourceTree = "<group>"; };
|
||||
B6E07F6419FE49010091446D /* TSDerivedSecrets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSDerivedSecrets.h; sourceTree = "<group>"; };
|
||||
B6E07F6519FE49010091446D /* TSDerivedSecrets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSDerivedSecrets.m; sourceTree = "<group>"; };
|
||||
B6E07F6619FE49010091446D /* SessionCipher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SessionCipher.h; sourceTree = "<group>"; };
|
||||
B6E07F6719FE49010091446D /* SessionCipher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionCipher.m; sourceTree = "<group>"; };
|
||||
B6E07F6919FE49010091446D /* SessionBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SessionBuilder.h; sourceTree = "<group>"; };
|
||||
B6E07F6A19FE49010091446D /* SessionBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionBuilder.m; sourceTree = "<group>"; };
|
||||
B6E07F6B19FE49010091446D /* SessionRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SessionRecord.h; sourceTree = "<group>"; };
|
||||
B6E07F6C19FE49010091446D /* SessionRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionRecord.m; sourceTree = "<group>"; };
|
||||
B6E07F6D19FE49010091446D /* SessionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SessionState.h; sourceTree = "<group>"; };
|
||||
B6E07F6E19FE49010091446D /* SessionState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SessionState.m; sourceTree = "<group>"; };
|
||||
B6E07F7019FE49010091446D /* AxolotlStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AxolotlStore.h; sourceTree = "<group>"; };
|
||||
B6E07F7119FE49010091446D /* IdentityKeyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IdentityKeyStore.h; sourceTree = "<group>"; };
|
||||
B6E07F7219FE49010091446D /* PreKeyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreKeyStore.h; sourceTree = "<group>"; };
|
||||
B6E07F7319FE49010091446D /* SessionStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SessionStore.h; sourceTree = "<group>"; };
|
||||
B6E07F7419FE49010091446D /* SignedPreKeyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignedPreKeyStore.h; sourceTree = "<group>"; };
|
||||
B6E07F7619FE49010091446D /* NSData+keyVersionByte.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+keyVersionByte.h"; sourceTree = "<group>"; };
|
||||
B6E07F7719FE49010091446D /* NSData+keyVersionByte.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+keyVersionByte.m"; sourceTree = "<group>"; };
|
||||
B6E07F7819FE49010091446D /* SerializationUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SerializationUtilities.h; sourceTree = "<group>"; };
|
||||
B6E07F7919FE49010091446D /* SerializationUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SerializationUtilities.m; sourceTree = "<group>"; };
|
||||
B6E37ADF19CF107C00E619CA /* ECCTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ECCTests.m; sourceTree = "<group>"; };
|
||||
C47E7DDC756D06556C4541B0 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
F1351BCBFEF67F0F64D4C98C /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
F18946391DB22E6D23D4168E /* Pods-AxolotlKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AxolotlKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AxolotlKitTests/Pods-AxolotlKitTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
FF81D400C79761E2EBFB1BEA /* Pods_AxolotlKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AxolotlKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -172,7 +90,7 @@
|
||||
B63B2FA1197D8AFD00E09F65 /* CoreGraphics.framework in Frameworks */,
|
||||
B63B2FA3197D8AFD00E09F65 /* UIKit.framework in Frameworks */,
|
||||
B63B2F9F197D8AFD00E09F65 /* Foundation.framework in Frameworks */,
|
||||
30BDB6D632952E18074FD70B /* libPods.a in Frameworks */,
|
||||
D75CF4C560CF9758A0E0550F /* Pods_AxolotlKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -183,7 +101,7 @@
|
||||
B63B2FDC197D8B9600E09F65 /* XCTest.framework in Frameworks */,
|
||||
B63B2FDE197D8B9600E09F65 /* UIKit.framework in Frameworks */,
|
||||
B63B2FDD197D8B9600E09F65 /* Foundation.framework in Frameworks */,
|
||||
719C4E40588B7AD1F1DD22FC /* libPods.a in Frameworks */,
|
||||
57470C183587C149ABC5FAB1 /* Pods_AxolotlKitTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -197,7 +115,8 @@
|
||||
B63B2FA0197D8AFD00E09F65 /* CoreGraphics.framework */,
|
||||
B63B2FA2197D8AFD00E09F65 /* UIKit.framework */,
|
||||
B63B2FB7197D8AFD00E09F65 /* XCTest.framework */,
|
||||
AD9F87ECA041E727CC457415 /* libPods.a */,
|
||||
FF81D400C79761E2EBFB1BEA /* Pods_AxolotlKit.framework */,
|
||||
1B975DDFFBFB50034D581DD9 /* Pods_AxolotlKitTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -272,7 +191,8 @@
|
||||
B63B2FE0197D8B9600E09F65 /* Supporting Files */,
|
||||
B6B3E64019FD5B0D0035422D /* Util */,
|
||||
);
|
||||
path = "AxolotlKit Tests";
|
||||
name = "AxolotlKit Tests";
|
||||
path = AxolotlKitTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B63B2FE0197D8B9600E09F65 /* Supporting Files */ = {
|
||||
@ -300,11 +220,10 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B63B2FA4197D8AFD00E09F65 /* AxolotlKit App */,
|
||||
B6B98F7B197D838A00B16B5E /* AxolotlKit */,
|
||||
B63B2FDF197D8B9600E09F65 /* AxolotlKit Tests */,
|
||||
B6B98F7A197D838A00B16B5E /* Products */,
|
||||
A616BD9DC39D4B7BBBA02F80 /* Frameworks */,
|
||||
F56DC7A75D226F3057DF227B /* Pods */,
|
||||
E253D8CE20A28C7C5B6EF9D0 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -317,166 +236,13 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6B98F7B197D838A00B16B5E /* AxolotlKit */ = {
|
||||
E253D8CE20A28C7C5B6EF9D0 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F3419FE49010091446D /* Classes */,
|
||||
B6B98F7C197D838A00B16B5E /* Supporting Files */,
|
||||
);
|
||||
path = AxolotlKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6B98F7C197D838A00B16B5E /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6B98F7D197D838A00B16B5E /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F3419FE49010091446D /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F3519FE49010091446D /* AxolotlExceptions.h */,
|
||||
B6E07F3619FE49010091446D /* CipherMessage */,
|
||||
B6E07F3C19FE49010091446D /* Constants.h */,
|
||||
B6E07F3D19FE49010091446D /* Crypto */,
|
||||
B6E07F4019FE49010091446D /* Groups */,
|
||||
B6E07F4219FE49010091446D /* Prekeys */,
|
||||
B6E07F4919FE49010091446D /* Protobuffs */,
|
||||
B6E07F4D19FE49010091446D /* Ratchet */,
|
||||
B6E07F6619FE49010091446D /* SessionCipher.h */,
|
||||
B6E07F6719FE49010091446D /* SessionCipher.m */,
|
||||
B6E07F6819FE49010091446D /* Sessions */,
|
||||
B6E07F6F19FE49010091446D /* State */,
|
||||
B6E07F7519FE49010091446D /* Utility */,
|
||||
);
|
||||
path = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F3619FE49010091446D /* CipherMessage */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F3719FE49010091446D /* CipherMessage.h */,
|
||||
B6E07F3819FE49010091446D /* PreKeyWhisperMessage.h */,
|
||||
B6E07F3919FE49010091446D /* PreKeyWhisperMessage.m */,
|
||||
B6E07F3A19FE49010091446D /* WhisperMessage.h */,
|
||||
B6E07F3B19FE49010091446D /* WhisperMessage.m */,
|
||||
);
|
||||
path = CipherMessage;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F3D19FE49010091446D /* Crypto */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F3E19FE49010091446D /* AES-CBC.h */,
|
||||
B6E07F3F19FE49010091446D /* AES-CBC.m */,
|
||||
);
|
||||
path = Crypto;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F4019FE49010091446D /* Groups */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Groups;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F4219FE49010091446D /* Prekeys */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F4319FE49010091446D /* PreKeyBundle.h */,
|
||||
B6E07F4419FE49010091446D /* PreKeyBundle.m */,
|
||||
B6E07F4519FE49010091446D /* PreKeyRecord.h */,
|
||||
B6E07F4619FE49010091446D /* PreKeyRecord.m */,
|
||||
B6E07F4719FE49010091446D /* SignedPrekeyRecord.h */,
|
||||
B6E07F4819FE49010091446D /* SignedPrekeyRecord.m */,
|
||||
);
|
||||
path = Prekeys;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F4919FE49010091446D /* Protobuffs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F4A19FE49010091446D /* WhisperTextProtocol.pb.h */,
|
||||
B6E07F4B19FE49010091446D /* WhisperTextProtocol.pb.m */,
|
||||
B6E07F4C19FE49010091446D /* WhisperTextProtocol.proto */,
|
||||
);
|
||||
path = Protobuffs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F4D19FE49010091446D /* Ratchet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F4E19FE49010091446D /* AliceAxolotlParameters.h */,
|
||||
B6E07F4F19FE49010091446D /* AliceAxolotlParameters.m */,
|
||||
B6E07F5019FE49010091446D /* AxolotlParameters.h */,
|
||||
B6E07F5119FE49010091446D /* BobAxolotlParameters.h */,
|
||||
B6E07F5219FE49010091446D /* BobAxolotlParameters.m */,
|
||||
B6E07F5319FE49010091446D /* Chain.h */,
|
||||
B6E07F5419FE49010091446D /* ChainAndIndex.h */,
|
||||
B6E07F5519FE49010091446D /* ChainAndIndex.m */,
|
||||
B6E07F5619FE49010091446D /* ChainKey.h */,
|
||||
B6E07F5719FE49010091446D /* ChainKey.m */,
|
||||
B6E07F5819FE49010091446D /* MessageKeys.h */,
|
||||
B6E07F5919FE49010091446D /* MessageKeys.m */,
|
||||
B6E07F5A19FE49010091446D /* RatchetingSession.h */,
|
||||
B6E07F5B19FE49010091446D /* RatchetingSession.m */,
|
||||
B6E07F5C19FE49010091446D /* ReceivingChain.h */,
|
||||
B6E07F5D19FE49010091446D /* ReceivingChain.m */,
|
||||
B6E07F5E19FE49010091446D /* RKCK.h */,
|
||||
B6E07F5F19FE49010091446D /* RKCK.m */,
|
||||
B6E07F6019FE49010091446D /* RootKey.h */,
|
||||
B6E07F6119FE49010091446D /* RootKey.m */,
|
||||
B6E07F6219FE49010091446D /* SendingChain.h */,
|
||||
B6E07F6319FE49010091446D /* SendingChain.m */,
|
||||
B6E07F6419FE49010091446D /* TSDerivedSecrets.h */,
|
||||
B6E07F6519FE49010091446D /* TSDerivedSecrets.m */,
|
||||
);
|
||||
path = Ratchet;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F6819FE49010091446D /* Sessions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F6919FE49010091446D /* SessionBuilder.h */,
|
||||
B6E07F6A19FE49010091446D /* SessionBuilder.m */,
|
||||
B6E07F6B19FE49010091446D /* SessionRecord.h */,
|
||||
B6E07F6C19FE49010091446D /* SessionRecord.m */,
|
||||
B6E07F6D19FE49010091446D /* SessionState.h */,
|
||||
B6E07F6E19FE49010091446D /* SessionState.m */,
|
||||
);
|
||||
path = Sessions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F6F19FE49010091446D /* State */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F7019FE49010091446D /* AxolotlStore.h */,
|
||||
B6E07F7119FE49010091446D /* IdentityKeyStore.h */,
|
||||
B6E07F7219FE49010091446D /* PreKeyStore.h */,
|
||||
B6E07F7319FE49010091446D /* SessionStore.h */,
|
||||
B6E07F7419FE49010091446D /* SignedPreKeyStore.h */,
|
||||
);
|
||||
path = State;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6E07F7519FE49010091446D /* Utility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6E07F7619FE49010091446D /* NSData+keyVersionByte.h */,
|
||||
B6E07F7719FE49010091446D /* NSData+keyVersionByte.m */,
|
||||
B6E07F7819FE49010091446D /* SerializationUtilities.h */,
|
||||
B6E07F7919FE49010091446D /* SerializationUtilities.m */,
|
||||
);
|
||||
path = Utility;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F56DC7A75D226F3057DF227B /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C47E7DDC756D06556C4541B0 /* Pods.debug.xcconfig */,
|
||||
F1351BCBFEF67F0F64D4C98C /* Pods.release.xcconfig */,
|
||||
273B59DE0B6958AC58F56CBC /* Pods-AxolotlKit.debug.xcconfig */,
|
||||
A301BD7DC87662E4F12CE89C /* Pods-AxolotlKit.release.xcconfig */,
|
||||
F18946391DB22E6D23D4168E /* Pods-AxolotlKitTests.debug.xcconfig */,
|
||||
535B95F3C3486538E9B66E8B /* Pods-AxolotlKitTests.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
@ -488,11 +254,12 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B63B2FC5197D8AFE00E09F65 /* Build configuration list for PBXNativeTarget "AxolotlKit" */;
|
||||
buildPhases = (
|
||||
2873D560FFC1302C095EDD03 /* Check Pods Manifest.lock */,
|
||||
CF91F5B0680A0D6B156CC067 /* [CP] Check Pods Manifest.lock */,
|
||||
B63B2F99197D8AFD00E09F65 /* Sources */,
|
||||
B63B2F9A197D8AFD00E09F65 /* Frameworks */,
|
||||
B63B2F9B197D8AFD00E09F65 /* Resources */,
|
||||
9C925C121D5B8E571ED994DC /* Copy Pods Resources */,
|
||||
1DFAB5EBB173B09F224A6D30 /* [CP] Embed Pods Frameworks */,
|
||||
8CB33842ECF2BD7AD5FB982F /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -507,11 +274,12 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B63B2FEA197D8B9600E09F65 /* Build configuration list for PBXNativeTarget "AxolotlKitTests" */;
|
||||
buildPhases = (
|
||||
99597A930ADDD17377AADBA4 /* Check Pods Manifest.lock */,
|
||||
F7D1D7822CA76333BE3F65B0 /* [CP] Check Pods Manifest.lock */,
|
||||
B63B2FD7197D8B9600E09F65 /* Sources */,
|
||||
B63B2FD8197D8B9600E09F65 /* Frameworks */,
|
||||
B63B2FD9197D8B9600E09F65 /* Resources */,
|
||||
A51F70DA7E9DF4FF44373955 /* Copy Pods Resources */,
|
||||
72AE5FCBF75F8EC4F08F3417 /* [CP] Embed Pods Frameworks */,
|
||||
826D88D030861B8DEF83B1EA /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -529,7 +297,7 @@
|
||||
B6B98F70197D838A00B16B5E /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0600;
|
||||
LastUpgradeCheck = 0830;
|
||||
ORGANIZATIONNAME = "Frederic Jacobs";
|
||||
TargetAttributes = {
|
||||
B63B2FDA197D8B9600E09F65 = {
|
||||
@ -542,6 +310,7 @@
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = B6B98F6F197D838A00B16B5E;
|
||||
@ -560,7 +329,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B6E07F8119FE49010091446D /* WhisperTextProtocol.proto in Resources */,
|
||||
B63B2FA9197D8AFD00E09F65 /* InfoPlist.strings in Resources */,
|
||||
B63B2FB1197D8AFD00E09F65 /* Images.xcassets in Resources */,
|
||||
);
|
||||
@ -577,64 +345,111 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
2873D560FFC1302C095EDD03 /* Check Pods Manifest.lock */ = {
|
||||
1DFAB5EBB173B09F224A6D30 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-AxolotlKit/Pods-AxolotlKit-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/AxolotlKit/AxolotlKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Curve25519Kit/Curve25519Kit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/HKDFKit/HKDFKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/ProtocolBuffers/ProtocolBuffers.framework",
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AxolotlKit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Curve25519Kit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HKDFKit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtocolBuffers.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AxolotlKit/Pods-AxolotlKit-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
99597A930ADDD17377AADBA4 /* Check Pods Manifest.lock */ = {
|
||||
72AE5FCBF75F8EC4F08F3417 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AxolotlKitTests/Pods-AxolotlKitTests-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9C925C121D5B8E571ED994DC /* Copy Pods Resources */ = {
|
||||
826D88D030861B8DEF83B1EA /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Pods Resources";
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AxolotlKitTests/Pods-AxolotlKitTests-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
A51F70DA7E9DF4FF44373955 /* Copy Pods Resources */ = {
|
||||
8CB33842ECF2BD7AD5FB982F /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Pods Resources";
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AxolotlKit/Pods-AxolotlKit-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
CF91F5B0680A0D6B156CC067 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-AxolotlKit-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F7D1D7822CA76333BE3F65B0 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-AxolotlKitTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@ -644,33 +459,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B6E07F8819FE49010091446D /* ReceivingChain.m in Sources */,
|
||||
B6E07F8419FE49010091446D /* ChainAndIndex.m in Sources */,
|
||||
B6E07F9219FE49010091446D /* SerializationUtilities.m in Sources */,
|
||||
B6E07F8919FE49010091446D /* RKCK.m in Sources */,
|
||||
B6E07F8519FE49010091446D /* ChainKey.m in Sources */,
|
||||
B6E07F7E19FE49010091446D /* PreKeyRecord.m in Sources */,
|
||||
B6E07F7A19FE49010091446D /* PreKeyWhisperMessage.m in Sources */,
|
||||
B6E07F8019FE49010091446D /* WhisperTextProtocol.pb.m in Sources */,
|
||||
B6E07F9019FE49010091446D /* SessionState.m in Sources */,
|
||||
B6E07F8319FE49010091446D /* BobAxolotlParameters.m in Sources */,
|
||||
B6E07F8619FE49010091446D /* MessageKeys.m in Sources */,
|
||||
B6E07F7B19FE49010091446D /* WhisperMessage.m in Sources */,
|
||||
B6E07F8219FE49010091446D /* AliceAxolotlParameters.m in Sources */,
|
||||
B6B3E64319FD5BC70035422D /* ECKeyPair+ECKeyPairTesting.m in Sources */,
|
||||
B6E07F7D19FE49010091446D /* PreKeyBundle.m in Sources */,
|
||||
B6E07F8F19FE49010091446D /* SessionRecord.m in Sources */,
|
||||
B6E07F8719FE49010091446D /* RatchetingSession.m in Sources */,
|
||||
B63B2FAF197D8AFD00E09F65 /* AppDelegate.m in Sources */,
|
||||
B6E07F9119FE49010091446D /* NSData+keyVersionByte.m in Sources */,
|
||||
B6E07F7C19FE49010091446D /* AES-CBC.m in Sources */,
|
||||
B6E07F8B19FE49010091446D /* SendingChain.m in Sources */,
|
||||
B6E07F8D19FE49010091446D /* SessionCipher.m in Sources */,
|
||||
B6E07F8C19FE49010091446D /* TSDerivedSecrets.m in Sources */,
|
||||
B6E07F7F19FE49010091446D /* SignedPrekeyRecord.m in Sources */,
|
||||
B6E07F8A19FE49010091446D /* RootKey.m in Sources */,
|
||||
B63B2FAB197D8AFD00E09F65 /* main.m in Sources */,
|
||||
B6E07F8E19FE49010091446D /* SessionBuilder.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -724,17 +515,20 @@
|
||||
/* Begin XCBuildConfiguration section */
|
||||
B63B2FC6197D8AFE00E09F65 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C47E7DDC756D06556C4541B0 /* Pods.debug.xcconfig */;
|
||||
baseConfigurationReference = 273B59DE0B6958AC58F56CBC /* Pods-AxolotlKit.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AxolotlKit/AxolotlKit-Prefix.pch";
|
||||
GCC_PREFIX_HEADER = "";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "AxolotlKit/AxolotlKit-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.whispersystems.SignalProtocolKitTestApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
@ -742,13 +536,16 @@
|
||||
};
|
||||
B63B2FC7197D8AFE00E09F65 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = F1351BCBFEF67F0F64D4C98C /* Pods.release.xcconfig */;
|
||||
baseConfigurationReference = A301BD7DC87662E4F12CE89C /* Pods-AxolotlKit.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AxolotlKit/AxolotlKit-Prefix.pch";
|
||||
GCC_PREFIX_HEADER = "";
|
||||
INFOPLIST_FILE = "AxolotlKit/AxolotlKit-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.whispersystems.SignalProtocolKitTestApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
@ -756,49 +553,52 @@
|
||||
};
|
||||
B63B2FEB197D8B9600E09F65 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C47E7DDC756D06556C4541B0 /* Pods.debug.xcconfig */;
|
||||
baseConfigurationReference = F18946391DB22E6D23D4168E /* Pods-AxolotlKitTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/AxolotlKit.app/AxolotlKit";
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AxolotlKit Tests/AxolotlKit Tests-Prefix.pch";
|
||||
GCC_PREFIX_HEADER = "AxolotlKitTests/AxolotlKit Tests-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "AxolotlKit Tests/AxolotlKit Tests-Info.plist";
|
||||
INFOPLIST_FILE = "AxolotlKitTests/AxolotlKit Tests-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.whispersystems.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AxolotlKit.app/AxolotlKit";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
B63B2FEC197D8B9600E09F65 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = F1351BCBFEF67F0F64D4C98C /* Pods.release.xcconfig */;
|
||||
baseConfigurationReference = 535B95F3C3486538E9B66E8B /* Pods-AxolotlKitTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/AxolotlKit.app/AxolotlKit";
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "AxolotlKit Tests/AxolotlKit Tests-Prefix.pch";
|
||||
INFOPLIST_FILE = "AxolotlKit Tests/AxolotlKit Tests-Info.plist";
|
||||
GCC_PREFIX_HEADER = "AxolotlKitTests/AxolotlKit Tests-Prefix.pch";
|
||||
INFOPLIST_FILE = "AxolotlKitTests/AxolotlKit Tests-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.whispersystems.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AxolotlKit.app/AxolotlKit";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
B6B98F8D197D838A00B16B5E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C47E7DDC756D06556C4541B0 /* Pods.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
@ -810,16 +610,20 @@
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@ -833,10 +637,10 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)/**",
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@ -848,7 +652,6 @@
|
||||
};
|
||||
B6B98F8E197D838A00B16B5E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = F1351BCBFEF67F0F64D4C98C /* Pods.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
@ -860,8 +663,10 @@
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@ -870,6 +675,7 @@
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
@ -877,10 +683,10 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)/**",
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0600"
|
||||
LastUpgradeVersion = "0830"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -23,10 +23,10 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@ -38,26 +38,6 @@
|
||||
ReferencedContainer = "container:AxolotlKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B63B2FDA197D8B9600E09F65"
|
||||
BuildableName = "AxolotlKitTests.xctest"
|
||||
BlueprintName = "AxolotlKitTests"
|
||||
ReferencedContainer = "container:AxolotlKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B63B2FDA197D8B9600E09F65"
|
||||
BuildableName = "AxolotlKitTests.xctest"
|
||||
BlueprintName = "AxolotlKitTests"
|
||||
ReferencedContainer = "container:AxolotlKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
@ -68,17 +48,21 @@
|
||||
ReferencedContainer = "container:AxolotlKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B63B2F9C197D8AFD00E09F65"
|
||||
@ -91,10 +75,10 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
LastUpgradeVersion = "0830"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@ -32,17 +32,21 @@
|
||||
ReferencedContainer = "container:AxolotlKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B63B2F9C197D8AFD00E09F65"
|
||||
@ -55,20 +59,11 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B63B2F9C197D8AFD00E09F65"
|
||||
BuildableName = "AxolotlKit.app"
|
||||
BlueprintName = "AxolotlKit"
|
||||
ReferencedContainer = "container:AxolotlKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
||||
@ -16,6 +16,11 @@
|
||||
// Override point for customization after application launch.
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
// required to avoid crash:
|
||||
// "Application windows are expected to have a root view controller at the end of application launch"
|
||||
|
||||
[self.window setRootViewController:[UIViewController new]];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.fredericjacobs.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
//
|
||||
// Prefix header
|
||||
//
|
||||
// The contents of this file are implicitly included at the beginning of every source file.
|
||||
//
|
||||
|
||||
#import <Availability.h>
|
||||
|
||||
#ifndef __IPHONE_3_0
|
||||
#warning "This project uses features only available in iOS SDK 3.0 and later."
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
@ -8,8 +8,15 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, CipherMessageType) {
|
||||
CipherMessageType_Prekey = 0,
|
||||
CipherMessageType_Whisper,
|
||||
};
|
||||
|
||||
@protocol CipherMessage <NSObject>
|
||||
|
||||
- (NSData*)serialized;
|
||||
- (NSData *)serialized;
|
||||
|
||||
@property (nonatomic, readonly) CipherMessageType cipherMessageType;
|
||||
|
||||
@end
|
||||
|
||||
@ -1,26 +1,32 @@
|
||||
//
|
||||
// PrekeyWhisperMessage.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WhisperMessage.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyWhisperMessage : NSObject <CipherMessage>
|
||||
|
||||
- (instancetype)initWithData:(NSData *)serialized;
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError;
|
||||
|
||||
- (instancetype)initWithWhisperMessage:(WhisperMessage*)whisperMessage registrationId:(int)registrationId prekeyId:(int)prekeyId signedPrekeyId:(int)signedPrekeyId baseKey:(NSData*)baseKey identityKey:(NSData*)identityKey;
|
||||
- (instancetype)init_throws_withWhisperMessage:(WhisperMessage *)whisperMessage
|
||||
registrationId:(int)registrationId
|
||||
prekeyId:(int)prekeyId
|
||||
signedPrekeyId:(int)signedPrekeyId
|
||||
baseKey:(NSData *)baseKey
|
||||
identityKey:(NSData *)identityKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@property (nonatomic, readonly) int registrationId;
|
||||
@property (nonatomic, readonly) int version;
|
||||
@property (nonatomic, readonly) int prekeyID;
|
||||
@property (nonatomic, readonly) int signedPrekeyId;
|
||||
@property (nonatomic, readonly) NSData *baseKey;
|
||||
@property (nonatomic, readonly) NSData *identityKey;
|
||||
@property (nonatomic, readonly) int registrationId;
|
||||
@property (nonatomic, readonly) int version;
|
||||
@property (nonatomic, readonly) int prekeyID;
|
||||
@property (nonatomic, readonly) int signedPrekeyId;
|
||||
@property (nonatomic, readonly) NSData *baseKey;
|
||||
@property (nonatomic, readonly) NSData *identityKey;
|
||||
@property (nonatomic, readonly) WhisperMessage *message;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,89 +1,148 @@
|
||||
//
|
||||
// PrekeyWhisperMessage.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "PreKeyWhisperMessage.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "Constants.h"
|
||||
#import "WhisperTextProtocol.pb.h"
|
||||
#import "SerializationUtilities.h"
|
||||
#import <AxolotlKit/AxolotlKit-Swift.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyWhisperMessage ()
|
||||
|
||||
@property (nonatomic, readwrite) NSData *identityKey;
|
||||
@property (nonatomic, readwrite) NSData *baseKey;
|
||||
@property (nonatomic, readwrite) NSData *serialized;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation PreKeyWhisperMessage
|
||||
|
||||
-(instancetype)initWithWhisperMessage:(WhisperMessage*)whisperMessage registrationId:(int)registrationId prekeyId:(int)prekeyId signedPrekeyId:(int)signedPrekeyId baseKey:(NSData*)baseKey identityKey:(NSData*)identityKey{
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_registrationId = registrationId;
|
||||
_version = whisperMessage.version;
|
||||
_prekeyID = prekeyId;
|
||||
_signedPrekeyId = signedPrekeyId;
|
||||
_baseKey = baseKey;
|
||||
_identityKey = identityKey;
|
||||
_message = whisperMessage;
|
||||
- (instancetype)init_throws_withWhisperMessage:(WhisperMessage *)whisperMessage
|
||||
registrationId:(int)registrationId
|
||||
prekeyId:(int)prekeyId
|
||||
signedPrekeyId:(int)signedPrekeyId
|
||||
baseKey:(NSData *)baseKey
|
||||
identityKey:(NSData *)identityKey
|
||||
{
|
||||
OWSAssert(whisperMessage);
|
||||
OWSAssert(baseKey);
|
||||
OWSAssert(identityKey);
|
||||
|
||||
if (self = [super init]) {
|
||||
_registrationId = registrationId;
|
||||
_version = whisperMessage.version;
|
||||
_prekeyID = prekeyId;
|
||||
_signedPrekeyId = signedPrekeyId;
|
||||
_baseKey = baseKey;
|
||||
_identityKey = identityKey;
|
||||
_message = whisperMessage;
|
||||
|
||||
SPKProtoTSProtoPreKeyWhisperMessageBuilder *messageBuilder = [SPKProtoTSProtoPreKeyWhisperMessage builderWithSignedPreKeyID:signedPrekeyId
|
||||
baseKey:baseKey
|
||||
identityKey:identityKey
|
||||
message:whisperMessage.serialized];
|
||||
[messageBuilder setRegistrationID:registrationId];
|
||||
|
||||
if (prekeyId != -1) {
|
||||
[messageBuilder setPreKeyID:prekeyId];
|
||||
}
|
||||
|
||||
Byte versionByte = [SerializationUtilities intsToByteHigh:_version low:CURRENT_VERSION];
|
||||
NSMutableData *serialized = [NSMutableData dataWithBytes:&versionByte length:1];
|
||||
|
||||
NSError *error;
|
||||
NSData *_Nullable messageData = [messageBuilder buildSerializedDataAndReturnError:&error];
|
||||
if (!messageData || error) {
|
||||
OWSFailDebug(@"Could not serialize proto: %@.", error);
|
||||
OWSRaiseException(InvalidMessageException, @"Could not serialize proto.");
|
||||
}
|
||||
[serialized appendData:messageData];
|
||||
|
||||
_serialized = [serialized copy];
|
||||
}
|
||||
|
||||
TSProtoPreKeyWhisperMessageBuilder *builder = [TSProtoPreKeyWhisperMessage builder];
|
||||
[builder setSignedPreKeyId:signedPrekeyId];
|
||||
[builder setBaseKey:baseKey];
|
||||
[builder setIdentityKey:identityKey];
|
||||
[builder setMessage:whisperMessage.serialized];
|
||||
[builder setRegistrationId:registrationId];
|
||||
|
||||
if (prekeyId != -1) {
|
||||
[builder setPreKeyId:prekeyId];
|
||||
}
|
||||
|
||||
Byte versionByte = [SerializationUtilities intsToByteHigh:_version low:CURRENT_VERSION];
|
||||
NSMutableData *serialized = [NSMutableData dataWithBytes:&versionByte length:1];
|
||||
NSData *messageBytes = builder.build.data;
|
||||
[serialized appendData:messageBytes];
|
||||
|
||||
_serialized = [NSData dataWithData:serialized];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData*)serialized{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError
|
||||
{
|
||||
@try {
|
||||
self = [self init_throws_withData:serialized];
|
||||
return self;
|
||||
} @catch (NSException *exception) {
|
||||
*outError = SCKExceptionWrapperErrorMake(exception);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized
|
||||
{
|
||||
if (self = [super init]) {
|
||||
if (serialized.length < 1) {
|
||||
OWSFailDebug(@"Empty data");
|
||||
OWSRaiseException(InvalidMessageException, @"Empty data");
|
||||
}
|
||||
|
||||
Byte version;
|
||||
[serialized getBytes:&version length:1];
|
||||
_version = [SerializationUtilities highBitsToIntFromByte:version];
|
||||
|
||||
|
||||
if (_version > CURRENT_VERSION && _version < MINIMUM_SUPPORTED_VERSION) {
|
||||
@throw [NSException exceptionWithName:InvalidVersionException reason:@"Unknown version" userInfo:@{@"version":[NSNumber numberWithInt:_version]}];
|
||||
@throw [NSException exceptionWithName:InvalidVersionException
|
||||
reason:@"Unknown version"
|
||||
userInfo:@{ @"version" : [NSNumber numberWithInt:_version] }];
|
||||
}
|
||||
|
||||
NSData *message = [serialized subdataWithRange:NSMakeRange(1, serialized.length-1)];
|
||||
|
||||
TSProtoPreKeyWhisperMessage *preKeyWhisperMessage = [TSProtoPreKeyWhisperMessage parseFromData:message];
|
||||
|
||||
if (!preKeyWhisperMessage.hasSignedPreKeyId || !preKeyWhisperMessage.hasBaseKey || !preKeyWhisperMessage.hasIdentityKey || !preKeyWhisperMessage.hasMessage) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Incomplete Message" userInfo:@{}];
|
||||
|
||||
NSUInteger messageDataLength;
|
||||
ows_sub_overflow(serialized.length, 1, &messageDataLength);
|
||||
NSData *messageData = [serialized subdataWithRange:NSMakeRange(1, messageDataLength)];
|
||||
|
||||
NSError *error;
|
||||
SPKProtoTSProtoPreKeyWhisperMessage *_Nullable preKeyWhisperMessage =
|
||||
[SPKProtoTSProtoPreKeyWhisperMessage parseData:messageData error:&error];
|
||||
if (!preKeyWhisperMessage || error) {
|
||||
OWSFailDebug(@"Could not parse proto: %@.", error);
|
||||
OWSRaiseException(InvalidMessageException, @"Could not parse proto.");
|
||||
}
|
||||
|
||||
_serialized = serialized;
|
||||
_registrationId = preKeyWhisperMessage.registrationId;
|
||||
_prekeyID = preKeyWhisperMessage.preKeyId;
|
||||
_signedPrekeyId = preKeyWhisperMessage.signedPreKeyId;
|
||||
_baseKey = preKeyWhisperMessage.baseKey;
|
||||
_identityKey = preKeyWhisperMessage.identityKey;
|
||||
_message = [[WhisperMessage alloc] initWithData:preKeyWhisperMessage.message];
|
||||
|
||||
_serialized = serialized;
|
||||
_registrationId = preKeyWhisperMessage.registrationID;
|
||||
|
||||
// This method is called when decrypting a received PreKeyMessage, but to be symmetrical with
|
||||
// encrypting a PreKeyWhisperMessage before sending, we use "-1" to indicate *no* unsigned prekey was
|
||||
// included.
|
||||
_prekeyID = preKeyWhisperMessage.hasPreKeyID ? preKeyWhisperMessage.preKeyID : -1;
|
||||
_signedPrekeyId = preKeyWhisperMessage.signedPreKeyID;
|
||||
_baseKey = preKeyWhisperMessage.baseKey;
|
||||
_identityKey = preKeyWhisperMessage.identityKey;
|
||||
_message = [[WhisperMessage alloc] init_throws_withData:preKeyWhisperMessage.message];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CipherMessageType)cipherMessageType {
|
||||
return CipherMessageType_Prekey;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,29 +1,40 @@
|
||||
//
|
||||
// WhisperMessage.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CipherMessage.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ECKeyPair;
|
||||
|
||||
@interface WhisperMessage : NSObject <CipherMessage>
|
||||
|
||||
@property (nonatomic, readonly) int version;
|
||||
@property (nonatomic, readonly) NSData *senderRatchetKey;
|
||||
@property (nonatomic, readonly) int previousCounter;
|
||||
@property (nonatomic, readonly) int counter;
|
||||
@property (nonatomic, readonly) NSData *cipherText;
|
||||
@property (nonatomic, readonly) NSData *serialized;
|
||||
@property (nonatomic, readonly) int version;
|
||||
@property (nonatomic, readonly) NSData *senderRatchetKey;
|
||||
@property (nonatomic, readonly) int previousCounter;
|
||||
@property (nonatomic, readonly) int counter;
|
||||
@property (nonatomic, readonly) NSData *cipherText;
|
||||
@property (nonatomic, readonly) NSData *serialized;
|
||||
|
||||
- (instancetype)initWithData:(NSData*)serialized;
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError;
|
||||
|
||||
- (instancetype)initWithVersion:(int)version macKey:(NSData*)macKey senderRatchetKey:(NSData*)senderRatchetKey counter:(int)counter previousCounter:(int)previousCounter cipherText:(NSData*)cipherText senderIdentityKey:(NSData*)senderIdentityKey receiverIdentityKey:(NSData*)receiverIdentityKey;
|
||||
- (instancetype)init_throws_withVersion:(int)version
|
||||
macKey:(NSData *)macKey
|
||||
senderRatchetKey:(NSData *)senderRatchetKey
|
||||
counter:(int)counter
|
||||
previousCounter:(int)previousCounter
|
||||
cipherText:(NSData *)cipherText
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
- (void)verifyMacWithVersion:(int)messageVersion senderIdentityKey:(NSData *)senderIdentityKey receiverIdentityKey:(NSData*)receiverIdentityKey macKey:(NSData *)macKey;
|
||||
- (void)throws_verifyMacWithVersion:(int)messageVersion
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,105 +1,200 @@
|
||||
//
|
||||
// WhisperMessage.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WhisperMessage.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "Constants.h"
|
||||
#import "WhisperMessage.h"
|
||||
#import "WhisperTextProtocol.pb.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
#import "SerializationUtilities.h"
|
||||
#import <AxolotlKit/AxolotlKit-Swift.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
#import <SignalCoreKit/SignalCoreKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define VERSION_LENGTH 1
|
||||
|
||||
@implementation WhisperMessage
|
||||
|
||||
- (instancetype)initWithVersion:(int)version macKey:(NSData*)macKey senderRatchetKey:(NSData*)senderRatchetKey counter:(int)counter previousCounter:(int)previousCounter cipherText:(NSData*)cipherText senderIdentityKey:(NSData*)senderIdentityKey receiverIdentityKey:(NSData*)receiverIdentityKey{
|
||||
self = [super init];
|
||||
|
||||
Byte versionByte = [SerializationUtilities intsToByteHigh:version low:CURRENT_VERSION];
|
||||
NSMutableData *serialized = [NSMutableData dataWithBytes:&versionByte length:1];
|
||||
- (instancetype)init_throws_withVersion:(int)version
|
||||
macKey:(NSData *)macKey
|
||||
senderRatchetKey:(NSData *)senderRatchetKey
|
||||
counter:(int)counter
|
||||
previousCounter:(int)previousCounter
|
||||
cipherText:(NSData *)cipherText
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
{
|
||||
OWSAssert(macKey);
|
||||
OWSAssert(senderRatchetKey);
|
||||
OWSAssert(cipherText);
|
||||
OWSAssert(cipherText);
|
||||
OWSAssert(senderIdentityKey);
|
||||
OWSAssert(receiverIdentityKey);
|
||||
|
||||
NSData *message = [[[[[[[TSProtoWhisperMessage builder]
|
||||
setRatchetKey:senderRatchetKey]
|
||||
setCounter:counter]
|
||||
setPreviousCounter:previousCounter]
|
||||
setCiphertext:cipherText]
|
||||
build] data];
|
||||
[serialized appendData:message];
|
||||
|
||||
NSData *mac = [SerializationUtilities macWithVersion:version
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:serialized];
|
||||
|
||||
[serialized appendData:mac];
|
||||
|
||||
if (self) {
|
||||
_version = version;
|
||||
if (self = [super init]) {
|
||||
Byte versionByte = [SerializationUtilities intsToByteHigh:version low:CURRENT_VERSION];
|
||||
NSMutableData *serialized = [NSMutableData dataWithBytes:&versionByte length:1];
|
||||
|
||||
SPKProtoTSProtoWhisperMessageBuilder *messageBuilder = [SPKProtoTSProtoWhisperMessage builderWithRatchetKey:senderRatchetKey
|
||||
counter:counter
|
||||
ciphertext:cipherText];
|
||||
[messageBuilder setPreviousCounter:previousCounter];
|
||||
NSError *error;
|
||||
NSData *_Nullable messageData = [messageBuilder buildSerializedDataAndReturnError:&error];
|
||||
if (!messageData || error) {
|
||||
OWSFailDebug(@"Could not serialize proto: %@.", error);
|
||||
OWSRaiseException(InvalidMessageException, @"Could not serialize proto.");
|
||||
}
|
||||
[serialized appendData:messageData];
|
||||
|
||||
NSData *mac = [SerializationUtilities throws_macWithVersion:version
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:serialized];
|
||||
|
||||
[serialized appendData:mac];
|
||||
|
||||
_version = version;
|
||||
_senderRatchetKey = senderRatchetKey;
|
||||
_previousCounter = previousCounter;
|
||||
_counter = counter;
|
||||
_cipherText = cipherText;
|
||||
_serialized = serialized;
|
||||
_previousCounter = previousCounter;
|
||||
_counter = counter;
|
||||
_cipherText = cipherText;
|
||||
_serialized = [serialized copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData*)serialized{
|
||||
if (serialized.length <= (VERSION_LENGTH + MAC_LENGTH)) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Message size is too short to have content" userInfo:@{}];
|
||||
}
|
||||
|
||||
Byte version;
|
||||
[serialized getBytes:&version length:VERSION_LENGTH];
|
||||
|
||||
NSData *messageAndMac = [serialized subdataWithRange:NSMakeRange(VERSION_LENGTH, serialized.length - VERSION_LENGTH)];
|
||||
|
||||
NSData *message = [messageAndMac subdataWithRange:NSMakeRange(0, messageAndMac.length - MAC_LENGTH)];
|
||||
|
||||
if ([SerializationUtilities highBitsToIntFromByte:version] < MINIMUM_SUPPORTED_VERSION) {
|
||||
@throw [NSException exceptionWithName:LegacyMessageException reason:@"Message was sent with an unsupported version of the TextSecure protocol." userInfo:@{}];
|
||||
}
|
||||
|
||||
if ([SerializationUtilities highBitsToIntFromByte:version] > CURRENT_VERSION) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Unknown Version" userInfo:@{@"Version": [NSNumber numberWithChar:[SerializationUtilities highBitsToIntFromByte:version]]}];
|
||||
}
|
||||
|
||||
TSProtoWhisperMessage *whisperMessage = [TSProtoWhisperMessage parseFromData:message];
|
||||
|
||||
if (!whisperMessage.hasCiphertext || !whisperMessage.hasCounter || !whisperMessage.hasRatchetKey) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Incomplete Message" userInfo:@{}];
|
||||
}
|
||||
|
||||
_serialized = serialized;
|
||||
_senderRatchetKey = [whisperMessage.ratchetKey removeKeyType];
|
||||
_version = [SerializationUtilities highBitsToIntFromByte:version];
|
||||
_counter = whisperMessage.counter;
|
||||
_previousCounter = whisperMessage.previousCounter;
|
||||
_cipherText = whisperMessage.ciphertext;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)verifyMacWithVersion:(int)messageVersion senderIdentityKey:(NSData *)senderIdentityKey receiverIdentityKey:(NSData*)receiverIdentityKey macKey:(NSData *)macKey{
|
||||
|
||||
NSData *data = [self.serialized subdataWithRange:NSMakeRange(0, self.serialized.length - MAC_LENGTH)];
|
||||
NSData *theirMac = [self.serialized subdataWithRange:NSMakeRange(self.serialized.length - MAC_LENGTH, MAC_LENGTH)];
|
||||
NSData *ourMac = [SerializationUtilities macWithVersion:messageVersion
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:data];
|
||||
|
||||
if (![theirMac isEqualToData:ourMac]) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Bad Mac!" userInfo:@{}];
|
||||
- (nullable instancetype)initWithData:(NSData *)serialized error:(NSError **)outError
|
||||
{
|
||||
@try {
|
||||
self = [self init_throws_withData:serialized];
|
||||
return self;
|
||||
} @catch (NSException *exception) {
|
||||
*outError = SCKExceptionWrapperErrorMake(exception);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init_throws_withData:(NSData *)serialized
|
||||
{
|
||||
if (self = [super init]) {
|
||||
if (serialized.length <= (VERSION_LENGTH + MAC_LENGTH)) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException
|
||||
reason:@"Message size is too short to have content"
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
Byte version;
|
||||
[serialized getBytes:&version length:VERSION_LENGTH];
|
||||
|
||||
NSUInteger messageAndMacLength;
|
||||
ows_sub_overflow(serialized.length, VERSION_LENGTH, &messageAndMacLength);
|
||||
NSData *messageAndMac = [serialized subdataWithRange:NSMakeRange(VERSION_LENGTH, messageAndMacLength)];
|
||||
|
||||
NSUInteger messageLength;
|
||||
ows_sub_overflow(messageAndMac.length, MAC_LENGTH, &messageLength);
|
||||
NSData *messageData = [messageAndMac subdataWithRange:NSMakeRange(0, messageLength)];
|
||||
|
||||
if ([SerializationUtilities highBitsToIntFromByte:version] < MINIMUM_SUPPORTED_VERSION) {
|
||||
@throw [NSException
|
||||
exceptionWithName:LegacyMessageException
|
||||
reason:@"Message was sent with an unsupported version of the TextSecure protocol."
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
if ([SerializationUtilities highBitsToIntFromByte:version] > CURRENT_VERSION) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException
|
||||
reason:@"Unknown Version"
|
||||
userInfo:@{
|
||||
@"Version" : [NSNumber
|
||||
numberWithChar:[SerializationUtilities highBitsToIntFromByte:version]]
|
||||
}];
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
SPKProtoTSProtoWhisperMessage *_Nullable whisperMessage =
|
||||
[SPKProtoTSProtoWhisperMessage parseData:messageData error:&error];
|
||||
if (!whisperMessage || error) {
|
||||
OWSFailDebug(@"Could not parse proto: %@.", error);
|
||||
OWSRaiseException(InvalidMessageException, @"Could not parse proto.");
|
||||
}
|
||||
|
||||
_serialized = serialized;
|
||||
_senderRatchetKey = [whisperMessage.ratchetKey throws_removeKeyType];
|
||||
_version = [SerializationUtilities highBitsToIntFromByte:version];
|
||||
_counter = whisperMessage.counter;
|
||||
_previousCounter = whisperMessage.previousCounter;
|
||||
_cipherText = whisperMessage.ciphertext;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)throws_verifyMacWithVersion:(int)messageVersion
|
||||
senderIdentityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
{
|
||||
OWSAssert(senderIdentityKey);
|
||||
OWSAssert(receiverIdentityKey);
|
||||
OWSAssert(macKey);
|
||||
|
||||
OWSDataParser *dataParser = [[OWSDataParser alloc] initWithData:self.serialized];
|
||||
NSError *error;
|
||||
|
||||
NSUInteger messageLength;
|
||||
if (__builtin_sub_overflow(self.serialized.length, MAC_LENGTH, &messageLength)) {
|
||||
OWSFailDebug(@"Data too short");
|
||||
OWSRaiseException(InvalidMessageException, @"Data too short");
|
||||
}
|
||||
NSData *_Nullable data = [dataParser nextDataWithLength:messageLength
|
||||
name:@"message data"
|
||||
error:&error];
|
||||
if (!data || error) {
|
||||
OWSFailDebug(@"Could not parse data: %@.", error);
|
||||
OWSRaiseException(InvalidMessageException, @"Could not parse data.");
|
||||
}
|
||||
NSData *_Nullable theirMac = [dataParser nextDataWithLength:MAC_LENGTH
|
||||
name:@"mac data"
|
||||
error:&error];
|
||||
if (!theirMac || error) {
|
||||
OWSFailDebug(@"Could not parse their mac: %@.", error);
|
||||
OWSRaiseException(InvalidMessageException, @"Could not parse their mac.");
|
||||
}
|
||||
|
||||
NSData *ourMac = [SerializationUtilities throws_macWithVersion:messageVersion
|
||||
identityKey:[senderIdentityKey prependKeyType]
|
||||
receiverIdentityKey:[receiverIdentityKey prependKeyType]
|
||||
macKey:macKey
|
||||
serialized:data];
|
||||
|
||||
if (![theirMac ows_constantTimeIsEqualToData:ourMac]) {
|
||||
OWSFailDebug(@"Bad Mac! Their Mac: %@ Our Mac: %@", theirMac, ourMac);
|
||||
OWSRaiseException(InvalidMessageException, @"Bad Mac!");
|
||||
}
|
||||
}
|
||||
|
||||
- (CipherMessageType)cipherMessageType {
|
||||
return CipherMessageType_Whisper;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
//
|
||||
// AES-CBC.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AES_CBC : NSObject
|
||||
|
||||
/**
|
||||
@ -20,7 +18,9 @@
|
||||
* @return ciphertext
|
||||
*/
|
||||
|
||||
+(NSData*)encryptCBCMode:(NSData*)data withKey:(NSData*)key withIV:(NSData*)iv;
|
||||
+ (NSData *)throws_encryptCBCMode:(NSData *)data
|
||||
withKey:(NSData *)key
|
||||
withIV:(NSData *)iv NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
/**
|
||||
* Decrypts with AES in CBC mode
|
||||
@ -32,6 +32,10 @@
|
||||
* @return plaintext
|
||||
*/
|
||||
|
||||
+(NSData*)decryptCBCMode:(NSData*)data withKey:(NSData*)key withIV:(NSData*)iv;
|
||||
+ (NSData *)throws_decryptCBCMode:(NSData *)data
|
||||
withKey:(NSData *)key
|
||||
withIV:(NSData *)iv NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,65 +1,104 @@
|
||||
//
|
||||
// AES-CBC.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AES-CBC.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "MessageKeys.h"
|
||||
#import "AES-CBC.h"
|
||||
#import <Security/Security.h>
|
||||
#import <CommonCrypto/CommonHMAC.h>
|
||||
#import <CommonCrypto/CommonCryptor.h>
|
||||
#import <CommonCrypto/CommonHMAC.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation AES_CBC
|
||||
|
||||
#pragma mark AESCBC Mode
|
||||
|
||||
+(NSData*)encryptCBCMode:(NSData*)data withKey:(NSData*)key withIV:(NSData*)iv{
|
||||
NSAssert(data, @"Missing data to encrypt");
|
||||
NSAssert([key length] == 32, @"AES key should be 256 bits");
|
||||
NSAssert([iv length] == 16, @"AES-CBC IV should be 128 bits");
|
||||
|
||||
size_t bufferSize = [data length] + kCCBlockSizeAES128;
|
||||
void* buffer = malloc(bufferSize);
|
||||
|
||||
size_t bytesEncrypted = 0;
|
||||
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
|
||||
[key bytes], [key length],
|
||||
[iv bytes],
|
||||
[data bytes], [data length],
|
||||
buffer, bufferSize,
|
||||
&bytesEncrypted);
|
||||
|
||||
if (cryptStatus == kCCSuccess){
|
||||
return [NSData dataWithBytesNoCopy:buffer length:bytesEncrypted];
|
||||
} else{
|
||||
free(buffer);
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"We encountered an issue while encrypting." userInfo:nil];
|
||||
+ (NSData *)throws_encryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv
|
||||
{
|
||||
if (!data) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"Missing data to encrypt." userInfo:nil];
|
||||
}
|
||||
if (data.length >= SIZE_MAX - kCCBlockSizeAES128) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"Oversize data." userInfo:nil];
|
||||
}
|
||||
if (key.length != 32) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"AES key should be 256 bits." userInfo:nil];
|
||||
}
|
||||
if (iv.length != 16) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"AES-CBC IV should be 128 bits." userInfo:nil];
|
||||
}
|
||||
|
||||
size_t bufferSize;
|
||||
ows_add_overflow(data.length, kCCBlockSizeAES128, &bufferSize);
|
||||
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize];
|
||||
OWSAssert(bufferData != nil);
|
||||
|
||||
size_t bytesEncrypted = 0;
|
||||
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
|
||||
kCCAlgorithmAES128,
|
||||
kCCOptionPKCS7Padding,
|
||||
[key bytes],
|
||||
[key length],
|
||||
[iv bytes],
|
||||
[data bytes],
|
||||
[data length],
|
||||
bufferData.mutableBytes,
|
||||
bufferSize,
|
||||
&bytesEncrypted);
|
||||
|
||||
if (cryptStatus == kCCSuccess) {
|
||||
return [bufferData subdataWithRange:NSMakeRange(0, bytesEncrypted)];
|
||||
} else {
|
||||
@throw [NSException exceptionWithName:CipherException
|
||||
reason:@"We encountered an issue while encrypting."
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
+(NSData*) decryptCBCMode:(NSData*)data withKey:(NSData*)key withIV:(NSData*)iv {
|
||||
+ (NSData *)throws_decryptCBCMode:(NSData *)data withKey:(NSData *)key withIV:(NSData *)iv
|
||||
{
|
||||
if (!data) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"Missing data to decrypt." userInfo:nil];
|
||||
}
|
||||
if (data.length >= SIZE_MAX - kCCBlockSizeAES128) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"Oversize data." userInfo:nil];
|
||||
}
|
||||
if (key.length != 32) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"AES key should be 256 bits." userInfo:nil];
|
||||
}
|
||||
if (iv.length != 16) {
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"AES-CBC IV should be 128 bits." userInfo:nil];
|
||||
}
|
||||
|
||||
size_t bufferSize;
|
||||
ows_add_overflow(data.length, kCCBlockSizeAES128, &bufferSize);
|
||||
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize];
|
||||
OWSAssert(bufferData != nil);
|
||||
|
||||
size_t bytesDecrypted = 0;
|
||||
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
|
||||
kCCAlgorithmAES128,
|
||||
kCCOptionPKCS7Padding,
|
||||
[key bytes],
|
||||
[key length],
|
||||
[iv bytes],
|
||||
[data bytes],
|
||||
[data length],
|
||||
bufferData.mutableBytes,
|
||||
bufferSize,
|
||||
&bytesDecrypted);
|
||||
|
||||
size_t bufferSize = [data length] + kCCBlockSizeAES128;
|
||||
void* buffer = malloc(bufferSize);
|
||||
|
||||
size_t bytesDecrypted = 0;
|
||||
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
|
||||
[key bytes], [key length],
|
||||
[iv bytes],
|
||||
[data bytes], [data length],
|
||||
buffer, bufferSize,
|
||||
&bytesDecrypted);
|
||||
|
||||
if (cryptStatus == kCCSuccess) {
|
||||
return [NSData dataWithBytesNoCopy:buffer length:bytesDecrypted];
|
||||
} else{
|
||||
free(buffer);
|
||||
@throw [NSException exceptionWithName:CipherException reason:@"We encountered an issue while decrypting." userInfo:nil];
|
||||
return [bufferData subdataWithRange:NSMakeRange(0, bytesDecrypted)];
|
||||
} else {
|
||||
@throw [NSException exceptionWithName:CipherException
|
||||
reason:@"We encountered an issue while decrypting."
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
//
|
||||
// AxolotlKeyFetch.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyBundle : NSObject <NSSecureCoding>
|
||||
|
||||
@property (nonatomic, readonly) NSData *identityKey;
|
||||
@ -19,13 +17,15 @@
|
||||
@property (nonatomic, readonly) int signedPreKeyId;
|
||||
@property (nonatomic, readonly) NSData *signedPreKeySignature;
|
||||
|
||||
- (instancetype)initWithRegistrationId:(int)registrationId
|
||||
deviceId:(int)deviceId
|
||||
preKeyId:(int)preKeyId
|
||||
preKeyPublic:(NSData*)preKeyPublic
|
||||
signedPreKeyPublic:(NSData*)signedPreKeyPublic
|
||||
signedPreKeyId:(int)signedPreKeyId
|
||||
signedPreKeySignature:(NSData*)signedPreKeySignature
|
||||
identityKey:(NSData*)identityKey;
|
||||
- (nullable instancetype)initWithRegistrationId:(int)registrationId
|
||||
deviceId:(int)deviceId
|
||||
preKeyId:(int)preKeyId
|
||||
preKeyPublic:(NSData *)preKeyPublic
|
||||
signedPreKeyPublic:(NSData *)signedPreKeyPublic
|
||||
signedPreKeyId:(int)signedPreKeyId
|
||||
signedPreKeySignature:(NSData *)signedPreKeySignature
|
||||
identityKey:(NSData *)identityKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
//
|
||||
// AxolotlKeyFetch.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PreKeyBundle.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kCoderPKBIdentityKey = @"kCoderPKBIdentityKey";
|
||||
static NSString* const kCoderPKBregistrationId = @"kCoderPKBregistrationId";
|
||||
@ -20,17 +17,34 @@ static NSString* const kCoderPKBsignedPreKeySignature = @"kCoderPKBsignedPreKeyS
|
||||
|
||||
@implementation PreKeyBundle
|
||||
|
||||
- (instancetype)initWithRegistrationId:(int)registrationId
|
||||
deviceId:(int)deviceId
|
||||
preKeyId:(int)preKeyId
|
||||
preKeyPublic:(NSData*)preKeyPublic
|
||||
signedPreKeyPublic:(NSData*)signedPreKeyPublic
|
||||
signedPreKeyId:(int)signedPreKeyId
|
||||
signedPreKeySignature:(NSData*)signedPreKeySignature
|
||||
identityKey:(NSData*)identityKey{
|
||||
|
||||
- (nullable instancetype)initWithRegistrationId:(int)registrationId
|
||||
deviceId:(int)deviceId
|
||||
preKeyId:(int)preKeyId
|
||||
preKeyPublic:(NSData *)preKeyPublic
|
||||
signedPreKeyPublic:(NSData *)signedPreKeyPublic
|
||||
signedPreKeyId:(int)signedPreKeyId
|
||||
signedPreKeySignature:(NSData *)signedPreKeySignature
|
||||
identityKey:(NSData *)identityKey
|
||||
{
|
||||
if (preKeyPublic && preKeyPublic.length != 33) {
|
||||
OWSFailDebug(@"preKeyPublic && preKeyPublic.length != 33");
|
||||
return nil;
|
||||
}
|
||||
if (signedPreKeyPublic.length != 33) {
|
||||
OWSFailDebug(@"signedPreKeyPublic.length != 33");
|
||||
return nil;
|
||||
}
|
||||
if (!signedPreKeySignature) {
|
||||
OWSFailDebug(@"!signedPreKeySignature");
|
||||
return nil;
|
||||
}
|
||||
if (identityKey.length != 33) {
|
||||
OWSFailDebug(@"identityKey.length != 33");
|
||||
return nil;
|
||||
}
|
||||
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self) {
|
||||
_identityKey = identityKey;
|
||||
_registrationId = registrationId;
|
||||
@ -45,7 +59,8 @@ static NSString* const kCoderPKBsignedPreKeySignature = @"kCoderPKBsignedPreKeyS
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
int registrationId = [aDecoder decodeIntForKey:kCoderPKBregistrationId];
|
||||
int deviceId = [aDecoder decodeIntForKey:kCoderPKBdeviceId];
|
||||
int preKeyId = [aDecoder decodeIntForKey:kCoderPKBpreKeyId];
|
||||
@ -86,3 +101,5 @@ static NSString* const kCoderPKBsignedPreKeySignature = @"kCoderPKBsignedPreKeyS
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,20 +1,24 @@
|
||||
//
|
||||
// PreKeyRecord.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PreKeyRecord : NSObject <NSSecureCoding>
|
||||
|
||||
@property (nonatomic, readonly) int Id;
|
||||
@property (nonatomic, readonly) ECKeyPair *keyPair;
|
||||
@property (nonatomic, readonly, nullable) NSDate *createdAt;
|
||||
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair*)keyPair;
|
||||
- (instancetype)initWithId:(int)identifier
|
||||
keyPair:(ECKeyPair *)keyPair
|
||||
createdAt:(NSDate *)createdAt;
|
||||
|
||||
- (void)setCreatedAtToNow;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -8,8 +8,11 @@
|
||||
|
||||
#import "PreKeyRecord.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kCoderPreKeyId = @"kCoderPreKeyId";
|
||||
static NSString* const kCoderPreKeyPair = @"kCoderPreKeyPair";
|
||||
static NSString* const kCoderCreatedAt = @"kCoderCreatedAt";
|
||||
|
||||
@implementation PreKeyRecord
|
||||
|
||||
@ -17,26 +20,41 @@ static NSString* const kCoderPreKeyPair = @"kCoderPreKeyPair";
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair*)keyPair{
|
||||
- (instancetype)initWithId:(int)identifier
|
||||
keyPair:(ECKeyPair*)keyPair
|
||||
createdAt:(NSDate *)createdAt
|
||||
{
|
||||
OWSAssert(keyPair);
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_Id = identifier;
|
||||
_keyPair = keyPair;
|
||||
_createdAt = createdAt;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId] keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]];
|
||||
- (nullable id)initWithCoder:(NSCoder *)aDecoder {
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId]
|
||||
keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]
|
||||
createdAt:[aDecoder decodeObjectOfClass:[NSDate class] forKey:kCoderCreatedAt]];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder{
|
||||
[aCoder encodeInteger:_Id forKey:kCoderPreKeyId];
|
||||
[aCoder encodeObject:_keyPair forKey:kCoderPreKeyPair];
|
||||
if (_createdAt != nil) {
|
||||
[aCoder encodeObject:_createdAt forKey:kCoderCreatedAt];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)setCreatedAtToNow {
|
||||
_createdAt = [NSDate date];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
//
|
||||
// SignedPrekeyRecord.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "PreKeyRecord.h"
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SignedPreKeyRecord : PreKeyRecord <NSSecureCoding>
|
||||
|
||||
@property (nonatomic, readonly) NSData *signature;
|
||||
@property (nonatomic, readonly) NSDate *generatedAt;
|
||||
// Defaults to NO. Should only be set after the service accepts this record.
|
||||
@property (nonatomic, readonly) BOOL wasAcceptedByService;
|
||||
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair *)keyPair signature:(NSData*)signature generatedAt:(NSDate*)generatedAt;
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair *)keyPair NS_UNAVAILABLE;
|
||||
|
||||
- (void)markAsAcceptedByService;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
//
|
||||
// SignedPrekeyRecord.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SignedPreKeyRecord.h"
|
||||
#import "SignedPrekeyRecord.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString* const kCoderPreKeyId = @"kCoderPreKeyId";
|
||||
static NSString* const kCoderPreKeyPair = @"kCoderPreKeyPair";
|
||||
static NSString* const kCoderPreKeyDate = @"kCoderPreKeyDate";
|
||||
static NSString* const kCoderPreKeySignature = @"kCoderPreKeySignature";
|
||||
static NSString *const kCoderPreKeyWasAcceptedByService = @"kCoderPreKeyWasAcceptedByService";
|
||||
|
||||
@implementation SignedPreKeyRecord
|
||||
|
||||
@ -19,8 +18,33 @@ static NSString* const kCoderPreKeySignature = @"kCoderPreKeySignature";
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(int)identifier
|
||||
keyPair:(ECKeyPair *)keyPair
|
||||
signature:(NSData *)signature
|
||||
generatedAt:(NSDate *)generatedAt
|
||||
wasAcceptedByService:(BOOL)wasAcceptedByService
|
||||
{
|
||||
OWSAssert(keyPair);
|
||||
OWSAssert(signature);
|
||||
OWSAssert(generatedAt);
|
||||
|
||||
self = [super initWithId:identifier
|
||||
keyPair:keyPair
|
||||
createdAt:generatedAt];
|
||||
|
||||
if (self) {
|
||||
_signature = signature;
|
||||
_generatedAt = generatedAt;
|
||||
_wasAcceptedByService = wasAcceptedByService;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair *)keyPair signature:(NSData*)signature generatedAt:(NSDate *)generatedAt{
|
||||
self = [super initWithId:identifier keyPair:keyPair];
|
||||
self = [super initWithId:identifier
|
||||
keyPair:keyPair
|
||||
createdAt:generatedAt];
|
||||
|
||||
if (self) {
|
||||
_signature = signature;
|
||||
@ -30,11 +54,12 @@ static NSString* const kCoderPreKeySignature = @"kCoderPreKeySignature";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId]
|
||||
keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]
|
||||
signature:[aDecoder decodeObjectOfClass:[NSData class] forKey:kCoderPreKeySignature]
|
||||
generatedAt:[aDecoder decodeObjectOfClass:[NSDate class] forKey:kCoderPreKeyDate]];
|
||||
- (nullable id)initWithCoder:(NSCoder *)aDecoder{
|
||||
return [self initWithId:[aDecoder decodeIntForKey:kCoderPreKeyId]
|
||||
keyPair:[aDecoder decodeObjectOfClass:[ECKeyPair class] forKey:kCoderPreKeyPair]
|
||||
signature:[aDecoder decodeObjectOfClass:[NSData class] forKey:kCoderPreKeySignature]
|
||||
generatedAt:[aDecoder decodeObjectOfClass:[NSDate class] forKey:kCoderPreKeyDate]
|
||||
wasAcceptedByService:[aDecoder decodeBoolForKey:kCoderPreKeyWasAcceptedByService]];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder{
|
||||
@ -42,11 +67,19 @@ static NSString* const kCoderPreKeySignature = @"kCoderPreKeySignature";
|
||||
[aCoder encodeObject:self.keyPair forKey:kCoderPreKeyPair];
|
||||
[aCoder encodeObject:self.signature forKey:kCoderPreKeySignature];
|
||||
[aCoder encodeObject:self.generatedAt forKey:kCoderPreKeyDate];
|
||||
[aCoder encodeBool:self.wasAcceptedByService forKey:kCoderPreKeyWasAcceptedByService];
|
||||
}
|
||||
|
||||
- (instancetype)initWithId:(int)identifier keyPair:(ECKeyPair*)keyPair{
|
||||
NSAssert(FALSE, @"Signed PreKeys need a signature");
|
||||
OWSAbstractMethod();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)markAsAcceptedByService
|
||||
{
|
||||
_wasAcceptedByService = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,435 +0,0 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
|
||||
#import "ProtocolBuffers.h"
|
||||
|
||||
// @@protoc_insertion_point(imports)
|
||||
|
||||
@class TSProtoKeyExchangeMessage;
|
||||
@class TSProtoKeyExchangeMessageBuilder;
|
||||
@class TSProtoPreKeyWhisperMessage;
|
||||
@class TSProtoPreKeyWhisperMessageBuilder;
|
||||
@class TSProtoSenderKeyDistributionMessage;
|
||||
@class TSProtoSenderKeyDistributionMessageBuilder;
|
||||
@class TSProtoSenderKeyMessage;
|
||||
@class TSProtoSenderKeyMessageBuilder;
|
||||
@class TSProtoWhisperMessage;
|
||||
@class TSProtoWhisperMessageBuilder;
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif // __has_feature
|
||||
|
||||
#ifndef NS_RETURNS_NOT_RETAINED
|
||||
#if __has_feature(attribute_ns_returns_not_retained)
|
||||
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
|
||||
#else
|
||||
#define NS_RETURNS_NOT_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@interface WhisperTextProtocolRoot : NSObject {
|
||||
}
|
||||
+ (PBExtensionRegistry*) extensionRegistry;
|
||||
+ (void) registerAllExtensions:(PBMutableExtensionRegistry*) registry;
|
||||
@end
|
||||
|
||||
@interface TSProtoWhisperMessage : PBGeneratedMessage {
|
||||
@private
|
||||
BOOL hasRatchetKey_:1;
|
||||
BOOL hasCiphertext_:1;
|
||||
BOOL hasCounter_:1;
|
||||
BOOL hasPreviousCounter_:1;
|
||||
NSData* ratchetKey;
|
||||
NSData* ciphertext;
|
||||
UInt32 counter;
|
||||
UInt32 previousCounter;
|
||||
}
|
||||
- (BOOL) hasRatchetKey;
|
||||
- (BOOL) hasCounter;
|
||||
- (BOOL) hasPreviousCounter;
|
||||
- (BOOL) hasCiphertext;
|
||||
@property (readonly, strong) NSData* ratchetKey;
|
||||
@property (readonly) UInt32 counter;
|
||||
@property (readonly) UInt32 previousCounter;
|
||||
@property (readonly, strong) NSData* ciphertext;
|
||||
|
||||
+ (TSProtoWhisperMessage*) defaultInstance;
|
||||
- (TSProtoWhisperMessage*) defaultInstance;
|
||||
|
||||
- (BOOL) isInitialized;
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
|
||||
- (TSProtoWhisperMessageBuilder*) builder;
|
||||
+ (TSProtoWhisperMessageBuilder*) builder;
|
||||
+ (TSProtoWhisperMessageBuilder*) builderWithPrototype:(TSProtoWhisperMessage*) prototype;
|
||||
- (TSProtoWhisperMessageBuilder*) toBuilder;
|
||||
|
||||
+ (TSProtoWhisperMessage*) parseFromData:(NSData*) data;
|
||||
+ (TSProtoWhisperMessage*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoWhisperMessage*) parseFromInputStream:(NSInputStream*) input;
|
||||
+ (TSProtoWhisperMessage*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoWhisperMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
+ (TSProtoWhisperMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
@end
|
||||
|
||||
@interface TSProtoWhisperMessageBuilder : PBGeneratedMessageBuilder {
|
||||
@private
|
||||
TSProtoWhisperMessage* result;
|
||||
}
|
||||
|
||||
- (TSProtoWhisperMessage*) defaultInstance;
|
||||
|
||||
- (TSProtoWhisperMessageBuilder*) clear;
|
||||
- (TSProtoWhisperMessageBuilder*) clone;
|
||||
|
||||
- (TSProtoWhisperMessage*) build;
|
||||
- (TSProtoWhisperMessage*) buildPartial;
|
||||
|
||||
- (TSProtoWhisperMessageBuilder*) mergeFrom:(TSProtoWhisperMessage*) other;
|
||||
- (TSProtoWhisperMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
- (TSProtoWhisperMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
|
||||
- (BOOL) hasRatchetKey;
|
||||
- (NSData*) ratchetKey;
|
||||
- (TSProtoWhisperMessageBuilder*) setRatchetKey:(NSData*) value;
|
||||
- (TSProtoWhisperMessageBuilder*) clearRatchetKey;
|
||||
|
||||
- (BOOL) hasCounter;
|
||||
- (UInt32) counter;
|
||||
- (TSProtoWhisperMessageBuilder*) setCounter:(UInt32) value;
|
||||
- (TSProtoWhisperMessageBuilder*) clearCounter;
|
||||
|
||||
- (BOOL) hasPreviousCounter;
|
||||
- (UInt32) previousCounter;
|
||||
- (TSProtoWhisperMessageBuilder*) setPreviousCounter:(UInt32) value;
|
||||
- (TSProtoWhisperMessageBuilder*) clearPreviousCounter;
|
||||
|
||||
- (BOOL) hasCiphertext;
|
||||
- (NSData*) ciphertext;
|
||||
- (TSProtoWhisperMessageBuilder*) setCiphertext:(NSData*) value;
|
||||
- (TSProtoWhisperMessageBuilder*) clearCiphertext;
|
||||
@end
|
||||
|
||||
@interface TSProtoPreKeyWhisperMessage : PBGeneratedMessage {
|
||||
@private
|
||||
BOOL hasBaseKey_:1;
|
||||
BOOL hasIdentityKey_:1;
|
||||
BOOL hasMessage_:1;
|
||||
BOOL hasRegistrationId_:1;
|
||||
BOOL hasPreKeyId_:1;
|
||||
BOOL hasSignedPreKeyId_:1;
|
||||
NSData* baseKey;
|
||||
NSData* identityKey;
|
||||
NSData* message;
|
||||
UInt32 registrationId;
|
||||
UInt32 preKeyId;
|
||||
UInt32 signedPreKeyId;
|
||||
}
|
||||
- (BOOL) hasRegistrationId;
|
||||
- (BOOL) hasPreKeyId;
|
||||
- (BOOL) hasSignedPreKeyId;
|
||||
- (BOOL) hasBaseKey;
|
||||
- (BOOL) hasIdentityKey;
|
||||
- (BOOL) hasMessage;
|
||||
@property (readonly) UInt32 registrationId;
|
||||
@property (readonly) UInt32 preKeyId;
|
||||
@property (readonly) UInt32 signedPreKeyId;
|
||||
@property (readonly, strong) NSData* baseKey;
|
||||
@property (readonly, strong) NSData* identityKey;
|
||||
@property (readonly, strong) NSData* message;
|
||||
|
||||
+ (TSProtoPreKeyWhisperMessage*) defaultInstance;
|
||||
- (TSProtoPreKeyWhisperMessage*) defaultInstance;
|
||||
|
||||
- (BOOL) isInitialized;
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) builder;
|
||||
+ (TSProtoPreKeyWhisperMessageBuilder*) builder;
|
||||
+ (TSProtoPreKeyWhisperMessageBuilder*) builderWithPrototype:(TSProtoPreKeyWhisperMessage*) prototype;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) toBuilder;
|
||||
|
||||
+ (TSProtoPreKeyWhisperMessage*) parseFromData:(NSData*) data;
|
||||
+ (TSProtoPreKeyWhisperMessage*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoPreKeyWhisperMessage*) parseFromInputStream:(NSInputStream*) input;
|
||||
+ (TSProtoPreKeyWhisperMessage*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoPreKeyWhisperMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
+ (TSProtoPreKeyWhisperMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
@end
|
||||
|
||||
@interface TSProtoPreKeyWhisperMessageBuilder : PBGeneratedMessageBuilder {
|
||||
@private
|
||||
TSProtoPreKeyWhisperMessage* result;
|
||||
}
|
||||
|
||||
- (TSProtoPreKeyWhisperMessage*) defaultInstance;
|
||||
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clear;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clone;
|
||||
|
||||
- (TSProtoPreKeyWhisperMessage*) build;
|
||||
- (TSProtoPreKeyWhisperMessage*) buildPartial;
|
||||
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) mergeFrom:(TSProtoPreKeyWhisperMessage*) other;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
|
||||
- (BOOL) hasRegistrationId;
|
||||
- (UInt32) registrationId;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) setRegistrationId:(UInt32) value;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clearRegistrationId;
|
||||
|
||||
- (BOOL) hasPreKeyId;
|
||||
- (UInt32) preKeyId;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) setPreKeyId:(UInt32) value;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clearPreKeyId;
|
||||
|
||||
- (BOOL) hasSignedPreKeyId;
|
||||
- (UInt32) signedPreKeyId;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) setSignedPreKeyId:(UInt32) value;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clearSignedPreKeyId;
|
||||
|
||||
- (BOOL) hasBaseKey;
|
||||
- (NSData*) baseKey;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) setBaseKey:(NSData*) value;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clearBaseKey;
|
||||
|
||||
- (BOOL) hasIdentityKey;
|
||||
- (NSData*) identityKey;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) setIdentityKey:(NSData*) value;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clearIdentityKey;
|
||||
|
||||
- (BOOL) hasMessage;
|
||||
- (NSData*) message;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) setMessage:(NSData*) value;
|
||||
- (TSProtoPreKeyWhisperMessageBuilder*) clearMessage;
|
||||
@end
|
||||
|
||||
@interface TSProtoKeyExchangeMessage : PBGeneratedMessage {
|
||||
@private
|
||||
BOOL hasBaseKey_:1;
|
||||
BOOL hasRatchetKey_:1;
|
||||
BOOL hasIdentityKey_:1;
|
||||
BOOL hasBaseKeySignature_:1;
|
||||
BOOL hasId_:1;
|
||||
NSData* baseKey;
|
||||
NSData* ratchetKey;
|
||||
NSData* identityKey;
|
||||
NSData* baseKeySignature;
|
||||
UInt32 id;
|
||||
}
|
||||
- (BOOL) hasId;
|
||||
- (BOOL) hasBaseKey;
|
||||
- (BOOL) hasRatchetKey;
|
||||
- (BOOL) hasIdentityKey;
|
||||
- (BOOL) hasBaseKeySignature;
|
||||
@property (readonly) UInt32 id;
|
||||
@property (readonly, strong) NSData* baseKey;
|
||||
@property (readonly, strong) NSData* ratchetKey;
|
||||
@property (readonly, strong) NSData* identityKey;
|
||||
@property (readonly, strong) NSData* baseKeySignature;
|
||||
|
||||
+ (TSProtoKeyExchangeMessage*) defaultInstance;
|
||||
- (TSProtoKeyExchangeMessage*) defaultInstance;
|
||||
|
||||
- (BOOL) isInitialized;
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) builder;
|
||||
+ (TSProtoKeyExchangeMessageBuilder*) builder;
|
||||
+ (TSProtoKeyExchangeMessageBuilder*) builderWithPrototype:(TSProtoKeyExchangeMessage*) prototype;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) toBuilder;
|
||||
|
||||
+ (TSProtoKeyExchangeMessage*) parseFromData:(NSData*) data;
|
||||
+ (TSProtoKeyExchangeMessage*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoKeyExchangeMessage*) parseFromInputStream:(NSInputStream*) input;
|
||||
+ (TSProtoKeyExchangeMessage*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoKeyExchangeMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
+ (TSProtoKeyExchangeMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
@end
|
||||
|
||||
@interface TSProtoKeyExchangeMessageBuilder : PBGeneratedMessageBuilder {
|
||||
@private
|
||||
TSProtoKeyExchangeMessage* result;
|
||||
}
|
||||
|
||||
- (TSProtoKeyExchangeMessage*) defaultInstance;
|
||||
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clear;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clone;
|
||||
|
||||
- (TSProtoKeyExchangeMessage*) build;
|
||||
- (TSProtoKeyExchangeMessage*) buildPartial;
|
||||
|
||||
- (TSProtoKeyExchangeMessageBuilder*) mergeFrom:(TSProtoKeyExchangeMessage*) other;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
|
||||
- (BOOL) hasId;
|
||||
- (UInt32) id;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) setId:(UInt32) value;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clearId;
|
||||
|
||||
- (BOOL) hasBaseKey;
|
||||
- (NSData*) baseKey;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) setBaseKey:(NSData*) value;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clearBaseKey;
|
||||
|
||||
- (BOOL) hasRatchetKey;
|
||||
- (NSData*) ratchetKey;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) setRatchetKey:(NSData*) value;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clearRatchetKey;
|
||||
|
||||
- (BOOL) hasIdentityKey;
|
||||
- (NSData*) identityKey;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) setIdentityKey:(NSData*) value;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clearIdentityKey;
|
||||
|
||||
- (BOOL) hasBaseKeySignature;
|
||||
- (NSData*) baseKeySignature;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) setBaseKeySignature:(NSData*) value;
|
||||
- (TSProtoKeyExchangeMessageBuilder*) clearBaseKeySignature;
|
||||
@end
|
||||
|
||||
@interface TSProtoSenderKeyMessage : PBGeneratedMessage {
|
||||
@private
|
||||
BOOL hasCiphertext_:1;
|
||||
BOOL hasId_:1;
|
||||
BOOL hasIteration_:1;
|
||||
NSData* ciphertext;
|
||||
UInt32 id;
|
||||
UInt32 iteration;
|
||||
}
|
||||
- (BOOL) hasId;
|
||||
- (BOOL) hasIteration;
|
||||
- (BOOL) hasCiphertext;
|
||||
@property (readonly) UInt32 id;
|
||||
@property (readonly) UInt32 iteration;
|
||||
@property (readonly, strong) NSData* ciphertext;
|
||||
|
||||
+ (TSProtoSenderKeyMessage*) defaultInstance;
|
||||
- (TSProtoSenderKeyMessage*) defaultInstance;
|
||||
|
||||
- (BOOL) isInitialized;
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
|
||||
- (TSProtoSenderKeyMessageBuilder*) builder;
|
||||
+ (TSProtoSenderKeyMessageBuilder*) builder;
|
||||
+ (TSProtoSenderKeyMessageBuilder*) builderWithPrototype:(TSProtoSenderKeyMessage*) prototype;
|
||||
- (TSProtoSenderKeyMessageBuilder*) toBuilder;
|
||||
|
||||
+ (TSProtoSenderKeyMessage*) parseFromData:(NSData*) data;
|
||||
+ (TSProtoSenderKeyMessage*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoSenderKeyMessage*) parseFromInputStream:(NSInputStream*) input;
|
||||
+ (TSProtoSenderKeyMessage*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoSenderKeyMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
+ (TSProtoSenderKeyMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
@end
|
||||
|
||||
@interface TSProtoSenderKeyMessageBuilder : PBGeneratedMessageBuilder {
|
||||
@private
|
||||
TSProtoSenderKeyMessage* result;
|
||||
}
|
||||
|
||||
- (TSProtoSenderKeyMessage*) defaultInstance;
|
||||
|
||||
- (TSProtoSenderKeyMessageBuilder*) clear;
|
||||
- (TSProtoSenderKeyMessageBuilder*) clone;
|
||||
|
||||
- (TSProtoSenderKeyMessage*) build;
|
||||
- (TSProtoSenderKeyMessage*) buildPartial;
|
||||
|
||||
- (TSProtoSenderKeyMessageBuilder*) mergeFrom:(TSProtoSenderKeyMessage*) other;
|
||||
- (TSProtoSenderKeyMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
- (TSProtoSenderKeyMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
|
||||
- (BOOL) hasId;
|
||||
- (UInt32) id;
|
||||
- (TSProtoSenderKeyMessageBuilder*) setId:(UInt32) value;
|
||||
- (TSProtoSenderKeyMessageBuilder*) clearId;
|
||||
|
||||
- (BOOL) hasIteration;
|
||||
- (UInt32) iteration;
|
||||
- (TSProtoSenderKeyMessageBuilder*) setIteration:(UInt32) value;
|
||||
- (TSProtoSenderKeyMessageBuilder*) clearIteration;
|
||||
|
||||
- (BOOL) hasCiphertext;
|
||||
- (NSData*) ciphertext;
|
||||
- (TSProtoSenderKeyMessageBuilder*) setCiphertext:(NSData*) value;
|
||||
- (TSProtoSenderKeyMessageBuilder*) clearCiphertext;
|
||||
@end
|
||||
|
||||
@interface TSProtoSenderKeyDistributionMessage : PBGeneratedMessage {
|
||||
@private
|
||||
BOOL hasChainKey_:1;
|
||||
BOOL hasSigningKey_:1;
|
||||
BOOL hasId_:1;
|
||||
BOOL hasIteration_:1;
|
||||
NSData* chainKey;
|
||||
NSData* signingKey;
|
||||
UInt32 id;
|
||||
UInt32 iteration;
|
||||
}
|
||||
- (BOOL) hasId;
|
||||
- (BOOL) hasIteration;
|
||||
- (BOOL) hasChainKey;
|
||||
- (BOOL) hasSigningKey;
|
||||
@property (readonly) UInt32 id;
|
||||
@property (readonly) UInt32 iteration;
|
||||
@property (readonly, strong) NSData* chainKey;
|
||||
@property (readonly, strong) NSData* signingKey;
|
||||
|
||||
+ (TSProtoSenderKeyDistributionMessage*) defaultInstance;
|
||||
- (TSProtoSenderKeyDistributionMessage*) defaultInstance;
|
||||
|
||||
- (BOOL) isInitialized;
|
||||
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) builder;
|
||||
+ (TSProtoSenderKeyDistributionMessageBuilder*) builder;
|
||||
+ (TSProtoSenderKeyDistributionMessageBuilder*) builderWithPrototype:(TSProtoSenderKeyDistributionMessage*) prototype;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) toBuilder;
|
||||
|
||||
+ (TSProtoSenderKeyDistributionMessage*) parseFromData:(NSData*) data;
|
||||
+ (TSProtoSenderKeyDistributionMessage*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoSenderKeyDistributionMessage*) parseFromInputStream:(NSInputStream*) input;
|
||||
+ (TSProtoSenderKeyDistributionMessage*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
+ (TSProtoSenderKeyDistributionMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
+ (TSProtoSenderKeyDistributionMessage*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
@end
|
||||
|
||||
@interface TSProtoSenderKeyDistributionMessageBuilder : PBGeneratedMessageBuilder {
|
||||
@private
|
||||
TSProtoSenderKeyDistributionMessage* result;
|
||||
}
|
||||
|
||||
- (TSProtoSenderKeyDistributionMessage*) defaultInstance;
|
||||
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) clear;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) clone;
|
||||
|
||||
- (TSProtoSenderKeyDistributionMessage*) build;
|
||||
- (TSProtoSenderKeyDistributionMessage*) buildPartial;
|
||||
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) mergeFrom:(TSProtoSenderKeyDistributionMessage*) other;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
|
||||
|
||||
- (BOOL) hasId;
|
||||
- (UInt32) id;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) setId:(UInt32) value;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) clearId;
|
||||
|
||||
- (BOOL) hasIteration;
|
||||
- (UInt32) iteration;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) setIteration:(UInt32) value;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) clearIteration;
|
||||
|
||||
- (BOOL) hasChainKey;
|
||||
- (NSData*) chainKey;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) setChainKey:(NSData*) value;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) clearChainKey;
|
||||
|
||||
- (BOOL) hasSigningKey;
|
||||
- (NSData*) signingKey;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) setSigningKey:(NSData*) value;
|
||||
- (TSProtoSenderKeyDistributionMessageBuilder*) clearSigningKey;
|
||||
@end
|
||||
|
||||
|
||||
// @@protoc_insertion_point(global_scope)
|
||||
File diff suppressed because it is too large
Load Diff
12
AxolotlKit/Classes/Protos/Makefile
Normal file
12
AxolotlKit/Classes/Protos/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
PROTOC=protoc \
|
||||
--proto_path='./'
|
||||
WRAPPER_SCRIPT=../../../../Signal-iOS/Scripts/ProtoWrappers.py \
|
||||
--proto-dir='./' --verbose --add-log-tag
|
||||
|
||||
all: webrtc_data_proto
|
||||
|
||||
webrtc_data_proto: WhisperTextProtocol.proto
|
||||
$(PROTOC) --swift_out=. \
|
||||
WhisperTextProtocol.proto
|
||||
$(WRAPPER_SCRIPT) --dst-dir=. \
|
||||
--wrapper-prefix=SPKProto --proto-prefix=SPKProtos --proto-file=WhisperTextProtocol.proto
|
||||
647
AxolotlKit/Classes/Protos/SPKProto.swift
Normal file
647
AxolotlKit/Classes/Protos/SPKProto.swift
Normal file
@ -0,0 +1,647 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// WARNING: This code is generated. Only edit within the markers.
|
||||
|
||||
public enum SPKProtoError: Error {
|
||||
case invalidProtobuf(description: String)
|
||||
}
|
||||
|
||||
// MARK: - SPKProtoTSProtoWhisperMessage
|
||||
|
||||
@objc public class SPKProtoTSProtoWhisperMessage: NSObject {
|
||||
|
||||
// MARK: - SPKProtoTSProtoWhisperMessageBuilder
|
||||
|
||||
@objc public class func builder(ratchetKey: Data, counter: UInt32, ciphertext: Data) -> SPKProtoTSProtoWhisperMessageBuilder {
|
||||
return SPKProtoTSProtoWhisperMessageBuilder(ratchetKey: ratchetKey, counter: counter, ciphertext: ciphertext)
|
||||
}
|
||||
|
||||
@objc public class SPKProtoTSProtoWhisperMessageBuilder: NSObject {
|
||||
|
||||
private var proto = SPKProtos_TSProtoWhisperMessage()
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc fileprivate init(ratchetKey: Data, counter: UInt32, ciphertext: Data) {
|
||||
super.init()
|
||||
|
||||
setRatchetKey(ratchetKey)
|
||||
setCounter(counter)
|
||||
setCiphertext(ciphertext)
|
||||
}
|
||||
|
||||
@objc public func setRatchetKey(_ valueParam: Data) {
|
||||
proto.ratchetKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setCounter(_ valueParam: UInt32) {
|
||||
proto.counter = valueParam
|
||||
}
|
||||
|
||||
@objc public func setPreviousCounter(_ valueParam: UInt32) {
|
||||
proto.previousCounter = valueParam
|
||||
}
|
||||
|
||||
@objc public func setCiphertext(_ valueParam: Data) {
|
||||
proto.ciphertext = valueParam
|
||||
}
|
||||
|
||||
@objc public func build() throws -> SPKProtoTSProtoWhisperMessage {
|
||||
return try SPKProtoTSProtoWhisperMessage.parseProto(proto)
|
||||
}
|
||||
|
||||
@objc public func buildSerializedData() throws -> Data {
|
||||
return try SPKProtoTSProtoWhisperMessage.parseProto(proto).serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let proto: SPKProtos_TSProtoWhisperMessage
|
||||
|
||||
@objc public let ratchetKey: Data
|
||||
|
||||
@objc public let counter: UInt32
|
||||
|
||||
@objc public let ciphertext: Data
|
||||
|
||||
@objc public var previousCounter: UInt32 {
|
||||
return proto.previousCounter
|
||||
}
|
||||
@objc public var hasPreviousCounter: Bool {
|
||||
return proto.hasPreviousCounter
|
||||
}
|
||||
|
||||
private init(proto: SPKProtos_TSProtoWhisperMessage,
|
||||
ratchetKey: Data,
|
||||
counter: UInt32,
|
||||
ciphertext: Data) {
|
||||
self.proto = proto
|
||||
self.ratchetKey = ratchetKey
|
||||
self.counter = counter
|
||||
self.ciphertext = ciphertext
|
||||
}
|
||||
|
||||
@objc
|
||||
public func serializedData() throws -> Data {
|
||||
return try self.proto.serializedData()
|
||||
}
|
||||
|
||||
@objc public class func parseData(_ serializedData: Data) throws -> SPKProtoTSProtoWhisperMessage {
|
||||
let proto = try SPKProtos_TSProtoWhisperMessage(serializedData: serializedData)
|
||||
return try parseProto(proto)
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SPKProtos_TSProtoWhisperMessage) throws -> SPKProtoTSProtoWhisperMessage {
|
||||
guard proto.hasRatchetKey else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: ratchetKey")
|
||||
}
|
||||
let ratchetKey = proto.ratchetKey
|
||||
|
||||
guard proto.hasCounter else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: counter")
|
||||
}
|
||||
let counter = proto.counter
|
||||
|
||||
guard proto.hasCiphertext else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: ciphertext")
|
||||
}
|
||||
let ciphertext = proto.ciphertext
|
||||
|
||||
// MARK: - Begin Validation Logic for SPKProtoTSProtoWhisperMessage -
|
||||
|
||||
// MARK: - End Validation Logic for SPKProtoTSProtoWhisperMessage -
|
||||
|
||||
let result = SPKProtoTSProtoWhisperMessage(proto: proto,
|
||||
ratchetKey: ratchetKey,
|
||||
counter: counter,
|
||||
ciphertext: ciphertext)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
extension SPKProtoTSProtoWhisperMessage {
|
||||
@objc public func serializedDataIgnoringErrors() -> Data? {
|
||||
return try! self.serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtoTSProtoWhisperMessage.SPKProtoTSProtoWhisperMessageBuilder {
|
||||
@objc public func buildIgnoringErrors() -> SPKProtoTSProtoWhisperMessage? {
|
||||
return try! self.build()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - SPKProtoTSProtoPreKeyWhisperMessage
|
||||
|
||||
@objc public class SPKProtoTSProtoPreKeyWhisperMessage: NSObject {
|
||||
|
||||
// MARK: - SPKProtoTSProtoPreKeyWhisperMessageBuilder
|
||||
|
||||
@objc public class func builder(signedPreKeyID: UInt32, baseKey: Data, identityKey: Data, message: Data) -> SPKProtoTSProtoPreKeyWhisperMessageBuilder {
|
||||
return SPKProtoTSProtoPreKeyWhisperMessageBuilder(signedPreKeyID: signedPreKeyID, baseKey: baseKey, identityKey: identityKey, message: message)
|
||||
}
|
||||
|
||||
@objc public class SPKProtoTSProtoPreKeyWhisperMessageBuilder: NSObject {
|
||||
|
||||
private var proto = SPKProtos_TSProtoPreKeyWhisperMessage()
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc fileprivate init(signedPreKeyID: UInt32, baseKey: Data, identityKey: Data, message: Data) {
|
||||
super.init()
|
||||
|
||||
setSignedPreKeyID(signedPreKeyID)
|
||||
setBaseKey(baseKey)
|
||||
setIdentityKey(identityKey)
|
||||
setMessage(message)
|
||||
}
|
||||
|
||||
@objc public func setRegistrationID(_ valueParam: UInt32) {
|
||||
proto.registrationID = valueParam
|
||||
}
|
||||
|
||||
@objc public func setPreKeyID(_ valueParam: UInt32) {
|
||||
proto.preKeyID = valueParam
|
||||
}
|
||||
|
||||
@objc public func setSignedPreKeyID(_ valueParam: UInt32) {
|
||||
proto.signedPreKeyID = valueParam
|
||||
}
|
||||
|
||||
@objc public func setBaseKey(_ valueParam: Data) {
|
||||
proto.baseKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setIdentityKey(_ valueParam: Data) {
|
||||
proto.identityKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setMessage(_ valueParam: Data) {
|
||||
proto.message = valueParam
|
||||
}
|
||||
|
||||
@objc public func build() throws -> SPKProtoTSProtoPreKeyWhisperMessage {
|
||||
return try SPKProtoTSProtoPreKeyWhisperMessage.parseProto(proto)
|
||||
}
|
||||
|
||||
@objc public func buildSerializedData() throws -> Data {
|
||||
return try SPKProtoTSProtoPreKeyWhisperMessage.parseProto(proto).serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let proto: SPKProtos_TSProtoPreKeyWhisperMessage
|
||||
|
||||
@objc public let signedPreKeyID: UInt32
|
||||
|
||||
@objc public let baseKey: Data
|
||||
|
||||
@objc public let identityKey: Data
|
||||
|
||||
@objc public let message: Data
|
||||
|
||||
@objc public var registrationID: UInt32 {
|
||||
return proto.registrationID
|
||||
}
|
||||
@objc public var hasRegistrationID: Bool {
|
||||
return proto.hasRegistrationID
|
||||
}
|
||||
|
||||
@objc public var preKeyID: UInt32 {
|
||||
return proto.preKeyID
|
||||
}
|
||||
@objc public var hasPreKeyID: Bool {
|
||||
return proto.hasPreKeyID
|
||||
}
|
||||
|
||||
private init(proto: SPKProtos_TSProtoPreKeyWhisperMessage,
|
||||
signedPreKeyID: UInt32,
|
||||
baseKey: Data,
|
||||
identityKey: Data,
|
||||
message: Data) {
|
||||
self.proto = proto
|
||||
self.signedPreKeyID = signedPreKeyID
|
||||
self.baseKey = baseKey
|
||||
self.identityKey = identityKey
|
||||
self.message = message
|
||||
}
|
||||
|
||||
@objc
|
||||
public func serializedData() throws -> Data {
|
||||
return try self.proto.serializedData()
|
||||
}
|
||||
|
||||
@objc public class func parseData(_ serializedData: Data) throws -> SPKProtoTSProtoPreKeyWhisperMessage {
|
||||
let proto = try SPKProtos_TSProtoPreKeyWhisperMessage(serializedData: serializedData)
|
||||
return try parseProto(proto)
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SPKProtos_TSProtoPreKeyWhisperMessage) throws -> SPKProtoTSProtoPreKeyWhisperMessage {
|
||||
guard proto.hasSignedPreKeyID else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: signedPreKeyID")
|
||||
}
|
||||
let signedPreKeyID = proto.signedPreKeyID
|
||||
|
||||
guard proto.hasBaseKey else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: baseKey")
|
||||
}
|
||||
let baseKey = proto.baseKey
|
||||
|
||||
guard proto.hasIdentityKey else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: identityKey")
|
||||
}
|
||||
let identityKey = proto.identityKey
|
||||
|
||||
guard proto.hasMessage else {
|
||||
throw SPKProtoError.invalidProtobuf(description: "\(logTag) missing required field: message")
|
||||
}
|
||||
let message = proto.message
|
||||
|
||||
// MARK: - Begin Validation Logic for SPKProtoTSProtoPreKeyWhisperMessage -
|
||||
|
||||
// MARK: - End Validation Logic for SPKProtoTSProtoPreKeyWhisperMessage -
|
||||
|
||||
let result = SPKProtoTSProtoPreKeyWhisperMessage(proto: proto,
|
||||
signedPreKeyID: signedPreKeyID,
|
||||
baseKey: baseKey,
|
||||
identityKey: identityKey,
|
||||
message: message)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
extension SPKProtoTSProtoPreKeyWhisperMessage {
|
||||
@objc public func serializedDataIgnoringErrors() -> Data? {
|
||||
return try! self.serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtoTSProtoPreKeyWhisperMessage.SPKProtoTSProtoPreKeyWhisperMessageBuilder {
|
||||
@objc public func buildIgnoringErrors() -> SPKProtoTSProtoPreKeyWhisperMessage? {
|
||||
return try! self.build()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - SPKProtoTSProtoKeyExchangeMessage
|
||||
|
||||
@objc public class SPKProtoTSProtoKeyExchangeMessage: NSObject {
|
||||
|
||||
// MARK: - SPKProtoTSProtoKeyExchangeMessageBuilder
|
||||
|
||||
@objc public class func builder() -> SPKProtoTSProtoKeyExchangeMessageBuilder {
|
||||
return SPKProtoTSProtoKeyExchangeMessageBuilder()
|
||||
}
|
||||
|
||||
@objc public class SPKProtoTSProtoKeyExchangeMessageBuilder: NSObject {
|
||||
|
||||
private var proto = SPKProtos_TSProtoKeyExchangeMessage()
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc public func setId(_ valueParam: UInt32) {
|
||||
proto.id = valueParam
|
||||
}
|
||||
|
||||
@objc public func setBaseKey(_ valueParam: Data) {
|
||||
proto.baseKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setRatchetKey(_ valueParam: Data) {
|
||||
proto.ratchetKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setIdentityKey(_ valueParam: Data) {
|
||||
proto.identityKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setBaseKeySignature(_ valueParam: Data) {
|
||||
proto.baseKeySignature = valueParam
|
||||
}
|
||||
|
||||
@objc public func build() throws -> SPKProtoTSProtoKeyExchangeMessage {
|
||||
return try SPKProtoTSProtoKeyExchangeMessage.parseProto(proto)
|
||||
}
|
||||
|
||||
@objc public func buildSerializedData() throws -> Data {
|
||||
return try SPKProtoTSProtoKeyExchangeMessage.parseProto(proto).serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let proto: SPKProtos_TSProtoKeyExchangeMessage
|
||||
|
||||
@objc public var id: UInt32 {
|
||||
return proto.id
|
||||
}
|
||||
@objc public var hasID: Bool {
|
||||
return proto.hasID
|
||||
}
|
||||
|
||||
@objc public var baseKey: Data? {
|
||||
guard proto.hasBaseKey else {
|
||||
return nil
|
||||
}
|
||||
return proto.baseKey
|
||||
}
|
||||
@objc public var hasBaseKey: Bool {
|
||||
return proto.hasBaseKey
|
||||
}
|
||||
|
||||
@objc public var ratchetKey: Data? {
|
||||
guard proto.hasRatchetKey else {
|
||||
return nil
|
||||
}
|
||||
return proto.ratchetKey
|
||||
}
|
||||
@objc public var hasRatchetKey: Bool {
|
||||
return proto.hasRatchetKey
|
||||
}
|
||||
|
||||
@objc public var identityKey: Data? {
|
||||
guard proto.hasIdentityKey else {
|
||||
return nil
|
||||
}
|
||||
return proto.identityKey
|
||||
}
|
||||
@objc public var hasIdentityKey: Bool {
|
||||
return proto.hasIdentityKey
|
||||
}
|
||||
|
||||
@objc public var baseKeySignature: Data? {
|
||||
guard proto.hasBaseKeySignature else {
|
||||
return nil
|
||||
}
|
||||
return proto.baseKeySignature
|
||||
}
|
||||
@objc public var hasBaseKeySignature: Bool {
|
||||
return proto.hasBaseKeySignature
|
||||
}
|
||||
|
||||
private init(proto: SPKProtos_TSProtoKeyExchangeMessage) {
|
||||
self.proto = proto
|
||||
}
|
||||
|
||||
@objc
|
||||
public func serializedData() throws -> Data {
|
||||
return try self.proto.serializedData()
|
||||
}
|
||||
|
||||
@objc public class func parseData(_ serializedData: Data) throws -> SPKProtoTSProtoKeyExchangeMessage {
|
||||
let proto = try SPKProtos_TSProtoKeyExchangeMessage(serializedData: serializedData)
|
||||
return try parseProto(proto)
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SPKProtos_TSProtoKeyExchangeMessage) throws -> SPKProtoTSProtoKeyExchangeMessage {
|
||||
// MARK: - Begin Validation Logic for SPKProtoTSProtoKeyExchangeMessage -
|
||||
|
||||
// MARK: - End Validation Logic for SPKProtoTSProtoKeyExchangeMessage -
|
||||
|
||||
let result = SPKProtoTSProtoKeyExchangeMessage(proto: proto)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
extension SPKProtoTSProtoKeyExchangeMessage {
|
||||
@objc public func serializedDataIgnoringErrors() -> Data? {
|
||||
return try! self.serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtoTSProtoKeyExchangeMessage.SPKProtoTSProtoKeyExchangeMessageBuilder {
|
||||
@objc public func buildIgnoringErrors() -> SPKProtoTSProtoKeyExchangeMessage? {
|
||||
return try! self.build()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - SPKProtoTSProtoSenderKeyMessage
|
||||
|
||||
@objc public class SPKProtoTSProtoSenderKeyMessage: NSObject {
|
||||
|
||||
// MARK: - SPKProtoTSProtoSenderKeyMessageBuilder
|
||||
|
||||
@objc public class func builder() -> SPKProtoTSProtoSenderKeyMessageBuilder {
|
||||
return SPKProtoTSProtoSenderKeyMessageBuilder()
|
||||
}
|
||||
|
||||
@objc public class SPKProtoTSProtoSenderKeyMessageBuilder: NSObject {
|
||||
|
||||
private var proto = SPKProtos_TSProtoSenderKeyMessage()
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc public func setId(_ valueParam: UInt32) {
|
||||
proto.id = valueParam
|
||||
}
|
||||
|
||||
@objc public func setIteration(_ valueParam: UInt32) {
|
||||
proto.iteration = valueParam
|
||||
}
|
||||
|
||||
@objc public func setCiphertext(_ valueParam: Data) {
|
||||
proto.ciphertext = valueParam
|
||||
}
|
||||
|
||||
@objc public func build() throws -> SPKProtoTSProtoSenderKeyMessage {
|
||||
return try SPKProtoTSProtoSenderKeyMessage.parseProto(proto)
|
||||
}
|
||||
|
||||
@objc public func buildSerializedData() throws -> Data {
|
||||
return try SPKProtoTSProtoSenderKeyMessage.parseProto(proto).serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let proto: SPKProtos_TSProtoSenderKeyMessage
|
||||
|
||||
@objc public var id: UInt32 {
|
||||
return proto.id
|
||||
}
|
||||
@objc public var hasID: Bool {
|
||||
return proto.hasID
|
||||
}
|
||||
|
||||
@objc public var iteration: UInt32 {
|
||||
return proto.iteration
|
||||
}
|
||||
@objc public var hasIteration: Bool {
|
||||
return proto.hasIteration
|
||||
}
|
||||
|
||||
@objc public var ciphertext: Data? {
|
||||
guard proto.hasCiphertext else {
|
||||
return nil
|
||||
}
|
||||
return proto.ciphertext
|
||||
}
|
||||
@objc public var hasCiphertext: Bool {
|
||||
return proto.hasCiphertext
|
||||
}
|
||||
|
||||
private init(proto: SPKProtos_TSProtoSenderKeyMessage) {
|
||||
self.proto = proto
|
||||
}
|
||||
|
||||
@objc
|
||||
public func serializedData() throws -> Data {
|
||||
return try self.proto.serializedData()
|
||||
}
|
||||
|
||||
@objc public class func parseData(_ serializedData: Data) throws -> SPKProtoTSProtoSenderKeyMessage {
|
||||
let proto = try SPKProtos_TSProtoSenderKeyMessage(serializedData: serializedData)
|
||||
return try parseProto(proto)
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SPKProtos_TSProtoSenderKeyMessage) throws -> SPKProtoTSProtoSenderKeyMessage {
|
||||
// MARK: - Begin Validation Logic for SPKProtoTSProtoSenderKeyMessage -
|
||||
|
||||
// MARK: - End Validation Logic for SPKProtoTSProtoSenderKeyMessage -
|
||||
|
||||
let result = SPKProtoTSProtoSenderKeyMessage(proto: proto)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
extension SPKProtoTSProtoSenderKeyMessage {
|
||||
@objc public func serializedDataIgnoringErrors() -> Data? {
|
||||
return try! self.serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtoTSProtoSenderKeyMessage.SPKProtoTSProtoSenderKeyMessageBuilder {
|
||||
@objc public func buildIgnoringErrors() -> SPKProtoTSProtoSenderKeyMessage? {
|
||||
return try! self.build()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - SPKProtoTSProtoSenderKeyDistributionMessage
|
||||
|
||||
@objc public class SPKProtoTSProtoSenderKeyDistributionMessage: NSObject {
|
||||
|
||||
// MARK: - SPKProtoTSProtoSenderKeyDistributionMessageBuilder
|
||||
|
||||
@objc public class func builder() -> SPKProtoTSProtoSenderKeyDistributionMessageBuilder {
|
||||
return SPKProtoTSProtoSenderKeyDistributionMessageBuilder()
|
||||
}
|
||||
|
||||
@objc public class SPKProtoTSProtoSenderKeyDistributionMessageBuilder: NSObject {
|
||||
|
||||
private var proto = SPKProtos_TSProtoSenderKeyDistributionMessage()
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc public func setId(_ valueParam: UInt32) {
|
||||
proto.id = valueParam
|
||||
}
|
||||
|
||||
@objc public func setIteration(_ valueParam: UInt32) {
|
||||
proto.iteration = valueParam
|
||||
}
|
||||
|
||||
@objc public func setChainKey(_ valueParam: Data) {
|
||||
proto.chainKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func setSigningKey(_ valueParam: Data) {
|
||||
proto.signingKey = valueParam
|
||||
}
|
||||
|
||||
@objc public func build() throws -> SPKProtoTSProtoSenderKeyDistributionMessage {
|
||||
return try SPKProtoTSProtoSenderKeyDistributionMessage.parseProto(proto)
|
||||
}
|
||||
|
||||
@objc public func buildSerializedData() throws -> Data {
|
||||
return try SPKProtoTSProtoSenderKeyDistributionMessage.parseProto(proto).serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let proto: SPKProtos_TSProtoSenderKeyDistributionMessage
|
||||
|
||||
@objc public var id: UInt32 {
|
||||
return proto.id
|
||||
}
|
||||
@objc public var hasID: Bool {
|
||||
return proto.hasID
|
||||
}
|
||||
|
||||
@objc public var iteration: UInt32 {
|
||||
return proto.iteration
|
||||
}
|
||||
@objc public var hasIteration: Bool {
|
||||
return proto.hasIteration
|
||||
}
|
||||
|
||||
@objc public var chainKey: Data? {
|
||||
guard proto.hasChainKey else {
|
||||
return nil
|
||||
}
|
||||
return proto.chainKey
|
||||
}
|
||||
@objc public var hasChainKey: Bool {
|
||||
return proto.hasChainKey
|
||||
}
|
||||
|
||||
@objc public var signingKey: Data? {
|
||||
guard proto.hasSigningKey else {
|
||||
return nil
|
||||
}
|
||||
return proto.signingKey
|
||||
}
|
||||
@objc public var hasSigningKey: Bool {
|
||||
return proto.hasSigningKey
|
||||
}
|
||||
|
||||
private init(proto: SPKProtos_TSProtoSenderKeyDistributionMessage) {
|
||||
self.proto = proto
|
||||
}
|
||||
|
||||
@objc
|
||||
public func serializedData() throws -> Data {
|
||||
return try self.proto.serializedData()
|
||||
}
|
||||
|
||||
@objc public class func parseData(_ serializedData: Data) throws -> SPKProtoTSProtoSenderKeyDistributionMessage {
|
||||
let proto = try SPKProtos_TSProtoSenderKeyDistributionMessage(serializedData: serializedData)
|
||||
return try parseProto(proto)
|
||||
}
|
||||
|
||||
fileprivate class func parseProto(_ proto: SPKProtos_TSProtoSenderKeyDistributionMessage) throws -> SPKProtoTSProtoSenderKeyDistributionMessage {
|
||||
// MARK: - Begin Validation Logic for SPKProtoTSProtoSenderKeyDistributionMessage -
|
||||
|
||||
// MARK: - End Validation Logic for SPKProtoTSProtoSenderKeyDistributionMessage -
|
||||
|
||||
let result = SPKProtoTSProtoSenderKeyDistributionMessage(proto: proto)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
extension SPKProtoTSProtoSenderKeyDistributionMessage {
|
||||
@objc public func serializedDataIgnoringErrors() -> Data? {
|
||||
return try! self.serializedData()
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtoTSProtoSenderKeyDistributionMessage.SPKProtoTSProtoSenderKeyDistributionMessageBuilder {
|
||||
@objc public func buildIgnoringErrors() -> SPKProtoTSProtoSenderKeyDistributionMessage? {
|
||||
return try! self.build()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
556
AxolotlKit/Classes/Protos/WhisperTextProtocol.pb.swift
Normal file
556
AxolotlKit/Classes/Protos/WhisperTextProtocol.pb.swift
Normal file
@ -0,0 +1,556 @@
|
||||
// DO NOT EDIT.
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: WhisperTextProtocol.proto
|
||||
//
|
||||
// For information on using the generated types, please see the documenation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
/// iOS - since we use a modern proto-compiler, we must specify
|
||||
/// the legacy proto format.
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
// was generated by a version of the `protoc` Swift plug-in that is
|
||||
// incompatible with the version of SwiftProtobuf to which you are linking.
|
||||
// Please ensure that your are building against the same version of the API
|
||||
// that was used to generate this file.
|
||||
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
|
||||
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
|
||||
typealias Version = _2
|
||||
}
|
||||
|
||||
struct SPKProtos_TSProtoWhisperMessage {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
/// @required
|
||||
var ratchetKey: Data {
|
||||
get {return _ratchetKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_ratchetKey = newValue}
|
||||
}
|
||||
/// Returns true if `ratchetKey` has been explicitly set.
|
||||
var hasRatchetKey: Bool {return self._ratchetKey != nil}
|
||||
/// Clears the value of `ratchetKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearRatchetKey() {self._ratchetKey = nil}
|
||||
|
||||
/// @required
|
||||
var counter: UInt32 {
|
||||
get {return _counter ?? 0}
|
||||
set {_counter = newValue}
|
||||
}
|
||||
/// Returns true if `counter` has been explicitly set.
|
||||
var hasCounter: Bool {return self._counter != nil}
|
||||
/// Clears the value of `counter`. Subsequent reads from it will return its default value.
|
||||
mutating func clearCounter() {self._counter = nil}
|
||||
|
||||
var previousCounter: UInt32 {
|
||||
get {return _previousCounter ?? 0}
|
||||
set {_previousCounter = newValue}
|
||||
}
|
||||
/// Returns true if `previousCounter` has been explicitly set.
|
||||
var hasPreviousCounter: Bool {return self._previousCounter != nil}
|
||||
/// Clears the value of `previousCounter`. Subsequent reads from it will return its default value.
|
||||
mutating func clearPreviousCounter() {self._previousCounter = nil}
|
||||
|
||||
/// @required
|
||||
var ciphertext: Data {
|
||||
get {return _ciphertext ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_ciphertext = newValue}
|
||||
}
|
||||
/// Returns true if `ciphertext` has been explicitly set.
|
||||
var hasCiphertext: Bool {return self._ciphertext != nil}
|
||||
/// Clears the value of `ciphertext`. Subsequent reads from it will return its default value.
|
||||
mutating func clearCiphertext() {self._ciphertext = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _ratchetKey: Data? = nil
|
||||
fileprivate var _counter: UInt32? = nil
|
||||
fileprivate var _previousCounter: UInt32? = nil
|
||||
fileprivate var _ciphertext: Data? = nil
|
||||
}
|
||||
|
||||
struct SPKProtos_TSProtoPreKeyWhisperMessage {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var registrationID: UInt32 {
|
||||
get {return _registrationID ?? 0}
|
||||
set {_registrationID = newValue}
|
||||
}
|
||||
/// Returns true if `registrationID` has been explicitly set.
|
||||
var hasRegistrationID: Bool {return self._registrationID != nil}
|
||||
/// Clears the value of `registrationID`. Subsequent reads from it will return its default value.
|
||||
mutating func clearRegistrationID() {self._registrationID = nil}
|
||||
|
||||
var preKeyID: UInt32 {
|
||||
get {return _preKeyID ?? 0}
|
||||
set {_preKeyID = newValue}
|
||||
}
|
||||
/// Returns true if `preKeyID` has been explicitly set.
|
||||
var hasPreKeyID: Bool {return self._preKeyID != nil}
|
||||
/// Clears the value of `preKeyID`. Subsequent reads from it will return its default value.
|
||||
mutating func clearPreKeyID() {self._preKeyID = nil}
|
||||
|
||||
/// @required
|
||||
var signedPreKeyID: UInt32 {
|
||||
get {return _signedPreKeyID ?? 0}
|
||||
set {_signedPreKeyID = newValue}
|
||||
}
|
||||
/// Returns true if `signedPreKeyID` has been explicitly set.
|
||||
var hasSignedPreKeyID: Bool {return self._signedPreKeyID != nil}
|
||||
/// Clears the value of `signedPreKeyID`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSignedPreKeyID() {self._signedPreKeyID = nil}
|
||||
|
||||
/// @required
|
||||
var baseKey: Data {
|
||||
get {return _baseKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_baseKey = newValue}
|
||||
}
|
||||
/// Returns true if `baseKey` has been explicitly set.
|
||||
var hasBaseKey: Bool {return self._baseKey != nil}
|
||||
/// Clears the value of `baseKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearBaseKey() {self._baseKey = nil}
|
||||
|
||||
/// @required
|
||||
var identityKey: Data {
|
||||
get {return _identityKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_identityKey = newValue}
|
||||
}
|
||||
/// Returns true if `identityKey` has been explicitly set.
|
||||
var hasIdentityKey: Bool {return self._identityKey != nil}
|
||||
/// Clears the value of `identityKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearIdentityKey() {self._identityKey = nil}
|
||||
|
||||
/// @required
|
||||
var message: Data {
|
||||
get {return _message ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_message = newValue}
|
||||
}
|
||||
/// Returns true if `message` has been explicitly set.
|
||||
var hasMessage: Bool {return self._message != nil}
|
||||
/// Clears the value of `message`. Subsequent reads from it will return its default value.
|
||||
mutating func clearMessage() {self._message = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _registrationID: UInt32? = nil
|
||||
fileprivate var _preKeyID: UInt32? = nil
|
||||
fileprivate var _signedPreKeyID: UInt32? = nil
|
||||
fileprivate var _baseKey: Data? = nil
|
||||
fileprivate var _identityKey: Data? = nil
|
||||
fileprivate var _message: Data? = nil
|
||||
}
|
||||
|
||||
struct SPKProtos_TSProtoKeyExchangeMessage {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var id: UInt32 {
|
||||
get {return _id ?? 0}
|
||||
set {_id = newValue}
|
||||
}
|
||||
/// Returns true if `id` has been explicitly set.
|
||||
var hasID: Bool {return self._id != nil}
|
||||
/// Clears the value of `id`. Subsequent reads from it will return its default value.
|
||||
mutating func clearID() {self._id = nil}
|
||||
|
||||
var baseKey: Data {
|
||||
get {return _baseKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_baseKey = newValue}
|
||||
}
|
||||
/// Returns true if `baseKey` has been explicitly set.
|
||||
var hasBaseKey: Bool {return self._baseKey != nil}
|
||||
/// Clears the value of `baseKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearBaseKey() {self._baseKey = nil}
|
||||
|
||||
var ratchetKey: Data {
|
||||
get {return _ratchetKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_ratchetKey = newValue}
|
||||
}
|
||||
/// Returns true if `ratchetKey` has been explicitly set.
|
||||
var hasRatchetKey: Bool {return self._ratchetKey != nil}
|
||||
/// Clears the value of `ratchetKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearRatchetKey() {self._ratchetKey = nil}
|
||||
|
||||
var identityKey: Data {
|
||||
get {return _identityKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_identityKey = newValue}
|
||||
}
|
||||
/// Returns true if `identityKey` has been explicitly set.
|
||||
var hasIdentityKey: Bool {return self._identityKey != nil}
|
||||
/// Clears the value of `identityKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearIdentityKey() {self._identityKey = nil}
|
||||
|
||||
var baseKeySignature: Data {
|
||||
get {return _baseKeySignature ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_baseKeySignature = newValue}
|
||||
}
|
||||
/// Returns true if `baseKeySignature` has been explicitly set.
|
||||
var hasBaseKeySignature: Bool {return self._baseKeySignature != nil}
|
||||
/// Clears the value of `baseKeySignature`. Subsequent reads from it will return its default value.
|
||||
mutating func clearBaseKeySignature() {self._baseKeySignature = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _id: UInt32? = nil
|
||||
fileprivate var _baseKey: Data? = nil
|
||||
fileprivate var _ratchetKey: Data? = nil
|
||||
fileprivate var _identityKey: Data? = nil
|
||||
fileprivate var _baseKeySignature: Data? = nil
|
||||
}
|
||||
|
||||
struct SPKProtos_TSProtoSenderKeyMessage {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var id: UInt32 {
|
||||
get {return _id ?? 0}
|
||||
set {_id = newValue}
|
||||
}
|
||||
/// Returns true if `id` has been explicitly set.
|
||||
var hasID: Bool {return self._id != nil}
|
||||
/// Clears the value of `id`. Subsequent reads from it will return its default value.
|
||||
mutating func clearID() {self._id = nil}
|
||||
|
||||
var iteration: UInt32 {
|
||||
get {return _iteration ?? 0}
|
||||
set {_iteration = newValue}
|
||||
}
|
||||
/// Returns true if `iteration` has been explicitly set.
|
||||
var hasIteration: Bool {return self._iteration != nil}
|
||||
/// Clears the value of `iteration`. Subsequent reads from it will return its default value.
|
||||
mutating func clearIteration() {self._iteration = nil}
|
||||
|
||||
var ciphertext: Data {
|
||||
get {return _ciphertext ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_ciphertext = newValue}
|
||||
}
|
||||
/// Returns true if `ciphertext` has been explicitly set.
|
||||
var hasCiphertext: Bool {return self._ciphertext != nil}
|
||||
/// Clears the value of `ciphertext`. Subsequent reads from it will return its default value.
|
||||
mutating func clearCiphertext() {self._ciphertext = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _id: UInt32? = nil
|
||||
fileprivate var _iteration: UInt32? = nil
|
||||
fileprivate var _ciphertext: Data? = nil
|
||||
}
|
||||
|
||||
struct SPKProtos_TSProtoSenderKeyDistributionMessage {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var id: UInt32 {
|
||||
get {return _id ?? 0}
|
||||
set {_id = newValue}
|
||||
}
|
||||
/// Returns true if `id` has been explicitly set.
|
||||
var hasID: Bool {return self._id != nil}
|
||||
/// Clears the value of `id`. Subsequent reads from it will return its default value.
|
||||
mutating func clearID() {self._id = nil}
|
||||
|
||||
var iteration: UInt32 {
|
||||
get {return _iteration ?? 0}
|
||||
set {_iteration = newValue}
|
||||
}
|
||||
/// Returns true if `iteration` has been explicitly set.
|
||||
var hasIteration: Bool {return self._iteration != nil}
|
||||
/// Clears the value of `iteration`. Subsequent reads from it will return its default value.
|
||||
mutating func clearIteration() {self._iteration = nil}
|
||||
|
||||
var chainKey: Data {
|
||||
get {return _chainKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_chainKey = newValue}
|
||||
}
|
||||
/// Returns true if `chainKey` has been explicitly set.
|
||||
var hasChainKey: Bool {return self._chainKey != nil}
|
||||
/// Clears the value of `chainKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearChainKey() {self._chainKey = nil}
|
||||
|
||||
var signingKey: Data {
|
||||
get {return _signingKey ?? SwiftProtobuf.Internal.emptyData}
|
||||
set {_signingKey = newValue}
|
||||
}
|
||||
/// Returns true if `signingKey` has been explicitly set.
|
||||
var hasSigningKey: Bool {return self._signingKey != nil}
|
||||
/// Clears the value of `signingKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSigningKey() {self._signingKey = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
init() {}
|
||||
|
||||
fileprivate var _id: UInt32? = nil
|
||||
fileprivate var _iteration: UInt32? = nil
|
||||
fileprivate var _chainKey: Data? = nil
|
||||
fileprivate var _signingKey: Data? = nil
|
||||
}
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "SPKProtos"
|
||||
|
||||
extension SPKProtos_TSProtoWhisperMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".TSProtoWhisperMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "ratchetKey"),
|
||||
2: .same(proto: "counter"),
|
||||
3: .same(proto: "previousCounter"),
|
||||
4: .same(proto: "ciphertext"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularBytesField(value: &self._ratchetKey)
|
||||
case 2: try decoder.decodeSingularUInt32Field(value: &self._counter)
|
||||
case 3: try decoder.decodeSingularUInt32Field(value: &self._previousCounter)
|
||||
case 4: try decoder.decodeSingularBytesField(value: &self._ciphertext)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._ratchetKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._counter {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._previousCounter {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3)
|
||||
}
|
||||
if let v = self._ciphertext {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
func _protobuf_generated_isEqualTo(other: SPKProtos_TSProtoWhisperMessage) -> Bool {
|
||||
if self._ratchetKey != other._ratchetKey {return false}
|
||||
if self._counter != other._counter {return false}
|
||||
if self._previousCounter != other._previousCounter {return false}
|
||||
if self._ciphertext != other._ciphertext {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtos_TSProtoPreKeyWhisperMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".TSProtoPreKeyWhisperMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
5: .same(proto: "registrationId"),
|
||||
1: .same(proto: "preKeyId"),
|
||||
6: .same(proto: "signedPreKeyId"),
|
||||
2: .same(proto: "baseKey"),
|
||||
3: .same(proto: "identityKey"),
|
||||
4: .same(proto: "message"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularUInt32Field(value: &self._preKeyID)
|
||||
case 2: try decoder.decodeSingularBytesField(value: &self._baseKey)
|
||||
case 3: try decoder.decodeSingularBytesField(value: &self._identityKey)
|
||||
case 4: try decoder.decodeSingularBytesField(value: &self._message)
|
||||
case 5: try decoder.decodeSingularUInt32Field(value: &self._registrationID)
|
||||
case 6: try decoder.decodeSingularUInt32Field(value: &self._signedPreKeyID)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._preKeyID {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._baseKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._identityKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 3)
|
||||
}
|
||||
if let v = self._message {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 4)
|
||||
}
|
||||
if let v = self._registrationID {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 5)
|
||||
}
|
||||
if let v = self._signedPreKeyID {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 6)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
func _protobuf_generated_isEqualTo(other: SPKProtos_TSProtoPreKeyWhisperMessage) -> Bool {
|
||||
if self._registrationID != other._registrationID {return false}
|
||||
if self._preKeyID != other._preKeyID {return false}
|
||||
if self._signedPreKeyID != other._signedPreKeyID {return false}
|
||||
if self._baseKey != other._baseKey {return false}
|
||||
if self._identityKey != other._identityKey {return false}
|
||||
if self._message != other._message {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtos_TSProtoKeyExchangeMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".TSProtoKeyExchangeMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
2: .same(proto: "baseKey"),
|
||||
3: .same(proto: "ratchetKey"),
|
||||
4: .same(proto: "identityKey"),
|
||||
5: .same(proto: "baseKeySignature"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularUInt32Field(value: &self._id)
|
||||
case 2: try decoder.decodeSingularBytesField(value: &self._baseKey)
|
||||
case 3: try decoder.decodeSingularBytesField(value: &self._ratchetKey)
|
||||
case 4: try decoder.decodeSingularBytesField(value: &self._identityKey)
|
||||
case 5: try decoder.decodeSingularBytesField(value: &self._baseKeySignature)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._id {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._baseKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._ratchetKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 3)
|
||||
}
|
||||
if let v = self._identityKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 4)
|
||||
}
|
||||
if let v = self._baseKeySignature {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 5)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
func _protobuf_generated_isEqualTo(other: SPKProtos_TSProtoKeyExchangeMessage) -> Bool {
|
||||
if self._id != other._id {return false}
|
||||
if self._baseKey != other._baseKey {return false}
|
||||
if self._ratchetKey != other._ratchetKey {return false}
|
||||
if self._identityKey != other._identityKey {return false}
|
||||
if self._baseKeySignature != other._baseKeySignature {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtos_TSProtoSenderKeyMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".TSProtoSenderKeyMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
2: .same(proto: "iteration"),
|
||||
3: .same(proto: "ciphertext"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularUInt32Field(value: &self._id)
|
||||
case 2: try decoder.decodeSingularUInt32Field(value: &self._iteration)
|
||||
case 3: try decoder.decodeSingularBytesField(value: &self._ciphertext)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._id {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._iteration {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._ciphertext {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
func _protobuf_generated_isEqualTo(other: SPKProtos_TSProtoSenderKeyMessage) -> Bool {
|
||||
if self._id != other._id {return false}
|
||||
if self._iteration != other._iteration {return false}
|
||||
if self._ciphertext != other._ciphertext {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SPKProtos_TSProtoSenderKeyDistributionMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
static let protoMessageName: String = _protobuf_package + ".TSProtoSenderKeyDistributionMessage"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
2: .same(proto: "iteration"),
|
||||
3: .same(proto: "chainKey"),
|
||||
4: .same(proto: "signingKey"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularUInt32Field(value: &self._id)
|
||||
case 2: try decoder.decodeSingularUInt32Field(value: &self._iteration)
|
||||
case 3: try decoder.decodeSingularBytesField(value: &self._chainKey)
|
||||
case 4: try decoder.decodeSingularBytesField(value: &self._signingKey)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
if let v = self._id {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = self._iteration {
|
||||
try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2)
|
||||
}
|
||||
if let v = self._chainKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 3)
|
||||
}
|
||||
if let v = self._signingKey {
|
||||
try visitor.visitSingularBytesField(value: v, fieldNumber: 4)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
func _protobuf_generated_isEqualTo(other: SPKProtos_TSProtoSenderKeyDistributionMessage) -> Bool {
|
||||
if self._id != other._id {return false}
|
||||
if self._iteration != other._iteration {return false}
|
||||
if self._chainKey != other._chainKey {return false}
|
||||
if self._signingKey != other._signingKey {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,30 @@
|
||||
package textsecure;
|
||||
// iOS - since we use a modern proto-compiler, we must specify
|
||||
// the legacy proto format.
|
||||
syntax = "proto2";
|
||||
|
||||
// iOS - package name determines class prefix
|
||||
package SPKProtos;
|
||||
|
||||
message TSProtoWhisperMessage {
|
||||
// @required
|
||||
optional bytes ratchetKey = 1;
|
||||
// @required
|
||||
optional uint32 counter = 2;
|
||||
optional uint32 previousCounter = 3;
|
||||
// @required
|
||||
optional bytes ciphertext = 4;
|
||||
}
|
||||
|
||||
message TSProtoPreKeyWhisperMessage {
|
||||
optional uint32 registrationId = 5;
|
||||
optional uint32 preKeyId = 1;
|
||||
// @required
|
||||
optional uint32 signedPreKeyId = 6;
|
||||
// @required
|
||||
optional bytes baseKey = 2;
|
||||
// @required
|
||||
optional bytes identityKey = 3;
|
||||
// @required
|
||||
optional bytes message = 4; // WhisperMessage
|
||||
}
|
||||
|
||||
@ -13,7 +13,13 @@
|
||||
@synthesize ourIdentityKeyPair=_ourIdentityKeyPair, theirIdentityKey=_theirIdentityKey;
|
||||
|
||||
- (instancetype)initWithIdentityKey:(ECKeyPair*)myIdentityKey theirIdentityKey:(NSData*)theirIdentityKey ourBaseKey:(ECKeyPair*)ourBaseKey theirSignedPreKey:(NSData*)theirSignedPreKey theirOneTimePreKey:(NSData*)theirOneTimePreKey theirRatchetKey:(NSData*)theirRatchetKey{
|
||||
|
||||
|
||||
OWSAssert(myIdentityKey);
|
||||
OWSAssert(theirIdentityKey);
|
||||
OWSAssert(ourBaseKey);
|
||||
OWSAssert(theirSignedPreKey);
|
||||
OWSAssert(theirRatchetKey);
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
//
|
||||
// AxolotlParameters.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
@protocol AxolotlParameters <NSObject>
|
||||
|
||||
|
||||
@ -13,6 +13,13 @@
|
||||
@synthesize theirIdentityKey=_theirIdentityKey, ourIdentityKeyPair=_ourIdentityKeyPair;
|
||||
|
||||
- (instancetype)initWithMyIdentityKeyPair:(ECKeyPair*)ourIdentityKeyPair theirIdentityKey:(NSData*)theirIdentityKey ourSignedPrekey:(ECKeyPair*)ourSignedPrekey ourRatchetKey:(ECKeyPair*)ourRatchetKey ourOneTimePrekey:(ECKeyPair*)ourOneTimeKeyPair theirBaseKey:(NSData*)theirBaseKey{
|
||||
|
||||
OWSAssert(ourIdentityKeyPair);
|
||||
OWSAssert(theirIdentityKey);
|
||||
OWSAssert(ourSignedPrekey);
|
||||
OWSAssert(ourRatchetKey);
|
||||
OWSAssert(theirBaseKey);
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
|
||||
@ -1,26 +1,25 @@
|
||||
//
|
||||
// ChainKey.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/08/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Chain.h"
|
||||
#import "MessageKeys.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ChainKey : NSObject <NSSecureCoding>
|
||||
|
||||
-(instancetype)initWithData:(NSData*)chainKey index:(int)index;
|
||||
@property (nonatomic, readonly) int index;
|
||||
@property (nonatomic, readonly) NSData *key;
|
||||
|
||||
-(instancetype)nextChainKey;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithData:(NSData *)chainKey index:(int)index NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
-(MessageKeys*)messageKeys;
|
||||
- (instancetype)nextChainKey;
|
||||
|
||||
-(NSData*)baseMaterial:(NSData*)seed;
|
||||
|
||||
@property (readonly) int index;
|
||||
@property (readonly) NSData *key;
|
||||
- (MessageKeys *)throws_messageKeys NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,75 +1,95 @@
|
||||
//
|
||||
// ChainKey.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/08/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ChainKey.h"
|
||||
#import "TSDerivedSecrets.h"
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation ChainKey
|
||||
|
||||
static NSString* const kCoderKey = @"kCoderKey";
|
||||
static NSString* const kCoderIndex = @"kCoderIndex";
|
||||
static NSString *const kCoderKey = @"kCoderKey";
|
||||
static NSString *const kCoderIndex = @"kCoderIndex";
|
||||
|
||||
#define kTSKeySeedLength 1
|
||||
|
||||
static uint8_t kMessageKeySeed[kTSKeySeedLength] = {01};
|
||||
static uint8_t kChainKeySeed[kTSKeySeedLength] = {02};
|
||||
static uint8_t kMessageKeySeed[kTSKeySeedLength] = { 01 };
|
||||
static uint8_t kChainKeySeed[kTSKeySeedLength] = { 02 };
|
||||
|
||||
+ (BOOL)supportsSecureCoding{
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_key = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCoderKey];
|
||||
_index = [aDecoder decodeIntForKey:kCoderIndex];
|
||||
}
|
||||
|
||||
return self;
|
||||
- (nullable id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
NSData *key = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCoderKey];
|
||||
int index = [aDecoder decodeIntForKey:kCoderIndex];
|
||||
|
||||
return [self initWithData:key index:index];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder{
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_key forKey:kCoderKey];
|
||||
[aCoder encodeInt:_index forKey:kCoderIndex];
|
||||
[aCoder encodeInt:_index forKey:kCoderIndex];
|
||||
}
|
||||
|
||||
-(instancetype)initWithData:(NSData *)chainKey index:(int)index{
|
||||
- (instancetype)initWithData:(NSData *)chainKey index:(int)index
|
||||
{
|
||||
OWSAssert(chainKey.length == 32);
|
||||
OWSAssert(index >= 0);
|
||||
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self) {
|
||||
_key = chainKey;
|
||||
_key = chainKey;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype) nextChainKey{
|
||||
NSData* nextCK = [self baseMaterial:[NSData dataWithBytes:kChainKeySeed length:kTSKeySeedLength]];
|
||||
|
||||
return [[ChainKey alloc] initWithData:nextCK index:self.index+1];
|
||||
- (instancetype)nextChainKey
|
||||
{
|
||||
NSData *nextCK = [self baseMaterial:[NSData dataWithBytes:kChainKeySeed length:kTSKeySeedLength]];
|
||||
OWSAssert(nextCK.length == 32);
|
||||
|
||||
int nextIndex;
|
||||
ows_add_overflow(self.index, 1, &nextIndex);
|
||||
return [[ChainKey alloc] initWithData:nextCK index:nextIndex];
|
||||
}
|
||||
|
||||
- (MessageKeys*)messageKeys{
|
||||
- (MessageKeys *)throws_messageKeys
|
||||
{
|
||||
NSData *inputKeyMaterial = [self baseMaterial:[NSData dataWithBytes:kMessageKeySeed length:kTSKeySeedLength]];
|
||||
TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets derivedMessageKeysWithData:inputKeyMaterial];
|
||||
return [[MessageKeys alloc] initWithCipherKey:derivedSecrets.cipherKey macKey:derivedSecrets.macKey iv:derivedSecrets.iv index:self.index];
|
||||
TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets throws_derivedMessageKeysWithData:inputKeyMaterial];
|
||||
return [[MessageKeys alloc] initWithCipherKey:derivedSecrets.cipherKey
|
||||
macKey:derivedSecrets.macKey
|
||||
iv:derivedSecrets.iv
|
||||
index:self.index];
|
||||
}
|
||||
|
||||
- (NSData*)baseMaterial:(NSData*)seed{
|
||||
uint8_t result[CC_SHA256_DIGEST_LENGTH] = {0};
|
||||
- (NSData *)baseMaterial:(NSData *)seed
|
||||
{
|
||||
OWSAssert(self.key);
|
||||
OWSAssert(self.key.length == 32);
|
||||
OWSAssert(seed);
|
||||
OWSAssert(seed.length == kTSKeySeedLength);
|
||||
|
||||
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
|
||||
OWSAssert(bufferData);
|
||||
|
||||
CCHmacContext ctx;
|
||||
CCHmacInit(&ctx, kCCHmacAlgSHA256, [self.key bytes], [self.key length]);
|
||||
CCHmacUpdate(&ctx, [seed bytes], [seed length]);
|
||||
CCHmacFinal(&ctx, result);
|
||||
return [NSData dataWithBytes:result length:sizeof(result)];
|
||||
CCHmacFinal(&ctx, bufferData.mutableBytes);
|
||||
return [bufferData copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -37,6 +37,11 @@ static NSString* const kCoderMessageKeysIndex = @"kCoderMessageKeysIndex";
|
||||
|
||||
|
||||
- (instancetype)initWithCipherKey:(NSData*)cipherKey macKey:(NSData*)macKey iv:(NSData *)data index:(int)index{
|
||||
|
||||
OWSAssert(cipherKey);
|
||||
OWSAssert(macKey);
|
||||
OWSAssert(data);
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
//
|
||||
// RKCK.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 1/15/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SessionState.h"
|
||||
#import "Chain.h"
|
||||
#import "RootKey.h"
|
||||
#import "SessionState.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class ECKeyPair;
|
||||
|
||||
@interface RKCK : NSObject
|
||||
@ -19,4 +16,4 @@
|
||||
|
||||
-(instancetype) initWithRK:(RootKey*)rootKey CK:(ChainKey*)chainKey;
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
//
|
||||
// RKCK.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 1/15/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RKCK.h"
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import "TSDerivedSecrets.h"
|
||||
|
||||
@implementation RKCK
|
||||
|
||||
- (instancetype)initWithRK:(RootKey*)rootKey CK:(ChainKey*)chainKey{
|
||||
OWSAssert(rootKey);
|
||||
OWSAssert(chainKey);
|
||||
|
||||
self = [super init];
|
||||
self.rootKey = rootKey;
|
||||
self.chainKey = chainKey;
|
||||
|
||||
@ -1,27 +1,41 @@
|
||||
//
|
||||
// RatchetingSession.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@class SessionState;
|
||||
|
||||
@class AliceAxolotlParameters;
|
||||
@class BobAxolotlParameters;
|
||||
@class ECKeyPair;
|
||||
@class SessionState;
|
||||
|
||||
@interface RatchetingSession : NSObject
|
||||
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters;
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion BobParameters:(BobAxolotlParameters*)parameters;
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
aliceParameters:(AliceAxolotlParameters *)aliceParameters
|
||||
error:(NSError **)outError;
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
BobParameters:(BobAxolotlParameters *)parameters NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
bobParameters:(BobAxolotlParameters *)bobParameters
|
||||
error:(NSError **)outError;
|
||||
|
||||
/**
|
||||
* For testing purposes
|
||||
*/
|
||||
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters senderRatchet:(ECKeyPair*)ratchet;
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters
|
||||
senderRatchet:(ECKeyPair *)ratchet NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
@ -1,40 +1,40 @@
|
||||
//
|
||||
// RatchetingSession.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RatchetingSession.h"
|
||||
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "ChainKey.h"
|
||||
#import "RootKey.h"
|
||||
#import "SessionState.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
#import <25519/Curve25519.h>
|
||||
#import "ChainKey.h"
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
@interface DHEResult : NSObject
|
||||
|
||||
@property (nonatomic, readonly) RootKey *rootKey;
|
||||
@property (nonatomic, readonly) NSData *chainKey;
|
||||
|
||||
- (instancetype)initWithMasterKey:(NSData*)data;
|
||||
- (instancetype)init_throws_withMasterKey:(NSData *)data NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
@implementation DHEResult
|
||||
|
||||
- (instancetype)initWithMasterKey:(NSData*)data{
|
||||
NSAssert([data length] != 32*4 || [data length] != 32*3, @"DHE Result is expected to be the result of 3 or 4 DHEs outputting 32 bytes each");
|
||||
|
||||
- (instancetype)init_throws_withMasterKey:(NSData *)data
|
||||
{
|
||||
// DHE Result is expected to be the result of 3 or 4 DHEs outputting 32 bytes each,
|
||||
// plus the 32 discontinuity bytes added to make V3 incompatible with V2
|
||||
OWSAssert([data length] == 32 * 4 || [data length] == 32 * 5);
|
||||
|
||||
self = [super init];
|
||||
const char *HKDFDefaultSalt[4] = {0};
|
||||
NSData *salt = [NSData dataWithBytes:HKDFDefaultSalt length:sizeof(HKDFDefaultSalt)];
|
||||
NSData *info = [@"WhisperText" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *derivedMaterial = [HKDFKit deriveKey:data info:info salt:salt outputSize:64];
|
||||
NSData *derivedMaterial = [HKDFKit throws_deriveKey:data info:info salt:salt outputSize:64];
|
||||
OWSAssert(derivedMaterial.length == 64);
|
||||
_rootKey = [[RootKey alloc] initWithData:[derivedMaterial subdataWithRange:NSMakeRange(0, 32)]];
|
||||
_chainKey = [derivedMaterial subdataWithRange:NSMakeRange(32, 32)];
|
||||
|
||||
@ -46,37 +46,92 @@
|
||||
|
||||
@implementation RatchetingSession
|
||||
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters{
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters
|
||||
{
|
||||
OWSAssert(session);
|
||||
OWSAssert(parameters);
|
||||
|
||||
ECKeyPair *sendingRatchetKey = [Curve25519 generateKeyPair];
|
||||
[self initializeSession:session sessionVersion:sessionVersion AliceParameters:parameters senderRatchet:sendingRatchetKey];
|
||||
OWSAssert(sendingRatchetKey);
|
||||
[self throws_initializeSession:session
|
||||
sessionVersion:sessionVersion
|
||||
AliceParameters:parameters
|
||||
senderRatchet:sendingRatchetKey];
|
||||
}
|
||||
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion BobParameters:(BobAxolotlParameters*)parameters{
|
||||
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
bobParameters:(BobAxolotlParameters *)bobParameters
|
||||
error:(NSError **)outError
|
||||
{
|
||||
return [SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
[self throws_initializeSession:session sessionVersion:sessionVersion BobParameters:bobParameters];
|
||||
}
|
||||
error:outError];
|
||||
}
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
BobParameters:(BobAxolotlParameters *)parameters
|
||||
{
|
||||
OWSAssert(session);
|
||||
OWSAssert(parameters);
|
||||
|
||||
[session setVersion:sessionVersion];
|
||||
[session setRemoteIdentityKey:parameters.theirIdentityKey];
|
||||
[session setLocalIdentityKey:parameters.ourIdentityKeyPair.publicKey];
|
||||
|
||||
DHEResult *result = [self DHEKeyAgreement:parameters];
|
||||
|
||||
|
||||
DHEResult *result = [self throws_DHEKeyAgreement:parameters];
|
||||
OWSAssert(result);
|
||||
|
||||
[session setSenderChain:parameters.ourRatchetKey chainKey:[[ChainKey alloc]initWithData:result.chainKey index:0]];
|
||||
[session setRootKey:result.rootKey];
|
||||
}
|
||||
|
||||
+ (void)initializeSession:(SessionState*)session sessionVersion:(int)sessionVersion AliceParameters:(AliceAxolotlParameters*)parameters senderRatchet:(ECKeyPair*)sendingRatchet{
|
||||
+ (BOOL)initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
aliceParameters:(AliceAxolotlParameters *)aliceParameters
|
||||
error:(NSError **)outError
|
||||
{
|
||||
return [SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
[self throws_initializeSession:session sessionVersion:sessionVersion AliceParameters:aliceParameters];
|
||||
}
|
||||
error:outError];
|
||||
}
|
||||
|
||||
+ (void)throws_initializeSession:(SessionState *)session
|
||||
sessionVersion:(int)sessionVersion
|
||||
AliceParameters:(AliceAxolotlParameters *)parameters
|
||||
senderRatchet:(ECKeyPair *)sendingRatchet
|
||||
{
|
||||
|
||||
OWSAssert(session);
|
||||
OWSAssert(parameters);
|
||||
OWSAssert(sendingRatchet);
|
||||
|
||||
[session setVersion:sessionVersion];
|
||||
[session setRemoteIdentityKey:parameters.theirIdentityKey];
|
||||
[session setLocalIdentityKey:parameters.ourIdentityKeyPair.publicKey];
|
||||
|
||||
DHEResult *result = [self DHEKeyAgreement:parameters];
|
||||
RKCK *sendingChain = [result.rootKey createChainWithTheirEphemeral:parameters.theirRatchetKey ourEphemeral:sendingRatchet];
|
||||
|
||||
|
||||
DHEResult *result = [self throws_DHEKeyAgreement:parameters];
|
||||
OWSAssert(result);
|
||||
RKCK *sendingChain =
|
||||
[result.rootKey throws_createChainWithTheirEphemeral:parameters.theirRatchetKey ourEphemeral:sendingRatchet];
|
||||
OWSAssert(sendingChain);
|
||||
|
||||
[session addReceiverChain:parameters.theirRatchetKey chainKey:[[ChainKey alloc]initWithData:result.chainKey index:0]];
|
||||
[session setSenderChain:sendingRatchet chainKey:sendingChain.chainKey];
|
||||
[session setRootKey:sendingChain.rootKey];
|
||||
}
|
||||
|
||||
+ (DHEResult*)DHEKeyAgreement:(id<AxolotlParameters>)parameters{
|
||||
+ (DHEResult *)throws_DHEKeyAgreement:(id<AxolotlParameters>)parameters
|
||||
{
|
||||
OWSAssert(parameters);
|
||||
|
||||
NSMutableData *masterKey = [NSMutableData data];
|
||||
|
||||
[masterKey appendData:[self discontinuityBytes]];
|
||||
@ -84,24 +139,32 @@
|
||||
if ([parameters isKindOfClass:[AliceAxolotlParameters class]]) {
|
||||
AliceAxolotlParameters *params = (AliceAxolotlParameters*)parameters;
|
||||
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirSignedPreKey andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirIdentityKey andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirSignedPreKey andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirSignedPreKey
|
||||
andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirIdentityKey
|
||||
andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirSignedPreKey
|
||||
andKeyPair:params.ourBaseKey]];
|
||||
if (params.theirOneTimePrekey) {
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirOneTimePrekey andKeyPair:params.ourBaseKey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirOneTimePrekey
|
||||
andKeyPair:params.ourBaseKey]];
|
||||
}
|
||||
} else if ([parameters isKindOfClass:[BobAxolotlParameters class]]){
|
||||
BobAxolotlParameters *params = (BobAxolotlParameters*)parameters;
|
||||
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirIdentityKey andKeyPair:params.ourSignedPrekey]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirBaseKey andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirBaseKey andKeyPair:params.ourSignedPrekey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirIdentityKey
|
||||
andKeyPair:params.ourSignedPrekey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirBaseKey
|
||||
andKeyPair:params.ourIdentityKeyPair]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirBaseKey
|
||||
andKeyPair:params.ourSignedPrekey]];
|
||||
if (params.ourOneTimePrekey) {
|
||||
[masterKey appendData:[Curve25519 generateSharedSecretFromPublicKey:params.theirBaseKey andKeyPair:params.ourOneTimePrekey]];
|
||||
[masterKey appendData:[Curve25519 throws_generateSharedSecretFromPublicKey:params.theirBaseKey
|
||||
andKeyPair:params.ourOneTimePrekey]];
|
||||
}
|
||||
}
|
||||
|
||||
return [[DHEResult alloc] initWithMasterKey:masterKey];
|
||||
|
||||
return [[DHEResult alloc] init_throws_withMasterKey:masterKey];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
//
|
||||
// ReceivingChain.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 02/09/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Chain.h"
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
@interface ReceivingChain : NSObject <Chain, NSSecureCoding>
|
||||
|
||||
|
||||
@ -41,6 +41,9 @@ static NSString* const kCoderMessageKeys = @"kCoderMessageKeys";
|
||||
}
|
||||
|
||||
- (instancetype)initWithChainKey:(ChainKey *)chainKey senderRatchetKey:(NSData *)senderRatchet{
|
||||
OWSAssert(chainKey);
|
||||
OWSAssert(senderRatchet);
|
||||
|
||||
self = [super init];
|
||||
|
||||
self.chainKey = chainKey;
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
//
|
||||
// RootKey.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@class RKCK;
|
||||
|
||||
@class ECKeyPair;
|
||||
@class RKCK;
|
||||
|
||||
@interface RootKey : NSObject <NSSecureCoding>
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data;
|
||||
- (RKCK*)createChainWithTheirEphemeral:(NSData*)theirEphemeral ourEphemeral:(ECKeyPair*)ourEphemeral;
|
||||
- (RKCK *)throws_createChainWithTheirEphemeral:(NSData *)theirEphemeral
|
||||
ourEphemeral:(ECKeyPair *)ourEphemeral NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@property (nonatomic, readonly) NSData *keyData;
|
||||
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
//
|
||||
// RootKey.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 22/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RootKey.h"
|
||||
#import "TSDerivedSecrets.h"
|
||||
#import "RKCK.h"
|
||||
#import <25519/Curve25519.h>
|
||||
#import "ChainKey.h"
|
||||
#import "RKCK.h"
|
||||
#import "TSDerivedSecrets.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
static NSString* const kCoderData = @"kCoderData";
|
||||
|
||||
@ -36,9 +32,9 @@ static NSString* const kCoderData = @"kCoderData";
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data{
|
||||
self = [super init];
|
||||
|
||||
NSAssert([data length] == 32, @"Expected 32-byte RootKey Data");
|
||||
|
||||
|
||||
OWSAssert(data.length == 32);
|
||||
|
||||
if (self) {
|
||||
_keyData = data;
|
||||
}
|
||||
@ -46,11 +42,18 @@ static NSString* const kCoderData = @"kCoderData";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RKCK*)createChainWithTheirEphemeral:(NSData*)theirEphemeral ourEphemeral:(ECKeyPair*)ourEphemeral{
|
||||
NSData *sharedSecret = [Curve25519 generateSharedSecretFromPublicKey:theirEphemeral andKeyPair:ourEphemeral];
|
||||
|
||||
TSDerivedSecrets *secrets = [TSDerivedSecrets derivedRatchetedSecretsWithSharedSecret:sharedSecret rootKey:_keyData];
|
||||
|
||||
- (RKCK *)throws_createChainWithTheirEphemeral:(NSData *)theirEphemeral ourEphemeral:(ECKeyPair *)ourEphemeral
|
||||
{
|
||||
OWSAssert(theirEphemeral);
|
||||
OWSAssert(ourEphemeral);
|
||||
|
||||
NSData *sharedSecret = [Curve25519 throws_generateSharedSecretFromPublicKey:theirEphemeral andKeyPair:ourEphemeral];
|
||||
OWSAssert(sharedSecret.length == 32);
|
||||
|
||||
TSDerivedSecrets *secrets =
|
||||
[TSDerivedSecrets throws_derivedRatchetedSecretsWithSharedSecret:sharedSecret rootKey:_keyData];
|
||||
OWSAssert(secrets);
|
||||
|
||||
RKCK *newRKCK = [[RKCK alloc] initWithRK:[[RootKey alloc] initWithData:secrets.cipherKey]
|
||||
CK:[[ChainKey alloc] initWithData:secrets.macKey index:0]];
|
||||
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
//
|
||||
// SendingChain.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 02/09/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Chain.h"
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
@interface SendingChain : NSObject <Chain, NSSecureCoding>
|
||||
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
//
|
||||
// SendingChain.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 02/09/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SendingChain.h"
|
||||
@ -39,6 +35,9 @@ static NSString* const kCoderSenderRatchet = @"kCoderSenderRatchet";
|
||||
- (instancetype)initWithChainKey:(ChainKey *)chainKey senderRatchetKeyPair:(ECKeyPair *)keyPair{
|
||||
self = [super init];
|
||||
|
||||
OWSAssert(chainKey.key.length == 32);
|
||||
OWSAssert(keyPair);
|
||||
|
||||
if (self) {
|
||||
_chainKey = chainKey;
|
||||
_senderRatchetKeyPair = keyPair;
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
//
|
||||
// TSDerivedSecrets.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 29/03/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TSDerivedSecrets : NSData
|
||||
|
||||
+ (instancetype)derivedInitialSecretsWithMasterKey:(NSData*)masterKey;
|
||||
+ (instancetype)derivedRatchetedSecretsWithSharedSecret:(NSData*)masterKey rootKey:(NSData*)rootKey;
|
||||
+ (instancetype)derivedMessageKeysWithData:(NSData*)data;
|
||||
+ (instancetype)throws_derivedInitialSecretsWithMasterKey:(NSData *)masterKey
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (instancetype)throws_derivedRatchetedSecretsWithSharedSecret:(NSData *)masterKey
|
||||
rootKey:(NSData *)rootKey
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
+ (instancetype)throws_derivedMessageKeysWithData:(NSData *)data NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@property NSData *cipherKey;
|
||||
@property NSData *macKey;
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
//
|
||||
// TSDerivedSecrets.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 29/03/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TSDerivedSecrets.h"
|
||||
#import "HKDFKit.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
|
||||
@implementation TSDerivedSecrets
|
||||
|
||||
+ (instancetype)derivedSecretsWithSeed:(NSData*)masterKey salt:(NSData*)salt info:(NSData*)info{
|
||||
+ (instancetype)throws_derivedSecretsWithSeed:(NSData *)masterKey salt:(NSData *)salt info:(NSData *)info
|
||||
{
|
||||
OWSAssert(masterKey.length == 32);
|
||||
OWSAssert(info);
|
||||
|
||||
TSDerivedSecrets *secrets = [[TSDerivedSecrets alloc] init];
|
||||
|
||||
OWSAssert(secrets);
|
||||
|
||||
if (!salt) {
|
||||
const char *HKDFDefaultSalt[4] = {0};
|
||||
salt = [NSData dataWithBytes:HKDFDefaultSalt length:sizeof(HKDFDefaultSalt)];
|
||||
}
|
||||
|
||||
@try {
|
||||
NSData *derivedMaterial = [HKDFKit deriveKey:masterKey info:info salt:salt outputSize:96];
|
||||
NSData *derivedMaterial = [HKDFKit throws_deriveKey:masterKey info:info salt:salt outputSize:96];
|
||||
secrets.cipherKey = [derivedMaterial subdataWithRange:NSMakeRange(0, 32)];
|
||||
secrets.macKey = [derivedMaterial subdataWithRange:NSMakeRange(32, 32)];
|
||||
secrets.iv = [derivedMaterial subdataWithRange:NSMakeRange(64, 16)];
|
||||
@ -28,23 +30,37 @@
|
||||
@catch (NSException *exception) {
|
||||
@throw NSInvalidArgumentException;
|
||||
}
|
||||
|
||||
|
||||
OWSAssert(secrets.cipherKey.length == 32);
|
||||
OWSAssert(secrets.macKey.length == 32);
|
||||
OWSAssert(secrets.iv.length == 16);
|
||||
|
||||
return secrets;
|
||||
}
|
||||
|
||||
+ (instancetype)derivedInitialSecretsWithMasterKey:(NSData*)masterKey{
|
||||
+ (instancetype)throws_derivedInitialSecretsWithMasterKey:(NSData *)masterKey
|
||||
{
|
||||
OWSAssert(masterKey);
|
||||
|
||||
NSData *info = [@"WhisperText" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self derivedSecretsWithSeed:masterKey salt:nil info:info];
|
||||
return [self throws_derivedSecretsWithSeed:masterKey salt:nil info:info];
|
||||
}
|
||||
|
||||
+ (instancetype)derivedRatchetedSecretsWithSharedSecret:(NSData*)masterKey rootKey:(NSData*)rootKey{
|
||||
+ (instancetype)throws_derivedRatchetedSecretsWithSharedSecret:(NSData *)masterKey rootKey:(NSData *)rootKey
|
||||
{
|
||||
OWSAssert(masterKey);
|
||||
OWSAssert(rootKey);
|
||||
|
||||
NSData *info = [@"WhisperRatchet" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self derivedSecretsWithSeed:masterKey salt:rootKey info:info];
|
||||
return [self throws_derivedSecretsWithSeed:masterKey salt:rootKey info:info];
|
||||
}
|
||||
|
||||
+ (instancetype)derivedMessageKeysWithData:(NSData*)data{
|
||||
+ (instancetype)throws_derivedMessageKeysWithData:(NSData *)data
|
||||
{
|
||||
OWSAssert(data);
|
||||
|
||||
NSData *info = [@"WhisperMessageKeys" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [self derivedSecretsWithSeed:data salt:nil info:info];
|
||||
return [self throws_derivedSecretsWithSeed:data salt:nil info:info];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
9
AxolotlKit/Classes/SPK-Bridging-Header.h
Normal file
9
AxolotlKit/Classes/SPK-Bridging-Header.h
Normal file
@ -0,0 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AxolotlStore.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <SignalCoreKit/NSObject+OWS.h>
|
||||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
@ -1,21 +1,19 @@
|
||||
//
|
||||
// TSAxolotlRatchet.h
|
||||
// AxolotlKit
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Created by Frederic Jacobs on 07/22/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "IdentityKeyStore.h"
|
||||
#import "SessionStore.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "PreKeyWhisperMessage.h"
|
||||
#import "SPKProtocolContext.h"
|
||||
#import "SessionState.h"
|
||||
#import "SessionStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import "WhisperMessage.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SessionCipher : NSObject
|
||||
|
||||
@ -23,11 +21,27 @@
|
||||
|
||||
- (instancetype)initWithSessionStore:(id<SessionStore>)sessionStore preKeyStore:(id<PreKeyStore>)preKeyStore signedPreKeyStore:(id<SignedPreKeyStore>)signedPreKeyStore identityKeyStore:(id<IdentityKeyStore>)identityKeyStore recipientId:(NSString*)recipientId deviceId:(int)deviceId;
|
||||
|
||||
- (id<CipherMessage>)encryptMessage:(NSData*)paddedMessage;
|
||||
// protocolContext is an optional parameter that can be used to ensure that all
|
||||
// identity and session store writes are coordinated and/or occur within a single
|
||||
// transaction.
|
||||
- (id<CipherMessage>)throws_encryptMessage:(NSData *)paddedMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable id<CipherMessage>)encryptMessage:(NSData *)paddedMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError;
|
||||
|
||||
- (NSData*)decrypt:(id<CipherMessage>)whisperMessage;
|
||||
- (NSData *)throws_decrypt:(id<CipherMessage>)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable NSData *)decrypt:(id<CipherMessage>)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError;
|
||||
|
||||
- (int)remoteRegistrationId;
|
||||
- (int)sessionVersion;
|
||||
- (int)throws_remoteRegistrationId:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
- (int)throws_sessionVersion:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,50 +1,48 @@
|
||||
//
|
||||
// TSAxolotlRatchet.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 1/12/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SessionCipher.h"
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <25519/Ed25519.h>
|
||||
|
||||
#import "NSData+keyVersionByte.h"
|
||||
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "SessionBuilder.h"
|
||||
#import "SessionStore.h"
|
||||
#import "AES-CBC.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "AxolotlParameters.h"
|
||||
#import "MessageKeys.h"
|
||||
#import "SessionState.h"
|
||||
#import "ChainKey.h"
|
||||
#import "RootKey.h"
|
||||
#import "WhisperMessage.h"
|
||||
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import "MessageKeys.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
#import "PreKeyStore.h"
|
||||
|
||||
#import "RootKey.h"
|
||||
#import "SessionBuilder.h"
|
||||
#import "SessionState.h"
|
||||
#import "SessionStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import "WhisperMessage.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SessionCipher ()
|
||||
|
||||
@property NSString* recipientId;
|
||||
@property int deviceId;
|
||||
@property (nonatomic, readonly) NSString *recipientId;
|
||||
@property (nonatomic, readonly) int deviceId;
|
||||
|
||||
@property (nonatomic, retain) id<SessionStore> sessionStore;
|
||||
@property (nonatomic, retain) SessionBuilder *sessionBuilder;
|
||||
@property (nonatomic, retain) id<PreKeyStore> prekeyStore;
|
||||
@property (nonatomic, readonly) id<IdentityKeyStore> identityKeyStore;
|
||||
@property (nonatomic, readonly) id<SessionStore> sessionStore;
|
||||
@property (nonatomic, readonly) SessionBuilder *sessionBuilder;
|
||||
@property (nonatomic, readonly) id<PreKeyStore> prekeyStore;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation SessionCipher
|
||||
|
||||
|
||||
- (instancetype)initWithAxolotlStore:(id<AxolotlStore>)sessionStore recipientId:(NSString*)recipientId deviceId:(int)deviceId{
|
||||
OWSAssert(sessionStore);
|
||||
OWSAssert(recipientId);
|
||||
return [self initWithSessionStore:sessionStore
|
||||
preKeyStore:sessionStore
|
||||
signedPreKeyStore:sessionStore
|
||||
@ -59,166 +57,374 @@
|
||||
identityKeyStore:(id<IdentityKeyStore>)identityKeyStore
|
||||
recipientId:(NSString*)recipientId
|
||||
deviceId:(int)deviceId{
|
||||
OWSAssert(sessionStore);
|
||||
OWSAssert(preKeyStore);
|
||||
OWSAssert(signedPreKeyStore);
|
||||
OWSAssert(identityKeyStore);
|
||||
OWSAssert(recipientId);
|
||||
|
||||
self = [super init];
|
||||
|
||||
if (self){
|
||||
self.recipientId = recipientId;
|
||||
self.deviceId = deviceId;
|
||||
self.sessionStore = sessionStore;
|
||||
self.sessionBuilder = [[SessionBuilder alloc] initWithSessionStore:sessionStore
|
||||
preKeyStore:preKeyStore
|
||||
signedPreKeyStore:signedPreKeyStore
|
||||
identityKeyStore:identityKeyStore
|
||||
recipientId:recipientId
|
||||
deviceId:deviceId];
|
||||
|
||||
_recipientId = recipientId;
|
||||
_deviceId = deviceId;
|
||||
_sessionStore = sessionStore;
|
||||
_identityKeyStore = identityKeyStore;
|
||||
_prekeyStore = preKeyStore;
|
||||
_sessionBuilder = [[SessionBuilder alloc] initWithSessionStore:sessionStore
|
||||
preKeyStore:preKeyStore
|
||||
signedPreKeyStore:signedPreKeyStore
|
||||
identityKeyStore:identityKeyStore
|
||||
recipientId:recipientId
|
||||
deviceId:deviceId];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<CipherMessage>)encryptMessage:(NSData*)paddedMessage{
|
||||
|
||||
SessionRecord *sessionRecord = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId];
|
||||
SessionState *session = sessionRecord.sessionState;
|
||||
ChainKey *chainKey = session.senderChainKey;
|
||||
MessageKeys *messageKeys = chainKey.messageKeys;
|
||||
NSData *senderRatchetKey = session.senderRatchetKey;
|
||||
int previousCounter = session.previousCounter;
|
||||
int sessionVersion = session.version;
|
||||
|
||||
- (nullable id<CipherMessage>)encryptMessage:(NSData *)paddedMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError
|
||||
{
|
||||
__block id<CipherMessage> result;
|
||||
[SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
result = [self throws_encryptMessage:paddedMessage protocolContext:protocolContext];
|
||||
}
|
||||
error:outError];
|
||||
|
||||
NSData *ciphertextBody = [AES_CBC encryptCBCMode:paddedMessage withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
return result;
|
||||
}
|
||||
|
||||
id<CipherMessage> cipherMessage = [[WhisperMessage alloc] initWithVersion:sessionVersion
|
||||
macKey:messageKeys.macKey
|
||||
senderRatchetKey:senderRatchetKey.prependKeyType
|
||||
counter:chainKey.index
|
||||
previousCounter:previousCounter
|
||||
cipherText:ciphertextBody
|
||||
senderIdentityKey:session.localIdentityKey.prependKeyType
|
||||
receiverIdentityKey:session.remoteIdentityKey.prependKeyType];
|
||||
|
||||
if ([session hasUnacknowledgedPreKeyMessage]){
|
||||
PendingPreKey *items = [session unacknowledgedPreKeyMessageItems];
|
||||
int localRegistrationId = [session localRegistrationId];
|
||||
|
||||
cipherMessage = [[PreKeyWhisperMessage alloc] initWithWhisperMessage:cipherMessage
|
||||
registrationId:localRegistrationId
|
||||
prekeyId:items.preKeyId
|
||||
signedPrekeyId:items.signedPreKeyId
|
||||
baseKey:items.baseKey.prependKeyType
|
||||
identityKey:session.localIdentityKey.prependKeyType];
|
||||
- (id<CipherMessage>)throws_encryptMessage:(NSData *)paddedMessage protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(paddedMessage);
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
SessionState *sessionState = sessionRecord.sessionState;
|
||||
ChainKey *chainKey = sessionState.senderChainKey;
|
||||
MessageKeys *messageKeys = [chainKey throws_messageKeys];
|
||||
NSData *senderRatchetKey = sessionState.senderRatchetKey;
|
||||
int previousCounter = sessionState.previousCounter;
|
||||
int sessionVersion = sessionState.version;
|
||||
|
||||
if (![self.identityKeyStore isTrustedIdentityKey:sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionOutgoing
|
||||
protocolContext:protocolContext]) {
|
||||
OWSLogWarn(
|
||||
@"%@ Previously known identity key for while encrypting for recipient: %@", self.tag, self.recipientId);
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException
|
||||
reason:@"There is a previously known identity key."
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
[session setSenderChainKey:[chainKey nextChainKey]];
|
||||
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:sessionRecord];
|
||||
|
||||
|
||||
[self.identityKeyStore saveRemoteIdentity:sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
|
||||
NSData *ciphertextBody =
|
||||
[AES_CBC throws_encryptCBCMode:paddedMessage withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
|
||||
id<CipherMessage> cipherMessage =
|
||||
[[WhisperMessage alloc] init_throws_withVersion:sessionVersion
|
||||
macKey:messageKeys.macKey
|
||||
senderRatchetKey:senderRatchetKey.prependKeyType
|
||||
counter:chainKey.index
|
||||
previousCounter:previousCounter
|
||||
cipherText:ciphertextBody
|
||||
senderIdentityKey:sessionState.localIdentityKey.prependKeyType
|
||||
receiverIdentityKey:sessionState.remoteIdentityKey.prependKeyType];
|
||||
|
||||
if ([sessionState hasUnacknowledgedPreKeyMessage]) {
|
||||
PendingPreKey *items = [sessionState unacknowledgedPreKeyMessageItems];
|
||||
int localRegistrationId = [sessionState localRegistrationId];
|
||||
|
||||
OWSLogInfo(@"Building PreKeyWhisperMessage for: %@.%lu with preKeyId: %d",
|
||||
self.recipientId, (unsigned long) self.deviceId, items.preKeyId);
|
||||
|
||||
cipherMessage =
|
||||
[[PreKeyWhisperMessage alloc] init_throws_withWhisperMessage:cipherMessage
|
||||
registrationId:localRegistrationId
|
||||
prekeyId:items.preKeyId
|
||||
signedPrekeyId:items.signedPreKeyId
|
||||
baseKey:items.baseKey.prependKeyType
|
||||
identityKey:sessionState.localIdentityKey.prependKeyType];
|
||||
}
|
||||
|
||||
[sessionState setSenderChainKey:[chainKey nextChainKey]];
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
session:sessionRecord
|
||||
protocolContext:protocolContext];
|
||||
|
||||
return cipherMessage;
|
||||
}
|
||||
|
||||
- (NSData*)decrypt:(id<CipherMessage>)whisperMessage{
|
||||
if ([whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) {
|
||||
return [self decryptPreKeyWhisperMessage:(PreKeyWhisperMessage*)whisperMessage];
|
||||
} else{
|
||||
return [self decryptWhisperMessage:whisperMessage];
|
||||
- (nullable NSData *)decrypt:(id<CipherMessage>)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError
|
||||
{
|
||||
__block NSData *_Nullable result;
|
||||
[SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
result = [self throws_decrypt:whisperMessage protocolContext:protocolContext];
|
||||
}
|
||||
error:outError];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSData *)throws_decrypt:(id<CipherMessage>)whisperMessage protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(whisperMessage);
|
||||
|
||||
switch (whisperMessage.cipherMessageType) {
|
||||
case CipherMessageType_Whisper:
|
||||
if (![whisperMessage isKindOfClass:[WhisperMessage class]]) {
|
||||
OWSFail(@"Unexpected message type: %@", [whisperMessage class]);
|
||||
return nil;
|
||||
}
|
||||
return [self throws_decryptWhisperMessage:(WhisperMessage *)whisperMessage protocolContext:protocolContext];
|
||||
case CipherMessageType_Prekey:
|
||||
if (![whisperMessage isKindOfClass:[PreKeyWhisperMessage class]]) {
|
||||
OWSFail(@"Unexpected message type: %@", [whisperMessage class]);
|
||||
return nil;
|
||||
}
|
||||
return [self throws_decryptPreKeyWhisperMessage:(PreKeyWhisperMessage *)whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
default:
|
||||
OWSFailDebug(@"Unexpected message type: %@", [whisperMessage class]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData*)decryptPreKeyWhisperMessage:(PreKeyWhisperMessage*)preKeyWhisperMessage{
|
||||
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];
|
||||
- (NSData *)throws_decryptPreKeyWhisperMessage:(PreKeyWhisperMessage *)preKeyWhisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(preKeyWhisperMessage);
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:sessionRecord];
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
int unsignedPreKeyId = [self.sessionBuilder throws_processPrekeyWhisperMessage:preKeyWhisperMessage
|
||||
withSession:sessionRecord
|
||||
protocolContext:protocolContext];
|
||||
NSData *plaintext = [self throws_decryptWithSessionRecord:sessionRecord
|
||||
whisperMessage:preKeyWhisperMessage.message
|
||||
protocolContext:protocolContext];
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
session:sessionRecord
|
||||
protocolContext:protocolContext];
|
||||
|
||||
// If there was an unsigned PreKey
|
||||
if (unsignedPreKeyId >= 0) {
|
||||
[self.prekeyStore removePreKey:unsignedPreKeyId];
|
||||
[self.prekeyStore removePreKey:unsignedPreKeyId
|
||||
protocolContext:protocolContext];
|
||||
}
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
- (NSData*)decryptWhisperMessage:(WhisperMessage*)message{
|
||||
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];
|
||||
- (NSData *)throws_decryptWhisperMessage:(WhisperMessage *)whisperMessage protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(whisperMessage);
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
NSData *plaintext = [self throws_decryptWithSessionRecord:sessionRecord
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
|
||||
// Our current session state may not have a remote identity key
|
||||
// if we decrypted this message using an old session. It's safe
|
||||
// to ignore this, as the identity key message was already surfaced
|
||||
// when it originally changed.
|
||||
if (sessionRecord.sessionState.remoteIdentityKey) {
|
||||
if (![self.identityKeyStore isTrustedIdentityKey:sessionRecord.sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionIncoming
|
||||
protocolContext:protocolContext]) {
|
||||
OWSLogWarn(
|
||||
@"%@ Previously known identity key for while decrypting from recipient: %@", self.tag, self.recipientId);
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException
|
||||
reason:@"There is a previously known identity key."
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
[self.identityKeyStore saveRemoteIdentity:sessionRecord.sessionState.remoteIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
}
|
||||
|
||||
SessionRecord *sessionRecord = [self.sessionStore loadSession:self.recipientId deviceId:self.deviceId];
|
||||
NSData *plaintext = [self decryptWithSessionRecord:sessionRecord whisperMessage:message];
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:sessionRecord];
|
||||
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
session:sessionRecord
|
||||
protocolContext:protocolContext];
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
- (void)logDecryptionFailureForWhisperMessage:(WhisperMessage *)whisperMessage sessionState:(SessionState *)sessionState
|
||||
{
|
||||
OWSFailDebug(@"Failed to decrypt whisper message with ratchet key: %@ and counter: %d. Session loaded using recipientId: %@ and deviceId: %d. Local session has base key: %@ and counter: %d", whisperMessage.senderRatchetKey.hexadecimalString, whisperMessage.counter, self.recipientId, self.deviceId, sessionState.senderRatchetKey.hexadecimalString, sessionState.previousCounter);
|
||||
}
|
||||
|
||||
- (NSData *)throws_decryptWithSessionRecord:(SessionRecord *)sessionRecord
|
||||
whisperMessage:(WhisperMessage *)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(sessionRecord);
|
||||
OWSAssert(whisperMessage);
|
||||
|
||||
-(NSData*)decryptWithSessionRecord:(SessionRecord*)sessionRecord whisperMessage:(WhisperMessage*)message{
|
||||
SessionState *sessionState = [sessionRecord sessionState];
|
||||
NSArray *previousStates = [sessionRecord previousSessionStates];
|
||||
NSMutableArray *exceptions = [NSMutableArray array];
|
||||
|
||||
@try {
|
||||
return [self decryptWithSessionState:sessionState whisperMessage:message];
|
||||
NSData *decryptedData = [self throws_decryptWithSessionState:sessionState
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
OWSLogDebug(@"%@ successfully decrypted with current session state: %@", self.tag, sessionState);
|
||||
return decryptedData;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
if ([exception.name isEqualToString:InvalidMessageException]) {
|
||||
[exceptions addObject:exception];
|
||||
} else {
|
||||
[self logDecryptionFailureForWhisperMessage:whisperMessage sessionState:sessionState];
|
||||
@throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
for (SessionState *previousState in previousStates) {
|
||||
@try {
|
||||
return [self decryptWithSessionState:previousState whisperMessage:message];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[exceptions addObject:exception];
|
||||
}
|
||||
|
||||
// If we can decrypt the message with an "old" session state, that means the sender is using an "old" session.
|
||||
// We can continue to receive messages from this sender, but we will send to them using our current session.
|
||||
__block NSData *_Nullable decryptedData;
|
||||
[[sessionRecord previousSessionStates]
|
||||
enumerateObjectsUsingBlock:^(SessionState *_Nonnull previousState, NSUInteger idx, BOOL *_Nonnull stop) {
|
||||
@try {
|
||||
decryptedData = [self throws_decryptWithSessionState:previousState
|
||||
whisperMessage:whisperMessage
|
||||
protocolContext:protocolContext];
|
||||
OWSLogInfo(@"%@ successfully decrypted with PREVIOUS session state: %@", self.tag, previousState);
|
||||
OWSAssert(decryptedData != nil);
|
||||
*stop = YES;
|
||||
} @catch (NSException *exception) {
|
||||
if (![exception.name isEqualToString:InvalidMessageException]) {
|
||||
[self logDecryptionFailureForWhisperMessage:whisperMessage sessionState:sessionState];
|
||||
}
|
||||
|
||||
[exceptions addObject:exception];
|
||||
}
|
||||
}];
|
||||
|
||||
if (decryptedData) {
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
BOOL containsActiveSession =
|
||||
[self.sessionStore containsSession:self.recipientId deviceId:self.deviceId protocolContext:protocolContext];
|
||||
OWSLogError(@"%@ No valid session for recipient: %@.%lu containsActiveSession: %@, previousStates: %lu",
|
||||
self.tag,
|
||||
self.recipientId,
|
||||
(unsigned long) self.deviceId,
|
||||
(containsActiveSession ? @"YES" : @"NO"),
|
||||
(unsigned long)sessionRecord.previousSessionStates.count);
|
||||
|
||||
if (containsActiveSession) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException
|
||||
reason:@"No valid sessions"
|
||||
userInfo:@{
|
||||
@"Exceptions" : exceptions
|
||||
}];
|
||||
} else {
|
||||
@throw [NSException
|
||||
exceptionWithName:NoSessionException
|
||||
reason:[NSString stringWithFormat:@"No session for: %@, %d", self.recipientId, self.deviceId]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"No valid sessions" userInfo:@{@"Exceptions":exceptions}];
|
||||
}
|
||||
|
||||
-(NSData*)decryptWithSessionState:(SessionState*)sessionState whisperMessage:(WhisperMessage*)message{
|
||||
- (NSData *)throws_decryptWithSessionState:(SessionState *)sessionState
|
||||
whisperMessage:(WhisperMessage *)whisperMessage
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(sessionState);
|
||||
OWSAssert(whisperMessage);
|
||||
|
||||
if (![sessionState hasSenderChain]) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:@"Uninitialized session!" userInfo:nil];
|
||||
}
|
||||
|
||||
if (message.version != sessionState.version) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException reason:[NSString stringWithFormat:@"Got message version %d but was expecting %d", message.version, sessionState.version] userInfo:nil];
|
||||
|
||||
if (whisperMessage.version != sessionState.version) {
|
||||
@throw [NSException exceptionWithName:InvalidMessageException
|
||||
reason:[NSString stringWithFormat:@"Got message version %d but was expecting %d",
|
||||
whisperMessage.version,
|
||||
sessionState.version]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
int messageVersion = message.version;
|
||||
NSData *theirEphemeral = message.senderRatchetKey.removeKeyType;
|
||||
int counter = message.counter;
|
||||
ChainKey *chainKey = [self getOrCreateChainKeys:sessionState theirEphemeral:theirEphemeral];
|
||||
MessageKeys *messageKeys = [self getOrCreateMessageKeysForSession:sessionState theirEphemeral:theirEphemeral chainKey:chainKey counter:counter];
|
||||
|
||||
[message verifyMacWithVersion:messageVersion senderIdentityKey:sessionState.remoteIdentityKey receiverIdentityKey:sessionState.localIdentityKey macKey:messageKeys.macKey];
|
||||
|
||||
NSData *plaintext = [AES_CBC decryptCBCMode:message.cipherText withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
|
||||
int messageVersion = whisperMessage.version;
|
||||
NSData *theirEphemeral = whisperMessage.senderRatchetKey.throws_removeKeyType;
|
||||
int counter = whisperMessage.counter;
|
||||
ChainKey *chainKey = [self throws_getOrCreateChainKeys:sessionState theirEphemeral:theirEphemeral];
|
||||
OWSAssert(chainKey);
|
||||
MessageKeys *messageKeys = [self throws_getOrCreateMessageKeysForSession:sessionState
|
||||
theirEphemeral:theirEphemeral
|
||||
chainKey:chainKey
|
||||
counter:counter];
|
||||
OWSAssert(messageKeys);
|
||||
|
||||
[whisperMessage throws_verifyMacWithVersion:messageVersion
|
||||
senderIdentityKey:sessionState.remoteIdentityKey
|
||||
receiverIdentityKey:sessionState.localIdentityKey
|
||||
macKey:messageKeys.macKey];
|
||||
|
||||
NSData *plaintext =
|
||||
[AES_CBC throws_decryptCBCMode:whisperMessage.cipherText withKey:messageKeys.cipherKey withIV:messageKeys.iv];
|
||||
|
||||
[sessionState clearUnacknowledgedPreKeyMessage];
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
- (ChainKey*)getOrCreateChainKeys:(SessionState*)sessionState theirEphemeral:(NSData*)theirEphemeral{
|
||||
- (ChainKey *)throws_getOrCreateChainKeys:(SessionState *)sessionState theirEphemeral:(NSData *)theirEphemeral
|
||||
{
|
||||
OWSAssert(sessionState);
|
||||
OWSGuardWithException(theirEphemeral, InvalidMessageException);
|
||||
OWSGuardWithException(theirEphemeral.length == 32, InvalidMessageException);
|
||||
|
||||
@try {
|
||||
if ([sessionState hasReceiverChain:theirEphemeral]) {
|
||||
OWSLogInfo(@"%@ %@.%d has existing receiver chain.", self.tag, self.recipientId, self.deviceId);
|
||||
return [sessionState receiverChainKey:theirEphemeral];
|
||||
} else{
|
||||
OWSLogInfo(@"%@ %@.%d creating new chains.", self.tag, self.recipientId, self.deviceId);
|
||||
RootKey *rootKey = [sessionState rootKey];
|
||||
OWSAssert(rootKey.keyData.length == 32);
|
||||
|
||||
ECKeyPair *ourEphemeral = [sessionState senderRatchetKeyPair];
|
||||
RKCK *receiverChain = [rootKey createChainWithTheirEphemeral:theirEphemeral ourEphemeral:ourEphemeral];
|
||||
OWSAssert(ourEphemeral.publicKey.length == 32);
|
||||
|
||||
RKCK *receiverChain =
|
||||
[rootKey throws_createChainWithTheirEphemeral:theirEphemeral ourEphemeral:ourEphemeral];
|
||||
|
||||
ECKeyPair *ourNewEphemeral = [Curve25519 generateKeyPair];
|
||||
RKCK *senderChain = [receiverChain.rootKey createChainWithTheirEphemeral:theirEphemeral ourEphemeral:ourNewEphemeral];
|
||||
|
||||
OWSAssert(ourNewEphemeral.publicKey.length == 32);
|
||||
|
||||
RKCK *senderChain = [receiverChain.rootKey throws_createChainWithTheirEphemeral:theirEphemeral
|
||||
ourEphemeral:ourNewEphemeral];
|
||||
|
||||
OWSAssert(senderChain.rootKey.keyData.length == 32);
|
||||
[sessionState setRootKey:senderChain.rootKey];
|
||||
|
||||
OWSAssert(receiverChain.chainKey.key.length == 32);
|
||||
[sessionState addReceiverChain:theirEphemeral chainKey:receiverChain.chainKey];
|
||||
[sessionState setPreviousCounter:MAX(sessionState.senderChainKey.index-1 , 0)];
|
||||
|
||||
int previousCounter;
|
||||
ows_sub_overflow(sessionState.senderChainKey.index, 1, &previousCounter);
|
||||
[sessionState setPreviousCounter:MAX(previousCounter, 0)];
|
||||
[sessionState setSenderChain:ourNewEphemeral chainKey:senderChain.chainKey];
|
||||
|
||||
return receiverChain.chainKey;
|
||||
@ -229,29 +435,55 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (MessageKeys*)getOrCreateMessageKeysForSession:(SessionState*)sessionState theirEphemeral:(NSData*)theirEphemeral chainKey:(ChainKey*)chainKey counter:(int)counter{
|
||||
|
||||
- (MessageKeys *)throws_getOrCreateMessageKeysForSession:(SessionState *)sessionState
|
||||
theirEphemeral:(NSData *)theirEphemeral
|
||||
chainKey:(ChainKey *)chainKey
|
||||
counter:(int)counter
|
||||
{
|
||||
OWSAssert(sessionState);
|
||||
OWSGuardWithException(theirEphemeral, InvalidMessageException);
|
||||
OWSGuardWithException(theirEphemeral.length == 32, InvalidMessageException);
|
||||
OWSAssert(chainKey);
|
||||
|
||||
if (chainKey.index > counter) {
|
||||
if ([sessionState hasMessageKeys:theirEphemeral counter:counter]) {
|
||||
return [sessionState removeMessageKeys:theirEphemeral counter:counter];
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
OWSLogInfo(
|
||||
@"%@ %@.%d Duplicate message for counter: %d", self.tag, self.recipientId, self.deviceId, counter);
|
||||
OWSLogFlush();
|
||||
@throw [NSException exceptionWithName:DuplicateMessageException reason:@"Received message with old counter!" userInfo:@{}];
|
||||
}
|
||||
}
|
||||
|
||||
if (chainKey.index - counter > 2000) {
|
||||
@throw [NSException exceptionWithName:@"Over 500 messages into the future!" reason:@"" userInfo:@{}];
|
||||
|
||||
NSUInteger kCounterLimit = 25000;
|
||||
int counterOffset;
|
||||
if (__builtin_sub_overflow(counter, chainKey.index, &counterOffset)) {
|
||||
OWSFailDebug(@"Overflow while calculating counter offset");
|
||||
OWSRaiseException(InvalidMessageException, @"Overflow while calculating counter offset");
|
||||
}
|
||||
|
||||
if (counterOffset > kCounterLimit) {
|
||||
OWSLogError(@"%@ %@.%d Exceeded future message limit: %lu, index: %d, counter: %d)",
|
||||
self.tag,
|
||||
self.recipientId,
|
||||
self.deviceId,
|
||||
(unsigned long)kCounterLimit,
|
||||
chainKey.index,
|
||||
counter);
|
||||
OWSLogFlush();
|
||||
@throw [NSException exceptionWithName:InvalidMessageException
|
||||
reason:@"Exceeded message keys chain length limit"
|
||||
userInfo:@{}];
|
||||
}
|
||||
|
||||
while (chainKey.index < counter) {
|
||||
MessageKeys *messageKeys = [chainKey messageKeys];
|
||||
MessageKeys *messageKeys = [chainKey throws_messageKeys];
|
||||
[sessionState setMessageKeys:theirEphemeral messageKeys:messageKeys];
|
||||
chainKey = chainKey.nextChainKey;
|
||||
}
|
||||
|
||||
[sessionState setReceiverChainKey:theirEphemeral chainKey:[chainKey nextChainKey]];
|
||||
return [chainKey messageKeys];
|
||||
return [chainKey throws_messageKeys];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,10 +498,11 @@
|
||||
return versionByte;
|
||||
}
|
||||
|
||||
- (int)throws_remoteRegistrationId:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
SessionRecord *_Nullable record =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:_deviceId protocolContext:protocolContext];
|
||||
|
||||
- (int)remoteRegistrationId{
|
||||
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:_deviceId];
|
||||
|
||||
if (!record) {
|
||||
@throw [NSException exceptionWithName:NoSessionException reason:@"Trying to get registration Id of a non-existing session." userInfo:nil];
|
||||
}
|
||||
@ -277,9 +510,11 @@
|
||||
return record.sessionState.remoteRegistrationId;
|
||||
}
|
||||
|
||||
- (int)sessionVersion{
|
||||
SessionRecord *record = [self.sessionStore loadSession:self.recipientId deviceId:_deviceId];
|
||||
|
||||
- (int)throws_sessionVersion:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
SessionRecord *_Nullable record =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:_deviceId protocolContext:protocolContext];
|
||||
|
||||
if (!record) {
|
||||
@throw [NSException exceptionWithName:NoSessionException reason:@"Trying to get the version of a non-existing session." userInfo:nil];
|
||||
}
|
||||
@ -287,4 +522,18 @@
|
||||
return record.sessionState.version;
|
||||
}
|
||||
|
||||
@end
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,32 +1,45 @@
|
||||
//
|
||||
// SessionBuilder.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AxolotlStore.h"
|
||||
#import "IdentityKeyStore.h"
|
||||
#import "PreKeyBundle.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "SPKProtocolContext.h"
|
||||
#import "SessionStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "PreKeyBundle.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class PreKeyWhisperMessage;
|
||||
|
||||
extern const int kPreKeyOfLastResortId;
|
||||
|
||||
@interface SessionBuilder : NSObject
|
||||
|
||||
- (instancetype)initWithAxolotlStore:(id<AxolotlStore>)sessionStore recipientId:(NSString*)recipientId deviceId:(int)deviceId;
|
||||
- (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
|
||||
recipientId:(NSString *)recipientId
|
||||
deviceId:(int)deviceId;
|
||||
|
||||
- (void)processPrekeyBundle:(PreKeyBundle*)preKeyBundle;
|
||||
- (int)processPrekeyWhisperMessage:(PreKeyWhisperMessage*)message withSession:(SessionRecord*)sessionRecord;
|
||||
- (void)throws_processPrekeyBundle:(PreKeyBundle *)preKeyBundle
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (BOOL)processPrekeyBundle:(PreKeyBundle *)preKeyBundle
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError;
|
||||
|
||||
- (int)throws_processPrekeyWhisperMessage:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,32 +1,30 @@
|
||||
//
|
||||
// SessionBuilder.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SessionBuilder.h"
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "AxolotlParameters.h"
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
|
||||
#import "NSData+keyVersionByte.h"
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
#import "SessionState.h"
|
||||
#import "SessionBuilder.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
#import "PreKeyWhisperMessage.h"
|
||||
#import "RatchetingSession.h"
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <25519/Ed25519.h>
|
||||
|
||||
#import "PrekeyBundle.h"
|
||||
#import "RatchetingSession.h"
|
||||
#import "SessionState.h"
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define CURRENT_VERSION 3
|
||||
#define MINUMUM_VERSION 3
|
||||
|
||||
const int kPreKeyOfLastResortId = 0xFFFFFF;
|
||||
|
||||
@interface SessionBuilder ()
|
||||
|
||||
@property (nonatomic, readonly)NSString* recipientId;
|
||||
@ -43,6 +41,9 @@
|
||||
@implementation SessionBuilder
|
||||
|
||||
- (instancetype)initWithAxolotlStore:(id<AxolotlStore>)sessionStore recipientId:(NSString*)recipientId deviceId:(int)deviceId{
|
||||
OWSAssert(sessionStore);
|
||||
OWSAssert(recipientId);
|
||||
|
||||
return [self initWithSessionStore:sessionStore
|
||||
preKeyStore:sessionStore
|
||||
signedPreKeyStore:sessionStore
|
||||
@ -57,65 +58,115 @@
|
||||
identityKeyStore:(id<IdentityKeyStore>)identityKeyStore
|
||||
recipientId:(NSString*)recipientId
|
||||
deviceId:(int)deviceId{
|
||||
|
||||
OWSAssert(sessionStore);
|
||||
OWSAssert(preKeyStore);
|
||||
OWSAssert(signedPreKeyStore);
|
||||
OWSAssert(identityKeyStore);
|
||||
OWSAssert(recipientId);
|
||||
|
||||
self = [super init];
|
||||
|
||||
_sessionStore = sessionStore;
|
||||
_prekeyStore = preKeyStore;
|
||||
_signedPreKeyStore = signedPreKeyStore;
|
||||
_identityStore = identityKeyStore;
|
||||
_recipientId = recipientId;
|
||||
_deviceId = deviceId;
|
||||
if (self) {
|
||||
_sessionStore = sessionStore;
|
||||
_prekeyStore = preKeyStore;
|
||||
_signedPreKeyStore = signedPreKeyStore;
|
||||
_identityStore = identityKeyStore;
|
||||
_recipientId = recipientId;
|
||||
_deviceId = deviceId;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)processPrekeyBundle:(PreKeyBundle*)preKeyBundle{
|
||||
NSData *theirIdentityKey = preKeyBundle.identityKey.removeKeyType;
|
||||
NSData *theirSignedPreKey = preKeyBundle.signedPreKeyPublic.removeKeyType;
|
||||
|
||||
if (![self.identityStore isTrustedIdentityKey:theirIdentityKey recipientId:self.recipientId]) {
|
||||
- (BOOL)processPrekeyBundle:(PreKeyBundle *)preKeyBundle
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
error:(NSError **)outError
|
||||
{
|
||||
return [SCKExceptionWrapper
|
||||
tryBlock:^{
|
||||
[self throws_processPrekeyBundle:preKeyBundle protocolContext:protocolContext];
|
||||
}
|
||||
error:outError];
|
||||
}
|
||||
|
||||
- (void)throws_processPrekeyBundle:(PreKeyBundle *)preKeyBundle protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(preKeyBundle);
|
||||
|
||||
NSData *theirIdentityKey = preKeyBundle.identityKey.throws_removeKeyType;
|
||||
NSData *theirSignedPreKey = preKeyBundle.signedPreKeyPublic.throws_removeKeyType;
|
||||
|
||||
if (![self.identityStore isTrustedIdentityKey:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionOutgoing
|
||||
protocolContext:protocolContext]) {
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException reason:@"Identity key is not valid" userInfo:@{}];
|
||||
}
|
||||
|
||||
if (![Ed25519 verifySignature:preKeyBundle.signedPreKeySignature publicKey:theirIdentityKey data:preKeyBundle.signedPreKeyPublic]) {
|
||||
// NOTE: we use preKeyBundle.signedPreKeyPublic which has the key type byte.
|
||||
if (![Ed25519 throws_verifySignature:preKeyBundle.signedPreKeySignature
|
||||
publicKey:theirIdentityKey
|
||||
data:preKeyBundle.signedPreKeyPublic]) {
|
||||
@throw [NSException exceptionWithName:InvalidKeyException reason:@"KeyIsNotValidlySigned" userInfo:nil];
|
||||
}
|
||||
|
||||
SessionRecord *sessionRecord = [self.sessionStore loadSession:self.recipientId deviceId:preKeyBundle.deviceId];
|
||||
|
||||
SessionRecord *sessionRecord =
|
||||
[self.sessionStore loadSession:self.recipientId deviceId:preKeyBundle.deviceId protocolContext:protocolContext];
|
||||
ECKeyPair *ourBaseKey = [Curve25519 generateKeyPair];
|
||||
NSData *theirOneTimePreKey = preKeyBundle.preKeyPublic.removeKeyType;
|
||||
NSData *theirOneTimePreKey = preKeyBundle.preKeyPublic.throws_removeKeyType;
|
||||
int theirOneTimePreKeyId = preKeyBundle.preKeyId;
|
||||
int theirSignedPreKeyId = preKeyBundle.signedPreKeyId;
|
||||
|
||||
|
||||
AliceAxolotlParameters *params = [[AliceAxolotlParameters alloc] initWithIdentityKey:[self.identityStore identityKeyPair]
|
||||
theirIdentityKey:theirIdentityKey
|
||||
ourBaseKey:ourBaseKey
|
||||
theirSignedPreKey:theirSignedPreKey
|
||||
theirOneTimePreKey:theirOneTimePreKey
|
||||
theirRatchetKey:theirSignedPreKey];
|
||||
|
||||
|
||||
|
||||
AliceAxolotlParameters *params =
|
||||
[[AliceAxolotlParameters alloc] initWithIdentityKey:[self.identityStore identityKeyPair:protocolContext]
|
||||
theirIdentityKey:theirIdentityKey
|
||||
ourBaseKey:ourBaseKey
|
||||
theirSignedPreKey:theirSignedPreKey
|
||||
theirOneTimePreKey:theirOneTimePreKey
|
||||
theirRatchetKey:theirSignedPreKey];
|
||||
|
||||
if (!sessionRecord.isFresh) {
|
||||
[sessionRecord archiveCurrentState];
|
||||
}
|
||||
|
||||
[RatchetingSession initializeSession:[sessionRecord sessionState] sessionVersion:CURRENT_VERSION AliceParameters:params];
|
||||
|
||||
|
||||
[RatchetingSession throws_initializeSession:[sessionRecord sessionState]
|
||||
sessionVersion:CURRENT_VERSION
|
||||
AliceParameters:params];
|
||||
|
||||
DDLogInfo(@"setUnacknowledgedPreKeyMessage for: %@ with preKeyId: %d", self.recipientId, theirOneTimePreKeyId);
|
||||
|
||||
[sessionRecord.sessionState setUnacknowledgedPreKeyMessage:theirOneTimePreKeyId signedPreKey:theirSignedPreKeyId baseKey:ourBaseKey.publicKey];
|
||||
[sessionRecord.sessionState setLocalRegistrationId:self.identityStore.localRegistrationId];
|
||||
[sessionRecord.sessionState setLocalRegistrationId:[self.identityStore localRegistrationId:protocolContext]];
|
||||
[sessionRecord.sessionState setRemoteRegistrationId:preKeyBundle.registrationId];
|
||||
[sessionRecord.sessionState setAliceBaseKey:ourBaseKey.publicKey];
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId deviceId:self.deviceId session:sessionRecord];
|
||||
[self.identityStore saveRemoteIdentity:theirIdentityKey recipientId:self.recipientId];
|
||||
|
||||
// Saving invalidates any existing sessions, so be sure to save *before* storing the new session.
|
||||
BOOL previousIdentityExisted = [self.identityStore saveRemoteIdentity:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
|
||||
[self.sessionStore storeSession:self.recipientId
|
||||
deviceId:self.deviceId
|
||||
session:sessionRecord
|
||||
protocolContext:protocolContext];
|
||||
}
|
||||
|
||||
- (int)processPrekeyWhisperMessage:(PreKeyWhisperMessage*)message withSession:(SessionRecord*)sessionRecord{
|
||||
- (int)throws_processPrekeyWhisperMessage:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(message);
|
||||
OWSAssert(sessionRecord);
|
||||
|
||||
int messageVersion = message.version;
|
||||
NSData *theirIdentityKey = message.identityKey.removeKeyType;
|
||||
NSData *theirIdentityKey = message.identityKey.throws_removeKeyType;
|
||||
|
||||
if (![self.identityStore isTrustedIdentityKey:theirIdentityKey recipientId:self.recipientId]) {
|
||||
if (![self.identityStore isTrustedIdentityKey:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
direction:TSMessageDirectionIncoming
|
||||
protocolContext:protocolContext]) {
|
||||
@throw [NSException exceptionWithName:UntrustedIdentityKeyException reason:@"There is a previously known identity key." userInfo:@{}];
|
||||
}
|
||||
|
||||
@ -123,49 +174,100 @@
|
||||
|
||||
switch (messageVersion) {
|
||||
case 3:
|
||||
unSignedPrekeyId = [self processPrekeyV3:message withSession:sessionRecord];
|
||||
unSignedPrekeyId =
|
||||
[self throws_processPrekeyV3:message withSession:sessionRecord protocolContext:protocolContext];
|
||||
break;
|
||||
default:
|
||||
@throw [NSException exceptionWithName:InvalidVersionException reason:@"Trying to initialize with unknown version" userInfo:@{}];
|
||||
break;
|
||||
}
|
||||
|
||||
[self.identityStore saveRemoteIdentity:theirIdentityKey recipientId:self.recipientId];
|
||||
|
||||
[self.identityStore saveRemoteIdentity:theirIdentityKey
|
||||
recipientId:self.recipientId
|
||||
protocolContext:protocolContext];
|
||||
|
||||
return unSignedPrekeyId;
|
||||
}
|
||||
|
||||
- (int)processPrekeyV3:(PreKeyWhisperMessage*)message withSession:(SessionRecord*)sessionRecord{
|
||||
|
||||
NSData *baseKey = message.baseKey.removeKeyType;
|
||||
|
||||
- (int)throws_processPrekeyV3:(PreKeyWhisperMessage *)message
|
||||
withSession:(SessionRecord *)sessionRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
OWSAssert(message);
|
||||
OWSAssert(sessionRecord);
|
||||
|
||||
NSData *baseKey = message.baseKey.throws_removeKeyType;
|
||||
|
||||
if ([sessionRecord hasSessionState:message.version baseKey:baseKey]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ECKeyPair *ourSignedPrekey = [self.signedPreKeyStore loadSignedPrekey:message.signedPrekeyId].keyPair;
|
||||
|
||||
BobAxolotlParameters *params = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:self.identityStore.identityKeyPair
|
||||
theirIdentityKey:message.identityKey.removeKeyType
|
||||
ourSignedPrekey:ourSignedPrekey
|
||||
ourRatchetKey:ourSignedPrekey
|
||||
ourOneTimePrekey:[self.prekeyStore loadPreKey:message.prekeyID].keyPair
|
||||
theirBaseKey:baseKey];
|
||||
|
||||
|
||||
SignedPreKeyRecord *_Nullable signedPreKeyRecord = [self.signedPreKeyStore loadSignedPreKey:message.signedPrekeyId
|
||||
protocolContext:protocolContext];
|
||||
if (signedPreKeyRecord == nil) {
|
||||
OWSLogWarn(@"Signed prekey id: %lu, prekey id: %lu.",
|
||||
(unsigned long) message.signedPrekeyId,
|
||||
(unsigned long) message.prekeyID);
|
||||
OWSLogWarn(@"Available signed prekey ids: %@",
|
||||
[self.signedPreKeyStore availableSignedPreKeyIdsWithProtocolContext:protocolContext]);
|
||||
|
||||
OWSRaiseException(InvalidKeyIdException, @"No signed prekey found matching key id");
|
||||
}
|
||||
ECKeyPair *ourSignedPrekey = signedPreKeyRecord.keyPair;
|
||||
|
||||
ECKeyPair *_Nullable ourOneTimePreKey;
|
||||
if (message.prekeyID >= 0) {
|
||||
PreKeyRecord *_Nullable preKey = [self.prekeyStore loadPreKey:message.prekeyID
|
||||
protocolContext:protocolContext];
|
||||
if (preKey == nil) {
|
||||
OWSFailDebug(@"Missing prekeyID: %lu", (unsigned long) message.prekeyID);
|
||||
OWSRaiseException(InvalidKeyIdException, @"No pre key found matching key id");
|
||||
}
|
||||
ourOneTimePreKey = preKey.keyPair;
|
||||
} else {
|
||||
DDLogWarn(@"%@ Processing PreKey message which had no one-time prekey.", self.tag);
|
||||
}
|
||||
|
||||
BobAxolotlParameters *params =
|
||||
[[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:[self.identityStore identityKeyPair:protocolContext]
|
||||
theirIdentityKey:message.identityKey.throws_removeKeyType
|
||||
ourSignedPrekey:ourSignedPrekey
|
||||
ourRatchetKey:ourSignedPrekey
|
||||
ourOneTimePrekey:ourOneTimePreKey
|
||||
theirBaseKey:baseKey];
|
||||
|
||||
if (!sessionRecord.isFresh) {
|
||||
[sessionRecord archiveCurrentState];
|
||||
}
|
||||
|
||||
[RatchetingSession initializeSession:sessionRecord.sessionState sessionVersion:message.version BobParameters:params];
|
||||
|
||||
[sessionRecord.sessionState setLocalRegistrationId:self.identityStore.localRegistrationId];
|
||||
|
||||
[RatchetingSession throws_initializeSession:sessionRecord.sessionState
|
||||
sessionVersion:message.version
|
||||
BobParameters:params];
|
||||
|
||||
[sessionRecord.sessionState setLocalRegistrationId:[self.identityStore localRegistrationId:protocolContext]];
|
||||
[sessionRecord.sessionState setRemoteRegistrationId:message.registrationId];
|
||||
[sessionRecord.sessionState setAliceBaseKey:baseKey];
|
||||
|
||||
if (message.prekeyID >= 0 && message.prekeyID != 0xFFFFFF) {
|
||||
|
||||
// If we used a prekey and it wasn't the prekey of last resort
|
||||
if (message.prekeyID >= 0 && message.prekeyID != kPreKeyOfLastResortId) {
|
||||
return message.prekeyID;
|
||||
} else{
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
//
|
||||
// SessionRecord.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 25/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -12,15 +8,14 @@
|
||||
@interface SessionRecord : NSObject <NSSecureCoding>
|
||||
|
||||
- (instancetype)init;
|
||||
- (instancetype)initWithSessionState:(SessionState*)sessionState;
|
||||
|
||||
- (BOOL)hasSessionState:(int)version baseKey:(NSData*)aliceBaseKey;
|
||||
- (SessionState*)sessionState;
|
||||
- (NSMutableArray*)previousSessionStates;
|
||||
- (NSArray<SessionState *> *)previousSessionStates;
|
||||
|
||||
- (BOOL)isFresh;
|
||||
- (void)markAsUnFresh;
|
||||
- (void)archiveCurrentState;
|
||||
- (void)promoteState:(SessionState*)promotedState;
|
||||
- (void)setState:(SessionState*)sessionState;
|
||||
|
||||
@end
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
//
|
||||
// SessionRecord.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 25/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SessionRecord.h"
|
||||
@ -57,17 +53,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)initWithSessionState:(SessionState *)sessionState{
|
||||
assert(sessionState);
|
||||
self = [self init];
|
||||
|
||||
self.sessionState = sessionState;
|
||||
self.fresh = false;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)hasSessionState:(int)version baseKey:(NSData *)aliceBaseKey{
|
||||
if (self.sessionState.version == version && [aliceBaseKey isEqualToData:self.sessionState.aliceBaseKey]) {
|
||||
return YES;
|
||||
@ -86,7 +71,8 @@
|
||||
return _sessionState;
|
||||
}
|
||||
|
||||
- (NSMutableArray*)previousSessionStates{
|
||||
- (NSArray<SessionState *> *)previousSessionStates
|
||||
{
|
||||
return _previousStates;
|
||||
}
|
||||
|
||||
@ -94,7 +80,16 @@
|
||||
return _fresh;
|
||||
}
|
||||
|
||||
- (void)markAsUnFresh
|
||||
{
|
||||
self.fresh = false;
|
||||
}
|
||||
|
||||
- (void)archiveCurrentState{
|
||||
if (self.sessionState.isFresh) {
|
||||
OWSLogInfo(@"Skipping archive, current session state is fresh.");
|
||||
return;
|
||||
}
|
||||
[self promoteState:[SessionState new]];
|
||||
}
|
||||
|
||||
@ -103,8 +98,11 @@
|
||||
self.sessionState = promotedState;
|
||||
|
||||
if (self.previousStates.count > ARCHIVED_STATES_MAX_LENGTH) {
|
||||
NSIndexSet *indexesToDelete = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(ARCHIVED_STATES_MAX_LENGTH, self.previousStates.count - ARCHIVED_STATES_MAX_LENGTH)];
|
||||
[self.previousSessionStates removeObjectsAtIndexes:indexesToDelete];
|
||||
NSUInteger deleteCount;
|
||||
ows_sub_overflow(self.previousStates.count, ARCHIVED_STATES_MAX_LENGTH, &deleteCount);
|
||||
NSIndexSet *indexesToDelete =
|
||||
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(ARCHIVED_STATES_MAX_LENGTH, deleteCount)];
|
||||
[self.previousStates removeObjectsAtIndexes:indexesToDelete];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
//
|
||||
// SessionState.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 01/03/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -43,13 +39,15 @@
|
||||
@property(nonatomic)int remoteRegistrationId;
|
||||
@property(nonatomic)int localRegistrationId;
|
||||
|
||||
@property (nonatomic, readonly) BOOL isFresh;
|
||||
|
||||
- (NSData*)senderRatchetKey;
|
||||
- (ECKeyPair*)senderRatchetKeyPair;
|
||||
|
||||
- (BOOL)hasReceiverChain:(NSData*)senderRatchet;
|
||||
- (BOOL)hasReceiverChain:(NSData *)senderEphemeral;
|
||||
- (BOOL)hasSenderChain;
|
||||
|
||||
- (ChainKey*)receiverChainKey:(NSData*)senderRatchetKey;
|
||||
- (ChainKey *)receiverChainKey:(NSData *)senderEphemeral;
|
||||
|
||||
- (void)setReceiverChainKey:(NSData*)senderEphemeral chainKey:(ChainKey*)chainKey;
|
||||
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
//
|
||||
// SessionState.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 01/03/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import "SessionState.h"
|
||||
#import "ReceivingChain.h"
|
||||
#import "SendingChain.h"
|
||||
@ -25,6 +21,8 @@ static NSString* const kCoderBaseKey = @"kCoderBaseKey";
|
||||
}
|
||||
|
||||
-(instancetype)initWithBaseKey:(NSData*)baseKey preKeyId:(int)preKeyId signedPreKeyId:(int)signedPrekeyId{
|
||||
OWSAssert(baseKey);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_preKeyId = preKeyId;
|
||||
@ -117,10 +115,15 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
[aCoder encodeObject:[NSNumber numberWithInt:self.remoteRegistrationId] forKey:kCoderRemoteRegID];
|
||||
[aCoder encodeObject:[NSNumber numberWithInt:self.localRegistrationId] forKey:kCoderLocalRegID];
|
||||
[aCoder encodeObject:self.sendingChain forKey:kCoderSendingChain];
|
||||
[aCoder encodeObject:self.receivingChains forKey:kCoderReceiverChains];
|
||||
[aCoder encodeObject:[self.receivingChains mutableCopy] forKey:kCoderReceiverChains];
|
||||
[aCoder encodeObject:self.pendingPreKey forKey:kCoderPendingPrekey];
|
||||
}
|
||||
|
||||
- (BOOL)isFresh
|
||||
{
|
||||
return self.remoteIdentityKey == nil && self.localIdentityKey == nil && self.sendingChain == nil && self.receivingChains.count == 0 && self.pendingPreKey == nil;
|
||||
}
|
||||
|
||||
- (NSData*)senderRatchetKey{
|
||||
return [[self senderRatchetKeyPair] publicKey];
|
||||
}
|
||||
@ -129,44 +132,53 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
return [[self sendingChain] senderRatchetKeyPair];
|
||||
}
|
||||
|
||||
- (BOOL)hasReceiverChain:(NSData*)senderRatchet{
|
||||
return [self receiverChainKey:senderRatchet] != nil;
|
||||
- (BOOL)hasReceiverChain:(NSData *)senderEphemeral
|
||||
{
|
||||
return [self receiverChain:senderEphemeral] != nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasSenderChain{
|
||||
return self.sendingChain != nil;
|
||||
}
|
||||
|
||||
- (ChainAndIndex*)receiverChain:(NSData*)senderRatchetKey{
|
||||
- (ChainAndIndex *)receiverChain:(NSData *)senderEphemeral
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
for (ReceivingChain *receiverChain in self.receivingChains) {
|
||||
NSData *chainSenderRatchetKey = receiverChain.senderRatchetKey;
|
||||
|
||||
if ([chainSenderRatchetKey isEqualToData:senderRatchetKey]) {
|
||||
|
||||
if ([chainSenderRatchetKey isEqualToData:senderEphemeral]) {
|
||||
ChainAndIndex *cai = [[ChainAndIndex alloc] init];
|
||||
cai.chain = receiverChain;
|
||||
cai.index = index;
|
||||
return cai;
|
||||
}
|
||||
index++;
|
||||
ows_add_overflow(index, 1, &index);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (ChainKey*)receiverChainKey:(NSData*)senderRatchetKey{
|
||||
ChainAndIndex *receiverChainAndIndex = [self receiverChain:senderRatchetKey];
|
||||
- (ChainKey *)receiverChainKey:(NSData *)senderEphemeral
|
||||
{
|
||||
OWSAssert(senderEphemeral);
|
||||
|
||||
ChainAndIndex *receiverChainAndIndex = [self receiverChain:senderEphemeral];
|
||||
ReceivingChain *receiverChain = (ReceivingChain*)receiverChainAndIndex.chain;
|
||||
|
||||
if (receiverChain == nil) {
|
||||
return nil;
|
||||
} else{
|
||||
OWSAssert(receiverChain.chainKey.key);
|
||||
return [[ChainKey alloc] initWithData:receiverChain.chainKey.key index:receiverChain.chainKey.index];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setReceiverChainKey:(NSData*)senderEphemeral chainKey:(ChainKey*)nextChainKey{
|
||||
OWSAssert(senderEphemeral);
|
||||
OWSAssert(nextChainKey);
|
||||
|
||||
ChainAndIndex *chainAndIndex = [self receiverChain:senderEphemeral];
|
||||
ReceivingChain *chain = (ReceivingChain*)chainAndIndex.chain;
|
||||
|
||||
@ -177,18 +189,24 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
}
|
||||
|
||||
- (void)addReceiverChain:(NSData*)senderRatchetKey chainKey:(ChainKey*)chainKey{
|
||||
|
||||
OWSAssert(senderRatchetKey);
|
||||
OWSAssert(chainKey);
|
||||
ReceivingChain *receivingChain = [[ReceivingChain alloc] initWithChainKey:chainKey senderRatchetKey:senderRatchetKey];
|
||||
|
||||
[self.receivingChains addObject:receivingChain];
|
||||
|
||||
if ([self.receivingChains count] > 5) {
|
||||
DDLogInfo(
|
||||
@"%@ Trimming excessive receivingChain count: %lu", self.tag, (unsigned long)self.receivingChains.count);
|
||||
// We keep 5 receiving chains to be able to decrypt out of order messages.
|
||||
[self.receivingChains removeObjectAtIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSenderChain:(ECKeyPair*)senderRatchetKeyPair chainKey:(ChainKey*)chainKey{
|
||||
OWSAssert(senderRatchetKeyPair);
|
||||
OWSAssert(chainKey);
|
||||
|
||||
self.sendingChain = [[SendingChain alloc]initWithChainKey:chainKey senderRatchetKeyPair:senderRatchetKeyPair];
|
||||
}
|
||||
|
||||
@ -197,18 +215,21 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
}
|
||||
|
||||
- (void)setSenderChainKey:(ChainKey*)nextChainKey{
|
||||
OWSAssert(nextChainKey);
|
||||
|
||||
SendingChain *sendingChain = self.sendingChain;
|
||||
sendingChain.chainKey = nextChainKey;
|
||||
|
||||
|
||||
self.sendingChain = sendingChain;
|
||||
}
|
||||
|
||||
- (BOOL)hasMessageKeys:(NSData*)senderRatchetKey counter:(int)counter{
|
||||
OWSAssert(senderRatchetKey);
|
||||
ChainAndIndex *chainAndIndex = [self receiverChain:senderRatchetKey];
|
||||
ReceivingChain *receivingChain = (ReceivingChain*)chainAndIndex.chain;
|
||||
|
||||
if (!receivingChain) {
|
||||
return false;
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray *messageKeyArray = receivingChain.messageKeysList;
|
||||
@ -247,10 +268,15 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
}
|
||||
|
||||
-(void)setReceiverChain:(int)index updatedChain:(ReceivingChain*)recvchain{
|
||||
OWSAssert(recvchain);
|
||||
|
||||
[self.receivingChains replaceObjectAtIndex:index withObject:recvchain];
|
||||
}
|
||||
|
||||
- (void)setMessageKeys:(NSData*)senderRatchetKey messageKeys:(MessageKeys*)messageKeys{
|
||||
OWSAssert(senderRatchetKey);
|
||||
OWSAssert(messageKeys);
|
||||
|
||||
ChainAndIndex *chainAndIndex = [self receiverChain:senderRatchetKey];
|
||||
ReceivingChain *chain = (ReceivingChain*)chainAndIndex.chain;
|
||||
[chain.messageKeysList addObject:messageKeys];
|
||||
@ -259,6 +285,8 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
}
|
||||
|
||||
- (void)setUnacknowledgedPreKeyMessage:(int)preKeyId signedPreKey:(int)signedPreKeyId baseKey:(NSData*)baseKey{
|
||||
OWSAssert(baseKey);
|
||||
|
||||
PendingPreKey *pendingPreKey = [[PendingPreKey alloc] initWithBaseKey:baseKey preKeyId:preKeyId signedPreKeyId:signedPreKeyId];
|
||||
|
||||
self.pendingPreKey = pendingPreKey;
|
||||
@ -275,4 +303,16 @@ static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey";
|
||||
self.pendingPreKey = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
//
|
||||
// SessionBuilder.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/07/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "IdentityKeyStore.h"
|
||||
#import "PreKeyStore.h"
|
||||
#import "SessionStore.h"
|
||||
#import "SignedPreKeyStore.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The Session Store defines the interface of the storage of sesssions.
|
||||
@ -19,3 +17,5 @@
|
||||
@protocol AxolotlStore <SessionStore, IdentityKeyStore, PreKeyStore, SessionStore, SignedPreKeyStore>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,19 +1,59 @@
|
||||
//
|
||||
// IdentityKeyStore.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 12/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SPKProtocolContext.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ECKeyPair;
|
||||
|
||||
typedef NS_ENUM(NSInteger, TSMessageDirection) {
|
||||
TSMessageDirectionUnknown = 0,
|
||||
TSMessageDirectionIncoming,
|
||||
TSMessageDirectionOutgoing
|
||||
};
|
||||
|
||||
// See a discussion of the protocolContext in SessionCipher.h.
|
||||
@protocol IdentityKeyStore <NSObject>
|
||||
|
||||
- (ECKeyPair*)identityKeyPair;
|
||||
- (int)localRegistrationId;
|
||||
- (void)saveRemoteIdentity:(NSData*)identityKey recipientId:(NSString*)recipientId;
|
||||
- (BOOL)isTrustedIdentityKey:(NSData*)identityKey recipientId:(NSString*)recipientId;
|
||||
- (nullable ECKeyPair *)identityKeyPair:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
- (int)localRegistrationId:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
/**
|
||||
* Record a recipients identity key
|
||||
*
|
||||
* @param identityKey key data used to identify the recipient
|
||||
* @param recipientId unique stable identifier for the recipient, e.g. e164 phone number
|
||||
*
|
||||
* @returns YES if we are replacing an existing known identity key for recipientId.
|
||||
* NO if there was no previously stored identity key for the recipient.
|
||||
*/
|
||||
- (BOOL)saveRemoteIdentity:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
/**
|
||||
* @param identityKey key data used to identify the recipient
|
||||
* @param recipientId unique stable identifier for the recipient, e.g. e164 phone number
|
||||
* @param direction whether the key is being used in a sending or receiving context, as this could affect the
|
||||
* decision to trust the key.
|
||||
*
|
||||
* @returns YES if the key is trusted
|
||||
* NO if the key is not trusted
|
||||
*/
|
||||
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
direction:(TSMessageDirection)direction
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId;
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
//
|
||||
// PreKeyStore.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 12/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "PreKeyRecord.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol PreKeyStore <NSObject>
|
||||
|
||||
- (PreKeyRecord*)loadPreKey:(int)preKeyId;
|
||||
- (nullable PreKeyRecord *)loadPreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord*)record;
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
- (BOOL)containsPreKey:(int)preKeyId;
|
||||
|
||||
- (void)removePreKey:(int)preKeyId;
|
||||
- (void)removePreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
//
|
||||
// SessionStore.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 12/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SPKProtocolContext.h"
|
||||
#import "SessionRecord.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// See a discussion of the protocolContext in SessionCipher.h.
|
||||
@protocol SessionStore <NSObject>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns a copy of the SessionRecord corresponding to the recipientId + deviceId tuple or a new SessionRecord if one does not currently exist.
|
||||
*
|
||||
@ -21,17 +19,27 @@
|
||||
*
|
||||
* @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple.
|
||||
*/
|
||||
- (SessionRecord *)loadSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (SessionRecord*)loadSession:(NSString*)contactIdentifier deviceId:(int)deviceId;
|
||||
- (NSArray *)subDevicesSessions:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext __attribute__((deprecated));
|
||||
|
||||
- (NSArray*)subDevicesSessions:(NSString*)contactIdentifier;
|
||||
- (void)storeSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
session:(SessionRecord *)session
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
- (void)storeSession:(NSString*)contactIdentifier deviceId:(int)deviceId session:(SessionRecord*)session;
|
||||
- (BOOL)containsSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (BOOL)containsSession:(NSString*)contactIdentifier deviceId:(int)deviceId;
|
||||
- (void)deleteSessionForContact:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
- (void)deleteSessionForContact:(NSString*)contactIdentifier deviceId:(int)deviceId;
|
||||
|
||||
- (void)deleteAllSessionsForContact:(NSString*)contactIdentifier;
|
||||
- (void)deleteAllSessionsForContact:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,25 +1,31 @@
|
||||
//
|
||||
// SignedPrekeyStore.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 12/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SignedPrekeyRecord.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol SignedPreKeyStore <NSObject>
|
||||
|
||||
- (SignedPreKeyRecord*)loadSignedPrekey:(int)signedPreKeyId;
|
||||
- (nullable SignedPreKeyRecord *)loadSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (NSArray*)loadSignedPreKeys;
|
||||
- (NSArray<SignedPreKeyRecord *> *)loadSignedPreKeysWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId signedPreKeyRecord:(SignedPreKeyRecord*)signedPreKeyRecord;
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId
|
||||
signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId;
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
- (void)removeSignedPreKey:(int)signedPrekeyId;
|
||||
- (void)removeSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext;
|
||||
|
||||
- (NSArray<NSString *> *)availableSignedPreKeyIdsWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
16
AxolotlKit/Classes/TestUtils/SPKMockProtocolStore.h
Normal file
16
AxolotlKit/Classes/TestUtils/SPKMockProtocolStore.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlStore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SPKMockProtocolStore : NSObject <AxolotlStore>
|
||||
|
||||
- (instancetype)initWithIdentityKeyPair:(ECKeyPair *)identityKeyPair
|
||||
localRegistrationId:(int)localRegistrationId NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
264
AxolotlKit/Classes/TestUtils/SPKMockProtocolStore.m
Normal file
264
AxolotlKit/Classes/TestUtils/SPKMockProtocolStore.m
Normal file
@ -0,0 +1,264 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SPKMockProtocolStore.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SPKMockProtocolStore ()
|
||||
|
||||
@property NSMutableDictionary<NSString *, NSMutableDictionary *> *sessionRecords;
|
||||
|
||||
// Signed PreKey Store
|
||||
|
||||
@property NSMutableDictionary *preKeyStore;
|
||||
@property NSMutableDictionary *signedPreKeyStore;
|
||||
|
||||
@property NSMutableDictionary *trustedKeys;
|
||||
|
||||
@property ECKeyPair *identityKeyPair;
|
||||
@property int localRegistrationId;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SPKMockProtocolStore
|
||||
|
||||
|
||||
#pragma mark General
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithIdentityKeyPair:[Curve25519 generateKeyPair] localRegistrationId:arc4random() % 16380];
|
||||
}
|
||||
|
||||
- (instancetype)initWithIdentityKeyPair:(ECKeyPair *)identityKeyPair localRegistrationId:(int)localRegistrationId
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_identityKeyPair = identityKeyPair;
|
||||
_localRegistrationId = localRegistrationId;
|
||||
|
||||
_preKeyStore = [NSMutableDictionary dictionary];
|
||||
_signedPreKeyStore = [NSMutableDictionary dictionary];
|
||||
_trustedKeys = [NSMutableDictionary dictionary];
|
||||
_sessionRecords = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Signed PreKey Store
|
||||
|
||||
- (nullable SignedPreKeyRecord *)loadSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
return [self.signedPreKeyStore objectForKey:[NSNumber numberWithInt:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray<SignedPreKeyRecord *> *)loadSignedPreKeysWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (SignedPreKeyRecord *signedPrekey in [self.signedPreKeyStore allValues]) {
|
||||
[results addObject:signedPrekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)availableSignedPreKeyIdsWithProtocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
return @[];
|
||||
}
|
||||
|
||||
- (void)storeSignedPreKey:(int)signedPreKeyId
|
||||
signedPreKeyRecord:(SignedPreKeyRecord *)signedPreKeyRecord
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.signedPreKeyStore setObject:signedPreKeyRecord forKey:[NSNumber numberWithInteger:signedPreKeyId]];
|
||||
}
|
||||
|
||||
- (BOOL)containsSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
if ([[self.signedPreKeyStore allKeys] containsObject:[NSNumber numberWithInteger:signedPreKeyId]]) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
- (void)removeSignedPreKey:(int)signedPreKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.signedPreKeyStore removeObjectForKey:[NSNumber numberWithInteger:signedPreKeyId]];
|
||||
}
|
||||
|
||||
#pragma mark PreKey Store
|
||||
|
||||
- (nullable PreKeyRecord *)loadPreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext;
|
||||
{
|
||||
return [self.preKeyStore objectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (NSArray *)loadPreKeys
|
||||
{
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
for (PreKeyRecord *prekey in [self.preKeyStore allValues]) {
|
||||
[results addObject:prekey];
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
- (void)storePreKey:(int)preKeyId preKeyRecord:(PreKeyRecord *)record
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.preKeyStore setObject:record forKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
- (void)removePreKey:(int)preKeyId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.preKeyStore removeObjectForKey:[NSNumber numberWithInt:preKeyId]];
|
||||
}
|
||||
|
||||
#pragma mark IdentityKeyStore
|
||||
|
||||
- (nullable ECKeyPair *)identityKeyPair:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
return self.identityKeyPair;
|
||||
}
|
||||
|
||||
- (int)localRegistrationId:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
return self.localRegistrationId;
|
||||
}
|
||||
|
||||
- (BOOL)saveRemoteIdentity:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSData *existingKey = [self.trustedKeys objectForKey:recipientId];
|
||||
|
||||
if ([existingKey isEqualToData:existingKey]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self.trustedKeys setObject:identityKey forKey:recipientId];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isTrustedIdentityKey:(NSData *)identityKey
|
||||
recipientId:(NSString *)recipientId
|
||||
direction:(TSMessageDirection)direction
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSData *data = [self.trustedKeys objectForKey:recipientId];
|
||||
if (!data) {
|
||||
// Trust on first use
|
||||
return YES;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case TSMessageDirectionIncoming:
|
||||
return YES;
|
||||
case TSMessageDirectionOutgoing:
|
||||
// In a real implementation you may wish to ensure the use has been properly notified of any
|
||||
// recent identity change before sending outgoing messages.
|
||||
return [data isEqualToData:identityKey];
|
||||
case TSMessageDirectionUnknown:
|
||||
NSAssert(NO, @"unknown message direction");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
|
||||
{
|
||||
return [self identityKeyForRecipientId:recipientId protocolContext:nil];
|
||||
}
|
||||
|
||||
- (nullable NSData *)identityKeyForRecipientId:(NSString *)recipientId
|
||||
protocolContext:(nullable id<SPKProtocolReadContext>)protocolContext
|
||||
{
|
||||
NSData *_Nullable data = [self.trustedKeys objectForKey:recipientId];
|
||||
return data;
|
||||
}
|
||||
|
||||
#pragma mark Session Store
|
||||
|
||||
- (SessionRecord *)loadSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
SessionRecord *sessionRecord = [[self deviceSessionRecordsForContactIdentifier:contactIdentifier]
|
||||
objectForKey:[NSNumber numberWithInteger:deviceId]];
|
||||
|
||||
if (!sessionRecord) {
|
||||
sessionRecord = [SessionRecord new];
|
||||
}
|
||||
|
||||
return sessionRecord;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
|
||||
- (NSArray *)subDevicesSessions:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
return [[self deviceSessionRecordsForContactIdentifier:contactIdentifier] allKeys];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
- (NSMutableDictionary *)deviceSessionRecordsForContactIdentifier:(NSString *)contactIdentifier
|
||||
{
|
||||
return [self.sessionRecords objectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
- (void)storeSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
session:(SessionRecord *)session
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSAssert(session, @"Session can't be nil");
|
||||
NSMutableDictionary *deviceSessions = self.sessionRecords[contactIdentifier];
|
||||
if (!deviceSessions) {
|
||||
deviceSessions = [NSMutableDictionary new];
|
||||
}
|
||||
deviceSessions[@(deviceId)] = session;
|
||||
|
||||
self.sessionRecords[contactIdentifier] = deviceSessions;
|
||||
}
|
||||
|
||||
- (BOOL)containsSession:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
|
||||
if ([[self.sessionRecords objectForKey:contactIdentifier] objectForKey:[NSNumber numberWithInt:deviceId]]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)deleteSessionForContact:(NSString *)contactIdentifier
|
||||
deviceId:(int)deviceId
|
||||
protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
NSMutableDictionary<NSNumber *, SessionRecord *> *sessions =
|
||||
[self deviceSessionRecordsForContactIdentifier:contactIdentifier];
|
||||
[sessions removeObjectForKey:@(deviceId)];
|
||||
}
|
||||
|
||||
- (void)deleteAllSessionsForContact:(NSString *)contactIdentifier protocolContext:(nullable id<SPKProtocolWriteContext>)protocolContext
|
||||
{
|
||||
[self.sessionRecords removeObjectForKey:contactIdentifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,16 +1,18 @@
|
||||
//
|
||||
// NSData+keyVersionByte.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSData (keyVersionByte)
|
||||
|
||||
- (instancetype)prependKeyType;
|
||||
- (instancetype)removeKeyType;
|
||||
|
||||
- (instancetype)throws_removeKeyType NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
- (nullable instancetype)removeKeyTypeAndReturnError:(NSError **)outError;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
//
|
||||
// NSData+keyVersionByte.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AxolotlExceptions.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
#import "AxolotlExceptions.h"
|
||||
#import <SignalCoreKit/SCKExceptionWrapper.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation NSData (keyVersionByte)
|
||||
|
||||
@ -18,20 +17,36 @@ const Byte DJB_TYPE = 0x05;
|
||||
NSMutableData *data = [NSMutableData dataWithBytes:&DJB_TYPE length:1];
|
||||
[data appendData:self.copy];
|
||||
return data;
|
||||
} else {
|
||||
OWSLogDebug(@"key length: %lu", (unsigned long)self.length);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)removeKeyType {
|
||||
- (nullable instancetype)removeKeyTypeAndReturnError:(NSError **)outError
|
||||
{
|
||||
@try {
|
||||
return self.throws_removeKeyType;
|
||||
} @catch (NSException *exception) {
|
||||
*outError = SCKExceptionWrapperErrorMake(exception);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)throws_removeKeyType
|
||||
{
|
||||
if (self.length == 33) {
|
||||
if ([[self subdataWithRange:NSMakeRange(0, 1)] isEqualToData:[NSData dataWithBytes:&DJB_TYPE length:1]]) {
|
||||
return [self subdataWithRange:NSMakeRange(1, 32)];
|
||||
} else{
|
||||
@throw [NSException exceptionWithName:InvalidKeyException reason:@"Key type is incorrect" userInfo:@{}];
|
||||
}
|
||||
} else{
|
||||
} else {
|
||||
OWSLogDebug(@"key length: %lu", (unsigned long)self.length);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
17
AxolotlKit/Classes/Utility/SPKProtocolContext.h
Normal file
17
AxolotlKit/Classes/Utility/SPKProtocolContext.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol SPKProtocolReadContext <NSObject>
|
||||
|
||||
@end
|
||||
|
||||
@protocol SPKProtocolWriteContext <SPKProtocolReadContext>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,13 +1,10 @@
|
||||
//
|
||||
// SerializationUtilities.h
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define MAC_LENGTH 8
|
||||
|
||||
@ -19,6 +16,12 @@
|
||||
|
||||
+ (Byte)intsToByteHigh:(int)highValue low:(int)lowValue;
|
||||
|
||||
+ (NSData*)macWithVersion:(int)version identityKey:(NSData*)senderIdentityKey receiverIdentityKey:(NSData*)receiverIdentityKey macKey:(NSData*)macKey serialized:(NSData*)serialized;
|
||||
+ (NSData *)throws_macWithVersion:(int)version
|
||||
identityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
serialized:(NSData *)serialized NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -1,41 +1,80 @@
|
||||
//
|
||||
// SerializationUtilities.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 26/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SerializationUtilities.h"
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation SerializationUtilities
|
||||
|
||||
+ (int)highBitsToIntFromByte:(Byte)byte{
|
||||
+ (int)highBitsToIntFromByte:(Byte)byte
|
||||
{
|
||||
return (byte & 0xFF) >> 4;
|
||||
}
|
||||
|
||||
+ (int)lowBitsToIntFromByte:(Byte)byte{
|
||||
+ (int)lowBitsToIntFromByte:(Byte)byte
|
||||
{
|
||||
return (byte & 0xF);
|
||||
}
|
||||
|
||||
+ (Byte)intsToByteHigh:(int)highValue low:(int)lowValue{
|
||||
+ (Byte)intsToByteHigh:(int)highValue low:(int)lowValue
|
||||
{
|
||||
return (Byte)((highValue << 4 | lowValue) & 0xFF);
|
||||
}
|
||||
|
||||
+ (NSData*)macWithVersion:(int)version identityKey:(NSData*)senderIdentityKey receiverIdentityKey:(NSData*)receiverIdentityKey macKey:(NSData*)macKey serialized:(NSData*)serialized {
|
||||
|
||||
uint8_t ourHmac[CC_SHA256_DIGEST_LENGTH] = {0};
|
||||
+ (NSData *)throws_macWithVersion:(int)version
|
||||
identityKey:(NSData *)senderIdentityKey
|
||||
receiverIdentityKey:(NSData *)receiverIdentityKey
|
||||
macKey:(NSData *)macKey
|
||||
serialized:(NSData *)serialized
|
||||
{
|
||||
if (!macKey) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Missing macKey." userInfo:nil];
|
||||
}
|
||||
if (macKey.length >= SIZE_MAX) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Oversize macKey." userInfo:nil];
|
||||
}
|
||||
if (!senderIdentityKey) {
|
||||
@throw
|
||||
[NSException exceptionWithName:NSInvalidArgumentException reason:@"Missing senderIdentityKey" userInfo:nil];
|
||||
}
|
||||
if (senderIdentityKey.length >= SIZE_MAX) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"Oversize senderIdentityKey"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (!receiverIdentityKey) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"Missing receiverIdentityKey"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (receiverIdentityKey.length >= SIZE_MAX) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException
|
||||
reason:@"Oversize receiverIdentityKey"
|
||||
userInfo:nil];
|
||||
}
|
||||
if (!serialized) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Missing serialized." userInfo:nil];
|
||||
}
|
||||
if (serialized.length >= SIZE_MAX) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Oversize serialized." userInfo:nil];
|
||||
}
|
||||
|
||||
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
|
||||
OWSAssert(bufferData);
|
||||
|
||||
CCHmacContext context;
|
||||
CCHmacInit (&context, kCCHmacAlgSHA256, [macKey bytes], [macKey length]);
|
||||
CCHmacInit(&context, kCCHmacAlgSHA256, [macKey bytes], [macKey length]);
|
||||
CCHmacUpdate(&context, [senderIdentityKey bytes], [senderIdentityKey length]);
|
||||
CCHmacUpdate(&context, [receiverIdentityKey bytes], [receiverIdentityKey length]);
|
||||
CCHmacUpdate(&context, [serialized bytes], [serialized length]);
|
||||
CCHmacFinal (&context, &ourHmac);
|
||||
|
||||
return [NSData dataWithBytes:ourHmac length:MAC_LENGTH];
|
||||
CCHmacFinal(&context, bufferData.mutableBytes);
|
||||
|
||||
return [bufferData subdataWithRange:NSMakeRange(0, MAC_LENGTH)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
7
AxolotlKit/SPKPrefix.h
Normal file
7
AxolotlKit/SPKPrefix.h
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SignalCoreKit/NSObject+OWS.h>
|
||||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.fredericjacobs.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
16
AxolotlKitTests/AxolotlKitSwiftTests.swift
Normal file
16
AxolotlKitTests/AxolotlKitSwiftTests.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
// Cocoapods-generated test targets (like this one)
|
||||
// fail to link if:
|
||||
//
|
||||
// * They only contain Obj-C tests.
|
||||
// * They depend on pods that use Swift.
|
||||
//
|
||||
// The work around is to add (this) empty swift file
|
||||
// to our test target.
|
||||
//
|
||||
// See: https://github.com/CocoaPods/CocoaPods/issues/7170
|
||||
@ -1,17 +1,11 @@
|
||||
//
|
||||
// ECCTests.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 21/09/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <25519/Ed25519.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface ECCTests : XCTestCase
|
||||
|
||||
@ -79,13 +73,15 @@
|
||||
(Byte) 0xe6, (Byte) 0x29};
|
||||
|
||||
NSData *sharedSecret = [NSData dataWithBytes:sharedBytes length:32];
|
||||
|
||||
ECKeyPair *aliceKeyPair = [ECKeyPair keyPairWithPrivateKey:alicePrivateKey publicKey:alicePublicKey];
|
||||
ECKeyPair *bobKeyPair = [ECKeyPair keyPairWithPrivateKey:bobPrivateKey publicKey:bobPublicKey];
|
||||
|
||||
NSData *aliceShared = [Curve25519 generateSharedSecretFromPublicKey:[bobKeyPair publicKey] andKeyPair:aliceKeyPair];
|
||||
NSData *bobShared = [Curve25519 generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair];
|
||||
|
||||
|
||||
ECKeyPair *aliceKeyPair = [ECKeyPair throws_keyPairWithPrivateKey:alicePrivateKey publicKey:alicePublicKey];
|
||||
ECKeyPair *bobKeyPair = [ECKeyPair throws_keyPairWithPrivateKey:bobPrivateKey publicKey:bobPublicKey];
|
||||
|
||||
NSData *aliceShared =
|
||||
[Curve25519 throws_generateSharedSecretFromPublicKey:[bobKeyPair publicKey] andKeyPair:aliceKeyPair];
|
||||
NSData *bobShared =
|
||||
[Curve25519 throws_generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair];
|
||||
|
||||
XCTAssert([aliceShared isEqualToData:sharedSecret], @"Alice's shared secret is equal to the expected one.");
|
||||
XCTAssert([bobShared isEqualToData:sharedSecret], @"Bob's shared secret is equal to the expected one.");
|
||||
}
|
||||
@ -94,8 +90,11 @@
|
||||
for (int i=0;i<100;i++) {
|
||||
ECKeyPair *aliceKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobKeyPair = [Curve25519 generateKeyPair];
|
||||
|
||||
XCTAssert([[Curve25519 generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair] isEqualToData:[Curve25519 generateSharedSecretFromPublicKey:[bobKeyPair publicKey] andKeyPair:aliceKeyPair]], @"Randomly generated keypairs produce same shared secret.");
|
||||
|
||||
XCTAssert([[Curve25519 throws_generateSharedSecretFromPublicKey:[aliceKeyPair publicKey] andKeyPair:bobKeyPair]
|
||||
isEqualToData:[Curve25519 throws_generateSharedSecretFromPublicKey:[bobKeyPair publicKey]
|
||||
andKeyPair:aliceKeyPair]],
|
||||
@"Randomly generated keypairs produce same shared secret.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,11 +143,11 @@
|
||||
NSData *ephemPublic = [NSData dataWithBytes:aliceEphemeralPublic length:33];
|
||||
|
||||
NSData *signature = [NSData dataWithBytes:aliceSignature length:ECCSignatureLength];
|
||||
|
||||
if (![Ed25519 verifySignature:signature publicKey:alicePublic data:ephemPublic]) {
|
||||
|
||||
if (![Ed25519 throws_verifySignature:signature publicKey:alicePublic data:ephemPublic]) {
|
||||
XCTAssert(NO, @"Sig verification failed!");
|
||||
}
|
||||
|
||||
|
||||
for (int i=0;i<[signature length];i++) {
|
||||
|
||||
NSMutableData *modifiedSignature = [signature mutableCopy];
|
||||
@ -160,28 +159,12 @@
|
||||
replacedByte ^= 0x01;
|
||||
|
||||
[modifiedSignature replaceBytesInRange:NSMakeRange(i, 1) withBytes:&replacedByte length:1];
|
||||
|
||||
if ([Ed25519 verifySignature:modifiedSignature publicKey:alicePublic data:ephemPublic]) {
|
||||
|
||||
if ([Ed25519 throws_verifySignature:modifiedSignature publicKey:alicePublic data:ephemPublic]) {
|
||||
XCTAssert(NO, @"Modified signature shouldn't be verified correctly");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)testSignatureOverflow{
|
||||
ECKeyPair *keys = [Curve25519 generateKeyPair];
|
||||
Byte message [4096] = {};
|
||||
NSData *data = [NSData dataWithBytes:&message length:4096];
|
||||
|
||||
@try {
|
||||
__unused NSData *signature = [Ed25519 sign:data withKeyPair:keys];
|
||||
XCTAssert(NO, @"Signature algorithm should have thrown on overflow");
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
XCTAssert(YES, @"Signing overflow detected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@ -1,14 +1,9 @@
|
||||
//
|
||||
// HKDFTest.m
|
||||
//
|
||||
//
|
||||
// Created by Frederic Jacobs on 23/09/14.
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface HKDFTest : XCTestCase
|
||||
|
||||
@ -45,7 +40,7 @@
|
||||
|
||||
NSData *okmData = [NSData dataWithBytes:okm length:42];
|
||||
|
||||
NSData *actualOutput = [HKDFKit deriveKey:ikmData info:infoData salt:saltData outputSize:42];
|
||||
NSData *actualOutput = [HKDFKit throws_deriveKey:ikmData info:infoData salt:saltData outputSize:42];
|
||||
|
||||
XCTAssert([okmData isEqualToData:actualOutput], @"HKDF output matches test vector");
|
||||
}
|
||||
@ -127,7 +122,7 @@
|
||||
|
||||
NSData *okmData = [NSData dataWithBytes:okm length:82];
|
||||
|
||||
NSData *actualOutput = [HKDFKit deriveKey:ikmData info:infoData salt:saltData outputSize:82];
|
||||
NSData *actualOutput = [HKDFKit throws_deriveKey:ikmData info:infoData salt:saltData outputSize:82];
|
||||
|
||||
XCTAssert([actualOutput isEqualToData:okmData], @"HKDF output matches long test vector");
|
||||
}
|
||||
@ -1,15 +1,12 @@
|
||||
//
|
||||
// PreKeyBundleTests.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 15/02/15.
|
||||
// Copyright (c) 2015 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "PreKeyBundle.h"
|
||||
#import <25519/Curve25519.h>
|
||||
#import <AxolotlKit/PreKeyBundle.h>
|
||||
#import <AxolotlKit/NSData+keyVersionByte.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
|
||||
@interface PreKeyBundleTests : XCTestCase
|
||||
|
||||
@ -31,11 +28,11 @@
|
||||
PreKeyBundle *bundle = [[PreKeyBundle alloc] initWithRegistrationId:1
|
||||
deviceId:2
|
||||
preKeyId:3
|
||||
preKeyPublic:[Curve25519 generateKeyPair].publicKey
|
||||
signedPreKeyPublic:[Curve25519 generateKeyPair].publicKey
|
||||
preKeyPublic:[Curve25519 generateKeyPair].publicKey.prependKeyType
|
||||
signedPreKeyPublic:[Curve25519 generateKeyPair].publicKey.prependKeyType
|
||||
signedPreKeyId:4
|
||||
signedPreKeySignature:[Curve25519 generateKeyPair].publicKey
|
||||
identityKey:[Curve25519 generateKeyPair].publicKey];
|
||||
identityKey:[Curve25519 generateKeyPair].publicKey.prependKeyType];
|
||||
|
||||
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:bundle];
|
||||
@ -1,17 +1,12 @@
|
||||
//
|
||||
// ProtobuffsTests.m
|
||||
// AxolotlKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 25/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AxolotlKit-Swift.h>
|
||||
#import <AxolotlKit/PreKeyWhisperMessage.h>
|
||||
#import <AxolotlKit/WhisperMessage.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "PreKeyWhisperMessage.h"
|
||||
#import "WhisperMessage.h"
|
||||
#import "WhisperTextProtocol.pb.h"
|
||||
|
||||
@interface ProtobuffsTests : XCTestCase
|
||||
|
||||
@end
|
||||
@ -33,18 +28,21 @@
|
||||
NSData *cipherText = [@"CipherText" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
int counter = 2;
|
||||
int previousCounter = 1;
|
||||
|
||||
SPKProtoTSProtoWhisperMessageBuilder *builder =
|
||||
[SPKProtoTSProtoWhisperMessage builderWithRatchetKey:ratchetKey
|
||||
counter:counter
|
||||
ciphertext:cipherText];
|
||||
[builder setPreviousCounter:previousCounter];
|
||||
SPKProtoTSProtoWhisperMessage *message = [builder buildIgnoringErrors];
|
||||
NSData *serializedMessage = [message serializedDataIgnoringErrors];
|
||||
|
||||
TSProtoWhisperMessage *helloMessage = [[[[[[[TSProtoWhisperMessage builder]
|
||||
setCounter:1]
|
||||
setRatchetKey:ratchetKey]
|
||||
setCiphertext:cipherText]
|
||||
setCounter:counter]
|
||||
setPreviousCounter:previousCounter] build];
|
||||
|
||||
NSData *serializedMessage = [helloMessage data];
|
||||
|
||||
TSProtoWhisperMessage *deserialized = [TSProtoWhisperMessage parseFromData:serializedMessage];
|
||||
|
||||
NSError *error;
|
||||
SPKProtoTSProtoWhisperMessage *_Nullable deserialized =
|
||||
[SPKProtoTSProtoWhisperMessage parseData:serializedMessage error:&error];
|
||||
XCTAssertNotNil(deserialized);
|
||||
XCTAssertNil(error);
|
||||
|
||||
XCTAssert(deserialized.counter == counter);
|
||||
XCTAssert(deserialized.previousCounter == previousCounter);
|
||||
XCTAssert([deserialized.ratchetKey isEqualToData:ratchetKey]);
|
||||
@ -1,25 +1,19 @@
|
||||
//
|
||||
// RatchetingSessionTest.m
|
||||
// AxolotlKit
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Created by Frederic Jacobs on 24/10/14.
|
||||
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
|
||||
//
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <25519/Curve25519.h>
|
||||
#import <25519/Ed25519.h>
|
||||
|
||||
#import "AxolotlInMemoryStore.h"
|
||||
#import "AliceAxolotlParameters.h"
|
||||
#import "BobAxolotlParameters.h"
|
||||
#import "SessionCipher.h"
|
||||
#import "SessionState.h"
|
||||
#import "RatchetingSession.h"
|
||||
#import "SessionRecord.h"
|
||||
#import "ChainKey.h"
|
||||
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
#import <AxolotlKit/AliceAxolotlParameters.h>
|
||||
#import <AxolotlKit/BobAxolotlParameters.h>
|
||||
#import <AxolotlKit/ChainKey.h>
|
||||
#import <AxolotlKit/RatchetingSession.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
#import <AxolotlKit/SessionRecord.h>
|
||||
#import <AxolotlKit/SessionState.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface RatchetingSessionTest : XCTestCase
|
||||
|
||||
@ -135,7 +129,10 @@
|
||||
(Byte) 0x05, (Byte) 0x40, (Byte) 0xE0, (Byte) 0xD8,
|
||||
(Byte) 0x45, (Byte) 0xDF, (Byte) 0xA2, (Byte) 0xF0,
|
||||
(Byte) 0x78, (Byte) 0x1E, (Byte) 0xBA, (Byte) 0x42};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *bobPreKeyPrivateKeyData = [NSData dataWithBytes:bobPreKeyPrivateKey length:32];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
|
||||
Byte bobPreKeyPublicKey [] = {(Byte) 0x05, (Byte) 0x52, (Byte) 0x02, (Byte) 0xA7,
|
||||
@ -147,7 +144,10 @@
|
||||
(Byte) 0xDF, (Byte) 0x51, (Byte) 0x38, (Byte) 0xA5,
|
||||
(Byte) 0xFC, (Byte) 0xD9, (Byte) 0xB6, (Byte) 0xC8,
|
||||
(Byte) 0x44};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *bobPreKeyPublicKeyData = [NSData dataWithBytes:bobPreKeyPublicKey length:33];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte aliceSendingRatchetPrivate [] = {(Byte) 0x98, (Byte) 0x04, (Byte) 0x0B, (Byte) 0xAE,
|
||||
(Byte) 0x6B, (Byte) 0x3D, (Byte) 0x02, (Byte) 0x9C,
|
||||
@ -178,7 +178,10 @@
|
||||
(Byte) 0x11, (Byte) 0xBF, (Byte) 0xA1, (Byte) 0xAA,
|
||||
(Byte) 0x16, (Byte) 0xAE, (Byte) 0xF3, (Byte) 0x6A,
|
||||
(Byte) 0x91, (Byte) 0xCD, (Byte) 0x1A, (Byte) 0x9B};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *aliceRootKeyData = [NSData dataWithBytes:aliceRootKey length:32];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte aliceSendingChainKey [] = {(Byte) 0x8A, (Byte) 0xA2, (Byte) 0x05, (Byte) 0xEA,
|
||||
(Byte) 0x17, (Byte) 0x00, (Byte) 0xC0, (Byte) 0x85,
|
||||
@ -244,7 +247,10 @@
|
||||
(Byte) 0xC2, (Byte) 0x8C, (Byte) 0x1C, (Byte) 0x6E,
|
||||
(Byte) 0x7C, (Byte) 0x48, (Byte) 0xB6, (Byte) 0x91,
|
||||
(Byte) 0x26, (Byte) 0x9B, (Byte) 0xF2, (Byte) 0xE6};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *bobSessionRecordRootKeyData = [NSData dataWithBytes:bobSessionRecordRootKey length:32];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte alicePlaintext [] = {(Byte) 0x54, (Byte) 0x68, (Byte) 0x69, (Byte) 0x73,
|
||||
(Byte) 0x20, (Byte) 0x69, (Byte) 0x73, (Byte) 0x20,
|
||||
@ -276,7 +282,10 @@
|
||||
(Byte) 0xC3, (Byte) 0xA6, (Byte) 0x96, (Byte) 0x06,
|
||||
(Byte) 0xAB, (Byte) 0xBE, (Byte) 0x2E, (Byte) 0x31,
|
||||
(Byte) 0x63, (Byte) 0x88};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *AliceSerializedWhisperMessageData = [NSData dataWithBytes:AliceSerializedWhisperMessage length:82];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte aliceCipherText [] = {(Byte) 0x9E, (Byte) 0xF2, (Byte) 0xD0, (Byte) 0xE1,
|
||||
(Byte) 0x30, (Byte) 0x4C, (Byte) 0x01, (Byte) 0xE0,
|
||||
@ -287,22 +296,27 @@
|
||||
(Byte) 0x8D, (Byte) 0xF7, (Byte) 0x22, (Byte) 0xDC,
|
||||
(Byte) 0x22, (Byte) 0x76, (Byte) 0xC3, (Byte) 0xA6};
|
||||
NSData *aliceCipherTextData = [NSData dataWithBytes:aliceCipherText length:32];
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey = [ECKeyPair keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey = [ECKeyPair keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey = [ECKeyPair keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey = [ECKeyPair keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet = [ECKeyPair keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
// ---
|
||||
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
@ -310,11 +324,16 @@
|
||||
AliceAxolotlParameters *aliceAxolotlParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKey theirIdentityKey:bobIdentityKey.publicKey ourBaseKey:aliceBaseKey theirSignedPreKey:bobBaseKey.publicKey theirOneTimePreKey:nil theirRatchetKey:bobBaseKey.publicKey];
|
||||
|
||||
BobAxolotlParameters *bobAxolotlParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKey theirIdentityKey:aliceIdentityKey.publicKey ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:nil theirBaseKey:aliceBaseKey.publicKey];
|
||||
|
||||
[RatchetingSession initializeSession:aliceSessionRecord.sessionState sessionVersion:3 AliceParameters:aliceAxolotlParams senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession initializeSession:bobSessionRecord.sessionState sessionVersion:3 BobParameters:bobAxolotlParams];
|
||||
|
||||
|
||||
[RatchetingSession throws_initializeSession:aliceSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
AliceParameters:aliceAxolotlParams
|
||||
senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession throws_initializeSession:bobSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
BobParameters:bobAxolotlParams];
|
||||
|
||||
NSString *aliceIdentifier = @"+483294823482";
|
||||
NSString *bobIdentifier = @"+389424728942";
|
||||
|
||||
@ -322,42 +341,45 @@
|
||||
XCTAssert([[@"This is a plaintext message." dataUsingEncoding:NSUTF8StringEncoding] isEqualToData:alicePlaintextData], @"Encoding is not correct");
|
||||
XCTAssert([aliceSessionRecord.sessionState.rootKey.keyData isEqualToData:aliceSessionRecordRootKeyData]);
|
||||
XCTAssert([aliceSessionRecord.sessionState.senderChainKey.key isEqualToData:aliceSendingChainKeyData]);
|
||||
XCTAssert([aliceSendingCipherKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.cipherKey]);
|
||||
XCTAssert([aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord];
|
||||
XCTAssert([aliceSendingCipherKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.cipherKey]);
|
||||
XCTAssert(
|
||||
[aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord protocolContext:nil];
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:bobIdentifier deviceId:1];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher encryptMessage:alicePlaintextData];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher throws_encryptMessage:alicePlaintextData protocolContext:nil];
|
||||
XCTAssert([aliceCipherTextData isEqualToData:message.cipherText]);
|
||||
|
||||
// Logging's Bob's Session initialization and first message decryption
|
||||
|
||||
XCTAssert([bobRootKeyData isEqualToData:bobSessionRecord.sessionState.rootKey.keyData]);
|
||||
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *plainData = [bobSessionCipher decrypt:message];
|
||||
|
||||
|
||||
NSData *plainData = [bobSessionCipher throws_decrypt:message protocolContext:nil];
|
||||
|
||||
XCTAssert([plainData isEqualToData:alicePlaintextData]);
|
||||
|
||||
for (int i = 0; i<100; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
WhisperMessage *encrypted = [aliceSessionCipher encryptMessage:message];
|
||||
|
||||
XCTAssert([message isEqualToData:[bobSessionCipher decrypt:encrypted]]);
|
||||
|
||||
WhisperMessage *encrypted = [aliceSessionCipher throws_encryptMessage:message protocolContext:nil];
|
||||
|
||||
XCTAssert([message isEqualToData:[bobSessionCipher throws_decrypt:encrypted protocolContext:nil]]);
|
||||
}
|
||||
|
||||
for (int i = 0; i<100; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
WhisperMessage *encrypted = [bobSessionCipher encryptMessage:message];
|
||||
|
||||
XCTAssert([message isEqualToData:[aliceSessionCipher decrypt:encrypted]]);
|
||||
|
||||
WhisperMessage *encrypted = [bobSessionCipher throws_encryptMessage:message protocolContext:nil];
|
||||
|
||||
XCTAssert([message isEqualToData:[aliceSessionCipher throws_decrypt:encrypted protocolContext:nil]]);
|
||||
}
|
||||
|
||||
NSMutableArray *plainTexts = [NSMutableArray new];
|
||||
@ -366,11 +388,12 @@
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[plainTexts addObject:message];
|
||||
[cipherMessages addObject:[bobSessionCipher encryptMessage:message]];
|
||||
[cipherMessages addObject:[bobSessionCipher throws_encryptMessage:message protocolContext:nil]];
|
||||
}
|
||||
|
||||
for (int i = 0; i < plainTexts.count; i++) {
|
||||
XCTAssert([[aliceSessionCipher decrypt:[cipherMessages objectAtIndex:i]] isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
XCTAssert([[aliceSessionCipher throws_decrypt:[cipherMessages objectAtIndex:i] protocolContext:nil]
|
||||
isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -469,8 +492,10 @@
|
||||
(Byte) 0x05, (Byte) 0x40, (Byte) 0xE0, (Byte) 0xD8,
|
||||
(Byte) 0x45, (Byte) 0xDF, (Byte) 0xA2, (Byte) 0xF0,
|
||||
(Byte) 0x78, (Byte) 0x1E, (Byte) 0xBA, (Byte) 0x42};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *bobPreKeyPrivateKeyData = [NSData dataWithBytes:bobPreKeyPrivateKey length:32];
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte bobPreKeyPublicKey [] = {(Byte) 0x05, (Byte) 0x52, (Byte) 0x02, (Byte) 0xA7,
|
||||
(Byte) 0xDE, (Byte) 0x5D, (Byte) 0x6C, (Byte) 0x23,
|
||||
@ -481,7 +506,10 @@
|
||||
(Byte) 0xDF, (Byte) 0x51, (Byte) 0x38, (Byte) 0xA5,
|
||||
(Byte) 0xFC, (Byte) 0xD9, (Byte) 0xB6, (Byte) 0xC8,
|
||||
(Byte) 0x44};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *bobPreKeyPublicKeyData = [NSData dataWithBytes:bobPreKeyPublicKey length:33];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte aliceSendingRatchetPrivate [] = {(Byte) 0x98, (Byte) 0x04, (Byte) 0x0B, (Byte) 0xAE,
|
||||
(Byte) 0x6B, (Byte) 0x3D, (Byte) 0x02, (Byte) 0x9C,
|
||||
@ -512,7 +540,10 @@
|
||||
(Byte) 0x11, (Byte) 0xBF, (Byte) 0xA1, (Byte) 0xAA,
|
||||
(Byte) 0x16, (Byte) 0xAE, (Byte) 0xF3, (Byte) 0x6A,
|
||||
(Byte) 0x91, (Byte) 0xCD, (Byte) 0x1A, (Byte) 0x9B};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *aliceRootKeyData = [NSData dataWithBytes:aliceRootKey length:32];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte aliceSendingChainKey [] = {(Byte) 0x8A, (Byte) 0xA2, (Byte) 0x05, (Byte) 0xEA,
|
||||
(Byte) 0x17, (Byte) 0x00, (Byte) 0xC0, (Byte) 0x85,
|
||||
@ -578,7 +609,10 @@
|
||||
(Byte) 0xC2, (Byte) 0x8C, (Byte) 0x1C, (Byte) 0x6E,
|
||||
(Byte) 0x7C, (Byte) 0x48, (Byte) 0xB6, (Byte) 0x91,
|
||||
(Byte) 0x26, (Byte) 0x9B, (Byte) 0xF2, (Byte) 0xE6};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *bobSessionRecordRootKeyData = [NSData dataWithBytes:bobSessionRecordRootKey length:32];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte alicePlaintext [] = {(Byte) 0x54, (Byte) 0x68, (Byte) 0x69, (Byte) 0x73,
|
||||
(Byte) 0x20, (Byte) 0x69, (Byte) 0x73, (Byte) 0x20,
|
||||
@ -610,7 +644,10 @@
|
||||
(Byte) 0xC3, (Byte) 0xA6, (Byte) 0x96, (Byte) 0x06,
|
||||
(Byte) 0xAB, (Byte) 0xBE, (Byte) 0x2E, (Byte) 0x31,
|
||||
(Byte) 0x63, (Byte) 0x88};
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
NSData *AliceSerializedWhisperMessageData = [NSData dataWithBytes:AliceSerializedWhisperMessage length:82];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
Byte aliceCipherText [] = {(Byte) 0x9E, (Byte) 0xF2, (Byte) 0xD0, (Byte) 0xE1,
|
||||
(Byte) 0x30, (Byte) 0x4C, (Byte) 0x01, (Byte) 0xE0,
|
||||
@ -621,22 +658,27 @@
|
||||
(Byte) 0x8D, (Byte) 0xF7, (Byte) 0x22, (Byte) 0xDC,
|
||||
(Byte) 0x22, (Byte) 0x76, (Byte) 0xC3, (Byte) 0xA6};
|
||||
NSData *aliceCipherTextData = [NSData dataWithBytes:aliceCipherText length:32];
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey = [ECKeyPair keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey = [ECKeyPair keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey = [ECKeyPair keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey = [ECKeyPair keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet = [ECKeyPair keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
|
||||
|
||||
ECKeyPair *aliceIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceIdentityPrivateKeyData publicKey:aliceIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *bobIdentityKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobIdentityPrivateKeyData publicKey:bobIdentityPublicKeyData];
|
||||
|
||||
ECKeyPair *aliceBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceBasePrivateKeyData publicKey:aliceBasePublicKeyData];
|
||||
|
||||
ECKeyPair *bobBaseKey =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:bobBasePrivateKeyData publicKey:bobBasePublicKeyData];
|
||||
|
||||
ECKeyPair *aliceSendingRatchet =
|
||||
[ECKeyPair throws_keyPairWithPrivateKey:aliceSendingRatchetPrivateData publicKey:aliceSendingRatchetPublicData];
|
||||
|
||||
// ---
|
||||
|
||||
AxolotlInMemoryStore *aliceStore = [AxolotlInMemoryStore new];
|
||||
AxolotlInMemoryStore *bobStore = [AxolotlInMemoryStore new];
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
@ -644,11 +686,16 @@
|
||||
AliceAxolotlParameters *aliceAxolotlParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKey theirIdentityKey:bobIdentityKey.publicKey ourBaseKey:aliceBaseKey theirSignedPreKey:bobBaseKey.publicKey theirOneTimePreKey:nil theirRatchetKey:bobBaseKey.publicKey];
|
||||
|
||||
BobAxolotlParameters *bobAxolotlParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKey theirIdentityKey:aliceIdentityKey.publicKey ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:nil theirBaseKey:aliceBaseKey.publicKey];
|
||||
|
||||
[RatchetingSession initializeSession:aliceSessionRecord.sessionState sessionVersion:3 AliceParameters:aliceAxolotlParams senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession initializeSession:bobSessionRecord.sessionState sessionVersion:3 BobParameters:bobAxolotlParams];
|
||||
|
||||
|
||||
[RatchetingSession throws_initializeSession:aliceSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
AliceParameters:aliceAxolotlParams
|
||||
senderRatchet:aliceSendingRatchet];
|
||||
|
||||
[RatchetingSession throws_initializeSession:bobSessionRecord.sessionState
|
||||
sessionVersion:3
|
||||
BobParameters:bobAxolotlParams];
|
||||
|
||||
NSString *aliceIdentifier = @"+483294823482";
|
||||
NSString *bobIdentifier = @"+389424728942";
|
||||
|
||||
@ -656,26 +703,29 @@
|
||||
XCTAssert([[@"This is a plaintext message." dataUsingEncoding:NSUTF8StringEncoding] isEqualToData:alicePlaintextData], @"Encoding is not correct");
|
||||
XCTAssert([aliceSessionRecord.sessionState.rootKey.keyData isEqualToData:aliceSessionRecordRootKeyData]);
|
||||
XCTAssert([aliceSessionRecord.sessionState.senderChainKey.key isEqualToData:aliceSendingChainKeyData]);
|
||||
XCTAssert([aliceSendingCipherKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.cipherKey]);
|
||||
XCTAssert([aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord];
|
||||
XCTAssert([aliceSendingCipherKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.cipherKey]);
|
||||
XCTAssert(
|
||||
[aliceSendingIVKeyData isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.iv]);
|
||||
XCTAssert([aliceSendingMacKeyData
|
||||
isEqualToData:aliceSessionRecord.sessionState.senderChainKey.throws_messageKeys.macKey]);
|
||||
|
||||
[aliceStore storeSession:bobIdentifier deviceId:1 session:aliceSessionRecord protocolContext:nil];
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:bobIdentifier deviceId:1];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher encryptMessage:alicePlaintextData];
|
||||
|
||||
WhisperMessage *message = [aliceSessionCipher throws_encryptMessage:alicePlaintextData protocolContext:nil];
|
||||
XCTAssert([aliceCipherTextData isEqualToData:message.cipherText]);
|
||||
|
||||
// Logging's Bob's Session initialization and first message decryption
|
||||
|
||||
XCTAssert([bobRootKeyData isEqualToData:bobSessionRecord.sessionState.rootKey.keyData]);
|
||||
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord];
|
||||
[bobStore storeSession:aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *plainData = [bobSessionCipher decrypt:message];
|
||||
|
||||
|
||||
NSData *plainData = [bobSessionCipher throws_decrypt:message protocolContext:nil];
|
||||
|
||||
XCTAssert([plainData isEqualToData:alicePlaintextData]);
|
||||
|
||||
|
||||
@ -685,11 +735,12 @@
|
||||
for (int i = 0 ; i < 30; i++) {
|
||||
NSData *message = [[NSString stringWithFormat:@"Message: %i", i] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[plainTexts addObject:message];
|
||||
[cipherMessages addObject:[bobSessionCipher encryptMessage:message]];
|
||||
[cipherMessages addObject:[bobSessionCipher throws_encryptMessage:message protocolContext:nil]];
|
||||
}
|
||||
|
||||
for (NSUInteger i = plainTexts.count-1; i > 0; i--) {
|
||||
XCTAssert([[aliceSessionCipher decrypt:[cipherMessages objectAtIndex:i]] isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
XCTAssert([[aliceSessionCipher throws_decrypt:[cipherMessages objectAtIndex:i] protocolContext:nil]
|
||||
isEqualToData:[plainTexts objectAtIndex:i]]);
|
||||
}
|
||||
|
||||
}
|
||||
164
AxolotlKitTests/SessionBuilderTests.m
Normal file
164
AxolotlKitTests/SessionBuilderTests.m
Normal file
@ -0,0 +1,164 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AxolotlExceptions.h>
|
||||
#import <AxolotlKit/NSData+keyVersionByte.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <AxolotlKit/SessionBuilder.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
#import <Curve25519Kit/Ed25519.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface PreKeyWhisperMessage ()
|
||||
|
||||
@property (nonatomic, readwrite) NSData *identityKey;
|
||||
@property (nonatomic, readwrite) NSData *baseKey;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface SessionBuilderTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation SessionBuilderTests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing session initialization with a basic PrekeyWhisperMessage
|
||||
*/
|
||||
|
||||
- (void)testBasicPreKey {
|
||||
|
||||
NSString *BOB_RECIPIENT_ID = @"+3828923892";
|
||||
NSString *ALICE_RECIPIENT_ID = @"alice@gmail.com";
|
||||
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
SessionBuilder *aliceSessionBuilder = [[SessionBuilder alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
ECKeyPair *bobPreKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature =
|
||||
[Ed25519 throws_sign:bobSignedPreKeyPair.publicKey.prependKeyType withKeyPair:[bobStore identityKeyPair:nil]];
|
||||
|
||||
PreKeyBundle *bobPreKey = [[PreKeyBundle alloc]initWithRegistrationId:[bobStore localRegistrationId:nil]
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair.publicKey.prependKeyType
|
||||
signedPreKeyPublic:bobSignedPreKeyPair.publicKey.prependKeyType
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature
|
||||
identityKey:[bobStore identityKeyPair:nil].publicKey.prependKeyType];
|
||||
|
||||
[aliceSessionBuilder throws_processPrekeyBundle:bobPreKey protocolContext:nil];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.version == 3);
|
||||
|
||||
NSString *originalMessage = @"Freedom is the right to tell people what they do not want to hear.";
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
WhisperMessage *outgoingMessage =
|
||||
[aliceSessionCipher throws_encryptMessage:[originalMessage dataUsingEncoding:NSUTF8StringEncoding]
|
||||
protocolContext:nil];
|
||||
|
||||
XCTAssert([outgoingMessage isKindOfClass:[PreKeyWhisperMessage class]], @"Message should be PreKey type");
|
||||
|
||||
PreKeyWhisperMessage *incomingMessage = (PreKeyWhisperMessage*)outgoingMessage;
|
||||
[bobStore storePreKey:31337 preKeyRecord:[[PreKeyRecord alloc] initWithId:bobPreKey.preKeyId
|
||||
keyPair:bobPreKeyPair
|
||||
createdAt:[NSDate date]]
|
||||
protocolContext:nil];
|
||||
[bobStore storeSignedPreKey:22
|
||||
signedPreKeyRecord:[[SignedPreKeyRecord alloc] initWithId:22
|
||||
keyPair:bobSignedPreKeyPair
|
||||
signature:bobSignedPreKeySignature
|
||||
generatedAt:[NSDate date]]
|
||||
protocolContext:nil];
|
||||
|
||||
SessionCipher *bobSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:bobStore recipientId:ALICE_RECIPIENT_ID deviceId:1];
|
||||
[bobSessionCipher throws_decrypt:incomingMessage protocolContext:nil];
|
||||
|
||||
XCTAssert([bobStore containsSession:ALICE_RECIPIENT_ID deviceId:1 protocolContext:nil]);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.version == 3);
|
||||
XCTAssert([bobStore loadSession:ALICE_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.aliceBaseKey != nil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the case where an attacker would send a new PreKeyWhisperMessage with another IdentityKey
|
||||
*/
|
||||
|
||||
- (void)testBasicPreKeyMITM {
|
||||
|
||||
NSString *BOB_RECIPIENT_ID = @"+3828923892";
|
||||
|
||||
SPKMockProtocolStore *aliceStore = [SPKMockProtocolStore new];
|
||||
SessionBuilder *aliceSessionBuilder = [[SessionBuilder alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
SPKMockProtocolStore *bobStore = [SPKMockProtocolStore new];
|
||||
ECKeyPair *bobIdentityKeyPair1 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobPreKeyPair1 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair1 = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature1 =
|
||||
[Ed25519 throws_sign:bobSignedPreKeyPair1.publicKey.prependKeyType withKeyPair:bobIdentityKeyPair1];
|
||||
|
||||
PreKeyBundle *bobPreKey1 = [[PreKeyBundle alloc] initWithRegistrationId:[bobStore localRegistrationId:nil]
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair1.publicKey.prependKeyType
|
||||
signedPreKeyPublic:bobSignedPreKeyPair1.publicKey.prependKeyType
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature1
|
||||
identityKey:bobIdentityKeyPair1.publicKey.prependKeyType];
|
||||
|
||||
[aliceSessionBuilder throws_processPrekeyBundle:bobPreKey1 protocolContext:nil];
|
||||
|
||||
XCTAssert([aliceStore containsSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil]);
|
||||
XCTAssert([aliceStore loadSession:BOB_RECIPIENT_ID deviceId:1 protocolContext:nil].sessionState.version == 3);
|
||||
|
||||
NSString *messageText = @"Freedom is the right to tell people what they do not want to hear.";
|
||||
SessionCipher *aliceSessionCipher = [[SessionCipher alloc] initWithAxolotlStore:aliceStore recipientId:BOB_RECIPIENT_ID deviceId:1];
|
||||
|
||||
WhisperMessage *outgoingMessage1 =
|
||||
[aliceSessionCipher throws_encryptMessage:[messageText dataUsingEncoding:NSUTF8StringEncoding]
|
||||
protocolContext:nil];
|
||||
|
||||
XCTAssert([outgoingMessage1 isKindOfClass:[PreKeyWhisperMessage class]], @"Message should be PreKey type");
|
||||
|
||||
ECKeyPair *bobIdentityKeyPair2 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobPreKeyPair2 = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobSignedPreKeyPair2 = [Curve25519 generateKeyPair];
|
||||
NSData *bobSignedPreKeySignature2 =
|
||||
[Ed25519 throws_sign:bobSignedPreKeyPair2.publicKey.prependKeyType withKeyPair:bobIdentityKeyPair2];
|
||||
|
||||
PreKeyBundle *bobPreKey2 = [[PreKeyBundle alloc] initWithRegistrationId:[bobStore localRegistrationId:nil]
|
||||
deviceId:1
|
||||
preKeyId:31337
|
||||
preKeyPublic:bobPreKeyPair2.publicKey.prependKeyType
|
||||
signedPreKeyPublic:bobSignedPreKeyPair2.publicKey.prependKeyType
|
||||
signedPreKeyId:22
|
||||
signedPreKeySignature:bobSignedPreKeySignature2
|
||||
identityKey:bobIdentityKeyPair2.publicKey.prependKeyType];
|
||||
|
||||
XCTAssertThrowsSpecificNamed([aliceSessionBuilder throws_processPrekeyBundle:bobPreKey2 protocolContext:nil],
|
||||
NSException,
|
||||
UntrustedIdentityKeyException);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
138
AxolotlKitTests/SessionCipherTest.m
Normal file
138
AxolotlKitTests/SessionCipherTest.m
Normal file
@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AliceAxolotlParameters.h>
|
||||
#import <AxolotlKit/BobAxolotlParameters.h>
|
||||
#import <AxolotlKit/ChainKey.h>
|
||||
#import <AxolotlKit/RatchetingSession.h>
|
||||
#import <AxolotlKit/SPKMockProtocolStore.h>
|
||||
#import <AxolotlKit/SessionBuilder.h>
|
||||
#import <AxolotlKit/SessionCipher.h>
|
||||
#import <AxolotlKit/SessionState.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface SessionRecord (Private)
|
||||
- (void)promoteState:(SessionState *)promotedState;
|
||||
@end
|
||||
|
||||
@interface SessionCipherTest : XCTestCase
|
||||
|
||||
@property (nonatomic, readonly) NSString *aliceIdentifier;
|
||||
@property (nonatomic, readonly) NSString *bobIdentifier;
|
||||
@property (nonatomic, readonly) SPKMockProtocolStore *aliceStore;
|
||||
@property (nonatomic, readonly) SPKMockProtocolStore *bobStore;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SessionCipherTest
|
||||
|
||||
- (NSString *)aliceIdentifier
|
||||
{
|
||||
return @"+3728378173821";
|
||||
}
|
||||
|
||||
- (NSString *)bobIdentifier
|
||||
{
|
||||
return @"bob@gmail.com";
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_aliceStore = [SPKMockProtocolStore new];
|
||||
_bobStore = [SPKMockProtocolStore new];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testBasicSession{
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
|
||||
[self throws_sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
|
||||
}
|
||||
|
||||
- (void)testPromotingOldSessionState
|
||||
{
|
||||
SessionRecord *aliceSessionRecord = [SessionRecord new];
|
||||
SessionRecord *bobSessionRecord = [SessionRecord new];
|
||||
|
||||
// 1.) Given Alice and Bob have initialized some session together
|
||||
SessionState *initialSessionState = bobSessionRecord.sessionState;
|
||||
[self throws_sessionInitializationWithAliceSessionRecord:aliceSessionRecord bobSessionRecord:bobSessionRecord];
|
||||
|
||||
SessionRecord *activeSession = [self.bobStore loadSession:self.aliceIdentifier deviceId:1 protocolContext:nil];
|
||||
XCTAssertNotNil(activeSession);
|
||||
XCTAssertEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
|
||||
// 2.) If for some reason, bob has promoted a different session...
|
||||
SessionState *newSessionState = [SessionState new];
|
||||
[bobSessionRecord promoteState:newSessionState];
|
||||
XCTAssertEqual(1, bobSessionRecord.previousSessionStates.count);
|
||||
[self.bobStore storeSession:self.aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
|
||||
activeSession = [self.bobStore loadSession:self.aliceIdentifier deviceId:1 protocolContext:nil];
|
||||
XCTAssertNotNil(activeSession);
|
||||
XCTAssertNotEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
XCTAssertEqualObjects(newSessionState, activeSession.sessionState);
|
||||
|
||||
// 3.) Bob should decrypt with initial session after receiving a message from that old session,
|
||||
// but importantly *not* promote it to be the active session.
|
||||
[self runInteractionWithAliceRecord:aliceSessionRecord bobRecord:bobSessionRecord];
|
||||
XCTAssertNotEqualObjects(initialSessionState, activeSession.sessionState);
|
||||
XCTAssertEqualObjects(newSessionState, activeSession.sessionState);
|
||||
XCTAssertEqual(1, bobSessionRecord.previousSessionStates.count);
|
||||
XCTAssertEqual(0, aliceSessionRecord.previousSessionStates.count);
|
||||
}
|
||||
|
||||
- (void)throws_sessionInitializationWithAliceSessionRecord:(SessionRecord *)aliceSessionRecord
|
||||
bobSessionRecord:(SessionRecord *)bobSessionRecord
|
||||
{
|
||||
|
||||
SessionState *aliceSessionState = aliceSessionRecord.sessionState;
|
||||
SessionState *bobSessionState = bobSessionRecord.sessionState;
|
||||
|
||||
ECKeyPair *aliceIdentityKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *aliceBaseKey = [Curve25519 generateKeyPair];
|
||||
|
||||
ECKeyPair *bobIdentityKeyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobBaseKey = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *bobOneTimePK = [Curve25519 generateKeyPair];
|
||||
|
||||
AliceAxolotlParameters *aliceParams = [[AliceAxolotlParameters alloc] initWithIdentityKey:aliceIdentityKeyPair theirIdentityKey:[bobIdentityKeyPair publicKey] ourBaseKey:aliceBaseKey theirSignedPreKey:[bobBaseKey publicKey] theirOneTimePreKey:[bobOneTimePK publicKey] theirRatchetKey:[bobBaseKey publicKey]];
|
||||
|
||||
BobAxolotlParameters *bobParams = [[BobAxolotlParameters alloc] initWithMyIdentityKeyPair:bobIdentityKeyPair theirIdentityKey:[aliceIdentityKeyPair publicKey] ourSignedPrekey:bobBaseKey ourRatchetKey:bobBaseKey ourOneTimePrekey:bobOneTimePK theirBaseKey:[aliceBaseKey publicKey]];
|
||||
|
||||
[RatchetingSession throws_initializeSession:bobSessionState sessionVersion:3 BobParameters:bobParams];
|
||||
|
||||
[RatchetingSession throws_initializeSession:aliceSessionState sessionVersion:3 AliceParameters:aliceParams];
|
||||
|
||||
[self.aliceStore saveRemoteIdentity:bobIdentityKeyPair.publicKey recipientId:self.bobIdentifier protocolContext:nil];
|
||||
[self.aliceStore storeSession:self.bobIdentifier deviceId:1 session:aliceSessionRecord protocolContext:nil];
|
||||
|
||||
[self.bobStore saveRemoteIdentity:aliceIdentityKeyPair.publicKey recipientId:self.aliceIdentifier protocolContext:nil];
|
||||
[self.bobStore storeSession:self.aliceIdentifier deviceId:1 session:bobSessionRecord protocolContext:nil];
|
||||
|
||||
XCTAssert([aliceSessionState.remoteIdentityKey isEqualToData:bobSessionState.localIdentityKey]);
|
||||
}
|
||||
|
||||
- (void)runInteractionWithAliceRecord:(SessionRecord*)aliceSessionRecord bobRecord:(SessionRecord*)bobSessionRecord {
|
||||
SessionCipher *aliceSessionCipher =
|
||||
[[SessionCipher alloc] initWithAxolotlStore:self.aliceStore recipientId:self.bobIdentifier deviceId:1];
|
||||
SessionCipher *bobSessionCipher =
|
||||
[[SessionCipher alloc] initWithAxolotlStore:self.bobStore recipientId:self.aliceIdentifier deviceId:1];
|
||||
|
||||
NSData *alicePlainText = [@"This is a plaintext message!" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
WhisperMessage *cipherText = [aliceSessionCipher throws_encryptMessage:alicePlainText protocolContext:nil];
|
||||
|
||||
NSData *bobPlaintext = [bobSessionCipher throws_decrypt:cipherText protocolContext:nil];
|
||||
|
||||
XCTAssert([bobPlaintext isEqualToData:alicePlainText]);
|
||||
}
|
||||
|
||||
@end
|
||||
16
AxolotlKitTests/Util/ECKeyPair+ECKeyPairTesting.h
Normal file
16
AxolotlKitTests/Util/ECKeyPair+ECKeyPairTesting.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Curve25519.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ECKeyPair (ECKeyPairTesting)
|
||||
|
||||
+ (ECKeyPair *)throws_keyPairWithPrivateKey:(NSData *)privateKey
|
||||
publicKey:(NSData *)publicKey NS_SWIFT_UNAVAILABLE("throws objc exceptions");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
30
AxolotlKitTests/Util/ECKeyPair+ECKeyPairTesting.m
Normal file
30
AxolotlKitTests/Util/ECKeyPair+ECKeyPairTesting.m
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ECKeyPair+ECKeyPairTesting.h"
|
||||
#import "NSData+keyVersionByte.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation ECKeyPair (testing)
|
||||
|
||||
+ (ECKeyPair *)throws_keyPairWithPrivateKey:(NSData *)privateKey publicKey:(NSData *)publicKey
|
||||
{
|
||||
if (([publicKey length] == 33)) {
|
||||
publicKey = [publicKey throws_removeKeyType];
|
||||
}
|
||||
|
||||
if ([privateKey length] != ECCKeyLength && [publicKey length] != ECCKeyLength) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Public or Private key is not required size" userInfo:@{@"PrivateKey":privateKey, @"Public Key":publicKey}];
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
ECKeyPair *_Nullable keyPairCopy = [[ECKeyPair alloc] initWithPublicKeyData:[publicKey copy] privateKeyData:[privateKey copy] error:&error];
|
||||
OWSAssertDebug(error == nil && keyPairCopy != nil);
|
||||
return keyPairCopy;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
62
AxolotlKitTests/WhisperMessageSerialization.m
Normal file
62
AxolotlKitTests/WhisperMessageSerialization.m
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/WhisperMessage.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface WhisperMessageSerialization : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation WhisperMessageSerialization
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testWhisperMessage {
|
||||
ECKeyPair *keyPair = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *fakeMacKey = [Curve25519 generateKeyPair];
|
||||
int counter = 0;
|
||||
NSData *cipherText = [@"I'm not really ciphertext" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
ECKeyPair *senderIdentityKey = [Curve25519 generateKeyPair];
|
||||
ECKeyPair *receiverIdentityKey = [Curve25519 generateKeyPair];
|
||||
|
||||
WhisperMessage *message = [[WhisperMessage alloc] init_throws_withVersion:3
|
||||
macKey:fakeMacKey.publicKey
|
||||
senderRatchetKey:keyPair.publicKey
|
||||
counter:counter
|
||||
previousCounter:0
|
||||
cipherText:cipherText
|
||||
senderIdentityKey:senderIdentityKey.publicKey
|
||||
receiverIdentityKey:receiverIdentityKey.publicKey];
|
||||
|
||||
WhisperMessage *deserializedMessage = [[WhisperMessage alloc] init_throws_withData:message.serialized];
|
||||
|
||||
|
||||
XCTAssert([[message.serialized subdataWithRange:NSMakeRange(0, message.serialized.length-8)] isEqualToData:[deserializedMessage.serialized subdataWithRange:NSMakeRange(0, deserializedMessage.serialized.length-8)]]);
|
||||
|
||||
[message throws_verifyMacWithVersion:3
|
||||
senderIdentityKey:senderIdentityKey.publicKey
|
||||
receiverIdentityKey:receiverIdentityKey.publicKey
|
||||
macKey:fakeMacKey.publicKey];
|
||||
[deserializedMessage throws_verifyMacWithVersion:3
|
||||
senderIdentityKey:senderIdentityKey.publicKey
|
||||
receiverIdentityKey:receiverIdentityKey.publicKey
|
||||
macKey:fakeMacKey.publicKey];
|
||||
|
||||
XCTAssert([message.cipherText isEqualToData:deserializedMessage.cipherText]);
|
||||
XCTAssert(message.version == deserializedMessage.version);
|
||||
XCTAssert([message.serialized isEqualToData:deserializedMessage.serialized]);
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
5
Gemfile
Normal file
5
Gemfile
Normal file
@ -0,0 +1,5 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'cocoapods'
|
||||
gem 'fastlane'
|
||||
|
||||
201
Gemfile.lock
Normal file
201
Gemfile.lock
Normal file
@ -0,0 +1,201 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.0)
|
||||
activesupport (4.2.10)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
cocoapods (1.5.3)
|
||||
activesupport (>= 4.0.2, < 5)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.5.3)
|
||||
cocoapods-deintegrate (>= 1.0.2, < 2.0)
|
||||
cocoapods-downloader (>= 1.2.0, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-stats (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.3.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (~> 2.0.1)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.6.5)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (~> 1.1)
|
||||
xcodeproj (>= 1.5.7, < 2.0)
|
||||
cocoapods-core (1.5.3)
|
||||
activesupport (>= 4.0.2, < 6)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.2)
|
||||
cocoapods-downloader (1.2.2)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.0)
|
||||
cocoapods-stats (1.0.0)
|
||||
cocoapods-trunk (1.3.1)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
concurrent-ruby (1.1.3)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.5.0)
|
||||
emoji_regex (0.1.1)
|
||||
escape (0.0.4)
|
||||
excon (0.62.0)
|
||||
faraday (0.15.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (0.12.2)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.4)
|
||||
fastlane (2.108.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 2.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (~> 0.1)
|
||||
excon (>= 0.45.0, < 1.0.0)
|
||||
faraday (~> 0.9)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.9)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.21.2, < 0.24.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
multi_json
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.2.2, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fourflusher (2.0.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.23.9)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.9)
|
||||
googleauth (0.6.7)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2018.0812)
|
||||
mini_magick (4.5.1)
|
||||
minitest (5.11.3)
|
||||
molinillo (0.6.6)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.6)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.0)
|
||||
netrc (0.11.0)
|
||||
os (1.0.0)
|
||||
plist (3.4.0)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (1.3.1)
|
||||
rubyzip (1.2.2)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.5)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (1.8.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
tty-cursor (0.6.0)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.8.0)
|
||||
tty-cursor (>= 0.5.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.7.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
||||
846
LICENSE
846
LICENSE
@ -1,339 +1,621 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
0. Definitions.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
1. Source Code.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
2. Basic Permissions.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
14. Revised Versions of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
NO WARRANTY
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
17
Podfile
17
Podfile
@ -1,7 +1,14 @@
|
||||
platform :ios, '6.0'
|
||||
platform :ios, '8.0'
|
||||
|
||||
link_with ["AxolotlKit", "AxolotlKitTests"]
|
||||
use_frameworks!
|
||||
|
||||
target 'AxolotlKit' do
|
||||
pod 'AxolotlKit', path: '.'
|
||||
pod 'Curve25519Kit', git: 'https://github.com/WhisperSystems/25519.git', branch: 'mkirk/framework-friendly'
|
||||
pod 'HKDFKit', git: 'https://github.com/WhisperSystems/HKDFKit.git'
|
||||
|
||||
target 'AxolotlKitTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
pod '25519', '~> 1.8', :inhibit_warnings => true
|
||||
pod 'HKDFKit', '~> 0.0.3'
|
||||
pod 'ProtocolBuffers', '~> 1.9.7'
|
||||
|
||||
50
Podfile.lock
50
Podfile.lock
@ -1,16 +1,48 @@
|
||||
PODS:
|
||||
- 25519 (1.8)
|
||||
- AxolotlKit (0.9.0):
|
||||
- CocoaLumberjack
|
||||
- Curve25519Kit (~> 2.1.0)
|
||||
- HKDFKit (~> 0.0.3)
|
||||
- ProtocolBuffers (~> 1.9.8)
|
||||
- CocoaLumberjack (3.1.0):
|
||||
- CocoaLumberjack/Default (= 3.1.0)
|
||||
- CocoaLumberjack/Extensions (= 3.1.0)
|
||||
- CocoaLumberjack/Default (3.1.0)
|
||||
- CocoaLumberjack/Extensions (3.1.0):
|
||||
- CocoaLumberjack/Default
|
||||
- Curve25519Kit (2.1.0)
|
||||
- HKDFKit (0.0.3)
|
||||
- ProtocolBuffers (1.9.7)
|
||||
- ProtocolBuffers (1.9.11)
|
||||
|
||||
DEPENDENCIES:
|
||||
- 25519 (~> 1.8)
|
||||
- HKDFKit (~> 0.0.3)
|
||||
- ProtocolBuffers (~> 1.9.7)
|
||||
- AxolotlKit (from `.`)
|
||||
- Curve25519Kit (from `https://github.com/WhisperSystems/25519.git`, branch `mkirk/framework-friendly`)
|
||||
- HKDFKit (from `https://github.com/WhisperSystems/HKDFKit.git`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
AxolotlKit:
|
||||
:path: .
|
||||
Curve25519Kit:
|
||||
:branch: mkirk/framework-friendly
|
||||
:git: https://github.com/WhisperSystems/25519.git
|
||||
HKDFKit:
|
||||
:git: https://github.com/WhisperSystems/HKDFKit.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
Curve25519Kit:
|
||||
:commit: 03a19c80aafc10a3464f0c086b1eb38239c507ac
|
||||
:git: https://github.com/WhisperSystems/25519.git
|
||||
HKDFKit:
|
||||
:commit: 6f9f183da3370c902344dd1f444ec0e0387a833f
|
||||
:git: https://github.com/WhisperSystems/HKDFKit.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
25519: 601ffb5d258aa33d642062d6fa4096db210e02e7
|
||||
HKDFKit: 5998cf1bbb611e7ecc6bd3eaaef8c7a7da7be949
|
||||
ProtocolBuffers: 37fdd327d36856f965ea9029b5ec0c7120e3a452
|
||||
AxolotlKit: 7cfd2e71329b8dc398734e95b936450a2081fd60
|
||||
CocoaLumberjack: 8311463ddf9ee86a06ef92a071dd656c89244500
|
||||
Curve25519Kit: 76d0859ecb34704f7732847812363f83b23a6a59
|
||||
HKDFKit: a7e8b8661f2a137e5d50a9e2197c22d538ae52d9
|
||||
ProtocolBuffers: d509225eb2ea43d9582a59e94348fcf86e2abd65
|
||||
|
||||
COCOAPODS: 0.35.0
|
||||
PODFILE CHECKSUM: c2e75658e321816107a217002a406b7ddc41c79a
|
||||
|
||||
COCOAPODS: 1.3.1
|
||||
|
||||
29
README.md
29
README.md
@ -1,28 +1,25 @@
|
||||
# AxolotlKit [](https://travis-ci.org/WhisperSystems/AxolotlKit)
|
||||
**Deprecation Warning**: It is recommended that the swift interface of [libsignal-client](https://github.com/signalapp/libsignal-client) be used for all new iOS applications. This library is no longer used by us or maintained.
|
||||
|
||||
AxolotlKit is a free (as in Freedom) implementation of the Axolotl protocol, written in Objective-C.
|
||||
|
||||

|
||||
# SignalProtocolKit [](https://travis-ci.org/WhisperSystems/AxolotlKit)
|
||||
|
||||
SignalProtocolKit is an implementation of the Signal protocol, written in Objective-C.
|
||||
|
||||
## Documentation
|
||||
|
||||
Browse the [API reference](http://cocoadocs.org/docsets/AxolotlKit/) on CocoaDocs.
|
||||
|
||||
### Status
|
||||
|
||||
Axolotl is currently being reviewed. All scrutiny welcome. Once ready, it will be distributed via CocoaPods.
|
||||
|
||||
## Goal
|
||||
|
||||
AxolotlKit was designed to be a drop-in library that can be easily integrated into existing projects.
|
||||
SignalProtocolKit was designed to be a drop-in library that can be easily integrated into existing projects.
|
||||
|
||||
Axolotl is an [asynchronous cryptographic ratcheting protocol](https://github.com/trevp/axolotl/wiki) [with interesting properties](https://github.com/WhisperSystems/TextSecure/wiki/ProtocolV2).
|
||||
The Signal protocol is an [asynchronous cryptographic ratcheting protocol](https://whispersystems.org/blog/advanced-ratcheting/) [with interesting properties](https://whispersystems.org/blog/asynchronous-security/).
|
||||
|
||||
## Integration
|
||||
|
||||
AxolotlKit was designed with enough abstraction to integrate it easily into your own project. Please refer to the documentation or the TextSecure example to properly implement the required Objective-C storage protocols.
|
||||
SignalProtocolKit was designed with enough abstraction to integrate it easily into your own project. Please refer to the documentation or the Signal example to properly implement the required Objective-C storage protocols.
|
||||
|
||||
Unlike OTR, Axolotl is designed for long-lived sessions, keys need to be stored. AxolotlKit defines interfaces of the storage classes (`IdentityKeyStore.h`, `PreKeyStore.h`, `SessionStore.h` and `SignedPreKeyStore.h`). AxolotlKit objects do comply to `NSSecureCoding` so serialization of objects for the database is provided for you.
|
||||
Signal Protocol is designed for long-lived sessions, keys need to be stored. SignalProtocolKit defines interfaces of the storage classes (`IdentityKeyStore.h`, `PreKeyStore.h`, `SessionStore.h` and `SignedPreKeyStore.h`). SignalProtocolKit objects do comply to `NSSecureCoding` so serialization of objects for the database is provided for you.
|
||||
|
||||
### Prekeys
|
||||
|
||||
@ -33,7 +30,7 @@ At install time, clients generate a single signed PreKey as well as a large list
|
||||
|
||||
### Sessions
|
||||
|
||||
The Axolotl protocol is session-oriented. Clients establish a "session," which is then used for all subsequent encrypt/decrypt operations. There is no need to ever tear down a session once one has been established.
|
||||
The Signal protocol is session-oriented. Clients establish a "session," which is then used for all subsequent encrypt/decrypt operations. There is no need to ever tear down a session once one has been established.
|
||||
|
||||
Sessions are established in one of these ways:
|
||||
|
||||
@ -55,11 +52,3 @@ State is kept in the following places:
|
||||
|
||||
- Session State. Clients will need to maintain the state of the sessions they have established.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: Will you release a Swift implementation of AxolotlKit too?
|
||||
A: If Swift ends up being a good language for cryptographic applications, that will be considered. It’s still too early to make that call now.
|
||||
|
||||
## Credit
|
||||
|
||||
Thanks to Trevor Perrin and Moxie Marlinspike for the amazing work on the Axolotl protocol and original implementation. Thanks to [Conor Heelan](http://www.conorheelan.com/) for the Axolotl illustration.
|
||||
|
||||
8
Scanfile
Normal file
8
Scanfile
Normal file
@ -0,0 +1,8 @@
|
||||
# For more information about this configuration visit
|
||||
# https://github.com/fastlane/fastlane/tree/master/scan#scanfile
|
||||
|
||||
# In general, you can use the options available
|
||||
# scan --help
|
||||
|
||||
scheme "AxolotlKit"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user