diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d578111 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +Pods/* +Libraries/bin/* +Libraries/include/* +*.lock +Libraries/src/* +.DS_Store +*.swp +.Trashes +*~.nib +xcuserdata +Libraries/lib/* +*.xccheckout +Build/* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70ee415 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: objective-c +before_install: + - export LANG=en_US.UTF-8 + - gem install cocoapods --no-ri --no-rdoc +xcode_workspace: TextSecureiOS.xcworkspace +xcode_scheme: TextSecureiOS +xcode_sdk: iphonesimulator + diff --git a/Builds/TextSecureiOS-unencryptedpipeline.ipa b/Builds/TextSecureiOS-unencryptedpipeline.ipa new file mode 100644 index 0000000..28adbf9 Binary files /dev/null and b/Builds/TextSecureiOS-unencryptedpipeline.ipa differ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2d1e360 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +## Contribute + +At this early stage there are two primary developers of TextSecure iOS: [@corbett](https://github.com/corbett) and [@FredericJacobs](https://github.com/fredericjacobs) and as we move from the early stages to more advanced ones opportunities will abound to contribute to the core code base. We are particularly interested in support and contributions associated localization, code review, and automated testing, with any of the [Open Issues](https://github.com/WhisperSystems/TextSecure-iOS/issues?state=open) or [Milestones](https://github.com/WhisperSystems/TextSecure-iOS/issues/milestones?state=open). It may help to open an issue or milestone if you plan to make a contribution so we can be sure to clarify questions, give an overview of architectural plans, and ensure we do not overlap work. +## Contributor agreement + +Apple requires contributors to iOS projects to relicense their code on submit. We'll have to have individual contributors sign something to enable this. + +Our volunteer legal have put together a form you can sign electronically. So no scanning, faxing, or carrier pigeons involved. How modern: +https://whispersystems.org/cla/ + +Please go ahead and sign, putting your github username in "Address line #2", so that we can accept your pull requests at our heart's delight. + +## Code Conventions + +We are trying to follow the [GitHub code conventions for Objective-C](https://github.com/github/objective-c-conventions) and we appreciate that pull requests do conform with those conventions. + +In addition to that, always add curly braces to your `if` conditionals, even if there is no `else`. Booleans should be declared according to their Objective-C definition, and hence take `YES` or `NO` as values. + +One note, for programmers joining us from Java or similar language communities, note that [exceptions are not commonly used for errors that may occur in normal use](http://stackoverflow.com/questions/324284/throwing-an-exception-in-objective-c-cocoa/324805#324805) so familiarize yourself with **NSError** + +###UI conventions +We prefer to use [Storyboards](https://developer.apple.com/library/ios/documentation/general/conceptual/Devpedia-CocoaApp/Storyboard.html) vs. building UI elements within the code itself. We are not at the stage to provide a .strings localizable file for translating, but the goal is to have translatable strings in a single entry point so that we can reach users in their native language wherever possible. + +Some tips +- any PR that does not use segues or story board conventions (red flags: ```[self.navigationController pushViewController:<#(UIViewController *)#> animated:<#(BOOL)#>]``` and/or manual creation of UI elements and/or orphaned ViewControllers in the storyboard) will need to be refactored prior to merge +- the following are the storyboarder's best friends: + +```- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;``` + +``` [self performSegueWithIdentifier:<#(NSString *)#> sender:<#(id)#>];``` + +## Tabs vs Spaces + +It's the eternal debate. We chose to adopt spaces. Please set your default Xcode configuration to 4 spaces for tabs, and 4 spaces for indentation (it's Xcode's default setting). + +![Tabs vs Spaces](http://cl.ly/TYPZ/Screen%20Shot%202014-01-26%20at%2019.02.28.png) + +If you don't agree with us, you can use the [ClangFormat Xcode plugin](https://github.com/travisjeffery/ClangFormat-Xcode) to code with your favorite indentation style! + +## PR conventions +If you are thinking about a major change, to speed up the merge talk with the lead developers first. PRs will not be accepted which break the master branch, so make sure to try, in addition to your unit tests, a simple "regression" test: register, send some messages back and forth, view a thread and conversation and make sure every is just as functional as before your PR only more so. + +## BitHub + +Open Whisper Systems is currently [experimenting](https://whispersystems.org/blog/bithub/) with the funding privacy Free and Open Source software. Payments are opt-in for the `TextSecure-iOS` repo and can be enabled by adding `MONEYMONEY` in a commit message string. For example, this is the current Open WhisperSystems payout per commit, rendered dynamically as an image by the Open WhisperSystems BitHub instance: + +[![Bithub Payment Amount](https://bithub.herokuapp.com/v1/status/payment/commit)](https://whispersystems.org/blog/bithub/) + +## Contributors + +We would like to particularly thank the following contributors: + +- Dylan Bourgeois: Substantial UI/UX Improvements +- Christine Corbett: Lead Developer +- Alban Diquet: Substantial contributions to the storage infrastructure +- Frederic Jacobs: Lead Developer +- Claudiu-Vlad Ursache: UI contributions + +TextSecure wouldn’t be possible without the many open-source projects we depend on. Big shoutout to the maintainers of all the [pods](https://github.com/WhisperSystems/TextSecure-iOS/blob/master/Podfile) we use! diff --git a/TextSecureiOS/Default-568h@2x.png b/Default-568h@2x.png similarity index 100% rename from TextSecureiOS/Default-568h@2x.png rename to Default-568h@2x.png diff --git a/TextSecureiOS/TextSecureiOSAssets/encyclopedia_1024 icon.psd b/Documentation/Axolotl-slides.pdf similarity index 52% rename from TextSecureiOS/TextSecureiOSAssets/encyclopedia_1024 icon.psd rename to Documentation/Axolotl-slides.pdf index 86d9921..fe65357 100644 Binary files a/TextSecureiOS/TextSecureiOSAssets/encyclopedia_1024 icon.psd and b/Documentation/Axolotl-slides.pdf differ diff --git a/Documentation/README.md b/Documentation/README.md new file mode 100644 index 0000000..559d8a4 --- /dev/null +++ b/Documentation/README.md @@ -0,0 +1,5 @@ +Protocol descriptions available + * https://github.com/WhisperSystems/TextSecure/wiki/ProtocolV2 + * https://github.com/WhisperSystems/TextSecure-Server/wiki/API-Protocol + * https://www.whispersystems.org/blog/advanced-ratcheting/ + * https://github.com/trevp/axolotl/wiki diff --git a/Libraries/README.md b/Libraries/README.md new file mode 100644 index 0000000..1432eee --- /dev/null +++ b/Libraries/README.md @@ -0,0 +1,8 @@ +# Build scripts + +You don't need the build scripts provided in this folder to start contributing thanks to the magic of `Cocoapods`. But for security reasons (App Store builds where verifying integrity of dependencies is important) and debugging reasons, we keep the build scripts of this folder. + +## [Curve25519 - Donna implementation](https://github.com/agl/curve25519-donna) + +## [OpenSSL Website](http://www.openssl.org) +OpenSSL is currently not used in this project and if it's a dependency we can avoid, that would be better. Because yeah, OpenSSL code ... \ No newline at end of file diff --git a/Libraries/RNCryptor b/Libraries/RNCryptor deleted file mode 160000 index af3b9d7..0000000 --- a/Libraries/RNCryptor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit af3b9d7a1c2c91b5ea2631c6ea388367b266e918 diff --git a/Libraries/build-libcurve25519.sh b/Libraries/build-libcurve25519.sh new file mode 100755 index 0000000..c0f7dc3 --- /dev/null +++ b/Libraries/build-libcurve25519.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Automatic build script for curve25519-donna for iPhoneOS and iPhoneSimulator +# Created by Christine Corbett Moran 11/30/2013 +# +# +########################################################################### +# Change values here # +# # +SDKVERSION="7.0" # +# # +# Probably shouldn't need to change anything under here + +CURRENTPATH=`pwd` +CFLAGS="-Wmissing-prototypes -Wdeclaration-after-statement -O2 -Wall" +ARCHS="i386 armv7 armv7s" +DEVELOPER=`xcode-select -print-path` + +mkdir -p "${CURRENTPATH}/src" +mkdir -p "${CURRENTPATH}/lib" + +cd "${CURRENTPATH}/src/" +git clone https://github.com/agl/curve25519-donna.git +cd "${CURRENTPATH}/src/curve25519-donna/" + +for ARCH in ${ARCHS} +do + make clean + if [ "${ARCH}" == "i386" ]; + then + PLATFORM="iPhoneSimulator" + else + PLATFORM="iPhoneOS" + fi + export DEVELOPER_PLATFORM="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer" + export SDK="${PLATFORM}${SDKVERSION}.sdk" + echo "Building curve25519-donna for ${PLATFORM} ${SDKVERSION} ${ARCH}" + export CC="/Applications/Xcode.app/Contents/Developer/usr/bin/gcc -arch ${ARCH} -miphoneos-version-min=7.0 -isysroot ${DEVELOPER_PLATFORM}/SDKs/${SDK} ${CFLAGS}" + mkdir -p "${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk" + LOG="${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk/curve25519-donna.log" + $CC -c curve25519-donna.c -m32 curve25519-donna.c >> "${LOG}" 2>&1 + ar -rc curve25519-donna.a curve25519-donna.o >> "${LOG}" 2>&1 + ranlib curve25519-donna.a >> "${LOG}" 2>&1 + mv curve25519-donna.a ${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk/curve25519-donna.a +done + +echo "Build library for ${ARCHS}..." +lipo -create ${CURRENTPATH}/bin/iPhoneSimulator${SDKVERSION}-i386.sdk/curve25519-donna.a ${CURRENTPATH}/bin/iPhoneOS${SDKVERSION}-armv7.sdk/curve25519-donna.a ${CURRENTPATH}/bin/iPhoneOS${SDKVERSION}-armv7s.sdk/curve25519-donna.a -output ${CURRENTPATH}/lib/curve25519-donna.a + diff --git a/Libraries/build-libssl.sh b/Libraries/build-libssl.sh old mode 100644 new mode 100755 index 264291f..90faf12 --- a/Libraries/build-libssl.sh +++ b/Libraries/build-libssl.sh @@ -22,7 +22,7 @@ # Change values here # # # VERSION="1.0.1e" # -SDKVERSION="6.1" # +SDKVERSION="7.0" # # # ########################################################################### # # @@ -57,6 +57,9 @@ DEVELOPER=`xcode-select -print-path` # mkdir -p "${CURRENTPATH}/lib" # # tar zxf openssl-${VERSION}.tar.gz -C "${CURRENTPATH}/src" + +mkdir -p "${CURRENTPATH}/lib" + cd "${CURRENTPATH}/src/openssl-${VERSION}" @@ -76,11 +79,11 @@ do echo "Building openssl-${VERSION} for ${PLATFORM} ${SDKVERSION} ${ARCH}" echo "Please stand by..." - export CC="${CROSS_TOP}/usr/bin/gcc -arch ${ARCH}" + export CC="/Applications/Xcode.app/Contents/Developer/usr/bin/gcc -arch ${ARCH} -miphoneos-version-min=7.0" mkdir -p "${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk" LOG="${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk/build-openssl-${VERSION}.log" - ./Configure iphoneos-cross --openssldir="${CURRENTPATH}/bin/${PLATFORM}${SDKVERSION}-${ARCH}.sdk" > "${LOG}" 2>&1 + ./Configure iphoneos-cross --openssldir="z > "${LOG}" 2>&1 # add -isysroot to CC= sed -ie "s!^CFLAG=!CFLAG=-isysroot ${CROSS_TOP}/SDKs/${CROSS_SDK} !" "Makefile" diff --git a/Podfile b/Podfile new file mode 100755 index 0000000..e501287 --- /dev/null +++ b/Podfile @@ -0,0 +1,37 @@ +platform :ios, '7.0' + +link_with ['TextSecureiOS', 'TextSecureiOS Tests'] + +pod 'RNCryptor', '~> 2.1' +pod 'FMDB', '~> 2.3' +pod 'HockeySDK', '~> 3.5.5' +pod 'libPhoneNumber-iOS', '~> 0.7.3' +pod 'AFNetworking', '~> 2.3.1' +pod 'SQLCipher', '~> 3.1.0' +pod 'GoogleProtobuf', '~> 2.5.0' +pod 'SWTableViewCell', '~> 0.3.0' +pod 'curve25519-donna', '~> 1.2.1' +pod 'UIImage-Categories', '~> 0.0.1' +pod 'JSMessagesViewController', '~> 3.4.4' +pod 'LBGIFImage', '~> 0.0.1' +pod 'Emoticonizer', '~> 1.0.0' +pod 'InAppSettingsKit', '~> 2.1' +pod 'HKDFKit', '~> 0.0.1' +pod 'RMStepsController', '~> 1.0.1' +pod 'Navajo', '~> 0.0.1' +pod 'SocketRocket', :podspec => "Podspecs/SocketRocket.podspec" + +link_with ['TextSecureiOS', 'TextSecureiOS Tests'] +post_install do |lib_rep| + lib_rep.project.targets.each do |target| + if target.name == 'Pods-FMDB' + target.build_configurations.each do |config| + if config.build_settings['OTHER_CFLAGS'].nil? + config.build_settings['OTHER_CFLAGS'] = Array.new + end + puts "Added -DSQLITE_HAS_CODEC CFlag to #{target.name} - #{config.name}" + config.build_settings['OTHER_CFLAGS'].unshift('-DSQLITE_HAS_CODEC') + end + end + end +end diff --git a/Podspecs/SocketRocket.podspec b/Podspecs/SocketRocket.podspec new file mode 100644 index 0000000..a0641cf --- /dev/null +++ b/Podspecs/SocketRocket.podspec @@ -0,0 +1,14 @@ +Pod::Spec.new do |s| + s.name = 'SocketRocket' + s.version = '0.3.2' + s.summary = 'A conforming WebSocket (RFC 6455) client library.' + s.homepage = 'https://github.com/square/SocketRocket' + s.authors = 'Square' + s.license = 'Apache License, Version 2.0' + s.source = { :git => 'https://github.com/FredericJacobs/SocketRocket.git', :tag => 'v0.3.2' } + s.source_files = 'SocketRocket/*.{h,m,c}' + s.requires_arc = true + s.ios.frameworks = %w{CFNetwork Security} + s.osx.frameworks = %w{CoreServices Security} + s.libraries = 'icucore' +end diff --git a/README.md b/README.md index b6aa73b..5f775fa 100644 --- a/README.md +++ b/README.md @@ -1 +1,91 @@ -TextSecure for iOS, currently in prototype stage. \ No newline at end of file +# TextSecure for iOS + +Currently in early development stage. Please see [Contributing](https://github.com/WhisperSystems/TextSecure-iOS/blob/master/CONTRIBUTING.md) for details how best to contribute. + +### This is a working directory. TextSecure will be the instant messaging part of [Signal](https://github.com/WhisperSystems/Signal-iOS) + +## Temporary notice + +The main Cocoapods repo got corrupted. Please [follow these instructions](http://blog.cocoapods.org/Repairing-Our-Broken-Specs-Repository/) for your next `pod update` + +## Building + +1) Clone the repo to a working directory + +2) [CocoaPods](http://cocoapods.org) is used to manage dependencies. Pods are setup easily and are distributed via a ruby gem. Follow the simple instructions on the website to setup. After setup, run the following command from the toplevel directory of TextSecureiOS to download the dependencies for TextSecure iOS: + +``` +pod install +``` +If you are having build issues, first make sure your pods are up to date +``` +pod update +pod install +``` +occasionally, CocoaPods itself will need to be updated. Do this with +``` +sudo gem update +``` + +3) Open the `TextSecureiOS.xcworkspace` in Xcode. **Note that for CocoaPods to work properly it is very important to always open the workspace and not the `.xcodeproj` file.** Build and Run and you are ready to go! + +4) Debugging network calls. If you are contributing networked code, PonyDebugger is integrated in Debug mode of the application. Check out https://github.com/square/PonyDebugger#quick-start and easily debug network code from the iOS simulator + +### Compile Error when building for 64-bit architecture + +Due to an issue in version 2.5.0 of the Google Protobuf Library the compiling fails when building the app for a 64-bit architecture (which is the case for the iPhone 5S) + +See the Google-Issue for this: https://code.google.com/p/protobuf/issues/detail?id=575. + +__However the specified Workaround in the Google Issue solves the compile errors__ + +## Certificate Pinning + +TextSecure uses certificate-pinning to avoid (wo)man-in-the-middle attacks. If you use your own server, here are the steps to generate the certificate file. + +1) Use OpenSSL to download the certificate (copy-paste the text between the `BEGIN` and `END` into a `cert.pem` file). + +```bash +openssl s_client -showcerts -connect textsecure-service.whispersystems.org:443 1.8 installed on your build machine, as well as the +"Android Support Repository" and "Google Repository" installed from the +Android SDK manager on your build machine. + +## Cryptography Notice + +This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. +BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. +See for more information. + +The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. +The form and manner of this distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code. + +## License + +Copyright 2013 Whisper Systems + +Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html diff --git a/Settings.bundle/Root.plist b/Settings.bundle/Root.plist new file mode 100644 index 0000000..fb72e4f --- /dev/null +++ b/Settings.bundle/Root.plist @@ -0,0 +1,94 @@ + + + + + PreferenceSpecifiers + + + Type + PSGroupSpecifier + Title + Security + + + Type + PSToggleSwitchSpecifier + Title + Screenshot Protection + Key + screenshotProtection + DefaultValue + + + + Type + PSGroupSpecifier + Title + Account + + + DefaultValue + TextSecure://?deregisterUserRequest + File + TextSecure://?deregisterUserRequest + Title + Deregister + Type + IASKOpenURLSpecifier + + + Type + PSGroupSpecifier + Title + Database + + + DefaultValue + TextSecure://?changePasswordRequest + File + TextSecure://?changePasswordRequest + Title + Reset Password + Type + IASKOpenURLSpecifier + + + KeyboardType + NumberPad + DefaultValue + 5 + Type + PSTextFieldSpecifier + Title + Lock database after (hrs) + Key + lockDBAfter + + + Title + Developer + Type + PSGroupSpecifier + + + DefaultValue + + Key + resetDB + Title + Reset Application + Type + PSToggleSwitchSpecifier + + + Title + + Type + PSGroupSpecifier + Title + + + StringsTable + Root + + diff --git a/Settings.bundle/en.lproj/Root.strings b/Settings.bundle/en.lproj/Root.strings new file mode 100644 index 0000000..8cd87b9 Binary files /dev/null and b/Settings.bundle/en.lproj/Root.strings differ diff --git a/TextSecureiOS Tests/CryptographyTests.mm b/TextSecureiOS Tests/CryptographyTests.mm new file mode 100644 index 0000000..f79d91e --- /dev/null +++ b/TextSecureiOS Tests/CryptographyTests.mm @@ -0,0 +1,93 @@ +// +// CryptographyTests.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/19/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#include +#import "Cryptography.h" +#import "NSData+Base64.h" +#import "NSString+Conversion.h" +#import "TSMessageSignal.hh" +#import "IncomingPushMessageSignal.pb.hh" +#import "TSEncryptedWhisperMessage.hh" +#import "TSPreKeyWhisperMessage.hh" +#import "TSPushMessageContent.hh" +@interface CryptographyTests : XCTestCase + +@end + +// To avoid + h files +@interface TSMessageSignal (Test) ++ (textsecure::IncomingPushMessageSignal *)deserialize:(NSData *)data; ++ (TSWhisperMessage*) getWhisperMessageForData:(NSData*) data ofType:(TSWhisperMessageType)contentType; +@end + +@interface Cryptography (Test) ++(NSData*) truncatedSHA256HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey truncation:(int)bytes; ++(NSData*)encryptCBCMode:(NSData*) dataToEncrypt withKey:(NSData*) key withIV:(NSData*) iv withVersion:(NSData*)version withHMACKey:(NSData*) hmacKey withHMACType:(TSMACType)hmacType computedHMAC:(NSData**)hmac; ++(NSData*) decryptCBCMode:(NSData*) dataToDecrypt withKey:(NSData*) key withIV:(NSData*) iv withVersion:(NSData*)version withHMACKey:(NSData*) hmacKey withHMACType:(TSMACType)hmacType forHMAC:(NSData *)hmac; +@end + +@implementation CryptographyTests + + +-(void) testLocalDecryption { + NSString* originalMessage = @"Hawaii is awesome"; + NSString* signalingKeyString = @"VJuRzZcwuY/6VjGw+QSPy5ROzHo8xE36mKwHNvkfyZ+mSPaDlSDcenUqavIX1Vwn\nRRIdrg=="; + NSData* signalingKey = [NSData dataFromBase64String:signalingKeyString]; + XCTAssertTrue([signalingKey length]==52, @"signaling key is not 52 bytes but %llu", (unsigned long long)[signalingKey length]); + NSData* signalingKeyAESKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(0, 32)]; + NSData* signalingKeyHMACKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(32, 20)]; + NSData* iv = [Cryptography generateRandomBytes:16]; + NSData* version = [Cryptography generateRandomBytes:1]; + NSData* mac; + //Encrypt + + NSData* encryption = [Cryptography encryptCBCMode:[originalMessage dataUsingEncoding:NSUTF8StringEncoding] withKey:signalingKeyAESKeyMaterial withIV:iv withVersion:version withHMACKey:signalingKeyHMACKeyMaterial withHMACType:TSHMACSHA1Truncated10Bytes computedHMAC:&mac]; //Encrypt + + NSMutableData *dataToHmac = [NSMutableData data ]; + [dataToHmac appendData:version]; + [dataToHmac appendData:iv]; + [dataToHmac appendData:encryption]; + + + NSData* expectedHmac = [Cryptography truncatedSHA1HMAC:dataToHmac withHMACKey:signalingKeyHMACKeyMaterial truncation:10]; + + XCTAssertTrue([mac isEqualToData:expectedHmac], @"Hmac of encrypted data %@, not equal to expected hmac %@", [mac base64EncodedString], [expectedHmac base64EncodedString]); + + NSData* decryption=[Cryptography decryptCBCMode:encryption withKey:signalingKeyAESKeyMaterial withIV:iv withVersion:version withHMACKey:signalingKeyHMACKeyMaterial withHMACType:TSHMACSHA1Truncated10Bytes forHMAC:mac]; + + NSString* decryptedMessage = [[NSString alloc] initWithData:decryption encoding:NSUTF8StringEncoding]; + XCTAssertTrue([decryptedMessage isEqualToString:originalMessage], @"Decrypted message: %@ is not equal to original: %@",decryptedMessage,originalMessage); + +} + + +-(void) testCTRModeDecryption { + NSString* originalMessage = @"Hawaii is awesome"; + + for(int i=0; i<20; i++) { + NSData* lastEncryption = nil; + TSMessageKeys * messageKeysMeta = [[TSMessageKeys alloc] initWithCipherKey:[Cryptography generateRandomBytes:32] macKey:[Cryptography generateRandomBytes:32] counter:0]; + for(int ctr = 0; ctr < 100; ctr++) { + TSMessageKeys *messageKeys = [[TSMessageKeys alloc] initWithCipherKey:messageKeysMeta.cipherKey macKey:messageKeysMeta.macKey counter:ctr]; + + NSData* encryption = [Cryptography encryptCTRMode:[originalMessage dataUsingEncoding:NSASCIIStringEncoding] withKeys:messageKeys]; + XCTAssertFalse([encryption isEqualToData:lastEncryption] , @"encryption is equal to a previous encryption! this shouldn't happen with different counters, ctr=%d",ctr); + lastEncryption = encryption; + NSData* decryption = [Cryptography decryptCTRMode:encryption withKeys:messageKeys]; + + NSString* decryptedMessage = [[NSString alloc] initWithData:decryption encoding:NSASCIIStringEncoding]; + XCTAssertTrue([decryptedMessage isEqualToString:originalMessage], @"Decrypted message: %@ is not equal to original: %@",decryptedMessage,originalMessage); + XCTAssertFalse([[originalMessage dataUsingEncoding:NSASCIIStringEncoding] isEqualToData:encryption], @"ctr encryption did nothing, as it encrypted data equals the original data. this is to catch that doesn't happen-as it could be disabled for testing/debugging"); + } + } +} + + +@end + diff --git a/TextSecureiOS Tests/Model/TSMessageIncomingTest.m b/TextSecureiOS Tests/Model/TSMessageIncomingTest.m new file mode 100644 index 0000000..b8aaf1d --- /dev/null +++ b/TextSecureiOS Tests/Model/TSMessageIncomingTest.m @@ -0,0 +1,117 @@ +// +// TSMessageIncomingTest.m +// TextSecureiOS +// +// Created by Daniel Cestari on 3/14/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +#import "TSMessageIncoming.h" +#import "TSKeyManager.h" +#import "TSMessagesDatabase.h" +#import "TSStorageMasterKey.h" + +@interface TSMessageIncomingTest : XCTestCase + +@end + +@implementation TSMessageIncomingTest + +- (void)setUp +{ + + static NSString *masterPw = @"1234test"; + static NSString *dbFileName = @"test.db"; + static NSString *dbPreference = @"WasTestDbCreated"; + + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. + + [TSKeyManager storeUsernameToken:@"56789"]; + + // Remove any existing DB + [TSMessagesDatabase databaseErase]; + + + [TSStorageMasterKey eraseStorageMasterKey]; + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + + NSError *error; + + // tests datbase creation + + XCTAssertTrue([TSMessagesDatabase databaseCreateWithError:&error], @"message db creation failed"); + XCTAssertNil(error, @"message db creation returned an error"); + + // tests is empty + NSArray* threadsFromDb = [TSMessagesDatabase conversations]; + XCTAssertTrue([threadsFromDb count]==0, @"there are threads in an empty db"); +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testInitMessageWithContent +{ + NSString *content = @"Hello"; + NSString *senderId = @"+1234567890"; + NSDate *date = [NSDate date]; + NSArray *attachments = @[]; + TSGroup *group = nil; + TSMessageIncomingState state = TSMessageStateReceived; + + TSMessageIncoming *message = [[TSMessageIncoming alloc] initMessageWithContent:content + sender:senderId + date:date + attachements:attachments + group:group + state:state]; + + XCTAssertNotNil(message); + XCTAssertEqual(message.content, content); + XCTAssertEqual(message.senderId, senderId); + XCTAssertEqual(message.timestamp, date); + XCTAssertEqual(message.attachments, attachments); + XCTAssertEqual(message.group, group); + XCTAssertEqual(message.state, state); +} + +- (void)testSetStateWithCompletion +{ + NSString *content = @"Hello"; + NSString *senderId = @"+1234567890"; + NSDate *date = [NSDate date]; + NSArray *attachments = @[]; + TSGroup *group = nil; + TSMessageIncomingState state = TSMessageStateReceived; + + TSMessageIncoming *message = [[TSMessageIncoming alloc] initMessageWithContent:content + sender:senderId + date:date + attachements:attachments + group:group + state:state]; + XCTAssertEqual(message.state, TSMessageStateReceived); + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + [message setState:TSMessageStateRead withCompletion:^(BOOL success) { + if (success) { + XCTAssertEqual(message.state, TSMessageStateRead); + } else { + XCTFail(@"method reported failure"); + } + + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); +} + + +@end diff --git a/TextSecureiOS Tests/Model/TSMessageOutgoingTest.m b/TextSecureiOS Tests/Model/TSMessageOutgoingTest.m new file mode 100644 index 0000000..720b76f --- /dev/null +++ b/TextSecureiOS Tests/Model/TSMessageOutgoingTest.m @@ -0,0 +1,57 @@ +// +// TSMessageOutgoingTest.m +// TextSecureiOS +// +// Created by Daniel Cestari on 3/12/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +#import "TSMessageOutgoing.h" +#import "TSGroup.h" + +@interface TSMessageOutgoingTest : XCTestCase + +@end + +@implementation TSMessageOutgoingTest + +- (void)setUp +{ + [super setUp]; + // Put setup code here; it will be run once, before the first test case. +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; +} + +- (void)testInitMessageWithContent +{ + NSString *content = @"Hello"; + NSString *recipientId = @"+1234567890"; + NSDate *date = [NSDate date]; + NSArray *attachments = @[]; + TSGroup *group = nil; + TSMessageOutgoingState state = TSMessageStatePendingSend; + + TSMessageOutgoing *message = [[TSMessageOutgoing alloc] initMessageWithContent:content + recipient:recipientId + date:date + attachements:attachments + group:group + state:state]; + + XCTAssertNotNil(message); + XCTAssertEqual(message.content, content); + XCTAssertEqual(message.recipientId, recipientId); + XCTAssertEqual(message.timestamp, date); + XCTAssertEqual(message.attachments, attachments); + XCTAssertEqual(message.group, group); + XCTAssertEqual(message.state, state); +} + +@end diff --git a/TextSecureiOS Tests/PasswordUnlockViewControllerTests.m b/TextSecureiOS Tests/PasswordUnlockViewControllerTests.m new file mode 100644 index 0000000..ec1f9bd --- /dev/null +++ b/TextSecureiOS Tests/PasswordUnlockViewControllerTests.m @@ -0,0 +1,136 @@ +// +// UnlockViewControllerTests.m +// TextSecureiOS +// +// Created by Daniel Witurna on 28.02.14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "PasswordUnlockViewController.h" +#import "TSStorageMasterKey.h" + +@interface PasswordUnlockViewControllerTests : XCTestCase +@property (strong, nonatomic) PasswordUnlockViewController *controller; +@end + +static NSString *masterPw = @"1234test"; + +// Get access to private delegate methods and textfield for testing. +@interface PasswordUnlockViewController () +@property(nonatomic, strong) IBOutlet UITextField *passwordTextField; +@end + +@implementation PasswordUnlockViewControllerTests + +- (void)setUp +{ + [super setUp]; + + NSError *err = nil; + //Setup storage key master and lock it. + [TSStorageMasterKey eraseStorageMasterKey]; + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:&err]; + [TSStorageMasterKey lockStorageMasterKey]; + + XCTAssertNil(err, @"Creating storage master key failed."); + + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; + self.controller = [storyboard instantiateViewControllerWithIdentifier:@"PasswordUnlockViewController"]; + [self.controller performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES]; +} + +- (void)tearDown +{ + self.controller = nil; + [super tearDown]; +} + +- (void)testEnterCorrectPassword +{ + UITextField *passwordTextField = self.controller.passwordTextField; + NSString *correctPassword = masterPw; + passwordTextField.text = correctPassword; + + [self.controller textFieldShouldReturn:passwordTextField]; //Triggers unlock button press + + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + XCTAssertTrue(didUnlock, @"Password unlock with correct password failed"); +} + +- (void)testEnterWrongPassword +{ + UITextField *passwordTextField = self.controller.passwordTextField; + NSString *wrongPassword = @"test1234"; + passwordTextField.text = wrongPassword; + + [self.controller textFieldShouldReturn:passwordTextField]; //Triggers unlock button press + + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + XCTAssertFalse(didUnlock, @"Password unlock with wrong password succeeded"); +} + +- (void)testEnterEmptyPassword +{ + UITextField *passwordTextField = self.controller.passwordTextField; + NSString *wrongPassword = @""; + passwordTextField.text = wrongPassword; + + [self.controller textFieldShouldReturn:passwordTextField]; //Triggers unlock button press + + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + XCTAssertFalse(didUnlock, @"Password unlock with wrong password succeeded"); +} + +- (void)testNilPassword +{ + UITextField *passwordTextField = self.controller.passwordTextField; + NSString *wrongPassword = nil; + passwordTextField.text = wrongPassword; + + [self.controller textFieldShouldReturn:passwordTextField]; //Triggers unlock button press + + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + XCTAssertFalse(didUnlock, @"Password unlock with wrong password succeeded"); +} + +- (void)testVeryLongPassword +{ + UITextField *passwordTextField = self.controller.passwordTextField; + // Creating a string with NSUIntegerMax length will lead to out-of-memory situation. + // After trying various values, decided for 2^20, which is still pretty long and + // works on 32-bit Simulator in a reasonable amount of time. + NSUInteger length = pow(2,20); + NSString *veryLongString = [@"" stringByPaddingToLength:length withString:@"a" startingAtIndex:0]; + passwordTextField.text = veryLongString; + + [self.controller textFieldShouldReturn:passwordTextField]; //Triggers unlock button press + + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + XCTAssertFalse(didUnlock, @"Password unlock with wrong password succeeded"); +} + +- (void)testUnusualCharactersPassword{ + UITextField *passwordTextField = self.controller.passwordTextField; + // Code for creating string with a lot of different unicode characters found + // here http://cocoadev.com/UniCode + NSMutableString *testString = [[NSMutableString alloc] initWithCapacity:55296-32]; + for (int i = 32; i < 55296; i++) { + [testString appendFormat:@"%C", (unichar)i]; + } + passwordTextField.text = testString; + + [self.controller textFieldShouldReturn:passwordTextField]; //Triggers unlock button press + + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + XCTAssertFalse(didUnlock, @"Password unlock with wrong password succeeded"); +} + + +@end \ No newline at end of file diff --git a/TextSecureiOS Tests/TSAxolotlConsistencyTest.m b/TextSecureiOS Tests/TSAxolotlConsistencyTest.m new file mode 100644 index 0000000..03beba7 --- /dev/null +++ b/TextSecureiOS Tests/TSAxolotlConsistencyTest.m @@ -0,0 +1,99 @@ +// +// TSAxolotlConsistencyTest.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 26/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + + + +/** + ...................................................................... + . o \ o / _ o __| \ / |__ o _ \ o / o . + . /|\ | /\ __\o \o | o/ o/__ /\ | /|\ . + . / \ / \ | \ /) | ( \ /o\ / ) | (\ / | / \ / \ . + . ....................................................... . + . \ o / . . \ o / . + . | . . | . + . / \ . . / \ . + . . . . + . _ o . . _ o . + . /\ . . /\ . + . | \ . . | \ . + . . . . + . . . . + . __\o . . __\o . + . /) | . . /) | . + . . . . + . __| . . __| . + . \o . . \o . + . ( \ . . ( \ . + . . . . + . \ / . . \ / . + . | . !!! WARNING !!! . | . + . /o\ . The following tests do not prove that the . /o\ . + . . ratchet is properly implemented but just . . + . |__ . that the implementation is consistent. . |__ . + . o/ . . o/ . + ./ ) . ./ ) . + . . . . + . . . . + . o/__ . . o/__ . + . | (\ . . | (\ . + . . . . + . o _ . . o _ . + . /\ . . /\ . + . / | . . / | . + . . . . + . \ o / . . \ o / . + . | . . | . + . / \ . . / \ . + . ....................................................... . + . o \ o / _ o __| \ / |__ o _ \ o / o . + . /|\ | /\ __\o \o | o/ o/__ /\ | /|\ . + . / \ / \ | \ /) | ( \ /o\ / ) | (\ / | / \ / \ . + dc..................................................................... + */ + + + + +#import + +@interface TSAxolotlConsistencyTest : XCTestCase + +@end + +@implementation TSAxolotlConsistencyTest + +- (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)testEncryptionDecryption +{ + NSString *senderID = @"+10000000"; + NSString *receiverID = @"+41000000"; + /** + * Session for encryption + */ + + + + /** + * Session for decryption + */ + + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/TextSecureiOS Tests/TSECKeyPairTests.m b/TextSecureiOS Tests/TSECKeyPairTests.m new file mode 100644 index 0000000..87e320f --- /dev/null +++ b/TextSecureiOS Tests/TSECKeyPairTests.m @@ -0,0 +1,66 @@ +// +// TSECKeyPairTests.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSECKeyPair.h" + + +@interface TSECKeyPairTests : XCTestCase + +@end + +@implementation TSECKeyPairTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here; it will be run once, before the first test case. +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; +} + + +- (void)testGenerateKeyPair +{ + TSECKeyPair *keyPair = [TSECKeyPair keyPairGenerateWithPreKeyId:1]; + XCTAssertNotNil(keyPair, @"Key pair generation returned a nil key pair."); + + NSData *publicKey = [keyPair publicKey]; + XCTAssertNotNil(publicKey, @"Key pair generation returned a nil public key."); +} + + +- (void)testGenerateSharedSecret +{ + TSECKeyPair *keyPair1 = [TSECKeyPair keyPairGenerateWithPreKeyId:1]; + TSECKeyPair *keyPair2 = [TSECKeyPair keyPairGenerateWithPreKeyId:2]; + + NSData *publicKey = [keyPair1 publicKey]; + NSData *sharedSecret = [keyPair2 generateSharedSecretFromPublicKey:publicKey]; + XCTAssertNotNil(sharedSecret, @"Shared secret generation returned a nil shared secret."); +} + + +- (void)testSerialization +{ + TSECKeyPair *keyPair1 = [TSECKeyPair keyPairGenerateWithPreKeyId:1]; + NSData *serializedKeyPair = [NSKeyedArchiver archivedDataWithRootObject:keyPair1]; + XCTAssertNotNil(serializedKeyPair, @"Key pair serialization returned a nil data object."); + + TSECKeyPair *keyPair2 = [NSKeyedUnarchiver unarchiveObjectWithData:serializedKeyPair]; + XCTAssertNotNil(keyPair2, @"Key pair de-serialization returned a nil key pair."); + + XCTAssertEqualObjects([keyPair1 publicKey], [keyPair2 publicKey], @"Key pair de-serialization returned a different public key"); +} + + +@end diff --git a/TextSecureiOS Tests/TSEncryptedDatabaseTests.m b/TextSecureiOS Tests/TSEncryptedDatabaseTests.m new file mode 100644 index 0000000..14dc02c --- /dev/null +++ b/TextSecureiOS Tests/TSEncryptedDatabaseTests.m @@ -0,0 +1,133 @@ +// +// TSEncryptedDatabase2Tests.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSStorageError.h" +#import "TSDatabaseManager.h" +#import "TSStorageMasterKey.h" +#import "FilePath.h" +#import "FMDatabase.h" +#import "FMDatabaseQueue.h" + +@interface TSEncryptedDatabaseTests : XCTestCase + +@end + + +static NSString *masterPw = @"1234test"; +static NSString *dbFileName = @"test.db"; +static NSString *dbPreference = @"WasTestDbCreated"; + + +@implementation TSEncryptedDatabaseTests + +- (void)setUp +{ + [super setUp]; + + // Create a storage master key + [TSStorageMasterKey eraseStorageMasterKey]; + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + + [TSDatabaseManager databaseEraseAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference]; +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; +} + + + +- (void)testDatabaseCreate +{ + NSError *error = nil; + TSDatabaseManager *encDb = [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:&error]; + + XCTAssertNotNil(encDb, @"database creation returned nil"); + XCTAssertNil(error, @"database creation returned an error"); +} + + +- (void)testDatabaseCreateWithPreviousDatabaseRemnants +{ + NSError *error = nil; + [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:@"" error:nil]; + + TSDatabaseManager *encDb = [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:&error]; + + XCTAssertNil(error, @"database creation returned an error"); + XCTAssertNotNil(encDb, @"database creation failed"); +} + + +- (void)testDatabaseCreateWithoutMasterStorageKey +{ + [TSStorageMasterKey eraseStorageMasterKey]; + NSError *error = nil; + TSDatabaseManager *encDb = [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:&error]; + + XCTAssertNotNil(error, @"database creation succeeded with no master key"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"database creation succeeded with no master key returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyNotCreated, @"database creation succeeded with no master key returned an unexpected error"); + XCTAssertNil(encDb, @"database creation succeeded with no master key"); + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; +} + + +- (void)testDatabaseCreateAndOverwrite +{ + NSError *error = nil; + [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:&error]; + + TSDatabaseManager *encDb = [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:&error]; + + XCTAssertNotNil(error, @"database overwrite did not return an error"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"database overwrite returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorDatabaseAlreadyCreated, @"database overwrite returned an unexpected error"); + XCTAssertNil(encDb, @"database overwrite succeeded"); +} + + +- (void)testDatabaseDecrypt +{ + [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:nil]; + + NSError *error = nil; + TSDatabaseManager *encDb = [TSDatabaseManager databaseOpenAndDecryptAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] error:&error]; + + XCTAssertNotNil(encDb, @"database decryption returned nil"); + XCTAssertNil(error, @"database decryption returned an error"); +} + +- (void)testDatabaseDecryptWithCorruptedStorageKey +{ + NSError *error = nil; + TSDatabaseManager *encDb = [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] updateBoolPreference:dbPreference error:nil]; + + // Write something to the DB + [encDb.dbQueue inDatabase: ^(FMDatabase *db) { + [db executeUpdate:@"CREATE TABLE user_identity_key (serialized_keypair BLOB)"]; + }]; + + // Replace the storage key but use the same password + [TSStorageMasterKey eraseStorageMasterKey]; + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:&error]; + + encDb = [TSDatabaseManager databaseOpenAndDecryptAtFilePath:[FilePath pathInDocumentsDirectory:dbFileName] error:&error]; + + XCTAssertNotNil(error, @"database decryption with invalid storage key did not return an error"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"database decryption with invalid storage key returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyCorrupted, @"database decryption with invalid storage key returned an unexpected error"); + XCTAssertNil(encDb, @"database decryption with invalid storage key succeeded"); +} + + + +@end diff --git a/TextSecureiOS Tests/TSMessagesDatabaseTests.m b/TextSecureiOS Tests/TSMessagesDatabaseTests.m new file mode 100644 index 0000000..39d51a4 --- /dev/null +++ b/TextSecureiOS Tests/TSMessagesDatabaseTests.m @@ -0,0 +1,135 @@ +// +// TSMessagesDatabase.m +// TSMessagesDatabase Tests +// +// Created by Alban Diquet on 11/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSMessagesDatabase.h" +#import "TSStorageError.h" +#import "Cryptography.h" +#import "NSData+Base64.h" +#import "TSStorageMasterKey.h" +#import "TSContact.h" +#import "TSECKeyPair.h" +#import "TSMessage.h" +#import "TSKeyManager.h" +#import "Constants.h" +#import "TSSession.h" + +static NSString *masterPw = @"1234test"; + +@interface TSECKeyPair (Test) +-(NSData*) getPrivateKey; +@end + + +@implementation TSECKeyPair (Test) +-(NSData*) getPrivateKey { + return [NSData dataWithBytes:self->privateKey length:32]; +} +@end + +@interface TSMessagesDatabaseTests : XCTestCase +@property (nonatomic) TSContact *contact; +@property (nonatomic) TSMessage* message; + +@end + +@implementation TSMessagesDatabaseTests + +- (void)setUp +{ + [super setUp]; + + [TSKeyManager storeUsernameToken:@"56789"]; + + self.contact = [[TSContact alloc] initWithRegisteredID:@"12345" relay:nil]; + + self.message = [[TSMessage alloc] initWithSenderId:[TSKeyManager getUsernameToken] recipientId:self.contact.registeredID date:[NSDate date] content:@"Hello World" attachements:nil groupId:nil]; + + // Remove any existing DB + [TSMessagesDatabase databaseErase]; + + + [TSStorageMasterKey eraseStorageMasterKey]; + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + + NSError *error; + + // tests datbase creation + + XCTAssertTrue([TSMessagesDatabase databaseCreateWithError:&error], @"message db creation failed"); + XCTAssertNil(error, @"message db creation returned an error"); + + // tests is empty + NSArray* threadsFromDb = [TSMessagesDatabase conversations]; + NSArray *messages = [TSMessagesDatabase messagesWithContact:self.contact]; + XCTAssertTrue([threadsFromDb count]==0, @"there are threads in an empty db"); + XCTAssertTrue([messages count]==0, @"there are threads in an empty db"); + + [TSMessagesDatabase storeMessage:self.message]; +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; + [TSStorageMasterKey eraseStorageMasterKey]; +} + +- (void) testStoreMessage { + NSArray *messages = [TSMessagesDatabase messagesWithContact:self.contact]; + XCTAssertTrue([messages count]==1, @"database should just have one message in it, instead has %lu",(unsigned long)[messages count]); + XCTAssertTrue([[[messages objectAtIndex:0] content] isEqualToString:self.message.content], @"message bodies not equal"); +} + +- (void)testStoreSession{ + XCTAssertTrue([[TSMessagesDatabase sessionsForContact:self.contact] count] == 0, @"We had sessions before test started!"); + + TSSession *session = [[TSSession alloc] initWithContact:self.contact deviceId:1]; + + session.rootKey = [Cryptography generateRandomBytes:10]; + session.senderChainKey = [[TSChainKey alloc]initWithChainKeyWithKey:[Cryptography generateRandomBytes:10] index:1]; + + session.senderEphemeral = [TSECKeyPair keyPairGenerateWithPreKeyId:0]; + + NSData *chainData = [Cryptography generateRandomBytes:10]; + NSData *chainKey = [Cryptography generateRandomBytes:10]; + + [session addReceiverChain:chainData chainKey:[[TSChainKey alloc] initWithChainKeyWithKey:chainKey index:1]]; + + [TSMessagesDatabase storeSession:session]; + + TSSession *retreivedSession = [TSMessagesDatabase sessionForRegisteredId:self.contact.registeredID deviceId:1]; + + XCTAssertTrue([retreivedSession.rootKey isEqualToData:session.rootKey], @"Rootkeys don't match"); + XCTAssertTrue([retreivedSession.senderChainKey.key isEqualToData:session.senderChainKey.key], @"SenderKeyChain keys don't match"); + XCTAssertTrue([retreivedSession.senderEphemeral.publicKey isEqualToData:session.senderEphemeral.publicKey], @"SenderEphemeral keys don't match"); + XCTAssertTrue([[retreivedSession receiverChainKey:chainData].key isEqualToData:chainKey], @"Receiver chain keys don't match"); + + // The basic properties seem to be saved properly, now let's test 5 with 5 receiving chains and a change of some properties. + + NSMutableArray *chainKeys = [NSMutableArray array]; + NSMutableArray *ephemerals = [NSMutableArray array]; + for (int i = 5; i > 0; i--) { + TSChainKey *chainKey = [[TSChainKey alloc]initWithChainKeyWithKey:[Cryptography generateRandomBytes:30] index:1]; + NSData *randomData = [Cryptography generateRandomBytes:32]; + [retreivedSession addReceiverChain:randomData chainKey:chainKey]; + [chainKeys addObject:chainKey]; + [ephemerals addObject:randomData]; + } + + [TSMessagesDatabase storeSession:retreivedSession]; + + TSSession *retreivedSession2 = [TSMessagesDatabase sessionForRegisteredId:self.contact.registeredID deviceId:1]; + + for (int i = 4; i >= 0; i --) { + XCTAssert([[retreivedSession2 receiverChainKey:[ephemerals objectAtIndex:i]].key isEqualToData: ((TSChainKey*)[chainKeys objectAtIndex:i]).key], @"ChainKey not updated!"); + } +} + + +@end diff --git a/TextSecureiOS Tests/TSProtocolBufferWrapperTests.mm b/TextSecureiOS Tests/TSProtocolBufferWrapperTests.mm new file mode 100644 index 0000000..d029da5 --- /dev/null +++ b/TextSecureiOS Tests/TSProtocolBufferWrapperTests.mm @@ -0,0 +1,464 @@ + // +// TSProtocolBufferWrapperTests.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/11/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSProtocolBufferWrapper.hh" +#import "Cryptography.h" +#import "TSWhisperMessage.hh" +#import "TSMessageSignal.hh" +#import "IncomingPushMessageSignal.pb.hh" +#import "TSPushMessageContent.hh" +#import "TSEncryptedWhisperMessage.hh" +#import "TSPreKeyWhisperMessage.hh" +#import "TSGroupContext.h" +#import "Constants.h" +@interface TSProtocolBufferWrapper (Test) +- (textsecure::IncomingPushMessageSignal *)deserialize:(NSData *)data; +@end + + + +@interface TSProtocolBufferWrapperTests : XCTestCase +@property(nonatomic,strong) TSProtocolBufferWrapper* pbWrapper; +@property(nonatomic,strong) NSString* body; +@property(nonatomic,strong) NSArray* attachments; +@property(nonatomic,strong) TSGroupContext* groupContext; +@property(nonatomic,strong) NSData* ephemeral; +@property(nonatomic,strong) NSData* myNextEphemeral; +@property(nonatomic,strong) NSNumber* prevCounter; +@property(nonatomic,strong) NSNumber* counter; +@property(nonatomic,strong) NSNumber* theirPrekeyId; +@property(nonatomic,strong) NSData* version; +@property(nonatomic,strong) NSData* cipherKey; +@property(nonatomic,strong) NSData* hmacKey; +@property(nonatomic,strong) TSMessageKeys *messageKeys; +@property(nonatomic,strong) NSString* source; +@property(nonatomic,strong) NSNumber* sourceDevice; +@property(nonatomic,strong) NSDate* timestamp; + +@end + + +@implementation TSProtocolBufferWrapperTests + +- (void)setUp { + [super setUp]; + + // Neded for TSPushmessageContent + _body = @"hello Hawaii"; + _attachments = nil; + _groupContext = nil; + + // needed for TSPreKeyWhisperMessage + _myNextEphemeral = [Cryptography generateRandomBytes:32]; + _theirPrekeyId = [NSNumber numberWithInt:1337]; + + // needed for TSEncryptedWhisperMessage + _ephemeral = [Cryptography generateRandomBytes:32]; + _prevCounter = [NSNumber numberWithInt:0]; + _counter = [NSNumber numberWithInt:0]; + _version = [Cryptography generateRandomBytes:1]; + + // needed for encryption of WhisperMessage + _cipherKey = [Cryptography generateRandomBytes:32]; + _hmacKey = [Cryptography generateRandomBytes:32]; + _messageKeys = [[TSMessageKeys alloc] initWithCipherKey:_cipherKey macKey:_hmacKey counter:[_counter intValue]]; + + // neede for the TSMessagesignal + _source = @"+11111111"; + _sourceDevice = [NSNumber numberWithUnsignedLong:7654321]; + _timestamp = [NSDate date]; + + + // needed for optional attachment testing + NSData* attachment1Key = [Cryptography generateRandomBytes:32]; + NSData* attachment2Key = [Cryptography generateRandomBytes:32]; + + TSAttachment *attachment1 = [[TSAttachment alloc] initWithAttachmentId:[NSNumber numberWithInt:42] contentMIMEType:@"image/jpg" decryptionKey:attachment1Key]; + TSAttachment *attachment2 = [[TSAttachment alloc] initWithAttachmentId:[NSNumber numberWithInt:35] contentMIMEType:@"video/mp4" decryptionKey:attachment2Key]; + _attachments = [NSArray arrayWithObjects:attachment1,attachment2, nil]; + + + // needed for optional group testing + NSString* member1 = @"+12345678"; + NSString* member2 = @"+987665"; + NSString* member3 = @"+11111111"; + NSData* groupId = [Cryptography generateRandomBytes:8]; + TSAttachment *avatar = attachment1; + + _groupContext = [[TSGroupContext alloc] initWithId:groupId withType:TSUpdateGroupContext withName:@"Winter Break of Code" withMembers:@[member1,member2,member3] withAvatar:avatar]; + + + + _pbWrapper = [[TSProtocolBufferWrapper alloc] init]; + +} + +- (void)tearDown { + [super tearDown]; +} + +/* + PushMessageSignal.type = {3=PreKeyWhisperMessage,0=Unencrypted,1=WhisperMessage } + PushMessageSignal.message = {PreKeyWhisperMessage,PushMessageContent,WhisperMessage} + + PreKeyWhisperMessage.message = WhisperMessage + PushMessageContent.body = "hey, here's the real message" + WhisperMessage.message = PushMessageContent + */ + + +-(void) testCompareAndroidSerialization { + + // Neded for TSPushmessageContent + NSString* body = @"hello Hawaii"; + + // needed for TSEncryptedWhisperMessage + const unsigned char zero32Bytes[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + const unsigned char zeroByte[] = {0x00}; + + NSData* zero32Data = [NSData dataWithBytes:zero32Bytes length:sizeof(zero32Bytes)]; + NSData* zero1Data = [NSData dataWithBytes:zeroByte length:sizeof(zeroByte)]; + NSData *ephemeral = zero32Data; + NSNumber *prevCounter = [NSNumber numberWithInt:0]; + NSNumber* counter = [NSNumber numberWithInt:0]; + NSData* version = zero1Data; + NSNumber* theirPrekeyId = [NSNumber numberWithInt:0]; + + // needed for encryption of WhisperMessage + NSData* cipherKey = zero32Data; + + + // Stuffing into objective c + TSPushMessageContent* pushContent = [[TSPushMessageContent alloc] initWithBody:body withAttachments:nil withGroupContext:nil]; + + TSEncryptedWhisperMessage* tsEncryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithEphemeralKey:ephemeral previousCounter:prevCounter counter:counter encryptedPushMessageContent:[pushContent getTextSecureProtocolData] forVersion:version HMACKey:cipherKey]; + TSPreKeyWhisperMessage* tsPreKeyWhisperMessage = [TSPreKeyWhisperMessage constructFirstMessageWithEncryptedPushMessageContent:[pushContent getTextSecureProtocolData] theirPrekeyId:theirPrekeyId myCurrentEphemeral:ephemeral myNextEphemeral:ephemeral forVersion:version withHMACKey:cipherKey]; + + + NSString* base64SerializediOSTSPushMessageContentCurrent = [[pushContent getTextSecureProtocolData] base64EncodedStringWithOptions:0]; + + NSString* base64SerializediOSTSEncryptedWhisperMessageCurrent = [[tsEncryptedMessage getTextSecureProtocolData] base64EncodedStringWithOptions:0]; + NSString* base64SerializediOSTSPreKeyWhisperMessageCurrent = [[tsPreKeyWhisperMessage getTextSecureProtocolData] base64EncodedStringWithOptions:0]; + + XCTAssertTrue([base64SerializediOSTSEncryptedWhisperMessageCurrent isEqualToString:[tsPreKeyWhisperMessage.message base64EncodedStringWithOptions:0]]); + + + NSLog(@"%@",base64SerializediOSTSPushMessageContentCurrent); + NSLog(@"%@",base64SerializediOSTSEncryptedWhisperMessageCurrent); + NSLog(@"%@",base64SerializediOSTSPreKeyWhisperMessageCurrent); + + + // // gives for iOS currently + // NSString* base64SerializediOSTSPushMessageContentCurrent = @"CgxoZWxsbyBIYXdhaWk="; + // NSString* base64SerializediOSTSEncryptedWhisperMessageCurrent = @"AAohBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAYACIOCgxoZWxsbyBIYXdhaWncdNiDCuAuow=="; + // NSString* base64SerializediOSTSPreKeyWhisperMessageCurrent = @"AAgAEiEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaACJAAAohBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAYACIOCgxoZWxsbyBIYXdhaWncdNiDCuAuow=="; + + + + // Droid gives us for equavalent data/ephemeral etc. + NSString* base64SerializedDroidTSPushMessageContentCurrent = @"CgxoZWxsbyBIYXdhaWk="; + NSString* base64SerializedDroidTSEncryptedWhisperMessageCurrent = @"CiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAGAAiDgoMaGVsbG8gSGF3YWlp"; + NSString* base64SerializedDroidTSPreKeyWhisperMessageCurrent = @"CAASIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI2CiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAGAAiDgoMaGVsbG8gSGF3YWlpKAA="; + + + XCTAssertTrue([base64SerializediOSTSPushMessageContentCurrent isEqualToString:base64SerializedDroidTSPushMessageContentCurrent], @"push messages PBs not equal ios %@ droid %@",base64SerializediOSTSPushMessageContentCurrent,base64SerializedDroidTSPushMessageContentCurrent); + XCTAssertTrue([base64SerializediOSTSEncryptedWhisperMessageCurrent isEqualToString:base64SerializedDroidTSEncryptedWhisperMessageCurrent], @"encrypted message PBs not equal ios %@ droid %@",base64SerializediOSTSEncryptedWhisperMessageCurrent,base64SerializedDroidTSEncryptedWhisperMessageCurrent); + XCTAssertTrue([base64SerializediOSTSPreKeyWhisperMessageCurrent isEqualToString:base64SerializedDroidTSPreKeyWhisperMessageCurrent], @"prekey messages PBs not equal ios %@ droid %@",base64SerializediOSTSPreKeyWhisperMessageCurrent,base64SerializedDroidTSPreKeyWhisperMessageCurrent); +} + +-(void) testPrekeyWhisperMessageSerialization { + /* TODO: debug this test. as iOS->iOS, Droid->iOS, iOS->Droid first sends now work clearly the PreKeyWhisperMessage protocol buffer serialization isn't all bad. but the // this is crashing // line is well, crashing here. Probably an issue with the test itself + (inna hurry) but should be massaged to pass */ + // Stuffing into objective c + TSPushMessageContent* pushContent = [[TSPushMessageContent alloc] initWithBody:_body withAttachments:nil withGroupContext:nil]; + NSData* encryptedContent = [Cryptography encryptCTRMode:[pushContent getTextSecureProtocolData] withKeys:_messageKeys]; + + TSEncryptedWhisperMessage* tsEncryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithEphemeralKey:_ephemeral previousCounter:_prevCounter counter:_counter encryptedPushMessageContent:encryptedContent forVersion:_version HMACKey:_cipherKey]; + + TSPreKeyWhisperMessage *tsPreKeyWhisperMessage = [TSPreKeyWhisperMessage constructFirstMessageWithEncryptedPushMessageContent:[tsEncryptedMessage getTextSecureProtocolData] theirPrekeyId:_theirPrekeyId myCurrentEphemeral:_ephemeral myNextEphemeral:_myNextEphemeral forVersion:_version withHMACKey:_cipherKey]; + + + + NSData* serializedPreKeyMessage = [tsPreKeyWhisperMessage getTextSecureProtocolData]; + + TSPreKeyWhisperMessage* deserializedPreKeyMessage = [[TSPreKeyWhisperMessage alloc] initWithTextSecureProtocolData:serializedPreKeyMessage]; // this is crashing + XCTAssertTrue([deserializedPreKeyMessage.version isEqualToData:tsPreKeyWhisperMessage.version],@"versions not equal: deserialized: %@, original %@",deserializedPreKeyMessage.version ,tsPreKeyWhisperMessage.version); + XCTAssertTrue([deserializedPreKeyMessage.preKeyId isEqualToNumber:tsPreKeyWhisperMessage.preKeyId],@"preKeyIds not equal: deserialized: %@, original %@",deserializedPreKeyMessage.preKeyId ,tsPreKeyWhisperMessage.preKeyId); + XCTAssertTrue([deserializedPreKeyMessage.baseKey isEqualToData:tsPreKeyWhisperMessage.baseKey],@"base keys not equal: deserialized: %@, original %@", deserializedPreKeyMessage.baseKey,tsPreKeyWhisperMessage.baseKey); + XCTAssertTrue([deserializedPreKeyMessage.identityKey isEqualToData:tsPreKeyWhisperMessage.identityKey],@"identity keys not equal: deserialized: %@, original %@", deserializedPreKeyMessage.identityKey,tsPreKeyWhisperMessage.identityKey); + + + XCTAssertTrue([deserializedPreKeyMessage.message isEqualToData:encryptedContent],@"encrypted push message content %@ not equal to deserialized version %@",encryptedContent,deserializedPreKeyMessage.message); + NSData* decryptedPushMessageContentData = [Cryptography decryptCTRMode:deserializedPreKeyMessage.message withKeys:_messageKeys]; + XCTAssertTrue([decryptedPushMessageContentData isEqualToData:[pushContent getTextSecureProtocolData]],@"decrypted push message content %@ not equal to original decrypted version %@",decryptedPushMessageContentData,[pushContent getTextSecureProtocolData]); + TSEncryptedWhisperMessage* deserializedEncryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithData:decryptedPushMessageContentData]; + XCTAssertTrue([deserializedEncryptedMessage.previousCounter isEqualToNumber:tsEncryptedMessage.previousCounter], @"previous counters unequal"); + XCTAssertTrue([deserializedEncryptedMessage.counter isEqualToNumber:tsEncryptedMessage.counter], @"counters unequal"); + XCTAssertTrue([deserializedEncryptedMessage.ephemeralKey isEqualToData:tsEncryptedMessage.ephemeralKey], @"ephemeral keys unequal; deserialization %@, encrypted %@",deserializedEncryptedMessage.ephemeralKey,tsEncryptedMessage.ephemeralKey); + + + NSData *decryptedSerializedPushMessageContent = [Cryptography decryptCTRMode:deserializedEncryptedMessage.message withKeys:_messageKeys]; + TSPushMessageContent *deserializedPushMessageContent = [[TSPushMessageContent alloc] initWithData:decryptedSerializedPushMessageContent]; + + XCTAssertTrue([deserializedPushMessageContent.body isEqualToString:pushContent.body], @"messages not equal derialized %@, original %@",deserializedPushMessageContent.body,pushContent.body); +} + +-(void) testMessageSignalSerializationNoAttachmentsNoGroup { + + + // Stuffing into objective c + TSPushMessageContent *pushContent = [[TSPushMessageContent alloc] initWithBody:_body withAttachments:nil withGroupContext:nil]; + NSData* encryptedContent = [Cryptography encryptCTRMode:[pushContent getTextSecureProtocolData] withKeys:_messageKeys]; + + TSEncryptedWhisperMessage *tsEncryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithEphemeralKey:_ephemeral previousCounter:_prevCounter counter:_counter encryptedPushMessageContent:encryptedContent forVersion:_version HMACKey:_cipherKey]; + TSMessageSignal* messageSignal = [[TSMessageSignal alloc] initWithMessage:tsEncryptedMessage withContentType:TSEncryptedWhisperMessageType withSource:_source withSourceDevice:_sourceDevice withTimestamp:_timestamp]; + + + NSData *serializedMessageSignal = [messageSignal getTextSecureProtocolData]; + TSMessageSignal* deserializedMessageSignal = [[TSMessageSignal alloc] initWithTextSecureProtocolData:serializedMessageSignal]; + + XCTAssertTrue(messageSignal.contentType == deserializedMessageSignal.contentType,@"TSMessageSignal contentType unequal after serialization"); + XCTAssertTrue([messageSignal.sourceDevice isEqualToNumber:deserializedMessageSignal.sourceDevice],@"TSMessageSignal sourceDevice unequal after serialization"); + XCTAssertTrue([messageSignal.source isEqualToString:deserializedMessageSignal.source],@"TSMessageSignal source unequal after serialization"); + + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; + [dateFormatter setTimeStyle:NSDateFormatterNoStyle]; + NSString* nowString = [dateFormatter stringFromDate:messageSignal.timestamp]; + NSString* convertedNowString = [dateFormatter stringFromDate:deserializedMessageSignal.timestamp]; + + XCTAssertTrue([nowString isEqualToString:convertedNowString],@"TSMessageSignal dates unequal after serialization"); + + + + TSEncryptedWhisperMessage *deserializedEncryptedMessage = (TSEncryptedWhisperMessage*)deserializedMessageSignal.message; + + NSData *decryptedSerializedPushMessageContent = [Cryptography decryptCTRMode:deserializedEncryptedMessage.message withKeys:_messageKeys]; + TSPushMessageContent *deserializedPushMessageContent = [[TSPushMessageContent alloc] initWithData:decryptedSerializedPushMessageContent]; + XCTAssertTrue([pushContent.body isEqualToString:deserializedPushMessageContent.body],@"TSMessageSignal message unequal after serialization"); +} + + + +-(void)testPushMessageContentBodySerialization { + TSPushMessageContent *pushContent = [[TSPushMessageContent alloc] initWithBody:_body withAttachments:nil withGroupContext:nil]; + + NSData *serializedMessageContent = [pushContent getTextSecureProtocolData]; + TSPushMessageContent *deserializedPushContent = [[TSPushMessageContent alloc] initWithData:serializedMessageContent]; + + XCTAssertTrue([pushContent.body isEqualToString:deserializedPushContent.body], @"Push message content serialization/deserialization failed"); + XCTAssertTrue([deserializedPushContent.attachments count]==0, @"deserialization has attachments when there should be none"); + +} + + +-(void)testEncryptedWhisperMessageSerializationNoAttachmentsNoGroup { + + // Stuffing into objective c + TSPushMessageContent *pushContent = [[TSPushMessageContent alloc] initWithBody:_body withAttachments:nil withGroupContext:nil]; + NSData* encryptedContent = [Cryptography encryptCTRMode:[pushContent getTextSecureProtocolData] withKeys:_messageKeys]; + + TSEncryptedWhisperMessage *tsEncryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithEphemeralKey:_ephemeral previousCounter:_prevCounter counter:_counter encryptedPushMessageContent:encryptedContent forVersion:_version HMACKey:_cipherKey]; + + + NSData* serializedEncryptedMessage = [tsEncryptedMessage getTextSecureProtocolData]; + + TSEncryptedWhisperMessage *deserializedEncryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithTextSecureProtocolData:serializedEncryptedMessage]; + + NSLog(@"encrypted whispermessage original vs new %@ vs. %@",tsEncryptedMessage,deserializedEncryptedMessage); + XCTAssertTrue([deserializedEncryptedMessage.previousCounter isEqualToNumber:tsEncryptedMessage.previousCounter], @"previous counters unequal"); + + XCTAssertTrue([deserializedEncryptedMessage.counter isEqualToNumber:tsEncryptedMessage.counter], @"counters unequal"); + XCTAssertTrue([deserializedEncryptedMessage.ephemeralKey isEqualToData:tsEncryptedMessage.ephemeralKey], @"ephemeral keys unequal; deserialization %@, encrypted %@",deserializedEncryptedMessage.ephemeralKey,tsEncryptedMessage.ephemeralKey); + + + NSData *decryptedSerializedPushMessageContent = [Cryptography decryptCTRMode:deserializedEncryptedMessage.message withKeys:_messageKeys]; + TSPushMessageContent *deserializedPushMessageContent = [[TSPushMessageContent alloc] initWithData:decryptedSerializedPushMessageContent]; + + XCTAssertTrue([deserializedPushMessageContent.body isEqualToString:pushContent.body], @"messages not equal"); +} + + + +-(void) testPushMessageContentAttachmentSerializationDynamic { + + TSPushMessageContent *pushContent = [[TSPushMessageContent alloc] initWithBody:_body withAttachments:_attachments withGroupContext:nil]; + + + + NSData *serializedMessageContent = [pushContent getTextSecureProtocolData]; + TSPushMessageContent *deserializedPushContent = [[TSPushMessageContent alloc] initWithData:serializedMessageContent]; + + XCTAssertTrue([pushContent.body isEqualToString:deserializedPushContent.body], @"Push message content serialization/deserialization failed"); + + XCTAssertTrue([deserializedPushContent.attachments count]==2, @"deserialization doesn't have the right number of attachments, actually has %lu",(unsigned long)[deserializedPushContent.attachments count]); + + TSAttachment *attachment1 = [pushContent.attachments objectAtIndex:0]; + TSAttachment *attachment2 = [pushContent.attachments objectAtIndex:1]; + + + TSAttachment *attachment1Deserialized = [deserializedPushContent.attachments objectAtIndex:0]; + TSAttachment *attachment2Deserialized = [deserializedPushContent.attachments objectAtIndex:1]; + + + + XCTAssertTrue([attachment1Deserialized.attachmentId isEqualToNumber:attachment1.attachmentId], @"deserialized ids do not match for attachment 1"); + XCTAssertTrue([attachment2Deserialized.attachmentId isEqualToNumber:attachment2.attachmentId], @"deserialized ids do not match for attachment 2"); + + XCTAssertTrue(attachment1Deserialized.attachmentType == attachment1.attachmentType, @"deserialized ids do not match for attachment 1"); + XCTAssertTrue(attachment2Deserialized.attachmentType == attachment2.attachmentType, @"deserialized ids do not match for attachment 2"); + + XCTAssertTrue([attachment1Deserialized.attachmentDecryptionKey isEqualToData:attachment1.attachmentDecryptionKey], @"deserialized ids do not match for attachment 1 deserialized %@ serialized %@",attachment1Deserialized.attachmentDecryptionKey,attachment1.attachmentDecryptionKey); + XCTAssertTrue([attachment2Deserialized.attachmentDecryptionKey isEqualToData:attachment2.attachmentDecryptionKey], @"deserialized ids do not match for attachment 2 deserialized %@ serialized %@",attachment2Deserialized.attachmentDecryptionKey,attachment2.attachmentDecryptionKey); + + +} + + +-(void) testPushMessageContentAttachmentSerializationStatic { + unsigned char testkey = 7; + NSData* attachment1Key = [NSData dataWithBytes:&testkey length:sizeof(testkey)]; + + TSAttachment *attachment1 = [[TSAttachment alloc] initWithAttachmentId:[NSNumber numberWithInt:42] contentMIMEType:@"image/jpg" decryptionKey:attachment1Key]; + TSMessage *message = [[TSMessage alloc] initWithSenderId:@"1234" recipientId:@"1234567" date:[[NSDate alloc] init] content:@"Surf is up" attachements:@[attachment1] groupId:nil]; + NSData *serializedMessageContent = [TSPushMessageContent serializedPushMessageContentForMessage:message withGroupContect:nil]; + TSPushMessageContent *deserializedPushContent = [[TSPushMessageContent alloc] initWithData:serializedMessageContent]; + + XCTAssertTrue([message.content isEqualToString:deserializedPushContent.body], @"Push message content serialization/deserialization failed"); + XCTAssertTrue([deserializedPushContent.attachments count]==1, @"deserialization doesn't have the right number of attachments, actually has %lu",(unsigned long)[deserializedPushContent.attachments count]); + + TSAttachment *attachment1Deserialized = [deserializedPushContent.attachments objectAtIndex:0]; + + + XCTAssertTrue([attachment1Deserialized.attachmentId isEqualToNumber:attachment1.attachmentId], @"deserialized ids do not match for attachment 1"); + + XCTAssertTrue(attachment1Deserialized.attachmentType == attachment1.attachmentType, @"deserialized ids do not match for attachment 1"); + + // TODO: this is currently failing meaning attachments don't make it through deserialization process + XCTAssertTrue([attachment1Deserialized.attachmentDecryptionKey isEqualToData:attachment1.attachmentDecryptionKey], @"deserialized ids do not match for attachment 1 deserialized %@ serialized %@",attachment1Deserialized.attachmentDecryptionKey,attachment1.attachmentDecryptionKey); + + +} + + +-(void) testPushMessageContentGroupSerializationDynamic { + TSPushMessageContent *pushContent = [[TSPushMessageContent alloc] initWithBody:_body withAttachments:nil withGroupContext:_groupContext]; + + NSData *serializedMessageContent = [pushContent getTextSecureProtocolData]; + TSPushMessageContent *deserializedPushContent = [[TSPushMessageContent alloc] initWithData:serializedMessageContent]; + + XCTAssertTrue([pushContent.body isEqualToString:deserializedPushContent.body], @"Push message content serialization/deserialization failed"); + XCTAssertTrue(deserializedPushContent.groupContext!=nil, @"deserialization doesn't give us a group, at all"); + + TSGroupContext *groupContextDeserialized = deserializedPushContent.groupContext; + XCTAssertTrue([groupContextDeserialized.gid isEqualToData:_groupContext.gid],@"deserialized group id doesn't match original"); + XCTAssertTrue(groupContextDeserialized.type==_groupContext.type,@"deserialized group type doesn't match original"); + XCTAssertTrue([groupContextDeserialized.members count]==3,@"deserialized group doesn't have same number of members as original"); + + for(NSUInteger i=0; i<3; i++) { + XCTAssertTrue([[groupContextDeserialized.members objectAtIndex:i] isEqualToString:[pushContent.groupContext.members objectAtIndex:i]],@"deserialized group member %d not the same as original",i); + } + + TSAttachment *groupContextAvatarDeserialized = deserializedPushContent.groupContext.avatar; + XCTAssertTrue([groupContextAvatarDeserialized.attachmentId isEqualToNumber:pushContent.groupContext.avatar.attachmentId], @"deserialized ids do not match for avatar"); + XCTAssertTrue(groupContextAvatarDeserialized.attachmentType == pushContent.groupContext.avatar.attachmentType, @"deserialized ids do not match for avatar"); + XCTAssertTrue([groupContextAvatarDeserialized.attachmentDecryptionKey isEqualToData:pushContent.groupContext.avatar.attachmentDecryptionKey], @"deserialized ids do not match for avatar"); + +} + + + +-(void) testPushMessageContentGroupSerializationStatic { + + NSString* member1 = @"12345678"; + NSString* member2 = @"987665"; + NSString* member3 = @"11111111"; + NSData* avatarKey = [Cryptography generateRandomBytes:32]; + NSData* groupId = [Cryptography generateRandomBytes:8]; + TSAttachment *avatar = [[TSAttachment alloc] initWithAttachmentId:[NSNumber numberWithInt:42] contentMIMEType:@"image/jpg" decryptionKey:avatarKey]; + + + + TSGroupContext *groupContext = [[TSGroupContext alloc] initWithId:groupId withType:TSUpdateGroupContext withName:@"Winter Break of Code" withMembers:[NSArray arrayWithObjects:member1,member2,member3, nil] withAvatar:avatar]; + + + TSMessage *message = [[TSMessage alloc] initWithSenderId:@"1234" recipientId:@"1234567" date:[[NSDate alloc] init] content:@"Surf is up" attachements:nil groupId:nil]; + NSData *serializedMessageContent = [TSPushMessageContent serializedPushMessageContentForMessage:message withGroupContect:groupContext]; + + + TSPushMessageContent *deserializedPushContent = [[TSPushMessageContent alloc] initWithData:serializedMessageContent]; + + XCTAssertTrue([message.content isEqualToString:deserializedPushContent.body], @"Push message content serialization/deserialization failed"); + XCTAssertTrue(deserializedPushContent.groupContext!=nil, @"deserialization doesn't give us a group, at all"); + + TSGroupContext *groupContextDeserialized = deserializedPushContent.groupContext; + XCTAssertTrue([groupContextDeserialized.gid isEqualToData:groupContext.gid],@"deserialized group id doesn't match original"); + XCTAssertTrue(groupContextDeserialized.type==groupContext.type,@"deserialized group type doesn't match original"); + XCTAssertTrue([groupContextDeserialized.members count]==3,@"deserialized group doesn't have same number of members as original"); + + + XCTAssertTrue([[groupContextDeserialized.members objectAtIndex:0] isEqualToString:member1],@"deserialized group member 1 not the same as original"); + XCTAssertTrue([[groupContextDeserialized.members objectAtIndex:1] isEqualToString:member2],@"deserialized group member 1 not the same as original"); + XCTAssertTrue([[groupContextDeserialized.members objectAtIndex:2] isEqualToString:member3],@"deserialized group member 1 not the same as original"); + + + TSAttachment *groupContextAvatarDeserialized = deserializedPushContent.groupContext.avatar; + XCTAssertTrue([groupContextAvatarDeserialized.attachmentId isEqualToNumber:avatar.attachmentId], @"deserialized ids do not match for avatar"); + XCTAssertTrue(groupContextAvatarDeserialized.attachmentType == avatar.attachmentType, @"deserialized ids do not match for avatar"); + XCTAssertTrue([groupContextAvatarDeserialized.attachmentDecryptionKey isEqualToData:avatar.attachmentDecryptionKey], @"deserialized ids do not match for avatar"); + +} + + +-(void) testObjcDateToCpp { + NSDate* now = [NSDate date]; + uint64_t cppDate = [self.pbWrapper objcDateToCpp:now]; + NSDate *convertedNow = [self.pbWrapper cppDateToObjc:cppDate]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; + [dateFormatter setTimeStyle:NSDateFormatterNoStyle]; + NSString* nowString = [dateFormatter stringFromDate:now]; + NSString* convertedNowString = [dateFormatter stringFromDate:convertedNow]; + XCTAssertTrue([nowString isEqualToString:convertedNowString], @"date conversion is off conversion %@ not equal to original %@",convertedNowString,nowString); + +} + +-(void) testObjcStringToCpp { + NSString *string = @"Hawaii is amazing"; + const std::string cppString = [self.pbWrapper objcStringToCpp:string]; + NSString *convertedString = [self.pbWrapper cppStringToObjc:cppString]; + XCTAssertTrue([convertedString isEqualToString:string], @"date conversion is off conversion %@ not equal to original %@",convertedString,string); +} + +-(void) testObjcDataToCppString { + NSData* data = [Cryptography generateRandomBytes:64]; + const std::string cppDataString = [self.pbWrapper objcDataToCppString:data]; + NSData* convertedData = [self.pbWrapper cppStringToObjcData:cppDataString]; + XCTAssertTrue([convertedData isEqualToData:data], @"data conversion is off conversion %@ not equal to original %@",convertedData,data); +} + +-(void) testObjcNumberToCppUInt32 { + NSNumber *number = [NSNumber numberWithUnsignedInt:arc4random()]; + uint32_t cppNumber = [self.pbWrapper objcNumberToCppUInt32:number]; + NSNumber *convertedNumber = [self.pbWrapper cppUInt32ToNSNumber:cppNumber]; + XCTAssertTrue([number isEqualToNumber:convertedNumber], @"date conversion is off conversion %@ not equal to original %@",convertedNumber,number); +} + +-(void) testObjcNumberToCppUInt64 { + NSNumber *number = [NSNumber numberWithUnsignedLong:arc4random()]; + uint64_t cppNumber = [self.pbWrapper objcNumberToCppUInt64:number]; + NSNumber *convertedNumber = [self.pbWrapper cppUInt64ToNSNumber:cppNumber]; + XCTAssertTrue([number isEqualToNumber:convertedNumber], @"date conversion is off conversion %@ not equal to original %@",convertedNumber,number); +} + + +@end diff --git a/TextSecureiOS Tests/TSStorageMasterKeyTests.m b/TextSecureiOS Tests/TSStorageMasterKeyTests.m new file mode 100644 index 0000000..de0d1d0 --- /dev/null +++ b/TextSecureiOS Tests/TSStorageMasterKeyTests.m @@ -0,0 +1,176 @@ +// +// TSStorageMasterKeyTests.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSStorageMasterKey.h" +#import "TSStorageError.h" +#import "KeychainWrapper.h" +#import "Constants.h" + + +@interface TSStorageMasterKeyTests : XCTestCase + +@end + +static NSString *masterPw = @"1234test"; + + +@implementation TSStorageMasterKeyTests + +- (void)setUp +{ + [super setUp]; + [TSStorageMasterKey eraseStorageMasterKey]; +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; +} + + +- (void)testCreate +{ + NSError *error = nil; + XCTAssertFalse([TSStorageMasterKey wasStorageMasterKeyCreated], @"wrong default preference"); + + NSData *masterKey = [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:&error]; + XCTAssertNotNil(masterKey, @"master storage key creation returned nil"); + XCTAssertNil(error, @"master storage key creation returned an error"); + XCTAssertTrue([TSStorageMasterKey wasStorageMasterKeyCreated], @"master storage key creation did not update preferences"); +} + +- (void)testCreateAndOverwrite +{ + NSError *error = nil; + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:&error]; + NSData *masterKey = [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:&error]; + + XCTAssertNotNil(error, @"master storage key overwrite did not return an error"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key overwrite returned an unexcpected error"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyAlreadyCreated, @"master storage key overwrite returned an unexcpected error"); + XCTAssertNil(masterKey, @"master storage key overwrite returned an unexcpected error"); +} + +- (void)testUnlock +{ + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:masterPw error:&error]; + XCTAssertNotNil(masterKey, @"master storage key unlocking returned nil"); + XCTAssertNil(error, @"master storage key unlocking returned an error"); +} + +- (void)testUnlockWithWrongPassword +{ + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:@"wrongpassword" error:&error]; + + XCTAssertNotNil(error, @"master storage key was unlocked with an invalid password"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key unlocking returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorInvalidPassword, @"master storage key unlocking returned an unexpected error"); + XCTAssertNil(masterKey, @"master storage key was unlocked with an invalid password"); +} + +- (void)testUnlockBeforeCreation +{ + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:@"wrongpassword" error:&error]; + + XCTAssertNotNil(error, @"master storage key was unlocked before creation"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key unlocking returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyNotCreated, @"master storage key unlocking returned an unexpected error"); + XCTAssertNil(masterKey, @"master storage key was unlocked before creation"); +} + +- (void)testGetBeforeCreation +{ + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey getStorageMasterKeyWithError:&error]; + + XCTAssertNotNil(error, @"master storage key was unlocked before creation"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key unlocking returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyNotCreated, @"master storage key unlocking returned an unexpected error"); + XCTAssertNil(masterKey, @"master storage key was unlocked before creation"); +} + +- (void)testLock +{ + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + [TSStorageMasterKey lockStorageMasterKey]; + + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey getStorageMasterKeyWithError:&error]; + + XCTAssertNotNil(error, @"master storage key locking failed"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key unlocking returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyLocked, @"master storage key unlocking returned an unexpected error"); + + XCTAssertTrue([TSStorageMasterKey isStorageMasterKeyLocked],@"master storage key locking failed"); + XCTAssertNil(masterKey, @"master storage key locking failed"); +} + +- (void)testPasswordChange +{ + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + + NSString *newPassword = @"test4321"; + NSError *error = nil; + [TSStorageMasterKey changeStorageMasterKeyPasswordFrom:masterPw to:newPassword error:&error]; + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:masterPw error:&error]; + XCTAssertNil(masterKey, @"master storage key password change failed"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key unlocking returned an unexpected error"); + XCTAssertEqual([error code], TSStorageErrorInvalidPassword, @"master storage key unlocking returned an unexpected error"); + XCTAssertNotNil(error, @"master storage key password change failed"); + + error = nil; + masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:newPassword error:&error]; + XCTAssertNotNil(masterKey, @"master storage key unlocking returned nil"); + XCTAssertNil(error, @"master storage key unlocking returned an error"); + +} + +- (void)testUnlockWithDeletedKeychain +{ + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + [TSStorageMasterKey lockStorageMasterKey]; + [KeychainWrapper deleteItemFromKeychainWithIdentifier:encryptedMasterSecretKeyStorageId]; + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:masterPw error:&error]; + + XCTAssertNotNil(error, @"master storage key locking failed"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key was unlocked with deleted keychain"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyCorrupted, @"master storage key was unlocked with deleted keychain"); + XCTAssertNil(masterKey, @"master storage key was unlocked with deleted keychain"); +} + + +- (void)testErase +{ + [TSStorageMasterKey createStorageMasterKeyWithPassword:masterPw error:nil]; + [TSStorageMasterKey eraseStorageMasterKey]; + + NSError *error = nil; + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:masterPw error:&error]; + + XCTAssertNil(masterKey, @"master storage key deletion failed"); + XCTAssertNotNil(error, @"master storage key deletion failed"); + XCTAssertTrue([[error domain] isEqualToString:TSStorageErrorDomain], @"master storage key deletion failed"); + XCTAssertEqual([error code], TSStorageErrorStorageKeyNotCreated, @"master storage key deletion failed"); +} + + +@end diff --git a/TextSecureiOS Tests/TSUserKeysDatabaseTests.m b/TextSecureiOS Tests/TSUserKeysDatabaseTests.m new file mode 100644 index 0000000..1b7d76f --- /dev/null +++ b/TextSecureiOS Tests/TSUserKeysDatabaseTests.m @@ -0,0 +1,59 @@ +// +// TSUserKeysDatabaseTests.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSUserKeysDatabase.h" +#import "TSStorageMasterKey.h" +#import "Constants.h" +@interface TSUserKeysDatabaseTests : XCTestCase + +@end + + +static NSString *storageKeyPw = @"1234test"; + + +@implementation TSUserKeysDatabaseTests + +- (void)setUp +{ + [super setUp]; + + // Remove any existing DB + [TSUserKeysDatabase databaseErase]; + + // Create a storage master key + [TSStorageMasterKey eraseStorageMasterKey]; + [TSStorageMasterKey createStorageMasterKeyWithPassword:storageKeyPw error:nil]; +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; +} + + +- (void)testDatabaseCreate +{ + NSError *error = nil; + XCTAssertTrue([TSUserKeysDatabase databaseCreateUserKeysWithError:&error], @"database creation failed"); + XCTAssertNil(error, @"database creation returned an error"); + + + XCTAssertNotNil([TSUserKeysDatabase allPreKeys], @"database creation returned nil prekeys"); + XCTAssertNotNil([TSUserKeysDatabase identityKey], @"database creation returned nil identity key"); + // Check for key of last resort + XCTAssertNotNil([TSUserKeysDatabase preKeyWithId:kLastResortKeyId], @"database creation returned nil for key of last resort"); +} + + + + + +@end diff --git a/TextSecureiOS Tests/TSWaitingPushMessageDatabaseTests.m b/TextSecureiOS Tests/TSWaitingPushMessageDatabaseTests.m new file mode 100644 index 0000000..b0bdf28 --- /dev/null +++ b/TextSecureiOS Tests/TSWaitingPushMessageDatabaseTests.m @@ -0,0 +1,32 @@ +// +// TSWaitingPushMessageDatabaseTests.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +#warning needs implemented +@interface TSWaitingPushMessageDatabaseTests : XCTestCase + +@end + +@implementation TSWaitingPushMessageDatabaseTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here; it will be run once, before the first test case. +} + +- (void)tearDown +{ + // Put teardown code here; it will be run once, after the last test case. + [super tearDown]; +} + + + +@end diff --git a/TextSecureiOSTests/TextSecureiOSTests-Info.plist b/TextSecureiOS Tests/TextSecureiOS Tests-Info.plist similarity index 88% rename from TextSecureiOSTests/TextSecureiOSTests-Info.plist rename to TextSecureiOS Tests/TextSecureiOS Tests-Info.plist index edee81c..418bf8d 100644 --- a/TextSecureiOSTests/TextSecureiOSTests-Info.plist +++ b/TextSecureiOS Tests/TextSecureiOS Tests-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.openwhispersystems.${PRODUCT_NAME:rfc1034identifier} + org.whispersystems.whisper.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/TextSecureiOS Tests/TextSecureiOS Tests-Prefix.pch b/TextSecureiOS Tests/TextSecureiOS Tests-Prefix.pch new file mode 100644 index 0000000..3fdee9d --- /dev/null +++ b/TextSecureiOS Tests/TextSecureiOS Tests-Prefix.pch @@ -0,0 +1,10 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/TextSecureiOS Tests/VerificationViewControllerTests.m b/TextSecureiOS Tests/VerificationViewControllerTests.m new file mode 100644 index 0000000..76c4488 --- /dev/null +++ b/TextSecureiOS Tests/VerificationViewControllerTests.m @@ -0,0 +1,123 @@ +// +// VerificationViewControllerTests.m +// TextSecureiOS +// +// Created by Troy Farrell on 2/25/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "VerificationViewController.h" + +@interface VerificationViewControllerTests : XCTestCase +@property (strong, nonatomic) VerificationViewController *controller; +@end + +// Steal this private method for testing. +@interface VerificationViewController () +-(void)setLocaleCountry; +@end + +@implementation VerificationViewControllerTests + +- (void)setUp +{ + [super setUp]; + + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; + self.controller = [storyboard instantiateViewControllerWithIdentifier:@"VerificationViewController"]; + [self.controller performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES]; +} + +- (void)tearDown +{ + self.controller = nil; + [super tearDown]; +} + +// This test verifies that entering a basic US phone number works +-(void)testBasicUSPhoneNumber +{ + // Focus and set the countryCodeInput. + UITextField *countryCodeInput = self.controller.countryCodeInput; + [self.controller textFieldShouldBeginEditing:countryCodeInput]; + countryCodeInput.text = @"+1"; + + // Focus and set the phoneNumber. + UITextField *phoneNumber = self.controller.phoneNumber; + NSRange range = NSMakeRange(0, 0); + NSArray *number = @[@"2", @"1", @"2", @"7", @"3", @"6", @"5", @"0", @"0", @"0"]; + [self.controller textFieldShouldBeginEditing:phoneNumber]; + for (NSUInteger i = 0; i < number.count; i++) { + if (4 == i || 7 == i) { // account for dashes in these positions + range.location++; + } + [self.controller textField:phoneNumber shouldChangeCharactersInRange:range replacementString:[number objectAtIndex:i]]; + range.location++; + } + + NSString *expectedPhoneNUmber = @"212-736-5000"; + XCTAssert([phoneNumber.text isEqualToString:expectedPhoneNUmber], @"phoneNumber does not match expected value"); +} + +// This test is to ensure that the countryCodeInput is never empty. This is +// a related, but separate issue to the -(void)testEmptyCountryCodeCrash +// test. +- (void)testEmptyCountryCodeInputPrevention +{ + // Pretend that the view controller became visible. + [self.controller setLocaleCountry]; + + // Focus on the countryCodeInput. This causes the existing text to be + // cleared. + UITextField *countryCodeInput = self.controller.countryCodeInput; + [self.controller textFieldShouldBeginEditing:countryCodeInput]; + + // Verify that the countryCodeInput text field has only a +. + XCTAssert([countryCodeInput.text isEqualToString:@"+"], @"countryCodeInput text is not only a +"); + + // Stop editing the text field. + [self.controller textFieldDidEndEditing:countryCodeInput]; + + // The countryCodeInput text must not be @"" or @"+". + XCTAssert(countryCodeInput.text.length > 1, @"countryCodeInput text is too short."); + XCTAssert([[countryCodeInput.text substringToIndex:1] isEqualToString:@"+"], @"countryCodeInput text does not start with +"); + + NSString *numbersPart = [countryCodeInput.text substringFromIndex:1]; + NSCharacterSet *numbers = [NSCharacterSet decimalDigitCharacterSet]; + NSRange range = [numbersPart rangeOfCharacterFromSet:numbers]; + + XCTAssert(range.location != NSNotFound, @"countryCodeInput text does not contain a digit"); +} + +// This test is to ensure that focusing on the countryCodeInput after +// selecting a country does not cause the country code to be hidden. +-(void)testCountrySelectAfterFocusingCountryCodeInput +{ + // Pretend that the view controller became visible. + [self.controller setLocaleCountry]; + + // Focus, set and blur the countryCodeInput. + UITextField *countryCodeInput = self.controller.countryCodeInput; + [self.controller textFieldShouldBeginEditing:countryCodeInput]; + countryCodeInput.text = @"+33"; + [self.controller textFieldDidEndEditing:countryCodeInput]; + XCTAssert([countryCodeInput.text isEqualToString:@"+33"], @"countryCodeInput text does not match set value"); + + // Pretend that user selected a country from the CountryViewController. + NSString *plistFile = [[NSBundle mainBundle] pathForResource:countryInfoPathInMainBundle ofType:@"plist"]; + NSDictionary *countryDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile]; + NSArray *countryList = [[countryDict allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + NSString *countryTitleForAndorra = [countryList objectAtIndex:4]; + NSDictionary *countryInfoForAndorra = [countryDict objectForKey:countryTitleForAndorra]; + [self.controller countryChosen:[NSNotification notificationWithName:@"CountryChosen" object:nil userInfo:countryInfoForAndorra]]; + + // On exiting the CountryViewController, the countryCodeInput is + // selected again. This time, it should not replace the country code + // that was just selected. + [self.controller textFieldShouldBeginEditing:countryCodeInput]; + NSString *expectedCountryCode = [NSString stringWithFormat:@"+%@",[countryInfoForAndorra objectForKey:countryInfoKeyCountryCode]]; + XCTAssert([countryCodeInput.text isEqualToString:expectedCountryCode], @"countryCodeInput text does not match selected country info"); +} + +@end diff --git a/TextSecureiOSTests/en.lproj/InfoPlist.strings b/TextSecureiOS Tests/en.lproj/InfoPlist.strings similarity index 100% rename from TextSecureiOSTests/en.lproj/InfoPlist.strings rename to TextSecureiOS Tests/en.lproj/InfoPlist.strings diff --git a/TextSecureiOS.xcodeproj/project.pbxproj b/TextSecureiOS.xcodeproj/project.pbxproj index 62d83a0..0743d14 100644 --- a/TextSecureiOS.xcodeproj/project.pbxproj +++ b/TextSecureiOS.xcodeproj/project.pbxproj @@ -7,58 +7,56 @@ objects = { /* Begin PBXBuildFile section */ - A51B776F17050C42002B8E53 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B776517050C42002B8E53 /* FMDatabase.m */; }; - A51B777017050C42002B8E53 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B776717050C42002B8E53 /* FMDatabaseAdditions.m */; }; - A51B777117050C42002B8E53 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B776917050C42002B8E53 /* FMDatabasePool.m */; }; - A51B777217050C42002B8E53 /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B776B17050C42002B8E53 /* FMDatabaseQueue.m */; }; - A51B777417050C42002B8E53 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B776E17050C42002B8E53 /* FMResultSet.m */; }; - A51B778F17050C71002B8E53 /* NSObject+SBJson.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B777717050C71002B8E53 /* NSObject+SBJson.m */; }; - A51B779017050C71002B8E53 /* SBJsonParser.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B777A17050C71002B8E53 /* SBJsonParser.m */; }; - A51B779117050C71002B8E53 /* SBJsonStreamParser.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B777C17050C71002B8E53 /* SBJsonStreamParser.m */; }; - A51B779217050C71002B8E53 /* SBJsonStreamParserAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B777E17050C71002B8E53 /* SBJsonStreamParserAccumulator.m */; }; - A51B779317050C71002B8E53 /* SBJsonStreamParserAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778017050C71002B8E53 /* SBJsonStreamParserAdapter.m */; }; - A51B779417050C71002B8E53 /* SBJsonStreamParserState.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778217050C71002B8E53 /* SBJsonStreamParserState.m */; }; - A51B779517050C71002B8E53 /* SBJsonStreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778417050C71002B8E53 /* SBJsonStreamWriter.m */; }; - A51B779617050C71002B8E53 /* SBJsonStreamWriterAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778617050C71002B8E53 /* SBJsonStreamWriterAccumulator.m */; }; - A51B779717050C71002B8E53 /* SBJsonStreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778817050C71002B8E53 /* SBJsonStreamWriterState.m */; }; - A51B779817050C71002B8E53 /* SBJsonTokeniser.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778A17050C71002B8E53 /* SBJsonTokeniser.m */; }; - A51B779917050C71002B8E53 /* SBJsonUTF8Stream.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778C17050C71002B8E53 /* SBJsonUTF8Stream.m */; }; - A51B779A17050C71002B8E53 /* SBJsonWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B778E17050C71002B8E53 /* SBJsonWriter.m */; }; - A51B77A117050F8C002B8E53 /* MessagesDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B77A017050F8C002B8E53 /* MessagesDatabase.m */; }; + 05B94BF218BD28E400708011 /* VerificationViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 05B94BF118BD28E400708011 /* VerificationViewControllerTests.m */; }; + 05B94BF418BD2EA000708011 /* TextSecureStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A53718CD16FF95DF00C6FCCF /* TextSecureStoryboard.storyboard */; }; + 4C0ACB4B186C876200B328D5 /* TSMessageConversationCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C0ACB4A186C876200B328D5 /* TSMessageConversationCell.m */; }; + 4C77526D18707A46000FCCA6 /* PasswordUnlockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C77526B18707A46000FCCA6 /* PasswordUnlockViewController.m */; }; + 4CFDC8D2187215F600BDB649 /* TSMessagesDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CFDC8D1187215F600BDB649 /* TSMessagesDatabaseTests.m */; }; + 6FBCCDFF18D5361300C5B5F5 /* TSMessageIncomingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FBCCDFD18D5361300C5B5F5 /* TSMessageIncomingTest.m */; }; + 6FBCCE0018D5361300C5B5F5 /* TSMessageOutgoingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FBCCDFE18D5361300C5B5F5 /* TSMessageOutgoingTest.m */; }; + 6FCE040B18D427CE00C33B34 /* TSReplaceSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FCE040818D427CE00C33B34 /* TSReplaceSegue.m */; }; + 6FCE040C18D427CE00C33B34 /* TSStartOverSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FCE040A18D427CE00C33B34 /* TSStartOverSegue.m */; }; + 8C49FC1018497E86006132F6 /* TSStorageError.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C49FC0F18497E86006132F6 /* TSStorageError.m */; }; + 8CF7BD8F187097570023A781 /* TSECKeyPairTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BD8E187097570023A781 /* TSECKeyPairTests.m */; }; + 8CF7BD951870C8450023A781 /* TSStorageMasterKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BD941870C8450023A781 /* TSStorageMasterKey.m */; }; + 8CF7BD9B1870F23D0023A781 /* TSUserKeysDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BD9A1870F23D0023A781 /* TSUserKeysDatabase.m */; }; + 8CF7BD9D18711B140023A781 /* TSUserKeysDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BD9C18711B140023A781 /* TSUserKeysDatabaseTests.m */; }; + 8CF7BD9F187130F80023A781 /* TSStorageMasterKeyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BD9E187130F80023A781 /* TSStorageMasterKeyTests.m */; }; + 8CF7BDA1187137640023A781 /* TSEncryptedDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BDA0187137640023A781 /* TSEncryptedDatabaseTests.m */; }; + A513465D18D65D97005B4FA3 /* TSDeregisterAccountRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A513465C18D65D97005B4FA3 /* TSDeregisterAccountRequest.m */; }; + A5145B31184AAD51007BDC73 /* TSMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = A5145B30184AAD51007BDC73 /* TSMessage.m */; }; + A5145B37184B66AD007BDC73 /* TSRequestAttachmentId.m in Sources */ = {isa = PBXBuildFile; fileRef = A5145B36184B66AD007BDC73 /* TSRequestAttachmentId.m */; }; + A5145B3A184B68EA007BDC73 /* TSRequestAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = A5145B39184B68EA007BDC73 /* TSRequestAttachment.m */; }; + A5145B3D184B6F35007BDC73 /* TSAttachmentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A5145B3C184B6F35007BDC73 /* TSAttachmentManager.m */; }; + A515AFDB180966E30084ABA5 /* TSMessagesDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = A515AFDA180966E30084ABA5 /* TSMessagesDatabase.m */; }; + A5169D08170FEA8D00A2CE9E /* VerificationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169CFC170FEA8D00A2CE9E /* VerificationViewController.m */; }; + A5169D09170FEA8D00A2CE9E /* TextSecureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169CFD170FEA8D00A2CE9E /* TextSecureViewController.m */; }; + A5169D0C170FEA8D00A2CE9E /* CountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D04170FEA8D00A2CE9E /* CountryViewController.m */; }; + A5169D0D170FEA8D00A2CE9E /* CountrySegue.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D06170FEA8D00A2CE9E /* CountrySegue.m */; }; + A5169D1E170FEB1900A2CE9E /* FilePath.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D15170FEB1900A2CE9E /* FilePath.m */; }; + A5169D1F170FEB1900A2CE9E /* NSString+Conversion.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D17170FEB1900A2CE9E /* NSString+Conversion.m */; }; + A5169D20170FEB1900A2CE9E /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D19170FEB1900A2CE9E /* Constants.m */; }; + A5169D21170FEB1900A2CE9E /* NSData+Conversion.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D1B170FEB1900A2CE9E /* NSData+Conversion.m */; }; + A5169D2B170FEB8F00A2CE9E /* KeychainWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D23170FEB8E00A2CE9E /* KeychainWrapper.m */; }; + A5169D2E170FEB8F00A2CE9E /* Cryptography.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D26170FEB8F00A2CE9E /* Cryptography.m */; }; + A5169D3D170FEDC600A2CE9E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D39170FEDC600A2CE9E /* main.m */; }; + A5195B9118CBBC0300AFEE04 /* contact_mask.png in Resources */ = {isa = PBXBuildFile; fileRef = A5195B8D18CBBC0300AFEE04 /* contact_mask.png */; }; + A5195B9218CBBC0300AFEE04 /* contact_mask@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A5195B8E18CBBC0300AFEE04 /* contact_mask@2x.png */; }; + A5195B9318CBBC0300AFEE04 /* Default_person.png in Resources */ = {isa = PBXBuildFile; fileRef = A5195B8F18CBBC0300AFEE04 /* Default_person.png */; }; + A5195B9418CBBC0300AFEE04 /* Default_person@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A5195B9018CBBC0300AFEE04 /* Default_person@2x.png */; }; + A5195B9618CBC00100AFEE04 /* apple_logo_animated.gif in Resources */ = {isa = PBXBuildFile; fileRef = A5195B9518CBC00100AFEE04 /* apple_logo_animated.gif */; }; A51B77A317051096002B8E53 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A51B77A217051096002B8E53 /* libsqlite3.dylib */; }; - A51B77A617051417002B8E53 /* Message.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B77A517051417002B8E53 /* Message.m */; }; - A51B77AC17052C5C002B8E53 /* FilePath.m in Sources */ = {isa = PBXBuildFile; fileRef = A51B77AB17052C5C002B8E53 /* FilePath.m */; }; - A51B77B3170561C3002B8E53 /* textsecure-verifybutton.png in Resources */ = {isa = PBXBuildFile; fileRef = A51B77AE170561C3002B8E53 /* textsecure-verifybutton.png */; }; - A51B77B4170561C3002B8E53 /* textsecure-selectyourcountry-field.png in Resources */ = {isa = PBXBuildFile; fileRef = A51B77AF170561C3002B8E53 /* textsecure-selectyourcountry-field.png */; }; - A51B77B5170561C3002B8E53 /* textsecure-phonenumber-field.png in Resources */ = {isa = PBXBuildFile; fileRef = A51B77B0170561C3002B8E53 /* textsecure-phonenumber-field.png */; }; - A51B77B6170561C3002B8E53 /* textsecure-gettingstarted-menubar.png in Resources */ = {isa = PBXBuildFile; fileRef = A51B77B1170561C3002B8E53 /* textsecure-gettingstarted-menubar.png */; }; - A51B77B7170561C3002B8E53 /* textsecure-countrycode-field.png in Resources */ = {isa = PBXBuildFile; fileRef = A51B77B2170561C3002B8E53 /* textsecure-countrycode-field.png */; }; - A51B77B917058E6C002B8E53 /* TextSecure-Global-canvas.png in Resources */ = {isa = PBXBuildFile; fileRef = A51B77B817058E6C002B8E53 /* TextSecure-Global-canvas.png */; }; + A52CE05D1862D88B00AF9E39 /* CryptographyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = A52CE05C1862D88B00AF9E39 /* CryptographyTests.mm */; }; + A52ECA7C17ED097300C80BC7 /* NSData+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = A52ECA7B17ED097300C80BC7 /* NSData+Base64.m */; }; + A530CB04184A02620052B615 /* TSRecipientPrekeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A530CB03184A02620052B615 /* TSRecipientPrekeyRequest.m */; }; A53718B616FF95DF00C6FCCF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718B516FF95DF00C6FCCF /* UIKit.framework */; }; A53718B816FF95DF00C6FCCF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718B716FF95DF00C6FCCF /* Foundation.framework */; }; A53718BA16FF95DF00C6FCCF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718B916FF95DF00C6FCCF /* CoreGraphics.framework */; }; - A53718C216FF95DF00C6FCCF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A53718C116FF95DF00C6FCCF /* main.m */; }; A53718C616FF95DF00C6FCCF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A53718C516FF95DF00C6FCCF /* AppDelegate.m */; }; - A53718CF16FF95DF00C6FCCF /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A53718CD16FF95DF00C6FCCF /* MainStoryboard.storyboard */; }; - A53718D216FF95DF00C6FCCF /* SMSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A53718D116FF95DF00C6FCCF /* SMSViewController.m */; }; - A53718DA16FF95DF00C6FCCF /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718D916FF95DF00C6FCCF /* SenTestingKit.framework */; }; - A53718DB16FF95DF00C6FCCF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718B516FF95DF00C6FCCF /* UIKit.framework */; }; - A53718DC16FF95DF00C6FCCF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718B716FF95DF00C6FCCF /* Foundation.framework */; }; - A53718E416FF95DF00C6FCCF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A53718E216FF95DF00C6FCCF /* InfoPlist.strings */; }; - A53718E716FF95DF00C6FCCF /* TextSecureiOSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A53718E616FF95DF00C6FCCF /* TextSecureiOSTests.m */; }; + A53718CF16FF95DF00C6FCCF /* TextSecureStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A53718CD16FF95DF00C6FCCF /* TextSecureStoryboard.storyboard */; }; A53718F116FF98C100C6FCCF /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53718F016FF98C100C6FCCF /* MessageUI.framework */; }; - A53718FF16FFB6F800C6FCCF /* tutorial_ok_button.png in Resources */ = {isa = PBXBuildFile; fileRef = A53718FD16FFB6F800C6FCCF /* tutorial_ok_button.png */; }; - A537190016FFB6F800C6FCCF /* tutorial_ok_button@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A53718FE16FFB6F800C6FCCF /* tutorial_ok_button@2x.png */; }; - A537190216FFB92C00C6FCCF /* tutorial_tutorial_arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = A537190116FFB92C00C6FCCF /* tutorial_tutorial_arrow.png */; }; - A537190516FFBEE300C6FCCF /* VerificationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A537190416FFBEE300C6FCCF /* VerificationViewController.m */; }; - A537190816FFC45C00C6FCCF /* textsecure.png in Resources */ = {isa = PBXBuildFile; fileRef = A537190616FFC45B00C6FCCF /* textsecure.png */; }; A537190916FFC45C00C6FCCF /* textsecure@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A537190716FFC45B00C6FCCF /* textsecure@2x.png */; }; - A537190C1700247600C6FCCF /* Server.m in Sources */ = {isa = PBXBuildFile; fileRef = A537190B1700247400C6FCCF /* Server.m */; }; - A537190F1700284900C6FCCF /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = A537190E1700284800C6FCCF /* Constants.m */; }; A53719251700D71B00C6FCCF /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A53719241700D71A00C6FCCF /* CoreTelephony.framework */; }; - A53719281700DC3300C6FCCF /* CountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A53719271700DC3100C6FCCF /* CountryViewController.m */; }; - A537192E1700FAAB00C6FCCF /* CountryCodes.plist in Resources */ = {isa = PBXBuildFile; fileRef = A537192D1700FAA900C6FCCF /* CountryCodes.plist */; }; - A53719311701040600C6FCCF /* CountrySegue.m in Sources */ = {isa = PBXBuildFile; fileRef = A53719301701040400C6FCCF /* CountrySegue.m */; }; A537294B17011A5200C6FCCF /* ad.png in Resources */ = {isa = PBXBuildFile; fileRef = A53727FE17011A4F00C6FCCF /* ad.png */; }; A537294C17011A5200C6FCCF /* ae.png in Resources */ = {isa = PBXBuildFile; fileRef = A53727FF17011A4F00C6FCCF /* ae.png */; }; A537294D17011A5200C6FCCF /* af.png in Resources */ = {isa = PBXBuildFile; fileRef = A537280017011A4F00C6FCCF /* af.png */; }; @@ -304,7 +302,6 @@ A5372A3D17011A5300C6FCCF /* qu.png in Resources */ = {isa = PBXBuildFile; fileRef = A53728F017011A5100C6FCCF /* qu.png */; }; A5372A3E17011A5300C6FCCF /* qu@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A53728F117011A5100C6FCCF /* qu@2x.png */; }; A5372A3F17011A5300C6FCCF /* re.png in Resources */ = {isa = PBXBuildFile; fileRef = A53728F217011A5100C6FCCF /* re.png */; }; - A5372A4017011A5300C6FCCF /* resizeAll.sh in Resources */ = {isa = PBXBuildFile; fileRef = A53728F317011A5100C6FCCF /* resizeAll.sh */; }; A5372A4117011A5300C6FCCF /* ro.png in Resources */ = {isa = PBXBuildFile; fileRef = A53728F417011A5100C6FCCF /* ro.png */; }; A5372A4217011A5300C6FCCF /* ro@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A53728F517011A5100C6FCCF /* ro@2x.png */; }; A5372A4317011A5300C6FCCF /* rs.png in Resources */ = {isa = PBXBuildFile; fileRef = A53728F617011A5100C6FCCF /* rs.png */; }; @@ -392,38 +389,92 @@ A5372A9517011A5300C6FCCF /* zh.png in Resources */ = {isa = PBXBuildFile; fileRef = A537294817011A5200C6FCCF /* zh.png */; }; A5372A9617011A5300C6FCCF /* zm.png in Resources */ = {isa = PBXBuildFile; fileRef = A537294917011A5200C6FCCF /* zm.png */; }; A5372A9717011A5300C6FCCF /* zw.png in Resources */ = {isa = PBXBuildFile; fileRef = A537294A17011A5200C6FCCF /* zw.png */; }; - A5F15A9817027FD000856385 /* Cryptography.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15A9717027FCF00856385 /* Cryptography.m */; }; + A538A27E1869DC6500CD0A3D /* TSKeyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A538A27D1869DC6500CD0A3D /* TSKeyManager.m */; }; + A539ED1118D5E06F006C1A61 /* TSSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A539ED1018D5E06F006C1A61 /* TSSettingsViewController.m */; }; + A5620DEF186BD4FA0033483E /* TSSubmitMessageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A5620DEE186BD4FA0033483E /* TSSubmitMessageRequest.m */; }; + A56312F818C85B6500222F99 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = A56312F718C85B6500222F99 /* Settings.bundle */; }; + A56312FB18C9A45A00222F99 /* TSWaitingPushMessageDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = A56312FA18C9A45A00222F99 /* TSWaitingPushMessageDatabase.m */; }; + A56312FD18C9BDCD00222F99 /* TSWaitingPushMessageDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A56312FC18C9BDCD00222F99 /* TSWaitingPushMessageDatabaseTests.m */; }; + A56807B818CCAF2A0059083C /* TSGroupContext.m in Sources */ = {isa = PBXBuildFile; fileRef = A56807B718CCAF290059083C /* TSGroupContext.m */; }; + A568910117E2F37000C360A1 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A568910017E2F37000C360A1 /* CoreText.framework */; }; + A568910317E2F37A00C360A1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A568910217E2F37A00C360A1 /* QuartzCore.framework */; }; + A56D31B9180FCA840093D51B /* TSRegisterPrekeysRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A56D31B8180FCA840093D51B /* TSRegisterPrekeysRequest.m */; }; + A580D2BD19323E4A00D6C1CC /* UIColor+TextSecure.m in Sources */ = {isa = PBXBuildFile; fileRef = A580D2BC19323E4A00D6C1CC /* UIColor+TextSecure.m */; }; + A5DE19D2170702E900ACAFDD /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5DE19D1170702E900ACAFDD /* AddressBookUI.framework */; }; + A5DE19D4170702ED00ACAFDD /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5DE19D3170702ED00ACAFDD /* AddressBook.framework */; }; + A5E6CBFC187629D4002A1735 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5E6CBFB187629D4002A1735 /* MediaPlayer.framework */; }; + A5E6CC09187AC6D5002A1735 /* TSDownloadAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC08187AC6D5002A1735 /* TSDownloadAttachment.m */; }; + A5E6CC6D187D0F4B002A1735 /* IncomingPushMessageSignal.pb.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC62187D0F4B002A1735 /* IncomingPushMessageSignal.pb.mm */; }; + A5E6CC6E187D0F4B002A1735 /* IncomingPushMessageSignal.proto in Resources */ = {isa = PBXBuildFile; fileRef = A5E6CC63187D0F4B002A1735 /* IncomingPushMessageSignal.proto */; }; + A5E6CC6F187D0F4B002A1735 /* PreKeyWhisperMessage.pb.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC65187D0F4B002A1735 /* PreKeyWhisperMessage.pb.mm */; }; + A5E6CC70187D0F4B002A1735 /* PreKeyWhisperMessage.proto in Resources */ = {isa = PBXBuildFile; fileRef = A5E6CC66187D0F4B002A1735 /* PreKeyWhisperMessage.proto */; }; + A5E6CC71187D0F4B002A1735 /* PushMessageContent.pb.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC68187D0F4B002A1735 /* PushMessageContent.pb.mm */; }; + A5E6CC72187D0F4B002A1735 /* PushMessageContent.proto in Resources */ = {isa = PBXBuildFile; fileRef = A5E6CC69187D0F4B002A1735 /* PushMessageContent.proto */; }; + A5E6CC73187D0F4B002A1735 /* WhisperMessage.pb.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC6B187D0F4B002A1735 /* WhisperMessage.pb.mm */; }; + A5E6CC74187D0F4B002A1735 /* WhisperMessage.proto in Resources */ = {isa = PBXBuildFile; fileRef = A5E6CC6C187D0F4B002A1735 /* WhisperMessage.proto */; }; + A5E6CC82187D0FF1002A1735 /* TSEncryptedWhisperMessage.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC76187D0FF1002A1735 /* TSEncryptedWhisperMessage.mm */; }; + A5E6CC83187D0FF1002A1735 /* TSMessageSignal.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC78187D0FF1002A1735 /* TSMessageSignal.mm */; }; + A5E6CC84187D0FF1002A1735 /* TSPreKeyWhisperMessage.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC7A187D0FF1002A1735 /* TSPreKeyWhisperMessage.mm */; }; + A5E6CC85187D0FF1002A1735 /* TSProtocolBufferWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC7C187D0FF1002A1735 /* TSProtocolBufferWrapper.mm */; }; + A5E6CC86187D0FF1002A1735 /* TSPushMessageContent.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC7E187D0FF1002A1735 /* TSPushMessageContent.mm */; }; + A5E6CC90187D1E41002A1735 /* TSWhisperMessage.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC8F187D1E41002A1735 /* TSWhisperMessage.mm */; }; + A5E6CC9B18826005002A1735 /* TSProtocolBufferWrapperTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC9A18826005002A1735 /* TSProtocolBufferWrapperTests.mm */; }; + A5E6CC9E18837C31002A1735 /* TSAxolotlRatchet.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5E6CC9D18837C31002A1735 /* TSAxolotlRatchet.mm */; }; A5F15A9A170280B700856385 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5F15A99170280B700856385 /* Security.framework */; }; - A5F15A9D1702949200856385 /* NSData+Conversion.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15A9C1702949100856385 /* NSData+Conversion.m */; }; - A5F15AA01702997A00856385 /* KeychainWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15A9F1702997A00856385 /* KeychainWrapper.m */; }; - A5F15AA61702DF6E00856385 /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5F15AA41702DF6E00856385 /* libssl.a */; }; - A5F15AA71702DF6E00856385 /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5F15AA51702DF6E00856385 /* libcrypto.a */; }; - A5F15B2F1703975F00856385 /* RNCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B221703975F00856385 /* RNCryptor.m */; }; - A5F15B301703975F00856385 /* RNCryptorEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B241703975F00856385 /* RNCryptorEngine.m */; }; - A5F15B311703975F00856385 /* RNDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B261703975F00856385 /* RNDecryptor.m */; }; - A5F15B321703975F00856385 /* RNEncryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B281703975F00856385 /* RNEncryptor.m */; }; - A5F15B331703975F00856385 /* RNOpenSSLCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B2A1703975F00856385 /* RNOpenSSLCryptor.m */; }; - A5F15B341703975F00856385 /* RNOpenSSLDecryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B2C1703975F00856385 /* RNOpenSSLDecryptor.m */; }; - A5F15B351703975F00856385 /* RNOpenSSLEncryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B2E1703975F00856385 /* RNOpenSSLEncryptor.m */; }; - A5F15B381703B98F00856385 /* NSString+Conversion.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B371703B98F00856385 /* NSString+Conversion.m */; }; - A5F15B3B1703D15000856385 /* UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = A5F15B3A1703D14F00856385 /* UserDefaults.m */; }; - A5F15B3E1703EB9F00856385 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B3C1703EB9F00856385 /* Default@2x.png */; }; - A5F15B3F1703EB9F00856385 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B3D1703EB9F00856385 /* Default.png */; }; - A5F15B491703EE2F00856385 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B481703EE2F00856385 /* Default-568h@2x.png */; }; - A5F15B551703F20D00856385 /* OpenSans-SemiboldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B4B1703F20D00856385 /* OpenSans-SemiboldItalic.ttf */; }; - A5F15B561703F20D00856385 /* OpenSans-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B4C1703F20D00856385 /* OpenSans-Semibold.ttf */; }; - A5F15B571703F20D00856385 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B4D1703F20D00856385 /* OpenSans-Regular.ttf */; }; - A5F15B581703F20D00856385 /* OpenSans-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B4E1703F20D00856385 /* OpenSans-LightItalic.ttf */; }; - A5F15B591703F20D00856385 /* OpenSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B4F1703F20D00856385 /* OpenSans-Light.ttf */; }; - A5F15B5A1703F20D00856385 /* OpenSans-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B501703F20D00856385 /* OpenSans-Italic.ttf */; }; - A5F15B5B1703F20D00856385 /* OpenSans-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B511703F20D00856385 /* OpenSans-ExtraBoldItalic.ttf */; }; - A5F15B5C1703F20D00856385 /* OpenSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B521703F20D00856385 /* OpenSans-ExtraBold.ttf */; }; - A5F15B5D1703F20D00856385 /* OpenSans-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B531703F20D00856385 /* OpenSans-BoldItalic.ttf */; }; - A5F15B5E1703F20D00856385 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A5F15B541703F20D00856385 /* OpenSans-Bold.ttf */; }; + A5FBAB95184E980E0012E9A5 /* TSUploadAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = A5FBAB94184E980E0012E9A5 /* TSUploadAttachment.m */; }; + A5FE7D84191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5FE7D7F191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.m */; }; + A5FE7D85191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5FE7D81191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.m */; }; + A5FE7D86191FA1FA0022C3F7 /* TSVerifyIdentityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5FE7D83191FA1FA0022C3F7 /* TSVerifyIdentityViewController.m */; }; + A6F91EC0B0694C57850C03BA /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AD77BB98A5B14A3490A31E06 /* libPods.a */; }; + B602B96318CE9A4B00181564 /* TSReceivingChain.m in Sources */ = {isa = PBXBuildFile; fileRef = B602B96218CE9A4B00181564 /* TSReceivingChain.m */; }; + B621AA731928032D00085CF7 /* TSSocketManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B621AA721928032D00085CF7 /* TSSocketManager.m */; }; + B635B3C7182181E1003CE732 /* TSMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B635B390182181E1003CE732 /* TSMessageViewController.m */; }; + B635B3ED18218456003CE732 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B635B3EC18218456003CE732 /* CoreAudio.framework */; }; + B635B3EF18218481003CE732 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B635B3EE18218481003CE732 /* AudioToolbox.framework */; }; + B639AF0D17F5A68100B2244D /* TSNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B639AF0C17F5A68100B2244D /* TSNetworkManager.m */; }; + B639AF1117F5AAA300B2244D /* TSRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B639AF1017F5AAA300B2244D /* TSRequest.m */; }; + B639F69F180955E100B7DF00 /* TSServerCodeVerificationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B639F69E180955E100B7DF00 /* TSServerCodeVerificationRequest.m */; }; + B63A72161808B8AF00C353F2 /* VerificationCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B63A72151808B8AF00C353F2 /* VerificationCodeViewController.m */; }; + B63A72181808BD3D00C353F2 /* gcm.textsecure.whispersystems.org.cer in Resources */ = {isa = PBXBuildFile; fileRef = B63A72171808BD3D00C353F2 /* gcm.textsecure.whispersystems.org.cer */; }; + B643D7DE189EB9020025C6F3 /* TSContactPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B643D7DD189EB9020025C6F3 /* TSContactPickerViewController.m */; }; + B644F95E18DF5D3700D41FA8 /* NSString+randomString.m in Sources */ = {isa = PBXBuildFile; fileRef = B644F95D18DF5D3700D41FA8 /* NSString+randomString.m */; }; + B646538A18E74D9C006010E0 /* TSDerivedSecrets.m in Sources */ = {isa = PBXBuildFile; fileRef = B646538918E74D9C006010E0 /* TSDerivedSecrets.m */; }; + B67E30F618CE19E4008502F5 /* TSContact.m in Sources */ = {isa = PBXBuildFile; fileRef = B67E30F518CE19E4008502F5 /* TSContact.m */; }; + B67E30F918CE1AA8008502F5 /* TSGroupSetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B67E30F818CE1AA8008502F5 /* TSGroupSetupViewController.m */; }; + B684346618D1FF2B007AEDF6 /* TSSendingChain.m in Sources */ = {isa = PBXBuildFile; fileRef = B684346518D1FF2B007AEDF6 /* TSSendingChain.m */; }; + B68BFDF2180987C6005CC817 /* TSSetMasterPasswordViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B68BFDF1180987C6005CC817 /* TSSetMasterPasswordViewController.m */; }; + B6B478E61809E7F5005DDE12 /* TSContactsIntersectionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B478E51809E7F5005DDE12 /* TSContactsIntersectionRequest.m */; }; + B6C5AE5817C212330022EA53 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B6C5AE5717C212330022EA53 /* Default-568h@2x.png */; }; + B6C5FA7418CE344700ECC200 /* TSConversation.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA7318CE344700ECC200 /* TSConversation.m */; }; + B6C5FA7718CE354800ECC200 /* TSSession.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA7618CE354800ECC200 /* TSSession.m */; }; + B6C5FA7A18CE355500ECC200 /* TSChainKey.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA7918CE355500ECC200 /* TSChainKey.m */; }; + B6C5FA7D18CE357200ECC200 /* TSGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA7C18CE357200ECC200 /* TSGroup.m */; }; + B6C5FA8018CE3AD600ECC200 /* TSAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA7F18CE3AD600ECC200 /* TSAttachment.m */; }; + B6C5FA8318CE3AED00ECC200 /* TSContactManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA8218CE3AED00ECC200 /* TSContactManager.m */; }; + B6C5FA8618CE3AFB00ECC200 /* TSECKeyPair.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA8518CE3AFB00ECC200 /* TSECKeyPair.m */; }; + B6C5FA8918CE3B0900ECC200 /* TSMessageIncoming.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA8818CE3B0900ECC200 /* TSMessageIncoming.m */; }; + B6C5FA8C18CE3B1000ECC200 /* TSMessageOutgoing.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA8B18CE3B1000ECC200 /* TSMessageOutgoing.m */; }; + B6C5FA8F18CE3B2000ECC200 /* TSPrekey.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA8E18CE3B2000ECC200 /* TSPrekey.m */; }; + B6C5FA9218CE3B3900ECC200 /* TSMessageKeys.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA9118CE3B3900ECC200 /* TSMessageKeys.m */; }; + B6C5FA9518CE3B4600ECC200 /* TSMessagesManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA9418CE3B4600ECC200 /* TSMessagesManager.mm */; }; + B6C5FA9818CE3B6300ECC200 /* RKCK.m in Sources */ = {isa = PBXBuildFile; fileRef = B6C5FA9718CE3B6300ECC200 /* RKCK.m */; }; + B6C5FA9918CE3BB900ECC200 /* TSDatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF7BD971870D9450023A781 /* TSDatabaseManager.m */; }; + B6CA8B2418E38F0D0088BC0C /* TSAxolotlConsistencyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B6CA8B2318E38F0D0088BC0C /* TSAxolotlConsistencyTest.m */; }; + B6CC7EAB18D52F09006480FB /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = A5169D19170FEB1900A2CE9E /* Constants.m */; }; + B6D23DAF18A91E9800F7E932 /* TSRegisterForPushRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B68A2291180AC32C00D93C16 /* TSRegisterForPushRequest.m */; }; + B6E2CFB317F7926000200B69 /* TSRequestVerificationCodeRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E2CFB217F7926000200B69 /* TSRequestVerificationCodeRequest.m */; }; + B6E2CFB617F794F600200B69 /* NSString+escape.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E2CFB517F794F600200B69 /* NSString+escape.m */; }; + B6F10B1717EF4A3A00723719 /* NSLocale+phonePrefixes.m in Sources */ = {isa = PBXBuildFile; fileRef = B6F10B1617EF4A3A00723719 /* NSLocale+phonePrefixes.m */; }; + B6F10B1C17EF61FE00723719 /* CountryCodes.plist in Resources */ = {isa = PBXBuildFile; fileRef = B6F10B1B17EF61FE00723719 /* CountryCodes.plist */; }; + B6F10B1F17EF8F3C00723719 /* NSString+PhoneFormating.m in Sources */ = {isa = PBXBuildFile; fileRef = B6F10B1E17EF8F3C00723719 /* NSString+PhoneFormating.m */; }; + B6F7FEC018D77E1500ECC4A2 /* NSData+TSKeyVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = B6F7FEBF18D77E1500ECC4A2 /* NSData+TSKeyVersion.m */; }; + CE74D2F57964464494640956 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AD77BB98A5B14A3490A31E06 /* libPods.a */; }; + F8694B5E18C5268500A44474 /* padlock_prompt.png in Resources */ = {isa = PBXBuildFile; fileRef = F8694B5D18C5268500A44474 /* padlock_prompt.png */; }; + FCD30FC918FDBBB7003BD188 /* SignUpStepsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCD30FC818FDBBB7003BD188 /* SignUpStepsViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - A53718DD16FF95DF00C6FCCF /* PBXContainerItemProxy */ = { + 8C42C14718551B8C000DD353 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A53718AA16FF95DF00C6FCCF /* Project object */; proxyType = 1; @@ -433,90 +484,93 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - A51B776417050C42002B8E53 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; - A51B776517050C42002B8E53 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; - A51B776617050C42002B8E53 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = ""; }; - A51B776717050C42002B8E53 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = ""; }; - A51B776817050C42002B8E53 /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = ""; }; - A51B776917050C42002B8E53 /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = ""; }; - A51B776A17050C42002B8E53 /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseQueue.h; sourceTree = ""; }; - A51B776B17050C42002B8E53 /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseQueue.m; sourceTree = ""; }; - A51B776D17050C42002B8E53 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = ""; }; - A51B776E17050C42002B8E53 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = ""; }; - A51B777617050C71002B8E53 /* NSObject+SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+SBJson.h"; sourceTree = ""; }; - A51B777717050C71002B8E53 /* NSObject+SBJson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+SBJson.m"; sourceTree = ""; }; - A51B777817050C71002B8E53 /* SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson.h; sourceTree = ""; }; - A51B777917050C71002B8E53 /* SBJsonParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonParser.h; sourceTree = ""; }; - A51B777A17050C71002B8E53 /* SBJsonParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonParser.m; sourceTree = ""; }; - A51B777B17050C71002B8E53 /* SBJsonStreamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParser.h; sourceTree = ""; }; - A51B777C17050C71002B8E53 /* SBJsonStreamParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParser.m; sourceTree = ""; }; - A51B777D17050C71002B8E53 /* SBJsonStreamParserAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAccumulator.h; sourceTree = ""; }; - A51B777E17050C71002B8E53 /* SBJsonStreamParserAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAccumulator.m; sourceTree = ""; }; - A51B777F17050C71002B8E53 /* SBJsonStreamParserAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAdapter.h; sourceTree = ""; }; - A51B778017050C71002B8E53 /* SBJsonStreamParserAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAdapter.m; sourceTree = ""; }; - A51B778117050C71002B8E53 /* SBJsonStreamParserState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserState.h; sourceTree = ""; }; - A51B778217050C71002B8E53 /* SBJsonStreamParserState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserState.m; sourceTree = ""; }; - A51B778317050C71002B8E53 /* SBJsonStreamWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriter.h; sourceTree = ""; }; - A51B778417050C71002B8E53 /* SBJsonStreamWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriter.m; sourceTree = ""; }; - A51B778517050C71002B8E53 /* SBJsonStreamWriterAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterAccumulator.h; sourceTree = ""; }; - A51B778617050C71002B8E53 /* SBJsonStreamWriterAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterAccumulator.m; sourceTree = ""; }; - A51B778717050C71002B8E53 /* SBJsonStreamWriterState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterState.h; sourceTree = ""; }; - A51B778817050C71002B8E53 /* SBJsonStreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterState.m; sourceTree = ""; }; - A51B778917050C71002B8E53 /* SBJsonTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonTokeniser.h; sourceTree = ""; }; - A51B778A17050C71002B8E53 /* SBJsonTokeniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonTokeniser.m; sourceTree = ""; }; - A51B778B17050C71002B8E53 /* SBJsonUTF8Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonUTF8Stream.h; sourceTree = ""; }; - A51B778C17050C71002B8E53 /* SBJsonUTF8Stream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonUTF8Stream.m; sourceTree = ""; }; - A51B778D17050C71002B8E53 /* SBJsonWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonWriter.h; sourceTree = ""; }; - A51B778E17050C71002B8E53 /* SBJsonWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonWriter.m; sourceTree = ""; }; - A51B779F17050F8C002B8E53 /* MessagesDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagesDatabase.h; sourceTree = ""; }; - A51B77A017050F8C002B8E53 /* MessagesDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessagesDatabase.m; sourceTree = ""; }; + 05B94BF118BD28E400708011 /* VerificationViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VerificationViewControllerTests.m; sourceTree = ""; wrapsLines = 1; }; + 4C0ACB49186C876200B328D5 /* TSMessageConversationCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessageConversationCell.h; path = Views/Cells/TSMessageConversationCell.h; sourceTree = ""; }; + 4C0ACB4A186C876200B328D5 /* TSMessageConversationCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageConversationCell.m; path = Views/Cells/TSMessageConversationCell.m; sourceTree = ""; }; + 4C77526A18707A46000FCCA6 /* PasswordUnlockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PasswordUnlockViewController.h; path = ViewControllers/PasswordUnlockViewController.h; sourceTree = ""; }; + 4C77526B18707A46000FCCA6 /* PasswordUnlockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PasswordUnlockViewController.m; path = ViewControllers/PasswordUnlockViewController.m; sourceTree = ""; }; + 4CFDC8D1187215F600BDB649 /* TSMessagesDatabaseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessagesDatabaseTests.m; sourceTree = ""; }; + 6FBCCDFD18D5361300C5B5F5 /* TSMessageIncomingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageIncomingTest.m; sourceTree = ""; }; + 6FBCCDFE18D5361300C5B5F5 /* TSMessageOutgoingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageOutgoingTest.m; sourceTree = ""; }; + 6FCE040718D427CE00C33B34 /* TSReplaceSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSReplaceSegue.h; path = Utility/TSReplaceSegue.h; sourceTree = ""; }; + 6FCE040818D427CE00C33B34 /* TSReplaceSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSReplaceSegue.m; path = Utility/TSReplaceSegue.m; sourceTree = ""; }; + 6FCE040918D427CE00C33B34 /* TSStartOverSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSStartOverSegue.h; path = Utility/TSStartOverSegue.h; sourceTree = ""; }; + 6FCE040A18D427CE00C33B34 /* TSStartOverSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSStartOverSegue.m; path = Utility/TSStartOverSegue.m; sourceTree = ""; }; + 8C42C13918551B8C000DD353 /* TextSecureiOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TextSecureiOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8C42C13A18551B8C000DD353 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 8C42C14018551B8C000DD353 /* TextSecureiOS Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TextSecureiOS Tests-Info.plist"; sourceTree = ""; }; + 8C42C14218551B8C000DD353 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 8C42C14618551B8C000DD353 /* TextSecureiOS Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TextSecureiOS Tests-Prefix.pch"; sourceTree = ""; }; + 8C49FC0E18497E86006132F6 /* TSStorageError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSStorageError.h; path = Model/TSStorageError.h; sourceTree = ""; }; + 8C49FC0F18497E86006132F6 /* TSStorageError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSStorageError.m; path = Model/TSStorageError.m; sourceTree = ""; }; + 8CF7BD8E187097570023A781 /* TSECKeyPairTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSECKeyPairTests.m; sourceTree = ""; }; + 8CF7BD931870C8450023A781 /* TSStorageMasterKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSStorageMasterKey.h; path = Security/TSStorageMasterKey.h; sourceTree = ""; }; + 8CF7BD941870C8450023A781 /* TSStorageMasterKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSStorageMasterKey.m; path = Security/TSStorageMasterKey.m; sourceTree = ""; }; + 8CF7BD961870D9450023A781 /* TSDatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSDatabaseManager.h; path = Model/TSDatabaseManager.h; sourceTree = ""; }; + 8CF7BD971870D9450023A781 /* TSDatabaseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSDatabaseManager.m; path = Model/TSDatabaseManager.m; sourceTree = ""; }; + 8CF7BD991870F23D0023A781 /* TSUserKeysDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSUserKeysDatabase.h; path = Model/TSUserKeysDatabase.h; sourceTree = ""; }; + 8CF7BD9A1870F23D0023A781 /* TSUserKeysDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSUserKeysDatabase.m; path = Model/TSUserKeysDatabase.m; sourceTree = ""; }; + 8CF7BD9C18711B140023A781 /* TSUserKeysDatabaseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSUserKeysDatabaseTests.m; path = "TextSecureiOS Tests/TSUserKeysDatabaseTests.m"; sourceTree = SOURCE_ROOT; }; + 8CF7BD9E187130F80023A781 /* TSStorageMasterKeyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSStorageMasterKeyTests.m; path = "TextSecureiOS Tests/TSStorageMasterKeyTests.m"; sourceTree = SOURCE_ROOT; }; + 8CF7BDA0187137640023A781 /* TSEncryptedDatabaseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSEncryptedDatabaseTests.m; sourceTree = ""; }; + 950A849531204A2EBF7475C2 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = SOURCE_ROOT; }; + A513465B18D65D97005B4FA3 /* TSDeregisterAccountRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSDeregisterAccountRequest.h; path = TextSecureiOS/Networking/Requests/TSDeregisterAccountRequest.h; sourceTree = ""; }; + A513465C18D65D97005B4FA3 /* TSDeregisterAccountRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSDeregisterAccountRequest.m; path = TextSecureiOS/Networking/Requests/TSDeregisterAccountRequest.m; sourceTree = ""; }; + A5145B2F184AAD51007BDC73 /* TSMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessage.h; path = Model/TSMessage.h; sourceTree = ""; }; + A5145B30184AAD51007BDC73 /* TSMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessage.m; path = Model/TSMessage.m; sourceTree = ""; }; + A5145B35184B66AD007BDC73 /* TSRequestAttachmentId.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRequestAttachmentId.h; path = TextSecureiOS/Networking/Requests/TSRequestAttachmentId.h; sourceTree = ""; }; + A5145B36184B66AD007BDC73 /* TSRequestAttachmentId.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRequestAttachmentId.m; path = TextSecureiOS/Networking/Requests/TSRequestAttachmentId.m; sourceTree = ""; }; + A5145B38184B68EA007BDC73 /* TSRequestAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRequestAttachment.h; path = TextSecureiOS/Networking/Requests/TSRequestAttachment.h; sourceTree = ""; }; + A5145B39184B68EA007BDC73 /* TSRequestAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRequestAttachment.m; path = TextSecureiOS/Networking/Requests/TSRequestAttachment.m; sourceTree = ""; }; + A5145B3B184B6F35007BDC73 /* TSAttachmentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSAttachmentManager.h; path = TextSecureiOS/Networking/TSAttachmentManager.h; sourceTree = ""; }; + A5145B3C184B6F35007BDC73 /* TSAttachmentManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSAttachmentManager.m; path = TextSecureiOS/Networking/TSAttachmentManager.m; sourceTree = ""; }; + A515AFD9180966E30084ABA5 /* TSMessagesDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessagesDatabase.h; path = Model/TSMessagesDatabase.h; sourceTree = ""; }; + A515AFDA180966E30084ABA5 /* TSMessagesDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessagesDatabase.m; path = Model/TSMessagesDatabase.m; sourceTree = ""; }; + A5169CFC170FEA8D00A2CE9E /* VerificationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VerificationViewController.m; path = ViewControllers/VerificationViewController.m; sourceTree = ""; }; + A5169CFD170FEA8D00A2CE9E /* TextSecureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TextSecureViewController.m; path = ViewControllers/TextSecureViewController.m; sourceTree = ""; }; + A5169CFF170FEA8D00A2CE9E /* VerificationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VerificationViewController.h; path = ViewControllers/VerificationViewController.h; sourceTree = ""; }; + A5169D00170FEA8D00A2CE9E /* TextSecureViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TextSecureViewController.h; path = ViewControllers/TextSecureViewController.h; sourceTree = ""; }; + A5169D04170FEA8D00A2CE9E /* CountryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CountryViewController.m; path = ViewControllers/CountryViewController.m; sourceTree = ""; }; + A5169D05170FEA8D00A2CE9E /* CountryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CountryViewController.h; path = ViewControllers/CountryViewController.h; sourceTree = ""; }; + A5169D06170FEA8D00A2CE9E /* CountrySegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CountrySegue.m; path = ViewControllers/CountrySegue.m; sourceTree = ""; }; + A5169D07170FEA8D00A2CE9E /* CountrySegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CountrySegue.h; path = ViewControllers/CountrySegue.h; sourceTree = ""; }; + A5169D14170FEB1900A2CE9E /* FilePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FilePath.h; path = Utility/FilePath.h; sourceTree = ""; }; + A5169D15170FEB1900A2CE9E /* FilePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FilePath.m; path = Utility/FilePath.m; sourceTree = ""; }; + A5169D16170FEB1900A2CE9E /* NSString+Conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+Conversion.h"; path = "TextSecureiOS/Utility/NSString+Conversion.h"; sourceTree = ""; }; + A5169D17170FEB1900A2CE9E /* NSString+Conversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+Conversion.m"; path = "TextSecureiOS/Utility/NSString+Conversion.m"; sourceTree = ""; }; + A5169D18170FEB1900A2CE9E /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Constants.h; path = Utility/Constants.h; sourceTree = ""; }; + A5169D19170FEB1900A2CE9E /* Constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Constants.m; path = Utility/Constants.m; sourceTree = ""; }; + A5169D1A170FEB1900A2CE9E /* NSData+Conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+Conversion.h"; path = "TextSecureiOS/Utility/NSData+Conversion.h"; sourceTree = ""; }; + A5169D1B170FEB1900A2CE9E /* NSData+Conversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+Conversion.m"; path = "TextSecureiOS/Utility/NSData+Conversion.m"; sourceTree = ""; }; + A5169D23170FEB8E00A2CE9E /* KeychainWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = KeychainWrapper.m; path = Security/KeychainWrapper.m; sourceTree = ""; }; + A5169D26170FEB8F00A2CE9E /* Cryptography.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Cryptography.m; path = Security/Cryptography.m; sourceTree = ""; }; + A5169D27170FEB8F00A2CE9E /* Cryptography.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Cryptography.h; path = Security/Cryptography.h; sourceTree = ""; }; + A5169D2A170FEB8F00A2CE9E /* KeychainWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeychainWrapper.h; path = Security/KeychainWrapper.h; sourceTree = ""; }; + A5169D39170FEDC600A2CE9E /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A5169D3A170FEDC600A2CE9E /* TextSecureiOS-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TextSecureiOS-Info.plist"; sourceTree = ""; }; + A5169D3C170FEDC600A2CE9E /* TextSecureiOS-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TextSecureiOS-Prefix.pch"; sourceTree = ""; }; + A5195B8D18CBBC0300AFEE04 /* contact_mask.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = contact_mask.png; path = TextSecureiOSAssets/contact_mask.png; sourceTree = ""; }; + A5195B8E18CBBC0300AFEE04 /* contact_mask@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "contact_mask@2x.png"; path = "TextSecureiOSAssets/contact_mask@2x.png"; sourceTree = ""; }; + A5195B8F18CBBC0300AFEE04 /* Default_person.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default_person.png; path = TextSecureiOSAssets/Default_person.png; sourceTree = ""; }; + A5195B9018CBBC0300AFEE04 /* Default_person@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default_person@2x.png"; path = "TextSecureiOSAssets/Default_person@2x.png"; sourceTree = ""; }; + A5195B9518CBC00100AFEE04 /* apple_logo_animated.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = apple_logo_animated.gif; path = TextSecureiOSAssets/apple_logo_animated.gif; sourceTree = ""; }; A51B77A217051096002B8E53 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; - A51B77A417051417002B8E53 /* Message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Message.h; sourceTree = ""; }; - A51B77A517051417002B8E53 /* Message.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Message.m; sourceTree = ""; }; - A51B77AA17052C5B002B8E53 /* FilePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FilePath.h; sourceTree = ""; }; - A51B77AB17052C5C002B8E53 /* FilePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FilePath.m; sourceTree = ""; }; - A51B77AE170561C3002B8E53 /* textsecure-verifybutton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "textsecure-verifybutton.png"; path = "TextSecureiOSAssets/vc-verification/textsecure-verifybutton.png"; sourceTree = ""; }; - A51B77AF170561C3002B8E53 /* textsecure-selectyourcountry-field.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "textsecure-selectyourcountry-field.png"; path = "TextSecureiOSAssets/vc-verification/textsecure-selectyourcountry-field.png"; sourceTree = ""; }; - A51B77B0170561C3002B8E53 /* textsecure-phonenumber-field.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "textsecure-phonenumber-field.png"; path = "TextSecureiOSAssets/vc-verification/textsecure-phonenumber-field.png"; sourceTree = ""; }; - A51B77B1170561C3002B8E53 /* textsecure-gettingstarted-menubar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "textsecure-gettingstarted-menubar.png"; path = "TextSecureiOSAssets/vc-verification/textsecure-gettingstarted-menubar.png"; sourceTree = ""; }; - A51B77B2170561C3002B8E53 /* textsecure-countrycode-field.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "textsecure-countrycode-field.png"; path = "TextSecureiOSAssets/vc-verification/textsecure-countrycode-field.png"; sourceTree = ""; }; - A51B77B817058E6C002B8E53 /* TextSecure-Global-canvas.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "TextSecure-Global-canvas.png"; path = "TextSecureiOSAssets/TextSecure-Global-canvas.png"; sourceTree = ""; }; + A52CE05C1862D88B00AF9E39 /* CryptographyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CryptographyTests.mm; sourceTree = ""; }; + A52ECA7A17ED097300C80BC7 /* NSData+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+Base64.h"; path = "TextSecureiOS/Utility/NSData+Base64.h"; sourceTree = ""; }; + A52ECA7B17ED097300C80BC7 /* NSData+Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+Base64.m"; path = "TextSecureiOS/Utility/NSData+Base64.m"; sourceTree = ""; }; + A530CB02184A02620052B615 /* TSRecipientPrekeyRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRecipientPrekeyRequest.h; path = TextSecureiOS/Networking/Requests/TSRecipientPrekeyRequest.h; sourceTree = ""; }; + A530CB03184A02620052B615 /* TSRecipientPrekeyRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRecipientPrekeyRequest.m; path = TextSecureiOS/Networking/Requests/TSRecipientPrekeyRequest.m; sourceTree = ""; }; A53718B216FF95DF00C6FCCF /* TextSecureiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TextSecureiOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; A53718B516FF95DF00C6FCCF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; A53718B716FF95DF00C6FCCF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; A53718B916FF95DF00C6FCCF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - A53718C116FF95DF00C6FCCF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - A53718C316FF95DF00C6FCCF /* TextSecureiOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TextSecureiOS-Prefix.pch"; sourceTree = ""; }; A53718C416FF95DF00C6FCCF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; A53718C516FF95DF00C6FCCF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - A53718CE16FF95DF00C6FCCF /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; - A53718D016FF95DF00C6FCCF /* SMSViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SMSViewController.h; sourceTree = ""; }; - A53718D116FF95DF00C6FCCF /* SMSViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SMSViewController.m; sourceTree = ""; }; - A53718D816FF95DF00C6FCCF /* TextSecureiOSTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TextSecureiOSTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + A53718CE16FF95DF00C6FCCF /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/TextSecureStoryboard.storyboard; sourceTree = ""; }; A53718D916FF95DF00C6FCCF /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; - A53718E116FF95DF00C6FCCF /* TextSecureiOSTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TextSecureiOSTests-Info.plist"; sourceTree = ""; }; - A53718E316FF95DF00C6FCCF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - A53718E516FF95DF00C6FCCF /* TextSecureiOSTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextSecureiOSTests.h; sourceTree = ""; }; - A53718E616FF95DF00C6FCCF /* TextSecureiOSTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TextSecureiOSTests.m; sourceTree = ""; }; A53718F016FF98C100C6FCCF /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; - A53718FB16FFA12000C6FCCF /* TextSecureiOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TextSecureiOS-Info.plist"; sourceTree = ""; }; - A53718FD16FFB6F800C6FCCF /* tutorial_ok_button.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = tutorial_ok_button.png; path = TextSecureiOSAssets/tutorial_ok_button.png; sourceTree = ""; }; - A53718FE16FFB6F800C6FCCF /* tutorial_ok_button@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "tutorial_ok_button@2x.png"; path = "TextSecureiOSAssets/tutorial_ok_button@2x.png"; sourceTree = ""; }; - A537190116FFB92C00C6FCCF /* tutorial_tutorial_arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = tutorial_tutorial_arrow.png; path = TextSecureiOSAssets/tutorial_tutorial_arrow.png; sourceTree = ""; }; - A537190316FFBEE200C6FCCF /* VerificationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VerificationViewController.h; sourceTree = ""; }; - A537190416FFBEE300C6FCCF /* VerificationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VerificationViewController.m; sourceTree = ""; }; - A537190616FFC45B00C6FCCF /* textsecure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = textsecure.png; path = TextSecureiOSAssets/MainIcon/textsecure.png; sourceTree = ""; }; A537190716FFC45B00C6FCCF /* textsecure@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "textsecure@2x.png"; path = "TextSecureiOSAssets/MainIcon/textsecure@2x.png"; sourceTree = ""; }; - A537190A1700247400C6FCCF /* Server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Server.h; sourceTree = ""; }; - A537190B1700247400C6FCCF /* Server.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Server.m; sourceTree = ""; }; - A537190D1700284800C6FCCF /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; - A537190E1700284800C6FCCF /* Constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = ""; }; A53719241700D71A00C6FCCF /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; - A53719261700DC2F00C6FCCF /* CountryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryViewController.h; sourceTree = ""; }; - A53719271700DC3100C6FCCF /* CountryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryViewController.m; sourceTree = ""; }; - A537192D1700FAA900C6FCCF /* CountryCodes.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CountryCodes.plist; sourceTree = ""; }; - A537192F1701040200C6FCCF /* CountrySegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountrySegue.h; sourceTree = ""; }; - A53719301701040400C6FCCF /* CountrySegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountrySegue.m; sourceTree = ""; }; A53727FE17011A4F00C6FCCF /* ad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ad.png; sourceTree = ""; }; A53727FF17011A4F00C6FCCF /* ae.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ae.png; sourceTree = ""; }; A537280017011A4F00C6FCCF /* af.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = af.png; sourceTree = ""; }; @@ -762,7 +816,6 @@ A53728F017011A5100C6FCCF /* qu.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = qu.png; sourceTree = ""; }; A53728F117011A5100C6FCCF /* qu@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "qu@2x.png"; sourceTree = ""; }; A53728F217011A5100C6FCCF /* re.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = re.png; sourceTree = ""; }; - A53728F317011A5100C6FCCF /* resizeAll.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = resizeAll.sh; sourceTree = ""; }; A53728F417011A5100C6FCCF /* ro.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ro.png; sourceTree = ""; }; A53728F517011A5100C6FCCF /* ro@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ro@2x.png"; sourceTree = ""; }; A53728F617011A5100C6FCCF /* rs.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rs.png; sourceTree = ""; }; @@ -850,130 +903,168 @@ A537294817011A5200C6FCCF /* zh.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = zh.png; sourceTree = ""; }; A537294917011A5200C6FCCF /* zm.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = zm.png; sourceTree = ""; }; A537294A17011A5200C6FCCF /* zw.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = zw.png; sourceTree = ""; }; - A5F15A9617027FCF00856385 /* Cryptography.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cryptography.h; sourceTree = ""; }; - A5F15A9717027FCF00856385 /* Cryptography.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Cryptography.m; sourceTree = ""; }; + A538A27C1869DC6500CD0A3D /* TSKeyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKeyManager.h; path = Security/TSKeyManager.h; sourceTree = ""; }; + A538A27D1869DC6500CD0A3D /* TSKeyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKeyManager.m; path = Security/TSKeyManager.m; sourceTree = ""; }; + A539ED0F18D5E06F006C1A61 /* TSSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSSettingsViewController.h; path = ViewControllers/TSSettingsViewController.h; sourceTree = ""; }; + A539ED1018D5E06F006C1A61 /* TSSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSSettingsViewController.m; path = ViewControllers/TSSettingsViewController.m; sourceTree = ""; }; + A5620DED186BD4FA0033483E /* TSSubmitMessageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSSubmitMessageRequest.h; path = TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.h; sourceTree = ""; }; + A5620DEE186BD4FA0033483E /* TSSubmitMessageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSSubmitMessageRequest.m; path = TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.m; sourceTree = ""; }; + A56312F718C85B6500222F99 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + A56312F918C9A45A00222F99 /* TSWaitingPushMessageDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSWaitingPushMessageDatabase.h; path = Model/TSWaitingPushMessageDatabase.h; sourceTree = ""; }; + A56312FA18C9A45A00222F99 /* TSWaitingPushMessageDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSWaitingPushMessageDatabase.m; path = Model/TSWaitingPushMessageDatabase.m; sourceTree = ""; }; + A56312FC18C9BDCD00222F99 /* TSWaitingPushMessageDatabaseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSWaitingPushMessageDatabaseTests.m; sourceTree = ""; }; + A56807B618CCAF290059083C /* TSGroupContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSGroupContext.h; path = Model/TSGroupContext.h; sourceTree = ""; }; + A56807B718CCAF290059083C /* TSGroupContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSGroupContext.m; path = Model/TSGroupContext.m; sourceTree = ""; }; + A568910017E2F37000C360A1 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; + A568910217E2F37A00C360A1 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + A56D31B7180FCA840093D51B /* TSRegisterPrekeysRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRegisterPrekeysRequest.h; path = TextSecureiOS/Networking/Requests/TSRegisterPrekeysRequest.h; sourceTree = ""; }; + A56D31B8180FCA840093D51B /* TSRegisterPrekeysRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRegisterPrekeysRequest.m; path = TextSecureiOS/Networking/Requests/TSRegisterPrekeysRequest.m; sourceTree = ""; }; + A580D2BB19323E4A00D6C1CC /* UIColor+TextSecure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+TextSecure.h"; path = "TextSecureiOS/Categories/UIColor+TextSecure.h"; sourceTree = ""; }; + A580D2BC19323E4A00D6C1CC /* UIColor+TextSecure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+TextSecure.m"; path = "TextSecureiOS/Categories/UIColor+TextSecure.m"; sourceTree = ""; }; + A5DE19D1170702E900ACAFDD /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; + A5DE19D3170702ED00ACAFDD /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; + A5E6CBFB187629D4002A1735 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + A5E6CC07187AC6D5002A1735 /* TSDownloadAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSDownloadAttachment.h; path = TextSecureiOS/Networking/Requests/TSDownloadAttachment.h; sourceTree = ""; }; + A5E6CC08187AC6D5002A1735 /* TSDownloadAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSDownloadAttachment.m; path = TextSecureiOS/Networking/Requests/TSDownloadAttachment.m; sourceTree = ""; }; + A5E6CC61187D0F4B002A1735 /* IncomingPushMessageSignal.pb.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IncomingPushMessageSignal.pb.hh; sourceTree = ""; }; + A5E6CC62187D0F4B002A1735 /* IncomingPushMessageSignal.pb.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = IncomingPushMessageSignal.pb.mm; sourceTree = ""; }; + A5E6CC63187D0F4B002A1735 /* IncomingPushMessageSignal.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = IncomingPushMessageSignal.proto; sourceTree = ""; }; + A5E6CC64187D0F4B002A1735 /* PreKeyWhisperMessage.pb.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PreKeyWhisperMessage.pb.hh; sourceTree = ""; }; + A5E6CC65187D0F4B002A1735 /* PreKeyWhisperMessage.pb.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PreKeyWhisperMessage.pb.mm; sourceTree = ""; }; + A5E6CC66187D0F4B002A1735 /* PreKeyWhisperMessage.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PreKeyWhisperMessage.proto; sourceTree = ""; }; + A5E6CC67187D0F4B002A1735 /* PushMessageContent.pb.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PushMessageContent.pb.hh; sourceTree = ""; }; + A5E6CC68187D0F4B002A1735 /* PushMessageContent.pb.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PushMessageContent.pb.mm; sourceTree = ""; }; + A5E6CC69187D0F4B002A1735 /* PushMessageContent.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PushMessageContent.proto; sourceTree = ""; }; + A5E6CC6A187D0F4B002A1735 /* WhisperMessage.pb.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WhisperMessage.pb.hh; sourceTree = ""; }; + A5E6CC6B187D0F4B002A1735 /* WhisperMessage.pb.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WhisperMessage.pb.mm; sourceTree = ""; }; + A5E6CC6C187D0F4B002A1735 /* WhisperMessage.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WhisperMessage.proto; sourceTree = ""; }; + A5E6CC75187D0FF1002A1735 /* TSEncryptedWhisperMessage.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSEncryptedWhisperMessage.hh; path = ProtocolBufferWrappers/TSEncryptedWhisperMessage.hh; sourceTree = ""; }; + A5E6CC76187D0FF1002A1735 /* TSEncryptedWhisperMessage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSEncryptedWhisperMessage.mm; path = ProtocolBufferWrappers/TSEncryptedWhisperMessage.mm; sourceTree = ""; }; + A5E6CC77187D0FF1002A1735 /* TSMessageSignal.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSMessageSignal.hh; path = ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.hh; sourceTree = ""; }; + A5E6CC78187D0FF1002A1735 /* TSMessageSignal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSMessageSignal.mm; path = ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.mm; sourceTree = ""; }; + A5E6CC79187D0FF1002A1735 /* TSPreKeyWhisperMessage.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSPreKeyWhisperMessage.hh; path = ProtocolBufferWrappers/TSPreKeyWhisperMessage.hh; sourceTree = ""; }; + A5E6CC7A187D0FF1002A1735 /* TSPreKeyWhisperMessage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSPreKeyWhisperMessage.mm; path = ProtocolBufferWrappers/TSPreKeyWhisperMessage.mm; sourceTree = ""; }; + A5E6CC7B187D0FF1002A1735 /* TSProtocolBufferWrapper.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSProtocolBufferWrapper.hh; path = ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.hh; sourceTree = ""; }; + A5E6CC7C187D0FF1002A1735 /* TSProtocolBufferWrapper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSProtocolBufferWrapper.mm; path = ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.mm; sourceTree = ""; }; + A5E6CC7D187D0FF1002A1735 /* TSPushMessageContent.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSPushMessageContent.hh; path = ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.hh; sourceTree = ""; }; + A5E6CC7E187D0FF1002A1735 /* TSPushMessageContent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSPushMessageContent.mm; path = ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.mm; sourceTree = ""; }; + A5E6CC81187D0FF1002A1735 /* TSWhisperMessage.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSWhisperMessage.hh; path = ProtocolBufferWrappers/TSWhisperMessage.hh; sourceTree = ""; }; + A5E6CC8F187D1E41002A1735 /* TSWhisperMessage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSWhisperMessage.mm; path = ProtocolBufferWrappers/TSWhisperMessage.mm; sourceTree = ""; }; + A5E6CC9A18826005002A1735 /* TSProtocolBufferWrapperTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TSProtocolBufferWrapperTests.mm; sourceTree = ""; }; + A5E6CC9C18837C31002A1735 /* TSAxolotlRatchet.hh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TSAxolotlRatchet.hh; path = Security/TSAxolotlRatchet.hh; sourceTree = ""; }; + A5E6CC9D18837C31002A1735 /* TSAxolotlRatchet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSAxolotlRatchet.mm; path = Security/TSAxolotlRatchet.mm; sourceTree = ""; }; A5F15A99170280B700856385 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - A5F15A9B1702949100856385 /* NSData+Conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Conversion.h"; sourceTree = ""; }; - A5F15A9C1702949100856385 /* NSData+Conversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Conversion.m"; sourceTree = ""; }; - A5F15A9E1702997A00856385 /* KeychainWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeychainWrapper.h; sourceTree = ""; }; - A5F15A9F1702997A00856385 /* KeychainWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeychainWrapper.m; sourceTree = ""; }; - A5F15AA41702DF6E00856385 /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssl.a; path = Libraries/lib/libssl.a; sourceTree = ""; }; - A5F15AA51702DF6E00856385 /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcrypto.a; path = Libraries/lib/libcrypto.a; sourceTree = ""; }; - A5F15AAA1702DF9000856385 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = ""; }; - A5F15AAB1702DF9000856385 /* asn1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asn1.h; sourceTree = ""; }; - A5F15AAC1702DF9000856385 /* asn1_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asn1_mac.h; sourceTree = ""; }; - A5F15AAD1702DF9000856385 /* asn1t.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asn1t.h; sourceTree = ""; }; - A5F15AAE1702DF9000856385 /* bio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bio.h; sourceTree = ""; }; - A5F15AAF1702DF9000856385 /* blowfish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blowfish.h; sourceTree = ""; }; - A5F15AB01702DF9000856385 /* bn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bn.h; sourceTree = ""; }; - A5F15AB11702DF9000856385 /* buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = buffer.h; sourceTree = ""; }; - A5F15AB21702DF9000856385 /* camellia.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = camellia.h; sourceTree = ""; }; - A5F15AB31702DF9000856385 /* cast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cast.h; sourceTree = ""; }; - A5F15AB41702DF9000856385 /* cmac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmac.h; sourceTree = ""; }; - A5F15AB51702DF9000856385 /* cms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cms.h; sourceTree = ""; }; - A5F15AB61702DF9000856385 /* comp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = comp.h; sourceTree = ""; }; - A5F15AB71702DF9000856385 /* conf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = conf.h; sourceTree = ""; }; - A5F15AB81702DF9000856385 /* conf_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = conf_api.h; sourceTree = ""; }; - A5F15AB91702DF9000856385 /* crypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto.h; sourceTree = ""; }; - A5F15ABA1702DF9000856385 /* des.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = des.h; sourceTree = ""; }; - A5F15ABB1702DF9000856385 /* des_old.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = des_old.h; sourceTree = ""; }; - A5F15ABC1702DF9000856385 /* dh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dh.h; sourceTree = ""; }; - A5F15ABD1702DF9000856385 /* dsa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsa.h; sourceTree = ""; }; - A5F15ABE1702DF9000856385 /* dso.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dso.h; sourceTree = ""; }; - A5F15ABF1702DF9000856385 /* dtls1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dtls1.h; sourceTree = ""; }; - A5F15AC01702DF9000856385 /* e_os2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = e_os2.h; sourceTree = ""; }; - A5F15AC11702DF9000856385 /* ebcdic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ebcdic.h; sourceTree = ""; }; - A5F15AC21702DF9000856385 /* ec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ec.h; sourceTree = ""; }; - A5F15AC31702DF9000856385 /* ecdh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ecdh.h; sourceTree = ""; }; - A5F15AC41702DF9000856385 /* ecdsa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ecdsa.h; sourceTree = ""; }; - A5F15AC51702DF9000856385 /* engine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = engine.h; sourceTree = ""; }; - A5F15AC61702DF9000856385 /* err.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = err.h; sourceTree = ""; }; - A5F15AC71702DF9000856385 /* evp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = evp.h; sourceTree = ""; }; - A5F15AC81702DF9000856385 /* hmac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hmac.h; sourceTree = ""; }; - A5F15AC91702DF9000856385 /* idea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = idea.h; sourceTree = ""; }; - A5F15ACA1702DF9000856385 /* krb5_asn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = krb5_asn.h; sourceTree = ""; }; - A5F15ACB1702DF9000856385 /* kssl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kssl.h; sourceTree = ""; }; - A5F15ACC1702DF9000856385 /* lhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lhash.h; sourceTree = ""; }; - A5F15ACD1702DF9000856385 /* md4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md4.h; sourceTree = ""; }; - A5F15ACE1702DF9000856385 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = ""; }; - A5F15ACF1702DF9000856385 /* mdc2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mdc2.h; sourceTree = ""; }; - A5F15AD01702DF9000856385 /* modes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modes.h; sourceTree = ""; }; - A5F15AD11702DF9000856385 /* obj_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = obj_mac.h; sourceTree = ""; }; - A5F15AD21702DF9000856385 /* objects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = objects.h; sourceTree = ""; }; - A5F15AD31702DF9000856385 /* ocsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ocsp.h; sourceTree = ""; }; - A5F15AD41702DF9000856385 /* opensslconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opensslconf.h; sourceTree = ""; }; - A5F15AD51702DF9000856385 /* opensslv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opensslv.h; sourceTree = ""; }; - A5F15AD61702DF9000856385 /* ossl_typ.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ossl_typ.h; sourceTree = ""; }; - A5F15AD71702DF9000856385 /* pem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pem.h; sourceTree = ""; }; - A5F15AD81702DF9000856385 /* pem2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pem2.h; sourceTree = ""; }; - A5F15AD91702DF9000856385 /* pkcs12.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pkcs12.h; sourceTree = ""; }; - A5F15ADA1702DF9000856385 /* pkcs7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pkcs7.h; sourceTree = ""; }; - A5F15ADB1702DF9000856385 /* pqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pqueue.h; sourceTree = ""; }; - A5F15ADC1702DF9000856385 /* rand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rand.h; sourceTree = ""; }; - A5F15ADD1702DF9000856385 /* rc2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rc2.h; sourceTree = ""; }; - A5F15ADE1702DF9000856385 /* rc4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rc4.h; sourceTree = ""; }; - A5F15ADF1702DF9000856385 /* ripemd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ripemd.h; sourceTree = ""; }; - A5F15AE01702DF9000856385 /* rsa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rsa.h; sourceTree = ""; }; - A5F15AE11702DF9000856385 /* safestack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safestack.h; sourceTree = ""; }; - A5F15AE21702DF9000856385 /* seed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = seed.h; sourceTree = ""; }; - A5F15AE31702DF9000856385 /* sha.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sha.h; sourceTree = ""; }; - A5F15AE41702DF9000856385 /* srp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = srp.h; sourceTree = ""; }; - A5F15AE51702DF9000856385 /* srtp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = srtp.h; sourceTree = ""; }; - A5F15AE61702DF9000856385 /* ssl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ssl.h; sourceTree = ""; }; - A5F15AE71702DF9000856385 /* ssl2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ssl2.h; sourceTree = ""; }; - A5F15AE81702DF9000856385 /* ssl23.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ssl23.h; sourceTree = ""; }; - A5F15AE91702DF9000856385 /* ssl3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ssl3.h; sourceTree = ""; }; - A5F15AEA1702DF9000856385 /* stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack.h; sourceTree = ""; }; - A5F15AEB1702DF9000856385 /* symhacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = symhacks.h; sourceTree = ""; }; - A5F15AEC1702DF9000856385 /* tls1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tls1.h; sourceTree = ""; }; - A5F15AED1702DF9000856385 /* ts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts.h; sourceTree = ""; }; - A5F15AEE1702DF9000856385 /* txt_db.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = txt_db.h; sourceTree = ""; }; - A5F15AEF1702DF9000856385 /* ui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui.h; sourceTree = ""; }; - A5F15AF01702DF9000856385 /* ui_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ui_compat.h; sourceTree = ""; }; - A5F15AF11702DF9000856385 /* whrlpool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = whrlpool.h; sourceTree = ""; }; - A5F15AF21702DF9000856385 /* x509.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509.h; sourceTree = ""; }; - A5F15AF31702DF9000856385 /* x509_vfy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509_vfy.h; sourceTree = ""; }; - A5F15AF41702DF9000856385 /* x509v3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509v3.h; sourceTree = ""; }; - A5F15B1F1703975F00856385 /* RNCryptor+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RNCryptor+Private.h"; sourceTree = ""; }; - A5F15B201703975F00856385 /* RNCryptor-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RNCryptor-Prefix.pch"; sourceTree = ""; }; - A5F15B211703975F00856385 /* RNCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCryptor.h; sourceTree = ""; }; - A5F15B221703975F00856385 /* RNCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCryptor.m; sourceTree = ""; }; - A5F15B231703975F00856385 /* RNCryptorEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCryptorEngine.h; sourceTree = ""; }; - A5F15B241703975F00856385 /* RNCryptorEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCryptorEngine.m; sourceTree = ""; }; - A5F15B251703975F00856385 /* RNDecryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNDecryptor.h; sourceTree = ""; }; - A5F15B261703975F00856385 /* RNDecryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNDecryptor.m; sourceTree = ""; }; - A5F15B271703975F00856385 /* RNEncryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNEncryptor.h; sourceTree = ""; }; - A5F15B281703975F00856385 /* RNEncryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNEncryptor.m; sourceTree = ""; }; - A5F15B291703975F00856385 /* RNOpenSSLCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNOpenSSLCryptor.h; sourceTree = ""; }; - A5F15B2A1703975F00856385 /* RNOpenSSLCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNOpenSSLCryptor.m; sourceTree = ""; }; - A5F15B2B1703975F00856385 /* RNOpenSSLDecryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNOpenSSLDecryptor.h; sourceTree = ""; }; - A5F15B2C1703975F00856385 /* RNOpenSSLDecryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNOpenSSLDecryptor.m; sourceTree = ""; }; - A5F15B2D1703975F00856385 /* RNOpenSSLEncryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNOpenSSLEncryptor.h; sourceTree = ""; }; - A5F15B2E1703975F00856385 /* RNOpenSSLEncryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNOpenSSLEncryptor.m; sourceTree = ""; }; - A5F15B361703B98F00856385 /* NSString+Conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Conversion.h"; sourceTree = ""; }; - A5F15B371703B98F00856385 /* NSString+Conversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Conversion.m"; sourceTree = ""; }; - A5F15B391703D14F00856385 /* UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserDefaults.h; sourceTree = ""; }; - A5F15B3A1703D14F00856385 /* UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserDefaults.m; sourceTree = ""; }; A5F15B3C1703EB9F00856385 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "TextSecureiOSAssets/Default@2x.png"; sourceTree = ""; }; A5F15B3D1703EB9F00856385 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = TextSecureiOSAssets/Default.png; sourceTree = ""; }; A5F15B481703EE2F00856385 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "TextSecureiOSAssets/Default-568h@2x.png"; sourceTree = ""; }; - A5F15B4B1703F20D00856385 /* OpenSans-SemiboldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-SemiboldItalic.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-SemiboldItalic.ttf"; sourceTree = ""; }; - A5F15B4C1703F20D00856385 /* OpenSans-Semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Semibold.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-Semibold.ttf"; sourceTree = ""; }; - A5F15B4D1703F20D00856385 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Regular.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-Regular.ttf"; sourceTree = ""; }; - A5F15B4E1703F20D00856385 /* OpenSans-LightItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-LightItalic.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-LightItalic.ttf"; sourceTree = ""; }; - A5F15B4F1703F20D00856385 /* OpenSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Light.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-Light.ttf"; sourceTree = ""; }; - A5F15B501703F20D00856385 /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Italic.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-Italic.ttf"; sourceTree = ""; }; - A5F15B511703F20D00856385 /* OpenSans-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-ExtraBoldItalic.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-ExtraBoldItalic.ttf"; sourceTree = ""; }; - A5F15B521703F20D00856385 /* OpenSans-ExtraBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-ExtraBold.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-ExtraBold.ttf"; sourceTree = ""; }; - A5F15B531703F20D00856385 /* OpenSans-BoldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-BoldItalic.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-BoldItalic.ttf"; sourceTree = ""; }; - A5F15B541703F20D00856385 /* OpenSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Bold.ttf"; path = "TextSecureiOSAssets/Fonts/OpenSans-Bold.ttf"; sourceTree = ""; }; + A5FBAB93184E980E0012E9A5 /* TSUploadAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSUploadAttachment.h; path = TextSecureiOS/Networking/Requests/TSUploadAttachment.h; sourceTree = ""; }; + A5FBAB94184E980E0012E9A5 /* TSUploadAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSUploadAttachment.m; path = TextSecureiOS/Networking/Requests/TSUploadAttachment.m; sourceTree = ""; }; + A5FE7D7E191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSPresentIdentityQRCodeViewController.h; path = ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.h; sourceTree = ""; }; + A5FE7D7F191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSPresentIdentityQRCodeViewController.m; path = ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.m; sourceTree = ""; }; + A5FE7D80191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSScanIdentityBarcodeViewController.h; path = ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.h; sourceTree = ""; }; + A5FE7D81191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSScanIdentityBarcodeViewController.m; path = ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.m; sourceTree = ""; }; + A5FE7D82191FA1FA0022C3F7 /* TSVerifyIdentityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSVerifyIdentityViewController.h; path = ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.h; sourceTree = ""; }; + A5FE7D83191FA1FA0022C3F7 /* TSVerifyIdentityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSVerifyIdentityViewController.m; path = ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.m; sourceTree = ""; }; + AD77BB98A5B14A3490A31E06 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + B602B96118CE9A4B00181564 /* TSReceivingChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSReceivingChain.h; path = Model/TSReceivingChain.h; sourceTree = ""; }; + B602B96218CE9A4B00181564 /* TSReceivingChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSReceivingChain.m; path = Model/TSReceivingChain.m; sourceTree = ""; }; + B621AA711928032D00085CF7 /* TSSocketManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSSocketManager.h; path = TextSecureiOS/Networking/TSSocketManager.h; sourceTree = ""; }; + B621AA721928032D00085CF7 /* TSSocketManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSSocketManager.m; path = TextSecureiOS/Networking/TSSocketManager.m; sourceTree = ""; }; + B635B38F182181E1003CE732 /* TSMessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessageViewController.h; path = ViewControllers/Conversations/TSMessageViewController.h; sourceTree = ""; }; + B635B390182181E1003CE732 /* TSMessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageViewController.m; path = ViewControllers/Conversations/TSMessageViewController.m; sourceTree = ""; }; + B635B3EC18218456003CE732 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + B635B3EE18218481003CE732 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + B639AF0B17F5A68100B2244D /* TSNetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSNetworkManager.h; path = TextSecureiOS/Networking/TSNetworkManager.h; sourceTree = ""; }; + B639AF0C17F5A68100B2244D /* TSNetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSNetworkManager.m; path = TextSecureiOS/Networking/TSNetworkManager.m; sourceTree = ""; }; + B639AF0F17F5AAA300B2244D /* TSRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRequest.h; path = TextSecureiOS/Networking/Requests/TSRequest.h; sourceTree = ""; }; + B639AF1017F5AAA300B2244D /* TSRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRequest.m; path = TextSecureiOS/Networking/Requests/TSRequest.m; sourceTree = ""; }; + B639F69D180955E100B7DF00 /* TSServerCodeVerificationRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSServerCodeVerificationRequest.h; path = TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.h; sourceTree = ""; }; + B639F69E180955E100B7DF00 /* TSServerCodeVerificationRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSServerCodeVerificationRequest.m; path = TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.m; sourceTree = ""; }; + B63A72141808B8AF00C353F2 /* VerificationCodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VerificationCodeViewController.h; path = ViewControllers/VerificationCodeViewController.h; sourceTree = ""; }; + B63A72151808B8AF00C353F2 /* VerificationCodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VerificationCodeViewController.m; path = ViewControllers/VerificationCodeViewController.m; sourceTree = ""; }; + B63A72171808BD3D00C353F2 /* gcm.textsecure.whispersystems.org.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = gcm.textsecure.whispersystems.org.cer; path = TextSecureiOS/gcm.textsecure.whispersystems.org.cer; sourceTree = ""; }; + B643D7DC189EB9020025C6F3 /* TSContactPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSContactPickerViewController.h; path = ViewControllers/TSContactPickerViewController.h; sourceTree = ""; }; + B643D7DD189EB9020025C6F3 /* TSContactPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSContactPickerViewController.m; path = ViewControllers/TSContactPickerViewController.m; sourceTree = ""; }; + B644F95C18DF5D3700D41FA8 /* NSString+randomString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+randomString.h"; path = "TextSecureiOS/Utility/NSString+randomString.h"; sourceTree = ""; }; + B644F95D18DF5D3700D41FA8 /* NSString+randomString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+randomString.m"; path = "TextSecureiOS/Utility/NSString+randomString.m"; sourceTree = ""; }; + B646538818E74D9C006010E0 /* TSDerivedSecrets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSDerivedSecrets.h; path = Model/TSDerivedSecrets.h; sourceTree = ""; }; + B646538918E74D9C006010E0 /* TSDerivedSecrets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSDerivedSecrets.m; path = Model/TSDerivedSecrets.m; sourceTree = ""; }; + B67E30F418CE19E4008502F5 /* TSContact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSContact.h; path = Contacts/TSContact.h; sourceTree = ""; }; + B67E30F518CE19E4008502F5 /* TSContact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSContact.m; path = Contacts/TSContact.m; sourceTree = ""; }; + B67E30F718CE1AA8008502F5 /* TSGroupSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSGroupSetupViewController.h; path = "ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.h"; sourceTree = ""; }; + B67E30F818CE1AA8008502F5 /* TSGroupSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSGroupSetupViewController.m; path = "ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.m"; sourceTree = ""; }; + B684346418D1FF2B007AEDF6 /* TSSendingChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSSendingChain.h; path = Security/TSSendingChain.h; sourceTree = ""; }; + B684346518D1FF2B007AEDF6 /* TSSendingChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSSendingChain.m; path = Security/TSSendingChain.m; sourceTree = ""; }; + B68A2290180AC32C00D93C16 /* TSRegisterForPushRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRegisterForPushRequest.h; path = TextSecureiOS/Networking/Requests/TSRegisterForPushRequest.h; sourceTree = ""; }; + B68A2291180AC32C00D93C16 /* TSRegisterForPushRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRegisterForPushRequest.m; path = TextSecureiOS/Networking/Requests/TSRegisterForPushRequest.m; sourceTree = ""; }; + B68BFDF0180987C6005CC817 /* TSSetMasterPasswordViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSSetMasterPasswordViewController.h; path = ViewControllers/TSSetMasterPasswordViewController.h; sourceTree = ""; }; + B68BFDF1180987C6005CC817 /* TSSetMasterPasswordViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSSetMasterPasswordViewController.m; path = ViewControllers/TSSetMasterPasswordViewController.m; sourceTree = ""; }; + B6B478E41809E7F5005DDE12 /* TSContactsIntersectionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSContactsIntersectionRequest.h; path = TextSecureiOS/Networking/Requests/TSContactsIntersectionRequest.h; sourceTree = ""; }; + B6B478E51809E7F5005DDE12 /* TSContactsIntersectionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSContactsIntersectionRequest.m; path = TextSecureiOS/Networking/Requests/TSContactsIntersectionRequest.m; sourceTree = ""; }; + B6C5AE5717C212330022EA53 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; + B6C5FA7218CE344700ECC200 /* TSConversation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSConversation.h; path = Model/TSConversation.h; sourceTree = ""; }; + B6C5FA7318CE344700ECC200 /* TSConversation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSConversation.m; path = Model/TSConversation.m; sourceTree = ""; }; + B6C5FA7518CE354800ECC200 /* TSSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSSession.h; path = Model/TSSession.h; sourceTree = ""; }; + B6C5FA7618CE354800ECC200 /* TSSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSSession.m; path = Model/TSSession.m; sourceTree = ""; }; + B6C5FA7818CE355500ECC200 /* TSChainKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSChainKey.h; path = Security/TSChainKey.h; sourceTree = ""; }; + B6C5FA7918CE355500ECC200 /* TSChainKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSChainKey.m; path = Security/TSChainKey.m; sourceTree = ""; }; + B6C5FA7B18CE357200ECC200 /* TSGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSGroup.h; path = Model/TSGroup.h; sourceTree = ""; }; + B6C5FA7C18CE357200ECC200 /* TSGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSGroup.m; path = Model/TSGroup.m; sourceTree = ""; }; + B6C5FA7E18CE3AD600ECC200 /* TSAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSAttachment.h; path = Model/TSAttachment.h; sourceTree = ""; }; + B6C5FA7F18CE3AD600ECC200 /* TSAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSAttachment.m; path = Model/TSAttachment.m; sourceTree = ""; }; + B6C5FA8118CE3AED00ECC200 /* TSContactManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSContactManager.h; path = TextSecureiOS/Contacts/TSContactManager.h; sourceTree = ""; }; + B6C5FA8218CE3AED00ECC200 /* TSContactManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSContactManager.m; path = TextSecureiOS/Contacts/TSContactManager.m; sourceTree = ""; }; + B6C5FA8418CE3AFB00ECC200 /* TSECKeyPair.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSECKeyPair.h; path = Security/TSECKeyPair.h; sourceTree = ""; }; + B6C5FA8518CE3AFB00ECC200 /* TSECKeyPair.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSECKeyPair.m; path = Security/TSECKeyPair.m; sourceTree = ""; }; + B6C5FA8718CE3B0900ECC200 /* TSMessageIncoming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessageIncoming.h; path = Model/TSMessageIncoming.h; sourceTree = ""; }; + B6C5FA8818CE3B0900ECC200 /* TSMessageIncoming.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageIncoming.m; path = Model/TSMessageIncoming.m; sourceTree = ""; }; + B6C5FA8A18CE3B1000ECC200 /* TSMessageOutgoing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessageOutgoing.h; path = Model/TSMessageOutgoing.h; sourceTree = ""; }; + B6C5FA8B18CE3B1000ECC200 /* TSMessageOutgoing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageOutgoing.m; path = Model/TSMessageOutgoing.m; sourceTree = ""; }; + B6C5FA8D18CE3B2000ECC200 /* TSPrekey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSPrekey.h; path = Model/TSPrekey.h; sourceTree = ""; }; + B6C5FA8E18CE3B2000ECC200 /* TSPrekey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSPrekey.m; path = Model/TSPrekey.m; sourceTree = ""; }; + B6C5FA9018CE3B3900ECC200 /* TSMessageKeys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessageKeys.h; path = Model/TSMessageKeys.h; sourceTree = ""; }; + B6C5FA9118CE3B3900ECC200 /* TSMessageKeys.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageKeys.m; path = Model/TSMessageKeys.m; sourceTree = ""; }; + B6C5FA9318CE3B4600ECC200 /* TSMessagesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSMessagesManager.h; path = MessagesManager/TSMessagesManager.h; sourceTree = ""; }; + B6C5FA9418CE3B4600ECC200 /* TSMessagesManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TSMessagesManager.mm; path = MessagesManager/TSMessagesManager.mm; sourceTree = ""; }; + B6C5FA9618CE3B6300ECC200 /* RKCK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RKCK.h; path = Security/RKCK.h; sourceTree = ""; }; + B6C5FA9718CE3B6300ECC200 /* RKCK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RKCK.m; path = Security/RKCK.m; sourceTree = ""; }; + B6CA8B2318E38F0D0088BC0C /* TSAxolotlConsistencyTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAxolotlConsistencyTest.m; sourceTree = ""; }; + B6E2CFB117F7926000200B69 /* TSRequestVerificationCodeRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSRequestVerificationCodeRequest.h; path = TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.h; sourceTree = ""; }; + B6E2CFB217F7926000200B69 /* TSRequestVerificationCodeRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSRequestVerificationCodeRequest.m; path = TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.m; sourceTree = ""; }; + B6E2CFB417F794F600200B69 /* NSString+escape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+escape.h"; path = "TextSecureiOS/Categories/NSString+escape.h"; sourceTree = ""; }; + B6E2CFB517F794F600200B69 /* NSString+escape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+escape.m"; path = "TextSecureiOS/Categories/NSString+escape.m"; sourceTree = ""; }; + B6F10B1517EF4A3A00723719 /* NSLocale+phonePrefixes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSLocale+phonePrefixes.h"; path = "TextSecureiOS/Categories/NSLocale+phonePrefixes.h"; sourceTree = ""; }; + B6F10B1617EF4A3A00723719 /* NSLocale+phonePrefixes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSLocale+phonePrefixes.m"; path = "TextSecureiOS/Categories/NSLocale+phonePrefixes.m"; sourceTree = ""; }; + B6F10B1B17EF61FE00723719 /* CountryCodes.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CountryCodes.plist; sourceTree = ""; }; + B6F10B1D17EF8F3C00723719 /* NSString+PhoneFormating.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+PhoneFormating.h"; path = "TextSecureiOS/Categories/NSString+PhoneFormating.h"; sourceTree = ""; }; + B6F10B1E17EF8F3C00723719 /* NSString+PhoneFormating.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+PhoneFormating.m"; path = "TextSecureiOS/Categories/NSString+PhoneFormating.m"; sourceTree = ""; }; + B6F7FEBE18D77E1500ECC4A2 /* NSData+TSKeyVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+TSKeyVersion.h"; path = "TextSecureiOS/Utility/NSData+TSKeyVersion.h"; sourceTree = ""; }; + B6F7FEBF18D77E1500ECC4A2 /* NSData+TSKeyVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+TSKeyVersion.m"; path = "TextSecureiOS/Utility/NSData+TSKeyVersion.m"; sourceTree = ""; }; + F8694B5D18C5268500A44474 /* padlock_prompt.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = padlock_prompt.png; path = TextSecureiOSAssets/padlock_prompt.png; sourceTree = ""; }; + FCD30FC718FDBBB7003BD188 /* SignUpStepsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SignUpStepsViewController.h; path = ViewControllers/SignUpStepsViewController.h; sourceTree = ""; }; + FCD30FC818FDBBB7003BD188 /* SignUpStepsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SignUpStepsViewController.m; path = ViewControllers/SignUpStepsViewController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 8C42C13618551B8C000DD353 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CE74D2F57964464494640956 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A53718AF16FF95DF00C6FCCF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A5E6CBFC187629D4002A1735 /* MediaPlayer.framework in Frameworks */, + B635B3EF18218481003CE732 /* AudioToolbox.framework in Frameworks */, + B635B3ED18218456003CE732 /* CoreAudio.framework in Frameworks */, + A568910317E2F37A00C360A1 /* QuartzCore.framework in Frameworks */, + A568910117E2F37000C360A1 /* CoreText.framework in Frameworks */, + A5DE19D4170702ED00ACAFDD /* AddressBook.framework in Frameworks */, + A5DE19D2170702E900ACAFDD /* AddressBookUI.framework in Frameworks */, A5F15A9A170280B700856385 /* Security.framework in Frameworks */, A53719251700D71B00C6FCCF /* CoreTelephony.framework in Frameworks */, A53718F116FF98C100C6FCCF /* MessageUI.framework in Frameworks */, @@ -981,106 +1072,129 @@ A53718B816FF95DF00C6FCCF /* Foundation.framework in Frameworks */, A53718BA16FF95DF00C6FCCF /* CoreGraphics.framework in Frameworks */, A51B77A317051096002B8E53 /* libsqlite3.dylib in Frameworks */, - A5F15AA61702DF6E00856385 /* libssl.a in Frameworks */, - A5F15AA71702DF6E00856385 /* libcrypto.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A53718D416FF95DF00C6FCCF /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A53718DA16FF95DF00C6FCCF /* SenTestingKit.framework in Frameworks */, - A53718DB16FF95DF00C6FCCF /* UIKit.framework in Frameworks */, - A53718DC16FF95DF00C6FCCF /* Foundation.framework in Frameworks */, + A6F91EC0B0694C57850C03BA /* libPods.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - A51B776317050C42002B8E53 /* fmdb */ = { + 05B94BF018BD286100708011 /* ViewControllers */ = { isa = PBXGroup; children = ( - A51B776417050C42002B8E53 /* FMDatabase.h */, - A51B776517050C42002B8E53 /* FMDatabase.m */, - A51B776617050C42002B8E53 /* FMDatabaseAdditions.h */, - A51B776717050C42002B8E53 /* FMDatabaseAdditions.m */, - A51B776817050C42002B8E53 /* FMDatabasePool.h */, - A51B776917050C42002B8E53 /* FMDatabasePool.m */, - A51B776A17050C42002B8E53 /* FMDatabaseQueue.h */, - A51B776B17050C42002B8E53 /* FMDatabaseQueue.m */, - A51B776D17050C42002B8E53 /* FMResultSet.h */, - A51B776E17050C42002B8E53 /* FMResultSet.m */, - ); - name = fmdb; - path = Libraries/fmdb/src; - sourceTree = SOURCE_ROOT; - }; - A51B777517050C71002B8E53 /* JSON */ = { + 05B94BF318BD28EB00708011 /* Sign up */, + ); + name = ViewControllers; + sourceTree = ""; + }; + 05B94BF318BD28EB00708011 /* Sign up */ = { isa = PBXGroup; children = ( - A51B777617050C71002B8E53 /* NSObject+SBJson.h */, - A51B777717050C71002B8E53 /* NSObject+SBJson.m */, - A51B777817050C71002B8E53 /* SBJson.h */, - A51B777917050C71002B8E53 /* SBJsonParser.h */, - A51B777A17050C71002B8E53 /* SBJsonParser.m */, - A51B777B17050C71002B8E53 /* SBJsonStreamParser.h */, - A51B777C17050C71002B8E53 /* SBJsonStreamParser.m */, - A51B777D17050C71002B8E53 /* SBJsonStreamParserAccumulator.h */, - A51B777E17050C71002B8E53 /* SBJsonStreamParserAccumulator.m */, - A51B777F17050C71002B8E53 /* SBJsonStreamParserAdapter.h */, - A51B778017050C71002B8E53 /* SBJsonStreamParserAdapter.m */, - A51B778117050C71002B8E53 /* SBJsonStreamParserState.h */, - A51B778217050C71002B8E53 /* SBJsonStreamParserState.m */, - A51B778317050C71002B8E53 /* SBJsonStreamWriter.h */, - A51B778417050C71002B8E53 /* SBJsonStreamWriter.m */, - A51B778517050C71002B8E53 /* SBJsonStreamWriterAccumulator.h */, - A51B778617050C71002B8E53 /* SBJsonStreamWriterAccumulator.m */, - A51B778717050C71002B8E53 /* SBJsonStreamWriterState.h */, - A51B778817050C71002B8E53 /* SBJsonStreamWriterState.m */, - A51B778917050C71002B8E53 /* SBJsonTokeniser.h */, - A51B778A17050C71002B8E53 /* SBJsonTokeniser.m */, - A51B778B17050C71002B8E53 /* SBJsonUTF8Stream.h */, - A51B778C17050C71002B8E53 /* SBJsonUTF8Stream.m */, - A51B778D17050C71002B8E53 /* SBJsonWriter.h */, - A51B778E17050C71002B8E53 /* SBJsonWriter.m */, - ); - name = JSON; - path = Libraries/JSON; - sourceTree = SOURCE_ROOT; + 05B94BF118BD28E400708011 /* VerificationViewControllerTests.m */, + ); + name = "Sign up"; + sourceTree = ""; }; - A51B779B17050EDB002B8E53 /* Model */ = { + 4C0ACB47186C86DE00B328D5 /* Views */ = { isa = PBXGroup; children = ( - A51B779F17050F8C002B8E53 /* MessagesDatabase.h */, - A51B77A017050F8C002B8E53 /* MessagesDatabase.m */, - A51B77A417051417002B8E53 /* Message.h */, - A51B77A517051417002B8E53 /* Message.m */, + 4C0ACB48186C86E600B328D5 /* Cells */, ); - name = Model; + name = Views; + sourceTree = ""; + }; + 4C0ACB48186C86E600B328D5 /* Cells */ = { + isa = PBXGroup; + children = ( + 4C0ACB49186C876200B328D5 /* TSMessageConversationCell.h */, + 4C0ACB4A186C876200B328D5 /* TSMessageConversationCell.m */, + ); + name = Cells; + sourceTree = ""; + }; + 6FBCCDFC18D5361300C5B5F5 /* Model */ = { + isa = PBXGroup; + children = ( + 6FBCCDFD18D5361300C5B5F5 /* TSMessageIncomingTest.m */, + 6FBCCDFE18D5361300C5B5F5 /* TSMessageOutgoingTest.m */, + ); + path = Model; sourceTree = ""; }; - A51B77AD170561AE002B8E53 /* vc-verification */ = { + 8C42C13E18551B8C000DD353 /* TextSecureiOS Tests */ = { isa = PBXGroup; children = ( - A51B77B817058E6C002B8E53 /* TextSecure-Global-canvas.png */, - A51B77AE170561C3002B8E53 /* textsecure-verifybutton.png */, - A51B77AF170561C3002B8E53 /* textsecure-selectyourcountry-field.png */, - A51B77B0170561C3002B8E53 /* textsecure-phonenumber-field.png */, - A51B77B1170561C3002B8E53 /* textsecure-gettingstarted-menubar.png */, - A51B77B2170561C3002B8E53 /* textsecure-countrycode-field.png */, - ); - name = "vc-verification"; + A5E6CC9A18826005002A1735 /* TSProtocolBufferWrapperTests.mm */, + 6FBCCDFC18D5361300C5B5F5 /* Model */, + A5C85AED18839E68001D2189 /* Security */, + 8C4328CB187885FD005F25E2 /* Storage */, + 8C42C13F18551B8C000DD353 /* Supporting Files */, + 05B94BF018BD286100708011 /* ViewControllers */, + ); + path = "TextSecureiOS Tests"; + sourceTree = ""; + }; + 8C42C13F18551B8C000DD353 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 8C42C14018551B8C000DD353 /* TextSecureiOS Tests-Info.plist */, + 8C42C14118551B8C000DD353 /* InfoPlist.strings */, + 8C42C14618551B8C000DD353 /* TextSecureiOS Tests-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 8C4328CB187885FD005F25E2 /* Storage */ = { + isa = PBXGroup; + children = ( + 4CFDC8D1187215F600BDB649 /* TSMessagesDatabaseTests.m */, + 8CF7BD9C18711B140023A781 /* TSUserKeysDatabaseTests.m */, + 8CF7BDA0187137640023A781 /* TSEncryptedDatabaseTests.m */, + 8CF7BD9E187130F80023A781 /* TSStorageMasterKeyTests.m */, + A56312FC18C9BDCD00222F99 /* TSWaitingPushMessageDatabaseTests.m */, + ); + name = Storage; + sourceTree = ""; + }; + 8CCC26381872A3CB00E521A1 /* Storage handlers */ = { + isa = PBXGroup; + children = ( + 8C49FC0E18497E86006132F6 /* TSStorageError.h */, + 8C49FC0F18497E86006132F6 /* TSStorageError.m */, + 8CF7BD991870F23D0023A781 /* TSUserKeysDatabase.h */, + 8CF7BD9A1870F23D0023A781 /* TSUserKeysDatabase.m */, + A56312F918C9A45A00222F99 /* TSWaitingPushMessageDatabase.h */, + A56312FA18C9A45A00222F99 /* TSWaitingPushMessageDatabase.m */, + 8CF7BD961870D9450023A781 /* TSDatabaseManager.h */, + 8CF7BD971870D9450023A781 /* TSDatabaseManager.m */, + A515AFD9180966E30084ABA5 /* TSMessagesDatabase.h */, + A515AFDA180966E30084ABA5 /* TSMessagesDatabase.m */, + 8CF7BD931870C8450023A781 /* TSStorageMasterKey.h */, + 8CF7BD941870C8450023A781 /* TSStorageMasterKey.m */, + ); + name = "Storage handlers"; + sourceTree = ""; + }; + A51B779B17050EDB002B8E53 /* Model */ = { + isa = PBXGroup; + children = ( + B602B95E18CE783D00181564 /* Contacts / Groups */, + B602B95D18CE77B700181564 /* Crypto */, + B602B95918CE764100181564 /* Messages */, + 8CCC26381872A3CB00E521A1 /* Storage handlers */, + ); + name = Model; sourceTree = ""; }; A53718A916FF95DF00C6FCCF = { isa = PBXGroup; children = ( + A56312F718C85B6500222F99 /* Settings.bundle */, A53718BB16FF95DF00C6FCCF /* TextSecureiOS */, - A53718DF16FF95DF00C6FCCF /* TextSecureiOSTests */, + 8C42C13E18551B8C000DD353 /* TextSecureiOS Tests */, A53718B416FF95DF00C6FCCF /* Frameworks */, A53718B316FF95DF00C6FCCF /* Products */, + 950A849531204A2EBF7475C2 /* Pods.xcconfig */, ); sourceTree = ""; }; @@ -1088,7 +1202,7 @@ isa = PBXGroup; children = ( A53718B216FF95DF00C6FCCF /* TextSecureiOS.app */, - A53718D816FF95DF00C6FCCF /* TextSecureiOSTests.octest */, + 8C42C13918551B8C000DD353 /* TextSecureiOS Tests.xctest */, ); name = Products; sourceTree = ""; @@ -1096,9 +1210,14 @@ A53718B416FF95DF00C6FCCF /* Frameworks */ = { isa = PBXGroup; children = ( + A5E6CBFB187629D4002A1735 /* MediaPlayer.framework */, + B635B3EE18218481003CE732 /* AudioToolbox.framework */, + B635B3EC18218456003CE732 /* CoreAudio.framework */, + A568910217E2F37A00C360A1 /* QuartzCore.framework */, + A568910017E2F37000C360A1 /* CoreText.framework */, A51B77A217051096002B8E53 /* libsqlite3.dylib */, - A5F15AA41702DF6E00856385 /* libssl.a */, - A5F15AA51702DF6E00856385 /* libcrypto.a */, + A5DE19D3170702ED00ACAFDD /* AddressBook.framework */, + A5DE19D1170702E900ACAFDD /* AddressBookUI.framework */, A5F15A99170280B700856385 /* Security.framework */, A53719241700D71A00C6FCCF /* CoreTelephony.framework */, A53718F016FF98C100C6FCCF /* MessageUI.framework */, @@ -1106,6 +1225,8 @@ A53718B716FF95DF00C6FCCF /* Foundation.framework */, A53718B916FF95DF00C6FCCF /* CoreGraphics.framework */, A53718D916FF95DF00C6FCCF /* SenTestingKit.framework */, + AD77BB98A5B14A3490A31E06 /* libPods.a */, + 8C42C13A18551B8C000DD353 /* XCTest.framework */, ); name = Frameworks; sourceTree = ""; @@ -1115,11 +1236,18 @@ children = ( A53718C416FF95DF00C6FCCF /* AppDelegate.h */, A53718C516FF95DF00C6FCCF /* AppDelegate.m */, + A53718CD16FF95DF00C6FCCF /* TextSecureStoryboard.storyboard */, + A53718FC16FFB6E600C6FCCF /* Assets */, + B6F10B1417EF49D700723719 /* Categories */, + B602B96018CE797D00181564 /* Contact Management */, + B602B95B18CE777900181564 /* Messaging */, A51B779B17050EDB002B8E53 /* Model */, - A5F15B1D1703740D00856385 /* View */, - A5F15AA217029BCF00856385 /* ViewControllers */, - A5F15B1C170373E700856385 /* Server */, + B639AF0A17F5A53700B2244D /* Networking */, + A5B0B82018198C8E00AF41A7 /* ProtocolBuffers */, A5F15AA117029BB400856385 /* Security */, + B61E05AB18C179F300AC49CA /* Storage */, + 4C0ACB47186C86DE00B328D5 /* Views */, + A5F15AA217029BCF00856385 /* ViewControllers */, A5F15AA317029C5300856385 /* Utility */, A53718BC16FF95DF00C6FCCF /* Supporting Files */, ); @@ -1129,33 +1257,11 @@ A53718BC16FF95DF00C6FCCF /* Supporting Files */ = { isa = PBXGroup; children = ( - A537192D1700FAA900C6FCCF /* CountryCodes.plist */, + B6F10B1B17EF61FE00723719 /* CountryCodes.plist */, + A5169D39170FEDC600A2CE9E /* main.m */, + A5169D3A170FEDC600A2CE9E /* TextSecureiOS-Info.plist */, + A5169D3C170FEDC600A2CE9E /* TextSecureiOS-Prefix.pch */, A53718FC16FFB6E600C6FCCF /* Assets */, - A53718FB16FFA12000C6FCCF /* TextSecureiOS-Info.plist */, - A53718C116FF95DF00C6FCCF /* main.m */, - A53718C316FF95DF00C6FCCF /* TextSecureiOS-Prefix.pch */, - A5F15B481703EE2F00856385 /* Default-568h@2x.png */, - A5F15B3C1703EB9F00856385 /* Default@2x.png */, - A5F15B3D1703EB9F00856385 /* Default.png */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - A53718DF16FF95DF00C6FCCF /* TextSecureiOSTests */ = { - isa = PBXGroup; - children = ( - A53718E516FF95DF00C6FCCF /* TextSecureiOSTests.h */, - A53718E616FF95DF00C6FCCF /* TextSecureiOSTests.m */, - A53718E016FF95DF00C6FCCF /* Supporting Files */, - ); - path = TextSecureiOSTests; - sourceTree = ""; - }; - A53718E016FF95DF00C6FCCF /* Supporting Files */ = { - isa = PBXGroup; - children = ( - A53718E116FF95DF00C6FCCF /* TextSecureiOSTests-Info.plist */, - A53718E216FF95DF00C6FCCF /* InfoPlist.strings */, ); name = "Supporting Files"; sourceTree = ""; @@ -1163,14 +1269,18 @@ A53718FC16FFB6E600C6FCCF /* Assets */ = { isa = PBXGroup; children = ( - A51B77AD170561AE002B8E53 /* vc-verification */, - A5F15B4A1703F1B000856385 /* Fonts */, + A5195B9518CBC00100AFEE04 /* apple_logo_animated.gif */, + A5195B8D18CBBC0300AFEE04 /* contact_mask.png */, + A5195B8E18CBBC0300AFEE04 /* contact_mask@2x.png */, + B6C5AE5717C212330022EA53 /* Default-568h@2x.png */, + A5195B8F18CBBC0300AFEE04 /* Default_person.png */, + A5195B9018CBBC0300AFEE04 /* Default_person@2x.png */, A53727FD17011A4E00C6FCCF /* flags */, - A537190616FFC45B00C6FCCF /* textsecure.png */, A537190716FFC45B00C6FCCF /* textsecure@2x.png */, - A537190116FFB92C00C6FCCF /* tutorial_tutorial_arrow.png */, - A53718FD16FFB6F800C6FCCF /* tutorial_ok_button.png */, - A53718FE16FFB6F800C6FCCF /* tutorial_ok_button@2x.png */, + A5F15B481703EE2F00856385 /* Default-568h@2x.png */, + A5F15B3C1703EB9F00856385 /* Default@2x.png */, + A5F15B3D1703EB9F00856385 /* Default.png */, + F8694B5D18C5268500A44474 /* padlock_prompt.png */, ); name = Assets; sourceTree = ""; @@ -1423,7 +1533,6 @@ A53728F017011A5100C6FCCF /* qu.png */, A53728F117011A5100C6FCCF /* qu@2x.png */, A53728F217011A5100C6FCCF /* re.png */, - A53728F317011A5100C6FCCF /* resizeAll.sh */, A53728F417011A5100C6FCCF /* ro.png */, A53728F517011A5100C6FCCF /* ro@2x.png */, A53728F617011A5100C6FCCF /* rs.png */, @@ -1516,14 +1625,92 @@ path = TextSecureiOSAssets/flags; sourceTree = ""; }; + A5B0B82018198C8E00AF41A7 /* ProtocolBuffers */ = { + isa = PBXGroup; + children = ( + A5E6CC51187CC1C5002A1735 /* Protocol Buffer Wrappers */, + A5E6CC1A187B82AA002A1735 /* Compiled */, + ); + path = ProtocolBuffers; + sourceTree = ""; + }; + A5C85AED18839E68001D2189 /* Security */ = { + isa = PBXGroup; + children = ( + A52CE05C1862D88B00AF9E39 /* CryptographyTests.mm */, + 8CF7BD8E187097570023A781 /* TSECKeyPairTests.m */, + B6CA8B2318E38F0D0088BC0C /* TSAxolotlConsistencyTest.m */, + ); + name = Security; + sourceTree = ""; + }; + A5E6CC1A187B82AA002A1735 /* Compiled */ = { + isa = PBXGroup; + children = ( + A5E6CC61187D0F4B002A1735 /* IncomingPushMessageSignal.pb.hh */, + A5E6CC62187D0F4B002A1735 /* IncomingPushMessageSignal.pb.mm */, + A5E6CC63187D0F4B002A1735 /* IncomingPushMessageSignal.proto */, + A5E6CC91187D3890002A1735 /* MessageTypes */, + A5E6CC67187D0F4B002A1735 /* PushMessageContent.pb.hh */, + A5E6CC68187D0F4B002A1735 /* PushMessageContent.pb.mm */, + A5E6CC69187D0F4B002A1735 /* PushMessageContent.proto */, + ); + path = Compiled; + sourceTree = ""; + }; + A5E6CC51187CC1C5002A1735 /* Protocol Buffer Wrappers */ = { + isa = PBXGroup; + children = ( + A5E6CC7B187D0FF1002A1735 /* TSProtocolBufferWrapper.hh */, + A5E6CC7C187D0FF1002A1735 /* TSProtocolBufferWrapper.mm */, + A5E6CC77187D0FF1002A1735 /* TSMessageSignal.hh */, + A5E6CC78187D0FF1002A1735 /* TSMessageSignal.mm */, + A5E6CC8D187D1E02002A1735 /* MessageTypes */, + A5E6CC7D187D0FF1002A1735 /* TSPushMessageContent.hh */, + A5E6CC7E187D0FF1002A1735 /* TSPushMessageContent.mm */, + ); + name = "Protocol Buffer Wrappers"; + path = ..; + sourceTree = ""; + }; + A5E6CC8D187D1E02002A1735 /* MessageTypes */ = { + isa = PBXGroup; + children = ( + A5E6CC81187D0FF1002A1735 /* TSWhisperMessage.hh */, + A5E6CC8F187D1E41002A1735 /* TSWhisperMessage.mm */, + A5E6CC75187D0FF1002A1735 /* TSEncryptedWhisperMessage.hh */, + A5E6CC76187D0FF1002A1735 /* TSEncryptedWhisperMessage.mm */, + A5E6CC79187D0FF1002A1735 /* TSPreKeyWhisperMessage.hh */, + A5E6CC7A187D0FF1002A1735 /* TSPreKeyWhisperMessage.mm */, + ); + name = MessageTypes; + path = ProtocolBuffers; + sourceTree = ""; + }; + A5E6CC91187D3890002A1735 /* MessageTypes */ = { + isa = PBXGroup; + children = ( + A5E6CC6A187D0F4B002A1735 /* WhisperMessage.pb.hh */, + A5E6CC6B187D0F4B002A1735 /* WhisperMessage.pb.mm */, + A5E6CC6C187D0F4B002A1735 /* WhisperMessage.proto */, + A5E6CC64187D0F4B002A1735 /* PreKeyWhisperMessage.pb.hh */, + A5E6CC65187D0F4B002A1735 /* PreKeyWhisperMessage.pb.mm */, + A5E6CC66187D0F4B002A1735 /* PreKeyWhisperMessage.proto */, + ); + name = MessageTypes; + sourceTree = ""; + }; A5F15AA117029BB400856385 /* Security */ = { isa = PBXGroup; children = ( - A5F15A9617027FCF00856385 /* Cryptography.h */, - A5F15A9717027FCF00856385 /* Cryptography.m */, - A5F15A9E1702997A00856385 /* KeychainWrapper.h */, - A5F15A9F1702997A00856385 /* KeychainWrapper.m */, - A5F15B1B170370A400856385 /* Libraries */, + A538A27C1869DC6500CD0A3D /* TSKeyManager.h */, + A538A27D1869DC6500CD0A3D /* TSKeyManager.m */, + A5169D2A170FEB8F00A2CE9E /* KeychainWrapper.h */, + A5169D23170FEB8E00A2CE9E /* KeychainWrapper.m */, + A5169D27170FEB8F00A2CE9E /* Cryptography.h */, + A5169D26170FEB8F00A2CE9E /* Cryptography.m */, + A5E6CC9C18837C31002A1735 /* TSAxolotlRatchet.hh */, + A5E6CC9D18837C31002A1735 /* TSAxolotlRatchet.mm */, ); name = Security; sourceTree = ""; @@ -1531,14 +1718,23 @@ A5F15AA217029BCF00856385 /* ViewControllers */ = { isa = PBXGroup; children = ( - A53718D016FF95DF00C6FCCF /* SMSViewController.h */, - A53718D116FF95DF00C6FCCF /* SMSViewController.m */, - A537190316FFBEE200C6FCCF /* VerificationViewController.h */, - A537190416FFBEE300C6FCCF /* VerificationViewController.m */, - A53719261700DC2F00C6FCCF /* CountryViewController.h */, - A53719271700DC3100C6FCCF /* CountryViewController.m */, - A537192F1701040200C6FCCF /* CountrySegue.h */, - A53719301701040400C6FCCF /* CountrySegue.m */, + A5FE7D7D191FA1BB0022C3F7 /* VerifyIdentity */, + A5169D00170FEA8D00A2CE9E /* TextSecureViewController.h */, + A5169CFD170FEA8D00A2CE9E /* TextSecureViewController.m */, + B63A72131808B87700C353F2 /* Sign up */, + B602B95F18CE786F00181564 /* Messaging */, + B643D7DC189EB9020025C6F3 /* TSContactPickerViewController.h */, + B643D7DD189EB9020025C6F3 /* TSContactPickerViewController.m */, + B67E30F718CE1AA8008502F5 /* TSGroupSetupViewController.h */, + B67E30F818CE1AA8008502F5 /* TSGroupSetupViewController.m */, + 4C77526A18707A46000FCCA6 /* PasswordUnlockViewController.h */, + 4C77526B18707A46000FCCA6 /* PasswordUnlockViewController.m */, + A5169D05170FEA8D00A2CE9E /* CountryViewController.h */, + A5169D04170FEA8D00A2CE9E /* CountryViewController.m */, + A5169D07170FEA8D00A2CE9E /* CountrySegue.h */, + A5169D06170FEA8D00A2CE9E /* CountrySegue.m */, + A539ED0F18D5E06F006C1A61 /* TSSettingsViewController.h */, + A539ED1018D5E06F006C1A61 /* TSSettingsViewController.m */, ); name = ViewControllers; sourceTree = ""; @@ -1546,191 +1742,270 @@ A5F15AA317029C5300856385 /* Utility */ = { isa = PBXGroup; children = ( - A537190D1700284800C6FCCF /* Constants.h */, - A537190E1700284800C6FCCF /* Constants.m */, - A51B77AA17052C5B002B8E53 /* FilePath.h */, - A51B77AB17052C5C002B8E53 /* FilePath.m */, - A5F15B391703D14F00856385 /* UserDefaults.h */, - A5F15B3A1703D14F00856385 /* UserDefaults.m */, - A5F15B361703B98F00856385 /* NSString+Conversion.h */, - A5F15B371703B98F00856385 /* NSString+Conversion.m */, - A5F15A9B1702949100856385 /* NSData+Conversion.h */, - A5F15A9C1702949100856385 /* NSData+Conversion.m */, - A51B777517050C71002B8E53 /* JSON */, - A51B776317050C42002B8E53 /* fmdb */, + 6FCE040718D427CE00C33B34 /* TSReplaceSegue.h */, + 6FCE040818D427CE00C33B34 /* TSReplaceSegue.m */, + 6FCE040918D427CE00C33B34 /* TSStartOverSegue.h */, + 6FCE040A18D427CE00C33B34 /* TSStartOverSegue.m */, + A5169D14170FEB1900A2CE9E /* FilePath.h */, + A5169D15170FEB1900A2CE9E /* FilePath.m */, + A5169D18170FEB1900A2CE9E /* Constants.h */, + A5169D19170FEB1900A2CE9E /* Constants.m */, ); name = Utility; sourceTree = ""; }; - A5F15AA81702DF9000856385 /* include */ = { + A5FE7D7D191FA1BB0022C3F7 /* VerifyIdentity */ = { isa = PBXGroup; children = ( - A5F15AA91702DF9000856385 /* openssl */, + A5FE7D7E191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.h */, + A5FE7D7F191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.m */, + A5FE7D80191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.h */, + A5FE7D81191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.m */, + A5FE7D82191FA1FA0022C3F7 /* TSVerifyIdentityViewController.h */, + A5FE7D83191FA1FA0022C3F7 /* TSVerifyIdentityViewController.m */, ); - name = include; - path = Libraries/include; - sourceTree = SOURCE_ROOT; + name = VerifyIdentity; + sourceTree = ""; }; - A5F15AA91702DF9000856385 /* openssl */ = { + B602B95918CE764100181564 /* Messages */ = { isa = PBXGroup; children = ( - A5F15AAA1702DF9000856385 /* aes.h */, - A5F15AAB1702DF9000856385 /* asn1.h */, - A5F15AAC1702DF9000856385 /* asn1_mac.h */, - A5F15AAD1702DF9000856385 /* asn1t.h */, - A5F15AAE1702DF9000856385 /* bio.h */, - A5F15AAF1702DF9000856385 /* blowfish.h */, - A5F15AB01702DF9000856385 /* bn.h */, - A5F15AB11702DF9000856385 /* buffer.h */, - A5F15AB21702DF9000856385 /* camellia.h */, - A5F15AB31702DF9000856385 /* cast.h */, - A5F15AB41702DF9000856385 /* cmac.h */, - A5F15AB51702DF9000856385 /* cms.h */, - A5F15AB61702DF9000856385 /* comp.h */, - A5F15AB71702DF9000856385 /* conf.h */, - A5F15AB81702DF9000856385 /* conf_api.h */, - A5F15AB91702DF9000856385 /* crypto.h */, - A5F15ABA1702DF9000856385 /* des.h */, - A5F15ABB1702DF9000856385 /* des_old.h */, - A5F15ABC1702DF9000856385 /* dh.h */, - A5F15ABD1702DF9000856385 /* dsa.h */, - A5F15ABE1702DF9000856385 /* dso.h */, - A5F15ABF1702DF9000856385 /* dtls1.h */, - A5F15AC01702DF9000856385 /* e_os2.h */, - A5F15AC11702DF9000856385 /* ebcdic.h */, - A5F15AC21702DF9000856385 /* ec.h */, - A5F15AC31702DF9000856385 /* ecdh.h */, - A5F15AC41702DF9000856385 /* ecdsa.h */, - A5F15AC51702DF9000856385 /* engine.h */, - A5F15AC61702DF9000856385 /* err.h */, - A5F15AC71702DF9000856385 /* evp.h */, - A5F15AC81702DF9000856385 /* hmac.h */, - A5F15AC91702DF9000856385 /* idea.h */, - A5F15ACA1702DF9000856385 /* krb5_asn.h */, - A5F15ACB1702DF9000856385 /* kssl.h */, - A5F15ACC1702DF9000856385 /* lhash.h */, - A5F15ACD1702DF9000856385 /* md4.h */, - A5F15ACE1702DF9000856385 /* md5.h */, - A5F15ACF1702DF9000856385 /* mdc2.h */, - A5F15AD01702DF9000856385 /* modes.h */, - A5F15AD11702DF9000856385 /* obj_mac.h */, - A5F15AD21702DF9000856385 /* objects.h */, - A5F15AD31702DF9000856385 /* ocsp.h */, - A5F15AD41702DF9000856385 /* opensslconf.h */, - A5F15AD51702DF9000856385 /* opensslv.h */, - A5F15AD61702DF9000856385 /* ossl_typ.h */, - A5F15AD71702DF9000856385 /* pem.h */, - A5F15AD81702DF9000856385 /* pem2.h */, - A5F15AD91702DF9000856385 /* pkcs12.h */, - A5F15ADA1702DF9000856385 /* pkcs7.h */, - A5F15ADB1702DF9000856385 /* pqueue.h */, - A5F15ADC1702DF9000856385 /* rand.h */, - A5F15ADD1702DF9000856385 /* rc2.h */, - A5F15ADE1702DF9000856385 /* rc4.h */, - A5F15ADF1702DF9000856385 /* ripemd.h */, - A5F15AE01702DF9000856385 /* rsa.h */, - A5F15AE11702DF9000856385 /* safestack.h */, - A5F15AE21702DF9000856385 /* seed.h */, - A5F15AE31702DF9000856385 /* sha.h */, - A5F15AE41702DF9000856385 /* srp.h */, - A5F15AE51702DF9000856385 /* srtp.h */, - A5F15AE61702DF9000856385 /* ssl.h */, - A5F15AE71702DF9000856385 /* ssl2.h */, - A5F15AE81702DF9000856385 /* ssl23.h */, - A5F15AE91702DF9000856385 /* ssl3.h */, - A5F15AEA1702DF9000856385 /* stack.h */, - A5F15AEB1702DF9000856385 /* symhacks.h */, - A5F15AEC1702DF9000856385 /* tls1.h */, - A5F15AED1702DF9000856385 /* ts.h */, - A5F15AEE1702DF9000856385 /* txt_db.h */, - A5F15AEF1702DF9000856385 /* ui.h */, - A5F15AF01702DF9000856385 /* ui_compat.h */, - A5F15AF11702DF9000856385 /* whrlpool.h */, - A5F15AF21702DF9000856385 /* x509.h */, - A5F15AF31702DF9000856385 /* x509_vfy.h */, - A5F15AF41702DF9000856385 /* x509v3.h */, - ); - path = openssl; + B6C5FA7E18CE3AD600ECC200 /* TSAttachment.h */, + B6C5FA7F18CE3AD600ECC200 /* TSAttachment.m */, + B6C5FA7218CE344700ECC200 /* TSConversation.h */, + B6C5FA7318CE344700ECC200 /* TSConversation.m */, + A5145B2F184AAD51007BDC73 /* TSMessage.h */, + A5145B30184AAD51007BDC73 /* TSMessage.m */, + B6C5FA8718CE3B0900ECC200 /* TSMessageIncoming.h */, + B6C5FA8818CE3B0900ECC200 /* TSMessageIncoming.m */, + B6C5FA8A18CE3B1000ECC200 /* TSMessageOutgoing.h */, + B6C5FA8B18CE3B1000ECC200 /* TSMessageOutgoing.m */, + ); + name = Messages; sourceTree = ""; }; - A5F15B1B170370A400856385 /* Libraries */ = { + B602B95B18CE777900181564 /* Messaging */ = { isa = PBXGroup; children = ( - A5F15B1E1703975F00856385 /* RNCryptor */, - A5F15AA81702DF9000856385 /* include */, + B6C5FA9318CE3B4600ECC200 /* TSMessagesManager.h */, + B6C5FA9418CE3B4600ECC200 /* TSMessagesManager.mm */, ); - name = Libraries; - path = ../Libraries/include; + name = Messaging; sourceTree = ""; }; - A5F15B1C170373E700856385 /* Server */ = { + B602B95C18CE779800181564 /* Axolotl Structures */ = { isa = PBXGroup; children = ( - A537190A1700247400C6FCCF /* Server.h */, - A537190B1700247400C6FCCF /* Server.m */, + B646538818E74D9C006010E0 /* TSDerivedSecrets.h */, + B646538918E74D9C006010E0 /* TSDerivedSecrets.m */, + B6C5FA9618CE3B6300ECC200 /* RKCK.h */, + B6C5FA9718CE3B6300ECC200 /* RKCK.m */, + B684346418D1FF2B007AEDF6 /* TSSendingChain.h */, + B684346518D1FF2B007AEDF6 /* TSSendingChain.m */, + B602B96118CE9A4B00181564 /* TSReceivingChain.h */, + B602B96218CE9A4B00181564 /* TSReceivingChain.m */, + B6C5FA7818CE355500ECC200 /* TSChainKey.h */, + B6C5FA7918CE355500ECC200 /* TSChainKey.m */, + B6C5FA9018CE3B3900ECC200 /* TSMessageKeys.h */, + B6C5FA9118CE3B3900ECC200 /* TSMessageKeys.m */, ); - name = Server; + name = "Axolotl Structures"; sourceTree = ""; }; - A5F15B1D1703740D00856385 /* View */ = { + B602B95D18CE77B700181564 /* Crypto */ = { isa = PBXGroup; children = ( - A53718CD16FF95DF00C6FCCF /* MainStoryboard.storyboard */, + B6C5FA8418CE3AFB00ECC200 /* TSECKeyPair.h */, + B6C5FA8518CE3AFB00ECC200 /* TSECKeyPair.m */, + B6C5FA7518CE354800ECC200 /* TSSession.h */, + B6C5FA7618CE354800ECC200 /* TSSession.m */, + B6C5FA8D18CE3B2000ECC200 /* TSPrekey.h */, + B6C5FA8E18CE3B2000ECC200 /* TSPrekey.m */, + B602B95C18CE779800181564 /* Axolotl Structures */, ); - name = View; + name = Crypto; sourceTree = ""; }; - A5F15B1E1703975F00856385 /* RNCryptor */ = { + B602B95E18CE783D00181564 /* Contacts / Groups */ = { isa = PBXGroup; children = ( - A5F15B1F1703975F00856385 /* RNCryptor+Private.h */, - A5F15B201703975F00856385 /* RNCryptor-Prefix.pch */, - A5F15B211703975F00856385 /* RNCryptor.h */, - A5F15B221703975F00856385 /* RNCryptor.m */, - A5F15B231703975F00856385 /* RNCryptorEngine.h */, - A5F15B241703975F00856385 /* RNCryptorEngine.m */, - A5F15B251703975F00856385 /* RNDecryptor.h */, - A5F15B261703975F00856385 /* RNDecryptor.m */, - A5F15B271703975F00856385 /* RNEncryptor.h */, - A5F15B281703975F00856385 /* RNEncryptor.m */, - A5F15B291703975F00856385 /* RNOpenSSLCryptor.h */, - A5F15B2A1703975F00856385 /* RNOpenSSLCryptor.m */, - A5F15B2B1703975F00856385 /* RNOpenSSLDecryptor.h */, - A5F15B2C1703975F00856385 /* RNOpenSSLDecryptor.m */, - A5F15B2D1703975F00856385 /* RNOpenSSLEncryptor.h */, - A5F15B2E1703975F00856385 /* RNOpenSSLEncryptor.m */, - ); - name = RNCryptor; - path = Libraries/RNCryptor/RNCryptor; - sourceTree = SOURCE_ROOT; - }; - A5F15B4A1703F1B000856385 /* Fonts */ = { + B67E30F418CE19E4008502F5 /* TSContact.h */, + B67E30F518CE19E4008502F5 /* TSContact.m */, + A56807B618CCAF290059083C /* TSGroupContext.h */, + A56807B718CCAF290059083C /* TSGroupContext.m */, + B6C5FA7B18CE357200ECC200 /* TSGroup.h */, + B6C5FA7C18CE357200ECC200 /* TSGroup.m */, + ); + name = "Contacts / Groups"; + sourceTree = ""; + }; + B602B95F18CE786F00181564 /* Messaging */ = { isa = PBXGroup; children = ( - A5F15B4B1703F20D00856385 /* OpenSans-SemiboldItalic.ttf */, - A5F15B4C1703F20D00856385 /* OpenSans-Semibold.ttf */, - A5F15B4D1703F20D00856385 /* OpenSans-Regular.ttf */, - A5F15B4E1703F20D00856385 /* OpenSans-LightItalic.ttf */, - A5F15B4F1703F20D00856385 /* OpenSans-Light.ttf */, - A5F15B501703F20D00856385 /* OpenSans-Italic.ttf */, - A5F15B511703F20D00856385 /* OpenSans-ExtraBoldItalic.ttf */, - A5F15B521703F20D00856385 /* OpenSans-ExtraBold.ttf */, - A5F15B531703F20D00856385 /* OpenSans-BoldItalic.ttf */, - A5F15B541703F20D00856385 /* OpenSans-Bold.ttf */, - ); - name = Fonts; + B635B38F182181E1003CE732 /* TSMessageViewController.h */, + B635B390182181E1003CE732 /* TSMessageViewController.m */, + ); + name = Messaging; + sourceTree = ""; + }; + B602B96018CE797D00181564 /* Contact Management */ = { + isa = PBXGroup; + children = ( + B6C5FA8118CE3AED00ECC200 /* TSContactManager.h */, + B6C5FA8218CE3AED00ECC200 /* TSContactManager.m */, + ); + name = "Contact Management"; + path = ..; + sourceTree = ""; + }; + B61E05AB18C179F300AC49CA /* Storage */ = { + isa = PBXGroup; + children = ( + 8C49FC0E18497E86006132F6 /* TSStorageError.h */, + 8C49FC0F18497E86006132F6 /* TSStorageError.m */, + 8CF7BD961870D9450023A781 /* TSDatabaseManager.h */, + 8CF7BD971870D9450023A781 /* TSDatabaseManager.m */, + 8CF7BD991870F23D0023A781 /* TSUserKeysDatabase.h */, + 8CF7BD9A1870F23D0023A781 /* TSUserKeysDatabase.m */, + A515AFD9180966E30084ABA5 /* TSMessagesDatabase.h */, + A515AFDA180966E30084ABA5 /* TSMessagesDatabase.m */, + 8CF7BD931870C8450023A781 /* TSStorageMasterKey.h */, + 8CF7BD941870C8450023A781 /* TSStorageMasterKey.m */, + ); + name = Storage; + sourceTree = ""; + }; + B639AF0A17F5A53700B2244D /* Networking */ = { + isa = PBXGroup; + children = ( + B6E2CFB717F7A89000200B69 /* Certificate */, + B639AF0E17F5AA6000B2244D /* Request Types */, + B639AF0B17F5A68100B2244D /* TSNetworkManager.h */, + B639AF0C17F5A68100B2244D /* TSNetworkManager.m */, + A5145B3B184B6F35007BDC73 /* TSAttachmentManager.h */, + A5145B3C184B6F35007BDC73 /* TSAttachmentManager.m */, + B621AA711928032D00085CF7 /* TSSocketManager.h */, + B621AA721928032D00085CF7 /* TSSocketManager.m */, + ); + name = Networking; + path = ..; + sourceTree = ""; + }; + B639AF0E17F5AA6000B2244D /* Request Types */ = { + isa = PBXGroup; + children = ( + A5620DED186BD4FA0033483E /* TSSubmitMessageRequest.h */, + A5620DEE186BD4FA0033483E /* TSSubmitMessageRequest.m */, + B639AF0F17F5AAA300B2244D /* TSRequest.h */, + B639AF1017F5AAA300B2244D /* TSRequest.m */, + B6E2CFB117F7926000200B69 /* TSRequestVerificationCodeRequest.h */, + B6E2CFB217F7926000200B69 /* TSRequestVerificationCodeRequest.m */, + B639F69D180955E100B7DF00 /* TSServerCodeVerificationRequest.h */, + B639F69E180955E100B7DF00 /* TSServerCodeVerificationRequest.m */, + B6B478E41809E7F5005DDE12 /* TSContactsIntersectionRequest.h */, + B6B478E51809E7F5005DDE12 /* TSContactsIntersectionRequest.m */, + B68A2290180AC32C00D93C16 /* TSRegisterForPushRequest.h */, + B68A2291180AC32C00D93C16 /* TSRegisterForPushRequest.m */, + A56D31B7180FCA840093D51B /* TSRegisterPrekeysRequest.h */, + A56D31B8180FCA840093D51B /* TSRegisterPrekeysRequest.m */, + A530CB02184A02620052B615 /* TSRecipientPrekeyRequest.h */, + A530CB03184A02620052B615 /* TSRecipientPrekeyRequest.m */, + A5145B35184B66AD007BDC73 /* TSRequestAttachmentId.h */, + A5145B36184B66AD007BDC73 /* TSRequestAttachmentId.m */, + A5145B38184B68EA007BDC73 /* TSRequestAttachment.h */, + A5145B39184B68EA007BDC73 /* TSRequestAttachment.m */, + A5FBAB93184E980E0012E9A5 /* TSUploadAttachment.h */, + A5FBAB94184E980E0012E9A5 /* TSUploadAttachment.m */, + A5E6CC07187AC6D5002A1735 /* TSDownloadAttachment.h */, + A5E6CC08187AC6D5002A1735 /* TSDownloadAttachment.m */, + A513465B18D65D97005B4FA3 /* TSDeregisterAccountRequest.h */, + A513465C18D65D97005B4FA3 /* TSDeregisterAccountRequest.m */, + ); + name = "Request Types"; + sourceTree = ""; + }; + B63A72131808B87700C353F2 /* Sign up */ = { + isa = PBXGroup; + children = ( + FCD30FC718FDBBB7003BD188 /* SignUpStepsViewController.h */, + FCD30FC818FDBBB7003BD188 /* SignUpStepsViewController.m */, + A5169CFF170FEA8D00A2CE9E /* VerificationViewController.h */, + A5169CFC170FEA8D00A2CE9E /* VerificationViewController.m */, + B63A72141808B8AF00C353F2 /* VerificationCodeViewController.h */, + B63A72151808B8AF00C353F2 /* VerificationCodeViewController.m */, + B68BFDF0180987C6005CC817 /* TSSetMasterPasswordViewController.h */, + B68BFDF1180987C6005CC817 /* TSSetMasterPasswordViewController.m */, + ); + name = "Sign up"; + sourceTree = ""; + }; + B6E2CFB717F7A89000200B69 /* Certificate */ = { + isa = PBXGroup; + children = ( + B63A72171808BD3D00C353F2 /* gcm.textsecure.whispersystems.org.cer */, + ); + name = Certificate; + sourceTree = ""; + }; + B6F10B1417EF49D700723719 /* Categories */ = { + isa = PBXGroup; + children = ( + A580D2BB19323E4A00D6C1CC /* UIColor+TextSecure.h */, + A580D2BC19323E4A00D6C1CC /* UIColor+TextSecure.m */, + B6F7FEBE18D77E1500ECC4A2 /* NSData+TSKeyVersion.h */, + B6F7FEBF18D77E1500ECC4A2 /* NSData+TSKeyVersion.m */, + B644F95C18DF5D3700D41FA8 /* NSString+randomString.h */, + B644F95D18DF5D3700D41FA8 /* NSString+randomString.m */, + A5169D16170FEB1900A2CE9E /* NSString+Conversion.h */, + A5169D17170FEB1900A2CE9E /* NSString+Conversion.m */, + A5169D1A170FEB1900A2CE9E /* NSData+Conversion.h */, + A5169D1B170FEB1900A2CE9E /* NSData+Conversion.m */, + B6F10B1517EF4A3A00723719 /* NSLocale+phonePrefixes.h */, + B6F10B1617EF4A3A00723719 /* NSLocale+phonePrefixes.m */, + B6F10B1D17EF8F3C00723719 /* NSString+PhoneFormating.h */, + B6F10B1E17EF8F3C00723719 /* NSString+PhoneFormating.m */, + B6E2CFB417F794F600200B69 /* NSString+escape.h */, + B6E2CFB517F794F600200B69 /* NSString+escape.m */, + A52ECA7A17ED097300C80BC7 /* NSData+Base64.h */, + A52ECA7B17ED097300C80BC7 /* NSData+Base64.m */, + ); + name = Categories; + path = ..; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 8C42C13818551B8C000DD353 /* TextSecureiOS Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8C42C14B18551B8C000DD353 /* Build configuration list for PBXNativeTarget "TextSecureiOS Tests" */; + buildPhases = ( + 7B5FB740FD6F406BBB0D8DDB /* Check Pods Manifest.lock */, + 8C42C13518551B8C000DD353 /* Sources */, + 8C42C13618551B8C000DD353 /* Frameworks */, + 8C42C13718551B8C000DD353 /* Resources */, + 33E229A93BF543029696288F /* Copy Pods Resources */, + 8C7D62D618659A71003E2B90 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 8C42C14818551B8C000DD353 /* PBXTargetDependency */, + ); + name = "TextSecureiOS Tests"; + productName = "TextSecureiOS Tests"; + productReference = 8C42C13918551B8C000DD353 /* TextSecureiOS Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; A53718B116FF95DF00C6FCCF /* TextSecureiOS */ = { isa = PBXNativeTarget; buildConfigurationList = A53718EA16FF95DF00C6FCCF /* Build configuration list for PBXNativeTarget "TextSecureiOS" */; buildPhases = ( + DE866B39AA57421C900F2883 /* Check Pods Manifest.lock */, A53718AE16FF95DF00C6FCCF /* Sources */, A53718AF16FF95DF00C6FCCF /* Frameworks */, A53718B016FF95DF00C6FCCF /* Resources */, + 3CB5212BC54445C68D08CFCC /* Copy Pods Resources */, ); buildRules = ( ); @@ -1741,33 +2016,27 @@ productReference = A53718B216FF95DF00C6FCCF /* TextSecureiOS.app */; productType = "com.apple.product-type.application"; }; - A53718D716FF95DF00C6FCCF /* TextSecureiOSTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = A53718ED16FF95DF00C6FCCF /* Build configuration list for PBXNativeTarget "TextSecureiOSTests" */; - buildPhases = ( - A53718D316FF95DF00C6FCCF /* Sources */, - A53718D416FF95DF00C6FCCF /* Frameworks */, - A53718D516FF95DF00C6FCCF /* Resources */, - A53718D616FF95DF00C6FCCF /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - A53718DE16FF95DF00C6FCCF /* PBXTargetDependency */, - ); - name = TextSecureiOSTests; - productName = TextSecureiOSTests; - productReference = A53718D816FF95DF00C6FCCF /* TextSecureiOSTests.octest */; - productType = "com.apple.product-type.bundle"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ A53718AA16FF95DF00C6FCCF /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0460; + LastUpgradeCheck = 0500; ORGANIZATIONNAME = "Open Whisper Systems"; + TargetAttributes = { + 8C42C13818551B8C000DD353 = { + TestTargetID = A53718B116FF95DF00C6FCCF; + }; + A53718B116FF95DF00C6FCCF = { + DevelopmentTeam = U68MSDN6DR; + SystemCapabilities = { + com.apple.BackgroundModes = { + enabled = 1; + }; + }; + }; + }; }; buildConfigurationList = A53718AD16FF95DF00C6FCCF /* Build configuration list for PBXProject "TextSecureiOS" */; compatibilityVersion = "Xcode 3.2"; @@ -1782,23 +2051,26 @@ projectRoot = ""; targets = ( A53718B116FF95DF00C6FCCF /* TextSecureiOS */, - A53718D716FF95DF00C6FCCF /* TextSecureiOSTests */, + 8C42C13818551B8C000DD353 /* TextSecureiOS Tests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 8C42C13718551B8C000DD353 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 05B94BF418BD2EA000708011 /* TextSecureStoryboard.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A53718B016FF95DF00C6FCCF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - A53718CF16FF95DF00C6FCCF /* MainStoryboard.storyboard in Resources */, - A53718FF16FFB6F800C6FCCF /* tutorial_ok_button.png in Resources */, - A537190016FFB6F800C6FCCF /* tutorial_ok_button@2x.png in Resources */, - A537190216FFB92C00C6FCCF /* tutorial_tutorial_arrow.png in Resources */, - A537190816FFC45C00C6FCCF /* textsecure.png in Resources */, + A53718CF16FF95DF00C6FCCF /* TextSecureStoryboard.storyboard in Resources */, A537190916FFC45C00C6FCCF /* textsecure@2x.png in Resources */, - A537192E1700FAAB00C6FCCF /* CountryCodes.plist in Resources */, A537294B17011A5200C6FCCF /* ad.png in Resources */, A537294C17011A5200C6FCCF /* ae.png in Resources */, A537294D17011A5200C6FCCF /* af.png in Resources */, @@ -1839,6 +2111,7 @@ A537297017011A5200C6FCCF /* bpy@2x.png in Resources */, A537297117011A5200C6FCCF /* br.png in Resources */, A537297217011A5200C6FCCF /* br@2x.png in Resources */, + A5E6CC6E187D0F4B002A1735 /* IncomingPushMessageSignal.proto in Resources */, A537297317011A5200C6FCCF /* bs.png in Resources */, A537297417011A5200C6FCCF /* bs@2x.png in Resources */, A537297517011A5200C6FCCF /* bt.png in Resources */, @@ -1898,6 +2171,7 @@ A53729AB17011A5200C6FCCF /* fo.png in Resources */, A53729AC17011A5200C6FCCF /* fr.png in Resources */, A53729AD17011A5200C6FCCF /* fr@2x.png in Resources */, + A5E6CC72187D0F4B002A1735 /* PushMessageContent.proto in Resources */, A53729AE17011A5200C6FCCF /* fy.png in Resources */, A53729AF17011A5200C6FCCF /* fy@2x.png in Resources */, A53729B017011A5200C6FCCF /* ga.png in Resources */, @@ -1916,6 +2190,7 @@ A53729BD17011A5200C6FCCF /* gs.png in Resources */, A53729BE17011A5200C6FCCF /* gt.png in Resources */, A53729BF17011A5200C6FCCF /* gu.png in Resources */, + A5E6CC74187D0F4B002A1735 /* WhisperMessage.proto in Resources */, A53729C017011A5200C6FCCF /* gu@2x.png in Resources */, A53729C117011A5200C6FCCF /* gw.png in Resources */, A53729C217011A5200C6FCCF /* gy.png in Resources */, @@ -1964,8 +2239,12 @@ A53729ED17011A5200C6FCCF /* la.png in Resources */, A53729EE17011A5200C6FCCF /* la@2x.png in Resources */, A53729EF17011A5200C6FCCF /* lb.png in Resources */, + A5195B9418CBBC0300AFEE04 /* Default_person@2x.png in Resources */, + A5195B9318CBBC0300AFEE04 /* Default_person.png in Resources */, + A5195B9218CBBC0300AFEE04 /* contact_mask@2x.png in Resources */, A53729F017011A5200C6FCCF /* lb@2x.png in Resources */, A53729F117011A5200C6FCCF /* lc.png in Resources */, + A5195B9618CBC00100AFEE04 /* apple_logo_animated.gif in Resources */, A53729F217011A5200C6FCCF /* li.png in Resources */, A53729F317011A5200C6FCCF /* lk.png in Resources */, A53729F417011A5200C6FCCF /* lr.png in Resources */, @@ -2000,6 +2279,7 @@ A5372A1117011A5300C6FCCF /* mw.png in Resources */, A5372A1217011A5300C6FCCF /* mx.png in Resources */, A5372A1317011A5300C6FCCF /* my.png in Resources */, + A5E6CC70187D0F4B002A1735 /* PreKeyWhisperMessage.proto in Resources */, A5372A1417011A5300C6FCCF /* mz.png in Resources */, A5372A1517011A5300C6FCCF /* na.png in Resources */, A5372A1617011A5300C6FCCF /* nap.png in Resources */, @@ -2044,7 +2324,6 @@ A5372A3D17011A5300C6FCCF /* qu.png in Resources */, A5372A3E17011A5300C6FCCF /* qu@2x.png in Resources */, A5372A3F17011A5300C6FCCF /* re.png in Resources */, - A5372A4017011A5300C6FCCF /* resizeAll.sh in Resources */, A5372A4117011A5300C6FCCF /* ro.png in Resources */, A5372A4217011A5300C6FCCF /* ro@2x.png in Resources */, A5372A4317011A5300C6FCCF /* rs.png in Resources */, @@ -2054,6 +2333,7 @@ A5372A4717011A5300C6FCCF /* sb.png in Resources */, A5372A4817011A5300C6FCCF /* sc.png in Resources */, A5372A4917011A5300C6FCCF /* scn.png in Resources */, + F8694B5E18C5268500A44474 /* padlock_prompt.png in Resources */, A5372A4A17011A5300C6FCCF /* scn@2x.png in Resources */, A5372A4B17011A5300C6FCCF /* scotland.png in Resources */, A5372A4C17011A5300C6FCCF /* sd.png in Resources */, @@ -2061,6 +2341,7 @@ A5372A4E17011A5300C6FCCF /* sg.png in Resources */, A5372A4F17011A5300C6FCCF /* sh.png in Resources */, A5372A5017011A5300C6FCCF /* sh@2x.png in Resources */, + A56312F818C85B6500222F99 /* Settings.bundle in Resources */, A5372A5117011A5300C6FCCF /* si.png in Resources */, A5372A5217011A5300C6FCCF /* sj.png in Resources */, A5372A5317011A5300C6FCCF /* sk.png in Resources */, @@ -2073,6 +2354,7 @@ A5372A5A17011A5300C6FCCF /* sq@2x.png in Resources */, A5372A5B17011A5300C6FCCF /* sr.png in Resources */, A5372A5C17011A5300C6FCCF /* st.png in Resources */, + A5195B9118CBBC0300AFEE04 /* contact_mask.png in Resources */, A5372A5D17011A5300C6FCCF /* su.png in Resources */, A5372A5E17011A5300C6FCCF /* sv.png in Resources */, A5372A5F17011A5300C6FCCF /* sw.png in Resources */, @@ -2128,157 +2410,314 @@ A5372A9117011A5300C6FCCF /* ws.png in Resources */, A5372A9217011A5300C6FCCF /* ye.png in Resources */, A5372A9317011A5300C6FCCF /* yt.png in Resources */, + B6F10B1C17EF61FE00723719 /* CountryCodes.plist in Resources */, A5372A9417011A5300C6FCCF /* za.png in Resources */, A5372A9517011A5300C6FCCF /* zh.png in Resources */, A5372A9617011A5300C6FCCF /* zm.png in Resources */, A5372A9717011A5300C6FCCF /* zw.png in Resources */, - A5F15B3E1703EB9F00856385 /* Default@2x.png in Resources */, - A5F15B3F1703EB9F00856385 /* Default.png in Resources */, - A5F15B491703EE2F00856385 /* Default-568h@2x.png in Resources */, - A5F15B551703F20D00856385 /* OpenSans-SemiboldItalic.ttf in Resources */, - A5F15B561703F20D00856385 /* OpenSans-Semibold.ttf in Resources */, - A5F15B571703F20D00856385 /* OpenSans-Regular.ttf in Resources */, - A5F15B581703F20D00856385 /* OpenSans-LightItalic.ttf in Resources */, - A5F15B591703F20D00856385 /* OpenSans-Light.ttf in Resources */, - A5F15B5A1703F20D00856385 /* OpenSans-Italic.ttf in Resources */, - A5F15B5B1703F20D00856385 /* OpenSans-ExtraBoldItalic.ttf in Resources */, - A5F15B5C1703F20D00856385 /* OpenSans-ExtraBold.ttf in Resources */, - A5F15B5D1703F20D00856385 /* OpenSans-BoldItalic.ttf in Resources */, - A5F15B5E1703F20D00856385 /* OpenSans-Bold.ttf in Resources */, - A51B77B3170561C3002B8E53 /* textsecure-verifybutton.png in Resources */, - A51B77B4170561C3002B8E53 /* textsecure-selectyourcountry-field.png in Resources */, - A51B77B5170561C3002B8E53 /* textsecure-phonenumber-field.png in Resources */, - A51B77B6170561C3002B8E53 /* textsecure-gettingstarted-menubar.png in Resources */, - A51B77B7170561C3002B8E53 /* textsecure-countrycode-field.png in Resources */, - A51B77B917058E6C002B8E53 /* TextSecure-Global-canvas.png in Resources */, + B6C5AE5817C212330022EA53 /* Default-568h@2x.png in Resources */, + B63A72181808BD3D00C353F2 /* gcm.textsecure.whispersystems.org.cer in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - A53718D516FF95DF00C6FCCF /* Resources */ = { - isa = PBXResourcesBuildPhase; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 33E229A93BF543029696288F /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( - A53718E416FF95DF00C6FCCF /* InfoPlist.strings in Resources */, + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - A53718D616FF95DF00C6FCCF /* ShellScript */ = { + 3CB5212BC54445C68D08CFCC /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; + shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n"; + }; + 7B5FB740FD6F406BBB0D8DDB /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + 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"; + showEnvVarsInLog = 0; + }; + 8C7D62D618659A71003E2B90 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Needed for the Test target to be able to access the App's Keychain\nif [[ \"${SDKROOT}\" != *Simulator* ]]\nthen codesign --verify --force --sign \"$CODE_SIGN_IDENTITY\" \"$CODESIGNING_FOLDER_PATH\"\nfi"; + }; + DE866B39AA57421C900F2883 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + 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"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - A53718AE16FF95DF00C6FCCF /* Sources */ = { + 8C42C13518551B8C000DD353 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A53718C216FF95DF00C6FCCF /* main.m in Sources */, - A53718C616FF95DF00C6FCCF /* AppDelegate.m in Sources */, - A53718D216FF95DF00C6FCCF /* SMSViewController.m in Sources */, - A537190516FFBEE300C6FCCF /* VerificationViewController.m in Sources */, - A537190C1700247600C6FCCF /* Server.m in Sources */, - A537190F1700284900C6FCCF /* Constants.m in Sources */, - A53719281700DC3300C6FCCF /* CountryViewController.m in Sources */, - A53719311701040600C6FCCF /* CountrySegue.m in Sources */, - A5F15A9817027FD000856385 /* Cryptography.m in Sources */, - A5F15A9D1702949200856385 /* NSData+Conversion.m in Sources */, - A5F15AA01702997A00856385 /* KeychainWrapper.m in Sources */, - A5F15B2F1703975F00856385 /* RNCryptor.m in Sources */, - A5F15B301703975F00856385 /* RNCryptorEngine.m in Sources */, - A5F15B311703975F00856385 /* RNDecryptor.m in Sources */, - A5F15B321703975F00856385 /* RNEncryptor.m in Sources */, - A5F15B331703975F00856385 /* RNOpenSSLCryptor.m in Sources */, - A5F15B341703975F00856385 /* RNOpenSSLDecryptor.m in Sources */, - A5F15B351703975F00856385 /* RNOpenSSLEncryptor.m in Sources */, - A5F15B381703B98F00856385 /* NSString+Conversion.m in Sources */, - A5F15B3B1703D15000856385 /* UserDefaults.m in Sources */, - A51B776F17050C42002B8E53 /* FMDatabase.m in Sources */, - A51B777017050C42002B8E53 /* FMDatabaseAdditions.m in Sources */, - A51B777117050C42002B8E53 /* FMDatabasePool.m in Sources */, - A51B777217050C42002B8E53 /* FMDatabaseQueue.m in Sources */, - A51B777417050C42002B8E53 /* FMResultSet.m in Sources */, - A51B778F17050C71002B8E53 /* NSObject+SBJson.m in Sources */, - A51B779017050C71002B8E53 /* SBJsonParser.m in Sources */, - A51B779117050C71002B8E53 /* SBJsonStreamParser.m in Sources */, - A51B779217050C71002B8E53 /* SBJsonStreamParserAccumulator.m in Sources */, - A51B779317050C71002B8E53 /* SBJsonStreamParserAdapter.m in Sources */, - A51B779417050C71002B8E53 /* SBJsonStreamParserState.m in Sources */, - A51B779517050C71002B8E53 /* SBJsonStreamWriter.m in Sources */, - A51B779617050C71002B8E53 /* SBJsonStreamWriterAccumulator.m in Sources */, - A51B779717050C71002B8E53 /* SBJsonStreamWriterState.m in Sources */, - A51B779817050C71002B8E53 /* SBJsonTokeniser.m in Sources */, - A51B779917050C71002B8E53 /* SBJsonUTF8Stream.m in Sources */, - A51B779A17050C71002B8E53 /* SBJsonWriter.m in Sources */, - A51B77A117050F8C002B8E53 /* MessagesDatabase.m in Sources */, - A51B77A617051417002B8E53 /* Message.m in Sources */, - A51B77AC17052C5C002B8E53 /* FilePath.m in Sources */, + B6CC7EAB18D52F09006480FB /* Constants.m in Sources */, + A5E6CC9B18826005002A1735 /* TSProtocolBufferWrapperTests.mm in Sources */, + 8CF7BD9F187130F80023A781 /* TSStorageMasterKeyTests.m in Sources */, + 4CFDC8D2187215F600BDB649 /* TSMessagesDatabaseTests.m in Sources */, + 05B94BF218BD28E400708011 /* VerificationViewControllerTests.m in Sources */, + 8CF7BD8F187097570023A781 /* TSECKeyPairTests.m in Sources */, + 8CF7BD9D18711B140023A781 /* TSUserKeysDatabaseTests.m in Sources */, + 8CF7BDA1187137640023A781 /* TSEncryptedDatabaseTests.m in Sources */, + A56312FD18C9BDCD00222F99 /* TSWaitingPushMessageDatabaseTests.m in Sources */, + 6FBCCDFF18D5361300C5B5F5 /* TSMessageIncomingTest.m in Sources */, + 6FBCCE0018D5361300C5B5F5 /* TSMessageOutgoingTest.m in Sources */, + A52CE05D1862D88B00AF9E39 /* CryptographyTests.mm in Sources */, + B6CA8B2418E38F0D0088BC0C /* TSAxolotlConsistencyTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - A53718D316FF95DF00C6FCCF /* Sources */ = { + A53718AE16FF95DF00C6FCCF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A53718E716FF95DF00C6FCCF /* TextSecureiOSTests.m in Sources */, + A5E6CC71187D0F4B002A1735 /* PushMessageContent.pb.mm in Sources */, + B6F10B1F17EF8F3C00723719 /* NSString+PhoneFormating.m in Sources */, + B6B478E61809E7F5005DDE12 /* TSContactsIntersectionRequest.m in Sources */, + A53718C616FF95DF00C6FCCF /* AppDelegate.m in Sources */, + B639F69F180955E100B7DF00 /* TSServerCodeVerificationRequest.m in Sources */, + A5E6CC84187D0FF1002A1735 /* TSPreKeyWhisperMessage.mm in Sources */, + A5145B3D184B6F35007BDC73 /* TSAttachmentManager.m in Sources */, + A5FE7D84191FA1FA0022C3F7 /* TSPresentIdentityQRCodeViewController.m in Sources */, + A52ECA7C17ED097300C80BC7 /* NSData+Base64.m in Sources */, + A5169D08170FEA8D00A2CE9E /* VerificationViewController.m in Sources */, + A5169D09170FEA8D00A2CE9E /* TextSecureViewController.m in Sources */, + B639AF0D17F5A68100B2244D /* TSNetworkManager.m in Sources */, + 6FCE040C18D427CE00C33B34 /* TSStartOverSegue.m in Sources */, + B63A72161808B8AF00C353F2 /* VerificationCodeViewController.m in Sources */, + FCD30FC918FDBBB7003BD188 /* SignUpStepsViewController.m in Sources */, + 8CF7BD9B1870F23D0023A781 /* TSUserKeysDatabase.m in Sources */, + 8CF7BD951870C8450023A781 /* TSStorageMasterKey.m in Sources */, + B6F10B1717EF4A3A00723719 /* NSLocale+phonePrefixes.m in Sources */, + B6C5FA8018CE3AD600ECC200 /* TSAttachment.m in Sources */, + A5E6CC90187D1E41002A1735 /* TSWhisperMessage.mm in Sources */, + A5E6CC85187D0FF1002A1735 /* TSProtocolBufferWrapper.mm in Sources */, + A538A27E1869DC6500CD0A3D /* TSKeyManager.m in Sources */, + 4C0ACB4B186C876200B328D5 /* TSMessageConversationCell.m in Sources */, + B6C5FA8618CE3AFB00ECC200 /* TSECKeyPair.m in Sources */, + A5145B3A184B68EA007BDC73 /* TSRequestAttachment.m in Sources */, + A5E6CC9E18837C31002A1735 /* TSAxolotlRatchet.mm in Sources */, + A5E6CC73187D0F4B002A1735 /* WhisperMessage.pb.mm in Sources */, + A5E6CC09187AC6D5002A1735 /* TSDownloadAttachment.m in Sources */, + B646538A18E74D9C006010E0 /* TSDerivedSecrets.m in Sources */, + A56D31B9180FCA840093D51B /* TSRegisterPrekeysRequest.m in Sources */, + A5169D0C170FEA8D00A2CE9E /* CountryViewController.m in Sources */, + B6C5FA7D18CE357200ECC200 /* TSGroup.m in Sources */, + A5E6CC6D187D0F4B002A1735 /* IncomingPushMessageSignal.pb.mm in Sources */, + B6E2CFB617F794F600200B69 /* NSString+escape.m in Sources */, + A515AFDB180966E30084ABA5 /* TSMessagesDatabase.m in Sources */, + A5FBAB95184E980E0012E9A5 /* TSUploadAttachment.m in Sources */, + B635B3C7182181E1003CE732 /* TSMessageViewController.m in Sources */, + A5E6CC6F187D0F4B002A1735 /* PreKeyWhisperMessage.pb.mm in Sources */, + B6D23DAF18A91E9800F7E932 /* TSRegisterForPushRequest.m in Sources */, + B6C5FA9918CE3BB900ECC200 /* TSDatabaseManager.m in Sources */, + B6C5FA9518CE3B4600ECC200 /* TSMessagesManager.mm in Sources */, + A5FE7D85191FA1FA0022C3F7 /* TSScanIdentityBarcodeViewController.m in Sources */, + A5E6CC83187D0FF1002A1735 /* TSMessageSignal.mm in Sources */, + B6E2CFB317F7926000200B69 /* TSRequestVerificationCodeRequest.m in Sources */, + A56312FB18C9A45A00222F99 /* TSWaitingPushMessageDatabase.m in Sources */, + 6FCE040B18D427CE00C33B34 /* TSReplaceSegue.m in Sources */, + B68BFDF2180987C6005CC817 /* TSSetMasterPasswordViewController.m in Sources */, + B643D7DE189EB9020025C6F3 /* TSContactPickerViewController.m in Sources */, + A5169D0D170FEA8D00A2CE9E /* CountrySegue.m in Sources */, + A5169D1E170FEB1900A2CE9E /* FilePath.m in Sources */, + A5E6CC86187D0FF1002A1735 /* TSPushMessageContent.mm in Sources */, + A513465D18D65D97005B4FA3 /* TSDeregisterAccountRequest.m in Sources */, + A5169D1F170FEB1900A2CE9E /* NSString+Conversion.m in Sources */, + A580D2BD19323E4A00D6C1CC /* UIColor+TextSecure.m in Sources */, + A5169D20170FEB1900A2CE9E /* Constants.m in Sources */, + B602B96318CE9A4B00181564 /* TSReceivingChain.m in Sources */, + A5169D21170FEB1900A2CE9E /* NSData+Conversion.m in Sources */, + A5145B31184AAD51007BDC73 /* TSMessage.m in Sources */, + A539ED1118D5E06F006C1A61 /* TSSettingsViewController.m in Sources */, + B6C5FA8918CE3B0900ECC200 /* TSMessageIncoming.m in Sources */, + A5E6CC82187D0FF1002A1735 /* TSEncryptedWhisperMessage.mm in Sources */, + A5FE7D86191FA1FA0022C3F7 /* TSVerifyIdentityViewController.m in Sources */, + B639AF1117F5AAA300B2244D /* TSRequest.m in Sources */, + A5169D2B170FEB8F00A2CE9E /* KeychainWrapper.m in Sources */, + B6F7FEC018D77E1500ECC4A2 /* NSData+TSKeyVersion.m in Sources */, + B67E30F918CE1AA8008502F5 /* TSGroupSetupViewController.m in Sources */, + B644F95E18DF5D3700D41FA8 /* NSString+randomString.m in Sources */, + B6C5FA8318CE3AED00ECC200 /* TSContactManager.m in Sources */, + 8C49FC1018497E86006132F6 /* TSStorageError.m in Sources */, + B6C5FA8C18CE3B1000ECC200 /* TSMessageOutgoing.m in Sources */, + A5145B37184B66AD007BDC73 /* TSRequestAttachmentId.m in Sources */, + A530CB04184A02620052B615 /* TSRecipientPrekeyRequest.m in Sources */, + A5620DEF186BD4FA0033483E /* TSSubmitMessageRequest.m in Sources */, + 4C77526D18707A46000FCCA6 /* PasswordUnlockViewController.m in Sources */, + B684346618D1FF2B007AEDF6 /* TSSendingChain.m in Sources */, + B6C5FA7A18CE355500ECC200 /* TSChainKey.m in Sources */, + A56807B818CCAF2A0059083C /* TSGroupContext.m in Sources */, + A5169D2E170FEB8F00A2CE9E /* Cryptography.m in Sources */, + B67E30F618CE19E4008502F5 /* TSContact.m in Sources */, + B6C5FA7718CE354800ECC200 /* TSSession.m in Sources */, + B6C5FA7418CE344700ECC200 /* TSConversation.m in Sources */, + B621AA731928032D00085CF7 /* TSSocketManager.m in Sources */, + B6C5FA8F18CE3B2000ECC200 /* TSPrekey.m in Sources */, + B6C5FA9218CE3B3900ECC200 /* TSMessageKeys.m in Sources */, + B6C5FA9818CE3B6300ECC200 /* RKCK.m in Sources */, + A5169D3D170FEDC600A2CE9E /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - A53718DE16FF95DF00C6FCCF /* PBXTargetDependency */ = { + 8C42C14818551B8C000DD353 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A53718B116FF95DF00C6FCCF /* TextSecureiOS */; - targetProxy = A53718DD16FF95DF00C6FCCF /* PBXContainerItemProxy */; + targetProxy = 8C42C14718551B8C000DD353 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - A53718CD16FF95DF00C6FCCF /* MainStoryboard.storyboard */ = { + 8C42C14118551B8C000DD353 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( - A53718CE16FF95DF00C6FCCF /* en */, + 8C42C14218551B8C000DD353 /* en */, ); - name = MainStoryboard.storyboard; + name = InfoPlist.strings; sourceTree = ""; }; - A53718E216FF95DF00C6FCCF /* InfoPlist.strings */ = { + A53718CD16FF95DF00C6FCCF /* TextSecureStoryboard.storyboard */ = { isa = PBXVariantGroup; children = ( - A53718E316FF95DF00C6FCCF /* en */, + A53718CE16FF95DF00C6FCCF /* en */, ); - name = InfoPlist.strings; + name = TextSecureStoryboard.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 8C42C14918551B8C000DD353 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 950A849531204A2EBF7475C2 /* Pods.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TextSecureiOS.app/TextSecureiOS"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TextSecureiOS Tests/TextSecureiOS Tests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = "TextSecureiOS Tests/TextSecureiOS Tests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 8C42C14A18551B8C000DD353 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 950A849531204A2EBF7475C2 /* Pods.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TextSecureiOS.app/TextSecureiOS"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + ENABLE_NS_ASSERTIONS = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "TextSecureiOS Tests/TextSecureiOS Tests-Prefix.pch"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + INFOPLIST_FILE = "TextSecureiOS Tests/TextSecureiOS Tests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; A53718E816FF95DF00C6FCCF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer: Christine Moran (87AB72WHFF)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Christine Moran (87AB72WHFF)"; COPY_PHASE_STRIP = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -2291,12 +2730,13 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)\"", ); ONLY_ACTIVE_ARCH = YES; + PROVISIONING_PROFILE = "D96269C2-F551-4984-903A-0205628FB988"; SDKROOT = iphoneos; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Libraries/include/**"; }; @@ -2308,24 +2748,27 @@ ALWAYS_SEARCH_USER_PATHS = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer: Christine Moran (87AB72WHFF)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Christine Moran (87AB72WHFF)"; COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.1; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)\"", ); OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + PROVISIONING_PROFILE = "D96269C2-F551-4984-903A-0205628FB988"; SDKROOT = iphoneos; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Libraries/include/**"; VALIDATE_PRODUCT = YES; @@ -2334,19 +2777,20 @@ }; A53718EB16FF95DF00C6FCCF /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 950A849531204A2EBF7475C2 /* Pods.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; + CODE_SIGN_IDENTITY = "iPhone Developer: Christine Moran (87AB72WHFF)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Christine Moran (87AB72WHFF)"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "TextSecureiOS/TextSecureiOS-Prefix.pch"; - HEADER_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "TextSecureiOS/TextSecureiOS-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)/**", - "\"$(SRCROOT)/Libraries/lib\"/**", - "\"$(SRCROOT)\"/**", - "\"$(SRCROOT)/../../../../Library/Developer/Xcode/DerivedData/RNCryptor-bpiygwtuyhesovfvrqwqcqnvfsmh/Build/Products/Debug-iphoneos\"/**", - ); + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LIBRARY_SEARCH_PATHS = "$(inherited)/**"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "F6107428-6DD2-4184-B965-C80E88D4D42B"; + TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Libraries/include/**"; WRAPPER_EXTENSION = app; }; @@ -2354,84 +2798,54 @@ }; A53718EC16FF95DF00C6FCCF /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 950A849531204A2EBF7475C2 /* Pods.xcconfig */; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; + CODE_SIGN_IDENTITY = "iPhone Developer: Christine Moran (87AB72WHFF)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Christine Moran (87AB72WHFF)"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "TextSecureiOS/TextSecureiOS-Prefix.pch"; - HEADER_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "TextSecureiOS/TextSecureiOS-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; LIBRARY_SEARCH_PATHS = ( "$(inherited)/**", - "\"$(SRCROOT)/Libraries/lib\"/**", - "\"$(SRCROOT)\"/**", - "\"$(SRCROOT)/../../../../Library/Developer/Xcode/DerivedData/RNCryptor-bpiygwtuyhesovfvrqwqcqnvfsmh/Build/Products/Debug-iphoneos\"/**", + "$(SRCROOT)/Libraries/lib/**", ); PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "F6107428-6DD2-4184-B965-C80E88D4D42B"; + TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Libraries/include/**"; WRAPPER_EXTENSION = app; }; name = Release; }; - A53718EE16FF95DF00C6FCCF /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TextSecureiOS.app/TextSecureiOS"; - FRAMEWORK_SEARCH_PATHS = ( - "\"$(SDKROOT)/Developer/Library/Frameworks\"", - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "TextSecureiOS/TextSecureiOS-Prefix.pch"; - INFOPLIST_FILE = "TextSecureiOSTests/TextSecureiOSTests-Info.plist"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUNDLE_LOADER)"; - WRAPPER_EXTENSION = octest; - }; - name = Debug; - }; - A53718EF16FF95DF00C6FCCF /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TextSecureiOS.app/TextSecureiOS"; - FRAMEWORK_SEARCH_PATHS = ( - "\"$(SDKROOT)/Developer/Library/Frameworks\"", - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "TextSecureiOS/TextSecureiOS-Prefix.pch"; - INFOPLIST_FILE = "TextSecureiOSTests/TextSecureiOSTests-Info.plist"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUNDLE_LOADER)"; - WRAPPER_EXTENSION = octest; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - A53718AD16FF95DF00C6FCCF /* Build configuration list for PBXProject "TextSecureiOS" */ = { + 8C42C14B18551B8C000DD353 /* Build configuration list for PBXNativeTarget "TextSecureiOS Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( - A53718E816FF95DF00C6FCCF /* Debug */, - A53718E916FF95DF00C6FCCF /* Release */, + 8C42C14918551B8C000DD353 /* Debug */, + 8C42C14A18551B8C000DD353 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - A53718EA16FF95DF00C6FCCF /* Build configuration list for PBXNativeTarget "TextSecureiOS" */ = { + A53718AD16FF95DF00C6FCCF /* Build configuration list for PBXProject "TextSecureiOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - A53718EB16FF95DF00C6FCCF /* Debug */, - A53718EC16FF95DF00C6FCCF /* Release */, + A53718E816FF95DF00C6FCCF /* Debug */, + A53718E916FF95DF00C6FCCF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - A53718ED16FF95DF00C6FCCF /* Build configuration list for PBXNativeTarget "TextSecureiOSTests" */ = { + A53718EA16FF95DF00C6FCCF /* Build configuration list for PBXNativeTarget "TextSecureiOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - A53718EE16FF95DF00C6FCCF /* Debug */, - A53718EF16FF95DF00C6FCCF /* Release */, + A53718EB16FF95DF00C6FCCF /* Debug */, + A53718EC16FF95DF00C6FCCF /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/TextSecureiOS.xcodeproj/project.xcworkspace/xcuserdata/corbett.xcuserdatad/UserInterfaceState.xcuserstate b/TextSecureiOS.xcodeproj/project.xcworkspace/xcuserdata/corbett.xcuserdatad/UserInterfaceState.xcuserstate index d6f5d1e..1bda808 100644 Binary files a/TextSecureiOS.xcodeproj/project.xcworkspace/xcuserdata/corbett.xcuserdatad/UserInterfaceState.xcuserstate and b/TextSecureiOS.xcodeproj/project.xcworkspace/xcuserdata/corbett.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TextSecureiOS.xcodeproj/xcshareddata/xcschemes/TextSecureiOS.xcscheme b/TextSecureiOS.xcodeproj/xcshareddata/xcschemes/TextSecureiOS.xcscheme new file mode 100644 index 0000000..af9da26 --- /dev/null +++ b/TextSecureiOS.xcodeproj/xcshareddata/xcschemes/TextSecureiOS.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TextSecureiOS.xcodeproj/xcuserdata/corbett.xcuserdatad/xcschemes/TextSecureiOS.xcscheme b/TextSecureiOS.xcodeproj/xcuserdata/corbett.xcuserdatad/xcschemes/TextSecureiOS.xcscheme index 6e6a489..c63c72c 100644 --- a/TextSecureiOS.xcodeproj/xcuserdata/corbett.xcuserdatad/xcschemes/TextSecureiOS.xcscheme +++ b/TextSecureiOS.xcodeproj/xcuserdata/corbett.xcuserdatad/xcschemes/TextSecureiOS.xcscheme @@ -1,6 +1,6 @@ + + + + SuppressBuildableAutocreation + 8C42C13818551B8C000DD353 + + primary + + A53718B116FF95DF00C6FCCF primary diff --git a/TextSecureiOS.xcworkspace/contents.xcworkspacedata b/TextSecureiOS.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..e903151 --- /dev/null +++ b/TextSecureiOS.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/TextSecureiOS/AppDelegate.h b/TextSecureiOS/AppDelegate.h index 9f9494d..3342a9a 100644 --- a/TextSecureiOS/AppDelegate.h +++ b/TextSecureiOS/AppDelegate.h @@ -7,12 +7,17 @@ // #import -#import "Server.h" -#import "MessagesDatabase.h" + +#ifdef DEBUG +@interface AppDelegate : UIResponder +#else @interface AppDelegate : UIResponder +#endif + @property (strong, nonatomic) UIWindow *window; -@property (strong,nonatomic) Server *server; -@property (strong,nonatomic) MessagesDatabase *messageDatabase; --(void) handlePush:(NSDictionary *)pushInfo; + +// used to obscure the application when the user backgrounds it +@property (strong, nonatomic) UIWindow *blankWindow; + @end diff --git a/TextSecureiOS/AppDelegate.m b/TextSecureiOS/AppDelegate.m index d2f891f..9c400e4 100644 --- a/TextSecureiOS/AppDelegate.m +++ b/TextSecureiOS/AppDelegate.m @@ -8,107 +8,250 @@ #import "AppDelegate.h" #import "Cryptography.h" -#import "UserDefaults.h" -#import "NSObject+SBJson.h" +#import "TSKeyManager.h" +#import "TSMessagesDatabase.h" +#import "TSStorageMasterKey.h" +#import "TSStorageError.h" +#import "TSRegisterForPushRequest.h" +#import "NSString+Conversion.h" +#import "TSMessagesManager.h" +#import "NSData+Base64.h" +#import "TSAttachmentManager.h" +#import "TSMessage.h" +#import "TSAttachment.h" +#import "TSWaitingPushMessageDatabase.h" +#import "TSStorageMasterKey.h" +#import "IASKSettingsReader.h" +#import "TSSocketManager.h" +#import "TSDeregisterAccountRequest.h" +#import "UIColor+TextSecure.h" +#define kChangePasswordAlertView 1 +#define kDeregisterAlertView 2 + @implementation AppDelegate -@synthesize server; -@synthesize messageDatabase; + +#pragma mark - UIApplication delegate methods + +#define firstLaunchKey @"FirstLaunch" + - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // notification details held in the launchOptions dictionary. if the dictionary is nil then the user tapped the application icon as normal. - self.server = [[Server alloc] init]; - self.messageDatabase = [[MessagesDatabase alloc] init]; - if(launchOptions!=nil) { - [self handlePush:launchOptions]; - - } - if([UserDefaults hasVerifiedPhone]) { - [[UIApplication sharedApplication] registerForRemoteNotificationTypes: - (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; + // UIAppearance proxy setup + [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor TSBlueBarColor]} forState:UIControlStateNormal]; + [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor TSLightTextColor]} forState:UIControlStateDisabled]; + + // If this is the first launch, we want to remove stuff from the Keychain that might be there from a previous install + + if (![[NSUserDefaults standardUserDefaults] boolForKey:firstLaunchKey]) { + [self setDefaultUserSettings]; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:firstLaunchKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; + [TSKeyManager removeAllKeychainItems]; + DLog(@"First Launch"); } - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(markVerifiedPhone:) name:@"VerifiedPhone" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(markSentVerification:) name:@"SentVerification" object:nil]; + + [self updateBasedOnUserSettings]; - return YES; +#ifdef DEBUG + [[BITHockeyManager sharedHockeyManager] configureWithBetaIdentifier:@"9e6b7f4732558ba8480fb2bcd0a5c3da" + liveIdentifier:@"9e6b7f4732558ba8480fb2bcd0a5c3da" + delegate:self]; + [[BITHockeyManager sharedHockeyManager] startManager]; +#endif + + if([TSKeyManager hasVerifiedPhoneNumber] && [TSMessagesDatabase databaseWasCreated]) { + [[UIApplication sharedApplication] registerForRemoteNotificationTypes: + (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; + } + + // we need to create the window here, if we do it in -applicationDidEnterBackground it's too late and it + // doesn't get screenshotted. + self.blankWindow = ({ + UIWindow *window = [[UIWindow alloc] initWithFrame:self.window.bounds]; + window.hidden = YES; + window.userInteractionEnabled = NO; + window.windowLevel = CGFLOAT_MAX; + self.blankWindow.rootViewController = [[UIViewController alloc] init]; + UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.blankWindow.bounds]; + if (self.blankWindow.bounds.size.height == 568) { + imageView.image = [UIImage imageNamed:@"Default-568h"]; + } else { + imageView.image = [UIImage imageNamed:@"Default"]; + } + imageView.opaque = YES; + [self.blankWindow.rootViewController.view addSubview:imageView]; + window; + }); + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(messageDatabaseDidUnlock) name:TSDatabaseDidUnlockNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBasedOnUserSettings) name:kIASKAppSettingChanged object:nil]; + + [self socketConnect]; + + return YES; } --(void) cryptoDemo { - [Cryptography testEncryption]; - // demoing generating and storing account authentication token. Just for prototyping - [Cryptography generateAndStoreNewAccountAuthenticationToken]; - // Testing storage - NSLog(@"testing storage %@",[Cryptography getAuthenticationToken]); - NSLog(@"testing out HMAC %@", [Cryptography computeSHA1DigestForString:@""]); - - // testing rn -// NSData *data = [@"Data" dataUsingEncoding:NSUTF8StringEncoding]; -// NSError *error; -// NSData *encryptedData = [RNEncryptor encryptData:data -// withSettings:kRNCryptorAES256Settings -// password:aPassword -// error:&error]; - +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { + + if([[url query] isEqualToString:@"changePasswordRequest"]) { + UIAlertView* changePasswordDialogue = [[UIAlertView alloc] initWithTitle:@"Change password" message:nil delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; + changePasswordDialogue.tag = kChangePasswordAlertView; + [changePasswordDialogue setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput]; + UITextField *oldPasswordField = [changePasswordDialogue textFieldAtIndex:0]; + oldPasswordField.placeholder = @"new password"; + oldPasswordField.secureTextEntry = YES; + UITextField *newPasswordField = [changePasswordDialogue textFieldAtIndex:1]; + newPasswordField.placeholder = @"confirm password"; + newPasswordField.secureTextEntry = YES; + [changePasswordDialogue show]; + } + else if([[url query] isEqualToString:@"deregisterUserRequest"]) { + UIAlertView* deregisterDialogue = [[UIAlertView alloc] initWithTitle:@"Deregister from TextSecure" message:@"the app will clear your data and restart" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; + deregisterDialogue.tag = kDeregisterAlertView; + [deregisterDialogue show]; + } + return YES; } --(void) markVerifiedPhone:(NSNotification*)notification { - [UserDefaults markVerifiedPhone]; +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { + if(alertView.tag == kChangePasswordAlertView) { + if(buttonIndex == 1) { + // here's where we change the database password @nabla-c0d3 + + //TODO: Decide if we should ask for old password. Maybe we could also use TSSetMasterPasswordViewController and its UI for entering the new password. + NSString *newPw = [alertView textFieldAtIndex:0].text; + NSString *confirmPw = [alertView textFieldAtIndex:1].text; + + if ([newPw isEqualToString:confirmPw]) { + NSError *error = nil; + [TSStorageMasterKey changeStorageMasterKeyPasswordTo:newPw error:&error]; + if (!error) { + [[[UIAlertView alloc] initWithTitle:@"Success" message:@"The password has been changed successfully." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; + } else { + [[[UIAlertView alloc] initWithTitle:@"Sorry" message:@"The password could not be changed." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; + } + } else { + [[[UIAlertView alloc] initWithTitle:@"Sorry" message:@"The passwords did not match." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; + } + } + } + else if(alertView.tag == kDeregisterAlertView) { + if(buttonIndex==1) { + // here we can deregister the user! + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSDeregisterAccountRequest alloc] initWithUser:[TSKeyManager getAuthenticationToken]] success:^(AFHTTPRequestOperation *operation, id responseObject){ + [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"resetDB"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + [self updateBasedOnUserSettings]; + + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + [[[UIAlertView alloc]initWithTitle:@"Sorry we had an issue with this request" message:@"Read Dlog" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show]; + }]; + + } + } } --(void) markSentVerification:(NSNotification*)notification { - [UserDefaults markSentVerification]; +- (void)applicationDidEnterBackground:(UIApplication *)application { + [application beginBackgroundTaskWithExpirationHandler:^{ + /** + * This code block allows the app to run in the background just the time to notify the socket that we disconnected. + */ + }]; + + [TSSocketManager resignActivity]; + if ([[NSUserDefaults standardUserDefaults] boolForKey:kScreenshotProtection]) { + self.blankWindow.hidden = NO; + } } - -- (void)applicationWillResignActive:(UIApplication *)application -{ - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +- (void)applicationWillEnterForeground:(UIApplication *)application { + self.blankWindow.hidden = YES; + [self updateBasedOnUserSettings]; + [self socketConnect]; } -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +- (void)socketConnect{ + if ([TSKeyManager hasVerifiedPhoneNumber] && ![TSStorageMasterKey isStorageMasterKeyLocked]) { + [TSSocketManager becomeActive]; + } } -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +- (void)applicationWillTerminate:(UIApplication *)application { + [[NSNotificationCenter defaultCenter] removeObserver:self name:TSDatabaseDidUnlockNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:kIASKAppSettingChanged object:nil]; } -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +#pragma mark settings +-(void) setDefaultUserSettings { + /* this is as apparently defaults set in settings bundle are just display defaults, must still set in code */ + NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO] ,@"resetDB",[NSNumber numberWithBool:YES], kScreenshotProtection,[NSNumber numberWithInt:5],@"lockDBAfter",[NSNumber numberWithBool:NO],kStorageMasterKeyWasCreated, nil]; + [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; + [[NSUserDefaults standardUserDefaults] synchronize]; } -- (void)applicationWillTerminate:(UIApplication *)application -{ - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +- (void)updateBasedOnUserSettings { + if([[NSUserDefaults standardUserDefaults] boolForKey:@"resetDB"]) { + [TSKeyManager removeAllKeychainItems]; + [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:[[NSBundle mainBundle] bundleIdentifier]]; + [[NSUserDefaults standardUserDefaults] synchronize]; + exit(0); + } } +#pragma mark - Push notifications + - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { - NSString *stringToken = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]; - stringToken = [stringToken stringByReplacingOccurrencesOfString:@" " withString:@""]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"SendAPN" object:self userInfo:[[NSDictionary alloc] initWithObjectsAndKeys:stringToken,@"apnRegistrationId", nil]]; + NSString *stringToken = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]; + stringToken = [stringToken stringByReplacingOccurrencesOfString:@" " withString:@""]; + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRegisterForPushRequest alloc] initWithPushIdentifier:stringToken] success:^(AFHTTPRequestOperation *operation, id responseObject) { -} + switch (operation.response.statusCode) { + case 200: + DLog(@"Device registered for push notifications"); + break; + + default: +#warning Add error handling if not able to send the token + break; + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { +#warning Add error handling if not able to send the token + }]; -- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error -{ - NSLog(@"Failed to get token, error: %@", error); } +- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { + + + // UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"TextSecure needs push notifications" message:@"We couldn't enable push notifications. TexSecure uses them heavily. Please try registering again." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + // [alert show]; + +#ifdef DEBUG +#warning registering with dummy ID so that we can proceed in the simulator. You'll want to change this! + NSData *deviceToken = [NSData dataFromBase64String:[@"christine" base64Encoded]]; + [self application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +#endif -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { - // TODO: add new message here! - [self handlePush:userInfo]; } --(void) handlePush:(NSDictionary *)pushInfo { - NSDictionary* fullMessageJson = [[pushInfo objectForKey:@"message_body"] JSONValue]; - NSLog(@"full message json %@",fullMessageJson); - Message *message = [[Message alloc] initWithText:[fullMessageJson objectForKey:@"messageText"] messageSource:[fullMessageJson objectForKey:@"source"] messageDestinations:[fullMessageJson objectForKey:@"destinations"] messageAttachments:[fullMessageJson objectForKey:@"attachments"] messageTimestamp:[NSDate date]]; - [self.messageDatabase addMessage:message]; +-(void) messageDatabaseDidUnlock { + // This method is triggered whenever DB is unlocked + if(![TSStorageMasterKey isStorageMasterKeyLocked]) { + [TSSocketManager becomeActive]; + } } + +#pragma mark - HockeyApp Delegate Methods + +#ifdef DEBUG +- (NSString *)customDeviceIdentifierForUpdateManager:(BITUpdateManager *)updateManager { +#ifndef CONFIGURATION_AppStore + if ([[UIDevice currentDevice] respondsToSelector:@selector(uniqueIdentifier)]) + return [[UIDevice currentDevice] performSelector:@selector(uniqueIdentifier)]; +#endif + return nil; +} +#endif + @end diff --git a/TextSecureiOS/Categories/NSLocale+phonePrefixes.h b/TextSecureiOS/Categories/NSLocale+phonePrefixes.h new file mode 100644 index 0000000..824bb77 --- /dev/null +++ b/TextSecureiOS/Categories/NSLocale+phonePrefixes.h @@ -0,0 +1,19 @@ +// +// NSLocale+phonePrefixes.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/22/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "NBPhoneNumberUtil.h" + +@interface NSLocale (phonePrefixes) + ++(NSString*) currentCountryPhonePrefix; + ++(NSString*) localizedCodeNameForPhonePrefix:(NSString*)prefix; + + +@end diff --git a/TextSecureiOS/Categories/NSLocale+phonePrefixes.m b/TextSecureiOS/Categories/NSLocale+phonePrefixes.m new file mode 100644 index 0000000..fd4b0ac --- /dev/null +++ b/TextSecureiOS/Categories/NSLocale+phonePrefixes.m @@ -0,0 +1,30 @@ +// +// NSLocale+phonePrefixes.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/22/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "NSLocale+phonePrefixes.h" + +@implementation NSLocale (phonePrefixes) + ++(NSString*) currentCountryPhonePrefix{ + NSLocale *locale = [self currentLocale]; + NSString *isoCode = [locale objectForKey:NSLocaleCountryCode]; + + return [self phonePrefixFromISOCode:isoCode]; +} + ++(NSString*) phonePrefixFromISOCode:(NSString*)isoCode{ + NBPhoneNumberUtil *phoneUtil = [NBPhoneNumberUtil sharedInstance]; + return [[NSString alloc] initWithFormat:@"%@",[phoneUtil getCountryCodeForRegion:isoCode]]; +} + ++(NSString*) localizedCodeNameForPhonePrefix:(NSString*)prefix{ + NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init]; + return [[NBPhoneNumberUtil sharedInstance] getRegionCodeForCountryCode:[formatter numberFromString:prefix]]; +} + +@end diff --git a/TextSecureiOS/Categories/NSString+PhoneFormating.h b/TextSecureiOS/Categories/NSString+PhoneFormating.h new file mode 100644 index 0000000..66cc5fb --- /dev/null +++ b/TextSecureiOS/Categories/NSString+PhoneFormating.h @@ -0,0 +1,16 @@ +// +// NSString+PhoneFormating.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/22/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +@interface NSString (phoneFormating) + +-(NSString*) prependPlus; +-(NSString*) removeAllFormattingButNumbers; + +@end diff --git a/TextSecureiOS/Categories/NSString+PhoneFormating.m b/TextSecureiOS/Categories/NSString+PhoneFormating.m new file mode 100644 index 0000000..8ee7127 --- /dev/null +++ b/TextSecureiOS/Categories/NSString+PhoneFormating.m @@ -0,0 +1,21 @@ +// +// NSString+PhoneFormating.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/22/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "NSString+PhoneFormating.h" + +@implementation NSString (phoneFormating) + +-(NSString*) prependPlus{ + return [@"+" stringByAppendingString:self]; +} + +-(NSString*) removeAllFormattingButNumbers{ + return [[self componentsSeparatedByCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""]; +} + +@end diff --git a/TextSecureiOS/Categories/NSString+escape.h b/TextSecureiOS/Categories/NSString+escape.h new file mode 100644 index 0000000..42f7614 --- /dev/null +++ b/TextSecureiOS/Categories/NSString+escape.h @@ -0,0 +1,13 @@ +// +// NSString+escape.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +@interface NSString (escape) +- (NSString*) escape; +@end diff --git a/TextSecureiOS/Categories/NSString+escape.m b/TextSecureiOS/Categories/NSString+escape.m new file mode 100644 index 0000000..ba9c41a --- /dev/null +++ b/TextSecureiOS/Categories/NSString+escape.m @@ -0,0 +1,18 @@ +// +// NSString+escape.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "NSString+escape.h" + +@implementation NSString (escape) + +- (NSString*) escape{ + return [self stringByAddingPercentEscapesUsingEncoding: + NSASCIIStringEncoding]; +} + +@end diff --git a/TextSecureiOS/Categories/UIColor+TextSecure.h b/TextSecureiOS/Categories/UIColor+TextSecure.h new file mode 100644 index 0000000..d86d1dc --- /dev/null +++ b/TextSecureiOS/Categories/UIColor+TextSecure.h @@ -0,0 +1,30 @@ +// +// UIColor+TextSecure.h +// TextSecureiOS +// +// Created by Dylan Bourgeois on 16/04/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface UIColor (TextSecure) +//Green color for validation ++(UIColor*)TSValidColor; +//Light gray background for buttons ++(UIColor*)TSButtonBackgroundColor; +//Light gray color for Text ++(UIColor*)TSLightTextColor; +//Light blue color for underlining text ++(UIColor*)TSBlueBarColor; +//Light blue color for underlining text with alpha=0.7 ++(UIColor*)TSBlueBarColorWithAlpha; +//Red invalid color ++(UIColor*)TSInvalidColor; +//Orange warning color ++(UIColor*)TSOrangeWarningColor; +//Yellow warning color ++(UIColor*)TSYellowWarningColor; + + +@end diff --git a/TextSecureiOS/Categories/UIColor+TextSecure.m b/TextSecureiOS/Categories/UIColor+TextSecure.m new file mode 100644 index 0000000..de10518 --- /dev/null +++ b/TextSecureiOS/Categories/UIColor+TextSecure.m @@ -0,0 +1,47 @@ +// +// UIColor+TextSecure.m +// TextSecureiOS +// +// Created by Dylan Bourgeois on 16/04/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "UIColor+TextSecure.h" + +@implementation UIColor (TextSecure) + ++(UIColor*)TSValidColor +{ + return [UIColor colorWithRed:105.f/255.f green:206.f/255.f blue:38.f/255.f alpha:1]; +} ++(UIColor*)TSButtonBackgroundColor +{ + return [UIColor colorWithRed:239.f/255.f green:239.f/255.f blue:244.f/255.f alpha:1]; +} ++(UIColor*)TSLightTextColor +{ + return [UIColor colorWithRed:175.f/255.f green:177.f/255.f blue:175.f/255.f alpha:1]; +} ++(UIColor*)TSBlueBarColor +{ + //Correction of RGB values for alpha=1 + return [UIColor colorWithRed:101.f/255.f green:187.f/255.f blue:231.f/255.f alpha:1]; +} ++(UIColor*)TSBlueBarColorWithAlpha +{ + //Alpha used in Storyboard for bars is 0.7 + return [UIColor colorWithRed:43.f/255.f green:166.f/255.f blue:224.f/255.f alpha:0.7]; +} ++(UIColor*)TSInvalidColor +{ + return [UIColor colorWithRed:183.f/255.f green:12.f/255.f blue:32.f/255.f alpha:1]; +} ++(UIColor*)TSOrangeWarningColor +{ + return [UIColor colorWithRed:255.f/255.f green:190.f/255.f blue:18.f/255.f alpha:1]; +} ++(UIColor*)TSYellowWarningColor +{ + return [UIColor colorWithRed:247.f/255.f green:229.f/255.f blue:0.f/255.f alpha:1]; +} +@end diff --git a/TextSecureiOS/Constants.h b/TextSecureiOS/Constants.h deleted file mode 100644 index cf1945d..0000000 --- a/TextSecureiOS/Constants.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Constants.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -// -// Constants.h -// Kliq -// -// Defines some constants needed by bamboo, the temporary API we are using as an interface to facebook connect -// -// Created by Christine Corbett Moran on 6/11/10. -// Copyright 2010 Cannytrophic LLC. All rights reserved. -// - -// We have three server instances running on three ports, two for development one for production -// This requires we have two FB apps, one for dev one for production. -// Their use is configured statically in the app here - -extern NSString* const textSecureServer; -extern NSString* const textSecureAccountsAPI; -extern NSString* const textSecureMessagesAPI; - -extern NSString* const appName; -extern NSString* const authenticationTokenStorageId; -extern NSString* const usernameTokenStorageId; -typedef enum { - CREATE_ACCOUNT=0, - VERIFY_ACCOUNT=1, - SEND_APN=2, - SEND_MESSAGE=3 -} TextSecureRequestType; - - diff --git a/TextSecureiOS/Constants.m b/TextSecureiOS/Constants.m deleted file mode 100644 index 36d705b..0000000 --- a/TextSecureiOS/Constants.m +++ /dev/null @@ -1,23 +0,0 @@ -// -// Constants.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "Constants.h" - - -#define TEST - -NSString* const appName = @"TextSecure"; -NSString* const authenticationTokenStorageId = @"TextSecureAuthenticationToken"; -NSString* const usernameTokenStorageId = @"UsernameAuthenticationToken"; -#ifdef TEST -NSString* const textSecureServer = @"http://textsecure-gcm-production.herokuapp.com"; -#else -NSString* const textSecureServer = @"https://gcm.textsecure.whispersystems.org"; -#endif -NSString* const textSecureAccountsAPI = @"v1/accounts"; -NSString* const textSecureMessagesAPI = @"v1/messages"; diff --git a/TextSecureiOS/Contacts/TSContact.h b/TextSecureiOS/Contacts/TSContact.h new file mode 100644 index 0000000..aef9ccf --- /dev/null +++ b/TextSecureiOS/Contacts/TSContact.h @@ -0,0 +1,77 @@ +// +// TSContact.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/20/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSContact : NSObject + +#pragma mark Initializing properties +@property (nonatomic, readonly) NSString *relay; +@property (nonatomic, readonly) NSString *registeredID; + +#pragma mark Optional properties + +@property (nonatomic) NSData *identityKey; +@property (nonatomic) NSMutableArray *deviceIDs; //NSNumber because needs to be serializable +@property BOOL identityKeyIsVerified; +@property BOOL isSelected; + +/** + * A TSContact is created to store information about contacts the user is communicating with. + * + * @param registeredID The registered phone number of the TextSecure user + * @param relay The relay on which the TextSecure user is registered + * @return TSContact instance + */ + + +- (instancetype)contactWithRegisteredID:(NSString*)registeredID; + +- (instancetype)initWithRegisteredID:(NSString*)registeredID relay:(NSString*)relay; + +/** + * Same as TS initWithRegisteredID:(NSString*)registeredID relay:(NSString*)relay. TSContacts are initiated when the user looks up which of his contacts are using TextSecure. Constructor reflects the parameters given by the contact intersection API (https://github.com/WhisperSystems/TextSecure-Server/wiki/API-Protocol#wiki-getting-a-contact-intersection) + * + * @param registeredID The registered phone number of the TextSecure user + * @param relay The relay on which the TextSecure user is registered + * @param abId The ABRecordRef for the user + * @return TSContact instance + */ +- (instancetype)initWithRegisteredID:(NSString*)registeredID relay:(NSString*)relay addressBookID:(NSNumber*)abId; + +/** + * Returns the name of the TextSecure user + * + * @return TextSecure user's name. Firstname + Lastname of addressbook + */ + +- (NSString*)name; + +/** + * Returns addressbook's identifier of the contact + * + * @return ABRecordRef NSNumber-encoded + */ +- (NSNumber*)addressBookID; + +/** + * Returns the label for the registered phone number + * + * @return Localized label of the contact's registered phone number + */ + +- (void)reverseIsSelected; +- (NSString*)labelForRegisteredNumber; + +/** + * Synchronously saves the TSContact to the database + */ + +- (void)save; + +@end diff --git a/TextSecureiOS/Contacts/TSContact.m b/TextSecureiOS/Contacts/TSContact.m new file mode 100644 index 0000000..4f71f11 --- /dev/null +++ b/TextSecureiOS/Contacts/TSContact.m @@ -0,0 +1,111 @@ +// +// TSContact.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/20/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSContactManager.h" +#import +#import "TSMessagesDatabase.h" + +@interface TSContact () +@property (copy) NSNumber *abID; +@end + +@implementation TSContact + +- (instancetype)contactWithRegisteredID:(NSString*)registeredID{ + return [self initWithRegisteredID:registeredID relay:nil]; +} + +-(instancetype) initWithRegisteredID:(NSString*)registeredID relay:(NSString*)relay{ + self = [super init];; + + if(self) { + _registeredID = registeredID; + _relay = relay; + } + return self; +} + +-(instancetype) initWithRegisteredID:(NSString*)registeredID relay:(NSString*)relay addressBookID:(NSNumber *)abId{ + self = [self initWithRegisteredID:registeredID relay:relay]; + if (self) { + _abID = abId; + } + return self; +} + + +- (NSNumber*) addressBookID{ + // Looking up a AddressBook Token might take time if user has a lot of contacts, let's cache it to avoid doing unecessary calls to the AdressBook. + if (!self.abID) { + self.abID = [[TSContactManager sharedManager]getContactIDForNumber:self.registeredID]; + } + return self.abID; +} + +- (NSString*) name{ + if ([self addressBookID]){ + ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, nil); + ABRecordRef currentPerson = ABAddressBookGetPersonWithRecordID(addressBook, [[self addressBookID] intValue]); + NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonFirstNameProperty) ; + NSString *surname = (__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonLastNameProperty) ; + CFRelease(addressBook); + return [NSString stringWithFormat:@"%@ %@", firstName?firstName:@"", surname?surname:@""]; + }else{ + return [self registeredID]; + } +} + +- (NSString*) labelForRegisteredNumber{ + if ([self addressBookID] && self.registeredID) { + ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, nil); + ABRecordRef currentPerson = ABAddressBookGetPersonWithRecordID(addressBook, [[self addressBookID] intValue]); + + ABMutableMultiValueRef phoneNumbers = ABRecordCopyValue(currentPerson, kABPersonPhoneProperty); + + NSString *label = @""; + + for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) + { + CFStringRef phoneNumber, phoneNumberLabel; + + phoneNumberLabel = ABMultiValueCopyLabelAtIndex(phoneNumbers, i); + phoneNumber = ABMultiValueCopyValueAtIndex(phoneNumbers, i); + + NSString *number = (__bridge NSString*) phoneNumber; + + if ([[TSContactManager cleanPhoneNumber:number] isEqualToString:self.registeredID]) { + + CFStringRef raw_label = ABMultiValueCopyLabelAtIndex(phoneNumbers, i); + label = (__bridge_transfer NSString *)(ABAddressBookCopyLocalizedLabel(raw_label)); + CFRelease(raw_label); + CFRelease(phoneNumberLabel); + CFRelease(phoneNumber); + break; + } + CFRelease(phoneNumberLabel); + CFRelease(phoneNumber); + } + + CFRelease(phoneNumbers); + CFRelease(addressBook); + + return label; + + } else { + return @""; + } +} + +-(void) reverseIsSelected { + self.isSelected = !self.isSelected; +} +-(void) save{ + [TSMessagesDatabase storeContact:self]; +} + +@end diff --git a/TextSecureiOS/Contacts/TSContactManager.h b/TextSecureiOS/Contacts/TSContactManager.h new file mode 100644 index 0000000..eb8cc42 --- /dev/null +++ b/TextSecureiOS/Contacts/TSContactManager.h @@ -0,0 +1,19 @@ +// +// TSContactManager.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSContact.h" + +@interface TSContactManager : NSObject + ++ (id)sharedManager; ++ (NSString*) cleanPhoneNumber:(NSString*)number; ++ (void) getAllContactsIDs:(void (^)(NSArray *contacts))contactFetchCompletionBlock; +- (NSNumber*) getContactIDForNumber:(NSString*) phoneNumber; + +@end diff --git a/TextSecureiOS/Contacts/TSContactManager.m b/TextSecureiOS/Contacts/TSContactManager.m new file mode 100644 index 0000000..f417f5d --- /dev/null +++ b/TextSecureiOS/Contacts/TSContactManager.m @@ -0,0 +1,181 @@ +// +// TSContactManager.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSContactManager.h" +#import +#import +#import +#import "NSString+Conversion.h" +#import "Cryptography.h" +#import "TSNetworkManager.h" +#import "TSContactsIntersectionRequest.h" + +@interface TSContactManager () +@property NSDictionary *cachedContactLookup; +@end + +@implementation TSContactManager + ++ (id)sharedManager { + static TSContactManager *sharedMyManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedMyManager = [[self alloc] init]; + }); + + return sharedMyManager; +} + +- (id)init { + if (self = [super init]) { + + } + return self; +} + +/** + * Returns a given phone number in international E.123 format but without any white-spaces + * + * @param number phone number to convert to E.123 + */ + ++ (NSString*) cleanPhoneNumber:(NSString*)number{ + NBPhoneNumberUtil *phoneUtil = [NBPhoneNumberUtil sharedInstance]; + + NBPhoneNumber *phone = [phoneUtil parse:number defaultRegion:[[NSLocale currentLocale]objectForKey:NSLocaleCountryCode] error:nil]; + return [NSString stringWithFormat:@"+%@%@", phone.countryCode,phone.nationalNumber]; +} + +- (NSNumber*) getContactIDForNumber:(NSString*) phoneNumber{ + + if (!self.cachedContactLookup) { + [self makeContactLookupTable]; + } + + return [self.cachedContactLookup objectForKey:[[self class]cleanPhoneNumber:phoneNumber]]; +} + +- (void)makeContactLookupTable{ + ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil); + NSMutableDictionary *ab = [NSMutableDictionary dictionary]; + __block BOOL accessGranted = NO; + + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + + ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { + accessGranted = granted; + dispatch_semaphore_signal(sema); + }); + + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + + if (accessGranted) { + CFArrayRef all = ABAddressBookCopyArrayOfAllPeople(addressBook); + CFIndex n = ABAddressBookGetPersonCount(addressBook); + + for( int i = 0 ; i < n ; i++ ) + { + ABRecordRef ref = CFArrayGetValueAtIndex(all, i); + int referenceID = ABRecordGetRecordID(ref); + NSNumber *contactReferenceID = [NSNumber numberWithInt:referenceID]; + // We iterate through users + + ABMultiValueRef phones = ABRecordCopyValue(ref, kABPersonPhoneProperty); + for(CFIndex j = 0; j < ABMultiValueGetCount(phones); j++) + { + NSString *phoneNumber = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, j); + + NSString *cleanedNumber = [[self class] cleanPhoneNumber:phoneNumber]; + + [ab setObject:contactReferenceID forKey:cleanedNumber]; + } + CFRelease(phones); + } + CFRelease(all); + } + CFRelease(addressBook); + + self.cachedContactLookup = ab; +} + ++ (void) getAllContactsIDs:(void (^)(NSArray *contacts))contactFetchCompletionBlock { + + ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil); + __block BOOL accessGranted = NO; + + dispatch_semaphore_t sema = dispatch_semaphore_create(0); + ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { + accessGranted = granted; + dispatch_semaphore_signal(sema); + }); + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + + if (accessGranted == NO) { + CFRelease(addressBook); + return; + } + + CFArrayRef all = ABAddressBookCopyArrayOfAllPeople(addressBook); + CFIndex n = ABAddressBookGetPersonCount(addressBook); + NSMutableDictionary *abIdLookup = [NSMutableDictionary dictionary]; + NSMutableDictionary *phoneNumberLookup = [NSMutableDictionary dictionary]; + + for( int i = 0 ; i < n ; i++ ) + { + ABRecordRef ref = CFArrayGetValueAtIndex(all, i); + int referenceID = ABRecordGetRecordID(ref); + NSNumber *contactReferenceID = [NSNumber numberWithInt:referenceID]; + // We iterate through users + + ABMultiValueRef phones = ABRecordCopyValue(ref, kABPersonPhoneProperty); + for(CFIndex j = 0; j < ABMultiValueGetCount(phones); j++) + { + NSString *phoneNumber = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, j); + NSString *cleanedNumber = [self cleanPhoneNumber:phoneNumber]; + NSString *hashedPhoneNumber = [Cryptography truncatedSHA1Base64EncodedWithoutPadding:cleanedNumber]; + + // Not showing the user himself in the buddy list. + + if (![cleanedNumber isEqualToString:[TSKeyManager getUsernameToken]]) { + [abIdLookup setObject:contactReferenceID forKey:hashedPhoneNumber]; + [phoneNumberLookup setObject:cleanedNumber forKey:hashedPhoneNumber]; + } + } + + CFRelease(phones); + } + + // Send hashes to server + [[TSNetworkManager sharedManager]queueAuthenticatedRequest:[[TSContactsIntersectionRequest alloc] initWithHashesArray:[abIdLookup allKeys]] success:^(AFHTTPRequestOperation *operation, id responseObject) { + NSArray *contactsHashes = [responseObject objectForKey:@"contacts"]; + + NSMutableArray *contacts = [NSMutableArray array]; + for (NSDictionary *contactHash in contactsHashes) { + + TSContact *contact = [[TSContact alloc]initWithRegisteredID:[phoneNumberLookup objectForKey:[contactHash objectForKey:@"token"]] relay:[phoneNumberLookup objectForKey:[contactHash objectForKey:@"relay"]] addressBookID:[abIdLookup objectForKey:[contactHash objectForKey:@"token"]]]; + + [contacts addObject:contact]; + } + + contactFetchCompletionBlock(contacts); + + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + + defaultNetworkErrorMessage + + }]; + + CFRelease(all); + CFRelease(addressBook); +} + +- (void)dealloc { + // Should never be called, but just here for clarity really. +} + +@end diff --git a/TextSecureiOS/CountryCodesBy2.plist b/TextSecureiOS/CountryCodesBy2.plist deleted file mode 100644 index 69995d3..0000000 --- a/TextSecureiOS/CountryCodesBy2.plist +++ /dev/null @@ -1,2067 +0,0 @@ - - - - - AD - - code - AD - country_code - 376 - name - Andorra - - AE - - code - AE - country_code - 971 - name - United Arab Emirates - - AF - - code - AF - country_code - 93 - name - Afghanistan - - AG - - code - AG - country_code - 1 - name - Antigua and Barbuda - - AI - - code - AI - country_code - 1 - name - Anguilla - - AL - - code - AL - country_code - 355 - name - Albania - - AM - - code - AM - country_code - 374 - name - Armenia - - AN - - code - AN - country_code - 599 - name - Netherlands Antilles - - AO - - code - AO - country_code - 244 - name - Angola - - AR - - code - AR - country_code - 54 - name - Argentina - - AS - - code - AS - country_code - 1 - name - American Samoa - - AT - - code - AT - country_code - 43 - name - Austria - - AU - - code - AU - country_code - 61 - name - Australia - - AW - - code - AW - country_code - 297 - name - Aruba - - AZ - - code - AZ - country_code - 994 - name - Azerbaijan - - BA - - code - BA - country_code - 387 - name - Bosnia - - BB - - code - BB - country_code - 1 - name - Barbados - - BD - - code - BD - country_code - 880 - name - Bangladesh - - BE - - code - BE - country_code - 32 - name - Belgium - - BF - - code - BF - country_code - 226 - name - Burkina Faso - - BG - - code - BG - country_code - 359 - name - Bulgaria - - BH - - code - BH - country_code - 973 - name - Bahrain - - BI - - code - BI - country_code - 257 - name - Burundi - - BJ - - code - BJ - country_code - 229 - name - Benin - - BM - - code - BM - country_code - 1 - name - Bermuda - - BN - - code - BN - country_code - 673 - name - Brunei Darussalam - - BO - - code - BO - country_code - 591 - name - Bolivia - - BR - - code - BR - country_code - 55 - name - Brazil - - BS - - code - BS - country_code - 1 - name - Bahamas - - BT - - code - BT - country_code - 975 - name - Bhutan - - BW - - code - BW - country_code - 267 - name - Botswana - - BY - - code - BY - country_code - 375 - name - Belarus - - BZ - - code - BZ - country_code - 501 - name - Belize - - CA - - code - CA - country_code - 1 - name - Canada - - CC - - code - CC - country_code - 61 - name - Cocos (Keeling) Islands - - CF - - code - CF - country_code - 236 - name - Central African Republic - - CG - - code - CG - country_code - 242 - name - Congo - - CH - - code - CH - country_code - 41 - name - Switzerland - - CI - - code - CI - country_code - 225 - name - Cote D'ivoire - - CK - - code - CK - country_code - 682 - name - Cook Islands - - CL - - code - CL - country_code - 56 - name - Chile - - CM - - code - CM - country_code - 237 - name - Cameroon - - CN - - code - CN - country_code - 86 - name - China - - CO - - code - CO - country_code - 57 - name - Colombia - - CR - - code - CR - country_code - 506 - name - Costa Rica - - CU - - code - CU - country_code - 53 - name - Cuba - - CV - - code - CV - country_code - 238 - name - Cape Verde - - CX - - code - CX - country_code - 61 - name - Christmas Island - - CY - - code - CY - country_code - 537 - name - Cyprus - - CZ - - code - CZ - country_code - 420 - name - Czech Republic - - DE - - code - DE - country_code - 49 - name - Germany - - DJ - - code - DJ - country_code - 253 - name - Djibouti - - DK - - code - DK - country_code - 45 - name - Denmark - - DM - - code - DM - country_code - 1 - name - Dominica - - DO - - code - DO - country_code - 1 - name - Dominican Republic - - DZ - - code - DZ - country_code - 213 - name - Algeria - - EC - - code - EC - country_code - 593 - name - Ecuador - - EE - - code - EE - country_code - 372 - name - Estonia - - EG - - code - EG - country_code - 20 - name - Egypt - - ER - - code - ER - country_code - 291 - name - Eritrea - - ES - - code - ES - country_code - 34 - name - Spain - - ET - - code - ET - country_code - 251 - name - Ethiopia - - FI - - code - FI - country_code - 358 - name - Finland - - FJ - - code - FJ - country_code - 679 - name - Fiji - - FK - - code - FK - country_code - 500 - name - Falkland Islands (Malvinas) - - FM - - code - FM - country_code - 691 - name - Micronesia - - FO - - code - FO - country_code - 298 - name - Faroe Islands - - FR - - code - FR - country_code - 33 - name - France - - GA - - code - GA - country_code - 241 - name - Gabon - - GB - - code - GB - country_code - 44 - name - United Kingdom - - GD - - code - GD - country_code - 1 - name - Grenada - - GE - - code - GE - country_code - 995 - name - Georgia - - GF - - code - GF - country_code - 594 - name - French Guiana - - GH - - code - GH - country_code - 233 - name - Ghana - - GI - - code - GI - country_code - 350 - name - Gibraltar - - GL - - code - GL - country_code - 299 - name - Greenland - - GM - - code - GM - country_code - 220 - name - Gambia - - GN - - code - GN - country_code - 224 - name - Guinea - - GP - - code - GP - country_code - 590 - name - Guadeloupe - - GQ - - code - GQ - country_code - 240 - name - Equatorial Guinea - - GR - - code - GR - country_code - 30 - name - Greece - - GT - - code - GT - country_code - 502 - name - Guatemala - - GU - - code - GU - country_code - 1 - name - Guam - - GW - - code - GW - country_code - 245 - name - Guinea-bissau - - GY - - code - GY - country_code - 595 - name - Guyana - - HK - - code - HK - country_code - 852 - name - Hong Kong - - HN - - code - HN - country_code - 504 - name - Honduras - - HR - - code - HR - country_code - 385 - name - Croatia - - HT - - code - HT - country_code - 509 - name - Haiti - - HU - - code - HU - country_code - 36 - name - Hungary - - ID - - code - ID - country_code - 62 - name - Indonesia - - IE - - code - IE - country_code - 353 - name - Ireland - - IL - - code - IL - country_code - 972 - name - Israel - - IN - - code - IN - country_code - 91 - name - India - - IO - - code - IO - country_code - 246 - name - British Indian Ocean Territory - - IQ - - code - IQ - country_code - 964 - name - Iraq - - IR - - code - IR - country_code - 98 - name - Iran - - IS - - code - IS - country_code - 354 - name - Iceland - - IT - - code - IT - country_code - 39 - name - Italy - - JM - - code - JM - country_code - 1 - name - Jamaica - - JO - - code - JO - country_code - 962 - name - Jordan - - JP - - code - JP - country_code - 81 - name - Japan - - KE - - code - KE - country_code - 254 - name - Kenya - - KG - - code - KG - country_code - 996 - name - Kyrgyzstan - - KH - - code - KH - country_code - 855 - name - Cambodia - - KI - - code - KI - country_code - 686 - name - Kiribati - - KM - - code - KM - country_code - 269 - name - Comoros - - KN - - code - KN - country_code - 1 - name - Saint Kitts and Nevis - - KR - - code - KR - country_code - 82 - name - South Korea - - KW - - code - KW - country_code - 965 - name - Kuwait - - KY - - code - KY - country_code - 345 - name - Cayman Islands - - KZ - - code - KZ - country_code - 77 - name - Kazakhstan - - LA - - code - LA - country_code - 856 - name - Laos - - LB - - code - LB - country_code - 961 - name - Lebanon - - LC - - code - LC - country_code - 1 - name - Saint Lucia - - LI - - code - LI - country_code - 423 - name - Liechtenstein - - LK - - code - LK - country_code - 94 - name - Sri Lanka - - LR - - code - LR - country_code - 231 - name - Liberia - - LS - - code - LS - country_code - 266 - name - Lesotho - - LT - - code - LT - country_code - 370 - name - Lithuania - - LU - - code - LU - country_code - 352 - name - Luxembourg - - LV - - code - LV - country_code - 371 - name - Latvia - - LY - - code - LY - country_code - 218 - name - Libya - - MA - - code - MA - country_code - 212 - name - Morocco - - MC - - code - MC - country_code - 377 - name - Monaco - - MD - - code - MD - country_code - 373 - name - Moldova - - ME - - code - ME - country_code - 382 - name - Montenegro - - MG - - code - MG - country_code - 261 - name - Madagascar - - MH - - code - MH - country_code - 692 - name - Marshall Islands - - MK - - code - MK - country_code - 389 - name - Macedonia - - ML - - code - ML - country_code - 223 - name - Mali - - MM - - code - MM - country_code - 95 - name - Myanmar - - MN - - code - MN - country_code - 976 - name - Mongolia - - MO - - code - MO - country_code - 853 - name - Macau - - MP - - code - MP - country_code - 1 - name - Northern Mariana Islands - - MQ - - code - MQ - country_code - 596 - name - Martinique - - MR - - code - MR - country_code - 222 - name - Mauritania - - MS - - code - MS - country_code - 1 - name - Montserrat - - MT - - code - MT - country_code - 356 - name - Malta - - MU - - code - MU - country_code - 230 - name - Mauritius - - MV - - code - MV - country_code - 960 - name - Maldives - - MW - - code - MW - country_code - 265 - name - Malawi - - MX - - code - MX - country_code - 52 - name - Mexico - - MY - - code - MY - country_code - 60 - name - Malaysia - - MZ - - code - MZ - country_code - 258 - name - Mozambique - - NA - - code - NA - country_code - 264 - name - Namibia - - NC - - code - NC - country_code - 687 - name - New Caledonia - - NE - - code - NE - country_code - 227 - name - Niger - - NF - - code - NF - country_code - 672 - name - Norfolk Island - - NG - - code - NG - country_code - 234 - name - Nigeria - - NI - - code - NI - country_code - 505 - name - Nicaragua - - NL - - code - NL - country_code - 31 - name - Netherlands - - NO - - code - NO - country_code - 47 - name - Norway - - NP - - code - NP - country_code - 977 - name - Nepal - - NR - - code - NR - country_code - 674 - name - Nauru - - NU - - code - NU - country_code - 683 - name - Niue - - NZ - - code - NZ - country_code - 64 - name - New Zealand - - OM - - code - OM - country_code - 968 - name - Oman - - PA - - code - PA - country_code - 507 - name - Panama - - PE - - code - PE - country_code - 51 - name - Peru - - PF - - code - PF - country_code - 689 - name - French Polynesia - - PG - - code - PG - country_code - 675 - name - Papua New Guinea - - PH - - code - PH - country_code - 63 - name - Philippines - - PK - - code - PK - country_code - 92 - name - Pakistan - - PL - - code - PL - country_code - 48 - name - Poland - - PM - - code - PM - country_code - 508 - name - St. Pierre and Miquelon - - PN - - code - PN - country_code - 872 - name - Pitcairn - - PR - - code - PR - country_code - 1 - name - Puerto Rico - - PT - - code - PT - country_code - 351 - name - Portugal - - PW - - code - PW - country_code - 680 - name - Palau - - PY - - code - PY - country_code - 595 - name - Paraguay - - QA - - code - QA - country_code - 974 - name - Qatar - - RE - - code - RE - country_code - 262 - name - Reunion - - RO - - code - RO - country_code - 40 - name - Romania - - RS - - code - RS - country_code - 381 - name - Serbia - - RU - - code - RU - country_code - 7 - name - Russian Federation - - RW - - code - RW - country_code - 250 - name - Rwanda - - SA - - code - SA - country_code - 966 - name - Saudi Arabia - - SB - - code - SB - country_code - 677 - name - Solomon Islands - - SC - - code - SC - country_code - 248 - name - Seychelles - - SD - - code - SD - country_code - 249 - name - Sudan - - SE - - code - SE - country_code - 46 - name - Sweden - - SG - - code - SG - country_code - 65 - name - Singapore - - SH - - code - SH - country_code - 290 - name - St. Helena - - SI - - code - SI - country_code - 386 - name - Slovenia - - SJ - - code - SJ - country_code - 47 - name - Svalbard and Jan Mayen Islands - - SK - - code - SK - country_code - 421 - name - Slovakia - - SL - - code - SL - country_code - 232 - name - Sierra Leone - - SM - - code - SM - country_code - 378 - name - San Marino - - SN - - code - SN - country_code - 221 - name - Senegal - - SO - - code - SO - country_code - 252 - name - Somalia - - SR - - code - SR - country_code - 597 - name - Suriname - - ST - - code - ST - country_code - 239 - name - Sao Tome and Principe - - SV - - code - SV - country_code - 503 - name - El Salvador - - SY - - code - SY - country_code - 963 - name - Syrian Arab Republic - - SZ - - code - SZ - country_code - 268 - name - Swaziland - - TC - - code - TC - country_code - 1 - name - Turks and Caicos Islands - - TD - - code - TD - country_code - 235 - name - Chad - - TG - - code - TG - country_code - 228 - name - Togo - - TH - - code - TH - country_code - 66 - name - Thailand - - TJ - - code - TJ - country_code - 992 - name - Tajikistan - - TK - - code - TK - country_code - 690 - name - Tokelau - - TM - - code - TM - country_code - 993 - name - Turkmenistan - - TN - - code - TN - country_code - 216 - name - Tunisia - - TO - - code - TO - country_code - 676 - name - Tonga - - TR - - code - TR - country_code - 90 - name - Turkey - - TT - - code - TT - country_code - 1 - name - Trinidad and Tobago - - TV - - code - TV - country_code - 688 - name - Tuvalu - - TW - - code - TW - country_code - 886 - name - Taiwan - - TZ - - code - TZ - country_code - 255 - name - Tanzania - - UA - - code - UA - country_code - 380 - name - Ukraine - - UG - - code - UG - country_code - 256 - name - Uganda - - US - - code - US - country_code - 1 - name - United States - - UY - - code - UY - country_code - 598 - name - Uruguay - - UZ - - code - UZ - country_code - 998 - name - Uzbekistan - - VA - - code - VA - country_code - 379 - name - Vatican City State - - VC - - code - VC - country_code - 1 - name - Saint Vincent and The Grenadines - - VE - - code - VE - country_code - 58 - name - Venezuela - - VG - - code - VG - country_code - 1 - name - Virgin Islands (British) - - VI - - code - VI - country_code - 1 - name - Virgin Islands (U.S.) - - VN - - code - VN - country_code - 84 - name - Vietnam - - VU - - code - VU - country_code - 678 - name - Vanuatu - - WF - - code - WF - country_code - 681 - name - Wallis and Futuna Islands - - WS - - code - WS - country_code - 685 - name - Samoa - - YE - - code - YE - country_code - 967 - name - Yemen - - YT - - code - YT - country_code - 262 - name - Mayotte - - ZA - - code - ZA - country_code - 27 - name - South Africa - - ZM - - code - ZM - country_code - 260 - name - Zambia - - ZW - - code - ZW - country_code - 263 - name - Zimbabwe - - - diff --git a/TextSecureiOS/Cryptography.h b/TextSecureiOS/Cryptography.h deleted file mode 100644 index 643cdbd..0000000 --- a/TextSecureiOS/Cryptography.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Cryptography.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/26/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import - -@interface Cryptography : NSObject -+(NSString*) generateAndStoreNewAccountAuthenticationToken; -+ (BOOL) storeAuthenticationToken:(NSString*)token; -+ (NSString*) getAuthenticationToken; -+ (BOOL) storeUsernameToken:(NSString*)token; -+ (NSString*) getUsernameToken; -+ (NSString*)computeSHA1DigestForString:(NSString*)input; -+ (void) generateECKeyPairSecurityFramework; // not used -+ (void) generateNISTp256ECCKeyPair; -+(void) testEncryption; -+ (NSString*) getAuthorizationToken; -@end diff --git a/TextSecureiOS/Cryptography.m b/TextSecureiOS/Cryptography.m deleted file mode 100644 index ed96816..0000000 --- a/TextSecureiOS/Cryptography.m +++ /dev/null @@ -1,145 +0,0 @@ -// -// Cryptography.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/26/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "Cryptography.h" -#import -#import -#import "NSData+Conversion.h" -#import "KeychainWrapper.h" -#import "Constants.h" -#import "RNEncryptor.h" -#import "RNDecryptor.h" -// Now we can use openssl -#include -#include -#include "NSString+Conversion.h" -@implementation Cryptography - -+(NSString*) generateAndStoreNewAccountAuthenticationToken { - NSMutableData* authToken = [NSMutableData dataWithLength:16]; - int err = 0; - err = SecRandomCopyBytes(kSecRandomDefault,16,[authToken mutableBytes]); - if(err != noErr) { - @throw [NSException exceptionWithName:@"authenicationProblem" reason:@"problem generating the random authentication token" userInfo:nil]; - } - NSString* authTokenPrint = [[NSData dataWithData:authToken] hexadecimalString]; - [Cryptography storeAuthenticationToken:authTokenPrint]; - return authTokenPrint; - -} -+ (BOOL) storeAuthenticationToken:(NSString*)token { - return [KeychainWrapper createKeychainValue:token forIdentifier:authenticationTokenStorageId]; -} - - -+ (NSString*) getAuthenticationToken { - return [KeychainWrapper keychainStringFromMatchingIdentifier:authenticationTokenStorageId]; -} - - -+ (BOOL) storeUsernameToken:(NSString*)token { - return [KeychainWrapper createKeychainValue:token forIdentifier:usernameTokenStorageId]; -} - - -+ (NSString*) getUsernameToken { - return [KeychainWrapper keychainStringFromMatchingIdentifier:usernameTokenStorageId]; -} - -+ (NSString*) getAuthorizationToken { - return [[NSString stringWithFormat:@"%@:%@",[Cryptography getUsernameToken],[Cryptography getAuthenticationToken]] base64Encoded]; -} - -+ (NSString*)computeSHA1DigestForString:(NSString*)input { - // Here we are taking in our string hash, placing that inside of a C Char Array, then parsing it through the SHA1 encryption method. - // Will be used for 160 bit HMAC (should be 10 char hex) - const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding]; - NSData *data = [NSData dataWithBytes:cstr length:input.length]; - uint8_t digest[CC_SHA1_DIGEST_LENGTH]; - - // This is an iOS5-specific method. - // It takes in the data, how much data, and then output format, which in this case is an int array. - CC_SHA1(data.bytes, data.length, digest); - - // Setup our Objective-C output. - NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; - - // Parse through the CC_SHA1 results (stored inside of digest[]). - for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { - [output appendFormat:@"%02x", digest[i]]; - } - - return output; -} - -+(void) testEncryption { - // we are going to want to use kCCModeCTR instead of kCCModeCBC... may need to change settings as well. - NSData *data = [@"hello world here is some much longer text in fact it is a paragraph of text omgz it is awesome hello world here is some much longer text in fact it is a paragraph of text omgz it is awesomehello world here is some much longer text in fact it is a paragraph of text omgz it is awesome ello world here is some much longer text in fact it is a paragraph of text omgz it is awesome hello world here is some much longer text in fact it is a paragraph of text omgz it is awesome" dataUsingEncoding:NSUTF8StringEncoding]; - NSError *error; - NSData *encryptedData = [RNEncryptor encryptData:data - withSettings:kRNCryptorAES256Settings - password:@"good password" - error:&error]; - NSLog(@"encrypting %@",encryptedData); - NSData *decryptedData = [RNDecryptor decryptData:encryptedData - withPassword:@"good password" - error:&error]; - NSLog(@"decrypting %@",[[NSString alloc] initWithData:decryptedData - encoding:NSUTF8StringEncoding]); -} - -+ (void) generateECKeyPairSecurityFramework { - // This native Security.framework method is unused, as it is not sufficiently documented and we are unable to use point compression. It is included here in case we wish to do comparisons later on. - SInt32 iKeySize = 256; // possible key size goes up to 521. - CFNumberRef keySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &iKeySize); - const void* values[] = {kSecAttrKeyTypeEC, keySize}; - const void* keys[] = {kSecAttrKeyType, kSecAttrKeySizeInBits}; - CFDictionaryRef parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL); - - SecKeyRef publicKey, privateKey; - OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey); - if(ret != errSecSuccess ){ - @throw [NSException exceptionWithName:@"ECGenerationProblem" reason:@"problem generating the EC key" userInfo:nil]; - } -} -+ (void) generateNISTp256ECCKeyPair { - EC_KEY *ecKey = EC_KEY_new(); - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); - EC_KEY_set_group(ecKey, group); - EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_COMPRESSED); - EC_KEY_generate_key(ecKey); -// const BIGNUM *privateKey = EC_KEY_get0_private_key(ecKey); -// const EC_POINT *publicKey = EC_KEY_get0_public_key(ecKey); -// -// int len = i2d_ECPrivateKey(ecKey,NULL); -// unsigned char *privateKeyBuf = OPENSSL_malloc(len); -// memset(privateKeyBuf, 0, len); -// int ret = i2d_ECPrivateKey(ecKey,&privateKeyBuf); -// if (!ret){ -// NSLog(@"Private key to DER failed\n"); -// return; -// } -// else { -// NSLog(@"Private key %s",privateKeyBuf); -// } -// len = i2o_ECPublicKey(ecKey,NULL); -// unsigned char *publicKeyBuf = OPENSSL_malloc(len); -// memset(publicKeyBuf, 0, len); -// ret = i2o_ECPublicKey(ecKey,&publicKeyBuf); -// if (!ret){ -// NSLog(@"Public key to octed failed\n"); -// return; -// } -// else { -// NSLog(@"Public key %s",publicKeyBuf); -// } - - NSLog(@"key generation generated"); -} - -@end diff --git a/TextSecureiOS/Documentation/verification.sh b/TextSecureiOS/Documentation/verification.sh new file mode 100755 index 0000000..fa048e9 --- /dev/null +++ b/TextSecureiOS/Documentation/verification.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +#test server verification +# Example: +#REGISTER +41791111111: +curl -k -X POST --header "Content-Length: 0" https://gcm.textsecure.whispersystems.org/v1/accounts/sms/number +# AUTHORIZE: +curl -k -X PUT -i -H "Authorization: Basic KzQxNzk5NjI0NDk5OjI1OTNlNWZhZTdiNzUxODYxYzcxOTE4YjRhNGU5YTE5" -H "Content-Type:application/json" --data "{\"signalingKey\" : \"Ti71M5PR63/SOnrermsyZMlrl2WrwAMD/5cH5Z/bjKEG1e3jjKzUBf1zI0bPt4ai\"}" https://gcm.textsecure.whispersystems.org/v1/accounts/code/111111 +# with 111111 being the verification code the phone number recieved by SMS + +#NOTE: k means ignoring the SSL warning, what we should be doing rather is using Moxie's cert diff --git a/TextSecureiOS/Message.h b/TextSecureiOS/Message.h deleted file mode 100644 index 67aa5e0..0000000 --- a/TextSecureiOS/Message.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Message.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/28/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import - -@interface Message : NSObject -@property (nonatomic,strong) NSString* source; -@property (nonatomic,strong) NSArray* destinations; -@property (nonatomic,strong) NSString* text; -@property (nonatomic,strong) NSArray* attachments; -@property (nonatomic,strong) NSDate* timestamp; --(id)initWithText:(NSString*)messageText messageSource:(NSString*) messageSource messageDestinations:(NSArray*)messageDestinations messageAttachments:(NSArray*) messageAttachments messageTimestamp:(NSDate*)messageTimestamp; -@end diff --git a/TextSecureiOS/Message.m b/TextSecureiOS/Message.m deleted file mode 100644 index 591776c..0000000 --- a/TextSecureiOS/Message.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// Message.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/28/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "Message.h" - -@implementation Message - -@synthesize source; -@synthesize destinations; -@synthesize text; -@synthesize attachments; -@synthesize timestamp; - --(id)initWithText:(NSString*)messageText messageSource:(NSString*) messageSource messageDestinations:(NSArray*)messageDestinations messageAttachments:(NSArray*) messageAttachments messageTimestamp:(NSDate*)messageTimestamp{ - self = [super init]; - if (self) { - self.source = messageSource; - self.destinations = messageDestinations; - self.text = messageText; - self.attachments = messageAttachments; - self.timestamp =messageTimestamp; - } - return self; -} - -@end diff --git a/TextSecureiOS/MessagesDatabase.h b/TextSecureiOS/MessagesDatabase.h deleted file mode 100644 index e7aebdc..0000000 --- a/TextSecureiOS/MessagesDatabase.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// MessagesDatabase.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/28/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import -#import "FMDatabase.h" -#import "Message.h" - -@interface MessagesDatabase : NSObject -@property (nonatomic,strong) FMDatabase *database; --(BOOL) addMessage:(Message*)message; --(NSArray*) getMessages; -@end diff --git a/TextSecureiOS/MessagesDatabase.m b/TextSecureiOS/MessagesDatabase.m deleted file mode 100644 index 6c6f27e..0000000 --- a/TextSecureiOS/MessagesDatabase.m +++ /dev/null @@ -1,61 +0,0 @@ -// -// MessagesDatabase.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/28/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "MessagesDatabase.h" -#import "FilePath.h" -@implementation MessagesDatabase -@synthesize database; - --(id) init { - if(self==[super init]) { - self.database = [FMDatabase databaseWithPath: - [FilePath pathInDocumentsDirectory:@"messages.db"]]; - if (![self.database open]) { - NSLog(@"Could not open message db."); - return nil; - } - [self.database executeUpdate:@"CREATE TABLE IF NOT EXISTS messages (source TEXT, text TEXT, destination TEXT,timestamp DATETIME DEFAULT current_timestamp)"]; - // Later we will need a more complicated schema, including handling of message threads, multiple desintations, attachments, multiple attachments, - } - return self; -} - - --(BOOL) addMessage:(Message*)message { - BOOL success=[self.database executeUpdate:@"INSERT INTO messages (source,text,destination) VALUES (?, ?, ?)", - message.source, - message.text, - [message.destinations objectAtIndex:0]]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"DatabaseUpdated" object:self]; - return success; - -} - - --(NSArray*) getMessages { - NSMutableArray* messages = [[NSMutableArray alloc] init]; - - FMResultSet *rs = [self.database executeQuery:@"SELECT * FROM messages"]; - - NSLog(@"results %@",rs); - while([rs next]){ - Message* message = [[Message alloc] initWithText:[rs stringForColumn:@"text"] - messageSource:[rs stringForColumn:@"source"] - messageDestinations:[[NSArray alloc] initWithObjects:[rs stringForColumn:@"destination"], nil] - messageAttachments:[[NSArray alloc] init] - messageTimestamp:[rs dateForColumn:@"timestamp"]]; - ; - [messages addObject:message]; - } - return messages; -} - - - - -@end diff --git a/TextSecureiOS/MessagesManager/TSMessagesManager.h b/TextSecureiOS/MessagesManager/TSMessagesManager.h new file mode 100644 index 0000000..8f8379c --- /dev/null +++ b/TextSecureiOS/MessagesManager/TSMessagesManager.h @@ -0,0 +1,32 @@ +// +// MessagesManager.h +// TextSecureiOS` +// +// Created by Frederic Jacobs on 30/11/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +/** + * The Messages Manager class is crucial, it does do all the Messages processing (push notifications, sending) + * The TSMessager is blocking and does work on + * + */ + + +#import +#import "TSContact.h" +#import "TSGroup.h" +@class TSMessageOutgoing; + +@interface TSMessagesManager : NSObject + ++ (id)sharedManager; + +- (void) receiveMessagePush:(NSDictionary*)pushDict; + +// Methods for sending messages should be - schedule send, cancel send +-(void) scheduleMessageSend:(TSMessageOutgoing*)message; // Sending messages should have a completion block for UI processing + +-(void) submitMessage:(TSMessageOutgoing*)message to:(NSString*)recipientId serializedMessage:(NSString*)serializedMessage ofType:(TSWhisperMessageType)messageType; + +@end diff --git a/TextSecureiOS/MessagesManager/TSMessagesManager.mm b/TextSecureiOS/MessagesManager/TSMessagesManager.mm new file mode 100644 index 0000000..46a3272 --- /dev/null +++ b/TextSecureiOS/MessagesManager/TSMessagesManager.mm @@ -0,0 +1,198 @@ +// +// MessagesManager.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 30/11/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSMessagesManager.h" +#import "TSAxolotlRatchet.hh" +#import "TSMessage.h" +#import "NSData+Base64.h" +#import "TSSubmitMessageRequest.h" +#import "TSMessagesDatabase.h" +#import "TSMessagesManager.h" +#import "TSAttachmentManager.h" +#import "TSKeyManager.h" +#import "Cryptography.h" +#import "TSMessageOutgoing.h" +#import "TSMessagesDatabase.h" +#import "TSAttachment.h" +#import "IncomingPushMessageSignal.pb.hh" +#import "TSMessageSignal.hh" +#import "TSContact.h" +#import "TSPreKeyWhisperMessage.hh" +#import "TSRecipientPrekeyRequest.h" +#import "TSSession.h" +#import "NSData+TSKeyVersion.h" + + +@interface TSMessagesManager (){ + dispatch_queue_t queue; +} +@end + +@implementation TSMessagesManager + +- (void)scheduleMessageSend:(TSMessageOutgoing *)message { + [TSMessagesDatabase storeMessage:message]; + if([message shouldSend ]) { + [self sendMessage:message]; + } +} + ++ (id)sharedManager { + static TSMessagesManager *sharedMyManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedMyManager = [[self alloc] init]; + }); + return sharedMyManager; +} + +- (id)init { + if (self = [super init]) { + queue = dispatch_queue_create("TSMessageManagerQueue", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +-(void)sendMessage:(TSMessageOutgoing*)message { + if(message.group!=nil) { + for(TSContact* recipient in [TSMessagesDatabase membersForGroup:message.group]) { + if([recipient.registeredID isEqualToString:[TSKeyManager getUsernameToken]]){ + continue; + + } + [self sendMessage:[message copyMessageToRecipient:recipient.registeredID] toContact:recipient]; + } + } + else { + TSContact *recipient = [TSMessagesDatabase contactForRegisteredID:message.recipientId]; + [self sendMessage:message toContact:recipient]; + } +} + + +-(void)sendMessage:(TSMessageOutgoing*)message toContact:(TSContact*) recipient{ + dispatch_async(queue, ^{ + TSContact *recipientInDb = [TSMessagesDatabase contactForRegisteredID:recipient.registeredID]; // makes sure that we get the latest relay info, etc. + + NSArray *sessions = [TSMessagesDatabase sessionsForContact:recipientInDb]; + + + if ([sessions count] > 0) { + for (TSSession *session in sessions){ + [[TSMessagesManager sharedManager] submitMessage:message to:message.recipientId serializedMessage:[[[TSAxolotlRatchet encryptMessage:message withSession:session] getTextSecureProtocolData] base64EncodedString] ofType:TSEncryptedWhisperMessageType]; + } + + } else{ + + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRecipientPrekeyRequest alloc] initWithRecipient:recipient] success:^(AFHTTPRequestOperation *operation, id responseObject) { + switch (operation.response.statusCode) { + case 200:{ + + + // Extracting the recipients keying material from server payload + + NSArray *keys = [responseObject objectForKey:@"keys"]; + + for (NSDictionary *responseObject in keys){ + NSData* theirIdentityKey = [NSData dataFromBase64String:[responseObject objectForKey:@"identityKey"]]; + + + if (recipient.identityKey && ![recipient.identityKey isEqualToData:theirIdentityKey]) { + // this is a weird case, where we already have a stored ID key for a person, but don't have a session for that person. + #warning we want to give user option to continue or not this will crash in the DB when the key is being stored, as we aren't allowed to do this in DB currently. at least user knows why with this message + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Warning!" message:@"The contact's identity key has changed from one you've previously received. This could either mean that someon is trying to intercept your communication or that this contact simply re-isntall TextSecure and now has a new identity key " delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil]; + [alert show]; + } + + NSData* theirEphemeralKey = [NSData dataFromBase64String:[responseObject objectForKey:@"publicKey"]]; + NSNumber* theirPrekeyId = [responseObject objectForKey:@"keyId"]; + [recipient.deviceIDs addObject:[responseObject objectForKey:@"deviceId"]]; + recipient.identityKey = theirEphemeralKey; + [TSMessagesDatabase storeContact:recipient]; + if(message.group==nil) { + [[NSNotificationCenter defaultCenter] postNotificationName:recipient.registeredID object:self]; + } + else { + [[NSNotificationCenter defaultCenter] postNotificationName:[message.group.groupContext getEncodedId] object:self]; + } + + // Bootstrap session with Prekey + TSSession *session = [[TSSession alloc] initWithContact:recipient deviceId:[[responseObject objectForKey:@"deviceId"] intValue]]; + session.pendingPreKey = [[TSPrekey alloc] initWithIdentityKey:[theirIdentityKey removeVersionByte] ephemeral:[theirEphemeralKey removeVersionByte] prekeyId:[theirPrekeyId intValue]]; + session.needsInitialization = YES; + + [[TSMessagesManager sharedManager] submitMessage:message to:message.recipientId serializedMessage:[[[TSAxolotlRatchet encryptMessage:message withSession:session] getTextSecureProtocolData] base64EncodedString] ofType:TSPreKeyWhisperMessageType]; + } + // nil + break; + } + default: + DLog(@"error sending message"); + #warning Add error handling if not able to get contacts prekey + break; + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + #warning right now it is not succesfully processing returned response, but is giving 200 + NSLog(@"Error %@", error); + }]; + } + }); +} + +- (void)receiveMessagePush:(NSDictionary *)pushInfo{ + NSData *decryptedPayload = [Cryptography decryptAppleMessagePayload:[NSData dataFromBase64String:[pushInfo objectForKey:@"message"]] withSignalingKey:[TSKeyManager getSignalingKeyToken]]; + NSLog(@"push message bytes %lu",(unsigned long) decryptedPayload.length); + + TSMessageSignal *signal = [[TSMessageSignal alloc] initWithTextSecureProtocolData:decryptedPayload]; + + if (![TSMessagesDatabase contactForRegisteredID:signal.source]) { + [[[TSContact alloc] initWithRegisteredID:signal.source relay:nil] save]; + } + + TSSession *session = [TSMessagesDatabase sessionForRegisteredId:signal.source deviceId:[signal.sourceDevice intValue]]; + + TSMessage *decryptedMessage = [TSAxolotlRatchet decryptWhisperMessage:signal.message withSession:session]; + if(decryptedMessage.group!=nil) { + NSLog(@"incoming group id is %@",[decryptedMessage.group.groupContext getEncodedId]); + } + [TSMessagesDatabase storeMessage:decryptedMessage]; +} + +-(void) submitMessage:(TSMessageOutgoing*)message to:(NSString*)recipientId serializedMessage:(NSString*)serializedMessage ofType:(TSWhisperMessageType)messageType{ + NSData* data=[serializedMessage dataUsingEncoding:NSUTF8StringEncoding]; + NSLog(@"submitting message of %lu bytes",(unsigned long)data.length); + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSSubmitMessageRequest alloc] initWithRecipient:recipientId message:serializedMessage ofType:messageType] success:^(AFHTTPRequestOperation *operation, id responseObject) { + + switch (operation.response.statusCode) { + case 200:{ + // Awesome! We consider the message as sent! (Improvement: add flag in DB for sent) + NSLog(@"Message sent! %@", responseObject); + [message setState:TSMessageStateSent withCompletion:^(BOOL success) { + // Proceed to UI refresh; + }]; + + break; + } + + default: + DLog(@"error sending message"); +#warning Add error handling if not able to get contacts prekey + // Use last resort key + break; + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { +#warning right now it is not succesfully processing returned response, but is giving 200 + DLog(@"failure %ld, %@, %@",(long)operation.response.statusCode,operation.response.description,[[NSString alloc] initWithData:operation.responseData encoding:NSUTF8StringEncoding]); + [[NSNotificationCenter defaultCenter] postNotificationName:kDBNewMessageNotification object:nil userInfo:@{@"messageType":@"sent"}]; + + }]; + +} + + +@end diff --git a/TextSecureiOS/Model/TSAttachment.h b/TextSecureiOS/Model/TSAttachment.h new file mode 100644 index 0000000..c79afee --- /dev/null +++ b/TextSecureiOS/Model/TSAttachment.h @@ -0,0 +1,36 @@ +// +// TSAttachment.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/2/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "Constants.h" + +typedef enum { + TSAttachmentEmpty, + TSAttachmentPhoto, + TSAttachmentVideo +} TSAttachmentType; + +@interface TSAttachment : NSObject + +@property (nonatomic,strong) NSString* attachmentDataPath; +@property (nonatomic) TSAttachmentType attachmentType; +@property (nonatomic,strong) NSNumber* attachmentId; +@property (nonatomic,strong) NSData* attachmentDecryptionKey; +@property (nonatomic,strong) NSURL* attachmentURL; + +-(id) initWithAttachmentDataPath:(NSString*) dataPath withType:(TSAttachmentType)type withDecryptionKey:attachmentDecryptionKey; +-(id) initWithAttachmentId:(NSNumber*)attachmentId contentMIMEType:(NSString*)contentType decryptionKey:(NSData*)decryptionKey; +-(UIImage*) getThumbnailOfSize:(int)size; +-(NSString*) getMIMEContentType; +-(NSData*) getData; +-(UIImage*) getImage; +-(BOOL) readyForUpload; + +- (void)testUpload; + +@end diff --git a/TextSecureiOS/Model/TSAttachment.m b/TextSecureiOS/Model/TSAttachment.m new file mode 100644 index 0000000..3fd0989 --- /dev/null +++ b/TextSecureiOS/Model/TSAttachment.m @@ -0,0 +1,92 @@ +// +// TSAttachment.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/2/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSAttachment.h" +#import +#import +#import +#import +#import "Cryptography.h" +#import "FilePath.h" +@implementation TSAttachment + +-(id) initWithAttachmentDataPath:(NSString*) dataPath withType:(TSAttachmentType)type withDecryptionKey:(NSData*)decryptionKey { + if(self=[super init]) { + self.attachmentDataPath = dataPath; + self.attachmentType = type; + self.attachmentDecryptionKey = decryptionKey; + } + return self; +} + +-(id) initWithAttachmentId:(NSNumber*)attachmentId contentMIMEType:(NSString*)contentType decryptionKey:(NSData*)decryptionKey { + if(self=[super init]) { + self.attachmentId = attachmentId; + self.attachmentDecryptionKey = decryptionKey; +#warning download attachment here + // encryption attachment data, write to file, and initialize the attachment + /* + NSData *randomEncryptionKey; + NSData *encryptedData = [Cryptography encryptAttachment:UIImagePNGRepresentation([UIImage imageNamed:@"photo.png"]) withKey:decryptionKey + + NSString* filename = [[Cryptography truncatedHMAC:encryptedData withHMACKey:randomEncryptionKey truncation:10] base64EncodedStringWithOptions:0]; + + NSString* writeToFile = [FilePath pathInDocumentsDirectory:filename]; + [encryptedData writeToFile:writeToFile atomically:YES]; + */ + self.attachmentDecryptionKey = decryptionKey; + self.attachmentDataPath = @"TODOFILE"; + self.attachmentType = [contentType isEqualToString:@"video/mp4"] ? TSAttachmentVideo : TSAttachmentPhoto; + + } + return self; +} + +-(NSData*) decryptedAttachmentData:(BOOL)isThumbnail { + NSData *attachmentData = [NSData dataWithContentsOfFile:self.attachmentDataPath options:NSDataReadingUncached error:nil]; + NSData *decryptedData= [Cryptography decryptAttachment:attachmentData withKey:self.attachmentDecryptionKey]; + if (isThumbnail && self.attachmentType==TSAttachmentVideo) { + return UIImagePNGRepresentation([UIImage imageNamed:@"movie.png"]); + } + return decryptedData; +} + +-(UIImage*) getThumbnailOfSize:(int)size { + UIImage* thumbnailImage = [UIImage imageWithData:[self decryptedAttachmentData:YES]]; + return [thumbnailImage thumbnailImage:size transparentBorder:0 cornerRadius:3.0 interpolationQuality:0]; +} + +-(UIImage*) getImage { + return [UIImage imageWithData:[self decryptedAttachmentData:YES]]; +} + +-(NSData*) getData { + return [self decryptedAttachmentData:NO]; +} + +-(NSString*) getMIMEContentType { + switch (self.attachmentType) { + case TSAttachmentEmpty: + return @""; + break; + case TSAttachmentPhoto: + return @"image/png"; + break; + case TSAttachmentVideo: + return @"video/mp4"; + default: + return @""; + break; + } +} + +-(BOOL) readyForUpload { + return (self.attachmentType != TSAttachmentEmpty && self.attachmentId != nil && self.attachmentURL != nil); +} + +@end diff --git a/TextSecureiOS/Model/TSConversation.h b/TextSecureiOS/Model/TSConversation.h new file mode 100644 index 0000000..7c65eb0 --- /dev/null +++ b/TextSecureiOS/Model/TSConversation.h @@ -0,0 +1,38 @@ +// +// TSConversation.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSMessage.h" +#import "TSGroup.h" + +/** + * TSConversation is a datastructure used to display a conversation in the message inbox. + */ + +@interface TSConversation : NSObject + +#pragma mark TSConversation messages info + +@property (readonly) BOOL containsNotReadMessages; +@property (nonatomic, readonly) NSString *lastMessage; +@property (nonatomic, readonly) NSDate *lastMessageDate; + +#pragma mark TSConversation contact info + +- (BOOL)isGroupConversation; + +@property(nonatomic, readonly) TSContact *contact; +@property(nonatomic, readonly) TSGroup *group; + +#pragma mark TSConversation Constructors + +- (instancetype) initWithLastMessage:(NSString*)lastMessage contact:(TSContact*)contact lastDate:(NSDate*)date containsNonReadMessages:(BOOL)nonread; + +- (instancetype) initWithLastMessage:(NSString*)lastMessage group:(TSGroup*)group lastDate:(NSDate*)date containsNonReadMessages:(BOOL)nonread; + +@end diff --git a/TextSecureiOS/Model/TSConversation.m b/TextSecureiOS/Model/TSConversation.m new file mode 100644 index 0000000..c80df15 --- /dev/null +++ b/TextSecureiOS/Model/TSConversation.m @@ -0,0 +1,49 @@ +// +// TSConversation.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSConversation.h" + +@implementation TSConversation + +- (instancetype)initWithMessage:(NSString*)message unread:(BOOL)unread onDate:(NSDate*)date { + self = [super init]; + + if (self) { + _lastMessage = message; + _containsNotReadMessages = unread; + _lastMessageDate = date; + } + return self; +} + +- (instancetype) initWithLastMessage:(NSString*)lastMessage contact:(TSContact*)contact lastDate:(NSDate*)date containsNonReadMessages:(BOOL)nonread{ + self = [self initWithMessage:lastMessage unread:nonread onDate:date]; + if (self) { + _contact = contact; + } + + return self; +} + +- (instancetype) initWithLastMessage:(NSString*)lastMessage group:(TSGroup*)group lastDate:(NSDate*)date containsNonReadMessages:(BOOL)nonread{ + self = [self initWithMessage:lastMessage unread:nonread onDate:date]; + if (self) { + _group = group; + } + + return self; +} + +- (BOOL) isGroupConversation { + if (self.group) { + return YES; + } + return NO; +} + +@end diff --git a/TextSecureiOS/Model/TSDatabaseManager.h b/TextSecureiOS/Model/TSDatabaseManager.h new file mode 100644 index 0000000..7f9bea9 --- /dev/null +++ b/TextSecureiOS/Model/TSDatabaseManager.h @@ -0,0 +1,70 @@ +// +// TSDatabaseManager.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + + +@class FMDatabaseQueue; + + +@interface TSDatabaseManager : NSObject +// TODO: Use notifications to set dbQueue to nil when [TSStorageMasterKey lockStorageMasterKey] gets called +// This would close opened DB handles when we lock the key +@property (nonatomic, retain) FMDatabaseQueue *dbQueue; + + +/** + * Calls databaseCreateAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName withPassword:(NSData*)dbMasterKey error:(NSError **)error + * with the password: [TSStorageMasterKey getStorageMasterKeyWithError:error] + */ ++(instancetype) databaseCreateAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName error:(NSError **)error; + + +/** + * Create an encrypted database and update the corresponding preference. + * @author Alban Diquet + * + * @param dbFilePath The file path where the database should be created. + * @param preferenceName A BOOL preference that should be set to TRUE upon successful creation of the database. + * @param the password for the encrypted database. Must not be nil, otherwise return will be nil + * @param error. + * @return The newly-created database or nil if an error occured. + */ ++(instancetype) databaseCreateAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName withPassword:(NSData*)dbKey error:(NSError **)error; + + + +/** + * Calls +(instancetype) databaseOpenAndDecryptAtFilePath:(NSString *)dbFilePath withPassword:(NSData*)dbKey error:(NSError **)error; + * with the password [TSStorageMasterKey getStorageMasterKeyWithError:error] + */ ++(instancetype) databaseOpenAndDecryptAtFilePath:(NSString *)dbFilePath error:(NSError **)error; + + +/** + * Open and decrypt a database. + * @author Alban Diquet + * + * @param dbFilePath The file path to the database. + * @param the password for the encrypted database. Must not be nil, otherwise return will be nil + * @param error. + * @return The database or nil if an error occured. + */ ++(instancetype) databaseOpenAndDecryptAtFilePath:(NSString *)dbFilePath withPassword:(NSData*)dbKey error:(NSError **)error; + + +/** + * Erase an encrypted database and update the corresponding preference. + * @author Alban Diquet + * + * @param dbFilePath The file path to the database. + * @param preferenceName A BOOL preference that should be set to FALSE upon deletion of the database. + */ ++(void) databaseEraseAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName; + +@end diff --git a/TextSecureiOS/Model/TSDatabaseManager.m b/TextSecureiOS/Model/TSDatabaseManager.m new file mode 100644 index 0000000..0a4f704 --- /dev/null +++ b/TextSecureiOS/Model/TSDatabaseManager.m @@ -0,0 +1,147 @@ +// +// TSDatabaseManager.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSDatabaseManager.h" +#import "FMDatabaseQueue.h" +#import "FMDatabase.h" +#import "TSStorageError.h" +#import "TSStorageMasterKey.h" + + +@interface TSDatabaseManager(Private) + +-(instancetype) initWithDatabaseQueue:(FMDatabaseQueue *)queue; + +@end + +@implementation TSDatabaseManager + ++(instancetype) databaseCreateAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName withPassword:(NSData*)dbMasterKey error:(NSError **)error { + // Sanity check + if (!dbMasterKey) { + return nil; + } + // Have we created a DB on this device already ? + if ([[NSUserDefaults standardUserDefaults] boolForKey:preferenceName]) { + if (error) { + *error = [TSStorageError errorDatabaseAlreadyCreated]; + } + return nil; + } + + // Cleanup remnants of a previous DB + [TSDatabaseManager databaseEraseAtFilePath:dbFilePath updateBoolPreference:preferenceName]; + + + // Create the DB + __block BOOL dbInitSuccess = NO; + FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; + [dbQueue inDatabase:^(FMDatabase *db) { + if(![db setKeyWithData:dbMasterKey]) { + return; + } + + FMResultSet *rset = [db executeQuery:@"SELECT count(*) FROM sqlite_master"]; + if (rset) { + [rset close]; + dbInitSuccess = YES; + return; + } + }]; + + if (!dbInitSuccess) { + if (error) { + *error = [TSStorageError errorDatabaseCreationFailed]; + } + // Cleanup + [TSDatabaseManager databaseEraseAtFilePath:dbFilePath updateBoolPreference:preferenceName]; + return nil; + } + + TSDatabaseManager *encryptedDB = [[TSDatabaseManager alloc] initWithDatabaseQueue:dbQueue]; + + // Success - store in the preferences that the DB has been successfully created + [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:preferenceName]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + return encryptedDB; +} + + ++(instancetype) databaseCreateAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName error:(NSError **)error { + // Retrieve storage master key + NSData *dbMasterKey = [TSStorageMasterKey getStorageMasterKeyWithError:error]; + if (!dbMasterKey) { + return nil; + } + return [TSDatabaseManager databaseCreateAtFilePath:dbFilePath updateBoolPreference:preferenceName withPassword:dbMasterKey error:error]; + + +} + ++(instancetype) databaseOpenAndDecryptAtFilePath:(NSString *)dbFilePath error:(NSError **)error { + // Get the storage master key + NSData *storageKey = [TSStorageMasterKey getStorageMasterKeyWithError:error]; + if (!storageKey) { + return nil; + } + return [TSDatabaseManager databaseOpenAndDecryptAtFilePath:dbFilePath withPassword:storageKey error:error]; +} + + ++(instancetype) databaseOpenAndDecryptAtFilePath:(NSString *)dbFilePath withPassword:(NSData*)storageKey error:(NSError **)error { + if (!storageKey) { + return nil; + } + // Try to open the DB + __block BOOL initSuccess = NO; + FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath]; + + [dbQueue inDatabase:^(FMDatabase *db) { + if(![db setKeyWithData:storageKey]) { + // Supplied password was valid but the master key wasn't + return; + } + // Do a test query to make sure the DB is available + // if this throws an error, the key was incorrect. If it succeeds and returns a numeric value, the key is correct; + FMResultSet *rset = [db executeQuery:@"SELECT count(*) FROM sqlite_master"]; + if (rset) { + [rset close]; + initSuccess = YES; + return; + }}]; + + if (!initSuccess) { + if (error) { + *error = [TSStorageError errorStorageKeyCorrupted]; + } + return nil; + } + + TSDatabaseManager *encryptedDB = [[TSDatabaseManager alloc] initWithDatabaseQueue:dbQueue]; + return encryptedDB; +} + ++(void) databaseEraseAtFilePath:(NSString *)dbFilePath updateBoolPreference:(NSString *)preferenceName { + // Update the preferences + [[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:preferenceName]; + [[NSUserDefaults standardUserDefaults] synchronize]; + // Erase the DB file + [[NSFileManager defaultManager] removeItemAtPath:dbFilePath error:nil]; +} + + +-(instancetype) initWithDatabaseQueue:(FMDatabaseQueue *)queue { + if(self=[super init]) { + self.dbQueue = queue; + } + return self; +} + + +@end diff --git a/TextSecureiOS/Model/TSDerivedSecrets.h b/TextSecureiOS/Model/TSDerivedSecrets.h new file mode 100644 index 0000000..d1b3212 --- /dev/null +++ b/TextSecureiOS/Model/TSDerivedSecrets.h @@ -0,0 +1,20 @@ +// +// TSDerivedSecrets.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 29/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSDerivedSecrets : NSData + ++ (instancetype)derivedInitialSecretsWithMasterKey:(NSData*)masterKey; ++ (instancetype)derivedRatchetedSecretsWithSharedSecret:(NSData*)masterKey rootKey:(NSData*)rootKey; ++ (instancetype)derivedMessageKeysWithData:(NSData*)data; + +@property NSData *cipherKey; +@property NSData *macKey; + +@end diff --git a/TextSecureiOS/Model/TSDerivedSecrets.m b/TextSecureiOS/Model/TSDerivedSecrets.m new file mode 100644 index 0000000..8c54c0e --- /dev/null +++ b/TextSecureiOS/Model/TSDerivedSecrets.m @@ -0,0 +1,46 @@ +// +// TSDerivedSecrets.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 29/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSDerivedSecrets.h" +#import "HKDFKit.h" + + + +@implementation TSDerivedSecrets + ++ (instancetype)derivedSecretsWithSeed:(NSData*)masterKey salt:(NSData*)salt info:(NSData*)info{ + TSDerivedSecrets *secrets = [[TSDerivedSecrets alloc] init]; + + if (!salt) { + const char *HKDFDefaultSalt[4] = {0}; + salt = [NSData dataWithBytes:HKDFDefaultSalt length:sizeof(HKDFDefaultSalt)]; + } + + NSData *derivedMaterial = [HKDFKit deriveKey:masterKey info:info salt:salt outputSize:64]; + + secrets.cipherKey = [derivedMaterial subdataWithRange:NSMakeRange(0, 32)]; + secrets.macKey = [derivedMaterial subdataWithRange:NSMakeRange(32, 32)]; + return secrets; +} + ++ (instancetype)derivedInitialSecretsWithMasterKey:(NSData*)masterKey{ + NSData *info = [@"WhisperText" dataUsingEncoding:NSUTF8StringEncoding]; + return [self derivedSecretsWithSeed:masterKey salt:nil info:info]; +} + ++ (instancetype)derivedRatchetedSecretsWithSharedSecret:(NSData*)masterKey rootKey:(NSData*)rootKey{ + NSData *info = [@"WhisperRatchet" dataUsingEncoding:NSUTF8StringEncoding]; + return [self derivedSecretsWithSeed:masterKey salt:rootKey info:info]; +} + ++ (instancetype)derivedMessageKeysWithData:(NSData*)data{ + NSData *info = [@"WhisperMessageKeys" dataUsingEncoding:NSUTF8StringEncoding]; + return [self derivedSecretsWithSeed:data salt:nil info:info]; +} + +@end diff --git a/TextSecureiOS/Model/TSGroup.h b/TextSecureiOS/Model/TSGroup.h new file mode 100644 index 0000000..d5030e2 --- /dev/null +++ b/TextSecureiOS/Model/TSGroup.h @@ -0,0 +1,20 @@ +// +// TSGroup.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSGroupContext.h" + +@interface TSGroup : NSObject +@property(nonatomic,assign) BOOL isBroadcastGroup; +@property(nonatomic,strong) NSString *groupName; +@property(nonatomic,strong) UIImage *groupImage; +@property(nonatomic,strong) TSGroupContext *groupContext; + +-(instancetype) initWithGroupContext:(TSGroupContext*)context; +-(instancetype) groupContextForDelivery; +@end diff --git a/TextSecureiOS/Model/TSGroup.m b/TextSecureiOS/Model/TSGroup.m new file mode 100644 index 0000000..8ddf57c --- /dev/null +++ b/TextSecureiOS/Model/TSGroup.m @@ -0,0 +1,45 @@ +// +// TSGroup.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSGroup.h" + +@implementation TSGroup + +-(instancetype) initWithGroupContext:(TSGroupContext*)context { + if(self = [super init]) { + self.groupContext = [[TSGroupContext alloc] initWithId:context.gid withType:context.type withName:context.name withMembers:context.members withAvatar:context.avatar]; + self.groupName = self.groupContext.name; + self.groupImage = self.groupContext.image; + } + return self; +} + + + +-(instancetype) groupContextForDelivery { + TSGroup* group = [[TSGroup alloc] init]; + group.isBroadcastGroup = self.isBroadcastGroup; + group.groupContext=[[TSGroupContext alloc] initWithId:self.groupContext.gid withType:TSDeliverGroupContext withName:nil withMembers:nil withAvatar:nil]; + return group; +} + +- (id)copyWithZone:(NSZone *)zone { + id copy = [[[self class] alloc] init]; + + if (copy) { + [copy setIsBroadcastGroup:self.isBroadcastGroup]; + [copy setGroupName:[self.groupName copy]]; + [copy setGroupImage:[self.groupImage copy]]; + + TSGroupContext *groupContext = [[TSGroupContext alloc] initWithId:self.groupContext.gid withType:self.groupContext.type withName:self.groupContext.name withMembers:self.groupContext.members withAvatar:self.groupContext.avatar]; + [copy setGroupContext:groupContext]; + } + + return copy; +} +@end diff --git a/TextSecureiOS/Model/TSGroupContext.h b/TextSecureiOS/Model/TSGroupContext.h new file mode 100644 index 0000000..88758c3 --- /dev/null +++ b/TextSecureiOS/Model/TSGroupContext.h @@ -0,0 +1,27 @@ +// +// TSGroupContext.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/9/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSAttachment.h" + +@interface TSGroupContext : NSObject +@property(nonatomic,strong) UIImage *image; +@property(nonatomic,assign) TSGroupContextType type; +@property(nonatomic,strong) NSData* gid; +@property(nonatomic,strong) NSString* name; + +@property(nonatomic,strong) NSArray *members; +@property(nonatomic,strong) TSAttachment *avatar; + + +-(id)initWithId:(NSData*)groupId withType:(TSGroupContextType)groupType withName:(NSString*)groupName withMembers:(NSArray*)groupMembers withAvatar:(TSAttachment*)groupAvatar; +-(id)initWithId:(NSData*)groupId withName:(NSString*)groupName withAvatar:(TSAttachment*)avatar; +-(NSString*) getEncodedId; ++(NSData*) createNewGroupId; ++(NSData*) getDecodedId:(NSString*)encodedId; +@end diff --git a/TextSecureiOS/Model/TSGroupContext.m b/TextSecureiOS/Model/TSGroupContext.m new file mode 100644 index 0000000..c7ebc04 --- /dev/null +++ b/TextSecureiOS/Model/TSGroupContext.m @@ -0,0 +1,46 @@ +// +// TSGroupContext.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/9/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSGroupContext.h" +#import "Cryptography.h" +#import "Constants.h" +#import "NSData+Base64.h" +#import "NSString+Conversion.h" +@implementation TSGroupContext + +-(id)initWithId:(NSData*)groupId withName:(NSString*)groupName withAvatar:(TSAttachment*)avatar{ + return [self initWithId:groupId withType:TSUnknownGroupContext withName:groupName withMembers:[[NSMutableArray alloc] init] withAvatar:avatar]; +} + +-(id)initWithId:(NSData*)groupId withType:(TSGroupContextType)groupType withName:(NSString*)groupName withMembers:(NSArray*)groupMembers withAvatar:(TSAttachment*)groupAvatar { + if(self=[super init]) { + self.gid = groupId; + self.type = groupType; + self.name = groupName; + self.members = groupMembers; + self.avatar = groupAvatar; + } + return self; +} + + +-(NSString*) getEncodedId { + return [self.gid base64EncodedString]; +} + ++(NSData*) createNewGroupId { + NSData* randomId = [Cryptography generateRandomBytes:32]; + return randomId; +} + ++(NSData*) getDecodedId:(NSString*)encodedId { + return [NSData dataFromBase64String:encodedId]; +} + + +@end diff --git a/TextSecureiOS/Model/TSMessage.h b/TextSecureiOS/Model/TSMessage.h new file mode 100644 index 0000000..e76bea3 --- /dev/null +++ b/TextSecureiOS/Model/TSMessage.h @@ -0,0 +1,47 @@ +// +// TSMessage.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + + +#import +@class TSContact; +@class TSGroup; + +typedef void (^TSMessageChangeState)(BOOL success); + +@interface TSMessage : NSObject +{ + NSString *_messageId; + NSString *_senderId; + NSString *_recipientId; + NSDate *_timestamp; + NSString *_content; + NSArray *_attachments; + TSGroup *_group; + int _state; + TSGroupContextType _metaMessage; + BOOL _isBroadcast; +} + +@property (nonatomic, readonly) NSString *senderId; +@property (nonatomic, readonly) NSString *recipientId; +@property (nonatomic, readonly) NSDate *timestamp; +@property (nonatomic, readonly) NSString *content; +@property (nonatomic, readonly) NSArray *attachments; +@property (nonatomic, readonly) TSGroup *group; +@property (nonatomic, readonly) int state; +@property (nonatomic, readonly) TSGroupContextType metaMessage; +@property (nonatomic, readonly) NSString *messageId; +@property (nonatomic,readonly) BOOL isBroadcast; + + +-(BOOL) isUnread; + +- (instancetype)initWithSenderId:(NSString*)senderId recipientId:(NSString*)recipientId date:(NSDate*)date content:(NSString*)content attachements:(NSArray*)attachements groupId:(TSGroup*)group; +- (instancetype)initWithSenderId:(NSString*)senderId recipientId:(NSString*)recipientId date:(NSDate*)date content:(NSString*)content attachements:(NSArray*)attachements groupId:(TSGroup*)group messageId:(NSString*)messageid; + +@end diff --git a/TextSecureiOS/Model/TSMessage.m b/TextSecureiOS/Model/TSMessage.m new file mode 100644 index 0000000..25e67f5 --- /dev/null +++ b/TextSecureiOS/Model/TSMessage.m @@ -0,0 +1,48 @@ +// +// TSMessage.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSMessage.h" +#import "NSString+randomString.h" +#import "TSGroup.h" +@implementation TSMessage + +- (instancetype)initWithSenderId:(NSString*)senderId recipientId:(NSString*)recipientId date:(NSDate*)date content:(NSString*)content attachements:(NSArray*)attachements groupId:(TSGroup*)group{ + self = [super init]; + + if (self) { + _messageId = [NSString genRandStringLength:20]; + _senderId = senderId; + _recipientId = recipientId; + _timestamp = date; + _content = content; + _attachments = attachements; + _group = group; + if(group!=nil) { + _metaMessage = group.groupContext.type; + } + } + + return self; +} + +- (instancetype)initWithSenderId:(NSString*)senderId recipientId:(NSString*)recipientId date:(NSDate*)date content:(NSString*)content attachements:(NSArray*)attachements groupId:(TSGroup*)group messageId:(NSString*)messageid{ + self = [self initWithSenderId:senderId recipientId:recipientId date:date content:content attachements:attachements groupId:group]; + + if (self) { + _messageId = messageid; + } + + return self; +} + +- (BOOL)isUnread{ + NSLog(@"TSMessage can't be instantiated!"); + exit(1); +} + +@end diff --git a/TextSecureiOS/Model/TSMessageIncoming.h b/TextSecureiOS/Model/TSMessageIncoming.h new file mode 100644 index 0000000..33323fd --- /dev/null +++ b/TextSecureiOS/Model/TSMessageIncoming.h @@ -0,0 +1,58 @@ +// +// TSMessageIncoming.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessage.h" + +@interface TSMessageIncoming : TSMessage + +typedef enum { + TSMessageStateReceived, + TSMessageStateRead +} TSMessageIncomingState; + +/** + * This method is used to initialize a new message when it's created, thus when it's received. + * + * @param text Body of the message. + * @param senderid Registered id of the sender - nil if sent to group. + * @param timestamp Timestamp from when the message was sent. + * @param attachements Attachements array containing TSAttachements. + * @param group Group to whom the message is sent - nil if sent to individual. + * @param state TSMessageIncomingState of the current state. + * + * @return An initialized TSMessageIncoming + */ + +- (instancetype)initMessageWithContent:(NSString *)text sender:(NSString *)sender date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageIncomingState)state; + +/** + * Method to recover a TSMessageIncoming from the database + * + * @param text Body of the message. + * @param senderid Registered id of the sender - nil if sent to group. + * @param timestamp Timestamp from when the message was sent. + * @param attachements Attachements array containing TSAttachements. + * @param group Group to whom the message is sent - nil if sent to individual. + * @param state TSMessageIncomingState of the current state. + * @param messageId Message Id of the current message + * + * @return An initialized TSMessageIncoming + */ + +- (instancetype)initMessageWithContent:(NSString *)text sender:(NSString *)sender date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageIncomingState)state messageId:(NSString*)messageId; + +/** + * This method mutates the state. + * + * @param state New state + * @param block Completion block to pass - likely a UI update. + */ + +- (void)setState:(TSMessageIncomingState)state withCompletion:(TSMessageChangeState)block; + +@end \ No newline at end of file diff --git a/TextSecureiOS/Model/TSMessageIncoming.m b/TextSecureiOS/Model/TSMessageIncoming.m new file mode 100644 index 0000000..fc5c02f --- /dev/null +++ b/TextSecureiOS/Model/TSMessageIncoming.m @@ -0,0 +1,53 @@ +// +// TSMessageIncoming.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageIncoming.h" +#import "TSMessagesDatabase.h" + +@interface TSMessageIncoming () +@end + +@implementation TSMessageIncoming + +-(instancetype) initMessageWithContent:(NSString *)text sender:(NSString *)sender date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageIncomingState)state{ + self = [super initWithSenderId:sender recipientId:[TSKeyManager getUsernameToken] date:timestamp content:text attachements:attachements groupId:group]; + + if (self) { + _state = state; + } + return self; +} + +-(instancetype) initMessageWithContent:(NSString *)text sender:(NSString *)sender date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageIncomingState)state messageId:(NSString*)messageId{ + self = [self initMessageWithContent:text sender:sender date:timestamp attachements:attachements group:group state:state]; + if (self) { + _messageId = messageId; + } + return self; +} + +- (void)setState:(TSMessageIncomingState)state withCompletion:(TSMessageChangeState)block{ + BOOL didSucceed = YES; + if(self.group==nil) { + + didSucceed = [TSMessagesDatabase storeMessage:self]; + _state = state; + } + block(didSucceed); +} + +-(BOOL) isUnread{ + if (self.state == TSMessageStateReceived) { + return YES; + } else{ + return NO; + } +} + + +@end diff --git a/TextSecureiOS/Model/TSMessageKeys.h b/TextSecureiOS/Model/TSMessageKeys.h new file mode 100644 index 0000000..98584e0 --- /dev/null +++ b/TextSecureiOS/Model/TSMessageKeys.h @@ -0,0 +1,19 @@ +// +// TSMessageKeys.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 09/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSMessageKeys : NSObject + +- (instancetype)initWithCipherKey:(NSData*)cipherKey macKey:(NSData*)macKey counter:(int)counter; + +@property (readonly)NSData *cipherKey; +@property (readonly)NSData *macKey; +@property (readonly)int counter; + +@end diff --git a/TextSecureiOS/Model/TSMessageKeys.m b/TextSecureiOS/Model/TSMessageKeys.m new file mode 100644 index 0000000..f25287a --- /dev/null +++ b/TextSecureiOS/Model/TSMessageKeys.m @@ -0,0 +1,27 @@ +// +// TSMessageKeys.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 09/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageKeys.h" + +@implementation TSMessageKeys + +- (instancetype)initWithCipherKey:(NSData*)cipherKey macKey:(NSData*)macKey counter:(int)counter{ + self = [super init]; + if (self) { + _cipherKey = cipherKey; + _macKey = macKey; + _counter = counter; + } + + return self; +} +-(NSString*) debugDescription { + return [NSString stringWithFormat:@"cipherKey: %@\n macKey %@\n",self.cipherKey,self.macKey]; +} + +@end diff --git a/TextSecureiOS/Model/TSMessageOutgoing.h b/TextSecureiOS/Model/TSMessageOutgoing.h new file mode 100644 index 0000000..a4938eb --- /dev/null +++ b/TextSecureiOS/Model/TSMessageOutgoing.h @@ -0,0 +1,99 @@ +// +// TSMessageOutgoing.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessage.h" + +typedef enum { + TSMessageStateDraft, + TSMessageStatePendingSend, + TSMessageStateSent +} TSMessageOutgoingState; + +@interface TSMessageOutgoing : TSMessage + +@property (readonly) TSMessageOutgoingState messageState; +/** + * This method is used to initialize a new message when it's created, thus when it's sent. + * + * @param text Body of the message. + * @param recipientId Registered id of the recipient - nil if sent to group. + * @param timestamp Date at which it is created, updated when sent. + * @param attachements Attachements array containing TSAttachements. + * @param group Group to whom the message is sent - nil if sent to individual. + * @param state TSMessageOutgoingState of the current state. + * + * @return An initialized TSMessageOutgoing + */ + +- (instancetype)initMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state; + + +/** + * This method is used to initialize a new message when it's created, thus when it's sent. + * + * @param text Body of the message. + * @param recipientId Registered id of the recipient - nil if sent to group. + * @param timestamp Date at which it is created, updated when sent. + * @param attachements Attachements array containing TSAttachements. + * @param group Group to whom the message is sent - nil if sent to individual. + * @param state TSMessageOutgoingState of the current state. + * + * @return An initialized TSMessageOutgoing + */ + +- (instancetype)initBroadcastMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state; + + +/** + * This method is similar to initMessageWithContent:recipient:date:attachements:group:state: but contains the messageId, this method is used to retreive a message from the database with an existing messageId. + * + * @param text Body of the message. + * @param recipientId Registered id of the recipient - nil if sent to group. + * @param timestamp Date at which it is created, updated when sent. + * @param attachements Attachements array containing TSAttachements. + * @param group Group to whom the message is sent - nil if sent to individual. + * @param state TSMessageOutgoingState of the current state. + * @param messageId Message Id of the current message + * + * @return An initialized TSMessageOutgoing + */ + +- (instancetype)initMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state messageId:(NSString*)messageId; +/** + * This method is similar to initMessageWithContent:recipient:date:attachements:group:state: but contains the messageId, this method is used to retreive a message from the database with an existing messageId. + * + * @param text Body of the message. + * @param recipientId Registered id of the recipient - nil if sent to group. + * @param timestamp Date at which it is created, updated when sent. + * @param attachements Attachements array containing TSAttachements. + * @param group Group to whom the message is sent - nil if sent to individual. + * @param state TSMessageOutgoingState of the current state. + * @param messageId Message Id of the current message + * + * @return An initialized TSMessageOutgoing + */ + +- (instancetype)initBroadcastMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state messageId:(NSString*)messageId; + + + +- (instancetype)copyMessageToRecipient:(NSString *)newRecipientId; + + +/** + * This method mutates the state. + * + * @param state New state + * @param block Completion block to pass - likely a UI update. + */ + +- (void)setState:(TSMessageOutgoingState)state withCompletion:(TSMessageChangeState)block; + +-(BOOL) shouldSend; + +@end diff --git a/TextSecureiOS/Model/TSMessageOutgoing.m b/TextSecureiOS/Model/TSMessageOutgoing.m new file mode 100644 index 0000000..ed74167 --- /dev/null +++ b/TextSecureiOS/Model/TSMessageOutgoing.m @@ -0,0 +1,93 @@ + // +// TSMessageOutgoing.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageOutgoing.h" +#import "TSMessagesDatabase.h" +#import "TSGroup.h" +@interface TSMessageOutgoing () +@end + +@implementation TSMessageOutgoing + + + + +-(BOOL) shouldSend { + return !self.group || !self.isBroadcast || self.group.groupContext.type == TSDeliverGroupContext; +} + +- (instancetype)initMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state { + + self = [super initWithSenderId:[TSKeyManager getUsernameToken] recipientId:recipientId date:timestamp content:text attachements:attachements groupId:group]; + + if (self) { + _state = state; + } + + return self; +} + +- (instancetype)initMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state messageId:(NSString*)messageId{ + self = [self initMessageWithContent:text recipient:recipientId date:timestamp attachements:attachements group:group state:state]; + + if (self) { + _messageId = messageId; + } + return self; +} + + +- (instancetype)initBroadcastMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state { + self = [self initMessageWithContent:text recipient:recipientId date:timestamp attachements:attachements group:group state:state]; + + if (self) { + _isBroadcast = YES; + } + return self; +} + +- (instancetype)initBroadcastMessageWithContent:(NSString *)text recipient:(NSString *)recipientId date:(NSDate*)timestamp attachements:(NSArray*)attachements group:(TSGroup*)group state:(TSMessageOutgoingState)state messageId:(NSString*)messageId { + self = [self initMessageWithContent:text recipient:recipientId date:timestamp attachements:attachements group:group state:state messageId:messageId]; + + if (self) { + _isBroadcast = YES; + } + return self; +} + + + +- (instancetype)copyMessageToRecipient:(NSString *)newRecipientId { + TSGroup *groupForMessage = nil; + if(self.isBroadcast) { + return [[TSMessageOutgoing alloc] initBroadcastMessageWithContent:self.content recipient:newRecipientId date:self.timestamp attachements:self.attachments group:groupForMessage state:self.messageState messageId:self.messageId];; + } + else if(self.group!= nil && self.group.groupContext.type == TSDeliverGroupContext) { + groupForMessage = [self.group groupContextForDelivery]; + } + else { + groupForMessage = [self.group copy]; + } + return [[TSMessageOutgoing alloc] initMessageWithContent:self.content recipient:newRecipientId date:self.timestamp attachements:self.attachments group:groupForMessage state:self.messageState messageId:self.messageId]; + +} + +- (void)setState:(TSMessageOutgoingState)state withCompletion:(TSMessageChangeState)block{ + BOOL didSucceed = YES; + if(self.group==nil && !self.isBroadcast) { + didSucceed = [TSMessagesDatabase storeMessage:self]; + _state = state; + } + block(didSucceed); +} + +- (BOOL)isUnread{ + return false; +} + +@end \ No newline at end of file diff --git a/TextSecureiOS/Model/TSMessagesDatabase.h b/TextSecureiOS/Model/TSMessagesDatabase.h new file mode 100755 index 0000000..a22c737 --- /dev/null +++ b/TextSecureiOS/Model/TSMessagesDatabase.h @@ -0,0 +1,71 @@ +// +// TSMessagesDatabase.h +// TextSecureiOS +// +// Created by Alban Diquet on 11/25/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +@class TSSession; +@class TSMessage; +@class TSContact; +@class TSConversation; +@class TSGroup; + +typedef void(^dataBaseFetchCompletionBlock)(NSArray* array); +typedef void(^dataBaseUpdateCompletionBlock)(BOOL success); // For retreival of arrays + +/** + * The TSMessagesDatabase contains everything used for messaging + * It contains 6 tables - contacts, sessions, messages, groups, attachements, settings + */ + +@interface TSMessagesDatabase : NSObject + ++ (BOOL)databaseCreateWithError:(NSError **)error; ++ (void)databaseErase; ++ (BOOL)databaseWasCreated; + +// Calling the following functions will fail if the storage master key hasn't been unlocked + +#pragma mark Settings ++ (BOOL)storePersistentSettings:(NSDictionary*)settingNamesAndValues; + +#pragma mark Contacts + ++ (TSContact*)contactForRegisteredID:(NSString*)registredID; ++ (BOOL)storeContact:(TSContact*)contact; + +#pragma mark Conversations + ++ (NSArray*)conversations; ++ (void)deleteMessagesForConversation:(TSConversation*)conversation completion:(dataBaseUpdateCompletionBlock) block; + +// Method for debug-only ++ (void)deleteMessagesAndSessionsForConversation:(TSConversation*)conversation completion:(dataBaseUpdateCompletionBlock) block; + +#pragma mark Sessions + +/** + * A session contains all information required by the Axolotl ratchet. It is unique to a registeredID and a deviceID. Because in the future, TS users will be able to add many devices to a single identity key/registered ID, we have to make sure that we can support multiple sessions with a TSContact. + */ ++ (BOOL)sessionExistsForContact:(TSContact*)contact; ++ (BOOL)deleteSession:(TSSession*)session; ++ (BOOL)deleteSessions:(TSContact*)contact; ++ (BOOL)storeSession:(TSSession*)session; ++ (NSArray*)sessionsForContact:(TSContact*)contact; ++ (TSSession*)sessionForRegisteredId:(NSString*)registeredId deviceId:(int)deviceId; +#pragma mark Groups ++ (NSArray*)membersForGroup:(TSGroup *)group; + +#pragma mark Messages + ++ (NSArray*)messagesWithContact:(TSContact*)contact; ++ (NSArray*)messagesForGroup:(TSGroup*)group; ++ (BOOL)storeMessage:(TSMessage*)msg; ++ (BOOL)deleteMessage:(TSMessage*)msg; + +@end + diff --git a/TextSecureiOS/Model/TSMessagesDatabase.m b/TextSecureiOS/Model/TSMessagesDatabase.m new file mode 100644 index 0000000..523c90a --- /dev/null +++ b/TextSecureiOS/Model/TSMessagesDatabase.m @@ -0,0 +1,696 @@ +// +// TSMessagesDatabase.m +// TextSecureiOS +// +// Created by Alban Diquet on 11/25/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSMessagesDatabase.h" +#import "TSStorageError.h" +#import +#import + +#import "FilePath.h" +#import "TSMessage.h" +#import "TSContact.h" +#import "TSAttachment.h" +#import "TSStorageMasterKey.h" +#import "TSDatabaseManager.h" +#import "TSKeyManager.h" +#import "TSGroup.h" +#import "TSMessageIncoming.h" +#import "TSMessageOutgoing.h" +#import "TSConversation.h" +#import "TSSession.h" +#import "TSChainKey.h" +#import "Cryptography.h" +#define kDBWasCreatedBool @"TSMessagesWasCreated" +#define databaseFileName @"TSMessages.db" + +#define openDBMacroNothing if (!messagesDb){[TSMessagesDatabase databaseOpenWithError:nil];} +#define openDBMacroBOOL if (!messagesDb){if (![TSMessagesDatabase databaseOpenWithError:nil]) {return NO;}} +#define openDBMacroNil if (!messagesDb){if (![TSMessagesDatabase databaseOpenWithError:nil]) {return nil;}} + +// Reference to the singleton +static TSDatabaseManager *messagesDb = nil; + +@interface TSMessagesDatabase(Private) + ++(BOOL) databaseOpenWithError:(NSError **)error; + +@end + +@implementation TSMessagesDatabase + +#pragma mark DB creation + ++ (NSString*)pathToDatabase{ + return [FilePath pathInDocumentsDirectory:databaseFileName]; +} + ++ (BOOL)databaseWasCreated { + return [[NSFileManager defaultManager] fileExistsAtPath:[self pathToDatabase]]; +} + ++ (BOOL)databaseCreateWithError:(NSError **)error { + + // Create the database + TSDatabaseManager *db = [TSDatabaseManager databaseCreateAtFilePath:[self pathToDatabase] updateBoolPreference:kDBWasCreatedBool error:error]; + if (!db) { + return NO; + } + + // Create the tables we need + __block BOOL dbInitSuccess = NO; + [db.dbQueue inDatabase:^(FMDatabase *db) { + + if (![db executeUpdate:@"CREATE TABLE settings (setting_name TEXT UNIQUE,setting_value TEXT)"]) { + return; + } + + if (![db executeUpdate:@"CREATE TABLE IF NOT EXISTS contacts (registered_id TEXT PRIMARY KEY, relay TEXT, identity_key BLOB UNIQUE, device_ids BLOB, verified_identity INTEGER)"]) { + return; + } + + if (![db executeUpdate:@"CREATE TABLE IF NOT EXISTS groups (group_id TEXT PRIMARY KEY, name TEXT, avatar_path TEXT, avatar_key BLOB, avatar_type INT, broadcast INT)"]) { + return; + } + + if (![db executeUpdate:@"CREATE TABLE IF NOT EXISTS group_membership (group_id TEXT, group_member TEXT,FOREIGN KEY(group_id) REFERENCES groups(group_id),FOREIGN KEY(group_member) REFERENCES contacts(registered_id))"]) { + return; + } + + + if (![db executeUpdate:@"CREATE TABLE IF NOT EXISTS messages (message_id TEXT PRIMARY KEY,message TEXT, timestamp DATE, attachements BLOB, state INTEGER, sender_id TEXT, recipient_id TEXT, group_id TEXT, meta_message INTEGER, broadcast INT, FOREIGN KEY(sender_id) REFERENCES contacts (registered_id), FOREIGN KEY(recipient_id) REFERENCES contacts(registered_id), FOREIGN KEY(group_id) REFERENCES groups (group_id))"]) { + return; + } + + if (![db executeUpdate:@"CREATE TABLE IF NOT EXISTS personal_prekeys (prekey_id INTEGER PRIMARY KEY,public_key TEXT,private_key TEXT, last_counter INTEGER)"]){ + return; + } + + if (![db executeUpdate:@"CREATE TABLE IF NOT EXISTS sessions (registered_id TEXT, device_id TEXT, serialized_session BLOB, FOREIGN KEY(registered_id) REFERENCES contacts (registered_id), PRIMARY KEY(registered_id, device_id))" ]) { + return; + } + + dbInitSuccess = YES; + + }]; + + if (!dbInitSuccess) { + if (error) { + *error = [TSStorageError errorDatabaseCreationFailed]; + } + // Cleanup + [TSMessagesDatabase databaseErase]; + return NO; + } + + messagesDb = db; + return YES; +} + + ++ (void)databaseErase { + [TSDatabaseManager databaseEraseAtFilePath:[self pathToDatabase] updateBoolPreference:kDBWasCreatedBool]; +} + ++ (BOOL)databaseOpenWithError:(NSError **)error { + + // DB was already unlocked + if (messagesDb){ + return YES; + } + + if (![TSMessagesDatabase databaseWasCreated]) { + if (error) { + *error = [TSStorageError errorDatabaseNotCreated]; + } + return NO; + } + + messagesDb = [TSDatabaseManager databaseOpenAndDecryptAtFilePath:[self pathToDatabase] error:error]; + if (!messagesDb) { + return NO; + } + return YES; +} + +#pragma mark Settings table + ++(BOOL) storePersistentSettings:(NSDictionary*)settingNamesAndValues { + openDBMacroBOOL; + // Decrypt the DB if it hasn't been done yet + if (!messagesDb) { + if (![TSMessagesDatabase databaseOpenWithError:nil]) { + // TODO: better error handling + return NO; + } + } + + __block BOOL updateSuccess = YES; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + if (![db executeUpdate:@"INSERT OR REPLACE INTO settings (setting_name,setting_value) VALUES (:setting_name, :setting_value)", settingNamesAndValues]) { + DLog(@"Error updating DB: %@", [db lastErrorMessage]); + updateSuccess = NO; + } + }]; + + return updateSuccess; +} + ++ (NSString*)settingForKey:(NSString*)key { + openDBMacroNil + + __block NSString *settingValue = nil; + + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *searchInDB = [db executeQuery:@"SELECT setting_value FROM settings WHERE setting_name=?" withArgumentsInArray:@[key]]; + + if ([searchInDB next]) { + settingValue = [searchInDB stringForColumn:key]; + } + [searchInDB close]; + }]; + + return settingValue; +} + + +#pragma mark Contacts table + ++ (BOOL)storeContact:(TSContact*)contact { + openDBMacroBOOL + + __block BOOL updateSuccess = YES; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + NSMutableDictionary *parameterDict = [@{@"registered_id": contact.registeredID, @"verified_identity": [NSNumber numberWithBool:contact.identityKeyIsVerified]} mutableCopy]; + + [parameterDict setObject:contact.relay?:[NSNull null] forKey:@"relay"]; + [parameterDict setObject:contact.identityKey?:[NSNull null] forKey:@"identity_key"]; + [parameterDict setObject:contact.deviceIDs?:[NSNull null] forKey:@"device_ids"]; + + if (![db executeUpdate:@"INSERT OR REPLACE INTO contacts VALUES (:registered_id, :relay, :identity_key, :device_ids, :verified_identity)" withParameterDictionary:parameterDict]){ + DLog(@"Error updating DB: %@", [db lastErrorMessage]); + updateSuccess = NO; + } + }]; + + return updateSuccess; +} + ++ (BOOL)storeContacts:(NSArray*)contacts { + BOOL success = YES; + for (TSContact *contact in contacts) { + success = ([self storeContact:contact])?:NO; + } + return success; +} + ++ (TSContact*)contactForRegisteredID:(NSString*)registredID { + openDBMacroNil + + __block TSContact *contact = nil; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *searchInDB = [db executeQuery:@"SELECT * FROM contacts WHERE registered_id=?" withArgumentsInArray:@[registredID]]; + + if ([searchInDB next]) { + contact = [[TSContact alloc] initWithRegisteredID:[searchInDB stringForColumn:@"registered_id"] relay:[searchInDB stringForColumn:@"relay"]]; + contact.identityKey = [searchInDB dataForColumn:@"identity_key"]; + NSData *deviceIds = [searchInDB dataForColumn:@"device_ids"]; + if (deviceIds) { + contact.deviceIDs = [NSKeyedUnarchiver unarchiveObjectWithData:deviceIds]; + } + contact.identityKeyIsVerified = [searchInDB boolForColumn:@"verified_identity"]; + } + [searchInDB close]; + }]; + + if (!contact) { + contact = [[TSContact alloc] initWithRegisteredID:registredID relay:nil]; + } + + return contact; +} + ++ (NSArray*)contacts{ + openDBMacroNil + + NSMutableArray *contacts = [NSMutableArray array]; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *searchInDB = [db executeQuery:@"SELECT * FROM contacts where registered_id!=?" withArgumentsInArray:@[[TSKeyManager getUsernameToken]]]; + + while ([searchInDB next]) { + + TSContact *contact = [[TSContact alloc] initWithRegisteredID:[searchInDB stringForColumn:@"registered_id"] relay:[searchInDB stringForColumn:@"relay"]]; + contact.identityKey = [searchInDB dataForColumn:@"identity_key"]; + NSData *deviceIds = [searchInDB dataForColumn:@"device_ids"]; + if (deviceIds) { + contact.deviceIDs = [NSKeyedUnarchiver unarchiveObjectWithData:deviceIds]; + } + contact.identityKeyIsVerified = [searchInDB boolForColumn:@"verified_identity"]; + [contacts addObject:contact]; + } + [searchInDB close]; + }]; + + return [contacts copy]; +} + +#pragma mark Sessions table + +#pragma mark Messages table + ++ (BOOL)storeMessage:(TSMessage*)msg { + openDBMacroBOOL + + __block TSMessage *message = msg; + __block BOOL success = NO; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + // separate storing pipeline for group and non-group messages + TSGroupContext *context = message.group.groupContext; + if(message.group!=nil && context.type != TSDeliverGroupContext) { + if(context.type == TSUpdateGroupContext) { + //CREATE TABLE IF NOT EXISTS groups (group_id TEXT PRIMARY KEY, name TEXT, avatar_path TEXT, avatar_key BLOB, avatar_type INT) + success = [db executeUpdate:@"INSERT OR REPLACE INTO groups (group_id,name,avatar_path,avatar_key,avatar_type,broadcast) VALUES (?, ?, ?, ?, ?, ?)" withArgumentsInArray:@[[context getEncodedId],context.name,[NSNull null],[NSNull null], [NSNull null],[NSNumber numberWithBool:message.group.isBroadcastGroup]]]; + NSString *metaMessage = @""; + for(TSContact* groupMember in context.members){ + // update the contact info in the group array + success = [db executeUpdate:@"INSERT OR IGNORE INTO contacts (registered_id) VALUES (?)" withArgumentsInArray:@[groupMember.registeredID]]; + success = [db executeUpdate:@"INSERT OR REPLACE INTO group_membership (group_id,group_member) VALUES (?, ?)" withArgumentsInArray:@[[context getEncodedId],groupMember.registeredID]]; + metaMessage = [metaMessage stringByAppendingString:[NSString stringWithFormat:@"%@ ",groupMember.registeredID]]; + } + metaMessage = [metaMessage stringByAppendingString:@"joined the group."]; + if(context.name) { + metaMessage = [metaMessage stringByAppendingString:[NSString stringWithFormat:@" Title is now %@.",context.name]]; + } + success = [db executeUpdate:@"INSERT OR REPLACE INTO messages (sender_id, group_id, message, timestamp, state, message_id, meta_message, broadcast) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" withArgumentsInArray:@[message.senderId, [context getEncodedId],metaMessage, message.timestamp, [NSNumber numberWithInt:message.state], message.messageId, [NSNumber numberWithInt:context.type],[NSNumber numberWithInt:message.isBroadcast]]]; + + } + else if(message.group.groupContext.type == TSQuitGroupContext) { + success = [db executeUpdate:@"DELETE from group_membership WHERE group_id=? AND group_member=? " withArgumentsInArray:@[[context getEncodedId],message.senderId]]; + success = [db executeUpdate:@"INSERT OR REPLACE INTO messages (sender_id, group_id, message, timestamp, state, message_id, meta_message, broadcast) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" withArgumentsInArray:@[message.senderId, [context getEncodedId], @"member left", message.timestamp, [NSNumber numberWithInt:message.state], message.messageId, [NSNumber numberWithInt:context.type],[NSNumber numberWithInt:message.isBroadcast]]]; + + } + else { + @throw [NSException exceptionWithName:@"group context type" reason:@"type unknown" userInfo:nil]; + } + + } + else { + id groupId = message.group ? [context getEncodedId]: [NSNull null]; + id receipientId = message.group ? [NSNull null] : message.recipientId; + success = [db executeUpdate:@"INSERT OR REPLACE INTO messages (sender_id, recipient_id, group_id, message, timestamp, attachements, state, message_id, broadcast) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" withArgumentsInArray:@[message.senderId, receipientId, groupId, message.content, message.timestamp, [NSKeyedArchiver archivedDataWithRootObject:message.attachments], [NSNumber numberWithInt:message.state], message.messageId,[NSNumber numberWithInt:message.isBroadcast]]]; + } + + }]; + + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:kDBNewMessageNotification object:nil]]; + + return success; +} + ++ (TSMessage*)messageForDBElement:(FMResultSet*)messages inGroup:(TSGroup*)group{ + //To determine if it's an incoming or outgoing message, we look if there is a sender_id + NSString *senderID = [messages stringForColumn:@"sender_id"]; + NSString *receiverID = [messages stringForColumn:@"recipient_id"]; + + NSDate *date = [messages dateForColumn:@"timestamp"]; + NSString *content = [messages stringForColumn:@"message"]; + NSArray *attachements = [NSKeyedUnarchiver unarchiveObjectWithData:[messages dataForColumn:@"attachements"]]; + //NSString *groupID = [messages stringForColumn:@"group_id"]; + int state = [messages intForColumn:@"state"]; + NSString *messageId = [messages stringForColumn:@"message_id"]; + if(group!=nil) { + TSGroupContextType meta_message = [messages intForColumn:@"meta_message"]; + + group.groupContext.type = meta_message; + } + + if (senderID) { + TSMessageIncoming *incoming = [[TSMessageIncoming alloc] initMessageWithContent:content sender:senderID date:date attachements:attachements group:group state:state messageId:messageId]; + return incoming; + } + else{ + if(group!=nil &&[messages intForColumn:@"broadcast"]) { + return [[TSMessageOutgoing alloc] initBroadcastMessageWithContent:content recipient:receiverID date:date attachements:attachements group:group state:state messageId:messageId]; + } + else { + return [[TSMessageOutgoing alloc] initMessageWithContent:content recipient:receiverID date:date attachements:attachements group:group state:state messageId:messageId];; + + } + } +} + + + ++ (NSArray*)messagesWithContact:(TSContact*)contact numberOfPosts:(int)numberOfPosts{ + // -1 returns everything + openDBMacroNil + + __block int nPosts = numberOfPosts; + + __block NSMutableArray *messagesArray = [NSMutableArray array]; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *messages= [db executeQuery:@"SELECT * FROM messages WHERE (sender_id=? OR recipient_id=?) AND group_id is NULL ORDER BY timestamp ASC" withArgumentsInArray:@[contact.registeredID, contact.registeredID]]; + + if (nPosts == -1) { + while ([messages next]) { + [messagesArray addObject:[self messageForDBElement:messages inGroup:nil]]; + } + } else { + while ([messages next] && nPosts > 0) { + [messagesArray addObject:[self messageForDBElement:messages inGroup:nil]]; + nPosts --; + } + } + [messages close]; + }]; + + return [messagesArray copy]; +} + ++ (NSArray*)messagesWithContact:(TSContact*)contact { + return [self messagesWithContact:contact numberOfPosts:-1]; +} + ++ (TSMessage*)lastMessageWithContact:(TSContact*) contact{ + openDBMacroNil + + __block NSMutableArray *messagesArray = [NSMutableArray array]; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *messages= [db executeQuery:@"SELECT * FROM messages WHERE (sender_id=? OR recipient_id=?) AND group_id is NULL ORDER BY timestamp DESC" withArgumentsInArray:@[contact.registeredID, contact.registeredID]]; + + if ([messages next]) { + [messagesArray addObject:[self messageForDBElement:messages inGroup:nil]]; + } + + [messages close]; + + }]; + + if ([messagesArray count] == 1) { + return [messagesArray lastObject]; + } else{ + return nil; + } +} + ++ (NSArray*)messagesForGroup:(TSGroup*)group numberOfPosts:(int)numberOfPosts{ + openDBMacroNil + __block int nPosts = numberOfPosts; + __block NSMutableArray *messagesArray = [NSMutableArray array]; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *messages= [db executeQuery:@"SELECT * FROM messages WHERE group_id=? ORDER BY timestamp ASC" withArgumentsInArray:@[[group.groupContext getEncodedId]]]; + if (nPosts == -1) { + while ([messages next]) { + [messagesArray addObject:[self messageForDBElement:messages inGroup:group]]; + } + } + else { + while ([messages next] && nPosts > 0) { + [messagesArray addObject:[self messageForDBElement:messages inGroup:group]]; + nPosts --; + } + } + [messages close]; + }]; + + return [messagesArray copy]; +} + ++ (NSArray*)messagesForGroup:(TSGroup*)group { + return [self messagesForGroup:group numberOfPosts:-1]; +} + ++ (TSMessage*)lastMessageForGroup:(TSGroup*)group { + openDBMacroNil + __block NSMutableArray *messagesArray = [NSMutableArray array]; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *messages= [db executeQuery:@"SELECT * FROM messages WHERE group_id=? ORDER BY timestamp DESC" withArgumentsInArray:@[[group.groupContext getEncodedId]]]; + + if([messages next]) { + [messagesArray addObject:[self messageForDBElement:messages inGroup:group]]; + } + + [messages close]; + }]; + if ([messagesArray count] == 1) { + return [messagesArray lastObject]; + } else{ + return nil; + } +} + ++ (BOOL)deleteMessage:(TSMessage*)msg{ + openDBMacroBOOL + + __block BOOL success = NO; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + success = [db executeUpdate:@"DELETE FROM messages WHERE message_id=?" withArgumentsInArray:@[msg.messageId]]; + }]; + + return success; +} + ++ (void)deleteMessagesForConversation:(TSConversation*)conversation completion:(dataBaseUpdateCompletionBlock) block{ + + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + openDBMacroNothing + + NSArray *messages; + + if ([conversation isGroupConversation]) { + messages = [self messagesForGroup:conversation.group]; + } else{ + messages = [self messagesWithContact:conversation.contact]; + } + + BOOL success = YES; + + for (TSMessage *message in messages){ + if (![self deleteMessage:message]) { + success = NO; + } + } + + dispatch_async(dispatch_get_main_queue(), ^(void){ + if (block) { + block(success); + } + }); + }); +} + ++ (void)deleteMessagesAndSessionsForConversation:(TSConversation*)conversation completion:(dataBaseUpdateCompletionBlock) block{ + + dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + openDBMacroNothing + + [self deleteMessagesForConversation:conversation completion:nil]; + BOOL success = FALSE; + if (conversation.contact) { + success = [self deleteSessions:conversation.contact]; + } else{ + // Group session deletion ambiguous and this method is just called in DEBUG mode. Deleting a group does not delete the sessions with each member of the group to maximize usefullness in debug. There's the edge case that a member you had a session with, you might want to delete in debug mode. Not supported. + success = YES; + + } + + dispatch_async(dispatch_get_main_queue(), ^(void){ + block(success); + }); + }); +} + + +#pragma mark Conversation Methods + ++ (NSArray*)conversations{ + openDBMacroNil + + NSArray *contacts = [self contacts]; + + NSMutableArray *array = [NSMutableArray array]; + + for(TSContact* contact in contacts){ + + TSMessage *tsMessage = [self lastMessageWithContact:contact]; + + if (tsMessage!=nil) { + TSConversation *conversation = [[TSConversation alloc]initWithLastMessage:tsMessage.content contact:contact lastDate:tsMessage.timestamp containsNonReadMessages:[tsMessage isUnread]]; + [array addObject:conversation]; + } + } + NSArray *groups = [self groups]; + for(TSGroup* group in groups){ + + TSMessage *tsMessage = [self lastMessageForGroup:group]; + + if (tsMessage!=nil) { + TSConversation *conversation = [[TSConversation alloc]initWithLastMessage:tsMessage.content group:group lastDate:tsMessage.timestamp containsNonReadMessages:[tsMessage isUnread]]; + [array addObject:conversation]; + } + } + return [array sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { + // most recent date will be first in array + NSDate *first = [(TSConversation*)a lastMessageDate]; + NSDate *second = [(TSConversation*)b lastMessageDate]; + return [second compare:first]; + }];; +} + +#pragma mark Groups table ++ (NSArray*)membersForGroup:(TSGroup *)group { + openDBMacroNil + NSMutableArray *members = [NSMutableArray array]; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *searchInDB = [db executeQuery:@"SELECT DISTINCT group_member FROM group_membership WHERE group_id=?" withArgumentsInArray:@[[group.groupContext getEncodedId]]]; + while ([searchInDB next]) { + [members addObject:[[TSContact alloc] initWithRegisteredID:[searchInDB stringForColumn:@"group_member"] relay:nil]]; + } + [searchInDB close]; + }]; + return [members copy]; +} + ++ (NSArray*)groups{ + openDBMacroNil + + NSMutableArray *groups = [NSMutableArray array]; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *searchInDB = [db executeQuery:@"SELECT * FROM groups"]; + + while ([searchInDB next]) { + TSGroup *group = [[TSGroup alloc] init]; + TSAttachment *attachment = [[TSAttachment alloc] initWithAttachmentDataPath:[searchInDB stringForColumn:@"avatar_path"] withType:[searchInDB intForColumn:@"avatar_type"] withDecryptionKey:[searchInDB dataForColumn:@"avatar_key"]]; + group.isBroadcastGroup = [searchInDB intForColumn:@"broadcast"]; + group.groupName = [searchInDB stringForColumn:@"name"]; + group.groupImage = [UIImage imageWithData:[Cryptography decryptAttachment:[NSData dataWithContentsOfFile:attachment.attachmentDataPath] withKey:attachment.attachmentDecryptionKey]]; + group.groupContext = [[TSGroupContext alloc] initWithId:[TSGroupContext getDecodedId:[searchInDB stringForColumn:@"group_id"]] withName:group.groupName withAvatar:attachment]; + + [groups addObject:group]; + } + [searchInDB close]; + }]; + for (TSGroup* group in groups) { + group.groupContext.members = [self membersForGroup:group]; + } + + return [groups copy]; +} + + +#pragma mark Attachements + +#warning TODO + +#pragma mark Sessions + ++ (BOOL)sessionExistsForContact:(TSContact*)contact{ + openDBMacroBOOL + __block BOOL sessionExists = NO; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *session = [db executeQuery:@"SELECT * FROM sessions WHERE registered_id=?" withArgumentsInArray:@[contact.registeredID]]; + + if ([session next]) { + sessionExists = YES; + } + + [session close]; + + }]; + + return sessionExists; +} + ++ (BOOL)storeSession:(TSSession*)session{ + openDBMacroBOOL + + __block BOOL success = NO; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + success = [db executeUpdate:@"INSERT OR REPLACE INTO sessions VALUES (?, ?, ?)" withArgumentsInArray:@[session.contact.registeredID, [NSNumber numberWithInt:session.deviceId], [NSKeyedArchiver archivedDataWithRootObject:session]]]; + }]; + + return success; +} + ++ (NSArray*)sessionsForContact:(TSContact*)contact{ + openDBMacroNil + __block NSMutableArray *sessions = [NSMutableArray array]; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *sessionResultSet = [db executeQuery:@"SELECT * FROM sessions WHERE registered_id=?" withArgumentsInArray:@[contact.registeredID]]; + while ([sessionResultSet next]) { + TSSession *session = [NSKeyedUnarchiver unarchiveObjectWithData:[sessionResultSet dataForColumn:@"serialized_session"]]; + [session addContact:contact deviceId:[sessionResultSet intForColumn:@"device_id"]]; + [sessions addObject:session]; + } + [sessionResultSet close]; + + }]; + + return [sessions copy]; +} + ++ (TSSession*)sessionForRegisteredId:(NSString*)registeredId deviceId:(int)deviceId{ + openDBMacroNil + __block TSSession *session; + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *sessionResultSet = [db executeQuery:@"SELECT * FROM sessions WHERE registered_id=? AND device_id=?" withArgumentsInArray:@[registeredId, [NSNumber numberWithInt:deviceId]]]; + while ([sessionResultSet next]) { + session = [NSKeyedUnarchiver unarchiveObjectWithData:[sessionResultSet dataForColumn:@"serialized_session"]]; + } + [sessionResultSet close]; + }]; + + TSContact *contact = [TSMessagesDatabase contactForRegisteredID:registeredId]; + + if (!session) { + session = [[TSSession alloc] initWithContact:contact deviceId:deviceId]; + } else{ + [session addContact:contact deviceId:deviceId]; + } + + return session; +} + + + + ++ (BOOL)deleteSession:(TSSession*)session{ + openDBMacroBOOL + + __block BOOL success = NO; + + [messagesDb.dbQueue inDatabase:^(FMDatabase *db) { + success = [db executeUpdate:@"DELETE FROM sessions WHERE registered_id=? AND device_id=?" withArgumentsInArray:@[session.contact.registeredID, [NSNumber numberWithInt:session.deviceId]]]; + }]; + + return success; +} + ++ (BOOL)deleteSessions:(TSContact*)contact{ + openDBMacroBOOL + NSArray *sessions = [self sessionsForContact:contact]; + + BOOL success = TRUE; + for (TSSession *session in sessions){ + if (![self deleteSession:session]) { + success = FALSE; + } + } + return success; +} + +@end diff --git a/TextSecureiOS/Model/TSPrekey.h b/TextSecureiOS/Model/TSPrekey.h new file mode 100644 index 0000000..94c58f9 --- /dev/null +++ b/TextSecureiOS/Model/TSPrekey.h @@ -0,0 +1,21 @@ +// +// TSPrekey.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 05/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSPrekey : NSObject + +@property (readonly)int prekeyId; +@property (readonly)NSData *identityKey; +@property (readonly)NSData *ephemeralKey; +@property (readonly)int deviceId; + +- (instancetype)initWithIdentityKey:(NSData*)identityKey ephemeral:(NSData*)ephemeral prekeyId:(int)prekeyId; + + +@end diff --git a/TextSecureiOS/Model/TSPrekey.m b/TextSecureiOS/Model/TSPrekey.m new file mode 100644 index 0000000..adfdcc1 --- /dev/null +++ b/TextSecureiOS/Model/TSPrekey.m @@ -0,0 +1,39 @@ +// +// TSPrekey.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 05/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSPrekey.h" + +@implementation TSPrekey + +static NSString* const kCoderIdentityKey = @"kCoderIdentityKey"; +static NSString* const kCoderEphemeral = @"kCoderEphemeral"; +static NSString* const kCoderPrekeyId = @"kCoderPrekeyId"; +- (instancetype)initWithIdentityKey:(NSData*)identityKey ephemeral:(NSData*)ephemeral prekeyId:(int)prekeyId{ + self = [super init]; + + if(self){ + _identityKey = identityKey; + _ephemeralKey = ephemeral; + _prekeyId = prekeyId; + } + + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder{ + self = [self initWithIdentityKey:[aDecoder decodeObjectForKey:kCoderIdentityKey] ephemeral:[aDecoder decodeObjectForKey:kCoderEphemeral] prekeyId:[aDecoder decodeIntegerForKey:kCoderPrekeyId]]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder{ + [aCoder encodeObject:self.identityKey forKey:kCoderIdentityKey]; + [aCoder encodeObject:self.ephemeralKey forKey:kCoderEphemeral]; + [aCoder encodeInteger:self.prekeyId forKey:kCoderPrekeyId]; +} + +@end diff --git a/TextSecureiOS/Model/TSReceivingChain.h b/TextSecureiOS/Model/TSReceivingChain.h new file mode 100644 index 0000000..cae96bc --- /dev/null +++ b/TextSecureiOS/Model/TSReceivingChain.h @@ -0,0 +1,20 @@ +// +// TSChain.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 11/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +@class TSChainKey; + +@interface TSReceivingChain : NSObject + +- (instancetype)initWithChainKey:(TSChainKey*)chainKey ephemeral:(NSData*)ephemeral; + +@property(readonly)TSChainKey *chainKey; +@property(readonly)NSData *ephemeral; +@property NSMutableArray *messageKeys; + +@end diff --git a/TextSecureiOS/Model/TSReceivingChain.m b/TextSecureiOS/Model/TSReceivingChain.m new file mode 100644 index 0000000..bbeb841 --- /dev/null +++ b/TextSecureiOS/Model/TSReceivingChain.m @@ -0,0 +1,46 @@ +// +// TSChain.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 11/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSReceivingChain.h" + +@implementation TSReceivingChain + +NSString *const kChainKey = @"kChainKey"; +NSString *const kChainEphemeral = @"kChainEphemeral"; +NSString *const kChainMessages = @"kChainMessages"; + +-(id)initWithCoder:(NSCoder *)aDecoder{ + self = [super init]; + + if (self) { + _messageKeys = [aDecoder decodeObjectForKey:kChainMessages]; + _ephemeral = [[aDecoder decodeObjectForKey:kChainEphemeral] mutableCopy]; + _chainKey = [aDecoder decodeObjectForKey:kChainKey]; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder{ + [aCoder encodeObject:self.chainKey forKey:kChainKey]; + [aCoder encodeObject:self.messageKeys forKey:kChainMessages]; + [aCoder encodeObject:self.ephemeral forKey:kChainEphemeral]; +} + +- (instancetype)initWithChainKey:(TSChainKey *)chainKey ephemeral:(NSData *)ephemeral{ + self = [super init]; + + if (self) { + _chainKey = chainKey; + _ephemeral = ephemeral; + _messageKeys = [NSMutableArray array]; + } + return self; +} + +@end diff --git a/TextSecureiOS/Model/TSSession.h b/TextSecureiOS/Model/TSSession.h new file mode 100644 index 0000000..f96e3c2 --- /dev/null +++ b/TextSecureiOS/Model/TSSession.h @@ -0,0 +1,67 @@ +// +// TSSession.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSContact.h" +#import "RKCK.h" +#import "TSMessageKeys.h" +#import "TSPrekey.h" +#import "TSChainKey.h" +#import "TSEncryptedWhisperMessage.hh" + +@interface TSSession : NSObject + +- (instancetype)initWithContact:(TSContact*)contact deviceId:(int)deviceId; +- (void)addContact:(TSContact*)contact deviceId:(int)deviceId; + +@property(readonly)int deviceId; +@property(readonly)TSContact *contact; + +@property NSData *rootKey; +@property int PN; +@property BOOL needsInitialization; + +- (BOOL)isInitialized; + +@property TSPrekey *pendingPreKey; // Prekey information to add in TSPrekeyWhisperMessage + +- (BOOL)hasPendingPreKey; + +- (BOOL)hasReceiverChain:(NSData*) ephemeral; +- (BOOL)hasSenderChain; + +- (TSChainKey*)receiverChainKey:(NSData*)senderEphemeral; + +- (void)setSenderChain:(TSECKeyPair*)senderEphemeralPair chainkey:(TSChainKey*)chainKey; +- (void)setSenderChainKey:(TSChainKey*)chainKey; +- (TSChainKey*)senderChainKey; + +- (void)addReceiverChain:(NSData*)senderEphemeral chainKey:(TSChainKey*)chainKey; +- (void)setReceiverChainKeyWithEphemeral:(NSData*)senderEphemeral chainKey:(TSChainKey*)chainKey; + +- (BOOL)hasMessageKeysForEphemeral:(NSData*)ephemeral counter:(int)counter; +- (TSMessageKeys*)removeMessageKeysForEphemeral:(NSData*)ephemeral counter:(int)counter; + +- (void)setMessageKeysWithEphemeral:(NSData*)ephemeral messageKey:(TSMessageKeys*)messageKeys; + +- (void)setSenderEphemeral:(TSECKeyPair*)ephemeralPair; + +- (TSECKeyPair*)senderEphemeral; + +#pragma mark Helper methods +- (NSData*)theirIdentityKey; + +- (void)save; + +/** + * The clear method removes all keying material of a session. Only properties remaining are the necessary deviceId and contact information + */ +- (void)removePendingPrekey; +- (void)clear; + +@end diff --git a/TextSecureiOS/Model/TSSession.m b/TextSecureiOS/Model/TSSession.m new file mode 100644 index 0000000..64985ac --- /dev/null +++ b/TextSecureiOS/Model/TSSession.m @@ -0,0 +1,200 @@ +// +// TSSession.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 01/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSUserKeysDatabase.h" +#import "TSSession.h" +#import "TSMessage.h" +#import "TSReceivingChain.h" +#import "TSSendingChain.h" +#import "TSPreKeyWhisperMessage.hh" + +#pragma mark Keys for coder + +static NSString* const kCoderPN = @"kCoderPN"; +static NSString* const kCoderRootKey = @"kCoderRoot"; +static NSString* const kCoderReceiverChains = @"kCoderReceiverChains"; +static NSString* const kCoderSendingChain = @"kCoderSendingChain"; +static NSString* const kCoderPendingPrekey = @"kCoderPendingPrekey"; + + +@interface TSSession (){ + TSSendingChain *senderChain; + NSMutableArray *receiverChains; +} + +@end + +@implementation TSSession + +- (id)initWithCoder:(NSCoder *)aDecoder{ + self = [super init]; + + if (self) { + self.rootKey = [aDecoder decodeObjectForKey:kCoderRootKey]; + self.PN = [aDecoder decodeIntForKey:kCoderPN]; + self.pendingPreKey = [aDecoder decodeObjectForKey:kCoderPendingPrekey]; + senderChain = [aDecoder decodeObjectForKey:kCoderSendingChain]; + receiverChains = [[aDecoder decodeObjectForKey:kCoderReceiverChains] mutableCopy]; + self.needsInitialization = NO; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder{ + [aCoder encodeObject:self.rootKey forKey:kCoderRootKey]; + [aCoder encodeInt:self.PN forKey:kCoderPN]; + [aCoder encodeObject:senderChain forKey:kCoderSendingChain]; + [aCoder encodeObject:receiverChains forKey:kCoderReceiverChains]; + [aCoder encodeObject:self.pendingPreKey forKey:kCoderPendingPrekey]; +} + +- (void)addContact:(TSContact*)contact deviceId:(int)deviceId{ + _contact = contact; + _deviceId = deviceId; +} + +- (instancetype)initWithContact:(TSContact*)contact deviceId:(int)deviceId{ + self = [super init]; + if (self) { + _contact = contact; + _deviceId = deviceId; + receiverChains = [NSMutableArray array]; + self.needsInitialization = NO; + } + return self; +} + +- (NSData*)theirIdentityKey{ + return self.contact.identityKey; +} + +- (BOOL)hasPendingPreKey{ + return self.pendingPreKey!= nil; +} + +- (TSPrekey *)pendingPrekey{ + return self.pendingPrekey; +} + +- (BOOL)hasSenderChain{ + return self.senderChainKey != nil; +} + +- (BOOL)isInitialized{ + return self.rootKey?YES:NO; +} + +- (TSChainKey*)senderChainKey{ + return senderChain.chainKey; +} + +- (void)setSenderChain:(TSECKeyPair*)senderEphemeralPair chainkey:(TSChainKey*)chainKey{ + self.senderChainKey = chainKey; + self.senderEphemeral = senderEphemeralPair; +} + +- (void)setSenderChainKey:(TSChainKey*)chainKey{ + senderChain = [[TSSendingChain alloc] initWithChainKey:chainKey ephemeral:senderChain.ephemeral]; +} + +- (TSECKeyPair*)senderEphemeral{ + return senderChain.ephemeral; +} + +- (void)setSenderEphemeral:(TSECKeyPair *)ephemeralPair{ + senderChain = [[TSSendingChain alloc] initWithChainKey:senderChain.chainKey ephemeral:ephemeralPair]; +} + +- (BOOL)hasReceiverChain:(NSData*) ephemeral{ + return [self receiverChainKey:ephemeral] != nil; +} + +- (TSChainKey*)receiverChainKey:(NSData*)senderEphemeral{ + return [self receiverChain:senderEphemeral].chainKey; +} + +- (TSReceivingChain*)receiverChain:(NSData*)senderEphemeral{ + for(TSReceivingChain *chain in receiverChains){ + if ([chain.ephemeral isEqualToData:senderEphemeral]) { + return chain; + } + } + return nil; +} + +- (void)addReceiverChain:(NSData*)senderEphemeral chainKey:(TSChainKey*)chainKey{ + + TSReceivingChain *chain = [[TSReceivingChain alloc]initWithChainKey:chainKey ephemeral:senderEphemeral]; + + if ([receiverChains count] > 4) { + [receiverChains removeObjectAtIndex:0]; + } + [receiverChains addObject:chain]; +} + +- (void)setReceiverChainKeyWithEphemeral:(NSData*)senderEphemeral chainKey:(TSChainKey*)chainKey{ + + TSReceivingChain *chain = [self receiverChain:senderEphemeral]; + + TSReceivingChain *newChain = [[TSReceivingChain alloc] initWithChainKey:chainKey ephemeral:senderEphemeral]; + + [receiverChains replaceObjectAtIndex:[receiverChains indexOfObject:chain] withObject:newChain]; +} + +- (BOOL)hasMessageKeysForEphemeral:(NSData*)ephemeral counter:(int)counter{ + for (TSMessageKeys *messageKeys in [self receiverChain:ephemeral].messageKeys){ + if (messageKeys.counter == counter) { + return true; + } + } + return false; +} + +- (TSMessageKeys*)removeMessageKeysForEphemeral:(NSData*)ephemeral counter:(int)counter{ + for(NSUInteger i = 0; i <[[self receiverChain:ephemeral].messageKeys count]; i++){ + + TSMessageKeys *messageKey = [[self receiverChain:ephemeral].messageKeys objectAtIndex:i]; + + if (messageKey.counter == counter) { + [[self receiverChain:ephemeral].messageKeys removeObjectAtIndex:i]; + return messageKey; + } + } + @throw [NSException exceptionWithName:@"Message Key not found" reason:@"" userInfo:nil]; +} + +- (void)setMessageKeysWithEphemeral:(NSData*)ephemeral messageKey:(TSMessageKeys*)messageKeys{ + TSReceivingChain *chain = [self receiverChain:ephemeral]; + [chain.messageKeys addObject:messageKeys]; +} + +#pragma mark Helper method + + +- (void)save{ + [TSMessagesDatabase storeSession:self]; +} + +- (void)removePendingPrekey{ + self.pendingPreKey = nil; +} + +/** + * The clear method removes all keying material of a session. Only properties remaining are the necessary deviceId and contact information + */ + +- (void)clear{ + senderChain = nil; + receiverChains = [NSMutableArray array]; + self.rootKey = nil; + self.senderEphemeral = nil; + self.PN = 0; +} + +@end diff --git a/TextSecureiOS/Model/TSStorageError.h b/TextSecureiOS/Model/TSStorageError.h new file mode 100644 index 0000000..e3275a5 --- /dev/null +++ b/TextSecureiOS/Model/TSStorageError.h @@ -0,0 +1,42 @@ +// +// TSStorageError.h +// TextSecureiOS +// +// Created by Alban Diquet on 11/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +extern NSString * const TSStorageErrorDomain; + +typedef enum TSStorageErrorCode { + TSStorageErrorDatabaseAlreadyCreated, + TSStorageErrorDatabaseCreationFailed, + TSStorageErrorDatabaseNotCreated, + TSStorageErrorDatabaseCorrupted, + TSStorageErrorInvalidPassword, + TSStorageErrorStorageKeyLocked, + TSStorageErrorStorageKeyCorrupted, + TSStorageErrorStorageKeyAlreadyCreated, + TSStorageErrorStorageKeyCreationFailed, + TSStorageErrorStorageKeyNotCreated +} TSStorageErrorCode; + + +@interface TSStorageError : NSObject + ++ (NSString *)domain; ++ (NSError *)errorDatabaseAlreadyCreated; ++ (NSError *)errorDatabaseCreationFailed; ++ (NSError *)errorDatabaseNotCreated; ++ (NSError *)errorDatabaseCorrupted; ++ (NSError *)errorInvalidPassword; ++ (NSError *)errorStorageKeyLocked; ++ (NSError *)errorStorageKeyCorrupted; ++ (NSError *)errorStorageKeyAlreadyCreated; ++ (NSError *)errorStorageKeyCreationFailed; ++ (NSError *)errorStorageKeyNotCreated; + +@end + diff --git a/TextSecureiOS/Model/TSStorageError.m b/TextSecureiOS/Model/TSStorageError.m new file mode 100644 index 0000000..ad9505d --- /dev/null +++ b/TextSecureiOS/Model/TSStorageError.m @@ -0,0 +1,91 @@ +// +// TSStorageError.m +// TextSecureiOS +// +// Created by Alban Diquet on 11/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSStorageError.h" + + +NSString * const TSStorageErrorDomain = @"org.whispersystems.whisper.textsecure.TSStorageErrorDomain"; + + +@implementation TSStorageError + ++ (NSError *)errorWithErrorCode:(NSInteger)errorCode userInfo:(NSDictionary *)userInfo { + return [NSError errorWithDomain:[self domain] code:errorCode userInfo:userInfo]; +} + + ++ (NSString *)domain +{ + return TSStorageErrorDomain; +} + + ++ (NSError *)errorDatabaseAlreadyCreated { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The database was already created" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorDatabaseAlreadyCreated userInfo:errorDetail]; +} + + ++ (NSError *)errorDatabaseNotCreated { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The database could not be found" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorDatabaseNotCreated userInfo:errorDetail]; +} + + ++ (NSError *)errorDatabaseCorrupted { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The database was corrupted and could not be opened" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorDatabaseCorrupted userInfo:errorDetail]; +} + + ++ (NSError *)errorDatabaseCreationFailed { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The database could not be created" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorDatabaseCreationFailed userInfo:errorDetail]; +} + ++ (NSError *)errorInvalidPassword { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"Could not unlock the storage master key because the supplied password was wrong" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorInvalidPassword userInfo:errorDetail]; +} + ++ (NSError *)errorStorageKeyLocked { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The storage key is locked and needs to be unlocked with the user's password" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorStorageKeyLocked userInfo:errorDetail]; +} + ++ (NSError *)errorStorageKeyCorrupted { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"Could not recover the storage master key from the Keychain" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorStorageKeyCorrupted userInfo:errorDetail]; +} + ++ (NSError *)errorStorageKeyAlreadyCreated { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The storage master key has already been created" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorStorageKeyAlreadyCreated userInfo:errorDetail]; +} + ++ (NSError *)errorStorageKeyCreationFailed { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"Could not generate the storage master key" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorStorageKeyCreationFailed userInfo:errorDetail]; +} + ++ (NSError *)errorStorageKeyNotCreated { + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:@"The storage master key has been created yet" forKey:NSLocalizedDescriptionKey]; + return [self errorWithErrorCode:TSStorageErrorStorageKeyNotCreated userInfo:errorDetail]; +} + +@end diff --git a/TextSecureiOS/Model/TSUserKeysDatabase.h b/TextSecureiOS/Model/TSUserKeysDatabase.h new file mode 100644 index 0000000..456df20 --- /dev/null +++ b/TextSecureiOS/Model/TSUserKeysDatabase.h @@ -0,0 +1,23 @@ +// +// TSUserKeysDatabase.h +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +@class TSECKeyPair; + + +@interface TSUserKeysDatabase : NSObject + ++(BOOL) databaseCreateUserKeysWithError:(NSError **)error; ++(void) databaseErase; + +// Calling the following functions will fail if the storage master key is in a "locked" state; see TSStorageMasterKey ++(TSECKeyPair*) identityKey; ++(NSArray*) allPreKeys; ++(TSECKeyPair*) preKeyWithId:(int32_t)preKeyId; + +@end diff --git a/TextSecureiOS/Model/TSUserKeysDatabase.m b/TextSecureiOS/Model/TSUserKeysDatabase.m new file mode 100644 index 0000000..11b9367 --- /dev/null +++ b/TextSecureiOS/Model/TSUserKeysDatabase.m @@ -0,0 +1,267 @@ +// +// TSUserKeysDatabase.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSUserKeysDatabase.h" +#import "TSDatabaseManager.h" +#import "TSStorageError.h" +#import "TSECKeyPair.h" +#import "FilePath.h" +#import "FMDatabase.h" +#import "FMDatabaseQueue.h" + +#define PREKEYS_NUMBER 70 + +#define USER_KEYS_DB_FILE_NAME @"TSUserKeys.db" +#define USER_KEYS_DB_PREFERENCE @"TSUserKeysDbWasCreated" + +static TSDatabaseManager *userKeysDb = nil; + +@interface TSUserKeysDatabase(Private) + ++(BOOL) databaseOpenWithError:(NSError **)error; + ++(BOOL) generateAndStorePreKeys; ++(BOOL) generateAndStoreIdentityKey; + +@end + +@implementation TSUserKeysDatabase + +#pragma mark DB creation + ++ (NSString*)pathToDatabase{ + return [FilePath pathInDocumentsDirectory:USER_KEYS_DB_FILE_NAME]; +} + ++ (BOOL)databaseCreateUserKeysWithError:(NSError **)error { + + // Create the database + TSDatabaseManager *db = [TSDatabaseManager databaseCreateAtFilePath:[self pathToDatabase] updateBoolPreference:USER_KEYS_DB_PREFERENCE error:error]; + if (!db) { + return NO; + } + + + // Create the tables we need + userKeysDb = db; + __block BOOL querySuccess = NO; + [userKeysDb.dbQueue inDatabase: ^(FMDatabase *db) { + + if (![db executeUpdate:@"CREATE TABLE user_identity_key (serialized_keypair BLOB)"]) { + return; + } + if (![db executeUpdate:@"CREATE TABLE user_prekeys (prekey_id INTEGER UNIQUE, serialized_keypair BLOB)"]){ + return; + } + + querySuccess = YES; + }]; + if (!querySuccess) { + if (error) { + *error = [TSStorageError errorDatabaseCreationFailed]; + } + // Cleanup + [TSUserKeysDatabase databaseErase]; + userKeysDb = nil; + return NO; + } + + + // Generate and store the TextSecure keys for the current user + if (!([TSUserKeysDatabase generateAndStorePreKeys] && ([TSUserKeysDatabase generateAndStoreIdentityKey]))) { + if (error) { + *error = [TSStorageError errorDatabaseCreationFailed]; + } + // Cleanup + [TSUserKeysDatabase databaseErase]; + userKeysDb = nil; + return NO; + }; + + + return YES; +} + + ++(void)databaseErase { + [TSDatabaseManager databaseEraseAtFilePath:[self pathToDatabase] updateBoolPreference:USER_KEYS_DB_PREFERENCE]; +} + ++ (BOOL)databaseWasCreated { + return [[NSFileManager defaultManager] fileExistsAtPath:[self pathToDatabase]]; +} + + +#pragma mark DB access - private + ++(BOOL)databaseOpenWithError:(NSError **)error { + + // DB was already unlocked + if (userKeysDb){ + return YES; + } + + if (![TSUserKeysDatabase databaseWasCreated]) { + if (error) { + *error = [TSStorageError errorDatabaseNotCreated]; + } + return NO; + } + + TSDatabaseManager *db = [TSDatabaseManager databaseOpenAndDecryptAtFilePath:[self pathToDatabase] error:error]; + if (!db) { + return NO; + } + userKeysDb = db; + return YES; +} + + +#pragma Keys access + ++ (TSECKeyPair*)identityKey{ + + if (!userKeysDb){ + [TSUserKeysDatabase databaseOpenWithError:nil]; + } + + // Fetch the key from the DB + __block NSData *serializedKeyPair = nil; + [userKeysDb.dbQueue inDatabase: ^(FMDatabase *db) { + FMResultSet *rs = [db executeQuery:@"SELECT serialized_keypair FROM user_identity_key"]; + if([rs next]) { + serializedKeyPair = [rs dataForColumn:@"serialized_keypair"]; + } + [rs close]; + }]; + return [NSKeyedUnarchiver unarchiveObjectWithData:serializedKeyPair]; +} + + ++ (NSArray *)allPreKeys { + __block NSError *error; + + // Decrypt the DB if it hasn't been done yet + if (!userKeysDb) { + if (![TSUserKeysDatabase databaseOpenWithError:&error]){ + DLog(@"We had issues opening the user keys database"); + return nil; + } + } + + // Fetch all keys from the DB + __block NSMutableArray *preKeys = [NSMutableArray arrayWithCapacity:PREKEYS_NUMBER]; + __block int preKeysNb = 0; + + [userKeysDb.dbQueue inDatabase: ^(FMDatabase *db) { + FMResultSet *rs = [db executeQuery:@"SELECT serialized_keypair FROM user_prekeys"]; + while([rs next]) { + preKeysNb++; + NSData *serializedKeyPair = [rs dataForColumn:@"serialized_keypair"]; + [preKeys addObject:[NSKeyedUnarchiver unarchiveObjectWithData:serializedKeyPair]]; + } + [rs close]; + + if (preKeysNb != PREKEYS_NUMBER+1) { + if (error) { + error = [TSStorageError errorDatabaseCorrupted]; + } + } + }]; + + if (!error) { + return preKeys; + } else{ + DLog(@"We had issues with the prekeys"); + return nil; + } +} + ++ (TSECKeyPair*)preKeyWithId:(int32_t)preKeyId{ + // Decrypt the DB if it hasn't been done yet + if (!userKeysDb) { + if (![TSUserKeysDatabase databaseOpenWithError:nil]) + return nil; + } + + // Fetch the key from the DB + __block NSData *serializedKeyPair = nil; + [userKeysDb.dbQueue inDatabase: ^(FMDatabase *db) { + FMResultSet *rs = [db executeQuery:[NSString stringWithFormat:@"SELECT serialized_keypair FROM user_prekeys WHERE prekey_id=%d", preKeyId]]; + if([rs next]) { + serializedKeyPair = [rs dataForColumn:@"serialized_keypair"]; + } + [rs close]; + }]; + if (!serializedKeyPair) { + return nil; + } + + return [NSKeyedUnarchiver unarchiveObjectWithData:serializedKeyPair]; +} + + +#pragma mark User keys generation - private + ++ (BOOL)generateAndStoreIdentityKey { + /* + An identity key is an ECC key pair that you generate at install time. It never changes, and is used to certify your identity (clients remember it whenever they see it communicated from other clients and ensure that it's always the same). + + In secure protocols, identity keys generally never actually encrypt anything, so it doesn't affect previous confidentiality if they are compromised. The typical relationship is that you have a long term identity key pair which is used to sign ephemeral keys (like the prekeys). + */ + + NSData *serializedKey = [NSKeyedArchiver archivedDataWithRootObject:[TSECKeyPair keyPairGenerateWithPreKeyId:0]]; + + __block BOOL updateSuccess; + [userKeysDb.dbQueue inDatabase: ^(FMDatabase *db) { + if ([db executeUpdate:@"INSERT INTO user_identity_key (serialized_keypair) VALUES (?)", serializedKey]) { + updateSuccess = YES; + } + }]; + if (!updateSuccess) { + return NO; + } + return YES; +} + ++ (BOOL)generateAndStorePreKeys { + + // Generate and store key of last resort + NSData *serializedPreKey = [NSKeyedArchiver archivedDataWithRootObject:[TSECKeyPair keyPairGenerateWithPreKeyId:kLastResortKeyId]]; + + __block BOOL updateSuccess; + [userKeysDb.dbQueue inDatabase: ^(FMDatabase *db) { + if ([db executeUpdate:@"INSERT INTO user_prekeys (prekey_id, serialized_keypair) VALUES (?,?)",[NSNumber numberWithInt:kLastResortKeyId], serializedPreKey]) { + updateSuccess = YES; + } + }]; + if (!updateSuccess) { + return NO; + } + + // Generate and store other pre keys + int prekeyCounter = arc4random() % kLastResortKeyId; + + for(int i=0; i + +/** + * Posted when the database is unlocked + */ +extern NSString * const TSDatabaseDidUnlockNotification; + +@interface TSWaitingPushMessageDatabase : NSObject + +#define WAITING_PUSH_MESSAGE_DB_FILE_NAME @"TSWaitingPushMessage.db" +#define WAITING_PUSH_MESSAGE_DB_PREFERENCE @"TSWaitingPushMessageDbWasCreated" +#define WAITING_PUSH_MESSAGE_DB_PASSWORD @"TSWaitingPushMessageDbPassword" + ++(BOOL) databaseCreateWaitingPushMessageDatabaseWithError:(NSError **)error; ++(void) databaseErase; + ++(void) queuePush:(NSDictionary*)pushMessageJson; ++(void) finishPushesQueued; ++(NSArray*) getPushesInReceiptOrder; +@end diff --git a/TextSecureiOS/Model/TSWaitingPushMessageDatabase.m b/TextSecureiOS/Model/TSWaitingPushMessageDatabase.m new file mode 100644 index 0000000..8dbeea4 --- /dev/null +++ b/TextSecureiOS/Model/TSWaitingPushMessageDatabase.m @@ -0,0 +1,166 @@ +// +// TSWaitingPushMessageDatabase.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSWaitingPushMessageDatabase.h" +#import "TSDatabaseManager.h" +#import "TSStorageError.h" +#import "FilePath.h" +#import "Cryptography.h" +#import "FMDatabase.h" +#import "FMDatabaseQueue.h" + +static TSDatabaseManager *waitingPushMessageDb = nil; +NSString * const TSDatabaseDidUnlockNotification = @"com.whispersystems.database.unlocked"; + + +@interface TSWaitingPushMessageDatabase(Private) + ++(BOOL) databaseOpenWithError:(NSError **)error; + +@end + +@implementation TSWaitingPushMessageDatabase + +#pragma mark DB creation + ++(BOOL) databaseCreateWaitingPushMessageDatabaseWithError:(NSError **)error { + // This DB is not required to be encrypted-the Push message content comes in pre-encrypted with the signaling key, and inside contents with the Axolotl ratchet + // For very limited obfuscation of meta-data (unread message count), and to reuse the encrypted DB architecture we encrypt the entire DB itself with a key stored in user preferences. + // The key cannot be stored somewhere accessible by password as this is designed to be deployed in the situation before the user enters her password. + NSData* waitingPushMessagePassword = [ Cryptography generateRandomBytes:32]; + TSDatabaseManager *db = [TSDatabaseManager databaseCreateAtFilePath:[FilePath pathInDocumentsDirectory:WAITING_PUSH_MESSAGE_DB_FILE_NAME] updateBoolPreference:WAITING_PUSH_MESSAGE_DB_PREFERENCE withPassword:waitingPushMessagePassword error:error]; + if (!db) { + return NO; + } + else { + [[NSUserDefaults standardUserDefaults] setObject:waitingPushMessagePassword forKey:WAITING_PUSH_MESSAGE_DB_PASSWORD]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + + + // Create the tables we need + waitingPushMessageDb = db; + __block BOOL querySuccess = NO; + [waitingPushMessageDb.dbQueue inDatabase: ^(FMDatabase *db) { + + if (![db executeUpdate:@"CREATE TABLE push_messages (message_serialized_json BLOB,timestamp DATE)"]) { + return; + } + querySuccess = YES; + }]; + if (!querySuccess) { + if (error) { + *error = [TSStorageError errorDatabaseCreationFailed]; + } + // Cleanup + [TSWaitingPushMessageDatabase databaseErase]; + waitingPushMessageDb = nil; + return NO; + } + + + return YES; +} + + ++(void) databaseErase { + [TSDatabaseManager databaseEraseAtFilePath:[FilePath pathInDocumentsDirectory:WAITING_PUSH_MESSAGE_DB_FILE_NAME] updateBoolPreference:WAITING_PUSH_MESSAGE_DB_PREFERENCE]; + [[NSUserDefaults standardUserDefaults] setObject:FALSE forKey:WAITING_PUSH_MESSAGE_DB_PASSWORD]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + + ++(BOOL) databaseWasCreated { + return [[NSUserDefaults standardUserDefaults] boolForKey:WAITING_PUSH_MESSAGE_DB_PREFERENCE]; +} + + + ++(void) queuePush:(NSDictionary*)pushMessageJson { + // Decrypt the DB if it hasn't been done yet + if (!waitingPushMessageDb) { + if (![TSWaitingPushMessageDatabase databaseOpenWithError:nil]) { + return; + } + } + [waitingPushMessageDb.dbQueue inDatabase:^(FMDatabase *db) { + [db executeUpdate:@"INSERT INTO push_messages (message_serialized_json,timestamp) VALUES (?, CURRENT_TIMESTAMP)",[NSJSONSerialization dataWithJSONObject:pushMessageJson options:kNilOptions error:nil]]; + }]; +} + + + ++(void) finishPushesQueued { + // Decrypt the DB if it hasn't been done yet + if (!waitingPushMessageDb) { + if (![TSWaitingPushMessageDatabase databaseOpenWithError:nil]) { + return; + } + } + [waitingPushMessageDb.dbQueue inDatabase:^(FMDatabase *db) { + [db executeUpdate:@"DELETE FROM push_messages"]; + }]; + +} + ++(NSArray*) getPushesInReceiptOrder { + + + // Decrypt the DB if it hasn't been done yet + if (!waitingPushMessageDb) { + if (![TSWaitingPushMessageDatabase databaseOpenWithError:nil]){ + NSLog(@"The database is locked!"); + return nil; + } + } + __block NSMutableArray *pushArray = [[NSMutableArray alloc] init]; + + [waitingPushMessageDb.dbQueue inDatabase:^(FMDatabase *db) { + FMResultSet *searchInDB = [db executeQuery:[NSString stringWithFormat:@"SELECT * FROM push_messages ORDER BY timestamp ASC"]]; + while([searchInDB next]) { + [pushArray addObject:[NSJSONSerialization JSONObjectWithData:[searchInDB dataForColumn:@"message_serialized_json"] options:kNilOptions error:nil]]; + } + [searchInDB close]; + }]; + + return pushArray; + +} + + + +#pragma mark DB access - private + ++(BOOL) databaseOpenWithError:(NSError **)error { + + // DB was already unlocked + if (waitingPushMessageDb){ + return YES; + } + + if (![TSWaitingPushMessageDatabase databaseWasCreated]) { + if (error) { + *error = [TSStorageError errorDatabaseNotCreated]; + } + return NO; + } + NSData* storageKey = [[NSUserDefaults standardUserDefaults] objectForKey:WAITING_PUSH_MESSAGE_DB_PASSWORD]; + if(!storageKey) { + return NO; + } + // We'll also want a "withPassword" here + TSDatabaseManager *db = [TSDatabaseManager databaseOpenAndDecryptAtFilePath:[FilePath pathInDocumentsDirectory:WAITING_PUSH_MESSAGE_DB_FILE_NAME] withPassword:storageKey error:error]; + if (!db) { + return NO; + } + waitingPushMessageDb = db; + return YES; +} + + +@end diff --git a/TextSecureiOS/Model/messagesDbSchema.txt b/TextSecureiOS/Model/messagesDbSchema.txt new file mode 100644 index 0000000..8089049 --- /dev/null +++ b/TextSecureiOS/Model/messagesDbSchema.txt @@ -0,0 +1,6 @@ +CREATE TABLE persistent_settings (setting_name TEXT UNIQUE,setting_value TEXT); +CREATE TABLE personal_prekeys (prekey_id INTEGER UNIQUE,public_key TEXT,private_key TEXT, last_counter INTEGER); +CREATE TABLE IF NOT EXISTS threads (thread_id TEXT PRIMARY KEY, RK BLOB, HKs BLOB, HKr BLOB, NHKs BLOB, NHKr BLOB, CKs BLOB, CKr BLOB, DHIs BLOB, DHIr BLOB, DHRs BLOB, DHRr BLOB, Ns INT, Nr INT, PNs INT, ratchet_flag BOOL, skipped_HK_MK BLOB); +CREATE TABLE IF NOT EXISTS missed_messages (skipped_MK BLOB,skipped_HKs BLOB, skipped_HKr BLOB,thread_id TEXT,FOREIGN KEY(thread_id) REFERENCES threads(thread_id)); +CREATE TABLE IF NOT EXISTS messages (message_id INT PRIMARY KEY,message TEXT,thread_id TEXT,sender_id TEXT,recipient_id TEXT, timestamp DATE,FOREIGN KEY(thread_id) REFERENCES threads(thread_id)); +CREATE TABLE IF NOT EXISTS contacts (registered_phone_number TEXT,relay TEXT, useraddressbookid INTEGER, identitykey TEXT, identityverified INTEGER, supports_sms INTEGER, next_key TEXT); \ No newline at end of file diff --git a/TextSecureiOS/Model/tstDb.db b/TextSecureiOS/Model/tstDb.db new file mode 100644 index 0000000..0516fa6 Binary files /dev/null and b/TextSecureiOS/Model/tstDb.db differ diff --git a/TextSecureiOS/NSString+Conversion.m b/TextSecureiOS/NSString+Conversion.m deleted file mode 100644 index dfb8d21..0000000 --- a/TextSecureiOS/NSString+Conversion.m +++ /dev/null @@ -1,74 +0,0 @@ -// -// NSString+Conversion.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/27/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "NSString+Conversion.h" - -// -// NSData+Conversion.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/26/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// -@implementation NSString (NSString_Conversion) - -#pragma mark - Base 64 Conversion -- (NSString *)base64Encoded { - NSData *theData = [self dataUsingEncoding: NSASCIIStringEncoding]; - const uint8_t* input = (const uint8_t*)[theData bytes]; - NSInteger length = [theData length]; - - static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - - NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; - uint8_t* output = (uint8_t*)data.mutableBytes; - - NSInteger i; - for (i=0; i < length; i += 3) { - NSInteger value = 0; - NSInteger j; - for (j = i; j < (i + 3); j++) { - value <<= 8; - - if (j < length) { - value |= (0xFF & input[j]); - } - } - - NSInteger theIndex = (i / 3) * 4; - output[theIndex + 0] = table[(value >> 18) & 0x3F]; - output[theIndex + 1] = table[(value >> 12) & 0x3F]; - output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '='; - output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '='; - } - - return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; -} - --(NSString *) rot13String { - const char *_string = [self cStringUsingEncoding:NSASCIIStringEncoding]; - int stringLength = [self length]; - char newString[stringLength+1]; - - int x; - for( x=0; x + +@interface TSRequest : NSMutableURLRequest + +@property (nonatomic,retain) NSMutableDictionary *parameters; +- (void) makeAuthenticatedRequest; +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequest.m b/TextSecureiOS/Networking/Requests/TSRequest.m new file mode 100644 index 0000000..f99655b --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequest.m @@ -0,0 +1,41 @@ +// +// TSRequest.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/27/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequest.h" +#import "TSKeyManager.h" + +@implementation TSRequest + +- (id)initWithURL:(NSURL *)URL{ + self = [super initWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:timeOutForRequests]; + self.parameters = [NSMutableDictionary dictionary]; + + + return self; +} + +- (id)init{ + [NSException raise:NSInternalInconsistencyException + format:@"You must use the initWithURL: method"]; + return nil; +} + +- (id)initWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval{ + [NSException raise:NSInternalInconsistencyException + format:@"You must use the initWithURL method"]; + return nil; +} + +- (void) makeAuthenticatedRequest{ + [self.parameters addEntriesFromDictionary:@{@"Authorization":[TSKeyManager getAuthorizationToken]}]; +} + +- (BOOL) usingExternalServer { + return NO; +} +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequestAttachment.h b/TextSecureiOS/Networking/Requests/TSRequestAttachment.h new file mode 100644 index 0000000..6b7be14 --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequestAttachment.h @@ -0,0 +1,13 @@ +// +// TSRequestAttachment.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequest.h" + +@interface TSRequestAttachment : TSRequest +-(TSRequest*) initWithId:(NSNumber*) attachmentId; +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequestAttachment.m b/TextSecureiOS/Networking/Requests/TSRequestAttachment.m new file mode 100644 index 0000000..802e2f2 --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequestAttachment.m @@ -0,0 +1,20 @@ +// +// TSRequestAttachment.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequestAttachment.h" +#import "Constants.h" +@implementation TSRequestAttachment +-(TSRequest*) initWithId:(NSNumber*) attachmentId { + + self = [super initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@",textSecureAttachmentsAPI,attachmentId]]]; + self.HTTPMethod = @"GET"; + return self; +} + + +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequestAttachmentId.h b/TextSecureiOS/Networking/Requests/TSRequestAttachmentId.h new file mode 100644 index 0000000..6e4236b --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequestAttachmentId.h @@ -0,0 +1,13 @@ +// +// TSRequestAttachmentId.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequest.h" + +@interface TSRequestAttachmentId : TSRequest + +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequestAttachmentId.m b/TextSecureiOS/Networking/Requests/TSRequestAttachmentId.m new file mode 100644 index 0000000..5d2e038 --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequestAttachmentId.m @@ -0,0 +1,20 @@ +// +// TSRequestAttachmentId.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequestAttachmentId.h" +#import "Constants.h" + +@implementation TSRequestAttachmentId +-(TSRequest*) init { + + self = [super initWithURL:[NSURL URLWithString:textSecureAttachmentsAPI]]; + self.HTTPMethod = @"GET"; + return self; +} + +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.h b/TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.h new file mode 100644 index 0000000..2d073fe --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.h @@ -0,0 +1,21 @@ +// +// TSSendSMSVerificationRequest.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + + +#import "TSRequest.h" + +typedef enum { + kSMSVerification, + kPhoneNumberVerification +} VerificationTransportType; + +@interface TSRequestVerificationCodeRequest : TSRequest + +- (TSRequest*) initRequestForPhoneNumber:(NSString*)phoneNumber transport:(VerificationTransportType)transport; + +@end diff --git a/TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.m b/TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.m new file mode 100644 index 0000000..b3bedde --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSRequestVerificationCodeRequest.m @@ -0,0 +1,22 @@ +// +// TSSendSMSVerificationRequest.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequestVerificationCodeRequest.h" + +@implementation TSRequestVerificationCodeRequest + +- (TSRequest*) initRequestForPhoneNumber:(NSString*)phoneNumber transport:(VerificationTransportType)transport{ + + self = [super initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/code/%@", textSecureAccountsAPI, (transport == kSMSVerification)? @"sms" : @"voice", [phoneNumber escape]]]]; + + [self setHTTPMethod:@"GET"]; + + return self; +} + +@end diff --git a/TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.h b/TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.h new file mode 100644 index 0000000..a85e06f --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.h @@ -0,0 +1,15 @@ +// +// TSServerCodeVerificationRequest.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequest.h" + +@interface TSServerCodeVerificationRequest : TSRequest + +- (TSRequest*) initWithVerificationCode:(NSString*)verificationCode signalingKey:(NSString*)signalingKey authToken:(NSString*)authToken; +@end + diff --git a/TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.m b/TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.m new file mode 100644 index 0000000..a56d579 --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSServerCodeVerificationRequest.m @@ -0,0 +1,29 @@ +// +// TSServerCodeVerificationRequest.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSServerCodeVerificationRequest.h" +#import "Cryptography.h" +#import "NSString+Conversion.h" + +@implementation TSServerCodeVerificationRequest + +- (TSRequest*) initWithVerificationCode:(NSString*)verificationCode signalingKey:(NSString*)signalingKey authToken:(NSString*)authToken{ + self = [super initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%@", textSecureAccountsAPI, @"code", verificationCode]]]; + + [self.parameters addEntriesFromDictionary:[[NSDictionary alloc] initWithObjects: + [[NSArray alloc] initWithObjects:signalingKey, authToken, nil] + forKeys:[[NSArray alloc] initWithObjects:@"signalingKey", @"AuthKey", nil]]]; + + [self setHTTPMethod:@"PUT"]; + + return self; +} + + + +@end diff --git a/TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.h b/TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.h new file mode 100644 index 0000000..9ce384b --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.h @@ -0,0 +1,13 @@ +// +// TSSubmitMessageRequest.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 11/30/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequest.h" +@class TSContact; +@interface TSSubmitMessageRequest : TSRequest +-(TSRequest*) initWithRecipient:(NSString*) contactRegisteredID message:(NSString*) messageBody ofType:(TSWhisperMessageType)type; +@end diff --git a/TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.m b/TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.m new file mode 100644 index 0000000..3922d07 --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSSubmitMessageRequest.m @@ -0,0 +1,32 @@ +// +// TSSubmitMessageRequest.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 11/30/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSSubmitMessageRequest.h" +#import "TSContact.h" + +@implementation TSSubmitMessageRequest + +-(TSRequest*) initWithRecipient:(NSString*) contactRegisteredID message:(NSString*) messageBody ofType:(TSWhisperMessageType)type { + NSMutableDictionary *messageDictionary = [[NSMutableDictionary alloc] + initWithObjects:[[NSArray alloc] + initWithObjects:[NSNumber numberWithInt:type], + contactRegisteredID, + messageBody, + [NSNumber numberWithLong:[[NSDate date] timeIntervalSince1970]], + nil] + forKeys:[[NSArray alloc] initWithObjects:@"type",@"destination",@"body",@"timestamp" ,nil]]; + + + self = [super initWithURL:[NSURL URLWithString:textSecureMessagesAPI]]; + NSMutableDictionary *allMessages = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[[NSArray alloc] initWithObjects: messageDictionary,nil],@"messages", nil]; + [self setHTTPMethod:@"POST"]; + [self setParameters:allMessages]; + return self; +} + +@end diff --git a/TextSecureiOS/Networking/Requests/TSUploadAttachment.h b/TextSecureiOS/Networking/Requests/TSUploadAttachment.h new file mode 100644 index 0000000..a5498fa --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSUploadAttachment.h @@ -0,0 +1,15 @@ +// +// TSUploadAttachment.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/3/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSRequest.h" +@class TSAttachment; +@interface TSUploadAttachment : TSRequest +@property(nonatomic,strong) TSAttachment* attachment; +-(TSRequest*) initWithAttachment:(TSAttachment*) attachment ; + +@end diff --git a/TextSecureiOS/Networking/Requests/TSUploadAttachment.m b/TextSecureiOS/Networking/Requests/TSUploadAttachment.m new file mode 100644 index 0000000..18cb187 --- /dev/null +++ b/TextSecureiOS/Networking/Requests/TSUploadAttachment.m @@ -0,0 +1,34 @@ +// +// TSUploadAttachment.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/3/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSUploadAttachment.h" +#import "TSAttachment.h" +#import +@implementation TSUploadAttachment + +-(TSRequest*) initWithAttachment:(TSAttachment*) attachment{ + + self = [super initWithURL:attachment.attachmentURL]; + self.HTTPMethod = @"PUT"; + self.attachment = attachment; + + [self setHTTPBody:[self.attachment getData]]; + [self setAllHTTPHeaderFields: @{@"Content-Type": @"application/octet-stream"}]; + + return self; + +} + + + + + + + + +@end diff --git a/TextSecureiOS/Networking/TSAttachmentManager.h b/TextSecureiOS/Networking/TSAttachmentManager.h new file mode 100644 index 0000000..98069f0 --- /dev/null +++ b/TextSecureiOS/Networking/TSAttachmentManager.h @@ -0,0 +1,19 @@ +// +// TSAttachmentManager.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +@class TSMessage; +@interface TSAttachmentManager : NSObject + +#pragma mark uploading files ++(void) uploadAttachment:(TSMessage*) attachement; +#pragma mark downloading files ++(void) downloadAttachment:(TSMessage*)attachment; + + +@end diff --git a/TextSecureiOS/Networking/TSAttachmentManager.m b/TextSecureiOS/Networking/TSAttachmentManager.m new file mode 100644 index 0000000..e8c37bd --- /dev/null +++ b/TextSecureiOS/Networking/TSAttachmentManager.m @@ -0,0 +1,129 @@ +// +// TSAttachmentManager.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSAttachmentManager.h" +#import "TSRequestAttachment.h" +#import "TSRequestAttachmentId.h" +#import "TSUploadAttachment.h" +#import "TSMessage.h" +#import "TSAttachment.h" +#import "TSMessagesManager.h" +#import "TSDownloadAttachment.h" +#import "Cryptography.h" +#import "FilePath.h" +@implementation TSAttachmentManager + ++(void) uploadAttachment:(TSMessage*) message { + + for(TSAttachment *attachment in message.attachments){ + + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRequestAttachmentId alloc] init] success:^(AFHTTPRequestOperation *operation, id responseObject) { + switch (operation.response.statusCode) { + // in both cases attachment info currently in header under "Content-Location " = "contentonamazonwebsite"; + case 200: { + attachment.attachmentId = [responseObject objectForKey:@"id"]; + attachment.attachmentURL = [NSURL URLWithString:[responseObject objectForKey:@"location"]]; + DLog(@"we have attachment id %@ location %@",attachment.attachmentId,attachment.attachmentURL); + //[TSAttachmentManager downloadAttachment:message]; // remove testing to see if upload = download +#warning later do this only after the attachment has been uploaded but still having success issues so am doing before for now. + // we can now send the messsage + // [[TSMessagesManager sharedManager] sendMessage:message]; + [[TSNetworkManager sharedManager] queueUnauthenticatedRequest:[[TSUploadAttachment alloc] initWithAttachment:attachment] success:^(AFHTTPRequestOperation *uploadOperation, id uploadResponseObject) { + switch (uploadOperation.response.statusCode) { + + case 200: { + NSLog(@"upload file success!!!!!"); +#warning remove this testing + + break; + } + default: + break; + } + } failure:^(AFHTTPRequestOperation *uploadOperation, NSError *uploadError) { + DLog(@"failure with uploading file, %ld, %@",(long)uploadOperation.response.statusCode,uploadOperation.response.description); + + + }]; + + break; + } + + default: + DLog(@"Issue getting attachment upload location "); +#warning Add error handling if not able to get contacts prekey + break; + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { +#warning Add error handling if not able to send the token + DLog(@"failure allocated upload location %ld, %@",(long)operation.response.statusCode,operation.response.description); + }]; + } +} + ++(void) downloadAttachment:(TSMessage*) message { + /* + example: + TSMessage *newMessage = [[TSMessage alloc] initWithMessage:@"" sender:@"" recipients:nil sentOnDate:nil attachment:[[TSAttachment alloc] init]]; + newMessage.attachment.attachmentId = [NSNumber numberWithUnsignedLongLong:7752343503763367516]; + [TSAttachmentManager downloadAttachment:newMessage]; + */ + + for(TSAttachment *attachment in message.attachments){ +#warning error handling + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRequestAttachment alloc] initWithId:attachment.attachmentId] success:^(AFHTTPRequestOperation *operation, id responseObject) { + switch (operation.response.statusCode) { + case 200: { + NSString* uploadLocation = [responseObject objectForKey:@"location"]; + // Now, download the data + DLog(@"we have attachment download id %@ location %@",responseObject,uploadLocation); + attachment.attachmentURL = [NSURL URLWithString:[responseObject objectForKey:@"location"]]; + + + [[TSNetworkManager sharedManager] queueUnauthenticatedRequest:[[TSDownloadAttachment alloc] initWithAttachment:attachment] success:^(AFHTTPRequestOperation *downloadOperation, id downloadResponseObject) { + switch (downloadOperation.response.statusCode) { + + case 200:{ + NSLog(@"download file success!!!!!"); + // Save the file + NSData* attachmentData = downloadResponseObject; + NSData *hmacKey = [Cryptography generateRandomBytes:32]; + //attachment.attachmentDataPath = [FilePath pathInDocumentsDirectory:[[Cryptography truncatedHMAC:attachmentData withHMACKey:hmacKey truncation:10]base64EncodedStringWithOptions:0]]; + [attachmentData writeToFile:attachment.attachmentDataPath atomically:YES]; + + break; + } + default: + break; + } + } failure:^(AFHTTPRequestOperation *downloadOperation, NSError *downloadError) { + DLog(@"failure with uploading file, %ld, %@",(long)downloadOperation.response.statusCode,downloadOperation.response.description); + + + }]; + break; + } + default: + DLog(@"Issue getting attachment upload location "); + break; + } + }failure:^(AFHTTPRequestOperation *operation, NSError *error) { + + DLog(@"failure attachment %ld, %@",(long)operation.response.statusCode,operation.response.description); + + + }]; + } +} + +-(NSData*) retrieveAttachmentForId:(NSString*)attachmentId { +# warning not implemented +} + + +@end diff --git a/TextSecureiOS/Networking/TSNetworkManager.h b/TextSecureiOS/Networking/TSNetworkManager.h new file mode 100644 index 0000000..9bf838f --- /dev/null +++ b/TextSecureiOS/Networking/TSNetworkManager.h @@ -0,0 +1,25 @@ +// +// TSNetworkManager.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/27/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import +#import "TSRequest.h" +#import "TSRequestVerificationCodeRequest.h" + +@interface TSNetworkManager : NSObject{ + AFHTTPRequestOperationManager *operationManager; +} + ++ (id)sharedManager; +/* requests outside of the TS Server */ +- (void) queueUnauthenticatedRequest:(TSRequest*) request success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successCompletionBlock failure: (void (^)(AFHTTPRequestOperation *operation, NSError *error)) failureCompletionBlock; + +/* requests inside the TS Server */ +- (void) queueAuthenticatedRequest:(TSRequest*) request success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successCompletionBlock failure: (void (^)(AFHTTPRequestOperation *operation, NSError *error)) failureCompletionBlock; + +@end diff --git a/TextSecureiOS/Networking/TSNetworkManager.m b/TextSecureiOS/Networking/TSNetworkManager.m new file mode 100644 index 0000000..e316aac --- /dev/null +++ b/TextSecureiOS/Networking/TSNetworkManager.m @@ -0,0 +1,90 @@ +// +// TSNetworkManager.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 9/27/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + + +#import "TSNetworkManager.h" +#import "TSRequest.h" +#import "TSKeyManager.h" +#import "TSServerCodeVerificationRequest.h" +#import "TSUploadAttachment.h" +#import "TSAttachment.h" +#import + +@implementation TSNetworkManager + +#pragma mark Singleton implementation + ++ (id)sharedManager { + static TSNetworkManager *sharedMyManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedMyManager = [[self alloc] init]; + }); + return sharedMyManager; +} + +- (id)init { + if (self = [super init]) { + operationManager = [[AFHTTPRequestOperationManager manager] initWithBaseURL:[[NSURL alloc] initWithString:textSecureServer]]; +#warning No pinning + operationManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; + operationManager.securityPolicy.allowInvalidCertificates = YES; // We are not signed by a valid certification authority. + operationManager.requestSerializer = [AFJSONRequestSerializer serializer]; + } + return self; +} + +#pragma mark Manager Methods + +- (void) queueUnauthenticatedRequest:(TSRequest*) request success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successCompletionBlock failure: (void (^)(AFHTTPRequestOperation *operation, NSError *error)) failureCompletionBlock{ + AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + + [operation setCompletionBlockWithSuccess:successCompletionBlock failure:failureCompletionBlock]; + [operationManager.operationQueue addOperation:operation ]; +} + +- (void) queueAuthenticatedRequest:(TSRequest*) request success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successCompletionBlock failure: (void (^)(AFHTTPRequestOperation *operation, NSError *error)) failureCompletionBlock{ + + if ([request isKindOfClass:[TSRequestVerificationCodeRequest class]]) { + // The only unauthenticated request is the initial request for a verification code + + operationManager.requestSerializer = [AFJSONRequestSerializer serializer]; + [operationManager GET:[textSecureServer stringByAppendingString:request.URL.absoluteString] parameters:request.parameters success:successCompletionBlock failure:failureCompletionBlock]; + } else if ([request isKindOfClass:[TSServerCodeVerificationRequest class]]){ + // We plant the Authorization parameter ourselves, no need to double add. + operationManager.requestSerializer = [AFJSONRequestSerializer serializer]; + + // Take out the Basic Auth Params + + [operationManager.requestSerializer setAuthorizationHeaderFieldWithUsername:[TSKeyManager getUsernameToken] password:[request.parameters objectForKey:@"AuthKey"]]; + + [request.parameters removeObjectForKey:@"AuthKey"]; + + [operationManager PUT:[textSecureServer stringByAppendingString:request.URL.absoluteString] parameters:request.parameters success:successCompletionBlock failure:failureCompletionBlock]; + } + else{ + // For all other equests, we do add an authorization header + operationManager.requestSerializer = [AFJSONRequestSerializer serializer]; + + [operationManager.requestSerializer setAuthorizationHeaderFieldWithUsername:[TSKeyManager getUsernameToken] password:[TSKeyManager getAuthenticationToken]]; + + if ([request.HTTPMethod isEqualToString:@"GET"]) { + [operationManager GET:[textSecureServer stringByAppendingString:request.URL.absoluteString] parameters:request.parameters success:successCompletionBlock failure:failureCompletionBlock]; + } else if ([request.HTTPMethod isEqualToString:@"POST"]){ + [operationManager POST:[textSecureServer stringByAppendingString:request.URL.absoluteString] parameters:request.parameters success:successCompletionBlock failure:failureCompletionBlock]; + } else if ([request.HTTPMethod isEqualToString:@"PUT"]){ + [operationManager PUT:[textSecureServer stringByAppendingString:request.URL.absoluteString] parameters:request.parameters success:successCompletionBlock failure:failureCompletionBlock]; + } + else if ([request.HTTPMethod isEqualToString:@"DELETE"]){ + [operationManager DELETE:[textSecureServer stringByAppendingString:request.URL.absoluteString] parameters:request.parameters success:successCompletionBlock failure:failureCompletionBlock]; + } + } +} + + +@end diff --git a/TextSecureiOS/Networking/TSSocketManager.h b/TextSecureiOS/Networking/TSSocketManager.h new file mode 100644 index 0000000..841398d --- /dev/null +++ b/TextSecureiOS/Networking/TSSocketManager.h @@ -0,0 +1,17 @@ +// +// TSSocketManager.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 17/05/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import + +@interface TSSocketManager : NSObject + ++ (void)becomeActive; ++ (void)resignActivity; + +@end diff --git a/TextSecureiOS/Networking/TSSocketManager.m b/TextSecureiOS/Networking/TSSocketManager.m new file mode 100644 index 0000000..7d86bf9 --- /dev/null +++ b/TextSecureiOS/Networking/TSSocketManager.m @@ -0,0 +1,121 @@ +// +// TSSocketManager.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 17/05/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "Constants.h" +#import "TSSocketManager.h" +#import "TSMessagesManager.h" +#import "TSStorageMasterKey.h" +#import "TSWaitingPushMessageDatabase.h" + +#define kWebSocketHeartBeat 15 + +@interface TSSocketManager () +@property (nonatomic, retain) NSTimer *timer; +@property (nonatomic, retain) SRWebSocket *websocket; +@end + +@implementation TSSocketManager + +- (id)init{ + self = [super init]; + + if (self) { + self.websocket = nil; + } + + return self; +} + ++ (id)sharedManager { + static TSSocketManager *sharedMyManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedMyManager = [[self alloc] init]; + }); + return sharedMyManager; +} + +#pragma mark - Manage Socket + ++ (void)becomeActive{ + SRWebSocket *socket =[[self sharedManager] websocket]; + + if (socket) { + switch ([socket readyState]) { + case SR_OPEN: + DLog(@"WebSocket already open on connection request"); + return; + case SR_CONNECTING: + DLog(@"WebSocket is already connecting"); + return; + default: + [socket close]; + socket.delegate = nil; + socket = nil; + break; + } + } + + NSString *webSocketConnect = [NSString stringWithFormat:@"%@?login=%@&password=%@", textSecureWebSocketAPI, [[TSKeyManager getUsernameToken] stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"],[TSKeyManager getAuthenticationToken]]; + NSURL *webSocketConnectURL = [NSURL URLWithString:webSocketConnect]; + NSLog(@"WebsocketURL %@", webSocketConnectURL); + socket = [[SRWebSocket alloc] initWithURL:webSocketConnectURL]; + socket.delegate = [self sharedManager]; + [socket setHeartbeatInterval:kWebSocketHeartBeat]; + [socket open]; + [[self sharedManager] setWebsocket:socket]; +} + ++ (void)resignActivity{ + SRWebSocket *socket =[[self sharedManager] websocket]; + [socket close]; +} + +#pragma mark - Delegate methods + +- (void) webSocketDidOpen:(SRWebSocket *)webSocket{ + DLog(@"WebSocket was sucessfully opened"); + self.timer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketHeartBeat target:self selector:@selector(webSocketHeartBeat) userInfo:nil repeats:YES]; +} + +- (void) webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{ + DLog(@"Error connecting to socket %@", error); + [self.timer invalidate]; +} + +- (void) webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{ + + NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding]; + + NSDictionary *serializedMessage = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + + + if(![TSStorageMasterKey isStorageMasterKeyLocked]) { + [[TSMessagesManager sharedManager]receiveMessagePush:serializedMessage]; + } + else { + DLog(@"Got message on the socket while storage db was closed."); + } + + DLog(@"Got message : %@", [serializedMessage objectForKey:@"message"]); + + NSString *ackedId = [serializedMessage objectForKey:@"id"]; + [self.websocket send:[[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:@{@"type":@"1", @"id":ackedId} options:0 error:nil] encoding:NSUTF8StringEncoding]]; + DLog(@"ACK sent : %@", ackedId); +} + +- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{ + DLog(@"WebSocket did close"); + [self.timer invalidate]; +} + +- (void)webSocketHeartBeat{ + // Send heartbeat. See: https://github.com/square/SocketRocket/pull/184 +} + +@end diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.pb.hh b/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.pb.hh new file mode 100644 index 0000000..47146e6 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.pb.hh @@ -0,0 +1,539 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: IncomingPushMessageSignal.proto + +#ifndef PROTOBUF_IncomingPushMessageSignal_2eproto__INCLUDED +#define PROTOBUF_IncomingPushMessageSignal_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2005000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_IncomingPushMessageSignal_2eproto(); +void protobuf_AssignDesc_IncomingPushMessageSignal_2eproto(); +void protobuf_ShutdownFile_IncomingPushMessageSignal_2eproto(); + +class IncomingPushMessageSignal; + +enum IncomingPushMessageSignal_Type { + IncomingPushMessageSignal_Type_UNKNOWN = 0, + IncomingPushMessageSignal_Type_CIPHERTEXT = 1, + IncomingPushMessageSignal_Type_KEY_EXCHANGE = 2, + IncomingPushMessageSignal_Type_PREKEY_BUNDLE = 3, + IncomingPushMessageSignal_Type_PLAINTEXT = 4 +}; +bool IncomingPushMessageSignal_Type_IsValid(int value); +const IncomingPushMessageSignal_Type IncomingPushMessageSignal_Type_Type_MIN = IncomingPushMessageSignal_Type_UNKNOWN; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal_Type_Type_MAX = IncomingPushMessageSignal_Type_PLAINTEXT; +const int IncomingPushMessageSignal_Type_Type_ARRAYSIZE = IncomingPushMessageSignal_Type_Type_MAX + 1; + +const ::google::protobuf::EnumDescriptor* IncomingPushMessageSignal_Type_descriptor(); +inline const ::std::string& IncomingPushMessageSignal_Type_Name(IncomingPushMessageSignal_Type value) { + return ::google::protobuf::internal::NameOfEnum( + IncomingPushMessageSignal_Type_descriptor(), value); +} +inline bool IncomingPushMessageSignal_Type_Parse( + const ::std::string& name, IncomingPushMessageSignal_Type* value) { + return ::google::protobuf::internal::ParseNamedEnum( + IncomingPushMessageSignal_Type_descriptor(), name, value); +} +// =================================================================== + +class IncomingPushMessageSignal : public ::google::protobuf::Message { + public: + IncomingPushMessageSignal(); + virtual ~IncomingPushMessageSignal(); + + IncomingPushMessageSignal(const IncomingPushMessageSignal& from); + + inline IncomingPushMessageSignal& operator=(const IncomingPushMessageSignal& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const IncomingPushMessageSignal& default_instance(); + + void Swap(IncomingPushMessageSignal* other); + + // implements Message ---------------------------------------------- + + IncomingPushMessageSignal* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const IncomingPushMessageSignal& from); + void MergeFrom(const IncomingPushMessageSignal& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef IncomingPushMessageSignal_Type Type; + static const Type UNKNOWN = IncomingPushMessageSignal_Type_UNKNOWN; + static const Type CIPHERTEXT = IncomingPushMessageSignal_Type_CIPHERTEXT; + static const Type KEY_EXCHANGE = IncomingPushMessageSignal_Type_KEY_EXCHANGE; + static const Type PREKEY_BUNDLE = IncomingPushMessageSignal_Type_PREKEY_BUNDLE; + static const Type PLAINTEXT = IncomingPushMessageSignal_Type_PLAINTEXT; + static inline bool Type_IsValid(int value) { + return IncomingPushMessageSignal_Type_IsValid(value); + } + static const Type Type_MIN = + IncomingPushMessageSignal_Type_Type_MIN; + static const Type Type_MAX = + IncomingPushMessageSignal_Type_Type_MAX; + static const int Type_ARRAYSIZE = + IncomingPushMessageSignal_Type_Type_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + Type_descriptor() { + return IncomingPushMessageSignal_Type_descriptor(); + } + static inline const ::std::string& Type_Name(Type value) { + return IncomingPushMessageSignal_Type_Name(value); + } + static inline bool Type_Parse(const ::std::string& name, + Type* value) { + return IncomingPushMessageSignal_Type_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 1; + inline ::textsecure::IncomingPushMessageSignal_Type type() const; + inline void set_type(::textsecure::IncomingPushMessageSignal_Type value); + + // optional string source = 2; + inline bool has_source() const; + inline void clear_source(); + static const int kSourceFieldNumber = 2; + inline const ::std::string& source() const; + inline void set_source(const ::std::string& value); + inline void set_source(const char* value); + inline void set_source(const char* value, size_t size); + inline ::std::string* mutable_source(); + inline ::std::string* release_source(); + inline void set_allocated_source(::std::string* source); + + // optional uint32 sourceDevice = 7; + inline bool has_sourcedevice() const; + inline void clear_sourcedevice(); + static const int kSourceDeviceFieldNumber = 7; + inline ::google::protobuf::uint32 sourcedevice() const; + inline void set_sourcedevice(::google::protobuf::uint32 value); + + // optional string relay = 3; + inline bool has_relay() const; + inline void clear_relay(); + static const int kRelayFieldNumber = 3; + inline const ::std::string& relay() const; + inline void set_relay(const ::std::string& value); + inline void set_relay(const char* value); + inline void set_relay(const char* value, size_t size); + inline ::std::string* mutable_relay(); + inline ::std::string* release_relay(); + inline void set_allocated_relay(::std::string* relay); + + // optional uint64 timestamp = 5; + inline bool has_timestamp() const; + inline void clear_timestamp(); + static const int kTimestampFieldNumber = 5; + inline ::google::protobuf::uint64 timestamp() const; + inline void set_timestamp(::google::protobuf::uint64 value); + + // optional bytes message = 6; + inline bool has_message() const; + inline void clear_message(); + static const int kMessageFieldNumber = 6; + inline const ::std::string& message() const; + inline void set_message(const ::std::string& value); + inline void set_message(const char* value); + inline void set_message(const void* value, size_t size); + inline ::std::string* mutable_message(); + inline ::std::string* release_message(); + inline void set_allocated_message(::std::string* message); + + // @@protoc_insertion_point(class_scope:textsecure.IncomingPushMessageSignal) + private: + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_source(); + inline void clear_has_source(); + inline void set_has_sourcedevice(); + inline void clear_has_sourcedevice(); + inline void set_has_relay(); + inline void clear_has_relay(); + inline void set_has_timestamp(); + inline void clear_has_timestamp(); + inline void set_has_message(); + inline void clear_has_message(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* source_; + int type_; + ::google::protobuf::uint32 sourcedevice_; + ::std::string* relay_; + ::google::protobuf::uint64 timestamp_; + ::std::string* message_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(6 + 31) / 32]; + + friend void protobuf_AddDesc_IncomingPushMessageSignal_2eproto(); + friend void protobuf_AssignDesc_IncomingPushMessageSignal_2eproto(); + friend void protobuf_ShutdownFile_IncomingPushMessageSignal_2eproto(); + + void InitAsDefaultInstance(); + static IncomingPushMessageSignal* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// IncomingPushMessageSignal + +// optional .textsecure.IncomingPushMessageSignal.Type type = 1; +inline bool IncomingPushMessageSignal::has_type() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void IncomingPushMessageSignal::set_has_type() { + _has_bits_[0] |= 0x00000001u; +} +inline void IncomingPushMessageSignal::clear_has_type() { + _has_bits_[0] &= ~0x00000001u; +} +inline void IncomingPushMessageSignal::clear_type() { + type_ = 0; + clear_has_type(); +} +inline ::textsecure::IncomingPushMessageSignal_Type IncomingPushMessageSignal::type() const { + return static_cast< ::textsecure::IncomingPushMessageSignal_Type >(type_); +} +inline void IncomingPushMessageSignal::set_type(::textsecure::IncomingPushMessageSignal_Type value) { + assert(::textsecure::IncomingPushMessageSignal_Type_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional string source = 2; +inline bool IncomingPushMessageSignal::has_source() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void IncomingPushMessageSignal::set_has_source() { + _has_bits_[0] |= 0x00000002u; +} +inline void IncomingPushMessageSignal::clear_has_source() { + _has_bits_[0] &= ~0x00000002u; +} +inline void IncomingPushMessageSignal::clear_source() { + if (source_ != &::google::protobuf::internal::kEmptyString) { + source_->clear(); + } + clear_has_source(); +} +inline const ::std::string& IncomingPushMessageSignal::source() const { + return *source_; +} +inline void IncomingPushMessageSignal::set_source(const ::std::string& value) { + set_has_source(); + if (source_ == &::google::protobuf::internal::kEmptyString) { + source_ = new ::std::string; + } + source_->assign(value); +} +inline void IncomingPushMessageSignal::set_source(const char* value) { + set_has_source(); + if (source_ == &::google::protobuf::internal::kEmptyString) { + source_ = new ::std::string; + } + source_->assign(value); +} +inline void IncomingPushMessageSignal::set_source(const char* value, size_t size) { + set_has_source(); + if (source_ == &::google::protobuf::internal::kEmptyString) { + source_ = new ::std::string; + } + source_->assign(reinterpret_cast(value), size); +} +inline ::std::string* IncomingPushMessageSignal::mutable_source() { + set_has_source(); + if (source_ == &::google::protobuf::internal::kEmptyString) { + source_ = new ::std::string; + } + return source_; +} +inline ::std::string* IncomingPushMessageSignal::release_source() { + clear_has_source(); + if (source_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = source_; + source_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void IncomingPushMessageSignal::set_allocated_source(::std::string* source) { + if (source_ != &::google::protobuf::internal::kEmptyString) { + delete source_; + } + if (source) { + set_has_source(); + source_ = source; + } else { + clear_has_source(); + source_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional uint32 sourceDevice = 7; +inline bool IncomingPushMessageSignal::has_sourcedevice() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void IncomingPushMessageSignal::set_has_sourcedevice() { + _has_bits_[0] |= 0x00000004u; +} +inline void IncomingPushMessageSignal::clear_has_sourcedevice() { + _has_bits_[0] &= ~0x00000004u; +} +inline void IncomingPushMessageSignal::clear_sourcedevice() { + sourcedevice_ = 0u; + clear_has_sourcedevice(); +} +inline ::google::protobuf::uint32 IncomingPushMessageSignal::sourcedevice() const { + return sourcedevice_; +} +inline void IncomingPushMessageSignal::set_sourcedevice(::google::protobuf::uint32 value) { + set_has_sourcedevice(); + sourcedevice_ = value; +} + +// optional string relay = 3; +inline bool IncomingPushMessageSignal::has_relay() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void IncomingPushMessageSignal::set_has_relay() { + _has_bits_[0] |= 0x00000008u; +} +inline void IncomingPushMessageSignal::clear_has_relay() { + _has_bits_[0] &= ~0x00000008u; +} +inline void IncomingPushMessageSignal::clear_relay() { + if (relay_ != &::google::protobuf::internal::kEmptyString) { + relay_->clear(); + } + clear_has_relay(); +} +inline const ::std::string& IncomingPushMessageSignal::relay() const { + return *relay_; +} +inline void IncomingPushMessageSignal::set_relay(const ::std::string& value) { + set_has_relay(); + if (relay_ == &::google::protobuf::internal::kEmptyString) { + relay_ = new ::std::string; + } + relay_->assign(value); +} +inline void IncomingPushMessageSignal::set_relay(const char* value) { + set_has_relay(); + if (relay_ == &::google::protobuf::internal::kEmptyString) { + relay_ = new ::std::string; + } + relay_->assign(value); +} +inline void IncomingPushMessageSignal::set_relay(const char* value, size_t size) { + set_has_relay(); + if (relay_ == &::google::protobuf::internal::kEmptyString) { + relay_ = new ::std::string; + } + relay_->assign(reinterpret_cast(value), size); +} +inline ::std::string* IncomingPushMessageSignal::mutable_relay() { + set_has_relay(); + if (relay_ == &::google::protobuf::internal::kEmptyString) { + relay_ = new ::std::string; + } + return relay_; +} +inline ::std::string* IncomingPushMessageSignal::release_relay() { + clear_has_relay(); + if (relay_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = relay_; + relay_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void IncomingPushMessageSignal::set_allocated_relay(::std::string* relay) { + if (relay_ != &::google::protobuf::internal::kEmptyString) { + delete relay_; + } + if (relay) { + set_has_relay(); + relay_ = relay; + } else { + clear_has_relay(); + relay_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional uint64 timestamp = 5; +inline bool IncomingPushMessageSignal::has_timestamp() const { + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void IncomingPushMessageSignal::set_has_timestamp() { + _has_bits_[0] |= 0x00000010u; +} +inline void IncomingPushMessageSignal::clear_has_timestamp() { + _has_bits_[0] &= ~0x00000010u; +} +inline void IncomingPushMessageSignal::clear_timestamp() { + timestamp_ = GOOGLE_ULONGLONG(0); + clear_has_timestamp(); +} +inline ::google::protobuf::uint64 IncomingPushMessageSignal::timestamp() const { + return timestamp_; +} +inline void IncomingPushMessageSignal::set_timestamp(::google::protobuf::uint64 value) { + set_has_timestamp(); + timestamp_ = value; +} + +// optional bytes message = 6; +inline bool IncomingPushMessageSignal::has_message() const { + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void IncomingPushMessageSignal::set_has_message() { + _has_bits_[0] |= 0x00000020u; +} +inline void IncomingPushMessageSignal::clear_has_message() { + _has_bits_[0] &= ~0x00000020u; +} +inline void IncomingPushMessageSignal::clear_message() { + if (message_ != &::google::protobuf::internal::kEmptyString) { + message_->clear(); + } + clear_has_message(); +} +inline const ::std::string& IncomingPushMessageSignal::message() const { + return *message_; +} +inline void IncomingPushMessageSignal::set_message(const ::std::string& value) { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + message_->assign(value); +} +inline void IncomingPushMessageSignal::set_message(const char* value) { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + message_->assign(value); +} +inline void IncomingPushMessageSignal::set_message(const void* value, size_t size) { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + message_->assign(reinterpret_cast(value), size); +} +inline ::std::string* IncomingPushMessageSignal::mutable_message() { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + return message_; +} +inline ::std::string* IncomingPushMessageSignal::release_message() { + clear_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = message_; + message_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void IncomingPushMessageSignal::set_allocated_message(::std::string* message) { + if (message_ != &::google::protobuf::internal::kEmptyString) { + delete message_; + } + if (message) { + set_has_message(); + message_ = message; + } else { + clear_has_message(); + message_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +#ifndef SWIG +namespace google { +namespace protobuf { + +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::textsecure::IncomingPushMessageSignal_Type>() { + return ::textsecure::IncomingPushMessageSignal_Type_descriptor(); +} + +} // namespace google +} // namespace protobuf +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_IncomingPushMessageSignal_2eproto__INCLUDED diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.pb.mm b/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.pb.mm new file mode 100644 index 0000000..88e921e --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.pb.mm @@ -0,0 +1,597 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: IncomingPushMessageSignal.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "IncomingPushMessageSignal.pb.hh" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +namespace { + +const ::google::protobuf::Descriptor* IncomingPushMessageSignal_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + IncomingPushMessageSignal_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* IncomingPushMessageSignal_Type_descriptor_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_IncomingPushMessageSignal_2eproto() { + protobuf_AddDesc_IncomingPushMessageSignal_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "IncomingPushMessageSignal.proto"); + GOOGLE_CHECK(file != NULL); + IncomingPushMessageSignal_descriptor_ = file->message_type(0); + static const int IncomingPushMessageSignal_offsets_[6] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, source_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, sourcedevice_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, relay_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, timestamp_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, message_), + }; + IncomingPushMessageSignal_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + IncomingPushMessageSignal_descriptor_, + IncomingPushMessageSignal::default_instance_, + IncomingPushMessageSignal_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(IncomingPushMessageSignal, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(IncomingPushMessageSignal)); + IncomingPushMessageSignal_Type_descriptor_ = IncomingPushMessageSignal_descriptor_->enum_type(0); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_IncomingPushMessageSignal_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + IncomingPushMessageSignal_descriptor_, &IncomingPushMessageSignal::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_IncomingPushMessageSignal_2eproto() { + delete IncomingPushMessageSignal::default_instance_; + delete IncomingPushMessageSignal_reflection_; +} + +void protobuf_AddDesc_IncomingPushMessageSignal_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\037IncomingPushMessageSignal.proto\022\ntexts" + "ecure\"\207\002\n\031IncomingPushMessageSignal\0228\n\004t" + "ype\030\001 \001(\0162*.textsecure.IncomingPushMessa" + "geSignal.Type\022\016\n\006source\030\002 \001(\t\022\024\n\014sourceD" + "evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" + "\005 \001(\004\022\017\n\007message\030\006 \001(\014\"W\n\004Type\022\013\n\007UNKNOW" + "N\020\000\022\016\n\nCIPHERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n" + "\rPREKEY_BUNDLE\020\003\022\r\n\tPLAINTEXT\020\004", 311); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "IncomingPushMessageSignal.proto", &protobuf_RegisterTypes); + IncomingPushMessageSignal::default_instance_ = new IncomingPushMessageSignal(); + IncomingPushMessageSignal::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_IncomingPushMessageSignal_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_IncomingPushMessageSignal_2eproto { + StaticDescriptorInitializer_IncomingPushMessageSignal_2eproto() { + protobuf_AddDesc_IncomingPushMessageSignal_2eproto(); + } +} static_descriptor_initializer_IncomingPushMessageSignal_2eproto_; + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* IncomingPushMessageSignal_Type_descriptor() { + protobuf_AssignDescriptorsOnce(); + return IncomingPushMessageSignal_Type_descriptor_; +} +bool IncomingPushMessageSignal_Type_IsValid(int value) { + switch(value) { + case 0: + case 1: + case 2: + case 3: + case 4: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::UNKNOWN; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::CIPHERTEXT; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::KEY_EXCHANGE; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::PREKEY_BUNDLE; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::PLAINTEXT; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::Type_MIN; +const IncomingPushMessageSignal_Type IncomingPushMessageSignal::Type_MAX; +const int IncomingPushMessageSignal::Type_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int IncomingPushMessageSignal::kTypeFieldNumber; +const int IncomingPushMessageSignal::kSourceFieldNumber; +const int IncomingPushMessageSignal::kSourceDeviceFieldNumber; +const int IncomingPushMessageSignal::kRelayFieldNumber; +const int IncomingPushMessageSignal::kTimestampFieldNumber; +const int IncomingPushMessageSignal::kMessageFieldNumber; +#endif // !_MSC_VER + +IncomingPushMessageSignal::IncomingPushMessageSignal() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void IncomingPushMessageSignal::InitAsDefaultInstance() { +} + +IncomingPushMessageSignal::IncomingPushMessageSignal(const IncomingPushMessageSignal& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void IncomingPushMessageSignal::SharedCtor() { + _cached_size_ = 0; + type_ = 0; + source_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + sourcedevice_ = 0u; + relay_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + timestamp_ = GOOGLE_ULONGLONG(0); + message_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +IncomingPushMessageSignal::~IncomingPushMessageSignal() { + SharedDtor(); +} + +void IncomingPushMessageSignal::SharedDtor() { + if (source_ != &::google::protobuf::internal::kEmptyString) { + delete source_; + } + if (relay_ != &::google::protobuf::internal::kEmptyString) { + delete relay_; + } + if (message_ != &::google::protobuf::internal::kEmptyString) { + delete message_; + } + if (this != default_instance_) { + } +} + +void IncomingPushMessageSignal::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* IncomingPushMessageSignal::descriptor() { + protobuf_AssignDescriptorsOnce(); + return IncomingPushMessageSignal_descriptor_; +} + +const IncomingPushMessageSignal& IncomingPushMessageSignal::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_IncomingPushMessageSignal_2eproto(); + return *default_instance_; +} + +IncomingPushMessageSignal* IncomingPushMessageSignal::default_instance_ = NULL; + +IncomingPushMessageSignal* IncomingPushMessageSignal::New() const { + return new IncomingPushMessageSignal; +} + +void IncomingPushMessageSignal::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + type_ = 0; + if (has_source()) { + if (source_ != &::google::protobuf::internal::kEmptyString) { + source_->clear(); + } + } + sourcedevice_ = 0u; + if (has_relay()) { + if (relay_ != &::google::protobuf::internal::kEmptyString) { + relay_->clear(); + } + } + timestamp_ = GOOGLE_ULONGLONG(0); + if (has_message()) { + if (message_ != &::google::protobuf::internal::kEmptyString) { + message_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool IncomingPushMessageSignal::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::textsecure::IncomingPushMessageSignal_Type_IsValid(value)) { + set_type(static_cast< ::textsecure::IncomingPushMessageSignal_Type >(value)); + } else { + mutable_unknown_fields()->AddVarint(1, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_source; + break; + } + + // optional string source = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_source: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_source())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->source().data(), this->source().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_relay; + break; + } + + // optional string relay = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_relay: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_relay())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->relay().data(), this->relay().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(40)) goto parse_timestamp; + break; + } + + // optional uint64 timestamp = 5; + case 5: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_timestamp: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, ×tamp_))); + set_has_timestamp(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(50)) goto parse_message; + break; + } + + // optional bytes message = 6; + case 6: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_message: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_message())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(56)) goto parse_sourceDevice; + break; + } + + // optional uint32 sourceDevice = 7; + case 7: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_sourceDevice: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &sourcedevice_))); + set_has_sourcedevice(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void IncomingPushMessageSignal::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 1, this->type(), output); + } + + // optional string source = 2; + if (has_source()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->source().data(), this->source().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 2, this->source(), output); + } + + // optional string relay = 3; + if (has_relay()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->relay().data(), this->relay().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 3, this->relay(), output); + } + + // optional uint64 timestamp = 5; + if (has_timestamp()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(5, this->timestamp(), output); + } + + // optional bytes message = 6; + if (has_message()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 6, this->message(), output); + } + + // optional uint32 sourceDevice = 7; + if (has_sourcedevice()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(7, this->sourcedevice(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* IncomingPushMessageSignal::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 1, this->type(), target); + } + + // optional string source = 2; + if (has_source()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->source().data(), this->source().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 2, this->source(), target); + } + + // optional string relay = 3; + if (has_relay()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->relay().data(), this->relay().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 3, this->relay(), target); + } + + // optional uint64 timestamp = 5; + if (has_timestamp()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(5, this->timestamp(), target); + } + + // optional bytes message = 6; + if (has_message()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 6, this->message(), target); + } + + // optional uint32 sourceDevice = 7; + if (has_sourcedevice()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(7, this->sourcedevice(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int IncomingPushMessageSignal::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .textsecure.IncomingPushMessageSignal.Type type = 1; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional string source = 2; + if (has_source()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->source()); + } + + // optional uint32 sourceDevice = 7; + if (has_sourcedevice()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->sourcedevice()); + } + + // optional string relay = 3; + if (has_relay()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->relay()); + } + + // optional uint64 timestamp = 5; + if (has_timestamp()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->timestamp()); + } + + // optional bytes message = 6; + if (has_message()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->message()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void IncomingPushMessageSignal::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const IncomingPushMessageSignal* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void IncomingPushMessageSignal::MergeFrom(const IncomingPushMessageSignal& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_source()) { + set_source(from.source()); + } + if (from.has_sourcedevice()) { + set_sourcedevice(from.sourcedevice()); + } + if (from.has_relay()) { + set_relay(from.relay()); + } + if (from.has_timestamp()) { + set_timestamp(from.timestamp()); + } + if (from.has_message()) { + set_message(from.message()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void IncomingPushMessageSignal::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void IncomingPushMessageSignal::CopyFrom(const IncomingPushMessageSignal& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool IncomingPushMessageSignal::IsInitialized() const { + + return true; +} + +void IncomingPushMessageSignal::Swap(IncomingPushMessageSignal* other) { + if (other != this) { + std::swap(type_, other->type_); + std::swap(source_, other->source_); + std::swap(sourcedevice_, other->sourcedevice_); + std::swap(relay_, other->relay_); + std::swap(timestamp_, other->timestamp_); + std::swap(message_, other->message_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata IncomingPushMessageSignal::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = IncomingPushMessageSignal_descriptor_; + metadata.reflection = IncomingPushMessageSignal_reflection_; + return metadata; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +// @@protoc_insertion_point(global_scope) diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.proto b/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.proto new file mode 100644 index 0000000..2288921 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/IncomingPushMessageSignal.proto @@ -0,0 +1,20 @@ +package textsecure; + +message IncomingPushMessageSignal { + enum Type { + UNKNOWN = 0; + CIPHERTEXT = 1; + KEY_EXCHANGE = 2; + PREKEY_BUNDLE = 3; + PLAINTEXT = 4; + } + optional Type type = 1; + optional string source = 2; + optional uint32 sourceDevice = 7; + optional string relay = 3; + optional uint64 timestamp = 5; + optional bytes message = 6; // Contains an encrypted PushMessageContent +// repeated string destinations = 4; // No longer supported +} + + diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.pb.hh b/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.pb.hh new file mode 100644 index 0000000..3311ba1 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.pb.hh @@ -0,0 +1,420 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: PreKeyWhisperMessage.proto + +#ifndef PROTOBUF_PreKeyWhisperMessage_2eproto__INCLUDED +#define PROTOBUF_PreKeyWhisperMessage_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2005000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_PreKeyWhisperMessage_2eproto(); +void protobuf_AssignDesc_PreKeyWhisperMessage_2eproto(); +void protobuf_ShutdownFile_PreKeyWhisperMessage_2eproto(); + +class PreKeyWhisperMessage; + +// =================================================================== + +class PreKeyWhisperMessage : public ::google::protobuf::Message { + public: + PreKeyWhisperMessage(); + virtual ~PreKeyWhisperMessage(); + + PreKeyWhisperMessage(const PreKeyWhisperMessage& from); + + inline PreKeyWhisperMessage& operator=(const PreKeyWhisperMessage& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const PreKeyWhisperMessage& default_instance(); + + void Swap(PreKeyWhisperMessage* other); + + // implements Message ---------------------------------------------- + + PreKeyWhisperMessage* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const PreKeyWhisperMessage& from); + void MergeFrom(const PreKeyWhisperMessage& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint32 preKeyId = 1; + inline bool has_prekeyid() const; + inline void clear_prekeyid(); + static const int kPreKeyIdFieldNumber = 1; + inline ::google::protobuf::uint32 prekeyid() const; + inline void set_prekeyid(::google::protobuf::uint32 value); + + // optional bytes baseKey = 2; + inline bool has_basekey() const; + inline void clear_basekey(); + static const int kBaseKeyFieldNumber = 2; + inline const ::std::string& basekey() const; + inline void set_basekey(const ::std::string& value); + inline void set_basekey(const char* value); + inline void set_basekey(const void* value, size_t size); + inline ::std::string* mutable_basekey(); + inline ::std::string* release_basekey(); + inline void set_allocated_basekey(::std::string* basekey); + + // optional bytes identityKey = 3; + inline bool has_identitykey() const; + inline void clear_identitykey(); + static const int kIdentityKeyFieldNumber = 3; + inline const ::std::string& identitykey() const; + inline void set_identitykey(const ::std::string& value); + inline void set_identitykey(const char* value); + inline void set_identitykey(const void* value, size_t size); + inline ::std::string* mutable_identitykey(); + inline ::std::string* release_identitykey(); + inline void set_allocated_identitykey(::std::string* identitykey); + + // optional bytes message = 4; + inline bool has_message() const; + inline void clear_message(); + static const int kMessageFieldNumber = 4; + inline const ::std::string& message() const; + inline void set_message(const ::std::string& value); + inline void set_message(const char* value); + inline void set_message(const void* value, size_t size); + inline ::std::string* mutable_message(); + inline ::std::string* release_message(); + inline void set_allocated_message(::std::string* message); + + // @@protoc_insertion_point(class_scope:textsecure.PreKeyWhisperMessage) + private: + inline void set_has_prekeyid(); + inline void clear_has_prekeyid(); + inline void set_has_basekey(); + inline void clear_has_basekey(); + inline void set_has_identitykey(); + inline void clear_has_identitykey(); + inline void set_has_message(); + inline void clear_has_message(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* basekey_; + ::std::string* identitykey_; + ::std::string* message_; + ::google::protobuf::uint32 prekeyid_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_PreKeyWhisperMessage_2eproto(); + friend void protobuf_AssignDesc_PreKeyWhisperMessage_2eproto(); + friend void protobuf_ShutdownFile_PreKeyWhisperMessage_2eproto(); + + void InitAsDefaultInstance(); + static PreKeyWhisperMessage* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// PreKeyWhisperMessage + +// optional uint32 preKeyId = 1; +inline bool PreKeyWhisperMessage::has_prekeyid() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void PreKeyWhisperMessage::set_has_prekeyid() { + _has_bits_[0] |= 0x00000001u; +} +inline void PreKeyWhisperMessage::clear_has_prekeyid() { + _has_bits_[0] &= ~0x00000001u; +} +inline void PreKeyWhisperMessage::clear_prekeyid() { + prekeyid_ = 0u; + clear_has_prekeyid(); +} +inline ::google::protobuf::uint32 PreKeyWhisperMessage::prekeyid() const { + return prekeyid_; +} +inline void PreKeyWhisperMessage::set_prekeyid(::google::protobuf::uint32 value) { + set_has_prekeyid(); + prekeyid_ = value; +} + +// optional bytes baseKey = 2; +inline bool PreKeyWhisperMessage::has_basekey() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void PreKeyWhisperMessage::set_has_basekey() { + _has_bits_[0] |= 0x00000002u; +} +inline void PreKeyWhisperMessage::clear_has_basekey() { + _has_bits_[0] &= ~0x00000002u; +} +inline void PreKeyWhisperMessage::clear_basekey() { + if (basekey_ != &::google::protobuf::internal::kEmptyString) { + basekey_->clear(); + } + clear_has_basekey(); +} +inline const ::std::string& PreKeyWhisperMessage::basekey() const { + return *basekey_; +} +inline void PreKeyWhisperMessage::set_basekey(const ::std::string& value) { + set_has_basekey(); + if (basekey_ == &::google::protobuf::internal::kEmptyString) { + basekey_ = new ::std::string; + } + basekey_->assign(value); +} +inline void PreKeyWhisperMessage::set_basekey(const char* value) { + set_has_basekey(); + if (basekey_ == &::google::protobuf::internal::kEmptyString) { + basekey_ = new ::std::string; + } + basekey_->assign(value); +} +inline void PreKeyWhisperMessage::set_basekey(const void* value, size_t size) { + set_has_basekey(); + if (basekey_ == &::google::protobuf::internal::kEmptyString) { + basekey_ = new ::std::string; + } + basekey_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PreKeyWhisperMessage::mutable_basekey() { + set_has_basekey(); + if (basekey_ == &::google::protobuf::internal::kEmptyString) { + basekey_ = new ::std::string; + } + return basekey_; +} +inline ::std::string* PreKeyWhisperMessage::release_basekey() { + clear_has_basekey(); + if (basekey_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = basekey_; + basekey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PreKeyWhisperMessage::set_allocated_basekey(::std::string* basekey) { + if (basekey_ != &::google::protobuf::internal::kEmptyString) { + delete basekey_; + } + if (basekey) { + set_has_basekey(); + basekey_ = basekey; + } else { + clear_has_basekey(); + basekey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional bytes identityKey = 3; +inline bool PreKeyWhisperMessage::has_identitykey() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void PreKeyWhisperMessage::set_has_identitykey() { + _has_bits_[0] |= 0x00000004u; +} +inline void PreKeyWhisperMessage::clear_has_identitykey() { + _has_bits_[0] &= ~0x00000004u; +} +inline void PreKeyWhisperMessage::clear_identitykey() { + if (identitykey_ != &::google::protobuf::internal::kEmptyString) { + identitykey_->clear(); + } + clear_has_identitykey(); +} +inline const ::std::string& PreKeyWhisperMessage::identitykey() const { + return *identitykey_; +} +inline void PreKeyWhisperMessage::set_identitykey(const ::std::string& value) { + set_has_identitykey(); + if (identitykey_ == &::google::protobuf::internal::kEmptyString) { + identitykey_ = new ::std::string; + } + identitykey_->assign(value); +} +inline void PreKeyWhisperMessage::set_identitykey(const char* value) { + set_has_identitykey(); + if (identitykey_ == &::google::protobuf::internal::kEmptyString) { + identitykey_ = new ::std::string; + } + identitykey_->assign(value); +} +inline void PreKeyWhisperMessage::set_identitykey(const void* value, size_t size) { + set_has_identitykey(); + if (identitykey_ == &::google::protobuf::internal::kEmptyString) { + identitykey_ = new ::std::string; + } + identitykey_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PreKeyWhisperMessage::mutable_identitykey() { + set_has_identitykey(); + if (identitykey_ == &::google::protobuf::internal::kEmptyString) { + identitykey_ = new ::std::string; + } + return identitykey_; +} +inline ::std::string* PreKeyWhisperMessage::release_identitykey() { + clear_has_identitykey(); + if (identitykey_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = identitykey_; + identitykey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PreKeyWhisperMessage::set_allocated_identitykey(::std::string* identitykey) { + if (identitykey_ != &::google::protobuf::internal::kEmptyString) { + delete identitykey_; + } + if (identitykey) { + set_has_identitykey(); + identitykey_ = identitykey; + } else { + clear_has_identitykey(); + identitykey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional bytes message = 4; +inline bool PreKeyWhisperMessage::has_message() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void PreKeyWhisperMessage::set_has_message() { + _has_bits_[0] |= 0x00000008u; +} +inline void PreKeyWhisperMessage::clear_has_message() { + _has_bits_[0] &= ~0x00000008u; +} +inline void PreKeyWhisperMessage::clear_message() { + if (message_ != &::google::protobuf::internal::kEmptyString) { + message_->clear(); + } + clear_has_message(); +} +inline const ::std::string& PreKeyWhisperMessage::message() const { + return *message_; +} +inline void PreKeyWhisperMessage::set_message(const ::std::string& value) { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + message_->assign(value); +} +inline void PreKeyWhisperMessage::set_message(const char* value) { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + message_->assign(value); +} +inline void PreKeyWhisperMessage::set_message(const void* value, size_t size) { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + message_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PreKeyWhisperMessage::mutable_message() { + set_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + message_ = new ::std::string; + } + return message_; +} +inline ::std::string* PreKeyWhisperMessage::release_message() { + clear_has_message(); + if (message_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = message_; + message_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PreKeyWhisperMessage::set_allocated_message(::std::string* message) { + if (message_ != &::google::protobuf::internal::kEmptyString) { + delete message_; + } + if (message) { + set_has_message(); + message_ = message; + } else { + clear_has_message(); + message_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +#ifndef SWIG +namespace google { +namespace protobuf { + + +} // namespace google +} // namespace protobuf +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_PreKeyWhisperMessage_2eproto__INCLUDED diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.pb.mm b/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.pb.mm new file mode 100644 index 0000000..6dca848 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.pb.mm @@ -0,0 +1,458 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: PreKeyWhisperMessage.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "PreKeyWhisperMessage.pb.hh" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +namespace { + +const ::google::protobuf::Descriptor* PreKeyWhisperMessage_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + PreKeyWhisperMessage_reflection_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_PreKeyWhisperMessage_2eproto() { + protobuf_AddDesc_PreKeyWhisperMessage_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "PreKeyWhisperMessage.proto"); + GOOGLE_CHECK(file != NULL); + PreKeyWhisperMessage_descriptor_ = file->message_type(0); + static const int PreKeyWhisperMessage_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PreKeyWhisperMessage, prekeyid_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PreKeyWhisperMessage, basekey_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PreKeyWhisperMessage, identitykey_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PreKeyWhisperMessage, message_), + }; + PreKeyWhisperMessage_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + PreKeyWhisperMessage_descriptor_, + PreKeyWhisperMessage::default_instance_, + PreKeyWhisperMessage_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PreKeyWhisperMessage, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PreKeyWhisperMessage, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(PreKeyWhisperMessage)); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_PreKeyWhisperMessage_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + PreKeyWhisperMessage_descriptor_, &PreKeyWhisperMessage::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_PreKeyWhisperMessage_2eproto() { + delete PreKeyWhisperMessage::default_instance_; + delete PreKeyWhisperMessage_reflection_; +} + +void protobuf_AddDesc_PreKeyWhisperMessage_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\032PreKeyWhisperMessage.proto\022\ntextsecure" + "\"_\n\024PreKeyWhisperMessage\022\020\n\010preKeyId\030\001 \001" + "(\r\022\017\n\007baseKey\030\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014" + "\022\017\n\007message\030\004 \001(\014", 137); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "PreKeyWhisperMessage.proto", &protobuf_RegisterTypes); + PreKeyWhisperMessage::default_instance_ = new PreKeyWhisperMessage(); + PreKeyWhisperMessage::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_PreKeyWhisperMessage_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_PreKeyWhisperMessage_2eproto { + StaticDescriptorInitializer_PreKeyWhisperMessage_2eproto() { + protobuf_AddDesc_PreKeyWhisperMessage_2eproto(); + } +} static_descriptor_initializer_PreKeyWhisperMessage_2eproto_; + +// =================================================================== + +#ifndef _MSC_VER +const int PreKeyWhisperMessage::kPreKeyIdFieldNumber; +const int PreKeyWhisperMessage::kBaseKeyFieldNumber; +const int PreKeyWhisperMessage::kIdentityKeyFieldNumber; +const int PreKeyWhisperMessage::kMessageFieldNumber; +#endif // !_MSC_VER + +PreKeyWhisperMessage::PreKeyWhisperMessage() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void PreKeyWhisperMessage::InitAsDefaultInstance() { +} + +PreKeyWhisperMessage::PreKeyWhisperMessage(const PreKeyWhisperMessage& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void PreKeyWhisperMessage::SharedCtor() { + _cached_size_ = 0; + prekeyid_ = 0u; + basekey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + identitykey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + message_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +PreKeyWhisperMessage::~PreKeyWhisperMessage() { + SharedDtor(); +} + +void PreKeyWhisperMessage::SharedDtor() { + if (basekey_ != &::google::protobuf::internal::kEmptyString) { + delete basekey_; + } + if (identitykey_ != &::google::protobuf::internal::kEmptyString) { + delete identitykey_; + } + if (message_ != &::google::protobuf::internal::kEmptyString) { + delete message_; + } + if (this != default_instance_) { + } +} + +void PreKeyWhisperMessage::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* PreKeyWhisperMessage::descriptor() { + protobuf_AssignDescriptorsOnce(); + return PreKeyWhisperMessage_descriptor_; +} + +const PreKeyWhisperMessage& PreKeyWhisperMessage::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_PreKeyWhisperMessage_2eproto(); + return *default_instance_; +} + +PreKeyWhisperMessage* PreKeyWhisperMessage::default_instance_ = NULL; + +PreKeyWhisperMessage* PreKeyWhisperMessage::New() const { + return new PreKeyWhisperMessage; +} + +void PreKeyWhisperMessage::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + prekeyid_ = 0u; + if (has_basekey()) { + if (basekey_ != &::google::protobuf::internal::kEmptyString) { + basekey_->clear(); + } + } + if (has_identitykey()) { + if (identitykey_ != &::google::protobuf::internal::kEmptyString) { + identitykey_->clear(); + } + } + if (has_message()) { + if (message_ != &::google::protobuf::internal::kEmptyString) { + message_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool PreKeyWhisperMessage::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint32 preKeyId = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &prekeyid_))); + set_has_prekeyid(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_baseKey; + break; + } + + // optional bytes baseKey = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_baseKey: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_basekey())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_identityKey; + break; + } + + // optional bytes identityKey = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_identityKey: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_identitykey())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_message; + break; + } + + // optional bytes message = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_message: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_message())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void PreKeyWhisperMessage::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional uint32 preKeyId = 1; + if (has_prekeyid()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(1, this->prekeyid(), output); + } + + // optional bytes baseKey = 2; + if (has_basekey()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 2, this->basekey(), output); + } + + // optional bytes identityKey = 3; + if (has_identitykey()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 3, this->identitykey(), output); + } + + // optional bytes message = 4; + if (has_message()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 4, this->message(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* PreKeyWhisperMessage::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional uint32 preKeyId = 1; + if (has_prekeyid()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(1, this->prekeyid(), target); + } + + // optional bytes baseKey = 2; + if (has_basekey()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 2, this->basekey(), target); + } + + // optional bytes identityKey = 3; + if (has_identitykey()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 3, this->identitykey(), target); + } + + // optional bytes message = 4; + if (has_message()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 4, this->message(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int PreKeyWhisperMessage::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional uint32 preKeyId = 1; + if (has_prekeyid()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->prekeyid()); + } + + // optional bytes baseKey = 2; + if (has_basekey()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->basekey()); + } + + // optional bytes identityKey = 3; + if (has_identitykey()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->identitykey()); + } + + // optional bytes message = 4; + if (has_message()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->message()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void PreKeyWhisperMessage::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const PreKeyWhisperMessage* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void PreKeyWhisperMessage::MergeFrom(const PreKeyWhisperMessage& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_prekeyid()) { + set_prekeyid(from.prekeyid()); + } + if (from.has_basekey()) { + set_basekey(from.basekey()); + } + if (from.has_identitykey()) { + set_identitykey(from.identitykey()); + } + if (from.has_message()) { + set_message(from.message()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void PreKeyWhisperMessage::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void PreKeyWhisperMessage::CopyFrom(const PreKeyWhisperMessage& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PreKeyWhisperMessage::IsInitialized() const { + + return true; +} + +void PreKeyWhisperMessage::Swap(PreKeyWhisperMessage* other) { + if (other != this) { + std::swap(prekeyid_, other->prekeyid_); + std::swap(basekey_, other->basekey_); + std::swap(identitykey_, other->identitykey_); + std::swap(message_, other->message_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata PreKeyWhisperMessage::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = PreKeyWhisperMessage_descriptor_; + metadata.reflection = PreKeyWhisperMessage_reflection_; + return metadata; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +// @@protoc_insertion_point(global_scope) diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.proto b/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.proto new file mode 100644 index 0000000..4d83b6c --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/PreKeyWhisperMessage.proto @@ -0,0 +1,9 @@ +package textsecure; + +message PreKeyWhisperMessage { + optional uint32 preKeyId = 1; + optional bytes baseKey = 2; + optional bytes identityKey = 3; + optional bytes message = 4; // Encrypted PushMessageContent +} + diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.pb.hh b/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.pb.hh new file mode 100644 index 0000000..bd31d3a --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.pb.hh @@ -0,0 +1,1108 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: PushMessageContent.proto + +#ifndef PROTOBUF_PushMessageContent_2eproto__INCLUDED +#define PROTOBUF_PushMessageContent_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2005000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_PushMessageContent_2eproto(); +void protobuf_AssignDesc_PushMessageContent_2eproto(); +void protobuf_ShutdownFile_PushMessageContent_2eproto(); + +class PushMessageContent; +class PushMessageContent_AttachmentPointer; +class PushMessageContent_GroupContext; + +enum PushMessageContent_GroupContext_Type { + PushMessageContent_GroupContext_Type_UNKNOWN = 0, + PushMessageContent_GroupContext_Type_UPDATE = 1, + PushMessageContent_GroupContext_Type_DELIVER = 2, + PushMessageContent_GroupContext_Type_QUIT = 3 +}; +bool PushMessageContent_GroupContext_Type_IsValid(int value); +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext_Type_Type_MIN = PushMessageContent_GroupContext_Type_UNKNOWN; +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext_Type_Type_MAX = PushMessageContent_GroupContext_Type_QUIT; +const int PushMessageContent_GroupContext_Type_Type_ARRAYSIZE = PushMessageContent_GroupContext_Type_Type_MAX + 1; + +const ::google::protobuf::EnumDescriptor* PushMessageContent_GroupContext_Type_descriptor(); +inline const ::std::string& PushMessageContent_GroupContext_Type_Name(PushMessageContent_GroupContext_Type value) { + return ::google::protobuf::internal::NameOfEnum( + PushMessageContent_GroupContext_Type_descriptor(), value); +} +inline bool PushMessageContent_GroupContext_Type_Parse( + const ::std::string& name, PushMessageContent_GroupContext_Type* value) { + return ::google::protobuf::internal::ParseNamedEnum( + PushMessageContent_GroupContext_Type_descriptor(), name, value); +} +enum PushMessageContent_Flags { + PushMessageContent_Flags_END_SESSION = 1 +}; +bool PushMessageContent_Flags_IsValid(int value); +const PushMessageContent_Flags PushMessageContent_Flags_Flags_MIN = PushMessageContent_Flags_END_SESSION; +const PushMessageContent_Flags PushMessageContent_Flags_Flags_MAX = PushMessageContent_Flags_END_SESSION; +const int PushMessageContent_Flags_Flags_ARRAYSIZE = PushMessageContent_Flags_Flags_MAX + 1; + +const ::google::protobuf::EnumDescriptor* PushMessageContent_Flags_descriptor(); +inline const ::std::string& PushMessageContent_Flags_Name(PushMessageContent_Flags value) { + return ::google::protobuf::internal::NameOfEnum( + PushMessageContent_Flags_descriptor(), value); +} +inline bool PushMessageContent_Flags_Parse( + const ::std::string& name, PushMessageContent_Flags* value) { + return ::google::protobuf::internal::ParseNamedEnum( + PushMessageContent_Flags_descriptor(), name, value); +} +// =================================================================== + +class PushMessageContent_AttachmentPointer : public ::google::protobuf::Message { + public: + PushMessageContent_AttachmentPointer(); + virtual ~PushMessageContent_AttachmentPointer(); + + PushMessageContent_AttachmentPointer(const PushMessageContent_AttachmentPointer& from); + + inline PushMessageContent_AttachmentPointer& operator=(const PushMessageContent_AttachmentPointer& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const PushMessageContent_AttachmentPointer& default_instance(); + + void Swap(PushMessageContent_AttachmentPointer* other); + + // implements Message ---------------------------------------------- + + PushMessageContent_AttachmentPointer* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const PushMessageContent_AttachmentPointer& from); + void MergeFrom(const PushMessageContent_AttachmentPointer& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional fixed64 id = 1; + inline bool has_id() const; + inline void clear_id(); + static const int kIdFieldNumber = 1; + inline ::google::protobuf::uint64 id() const; + inline void set_id(::google::protobuf::uint64 value); + + // optional string contentType = 2; + inline bool has_contenttype() const; + inline void clear_contenttype(); + static const int kContentTypeFieldNumber = 2; + inline const ::std::string& contenttype() const; + inline void set_contenttype(const ::std::string& value); + inline void set_contenttype(const char* value); + inline void set_contenttype(const char* value, size_t size); + inline ::std::string* mutable_contenttype(); + inline ::std::string* release_contenttype(); + inline void set_allocated_contenttype(::std::string* contenttype); + + // optional bytes key = 3; + inline bool has_key() const; + inline void clear_key(); + static const int kKeyFieldNumber = 3; + inline const ::std::string& key() const; + inline void set_key(const ::std::string& value); + inline void set_key(const char* value); + inline void set_key(const void* value, size_t size); + inline ::std::string* mutable_key(); + inline ::std::string* release_key(); + inline void set_allocated_key(::std::string* key); + + // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.AttachmentPointer) + private: + inline void set_has_id(); + inline void clear_has_id(); + inline void set_has_contenttype(); + inline void clear_has_contenttype(); + inline void set_has_key(); + inline void clear_has_key(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::uint64 id_; + ::std::string* contenttype_; + ::std::string* key_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + friend void protobuf_AddDesc_PushMessageContent_2eproto(); + friend void protobuf_AssignDesc_PushMessageContent_2eproto(); + friend void protobuf_ShutdownFile_PushMessageContent_2eproto(); + + void InitAsDefaultInstance(); + static PushMessageContent_AttachmentPointer* default_instance_; +}; +// ------------------------------------------------------------------- + +class PushMessageContent_GroupContext : public ::google::protobuf::Message { + public: + PushMessageContent_GroupContext(); + virtual ~PushMessageContent_GroupContext(); + + PushMessageContent_GroupContext(const PushMessageContent_GroupContext& from); + + inline PushMessageContent_GroupContext& operator=(const PushMessageContent_GroupContext& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const PushMessageContent_GroupContext& default_instance(); + + void Swap(PushMessageContent_GroupContext* other); + + // implements Message ---------------------------------------------- + + PushMessageContent_GroupContext* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const PushMessageContent_GroupContext& from); + void MergeFrom(const PushMessageContent_GroupContext& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef PushMessageContent_GroupContext_Type Type; + static const Type UNKNOWN = PushMessageContent_GroupContext_Type_UNKNOWN; + static const Type UPDATE = PushMessageContent_GroupContext_Type_UPDATE; + static const Type DELIVER = PushMessageContent_GroupContext_Type_DELIVER; + static const Type QUIT = PushMessageContent_GroupContext_Type_QUIT; + static inline bool Type_IsValid(int value) { + return PushMessageContent_GroupContext_Type_IsValid(value); + } + static const Type Type_MIN = + PushMessageContent_GroupContext_Type_Type_MIN; + static const Type Type_MAX = + PushMessageContent_GroupContext_Type_Type_MAX; + static const int Type_ARRAYSIZE = + PushMessageContent_GroupContext_Type_Type_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + Type_descriptor() { + return PushMessageContent_GroupContext_Type_descriptor(); + } + static inline const ::std::string& Type_Name(Type value) { + return PushMessageContent_GroupContext_Type_Name(value); + } + static inline bool Type_Parse(const ::std::string& name, + Type* value) { + return PushMessageContent_GroupContext_Type_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional bytes id = 1; + inline bool has_id() const; + inline void clear_id(); + static const int kIdFieldNumber = 1; + inline const ::std::string& id() const; + inline void set_id(const ::std::string& value); + inline void set_id(const char* value); + inline void set_id(const void* value, size_t size); + inline ::std::string* mutable_id(); + inline ::std::string* release_id(); + inline void set_allocated_id(::std::string* id); + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + inline bool has_type() const; + inline void clear_type(); + static const int kTypeFieldNumber = 2; + inline ::textsecure::PushMessageContent_GroupContext_Type type() const; + inline void set_type(::textsecure::PushMessageContent_GroupContext_Type value); + + // optional string name = 3; + inline bool has_name() const; + inline void clear_name(); + static const int kNameFieldNumber = 3; + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline void set_name(const char* value, size_t size); + inline ::std::string* mutable_name(); + inline ::std::string* release_name(); + inline void set_allocated_name(::std::string* name); + + // repeated string members = 4; + inline int members_size() const; + inline void clear_members(); + static const int kMembersFieldNumber = 4; + inline const ::std::string& members(int index) const; + inline ::std::string* mutable_members(int index); + inline void set_members(int index, const ::std::string& value); + inline void set_members(int index, const char* value); + inline void set_members(int index, const char* value, size_t size); + inline ::std::string* add_members(); + inline void add_members(const ::std::string& value); + inline void add_members(const char* value); + inline void add_members(const char* value, size_t size); + inline const ::google::protobuf::RepeatedPtrField< ::std::string>& members() const; + inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_members(); + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + inline bool has_avatar() const; + inline void clear_avatar(); + static const int kAvatarFieldNumber = 5; + inline const ::textsecure::PushMessageContent_AttachmentPointer& avatar() const; + inline ::textsecure::PushMessageContent_AttachmentPointer* mutable_avatar(); + inline ::textsecure::PushMessageContent_AttachmentPointer* release_avatar(); + inline void set_allocated_avatar(::textsecure::PushMessageContent_AttachmentPointer* avatar); + + // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent.GroupContext) + private: + inline void set_has_id(); + inline void clear_has_id(); + inline void set_has_type(); + inline void clear_has_type(); + inline void set_has_name(); + inline void clear_has_name(); + inline void set_has_avatar(); + inline void clear_has_avatar(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* id_; + ::std::string* name_; + ::google::protobuf::RepeatedPtrField< ::std::string> members_; + ::textsecure::PushMessageContent_AttachmentPointer* avatar_; + int type_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32]; + + friend void protobuf_AddDesc_PushMessageContent_2eproto(); + friend void protobuf_AssignDesc_PushMessageContent_2eproto(); + friend void protobuf_ShutdownFile_PushMessageContent_2eproto(); + + void InitAsDefaultInstance(); + static PushMessageContent_GroupContext* default_instance_; +}; +// ------------------------------------------------------------------- + +class PushMessageContent : public ::google::protobuf::Message { + public: + PushMessageContent(); + virtual ~PushMessageContent(); + + PushMessageContent(const PushMessageContent& from); + + inline PushMessageContent& operator=(const PushMessageContent& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const PushMessageContent& default_instance(); + + void Swap(PushMessageContent* other); + + // implements Message ---------------------------------------------- + + PushMessageContent* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const PushMessageContent& from); + void MergeFrom(const PushMessageContent& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef PushMessageContent_AttachmentPointer AttachmentPointer; + typedef PushMessageContent_GroupContext GroupContext; + + typedef PushMessageContent_Flags Flags; + static const Flags END_SESSION = PushMessageContent_Flags_END_SESSION; + static inline bool Flags_IsValid(int value) { + return PushMessageContent_Flags_IsValid(value); + } + static const Flags Flags_MIN = + PushMessageContent_Flags_Flags_MIN; + static const Flags Flags_MAX = + PushMessageContent_Flags_Flags_MAX; + static const int Flags_ARRAYSIZE = + PushMessageContent_Flags_Flags_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + Flags_descriptor() { + return PushMessageContent_Flags_descriptor(); + } + static inline const ::std::string& Flags_Name(Flags value) { + return PushMessageContent_Flags_Name(value); + } + static inline bool Flags_Parse(const ::std::string& name, + Flags* value) { + return PushMessageContent_Flags_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + // optional string body = 1; + inline bool has_body() const; + inline void clear_body(); + static const int kBodyFieldNumber = 1; + inline const ::std::string& body() const; + inline void set_body(const ::std::string& value); + inline void set_body(const char* value); + inline void set_body(const char* value, size_t size); + inline ::std::string* mutable_body(); + inline ::std::string* release_body(); + inline void set_allocated_body(::std::string* body); + + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + inline int attachments_size() const; + inline void clear_attachments(); + static const int kAttachmentsFieldNumber = 2; + inline const ::textsecure::PushMessageContent_AttachmentPointer& attachments(int index) const; + inline ::textsecure::PushMessageContent_AttachmentPointer* mutable_attachments(int index); + inline ::textsecure::PushMessageContent_AttachmentPointer* add_attachments(); + inline const ::google::protobuf::RepeatedPtrField< ::textsecure::PushMessageContent_AttachmentPointer >& + attachments() const; + inline ::google::protobuf::RepeatedPtrField< ::textsecure::PushMessageContent_AttachmentPointer >* + mutable_attachments(); + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + inline bool has_group() const; + inline void clear_group(); + static const int kGroupFieldNumber = 3; + inline const ::textsecure::PushMessageContent_GroupContext& group() const; + inline ::textsecure::PushMessageContent_GroupContext* mutable_group(); + inline ::textsecure::PushMessageContent_GroupContext* release_group(); + inline void set_allocated_group(::textsecure::PushMessageContent_GroupContext* group); + + // optional uint32 flags = 4; + inline bool has_flags() const; + inline void clear_flags(); + static const int kFlagsFieldNumber = 4; + inline ::google::protobuf::uint32 flags() const; + inline void set_flags(::google::protobuf::uint32 value); + + // @@protoc_insertion_point(class_scope:textsecure.PushMessageContent) + private: + inline void set_has_body(); + inline void clear_has_body(); + inline void set_has_group(); + inline void clear_has_group(); + inline void set_has_flags(); + inline void clear_has_flags(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* body_; + ::google::protobuf::RepeatedPtrField< ::textsecure::PushMessageContent_AttachmentPointer > attachments_; + ::textsecure::PushMessageContent_GroupContext* group_; + ::google::protobuf::uint32 flags_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_PushMessageContent_2eproto(); + friend void protobuf_AssignDesc_PushMessageContent_2eproto(); + friend void protobuf_ShutdownFile_PushMessageContent_2eproto(); + + void InitAsDefaultInstance(); + static PushMessageContent* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// PushMessageContent_AttachmentPointer + +// optional fixed64 id = 1; +inline bool PushMessageContent_AttachmentPointer::has_id() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void PushMessageContent_AttachmentPointer::set_has_id() { + _has_bits_[0] |= 0x00000001u; +} +inline void PushMessageContent_AttachmentPointer::clear_has_id() { + _has_bits_[0] &= ~0x00000001u; +} +inline void PushMessageContent_AttachmentPointer::clear_id() { + id_ = GOOGLE_ULONGLONG(0); + clear_has_id(); +} +inline ::google::protobuf::uint64 PushMessageContent_AttachmentPointer::id() const { + return id_; +} +inline void PushMessageContent_AttachmentPointer::set_id(::google::protobuf::uint64 value) { + set_has_id(); + id_ = value; +} + +// optional string contentType = 2; +inline bool PushMessageContent_AttachmentPointer::has_contenttype() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void PushMessageContent_AttachmentPointer::set_has_contenttype() { + _has_bits_[0] |= 0x00000002u; +} +inline void PushMessageContent_AttachmentPointer::clear_has_contenttype() { + _has_bits_[0] &= ~0x00000002u; +} +inline void PushMessageContent_AttachmentPointer::clear_contenttype() { + if (contenttype_ != &::google::protobuf::internal::kEmptyString) { + contenttype_->clear(); + } + clear_has_contenttype(); +} +inline const ::std::string& PushMessageContent_AttachmentPointer::contenttype() const { + return *contenttype_; +} +inline void PushMessageContent_AttachmentPointer::set_contenttype(const ::std::string& value) { + set_has_contenttype(); + if (contenttype_ == &::google::protobuf::internal::kEmptyString) { + contenttype_ = new ::std::string; + } + contenttype_->assign(value); +} +inline void PushMessageContent_AttachmentPointer::set_contenttype(const char* value) { + set_has_contenttype(); + if (contenttype_ == &::google::protobuf::internal::kEmptyString) { + contenttype_ = new ::std::string; + } + contenttype_->assign(value); +} +inline void PushMessageContent_AttachmentPointer::set_contenttype(const char* value, size_t size) { + set_has_contenttype(); + if (contenttype_ == &::google::protobuf::internal::kEmptyString) { + contenttype_ = new ::std::string; + } + contenttype_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PushMessageContent_AttachmentPointer::mutable_contenttype() { + set_has_contenttype(); + if (contenttype_ == &::google::protobuf::internal::kEmptyString) { + contenttype_ = new ::std::string; + } + return contenttype_; +} +inline ::std::string* PushMessageContent_AttachmentPointer::release_contenttype() { + clear_has_contenttype(); + if (contenttype_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = contenttype_; + contenttype_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PushMessageContent_AttachmentPointer::set_allocated_contenttype(::std::string* contenttype) { + if (contenttype_ != &::google::protobuf::internal::kEmptyString) { + delete contenttype_; + } + if (contenttype) { + set_has_contenttype(); + contenttype_ = contenttype; + } else { + clear_has_contenttype(); + contenttype_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional bytes key = 3; +inline bool PushMessageContent_AttachmentPointer::has_key() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void PushMessageContent_AttachmentPointer::set_has_key() { + _has_bits_[0] |= 0x00000004u; +} +inline void PushMessageContent_AttachmentPointer::clear_has_key() { + _has_bits_[0] &= ~0x00000004u; +} +inline void PushMessageContent_AttachmentPointer::clear_key() { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + clear_has_key(); +} +inline const ::std::string& PushMessageContent_AttachmentPointer::key() const { + return *key_; +} +inline void PushMessageContent_AttachmentPointer::set_key(const ::std::string& value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void PushMessageContent_AttachmentPointer::set_key(const char* value) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(value); +} +inline void PushMessageContent_AttachmentPointer::set_key(const void* value, size_t size) { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + key_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PushMessageContent_AttachmentPointer::mutable_key() { + set_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + key_ = new ::std::string; + } + return key_; +} +inline ::std::string* PushMessageContent_AttachmentPointer::release_key() { + clear_has_key(); + if (key_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = key_; + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PushMessageContent_AttachmentPointer::set_allocated_key(::std::string* key) { + if (key_ != &::google::protobuf::internal::kEmptyString) { + delete key_; + } + if (key) { + set_has_key(); + key_ = key; + } else { + clear_has_key(); + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// ------------------------------------------------------------------- + +// PushMessageContent_GroupContext + +// optional bytes id = 1; +inline bool PushMessageContent_GroupContext::has_id() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void PushMessageContent_GroupContext::set_has_id() { + _has_bits_[0] |= 0x00000001u; +} +inline void PushMessageContent_GroupContext::clear_has_id() { + _has_bits_[0] &= ~0x00000001u; +} +inline void PushMessageContent_GroupContext::clear_id() { + if (id_ != &::google::protobuf::internal::kEmptyString) { + id_->clear(); + } + clear_has_id(); +} +inline const ::std::string& PushMessageContent_GroupContext::id() const { + return *id_; +} +inline void PushMessageContent_GroupContext::set_id(const ::std::string& value) { + set_has_id(); + if (id_ == &::google::protobuf::internal::kEmptyString) { + id_ = new ::std::string; + } + id_->assign(value); +} +inline void PushMessageContent_GroupContext::set_id(const char* value) { + set_has_id(); + if (id_ == &::google::protobuf::internal::kEmptyString) { + id_ = new ::std::string; + } + id_->assign(value); +} +inline void PushMessageContent_GroupContext::set_id(const void* value, size_t size) { + set_has_id(); + if (id_ == &::google::protobuf::internal::kEmptyString) { + id_ = new ::std::string; + } + id_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PushMessageContent_GroupContext::mutable_id() { + set_has_id(); + if (id_ == &::google::protobuf::internal::kEmptyString) { + id_ = new ::std::string; + } + return id_; +} +inline ::std::string* PushMessageContent_GroupContext::release_id() { + clear_has_id(); + if (id_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = id_; + id_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PushMessageContent_GroupContext::set_allocated_id(::std::string* id) { + if (id_ != &::google::protobuf::internal::kEmptyString) { + delete id_; + } + if (id) { + set_has_id(); + id_ = id; + } else { + clear_has_id(); + id_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional .textsecure.PushMessageContent.GroupContext.Type type = 2; +inline bool PushMessageContent_GroupContext::has_type() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void PushMessageContent_GroupContext::set_has_type() { + _has_bits_[0] |= 0x00000002u; +} +inline void PushMessageContent_GroupContext::clear_has_type() { + _has_bits_[0] &= ~0x00000002u; +} +inline void PushMessageContent_GroupContext::clear_type() { + type_ = 0; + clear_has_type(); +} +inline ::textsecure::PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::type() const { + return static_cast< ::textsecure::PushMessageContent_GroupContext_Type >(type_); +} +inline void PushMessageContent_GroupContext::set_type(::textsecure::PushMessageContent_GroupContext_Type value) { + assert(::textsecure::PushMessageContent_GroupContext_Type_IsValid(value)); + set_has_type(); + type_ = value; +} + +// optional string name = 3; +inline bool PushMessageContent_GroupContext::has_name() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void PushMessageContent_GroupContext::set_has_name() { + _has_bits_[0] |= 0x00000004u; +} +inline void PushMessageContent_GroupContext::clear_has_name() { + _has_bits_[0] &= ~0x00000004u; +} +inline void PushMessageContent_GroupContext::clear_name() { + if (name_ != &::google::protobuf::internal::kEmptyString) { + name_->clear(); + } + clear_has_name(); +} +inline const ::std::string& PushMessageContent_GroupContext::name() const { + return *name_; +} +inline void PushMessageContent_GroupContext::set_name(const ::std::string& value) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void PushMessageContent_GroupContext::set_name(const char* value) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void PushMessageContent_GroupContext::set_name(const char* value, size_t size) { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + name_ = new ::std::string; + } + name_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PushMessageContent_GroupContext::mutable_name() { + set_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + name_ = new ::std::string; + } + return name_; +} +inline ::std::string* PushMessageContent_GroupContext::release_name() { + clear_has_name(); + if (name_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = name_; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PushMessageContent_GroupContext::set_allocated_name(::std::string* name) { + if (name_ != &::google::protobuf::internal::kEmptyString) { + delete name_; + } + if (name) { + set_has_name(); + name_ = name; + } else { + clear_has_name(); + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// repeated string members = 4; +inline int PushMessageContent_GroupContext::members_size() const { + return members_.size(); +} +inline void PushMessageContent_GroupContext::clear_members() { + members_.Clear(); +} +inline const ::std::string& PushMessageContent_GroupContext::members(int index) const { + return members_.Get(index); +} +inline ::std::string* PushMessageContent_GroupContext::mutable_members(int index) { + return members_.Mutable(index); +} +inline void PushMessageContent_GroupContext::set_members(int index, const ::std::string& value) { + members_.Mutable(index)->assign(value); +} +inline void PushMessageContent_GroupContext::set_members(int index, const char* value) { + members_.Mutable(index)->assign(value); +} +inline void PushMessageContent_GroupContext::set_members(int index, const char* value, size_t size) { + members_.Mutable(index)->assign( + reinterpret_cast(value), size); +} +inline ::std::string* PushMessageContent_GroupContext::add_members() { + return members_.Add(); +} +inline void PushMessageContent_GroupContext::add_members(const ::std::string& value) { + members_.Add()->assign(value); +} +inline void PushMessageContent_GroupContext::add_members(const char* value) { + members_.Add()->assign(value); +} +inline void PushMessageContent_GroupContext::add_members(const char* value, size_t size) { + members_.Add()->assign(reinterpret_cast(value), size); +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +PushMessageContent_GroupContext::members() const { + return members_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +PushMessageContent_GroupContext::mutable_members() { + return &members_; +} + +// optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; +inline bool PushMessageContent_GroupContext::has_avatar() const { + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void PushMessageContent_GroupContext::set_has_avatar() { + _has_bits_[0] |= 0x00000010u; +} +inline void PushMessageContent_GroupContext::clear_has_avatar() { + _has_bits_[0] &= ~0x00000010u; +} +inline void PushMessageContent_GroupContext::clear_avatar() { + if (avatar_ != NULL) avatar_->::textsecure::PushMessageContent_AttachmentPointer::Clear(); + clear_has_avatar(); +} +inline const ::textsecure::PushMessageContent_AttachmentPointer& PushMessageContent_GroupContext::avatar() const { + return avatar_ != NULL ? *avatar_ : *default_instance_->avatar_; +} +inline ::textsecure::PushMessageContent_AttachmentPointer* PushMessageContent_GroupContext::mutable_avatar() { + set_has_avatar(); + if (avatar_ == NULL) avatar_ = new ::textsecure::PushMessageContent_AttachmentPointer; + return avatar_; +} +inline ::textsecure::PushMessageContent_AttachmentPointer* PushMessageContent_GroupContext::release_avatar() { + clear_has_avatar(); + ::textsecure::PushMessageContent_AttachmentPointer* temp = avatar_; + avatar_ = NULL; + return temp; +} +inline void PushMessageContent_GroupContext::set_allocated_avatar(::textsecure::PushMessageContent_AttachmentPointer* avatar) { + delete avatar_; + avatar_ = avatar; + if (avatar) { + set_has_avatar(); + } else { + clear_has_avatar(); + } +} + +// ------------------------------------------------------------------- + +// PushMessageContent + +// optional string body = 1; +inline bool PushMessageContent::has_body() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void PushMessageContent::set_has_body() { + _has_bits_[0] |= 0x00000001u; +} +inline void PushMessageContent::clear_has_body() { + _has_bits_[0] &= ~0x00000001u; +} +inline void PushMessageContent::clear_body() { + if (body_ != &::google::protobuf::internal::kEmptyString) { + body_->clear(); + } + clear_has_body(); +} +inline const ::std::string& PushMessageContent::body() const { + return *body_; +} +inline void PushMessageContent::set_body(const ::std::string& value) { + set_has_body(); + if (body_ == &::google::protobuf::internal::kEmptyString) { + body_ = new ::std::string; + } + body_->assign(value); +} +inline void PushMessageContent::set_body(const char* value) { + set_has_body(); + if (body_ == &::google::protobuf::internal::kEmptyString) { + body_ = new ::std::string; + } + body_->assign(value); +} +inline void PushMessageContent::set_body(const char* value, size_t size) { + set_has_body(); + if (body_ == &::google::protobuf::internal::kEmptyString) { + body_ = new ::std::string; + } + body_->assign(reinterpret_cast(value), size); +} +inline ::std::string* PushMessageContent::mutable_body() { + set_has_body(); + if (body_ == &::google::protobuf::internal::kEmptyString) { + body_ = new ::std::string; + } + return body_; +} +inline ::std::string* PushMessageContent::release_body() { + clear_has_body(); + if (body_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = body_; + body_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void PushMessageContent::set_allocated_body(::std::string* body) { + if (body_ != &::google::protobuf::internal::kEmptyString) { + delete body_; + } + if (body) { + set_has_body(); + body_ = body; + } else { + clear_has_body(); + body_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; +inline int PushMessageContent::attachments_size() const { + return attachments_.size(); +} +inline void PushMessageContent::clear_attachments() { + attachments_.Clear(); +} +inline const ::textsecure::PushMessageContent_AttachmentPointer& PushMessageContent::attachments(int index) const { + return attachments_.Get(index); +} +inline ::textsecure::PushMessageContent_AttachmentPointer* PushMessageContent::mutable_attachments(int index) { + return attachments_.Mutable(index); +} +inline ::textsecure::PushMessageContent_AttachmentPointer* PushMessageContent::add_attachments() { + return attachments_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::textsecure::PushMessageContent_AttachmentPointer >& +PushMessageContent::attachments() const { + return attachments_; +} +inline ::google::protobuf::RepeatedPtrField< ::textsecure::PushMessageContent_AttachmentPointer >* +PushMessageContent::mutable_attachments() { + return &attachments_; +} + +// optional .textsecure.PushMessageContent.GroupContext group = 3; +inline bool PushMessageContent::has_group() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void PushMessageContent::set_has_group() { + _has_bits_[0] |= 0x00000004u; +} +inline void PushMessageContent::clear_has_group() { + _has_bits_[0] &= ~0x00000004u; +} +inline void PushMessageContent::clear_group() { + if (group_ != NULL) group_->::textsecure::PushMessageContent_GroupContext::Clear(); + clear_has_group(); +} +inline const ::textsecure::PushMessageContent_GroupContext& PushMessageContent::group() const { + return group_ != NULL ? *group_ : *default_instance_->group_; +} +inline ::textsecure::PushMessageContent_GroupContext* PushMessageContent::mutable_group() { + set_has_group(); + if (group_ == NULL) group_ = new ::textsecure::PushMessageContent_GroupContext; + return group_; +} +inline ::textsecure::PushMessageContent_GroupContext* PushMessageContent::release_group() { + clear_has_group(); + ::textsecure::PushMessageContent_GroupContext* temp = group_; + group_ = NULL; + return temp; +} +inline void PushMessageContent::set_allocated_group(::textsecure::PushMessageContent_GroupContext* group) { + delete group_; + group_ = group; + if (group) { + set_has_group(); + } else { + clear_has_group(); + } +} + +// optional uint32 flags = 4; +inline bool PushMessageContent::has_flags() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void PushMessageContent::set_has_flags() { + _has_bits_[0] |= 0x00000008u; +} +inline void PushMessageContent::clear_has_flags() { + _has_bits_[0] &= ~0x00000008u; +} +inline void PushMessageContent::clear_flags() { + flags_ = 0u; + clear_has_flags(); +} +inline ::google::protobuf::uint32 PushMessageContent::flags() const { + return flags_; +} +inline void PushMessageContent::set_flags(::google::protobuf::uint32 value) { + set_has_flags(); + flags_ = value; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +#ifndef SWIG +namespace google { +namespace protobuf { + +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::textsecure::PushMessageContent_GroupContext_Type>() { + return ::textsecure::PushMessageContent_GroupContext_Type_descriptor(); +} +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::textsecure::PushMessageContent_Flags>() { + return ::textsecure::PushMessageContent_Flags_descriptor(); +} + +} // namespace google +} // namespace protobuf +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_PushMessageContent_2eproto__INCLUDED diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.pb.mm b/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.pb.mm new file mode 100644 index 0000000..c966d0d --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.pb.mm @@ -0,0 +1,1291 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: PushMessageContent.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "PushMessageContent.pb.hh" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +namespace { + +const ::google::protobuf::Descriptor* PushMessageContent_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + PushMessageContent_reflection_ = NULL; +const ::google::protobuf::Descriptor* PushMessageContent_AttachmentPointer_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + PushMessageContent_AttachmentPointer_reflection_ = NULL; +const ::google::protobuf::Descriptor* PushMessageContent_GroupContext_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + PushMessageContent_GroupContext_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* PushMessageContent_GroupContext_Type_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* PushMessageContent_Flags_descriptor_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_PushMessageContent_2eproto() { + protobuf_AddDesc_PushMessageContent_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "PushMessageContent.proto"); + GOOGLE_CHECK(file != NULL); + PushMessageContent_descriptor_ = file->message_type(0); + static const int PushMessageContent_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent, body_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent, attachments_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent, group_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent, flags_), + }; + PushMessageContent_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + PushMessageContent_descriptor_, + PushMessageContent::default_instance_, + PushMessageContent_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(PushMessageContent)); + PushMessageContent_AttachmentPointer_descriptor_ = PushMessageContent_descriptor_->nested_type(0); + static const int PushMessageContent_AttachmentPointer_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_AttachmentPointer, id_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_AttachmentPointer, contenttype_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_AttachmentPointer, key_), + }; + PushMessageContent_AttachmentPointer_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + PushMessageContent_AttachmentPointer_descriptor_, + PushMessageContent_AttachmentPointer::default_instance_, + PushMessageContent_AttachmentPointer_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_AttachmentPointer, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_AttachmentPointer, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(PushMessageContent_AttachmentPointer)); + PushMessageContent_GroupContext_descriptor_ = PushMessageContent_descriptor_->nested_type(1); + static const int PushMessageContent_GroupContext_offsets_[5] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, id_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, members_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, avatar_), + }; + PushMessageContent_GroupContext_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + PushMessageContent_GroupContext_descriptor_, + PushMessageContent_GroupContext::default_instance_, + PushMessageContent_GroupContext_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(PushMessageContent_GroupContext, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(PushMessageContent_GroupContext)); + PushMessageContent_GroupContext_Type_descriptor_ = PushMessageContent_GroupContext_descriptor_->enum_type(0); + PushMessageContent_Flags_descriptor_ = PushMessageContent_descriptor_->enum_type(0); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_PushMessageContent_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + PushMessageContent_descriptor_, &PushMessageContent::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + PushMessageContent_AttachmentPointer_descriptor_, &PushMessageContent_AttachmentPointer::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + PushMessageContent_GroupContext_descriptor_, &PushMessageContent_GroupContext::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_PushMessageContent_2eproto() { + delete PushMessageContent::default_instance_; + delete PushMessageContent_reflection_; + delete PushMessageContent_AttachmentPointer::default_instance_; + delete PushMessageContent_AttachmentPointer_reflection_; + delete PushMessageContent_GroupContext::default_instance_; + delete PushMessageContent_GroupContext_reflection_; +} + +void protobuf_AddDesc_PushMessageContent_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\030PushMessageContent.proto\022\ntextsecure\"\207" + "\004\n\022PushMessageContent\022\014\n\004body\030\001 \001(\t\022E\n\013a" + "ttachments\030\002 \003(\01320.textsecure.PushMessag" + "eContent.AttachmentPointer\022:\n\005group\030\003 \001(" + "\0132+.textsecure.PushMessageContent.GroupC" + "ontext\022\r\n\005flags\030\004 \001(\r\032A\n\021AttachmentPoint" + "er\022\n\n\002id\030\001 \001(\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003k" + "ey\030\003 \001(\014\032\363\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022>\n" + "\004type\030\002 \001(\01620.textsecure.PushMessageCont" + "ent.GroupContext.Type\022\014\n\004name\030\003 \001(\t\022\017\n\007m" + "embers\030\004 \003(\t\022@\n\006avatar\030\005 \001(\01320.textsecur" + "e.PushMessageContent.AttachmentPointer\"6" + "\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001\022\013\n\007DELIV" + "ER\020\002\022\010\n\004QUIT\020\003\"\030\n\005Flags\022\017\n\013END_SESSION\020\001", 560); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "PushMessageContent.proto", &protobuf_RegisterTypes); + PushMessageContent::default_instance_ = new PushMessageContent(); + PushMessageContent_AttachmentPointer::default_instance_ = new PushMessageContent_AttachmentPointer(); + PushMessageContent_GroupContext::default_instance_ = new PushMessageContent_GroupContext(); + PushMessageContent::default_instance_->InitAsDefaultInstance(); + PushMessageContent_AttachmentPointer::default_instance_->InitAsDefaultInstance(); + PushMessageContent_GroupContext::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_PushMessageContent_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_PushMessageContent_2eproto { + StaticDescriptorInitializer_PushMessageContent_2eproto() { + protobuf_AddDesc_PushMessageContent_2eproto(); + } +} static_descriptor_initializer_PushMessageContent_2eproto_; + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* PushMessageContent_Flags_descriptor() { + protobuf_AssignDescriptorsOnce(); + return PushMessageContent_Flags_descriptor_; +} +bool PushMessageContent_Flags_IsValid(int value) { + switch(value) { + case 1: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const PushMessageContent_Flags PushMessageContent::END_SESSION; +const PushMessageContent_Flags PushMessageContent::Flags_MIN; +const PushMessageContent_Flags PushMessageContent::Flags_MAX; +const int PushMessageContent::Flags_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int PushMessageContent_AttachmentPointer::kIdFieldNumber; +const int PushMessageContent_AttachmentPointer::kContentTypeFieldNumber; +const int PushMessageContent_AttachmentPointer::kKeyFieldNumber; +#endif // !_MSC_VER + +PushMessageContent_AttachmentPointer::PushMessageContent_AttachmentPointer() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void PushMessageContent_AttachmentPointer::InitAsDefaultInstance() { +} + +PushMessageContent_AttachmentPointer::PushMessageContent_AttachmentPointer(const PushMessageContent_AttachmentPointer& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void PushMessageContent_AttachmentPointer::SharedCtor() { + _cached_size_ = 0; + id_ = GOOGLE_ULONGLONG(0); + contenttype_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + key_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +PushMessageContent_AttachmentPointer::~PushMessageContent_AttachmentPointer() { + SharedDtor(); +} + +void PushMessageContent_AttachmentPointer::SharedDtor() { + if (contenttype_ != &::google::protobuf::internal::kEmptyString) { + delete contenttype_; + } + if (key_ != &::google::protobuf::internal::kEmptyString) { + delete key_; + } + if (this != default_instance_) { + } +} + +void PushMessageContent_AttachmentPointer::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* PushMessageContent_AttachmentPointer::descriptor() { + protobuf_AssignDescriptorsOnce(); + return PushMessageContent_AttachmentPointer_descriptor_; +} + +const PushMessageContent_AttachmentPointer& PushMessageContent_AttachmentPointer::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_PushMessageContent_2eproto(); + return *default_instance_; +} + +PushMessageContent_AttachmentPointer* PushMessageContent_AttachmentPointer::default_instance_ = NULL; + +PushMessageContent_AttachmentPointer* PushMessageContent_AttachmentPointer::New() const { + return new PushMessageContent_AttachmentPointer; +} + +void PushMessageContent_AttachmentPointer::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + id_ = GOOGLE_ULONGLONG(0); + if (has_contenttype()) { + if (contenttype_ != &::google::protobuf::internal::kEmptyString) { + contenttype_->clear(); + } + } + if (has_key()) { + if (key_ != &::google::protobuf::internal::kEmptyString) { + key_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool PushMessageContent_AttachmentPointer::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional fixed64 id = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_FIXED64>( + input, &id_))); + set_has_id(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_contentType; + break; + } + + // optional string contentType = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_contentType: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_contenttype())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->contenttype().data(), this->contenttype().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_key; + break; + } + + // optional bytes key = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_key: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_key())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void PushMessageContent_AttachmentPointer::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional fixed64 id = 1; + if (has_id()) { + ::google::protobuf::internal::WireFormatLite::WriteFixed64(1, this->id(), output); + } + + // optional string contentType = 2; + if (has_contenttype()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->contenttype().data(), this->contenttype().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 2, this->contenttype(), output); + } + + // optional bytes key = 3; + if (has_key()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 3, this->key(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* PushMessageContent_AttachmentPointer::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional fixed64 id = 1; + if (has_id()) { + target = ::google::protobuf::internal::WireFormatLite::WriteFixed64ToArray(1, this->id(), target); + } + + // optional string contentType = 2; + if (has_contenttype()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->contenttype().data(), this->contenttype().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 2, this->contenttype(), target); + } + + // optional bytes key = 3; + if (has_key()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 3, this->key(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int PushMessageContent_AttachmentPointer::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional fixed64 id = 1; + if (has_id()) { + total_size += 1 + 8; + } + + // optional string contentType = 2; + if (has_contenttype()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->contenttype()); + } + + // optional bytes key = 3; + if (has_key()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->key()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void PushMessageContent_AttachmentPointer::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const PushMessageContent_AttachmentPointer* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void PushMessageContent_AttachmentPointer::MergeFrom(const PushMessageContent_AttachmentPointer& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_id()) { + set_id(from.id()); + } + if (from.has_contenttype()) { + set_contenttype(from.contenttype()); + } + if (from.has_key()) { + set_key(from.key()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void PushMessageContent_AttachmentPointer::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void PushMessageContent_AttachmentPointer::CopyFrom(const PushMessageContent_AttachmentPointer& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PushMessageContent_AttachmentPointer::IsInitialized() const { + + return true; +} + +void PushMessageContent_AttachmentPointer::Swap(PushMessageContent_AttachmentPointer* other) { + if (other != this) { + std::swap(id_, other->id_); + std::swap(contenttype_, other->contenttype_); + std::swap(key_, other->key_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata PushMessageContent_AttachmentPointer::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = PushMessageContent_AttachmentPointer_descriptor_; + metadata.reflection = PushMessageContent_AttachmentPointer_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +const ::google::protobuf::EnumDescriptor* PushMessageContent_GroupContext_Type_descriptor() { + protobuf_AssignDescriptorsOnce(); + return PushMessageContent_GroupContext_Type_descriptor_; +} +bool PushMessageContent_GroupContext_Type_IsValid(int value) { + switch(value) { + case 0: + case 1: + case 2: + case 3: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::UNKNOWN; +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::UPDATE; +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::DELIVER; +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::QUIT; +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::Type_MIN; +const PushMessageContent_GroupContext_Type PushMessageContent_GroupContext::Type_MAX; +const int PushMessageContent_GroupContext::Type_ARRAYSIZE; +#endif // _MSC_VER +#ifndef _MSC_VER +const int PushMessageContent_GroupContext::kIdFieldNumber; +const int PushMessageContent_GroupContext::kTypeFieldNumber; +const int PushMessageContent_GroupContext::kNameFieldNumber; +const int PushMessageContent_GroupContext::kMembersFieldNumber; +const int PushMessageContent_GroupContext::kAvatarFieldNumber; +#endif // !_MSC_VER + +PushMessageContent_GroupContext::PushMessageContent_GroupContext() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void PushMessageContent_GroupContext::InitAsDefaultInstance() { + avatar_ = const_cast< ::textsecure::PushMessageContent_AttachmentPointer*>(&::textsecure::PushMessageContent_AttachmentPointer::default_instance()); +} + +PushMessageContent_GroupContext::PushMessageContent_GroupContext(const PushMessageContent_GroupContext& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void PushMessageContent_GroupContext::SharedCtor() { + _cached_size_ = 0; + id_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + type_ = 0; + name_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + avatar_ = NULL; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +PushMessageContent_GroupContext::~PushMessageContent_GroupContext() { + SharedDtor(); +} + +void PushMessageContent_GroupContext::SharedDtor() { + if (id_ != &::google::protobuf::internal::kEmptyString) { + delete id_; + } + if (name_ != &::google::protobuf::internal::kEmptyString) { + delete name_; + } + if (this != default_instance_) { + delete avatar_; + } +} + +void PushMessageContent_GroupContext::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* PushMessageContent_GroupContext::descriptor() { + protobuf_AssignDescriptorsOnce(); + return PushMessageContent_GroupContext_descriptor_; +} + +const PushMessageContent_GroupContext& PushMessageContent_GroupContext::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_PushMessageContent_2eproto(); + return *default_instance_; +} + +PushMessageContent_GroupContext* PushMessageContent_GroupContext::default_instance_ = NULL; + +PushMessageContent_GroupContext* PushMessageContent_GroupContext::New() const { + return new PushMessageContent_GroupContext; +} + +void PushMessageContent_GroupContext::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (has_id()) { + if (id_ != &::google::protobuf::internal::kEmptyString) { + id_->clear(); + } + } + type_ = 0; + if (has_name()) { + if (name_ != &::google::protobuf::internal::kEmptyString) { + name_->clear(); + } + } + if (has_avatar()) { + if (avatar_ != NULL) avatar_->::textsecure::PushMessageContent_AttachmentPointer::Clear(); + } + } + members_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool PushMessageContent_GroupContext::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional bytes id = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_id())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_type; + break; + } + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_type: + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::textsecure::PushMessageContent_GroupContext_Type_IsValid(value)) { + set_type(static_cast< ::textsecure::PushMessageContent_GroupContext_Type >(value)); + } else { + mutable_unknown_fields()->AddVarint(2, value); + } + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(26)) goto parse_name; + break; + } + + // optional string name = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_name: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_name())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->name().data(), this->name().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_members; + break; + } + + // repeated string members = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_members: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_members())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->members(this->members_size() - 1).data(), + this->members(this->members_size() - 1).length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_members; + if (input->ExpectTag(42)) goto parse_avatar; + break; + } + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + case 5: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_avatar: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_avatar())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void PushMessageContent_GroupContext::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional bytes id = 1; + if (has_id()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 1, this->id(), output); + } + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + if (has_type()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 2, this->type(), output); + } + + // optional string name = 3; + if (has_name()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->name().data(), this->name().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 3, this->name(), output); + } + + // repeated string members = 4; + for (int i = 0; i < this->members_size(); i++) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->members(i).data(), this->members(i).length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 4, this->members(i), output); + } + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + if (has_avatar()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 5, this->avatar(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* PushMessageContent_GroupContext::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional bytes id = 1; + if (has_id()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 1, this->id(), target); + } + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + if (has_type()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 2, this->type(), target); + } + + // optional string name = 3; + if (has_name()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->name().data(), this->name().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 3, this->name(), target); + } + + // repeated string members = 4; + for (int i = 0; i < this->members_size(); i++) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->members(i).data(), this->members(i).length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(4, this->members(i), target); + } + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + if (has_avatar()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 5, this->avatar(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int PushMessageContent_GroupContext::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional bytes id = 1; + if (has_id()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->id()); + } + + // optional .textsecure.PushMessageContent.GroupContext.Type type = 2; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->type()); + } + + // optional string name = 3; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->name()); + } + + // optional .textsecure.PushMessageContent.AttachmentPointer avatar = 5; + if (has_avatar()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->avatar()); + } + + } + // repeated string members = 4; + total_size += 1 * this->members_size(); + for (int i = 0; i < this->members_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->members(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void PushMessageContent_GroupContext::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const PushMessageContent_GroupContext* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void PushMessageContent_GroupContext::MergeFrom(const PushMessageContent_GroupContext& from) { + GOOGLE_CHECK_NE(&from, this); + members_.MergeFrom(from.members_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_id()) { + set_id(from.id()); + } + if (from.has_type()) { + set_type(from.type()); + } + if (from.has_name()) { + set_name(from.name()); + } + if (from.has_avatar()) { + mutable_avatar()->::textsecure::PushMessageContent_AttachmentPointer::MergeFrom(from.avatar()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void PushMessageContent_GroupContext::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void PushMessageContent_GroupContext::CopyFrom(const PushMessageContent_GroupContext& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PushMessageContent_GroupContext::IsInitialized() const { + + return true; +} + +void PushMessageContent_GroupContext::Swap(PushMessageContent_GroupContext* other) { + if (other != this) { + std::swap(id_, other->id_); + std::swap(type_, other->type_); + std::swap(name_, other->name_); + members_.Swap(&other->members_); + std::swap(avatar_, other->avatar_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata PushMessageContent_GroupContext::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = PushMessageContent_GroupContext_descriptor_; + metadata.reflection = PushMessageContent_GroupContext_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int PushMessageContent::kBodyFieldNumber; +const int PushMessageContent::kAttachmentsFieldNumber; +const int PushMessageContent::kGroupFieldNumber; +const int PushMessageContent::kFlagsFieldNumber; +#endif // !_MSC_VER + +PushMessageContent::PushMessageContent() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void PushMessageContent::InitAsDefaultInstance() { + group_ = const_cast< ::textsecure::PushMessageContent_GroupContext*>(&::textsecure::PushMessageContent_GroupContext::default_instance()); +} + +PushMessageContent::PushMessageContent(const PushMessageContent& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void PushMessageContent::SharedCtor() { + _cached_size_ = 0; + body_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + group_ = NULL; + flags_ = 0u; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +PushMessageContent::~PushMessageContent() { + SharedDtor(); +} + +void PushMessageContent::SharedDtor() { + if (body_ != &::google::protobuf::internal::kEmptyString) { + delete body_; + } + if (this != default_instance_) { + delete group_; + } +} + +void PushMessageContent::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* PushMessageContent::descriptor() { + protobuf_AssignDescriptorsOnce(); + return PushMessageContent_descriptor_; +} + +const PushMessageContent& PushMessageContent::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_PushMessageContent_2eproto(); + return *default_instance_; +} + +PushMessageContent* PushMessageContent::default_instance_ = NULL; + +PushMessageContent* PushMessageContent::New() const { + return new PushMessageContent; +} + +void PushMessageContent::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (has_body()) { + if (body_ != &::google::protobuf::internal::kEmptyString) { + body_->clear(); + } + } + if (has_group()) { + if (group_ != NULL) group_->::textsecure::PushMessageContent_GroupContext::Clear(); + } + flags_ = 0u; + } + attachments_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool PushMessageContent::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string body = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_body())); + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->body().data(), this->body().length(), + ::google::protobuf::internal::WireFormat::PARSE); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_attachments; + break; + } + + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_attachments: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_attachments())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(18)) goto parse_attachments; + if (input->ExpectTag(26)) goto parse_group; + break; + } + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_group: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_group())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(32)) goto parse_flags; + break; + } + + // optional uint32 flags = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_flags: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &flags_))); + set_has_flags(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void PushMessageContent::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional string body = 1; + if (has_body()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->body().data(), this->body().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + ::google::protobuf::internal::WireFormatLite::WriteString( + 1, this->body(), output); + } + + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + for (int i = 0; i < this->attachments_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->attachments(i), output); + } + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + if (has_group()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 3, this->group(), output); + } + + // optional uint32 flags = 4; + if (has_flags()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->flags(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* PushMessageContent::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional string body = 1; + if (has_body()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8String( + this->body().data(), this->body().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->body(), target); + } + + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + for (int i = 0; i < this->attachments_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->attachments(i), target); + } + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + if (has_group()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 3, this->group(), target); + } + + // optional uint32 flags = 4; + if (has_flags()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(4, this->flags(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int PushMessageContent::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string body = 1; + if (has_body()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->body()); + } + + // optional .textsecure.PushMessageContent.GroupContext group = 3; + if (has_group()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->group()); + } + + // optional uint32 flags = 4; + if (has_flags()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->flags()); + } + + } + // repeated .textsecure.PushMessageContent.AttachmentPointer attachments = 2; + total_size += 1 * this->attachments_size(); + for (int i = 0; i < this->attachments_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->attachments(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void PushMessageContent::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const PushMessageContent* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void PushMessageContent::MergeFrom(const PushMessageContent& from) { + GOOGLE_CHECK_NE(&from, this); + attachments_.MergeFrom(from.attachments_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_body()) { + set_body(from.body()); + } + if (from.has_group()) { + mutable_group()->::textsecure::PushMessageContent_GroupContext::MergeFrom(from.group()); + } + if (from.has_flags()) { + set_flags(from.flags()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void PushMessageContent::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void PushMessageContent::CopyFrom(const PushMessageContent& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PushMessageContent::IsInitialized() const { + + return true; +} + +void PushMessageContent::Swap(PushMessageContent* other) { + if (other != this) { + std::swap(body_, other->body_); + attachments_.Swap(&other->attachments_); + std::swap(group_, other->group_); + std::swap(flags_, other->flags_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata PushMessageContent::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = PushMessageContent_descriptor_; + metadata.reflection = PushMessageContent_reflection_; + return metadata; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +// @@protoc_insertion_point(global_scope) diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.proto b/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.proto new file mode 100644 index 0000000..6ae3702 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/PushMessageContent.proto @@ -0,0 +1,33 @@ +package textsecure; + + +message PushMessageContent { + message AttachmentPointer { + optional fixed64 id = 1; // this ID can be used to retrieve from server the location in the cloud of the attachment + optional string contentType = 2; // MIME type + optional bytes key = 3; // symmetric decryption key + } + + message GroupContext { + enum Type { + UNKNOWN = 0; + UPDATE = 1; + DELIVER = 2; + QUIT = 3; + } + optional bytes id = 1; + optional Type type = 2; + optional string name = 3; + repeated string members = 4; + optional AttachmentPointer avatar = 5; + } + + enum Flags { + END_SESSION = 1; + } + + optional string body = 1; + repeated AttachmentPointer attachments = 2; + optional GroupContext group = 3; + optional uint32 flags = 4; +} diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/TextSecure_PreKeyWhisperMessage.proto b/TextSecureiOS/ProtocolBuffers/Compiled/TextSecure_PreKeyWhisperMessage.proto new file mode 100644 index 0000000..8fcbe63 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/TextSecure_PreKeyWhisperMessage.proto @@ -0,0 +1,4 @@ +struct { + opaque version[1]; + opaque PreKeyWhisperMessage[...]; +} TextSecure_PreKeyWhisperMessage; \ No newline at end of file diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.pb.hh b/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.pb.hh new file mode 100644 index 0000000..0e14c5c --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.pb.hh @@ -0,0 +1,367 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WhisperMessage.proto + +#ifndef PROTOBUF_WhisperMessage_2eproto__INCLUDED +#define PROTOBUF_WhisperMessage_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2005000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2005000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_WhisperMessage_2eproto(); +void protobuf_AssignDesc_WhisperMessage_2eproto(); +void protobuf_ShutdownFile_WhisperMessage_2eproto(); + +class WhisperMessage; + +// =================================================================== + +class WhisperMessage : public ::google::protobuf::Message { + public: + WhisperMessage(); + virtual ~WhisperMessage(); + + WhisperMessage(const WhisperMessage& from); + + inline WhisperMessage& operator=(const WhisperMessage& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const WhisperMessage& default_instance(); + + void Swap(WhisperMessage* other); + + // implements Message ---------------------------------------------- + + WhisperMessage* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const WhisperMessage& from); + void MergeFrom(const WhisperMessage& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional bytes ephemeralKey = 1; + inline bool has_ephemeralkey() const; + inline void clear_ephemeralkey(); + static const int kEphemeralKeyFieldNumber = 1; + inline const ::std::string& ephemeralkey() const; + inline void set_ephemeralkey(const ::std::string& value); + inline void set_ephemeralkey(const char* value); + inline void set_ephemeralkey(const void* value, size_t size); + inline ::std::string* mutable_ephemeralkey(); + inline ::std::string* release_ephemeralkey(); + inline void set_allocated_ephemeralkey(::std::string* ephemeralkey); + + // optional uint32 counter = 2; + inline bool has_counter() const; + inline void clear_counter(); + static const int kCounterFieldNumber = 2; + inline ::google::protobuf::uint32 counter() const; + inline void set_counter(::google::protobuf::uint32 value); + + // optional uint32 previousCounter = 3; + inline bool has_previouscounter() const; + inline void clear_previouscounter(); + static const int kPreviousCounterFieldNumber = 3; + inline ::google::protobuf::uint32 previouscounter() const; + inline void set_previouscounter(::google::protobuf::uint32 value); + + // optional bytes ciphertext = 4; + inline bool has_ciphertext() const; + inline void clear_ciphertext(); + static const int kCiphertextFieldNumber = 4; + inline const ::std::string& ciphertext() const; + inline void set_ciphertext(const ::std::string& value); + inline void set_ciphertext(const char* value); + inline void set_ciphertext(const void* value, size_t size); + inline ::std::string* mutable_ciphertext(); + inline ::std::string* release_ciphertext(); + inline void set_allocated_ciphertext(::std::string* ciphertext); + + // @@protoc_insertion_point(class_scope:textsecure.WhisperMessage) + private: + inline void set_has_ephemeralkey(); + inline void clear_has_ephemeralkey(); + inline void set_has_counter(); + inline void clear_has_counter(); + inline void set_has_previouscounter(); + inline void clear_has_previouscounter(); + inline void set_has_ciphertext(); + inline void clear_has_ciphertext(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::std::string* ephemeralkey_; + ::google::protobuf::uint32 counter_; + ::google::protobuf::uint32 previouscounter_; + ::std::string* ciphertext_; + + mutable int _cached_size_; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + friend void protobuf_AddDesc_WhisperMessage_2eproto(); + friend void protobuf_AssignDesc_WhisperMessage_2eproto(); + friend void protobuf_ShutdownFile_WhisperMessage_2eproto(); + + void InitAsDefaultInstance(); + static WhisperMessage* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// WhisperMessage + +// optional bytes ephemeralKey = 1; +inline bool WhisperMessage::has_ephemeralkey() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void WhisperMessage::set_has_ephemeralkey() { + _has_bits_[0] |= 0x00000001u; +} +inline void WhisperMessage::clear_has_ephemeralkey() { + _has_bits_[0] &= ~0x00000001u; +} +inline void WhisperMessage::clear_ephemeralkey() { + if (ephemeralkey_ != &::google::protobuf::internal::kEmptyString) { + ephemeralkey_->clear(); + } + clear_has_ephemeralkey(); +} +inline const ::std::string& WhisperMessage::ephemeralkey() const { + return *ephemeralkey_; +} +inline void WhisperMessage::set_ephemeralkey(const ::std::string& value) { + set_has_ephemeralkey(); + if (ephemeralkey_ == &::google::protobuf::internal::kEmptyString) { + ephemeralkey_ = new ::std::string; + } + ephemeralkey_->assign(value); +} +inline void WhisperMessage::set_ephemeralkey(const char* value) { + set_has_ephemeralkey(); + if (ephemeralkey_ == &::google::protobuf::internal::kEmptyString) { + ephemeralkey_ = new ::std::string; + } + ephemeralkey_->assign(value); +} +inline void WhisperMessage::set_ephemeralkey(const void* value, size_t size) { + set_has_ephemeralkey(); + if (ephemeralkey_ == &::google::protobuf::internal::kEmptyString) { + ephemeralkey_ = new ::std::string; + } + ephemeralkey_->assign(reinterpret_cast(value), size); +} +inline ::std::string* WhisperMessage::mutable_ephemeralkey() { + set_has_ephemeralkey(); + if (ephemeralkey_ == &::google::protobuf::internal::kEmptyString) { + ephemeralkey_ = new ::std::string; + } + return ephemeralkey_; +} +inline ::std::string* WhisperMessage::release_ephemeralkey() { + clear_has_ephemeralkey(); + if (ephemeralkey_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = ephemeralkey_; + ephemeralkey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void WhisperMessage::set_allocated_ephemeralkey(::std::string* ephemeralkey) { + if (ephemeralkey_ != &::google::protobuf::internal::kEmptyString) { + delete ephemeralkey_; + } + if (ephemeralkey) { + set_has_ephemeralkey(); + ephemeralkey_ = ephemeralkey; + } else { + clear_has_ephemeralkey(); + ephemeralkey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + +// optional uint32 counter = 2; +inline bool WhisperMessage::has_counter() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void WhisperMessage::set_has_counter() { + _has_bits_[0] |= 0x00000002u; +} +inline void WhisperMessage::clear_has_counter() { + _has_bits_[0] &= ~0x00000002u; +} +inline void WhisperMessage::clear_counter() { + counter_ = 0u; + clear_has_counter(); +} +inline ::google::protobuf::uint32 WhisperMessage::counter() const { + return counter_; +} +inline void WhisperMessage::set_counter(::google::protobuf::uint32 value) { + set_has_counter(); + counter_ = value; +} + +// optional uint32 previousCounter = 3; +inline bool WhisperMessage::has_previouscounter() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void WhisperMessage::set_has_previouscounter() { + _has_bits_[0] |= 0x00000004u; +} +inline void WhisperMessage::clear_has_previouscounter() { + _has_bits_[0] &= ~0x00000004u; +} +inline void WhisperMessage::clear_previouscounter() { + previouscounter_ = 0u; + clear_has_previouscounter(); +} +inline ::google::protobuf::uint32 WhisperMessage::previouscounter() const { + return previouscounter_; +} +inline void WhisperMessage::set_previouscounter(::google::protobuf::uint32 value) { + set_has_previouscounter(); + previouscounter_ = value; +} + +// optional bytes ciphertext = 4; +inline bool WhisperMessage::has_ciphertext() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void WhisperMessage::set_has_ciphertext() { + _has_bits_[0] |= 0x00000008u; +} +inline void WhisperMessage::clear_has_ciphertext() { + _has_bits_[0] &= ~0x00000008u; +} +inline void WhisperMessage::clear_ciphertext() { + if (ciphertext_ != &::google::protobuf::internal::kEmptyString) { + ciphertext_->clear(); + } + clear_has_ciphertext(); +} +inline const ::std::string& WhisperMessage::ciphertext() const { + return *ciphertext_; +} +inline void WhisperMessage::set_ciphertext(const ::std::string& value) { + set_has_ciphertext(); + if (ciphertext_ == &::google::protobuf::internal::kEmptyString) { + ciphertext_ = new ::std::string; + } + ciphertext_->assign(value); +} +inline void WhisperMessage::set_ciphertext(const char* value) { + set_has_ciphertext(); + if (ciphertext_ == &::google::protobuf::internal::kEmptyString) { + ciphertext_ = new ::std::string; + } + ciphertext_->assign(value); +} +inline void WhisperMessage::set_ciphertext(const void* value, size_t size) { + set_has_ciphertext(); + if (ciphertext_ == &::google::protobuf::internal::kEmptyString) { + ciphertext_ = new ::std::string; + } + ciphertext_->assign(reinterpret_cast(value), size); +} +inline ::std::string* WhisperMessage::mutable_ciphertext() { + set_has_ciphertext(); + if (ciphertext_ == &::google::protobuf::internal::kEmptyString) { + ciphertext_ = new ::std::string; + } + return ciphertext_; +} +inline ::std::string* WhisperMessage::release_ciphertext() { + clear_has_ciphertext(); + if (ciphertext_ == &::google::protobuf::internal::kEmptyString) { + return NULL; + } else { + ::std::string* temp = ciphertext_; + ciphertext_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + return temp; + } +} +inline void WhisperMessage::set_allocated_ciphertext(::std::string* ciphertext) { + if (ciphertext_ != &::google::protobuf::internal::kEmptyString) { + delete ciphertext_; + } + if (ciphertext) { + set_has_ciphertext(); + ciphertext_ = ciphertext; + } else { + clear_has_ciphertext(); + ciphertext_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + } +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +#ifndef SWIG +namespace google { +namespace protobuf { + + +} // namespace google +} // namespace protobuf +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_WhisperMessage_2eproto__INCLUDED diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.pb.mm b/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.pb.mm new file mode 100644 index 0000000..9be9837 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.pb.mm @@ -0,0 +1,450 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WhisperMessage.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "WhisperMessage.pb.hh" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace textsecure { + +namespace { + +const ::google::protobuf::Descriptor* WhisperMessage_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + WhisperMessage_reflection_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_WhisperMessage_2eproto() { + protobuf_AddDesc_WhisperMessage_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "WhisperMessage.proto"); + GOOGLE_CHECK(file != NULL); + WhisperMessage_descriptor_ = file->message_type(0); + static const int WhisperMessage_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(WhisperMessage, ephemeralkey_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(WhisperMessage, counter_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(WhisperMessage, previouscounter_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(WhisperMessage, ciphertext_), + }; + WhisperMessage_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + WhisperMessage_descriptor_, + WhisperMessage::default_instance_, + WhisperMessage_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(WhisperMessage, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(WhisperMessage, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(WhisperMessage)); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_WhisperMessage_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + WhisperMessage_descriptor_, &WhisperMessage::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_WhisperMessage_2eproto() { + delete WhisperMessage::default_instance_; + delete WhisperMessage_reflection_; +} + +void protobuf_AddDesc_WhisperMessage_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\024WhisperMessage.proto\022\ntextsecure\"d\n\016Wh" + "isperMessage\022\024\n\014ephemeralKey\030\001 \001(\014\022\017\n\007co" + "unter\030\002 \001(\r\022\027\n\017previousCounter\030\003 \001(\r\022\022\n\n" + "ciphertext\030\004 \001(\014", 136); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "WhisperMessage.proto", &protobuf_RegisterTypes); + WhisperMessage::default_instance_ = new WhisperMessage(); + WhisperMessage::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_WhisperMessage_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_WhisperMessage_2eproto { + StaticDescriptorInitializer_WhisperMessage_2eproto() { + protobuf_AddDesc_WhisperMessage_2eproto(); + } +} static_descriptor_initializer_WhisperMessage_2eproto_; + +// =================================================================== + +#ifndef _MSC_VER +const int WhisperMessage::kEphemeralKeyFieldNumber; +const int WhisperMessage::kCounterFieldNumber; +const int WhisperMessage::kPreviousCounterFieldNumber; +const int WhisperMessage::kCiphertextFieldNumber; +#endif // !_MSC_VER + +WhisperMessage::WhisperMessage() + : ::google::protobuf::Message() { + SharedCtor(); +} + +void WhisperMessage::InitAsDefaultInstance() { +} + +WhisperMessage::WhisperMessage(const WhisperMessage& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); +} + +void WhisperMessage::SharedCtor() { + _cached_size_ = 0; + ephemeralkey_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + counter_ = 0u; + previouscounter_ = 0u; + ciphertext_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +WhisperMessage::~WhisperMessage() { + SharedDtor(); +} + +void WhisperMessage::SharedDtor() { + if (ephemeralkey_ != &::google::protobuf::internal::kEmptyString) { + delete ephemeralkey_; + } + if (ciphertext_ != &::google::protobuf::internal::kEmptyString) { + delete ciphertext_; + } + if (this != default_instance_) { + } +} + +void WhisperMessage::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* WhisperMessage::descriptor() { + protobuf_AssignDescriptorsOnce(); + return WhisperMessage_descriptor_; +} + +const WhisperMessage& WhisperMessage::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_WhisperMessage_2eproto(); + return *default_instance_; +} + +WhisperMessage* WhisperMessage::default_instance_ = NULL; + +WhisperMessage* WhisperMessage::New() const { + return new WhisperMessage; +} + +void WhisperMessage::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (has_ephemeralkey()) { + if (ephemeralkey_ != &::google::protobuf::internal::kEmptyString) { + ephemeralkey_->clear(); + } + } + counter_ = 0u; + previouscounter_ = 0u; + if (has_ciphertext()) { + if (ciphertext_ != &::google::protobuf::internal::kEmptyString) { + ciphertext_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool WhisperMessage::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional bytes ephemeralKey = 1; + case 1: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_ephemeralkey())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(16)) goto parse_counter; + break; + } + + // optional uint32 counter = 2; + case 2: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_counter: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &counter_))); + set_has_counter(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(24)) goto parse_previousCounter; + break; + } + + // optional uint32 previousCounter = 3; + case 3: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_previousCounter: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &previouscounter_))); + set_has_previouscounter(); + } else { + goto handle_uninterpreted; + } + if (input->ExpectTag(34)) goto parse_ciphertext; + break; + } + + // optional bytes ciphertext = 4; + case 4: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { + parse_ciphertext: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_ciphertext())); + } else { + goto handle_uninterpreted; + } + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +void WhisperMessage::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // optional bytes ephemeralKey = 1; + if (has_ephemeralkey()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 1, this->ephemeralkey(), output); + } + + // optional uint32 counter = 2; + if (has_counter()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(2, this->counter(), output); + } + + // optional uint32 previousCounter = 3; + if (has_previouscounter()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->previouscounter(), output); + } + + // optional bytes ciphertext = 4; + if (has_ciphertext()) { + ::google::protobuf::internal::WireFormatLite::WriteBytes( + 4, this->ciphertext(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } +} + +::google::protobuf::uint8* WhisperMessage::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // optional bytes ephemeralKey = 1; + if (has_ephemeralkey()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 1, this->ephemeralkey(), target); + } + + // optional uint32 counter = 2; + if (has_counter()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(2, this->counter(), target); + } + + // optional uint32 previousCounter = 3; + if (has_previouscounter()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(3, this->previouscounter(), target); + } + + // optional bytes ciphertext = 4; + if (has_ciphertext()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 4, this->ciphertext(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + return target; +} + +int WhisperMessage::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional bytes ephemeralKey = 1; + if (has_ephemeralkey()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->ephemeralkey()); + } + + // optional uint32 counter = 2; + if (has_counter()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->counter()); + } + + // optional uint32 previousCounter = 3; + if (has_previouscounter()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->previouscounter()); + } + + // optional bytes ciphertext = 4; + if (has_ciphertext()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->ciphertext()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void WhisperMessage::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const WhisperMessage* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void WhisperMessage::MergeFrom(const WhisperMessage& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_ephemeralkey()) { + set_ephemeralkey(from.ephemeralkey()); + } + if (from.has_counter()) { + set_counter(from.counter()); + } + if (from.has_previouscounter()) { + set_previouscounter(from.previouscounter()); + } + if (from.has_ciphertext()) { + set_ciphertext(from.ciphertext()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void WhisperMessage::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void WhisperMessage::CopyFrom(const WhisperMessage& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool WhisperMessage::IsInitialized() const { + + return true; +} + +void WhisperMessage::Swap(WhisperMessage* other) { + if (other != this) { + std::swap(ephemeralkey_, other->ephemeralkey_); + std::swap(counter_, other->counter_); + std::swap(previouscounter_, other->previouscounter_); + std::swap(ciphertext_, other->ciphertext_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata WhisperMessage::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = WhisperMessage_descriptor_; + metadata.reflection = WhisperMessage_reflection_; + return metadata; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace textsecure + +// @@protoc_insertion_point(global_scope) diff --git a/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.proto b/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.proto new file mode 100644 index 0000000..ce3bc1a --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/Compiled/WhisperMessage.proto @@ -0,0 +1,11 @@ +package textsecure; + +message WhisperMessage { + optional bytes ephemeralKey = 1; + optional uint32 counter = 2; + optional uint32 previousCounter = 3; + optional bytes ciphertext = 4; // encrypted PushMessageContent +} + + + diff --git a/TextSecureiOS/ProtocolBuffers/IncomingPushMessageSignal.hh b/TextSecureiOS/ProtocolBuffers/IncomingPushMessageSignal.hh new file mode 100644 index 0000000..4e13ce3 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/IncomingPushMessageSignal.hh @@ -0,0 +1,26 @@ +// +// IncomingPushMessageSignal.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 10/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. + +// NOTE THAT ANY FILE WHICH INCLUDES THIS IS THEREBY OBJECTIVE-C++ +// That means to compile correctly, it needs an .hh/.mm extension + +#import +#import "IncomingPushMessageSignal.pb.hh" +@class TSMessage; +@class TSAttachment; +@interface IncomingPushMessageSignal : NSObject ++ (NSData *)getDataForIncomingPushMessageSignal:(textsecure::IncomingPushMessageSignal *)incomingPushMessage; ++ (textsecure::IncomingPushMessageSignal *)getIncomingPushMessageSignalForData:(NSData *)data; ++ (textsecure::PushMessageContent *)getPushMessageContentForData:(NSData *)data; ++ (NSString*)prettyPrint:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal; ++ (NSString*)prettyPrintPushMessageContent:(textsecure::PushMessageContent *)pushMessageContent; ++ (NSData *)createSerializedPushMessageContent:(TSMessage*) message; ++ (NSString*) getMessageBody:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal; ++(TSMessage*)getTSMessageForIncomingPushMessageSignal:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal; + ++ (TSAttachment*) getMessageAttachmentData:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal; +@end diff --git a/TextSecureiOS/ProtocolBuffers/IncomingPushMessageSignal.mm b/TextSecureiOS/ProtocolBuffers/IncomingPushMessageSignal.mm new file mode 100644 index 0000000..b43b961 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/IncomingPushMessageSignal.mm @@ -0,0 +1,158 @@ +// +// IncomingPushMessageSignal.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 10/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "IncomingPushMessageSignal.hh" +#import "TSMessage.h" +#import "TSAttachment.h" +#import "NSData+Base64.h" +#include + +@implementation IncomingPushMessageSignal + + + +// Serialize IncomingPushMessageSignal to NSData. ++ (NSData *)getDataForIncomingPushMessageSignal:(textsecure::IncomingPushMessageSignal *)incomingPushMessage { + std::string ps = incomingPushMessage->SerializeAsString(); + return [NSData dataWithBytes:ps.c_str() length:ps.size()]; +} + +// De-serialize IncomingPushMessageSignal from an NSData object. ++ (textsecure::IncomingPushMessageSignal *)getIncomingPushMessageSignalForData:(NSData *)data { + int len = [data length]; + char raw[len]; + textsecure::IncomingPushMessageSignal *incomingPushMessage = new textsecure::IncomingPushMessageSignal; + [data getBytes:raw length:len]; + incomingPushMessage->ParseFromArray(raw, len); + return incomingPushMessage; +} + +// Serialize TSAttachment to AttachmentPointer + +// De-Serialize AttachmentPointer to TSAttachment + +// Serialize PushMessageContent to NSData. ++ (NSData *)getDataForPushMessageContent:(textsecure::PushMessageContent *)pushMessageContent { + std::string ps = pushMessageContent->SerializeAsString(); + return [NSData dataWithBytes:ps.c_str() length:ps.size()]; +} + + +// De-serialize PushMessageContent from an NSData object. ++ (textsecure::PushMessageContent *)getPushMessageContentForData:(NSData *)data { + int len = [data length]; + char raw[len]; + textsecure::PushMessageContent *pushMessageContent = new textsecure::PushMessageContent; + [data getBytes:raw length:len]; + pushMessageContent->ParseFromArray(raw, len); + return pushMessageContent; +} + +// Create PushMessageContent from it's Objective C contents ++ (NSData *)createSerializedPushMessageContent:(TSMessage*) message { +#warning no attachments suppoart yet + textsecure::PushMessageContent *pushMessageContent = new textsecure::PushMessageContent(); + const std::string body([message.message cStringUsingEncoding:NSASCIIStringEncoding]); + + pushMessageContent->set_body(body); + if([message.attachment readyForUpload]) { + textsecure::PushMessageContent_AttachmentPointer *attachmentPointer = pushMessageContent->add_attachments(); + const uint64_t attachment_id = [message.attachment.attachmentId unsignedLongLongValue]; + + attachmentPointer->set_id(attachment_id); + const std::string attachment_encryption_key([[message.attachment.attachmentDecryptionKey base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength] cStringUsingEncoding:NSASCIIStringEncoding]); + attachmentPointer->set_key(attachment_encryption_key); + std::string attachment_contenttype([[message.attachment getMIMEContentType] cStringUsingEncoding:NSASCIIStringEncoding]); + attachmentPointer->set_contenttype(attachment_contenttype); + } + NSData *serializedPushMessageContent = [IncomingPushMessageSignal getDataForPushMessageContent:pushMessageContent]; + delete pushMessageContent; + return serializedPushMessageContent; +} + + ++ (NSString*) getMessageBody:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal { + const std::string cppMessage = incomingPushMessageSignal->message(); + NSData *messageData =[NSData dataWithBytes:cppMessage.c_str() length:cppMessage.size()]; + textsecure::PushMessageContent *messageContent = [IncomingPushMessageSignal getPushMessageContentForData:messageData]; + return [IncomingPushMessageSignal prettyPrintPushMessageContent:messageContent]; +} + ++ (TSAttachment*) getMessageAttachmentData:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal { + const std::string cppMessage = incomingPushMessageSignal->message(); + NSData *messageData =[NSData dataWithBytes:cppMessage.c_str() length:cppMessage.size()]; + textsecure::PushMessageContent *messageContent = [IncomingPushMessageSignal getPushMessageContentForData:messageData]; + if(messageContent->attachments_size()>0) { + const textsecure::PushMessageContent_AttachmentPointer *attachmentPointer = messageContent->mutable_attachments(0); + + const std::string cppContentType = attachmentPointer->contenttype(); + NSString *contentType = [NSString stringWithCString:cppContentType.c_str() encoding:NSASCIIStringEncoding]; + const std::string cppKey = attachmentPointer->key(); + NSData *decryptionKey = [NSData dataFromBase64String:[NSString stringWithCString:cppKey.c_str() encoding:NSASCIIStringEncoding]]; + const uint64_t cppId = attachmentPointer->id(); + +#warning retrieve attachment here + + return [[TSAttachment alloc] initWithAttachmentId:[NSNumber numberWithUnsignedLongLong:cppId] contentMIMEType:contentType decryptionKey:decryptionKey]; + + } + else { + return nil; + } + +} ++ (NSString*)prettyPrint:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal { + /* + Type + Allowed source + Destinations + Timestamp + Allocated Message + */ + + const uint32_t cppType = incomingPushMessageSignal->type(); + const std::string cppSource = incomingPushMessageSignal->source(); + const uint64_t cppTimestamp = incomingPushMessageSignal->timestamp(); + /* testing conversion to objective c objects */ + NSNumber* type = [NSNumber numberWithInteger:cppType]; + NSString* source = [NSString stringWithCString:cppSource.c_str() encoding:NSASCIIStringEncoding]; + NSNumber* timestamp = [NSNumber numberWithInteger:cppTimestamp]; + + NSString* message = [IncomingPushMessageSignal getMessageBody:incomingPushMessageSignal]; + NSString *fullInfo = [NSString stringWithFormat:@"Type: %@ \n source: %@ \n message: %@", + type,source,message]; + return fullInfo; +} +// Dlog ++ (NSString*)prettyPrintPushMessageContent:(textsecure::PushMessageContent *)pushMessageContent { + const std::string cppBody = pushMessageContent->body(); + NSString* body = [NSString stringWithCString:cppBody.c_str() encoding:NSASCIIStringEncoding]; + return body; +#warning doesn't handle attachments yet + +} + ++(TSMessage*)getTSMessageForIncomingPushMessageSignal:(textsecure::IncomingPushMessageSignal *)incomingPushMessageSignal { + const uint32_t cppType = incomingPushMessageSignal->type(); + const std::string cppSource = incomingPushMessageSignal->source(); + const uint64_t cppTimestamp = incomingPushMessageSignal->timestamp(); + /* testing conversion to objective c objects */ + NSNumber* type = [NSNumber numberWithInteger:cppType]; + NSString* source = [NSString stringWithCString:cppSource.c_str() encoding:NSASCIIStringEncoding]; + + NSNumber* timestamp = [NSNumber numberWithInteger:cppTimestamp]; + //[[NSDate alloc] initWithTimeIntervalSince1970:[timestamp longLongValue]] + NSString* message = [IncomingPushMessageSignal getMessageBody:incomingPushMessageSignal]; +#warning ignoring timestamp sent, setting to now, fix issue with timestamp received being incorrectly interpreted (a few years off). currently behavior is when received. + TSAttachment *tsAttachment = [IncomingPushMessageSignal getMessageAttachmentData:incomingPushMessageSignal]; // not implemented + // this phone is the recipient of the message + TSMessage *tsMessage = [[TSMessage alloc] initWithMessage:message sender:source recipients:[[NSArray alloc] initWithObjects:[TSKeyManager getUsernameToken], nil] sentOnDate:[NSDate date] attachment:tsAttachment]; + return tsMessage; +} + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSEncryptedWhisperMessage.hh b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSEncryptedWhisperMessage.hh new file mode 100644 index 0000000..ae87fe0 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSEncryptedWhisperMessage.hh @@ -0,0 +1,26 @@ +// +// TSWhisperMessage.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/6/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSWhisperMessage.hh" + +@interface TSEncryptedWhisperMessage : TSWhisperMessage +@property (readonly,nonatomic,strong) NSData* ephemeralKey; +@property (readonly,nonatomic,strong) NSNumber* counter; +@property (readonly,nonatomic,strong) NSNumber* previousCounter; +@property (readonly,nonatomic,strong) NSData* hmac; +@property (readonly,nonatomic,strong) NSData* message; +@property (readonly,nonatomic,strong) NSData* version; +@property (readonly,nonatomic,strong) NSData *protocolData; + + +-(instancetype) initWithEphemeralKey:(NSData*)ephemeral previousCounter:(NSNumber*)prevCounter counter:(NSNumber*)ctr encryptedPushMessageContent:(NSData*)ciphertext forVersion:(NSData*)version HMACKey:(NSData*)hmacKey; + +- (BOOL)verifyHMAC:(NSData*)hmacKey; + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSEncryptedWhisperMessage.mm b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSEncryptedWhisperMessage.mm new file mode 100644 index 0000000..f086a58 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSEncryptedWhisperMessage.mm @@ -0,0 +1,159 @@ +// +// TSWhisperMessage.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/6/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSEncryptedWhisperMessage.hh" +#import "WhisperMessage.pb.hh" +#import "Cryptography.h" +#import "NSData+TSKeyVersion.h" + + +@interface TSEncryptedWhisperMessage () + +@property (nonatomic,strong) NSData* ephemeralKey; +@property (nonatomic,strong) NSNumber* counter; +@property (nonatomic,strong) NSNumber* previousCounter; +@property (nonatomic,strong) NSData* hmac; +@property (nonatomic,strong) NSData* message; +@property (nonatomic,strong) NSData* version; +@property (nonatomic,strong) NSData *protocolData; + + +@end + + +@implementation TSEncryptedWhisperMessage + +-(instancetype) initWithEphemeralKey:(NSData*)ephemeral previousCounter:(NSNumber*)prevCounter counter:(NSNumber*)ctr encryptedPushMessageContent:(NSData*)ciphertext forVersion:(NSData*)version HMACKey:(NSData*)hmacKey{ + if(self = [super init]) { + self.ephemeralKey = ephemeral; + self.previousCounter = prevCounter; + self.counter = ctr; + self.message=ciphertext; + self.version = version; + self.hmac = [self hMacWithKey:hmacKey]; + self.protocolData = [self getTextSecure_WhisperMessage]; + } + return self; +} + + +-(instancetype) initWithTextSecureProtocolData:(NSData*) data { + return [self initWithTextSecure_WhisperMessage:data]; +} + +-(NSData*) getTextSecureProtocolData { + return self.protocolData; +} + +-(instancetype) initWithTextSecure_WhisperMessage:(NSData*) data { + /* Protocol v2 + struct { + opaque version[1]; + opaque WhisperMessage[...]; + opaque mac[8]; + } TextSecure_WhisperMessage; + message WhisperMessage { + optional bytes ephemeralKey = 1; + optional uint32 counter = 2; + optional uint32 previousCounter = 3; + optional bytes ciphertext = 4; + } + + */ + if(self = [super init]) { + // 1st extract out version and mac + self.version = [data subdataWithRange:NSMakeRange(0, 1)]; + self.hmac = [data subdataWithRange:NSMakeRange([data length]-8, 8)]; + NSData* whisperMessageProtobuf = [data subdataWithRange:NSMakeRange(1, [data length] -8-1)]; + // c++ + textsecure::WhisperMessage *whisperMessage = [self deserializeProtocolBuffer:whisperMessageProtobuf]; + const std::string cppEphemeralKey = whisperMessage->ephemeralkey(); + const uint32_t cppCounter = whisperMessage->counter(); + const uint32_t cppPreviousCounter = whisperMessage->previouscounter(); + const std::string cppMessage = whisperMessage->ciphertext(); + + // c++->objective C + self.protocolData = data; + + self.ephemeralKey = [[self cppStringToObjcData:cppEphemeralKey] removeVersionByte]; // this crashes on first receive from group send. this MUST be a protobuf + group issue in the context of a prekeywhispermessage. that's why it does work without groups, or if a session is already started. + self.counter = [self cppUInt32ToNSNumber:cppCounter]; + self.previousCounter = [self cppUInt32ToNSNumber:cppPreviousCounter]; + self.message = [self cppStringToObjcData:cppMessage]; + } + return self; // super is abstract class +} + + +-(NSData*) getTextSecure_WhisperMessage{ + + NSMutableData *serialized = [NSMutableData data]; + [serialized appendData:self.version]; + [serialized appendData:[self serializedProtocolBuffer]]; + [serialized appendData:self.hmac]; + return serialized; +} + + +-(NSString*) debugDescription { + return [NSString stringWithFormat:@"WhisperMessage:\n ephemeralKey: %@\n previousCounter: %@\n counter: %@\n message: %@\n version: %@\n hmac:%@\n",self.ephemeralKey,self.previousCounter,self.counter,self.message,self.version,self.hmac]; +} + + +-(const std::string) serializedProtocolBufferAsString { + textsecure::WhisperMessage *whisperMessage = new textsecure::WhisperMessage; + // objective c->c++ + const std::string cppEphemeralKey = [self objcDataToCppString:[self.ephemeralKey prependVersionByte]]; + const uint32_t cppCounter = [self objcNumberToCppUInt32:self.counter]; + const uint32_t cppPreviousCounter = [self objcNumberToCppUInt32:self.previousCounter]; + const std::string cppMessage = [self objcDataToCppString:self.message]; + + + // c++->protocol buffer + whisperMessage->set_ephemeralkey(cppEphemeralKey); + whisperMessage->set_counter(cppCounter); + whisperMessage->set_previouscounter(cppPreviousCounter); + whisperMessage->set_ciphertext(cppMessage); + std::string ps = whisperMessage->SerializeAsString(); + + return ps; +} + +#pragma mark private methods +- (textsecure::WhisperMessage *)deserializeProtocolBuffer:(NSData *)data { + int len = [data length]; + char raw[len]; + textsecure::WhisperMessage *messageSignal = new textsecure::WhisperMessage; + [data getBytes:raw length:len]; + messageSignal->ParseFromArray(raw, len); + + return messageSignal; +} + +- (BOOL)verifyHMAC:(NSData*)hmacKey{ + NSData *ourHmac = [self hMacWithKey:hmacKey]; + + if ([ourHmac isEqualToData:self.hmac]) { + return YES; + } + + return NO; +} + +- (NSData*)hMacWithKey:(NSData*)hmacKey{ + NSMutableData *hmacData = [NSMutableData data]; + [hmacData appendData:self.version]; + [hmacData appendData:[self serializedProtocolBuffer]]; + + return [[self class] hmacWithKey:hmacKey data:hmacData]; +} + ++ (NSData*)hmacWithKey:(NSData*)macKey data:(NSData*)data{ + NSData *hash = [Cryptography computeSHA256HMAC:data withHMACKey:macKey]; + return [hash subdataWithRange:NSMakeRange(0, 8)]; +} +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.hh b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.hh new file mode 100644 index 0000000..9b0a978 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.hh @@ -0,0 +1,26 @@ +// +// TSMessageSignal.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSProtocolBufferWrapper.hh" +#import "TSMessage.h" +#import "Constants.h" + +@class TSWhisperMessage; +@interface TSMessageSignal : TSProtocolBufferWrapper + +@property (readonly,nonatomic,strong) TSWhisperMessage *message; +@property (readonly,nonatomic,strong) NSString* source; +@property (readonly,nonatomic,strong) NSNumber* sourceDevice; +@property (readonly,nonatomic) TSWhisperMessageType contentType; +@property (readonly,nonatomic,strong) NSDate *timestamp; +@property (readonly,nonatomic,strong) NSData *protocolData; + + +-(instancetype) initWithMessage:(TSWhisperMessage*) message withContentType:(TSWhisperMessageType)contentType withSource:(NSString*)source withSourceDevice:(NSNumber*)sourceDevice withTimestamp:(NSDate*) timestamp; +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.mm b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.mm new file mode 100644 index 0000000..19c88b6 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSMessageSignal.mm @@ -0,0 +1,124 @@ +// +// TSMessageSignal.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageSignal.hh" + +#import "TSEncryptedWhisperMessage.hh" +#import "TSPreKeyWhisperMessage.hh" +#import "IncomingPushMessageSignal.pb.hh" + +@interface TSMessageSignal () + +@property (nonatomic,strong) TSWhisperMessage *message; +@property (nonatomic,strong) NSString* source; +@property (nonatomic,strong) NSNumber* sourceDevice; +@property (nonatomic) TSWhisperMessageType contentType; +@property (nonatomic,strong) NSDate *timestamp; +@property (nonatomic,strong) NSData *protocolData; +@end + + +@implementation TSMessageSignal + + +-(instancetype) initWithMessage:(TSWhisperMessage*) message withContentType:(TSWhisperMessageType)contentType withSource:(NSString*)source withSourceDevice:(NSNumber*)sourceDevice withTimestamp:(NSDate*) timestamp { + if(self = [super init]) { + self.message = message; + self.contentType = contentType; + self.source = source; + self.sourceDevice = sourceDevice; + self.timestamp = timestamp; + self.protocolData = [self serializedProtocolBuffer]; + } + return self; +} + +-(instancetype) initWithTextSecureProtocolData:(NSData*) data { + return [self initWithData:data]; +} + +-(NSData*) getTextSecureProtocolData { + return self.protocolData; +} + + +-(instancetype) initWithData:(NSData*) data { + + if(self = [super init]) { + // c++ + textsecure::IncomingPushMessageSignal *incomingPushMessageSignal = [self deserialize:data]; + const std::string cppMessage = incomingPushMessageSignal->message(); + const uint32_t cppType = incomingPushMessageSignal->type(); + const std::string cppSource = incomingPushMessageSignal->source(); + const uint32_t cppSourceDevice = incomingPushMessageSignal->sourcedevice(); + const uint64_t cppTimestamp = incomingPushMessageSignal->timestamp(); + + // c++->objective C + self.protocolData = data; + self.contentType = (TSWhisperMessageType)cppType; + + self.message = [self getWhisperMessageForData:[self cppStringToObjcData:cppMessage] ofContentType:self.contentType]; + self.source = [self cppStringToObjc:cppSource]; + self.sourceDevice = [self cppUInt32ToNSNumber:cppSourceDevice]; + self.timestamp = [self cppDateToObjc:cppTimestamp]; + } + return self; +} + + + + +#pragma mark private + +-(const std::string) serializedProtocolBufferAsString { + textsecure::IncomingPushMessageSignal *messageSignal = new textsecure::IncomingPushMessageSignal; + // objective c->c++ + const textsecure::IncomingPushMessageSignal_Type cppType = (textsecure::IncomingPushMessageSignal_Type)self.contentType; + const std::string cppSource = [self objcStringToCpp:self.source]; + const uint32_t cppSourceDevice = [self objcNumberToCppUInt32:self.sourceDevice]; + const uint64_t cppTimestamp = [self objcDateToCpp:self.timestamp]; + const std::string cppMessage = [self objcDataToCppString:[self.message getTextSecureProtocolData]]; + // c++->protocol buffer + messageSignal->set_type(cppType); + messageSignal->set_source(cppSource); + messageSignal->set_sourcedevice(cppSourceDevice); + messageSignal->set_timestamp(cppTimestamp); + messageSignal->set_message(cppMessage); + + std::string ps = messageSignal->SerializeAsString(); + return ps; +} + +#pragma mark private methods +- (textsecure::IncomingPushMessageSignal *)deserialize:(NSData *)data { + int len = [data length]; + char raw[len]; + textsecure::IncomingPushMessageSignal *messageSignal = new textsecure::IncomingPushMessageSignal; + [data getBytes:raw length:len]; + messageSignal->ParseFromArray(raw, len); + return messageSignal; +} + +-(TSWhisperMessage*) getWhisperMessageForData:(NSData*) data ofContentType:(TSWhisperMessageType) contentType{ + switch (contentType) { + case TSEncryptedWhisperMessageType: { + return [[TSEncryptedWhisperMessage alloc] initWithTextSecureProtocolData:data]; + break; + } + case TSPreKeyWhisperMessageType: { + return [[TSPreKeyWhisperMessage alloc] initWithTextSecureProtocolData:data]; + break; + } + default: { + return nil; + break; + } + } +} + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPreKeyWhisperMessage.hh b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPreKeyWhisperMessage.hh new file mode 100644 index 0000000..b82ef65 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPreKeyWhisperMessage.hh @@ -0,0 +1,26 @@ +// +// TSPrekeyWhisperMessage.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/6/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSEncryptedWhisperMessage.hh" +#import "TSMessagesDatabase.h" + +@class TSECKeyPair; + +@interface TSPreKeyWhisperMessage : TSWhisperMessage +@property (readonly,nonatomic,strong) NSData *protocolData; +@property (readonly,nonatomic,strong) NSData* version; + +@property (readonly,nonatomic,strong) NSNumber* preKeyId; +@property (readonly,nonatomic,strong) NSData* baseKey; // base Curve25519 key exchange ephemeral: A0 in axolotl +@property (readonly,nonatomic,strong) NSData* identityKey; //Curve25519 identity key of the sender: A in axolotl +@property (readonly,nonatomic,strong) NSData* message; + ++(TSPreKeyWhisperMessage *) constructFirstMessageWithEncryptedPushMessageContent:(NSData*)ciphertext theirPrekeyId:(NSNumber*) theirPrekeyId myCurrentEphemeral:(NSData*) currentEphemeral myNextEphemeral:(NSData*)myNextEphemeral forVersion:(NSData*)version withHMACKey:(NSData*)hmac; + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPreKeyWhisperMessage.mm b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPreKeyWhisperMessage.mm new file mode 100644 index 0000000..dbb8363 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPreKeyWhisperMessage.mm @@ -0,0 +1,152 @@ +// +// TSPrekeyWhisperMessage.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/6/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSPrekeyWhisperMessage.hh" +#import "PreKeyWhisperMessage.pb.hh" +#import "TSEncryptedWhisperMessage.hh" +#import "TSECKeyPair.h" +#import "TSUserKeysDatabase.h" +#import "NSData+TSKeyVersion.h" +#import "NSData+Base64.h" + +@interface TSPreKeyWhisperMessage () + +@property (nonatomic,strong) NSData *protocolData; +@property (nonatomic,strong) NSData* version; + +@property (nonatomic,strong) NSNumber* preKeyId; +@property (nonatomic,strong) NSData* baseKey; // base Curve25519 key exchange ephemeral: A0 in axolotl +@property (nonatomic,strong) NSData* identityKey; //Curve25519 identity key of the sender: A in axolotl +@property (nonatomic,strong) NSData* message; + + +@end + + +@implementation TSPreKeyWhisperMessage +-(instancetype)initWithPreKeyId:(NSNumber*)prekeyId senderPrekey:(NSData*)prekey senderIdentityKey:(NSData*)identityKey message:(NSData*)messageContents forVersion:(NSData*)vers{ + if(self=[super init]) { + self.version = vers; + self.preKeyId = prekeyId; + self.baseKey = prekey; + self.identityKey = identityKey; + self.message = messageContents; + self.protocolData = [self getTextSecure_PreKeyWhisperMessage]; + } + return self; +} + + +-(instancetype) initWithTextSecureProtocolData:(NSData*) data { + return [self initWithTextSecure_PreKeyWhisperMessage:data]; +} + +-(NSData*) getTextSecureProtocolData { + return self.protocolData; +} + + +-(instancetype) initWithTextSecure_PreKeyWhisperMessage:(NSData*) data { + /* + struct { + opaque version[1]; + opaque PreKeyWhisperMessage[...]; + } TextSecure_PreKeyWhisperMessage; + + # ProtocolBuffer + message PreKeyWhisperMessage { + optional uint32 preKeyId = 1; + optional bytes baseKey = 2; + optional bytes identityKey = 3; + optional bytes message = 4; + } + + */ + if(self = [super init]) { + // c++ + textsecure::PreKeyWhisperMessage *prekeyWhisperMessage = [self deserializeProtocolBuffer:[data subdataWithRange:NSMakeRange(1, [data length]-1)]]; + uint32_t cppPreKeyId = prekeyWhisperMessage->prekeyid(); + const std::string cppBaseKey = prekeyWhisperMessage->basekey(); + const std::string cppIdentityKey = prekeyWhisperMessage->identitykey(); + const std::string cppMessage = prekeyWhisperMessage->message(); + + // c++->objective C + self.protocolData = data; + self.version = [data subdataWithRange:NSMakeRange(0, 1)]; + self.preKeyId = [self cppUInt32ToNSNumber:cppPreKeyId]; + self.baseKey = [[self cppStringToObjcData:cppBaseKey] removeVersionByte]; + self.identityKey = [[self cppStringToObjcData:cppIdentityKey] removeVersionByte]; + self.message = [self cppStringToObjcData:cppMessage]; + } + return self; // super is abstract class +} + + +-(const std::string) serializedProtocolBufferAsString { + textsecure::PreKeyWhisperMessage *preKeyMessage = new textsecure::PreKeyWhisperMessage; + // objective c->c++ + uint32_t cppPreKeyId = [self objcNumberToCppUInt32:self.preKeyId]; + const std::string cppBaseKey = [self objcDataToCppString:[self.baseKey prependVersionByte]]; + const std::string cppIdentityKey = [self objcDataToCppString:[self.identityKey prependVersionByte]]; + const std::string cppMessage = [self objcDataToCppString:self.message]; + // c++->protocol buffer + preKeyMessage->set_prekeyid(cppPreKeyId); + preKeyMessage->set_basekey(cppBaseKey); + preKeyMessage->set_identitykey(cppIdentityKey); + preKeyMessage->set_message(cppMessage); + std::string ps = preKeyMessage->SerializeAsString(); + return ps; +} + +#pragma mark private methods +- (textsecure::PreKeyWhisperMessage *)deserializeProtocolBuffer:(NSData *)data { + int len = [data length]; + char raw[len]; + textsecure::PreKeyWhisperMessage *messageSignal = new textsecure::PreKeyWhisperMessage; + [data getBytes:raw length:len]; + messageSignal->ParseFromArray(raw, len); + return messageSignal; +} + + + +-(NSData*) getTextSecure_PreKeyWhisperMessage { + NSMutableData *serialized = [NSMutableData data]; + [serialized appendData:self.version]; + [serialized appendData:[self serializedProtocolBuffer]]; + return serialized; +} + + +- (NSString *)debugDescription { + return [NSString stringWithFormat:@"PreKeyWhisperMessage:\n prekeyId: %@\n baseKey: %@\n identityKey: %@\n message: %@\n version: %@",self.preKeyId,self.baseKey,self.identityKey,self.message,self.version]; +} + +#pragma mark public static methods ++(TSPreKeyWhisperMessage *) constructFirstMessageWithEncryptedPushMessageContent:(NSData*)ciphertext theirPrekeyId:(NSNumber*) theirPrekeyId myCurrentEphemeral:(NSData*) currentEphemeral myNextEphemeral:(NSData*)myNextEphemeral forVersion:(NSData*)version withHMACKey:(NSData*)hmac { + + TSEncryptedWhisperMessage *encryptedWhisperMessage = [[TSEncryptedWhisperMessage alloc] + initWithEphemeralKey:myNextEphemeral + previousCounter:[NSNumber numberWithInt:0] + counter:[NSNumber numberWithInt:0] + encryptedPushMessageContent:ciphertext + forVersion:version + HMACKey:hmac]; + + TSECKeyPair *identityKey = [TSUserKeysDatabase identityKey]; + + TSPreKeyWhisperMessage *prekeyMessage = [[TSPreKeyWhisperMessage alloc] + initWithPreKeyId:theirPrekeyId + senderPrekey:currentEphemeral + senderIdentityKey:[identityKey publicKey] + message:[encryptedWhisperMessage getTextSecureProtocolData] + forVersion:version]; + return prekeyMessage; +} + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.hh b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.hh new file mode 100644 index 0000000..447c65a --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.hh @@ -0,0 +1,53 @@ +// +// TSProtocolBufferWrapper.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +#ifdef __cplusplus +#import +#endif + +@protocol TSProtocolBufferWrapperMethods +#pragma mark these must be overridden by subclass +-(instancetype) initWithData:(NSData*) buffer; + +#pragma mark boilerplate code +// raw protocol buffer +-(NSData*) serializedProtocolBuffer; +// these pre or post pend version and hmac info to serialized protocol buffer +// C++<->Objc dates + +#ifdef __cplusplus +-(const std::string) serializedProtocolBufferAsString; +-(uint64_t) objcDateToCpp:(NSDate*)objcDate; +-(NSDate*) cppDateToObjc:(uint64_t)cppDate; +#endif + +// C++<->Objc strings +#ifdef __cplusplus +-(const std::string) objcStringToCpp:(NSString*)objcString; +-(NSString*) cppStringToObjc:(const std::string)cppString; +-(const std::string) objcDataToCppString:(NSData*)objcData; +-(NSData*) cppStringToObjcData:(const std::string) cppString; +#endif + +// C++<->Objc ints +-(uint32_t) objcNumberToCppUInt32:(NSNumber*)objcNumber; +-(NSNumber*) cppUInt32ToNSNumber:(uint32_t)cppInt; +-(uint64_t) objcNumberToCppUInt64:(NSNumber*)objcNumber; +-(NSNumber*) cppUInt64ToNSNumber:(uint64_t)cppInt; +@end + +@interface TSProtocolBufferWrapper : NSObject +#pragma mark these must be overridden by subclass + +-(id) initWithTextSecureProtocolData:(NSData*) data; +-(NSData*) getTextSecureProtocolData; + + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.mm b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.mm new file mode 100644 index 0000000..23e4e4c --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSProtocolBufferWrapper.mm @@ -0,0 +1,98 @@ +// +// TSProtocolBufferWrapper.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSProtocolBufferWrapper.hh" + + +@implementation TSProtocolBufferWrapper + +#pragma mark these must be overridden by subclass + +-(instancetype) initWithTextSecureProtocolData:(NSData*) data { + throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"'abstract method' must override %@ in a subclass", NSStringFromSelector(_cmd)] + userInfo:nil]; +} +-(NSData*) getTextSecureProtocolData { + throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"'abstract method' must override %@ in a subclass", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + + + + + +#pragma mark protected methods + +-(const std::string) serializedProtocolBufferAsString { + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"'abstract method' must override %@ in a subclass", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +-(id) initWithData:(NSData*) buffer{ + NSAssert(@"Must be overriden in subclasses!", @"initWithData: needs to be implemented in ProtocolBufferWrapper's subclasses"); + return nil; +} + +#pragma mark boilerplate serialization method +-(NSData*) serializedProtocolBuffer { + std::string ps = [self serializedProtocolBufferAsString]; + return [NSData dataWithBytes:ps.c_str() length:ps.size()]; +} + + +#pragma mark boilerplate conversion methods +-(uint64_t) objcDateToCpp:(NSDate*)objcDate { + return round([objcDate timeIntervalSince1970]); +} + +-(NSDate*) cppDateToObjc:(uint64_t)cppDate { + return [NSDate dateWithTimeIntervalSince1970:[[NSNumber numberWithInteger:cppDate] doubleValue]]; +} + +-(const std::string) objcStringToCpp:(NSString*)objcString { + const char* cstring = [objcString cStringUsingEncoding:NSUTF8StringEncoding]; + const std::string stringFromBytes(cstring); + return stringFromBytes; +} + +-(NSString*) cppStringToObjc:(const std::string)cppString { + return [NSString stringWithCString:cppString.c_str() encoding:NSUTF8StringEncoding]; +} + +-(const std::string) objcDataToCppString:(NSData*)objcData { + int len = [objcData length]; + char raw[len]; + [objcData getBytes:raw length:len]; + const std::string stringFromBytes(raw, len); + return stringFromBytes; +} + +-(NSData*) cppStringToObjcData:(const std::string)cppString { + return [NSData dataWithBytes:cppString.data() length:cppString.size()]; +} + +-(uint32_t) objcNumberToCppUInt32:(NSNumber*)objcNumber { + return [objcNumber unsignedLongValue]; +} +-(NSNumber*) cppUInt32ToNSNumber:(uint32_t)cppInt { + return [NSNumber numberWithUnsignedLong:cppInt]; +} + +-(uint64_t) objcNumberToCppUInt64:(NSNumber*)objcNumber { + return [objcNumber unsignedLongLongValue]; +} +-(NSNumber*) cppUInt64ToNSNumber:(uint64_t)cppInt { + return [NSNumber numberWithUnsignedLongLong:cppInt]; + +} + + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.hh b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.hh new file mode 100644 index 0000000..a8c8bea --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.hh @@ -0,0 +1,25 @@ +// +// TSPushMessageContent.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSProtocolBufferWrapper.hh" +#import "TSGroupContext.h" + +@class TSMessage; + +@interface TSPushMessageContent : TSProtocolBufferWrapper +@property (readonly,nonatomic,strong) NSString* body; +@property (readonly,nonatomic,strong) NSArray* attachments; +@property (readonly,nonatomic,strong) TSGroupContext* groupContext; +@property (readonly,nonatomic,assign) TSPushMessageFlags messageFlags; +@property (readonly,nonatomic,strong) NSData *serializedProtocolData; + +-(instancetype) initWithBody:(NSString*)body withAttachments:(NSArray*)attachments withGroupContext:(TSGroupContext*)groupContext; + ++ (NSData *)serializedPushMessageContentForMessage:(TSMessage*) message withGroupContect:(TSGroupContext*)groupContext; +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.mm b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.mm new file mode 100644 index 0000000..0cc7f20 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSPushMessageContent.mm @@ -0,0 +1,180 @@ +// +// TSPushMessageContent.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSPushMessageContent.hh" +#import "PushMessageContent.pb.hh" +#import "TSContact.h" +#import "TSMessage.h" +#import "TSAttachment.h" + +@interface TSPushMessageContent () +@property (nonatomic,strong) NSString* body; +@property (nonatomic,strong) NSArray* attachments; +@property (nonatomic,strong) TSGroupContext* groupContext; +@property (nonatomic,assign) TSPushMessageFlags messageFlags; +@property (nonatomic,strong) NSData *serializedProtocolData; + +@end + +@implementation TSPushMessageContent + +-(instancetype) initWithBody:(NSString*)body withAttachments:(NSArray*)attachments withGroupContext:(TSGroupContext*)groupContext { + if(self = [super init]) { + self.body = body; + self.attachments = attachments; + self.groupContext = groupContext; + self.serializedProtocolData = [self serializedProtocolBuffer]; + self.messageFlags = TSNoFlag; + } + return self; +} + +-(instancetype) initWithTextSecureProtocolData:(NSData*) data { + return [self initWithData:data]; +} +-(NSData*) getTextSecureProtocolData { + return self.serializedProtocolBuffer; +} + + +-(instancetype) initWithData:(NSData*) data { + + if(self = [super init]) { + // c++ + textsecure::PushMessageContent *pushMessageContent = [self deserialize:data]; + const std::string cppMessage = pushMessageContent->body(); + const uint32_t cppFlags = pushMessageContent->flags(); + if(pushMessageContent->has_group()) { + + const textsecure::PushMessageContent_GroupContext groupContext = pushMessageContent->group(); + // c++ assumed + const std::string cppGroupId = groupContext.id(); + const textsecure::PushMessageContent_GroupContext_Type cppGroupType = groupContext.type(); + NSMutableArray *groupMembers = [[NSMutableArray alloc] init]; + for(int i=0; i < groupContext.members_size(); i++) { + const std::string cppMember = groupContext.members(i); + [groupMembers addObject:[[TSContact alloc] initWithRegisteredID:[self cppStringToObjc:cppMember] relay:nil]]; + } + NSData* groupId = [self cppStringToObjcData:cppGroupId]; + TSGroupContextType groupType = (TSGroupContextType) cppGroupType; + + // c++ optional + NSString* groupName = nil; + TSAttachment *groupAvatar = nil; + if(groupContext.has_name()) { + const std::string cppName = groupContext.name(); + groupName = [self cppStringToObjc:cppName]; + } + if(groupContext.has_avatar()) { + const textsecure::PushMessageContent_AttachmentPointer attachmentPointer = groupContext.avatar(); + // c++ + const std::string cppContentType = attachmentPointer.contenttype(); + const std::string cppKey = attachmentPointer.key(); + const uint64_t cppId = attachmentPointer.id(); + // Objc + NSString *contentType = [self cppStringToObjc:cppContentType]; + NSData *decryptionKey = [self cppStringToObjcData:cppKey]; + NSNumber *attachmentId = [self cppUInt64ToNSNumber:cppId]; + groupAvatar = [[TSAttachment alloc] initWithAttachmentId:attachmentId contentMIMEType:contentType decryptionKey:decryptionKey]; + + } + self.groupContext = [[TSGroupContext alloc] initWithId:groupId withType:groupType withName:groupName withMembers:groupMembers withAvatar:groupAvatar]; + } + + + NSMutableArray *messageAttachments= [[NSMutableArray alloc] init]; + for(int i=0; iattachments_size();i++) { + + const textsecure::PushMessageContent_AttachmentPointer *attachmentPointer = pushMessageContent->mutable_attachments(i); + // c++ + const std::string cppContentType = attachmentPointer->contenttype(); + const std::string cppKey = attachmentPointer->key(); + const uint64_t cppId = attachmentPointer->id(); + // Objc + NSString *contentType = [self cppStringToObjc:cppContentType]; + NSData *decryptionKey = [self cppStringToObjcData:cppKey]; + NSNumber *attachmentId = [self cppUInt64ToNSNumber:cppId]; + TSAttachment *tsAttachment = [[TSAttachment alloc] initWithAttachmentId:attachmentId contentMIMEType:contentType decryptionKey:decryptionKey]; + [messageAttachments addObject:tsAttachment]; + } + + // c++->objective C + self.body = [self cppStringToObjc:cppMessage]; + self.attachments = messageAttachments; + self.messageFlags = (TSPushMessageFlags)cppFlags; + self.serializedProtocolData = [self serializedProtocolBuffer]; // This is causing a crash, presumably because things are null that I was not expecting. + } + return self; +} + + +-(const std::string) serializedProtocolBufferAsString { + textsecure::PushMessageContent *pushMessageContent = new textsecure::PushMessageContent; + // objective c->c++ + const std::string cppMessage = [self objcStringToCpp:self.body]; + // c++->protocol buffer + pushMessageContent->set_body(cppMessage); + for(TSAttachment* attachment in self.attachments) { + textsecure::PushMessageContent_AttachmentPointer *attachmentPointer = pushMessageContent->add_attachments(); + const uint64_t attachment_id = [self objcNumberToCppUInt64:attachment.attachmentId]; + const std::string attachment_encryption_key = [self objcDataToCppString:attachment.attachmentDecryptionKey]; + std::string attachment_contenttype = [self objcStringToCpp:[attachment getMIMEContentType]]; + attachmentPointer->set_id(attachment_id); + attachmentPointer->set_key(attachment_encryption_key); + attachmentPointer->set_contenttype(attachment_contenttype); + + } + + if(self.groupContext!=nil) { + textsecure::PushMessageContent_GroupContext *serializedGroupContext = new textsecure::PushMessageContent_GroupContext; + serializedGroupContext->set_id([self objcDataToCppString:self.groupContext.gid]); + serializedGroupContext->set_type((textsecure::PushMessageContent_GroupContext_Type)self.groupContext.type); + if(self.groupContext.type == TSUpdateGroupContext) { + serializedGroupContext->set_name([self objcStringToCpp:self.groupContext.name]); + for(TSContact* member in self.groupContext.members) { + serializedGroupContext->add_members([self objcStringToCpp:member.registeredID]); + } + + if(self.groupContext.avatar!=nil) { + textsecure::PushMessageContent_AttachmentPointer *avatar = new textsecure::PushMessageContent_AttachmentPointer; + const uint64_t attachment_id = [self objcNumberToCppUInt64:self.groupContext.avatar.attachmentId]; + const std::string attachment_encryption_key = [self objcDataToCppString:self.groupContext.avatar.attachmentDecryptionKey]; + std::string attachment_contenttype = [self objcStringToCpp:[self.groupContext.avatar getMIMEContentType]]; + avatar->set_id(attachment_id); + avatar->set_key(attachment_encryption_key); + avatar->set_contenttype(attachment_contenttype); + serializedGroupContext->set_allocated_avatar(avatar); + } + } + pushMessageContent->set_allocated_group(serializedGroupContext); + + } + if(self.messageFlags) { + pushMessageContent->set_flags(self.messageFlags); + } + std::string ps = pushMessageContent->SerializeAsString(); + return ps; +} + +- (textsecure::PushMessageContent *)deserialize:(NSData *)data { + int len = [data length]; + char raw[len]; + textsecure::PushMessageContent *messageSignal = new textsecure::PushMessageContent; + [data getBytes:raw length:len]; + messageSignal->ParseFromArray(raw, len); + return messageSignal; +} + + ++ (NSData *)serializedPushMessageContentForMessage:(TSMessage*) message withGroupContect:(TSGroupContext*)groupContext{ + TSPushMessageContent* tsPushMessageContent = [[TSPushMessageContent alloc] initWithBody:message.content withAttachments:message.attachments withGroupContext:groupContext]; + return [tsPushMessageContent getTextSecureProtocolData]; +} + + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSWhisperMessage.hh b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSWhisperMessage.hh new file mode 100644 index 0000000..8ec093e --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSWhisperMessage.hh @@ -0,0 +1,20 @@ +// +// TSWhisperMessage.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSMessage.h" + +#import "TSProtocolBufferWrapper.hh" + + +@interface TSWhisperMessage : TSProtocolBufferWrapper + + + + +@end diff --git a/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSWhisperMessage.mm b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSWhisperMessage.mm new file mode 100644 index 0000000..73324de --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/ProtocolBufferWrappers/TSWhisperMessage.mm @@ -0,0 +1,14 @@ +// +// TSWhisperMessage.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/7/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSWhisperMessage.hh" + +@implementation TSWhisperMessage + + +@end diff --git a/TextSecureiOS/ProtocolBuffers/README.md b/TextSecureiOS/ProtocolBuffers/README.md new file mode 100644 index 0000000..7c336c4 --- /dev/null +++ b/TextSecureiOS/ProtocolBuffers/README.md @@ -0,0 +1,19 @@ +# protoc supported for iOS via cocoapods. +added to Podfile +```pod 'GoogleProtobuf', '~> 2.5.0'``` +ran +```pod install``` + +# protobuffer format from: +https://github.com/WhisperSystems/TextSecure/blob/push-library/library/protobuf/IncomingPushMessageSignal.proto + +#protoc compiler generate from pod +run with +```../../Pods/GoogleProtobuf/bin/protoc -I=`pwd` --cpp_out=`pwd` `pwd`/IncomingPushMessageSignal.proto ``` + +# Objective-C++ code wrapper code written +Code that includes this header is itself objective C++ and must be named accordingly (.hh/.mm extension) to compile, that means that after you compile your classes (compiler will generate pb.h and pb.cc, so these must be moved to pb.hh and pb.cc respectively). Don't forget to also change their includes to be .hh! + + + + diff --git a/TextSecureiOS/SMSViewController.h b/TextSecureiOS/SMSViewController.h deleted file mode 100644 index 54c85ef..0000000 --- a/TextSecureiOS/SMSViewController.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// ViewController.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import -#import -#import -#import -#import -#import "MessagesDatabase.h" - - -@interface SMSViewController : UITableViewController -@property (nonatomic, retain) NSArray *messages; -@property (nonatomic,retain) MessagesDatabase *messagesDB; -@property (nonatomic,retain) NSString* composingMessagePhoneNumber; -@property (nonatomic,retain) NSString* composingMessageText; -- (IBAction) Edit:(id)sender; -@end diff --git a/TextSecureiOS/SMSViewController.m b/TextSecureiOS/SMSViewController.m deleted file mode 100644 index 292d4ad..0000000 --- a/TextSecureiOS/SMSViewController.m +++ /dev/null @@ -1,138 +0,0 @@ -// -// ViewController.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "SMSViewController.h" -#import "UserDefaults.h" -#import "Message.h" -#import "Cryptography.h" -@implementation SMSViewController -@synthesize composingMessageText; -@synthesize messages; -@synthesize messagesDB; -- (void)viewDidLoad { - [super viewDidLoad]; - self.navigationController.navigationBarHidden = NO; - self.messagesDB = [[MessagesDatabase alloc] init]; - self.messages = [self.messagesDB getMessages]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadModel:) name:@"DatabaseUpdated" object:nil]; -} - --(void)viewDidAppear:(BOOL)animated { - self.navigationController.navigationBarHidden = NO; - if(![UserDefaults hasVerifiedPhone]){ - [self performSegueWithIdentifier:@"ObtainVerificationCode" sender:self]; - } - -} - --(void) reloadModel:(NSNotification*)notification { - self.messages=[self.messagesDB getMessages]; - [self.tableView reloadData]; -} - -- (IBAction)composeSMS:(id)sender { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Phone Number" message:@"Phone" delegate:self cancelButtonTitle:@"Done" otherButtonTitles:nil]; - alert.alertViewStyle = UIAlertViewStylePlainTextInput; - alert.tag = 0; - [alert show]; -} - - --(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ - NSLog(@"%@", [alertView textFieldAtIndex:0].text); - if(alertView.tag == 0) { - self.composingMessagePhoneNumber = [alertView textFieldAtIndex:0].text; - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"Message" delegate:self cancelButtonTitle:@"Done" otherButtonTitles:nil]; - alert.alertViewStyle = UIAlertViewStylePlainTextInput; - alert.tag = 1; - [alert show]; - } - else if(alertView.tag==1) { - self.composingMessageText=[alertView textFieldAtIndex:0].text; - Message *newMessage = [[Message alloc] - initWithText:self.composingMessageText - messageSource:[Cryptography getUsernameToken] - messageDestinations:[[NSArray alloc] initWithObjects:self.composingMessagePhoneNumber,nil] - messageAttachments:[[NSArray alloc] init] - messageTimestamp:[NSDate date]]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"SendMessage" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:newMessage, @"message",nil]]; - [self.messagesDB addMessage:newMessage]; - self.composingMessageText = nil; - self.composingMessagePhoneNumber = nil; - } -} - - - -/* // for custom designed cells -- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath { - // for custom designed cells - UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"TextSecureSMS"]; - UILabel *phoneNumberLabel = (UILabel *)[cell viewWithTag:0]; - UILabel *previewLabel = (UILabel *)[cell viewWithTag:1]; - UILabel *dateLabel = (UILabel *)[cell viewWithTag:2]; - return cell; -} - */ - - -- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath { - // for default cells - UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"TextSecureSMSDefault"]; - Message* message = [self.messages objectAtIndex:indexPath.row]; - cell.textLabel.text = message.source; - cell.detailTextLabel.text = message.text; - return cell; -} - - -- (void )tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - NSLog(@"Selected a cell!"); - -} - - -- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { - if(self.editing == NO || !indexPath) { - return UITableViewCellEditingStyleNone; - } - else { - return UITableViewCellEditingStyleDelete; - } -} - - -- (IBAction) Edit:(id)sender{ - if(self.editing) { - [super setEditing:NO animated:NO]; - [self.tableView setEditing:NO animated:NO]; - [self.tableView reloadData]; - - } - else { - [super setEditing:YES animated:YES]; - [self.tableView setEditing:YES animated:YES]; - [self.tableView reloadData]; - } -} - -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { - if (editingStyle == UITableViewCellEditingStyleDelete) { - // TODO: update with ability to delete - [self Edit:self]; - } - else if (editingStyle == UITableViewCellEditingStyleInsert) { - [self Edit:self]; - } -} -- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section{ - return [self.messages count]; -} - - -@end diff --git a/TextSecureiOS/Security/Cryptography.h b/TextSecureiOS/Security/Cryptography.h new file mode 100755 index 0000000..9cb02e8 --- /dev/null +++ b/TextSecureiOS/Security/Cryptography.h @@ -0,0 +1,34 @@ +// +// Cryptography.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/26/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSMessageKeys.h" + +@interface Cryptography : NSObject ++(NSMutableData*) generateRandomBytes:(int)numberBytes; +#pragma mark SHA and HMAC methods ++(NSData*) computeSHA256:(NSData *)data truncatedToBytes:(int)truncatedBytes; ++(NSString*)truncatedSHA1Base64EncodedWithoutPadding:(NSString*)string; ++ (NSString*)computeSHA1DigestForString:(NSString*)input; + ++(NSData*) computeSHA256HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey; ++(NSData*) computeSHA1HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey; ++(NSData*) truncatedSHA1HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey truncation:(int)bytes; + ++(NSData*)decryptCTRMode:(NSData*)ciphertext withKeys:(TSMessageKeys*)keys; + ++(NSData*)encryptCTRMode:(NSData*)dataToEncrypt withKeys:(TSMessageKeys*)keys; +#pragma mark decrypt symmetrically with key given to server this first layer just hides from apple encrypted protobufs message ++(NSData*) decryptAppleMessagePayload:(NSData*)payload withSignalingKey:(NSString*)signalingKeyString; + +#pragma mark encrypt and decrypt attachment data ++(NSData*) decryptAttachment:(NSData*) dataToDecrypt withKey:(NSData*) key ; ++(NSData*) encryptAttachment:(NSData*) attachment withRandomKey:(NSData**)key; + + +@end diff --git a/TextSecureiOS/Security/Cryptography.m b/TextSecureiOS/Security/Cryptography.m new file mode 100755 index 0000000..7f67f41 --- /dev/null +++ b/TextSecureiOS/Security/Cryptography.m @@ -0,0 +1,333 @@ +// +// Cryptography.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/26/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "Cryptography.h" +#import +#import +#import +#import "NSData+Conversion.h" +#import "KeychainWrapper.h" +#import "Constants.h" +#include "NSString+Conversion.h" +#include "NSData+Base64.h" +#import "FilePath.h" + +@implementation Cryptography + + +#pragma mark random bytes methods ++(NSMutableData*) generateRandomBytes:(int)numberBytes { + /* used to generate db master key, and to generate signaling key, both at install */ + NSMutableData* randomBytes = [NSMutableData dataWithLength:numberBytes]; + int err = 0; + err = SecRandomCopyBytes(kSecRandomDefault,numberBytes,[randomBytes mutableBytes]); + if(err != noErr) { + @throw [NSException exceptionWithName:@"random problem" reason:@"problem generating the random " userInfo:nil]; + } + return randomBytes; +} + + + + +#pragma mark SHA1 + ++(NSString*)truncatedSHA1Base64EncodedWithoutPadding:(NSString*)string{ + /* used by TSContactManager to send hashed/truncated contact list to server */ + NSMutableData *hashData = [NSMutableData dataWithLength:20]; + CC_SHA1([[string dataUsingEncoding:NSUTF8StringEncoding] bytes], [[string dataUsingEncoding:NSUTF8StringEncoding] length], [hashData mutableBytes]); + NSData *truncatedData = [hashData subdataWithRange:NSMakeRange(0, 10)]; + + return [[truncatedData base64EncodedString] stringByReplacingOccurrencesOfString:@"=" withString:@""]; +} + ++ (NSString*)computeSHA1DigestForString:(NSString*)input { + // Here we are taking in our string hash, placing that inside of a C Char Array, then parsing it through the SHA1 encryption method. + const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding]; + NSData *data = [NSData dataWithBytes:cstr length:input.length]; + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; + + CC_SHA1(data.bytes, data.length, digest); + + NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; + + for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { + [output appendFormat:@"%02x", digest[i]]; + } + + return output; +} + +#pragma makr SHA256 ++(NSData*) computeSHA256:(NSData *)data truncatedToBytes:(int)truncatedBytes { + uint8_t digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(data.bytes, data.length, digest); + return [[NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH] subdataWithRange:NSMakeRange(0, truncatedBytes)]; +} + + +#pragma mark HMAC/SHA256 ++(NSData*) computeSHA256HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey{ + uint8_t ourHmac[CC_SHA256_DIGEST_LENGTH] = {0}; + CCHmac(kCCHmacAlgSHA256, + [HMACKey bytes], + [HMACKey length], + [dataToHMAC bytes], + [dataToHMAC length], + ourHmac); + return [NSData dataWithBytes:ourHmac length:CC_SHA256_DIGEST_LENGTH]; +} + ++(NSData*) computeSHA1HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey{ + uint8_t ourHmac[CC_SHA256_DIGEST_LENGTH] = {0}; + CCHmac(kCCHmacAlgSHA1, + [HMACKey bytes], + [HMACKey length], + [dataToHMAC bytes], + [dataToHMAC length], + ourHmac); + return [NSData dataWithBytes:ourHmac length:CC_SHA256_DIGEST_LENGTH]; +} + + ++(NSData*) truncatedSHA1HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey truncation:(int)bytes{ + return [[Cryptography computeSHA1HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, bytes)]; +} + ++(NSData*) truncatedSHA256HMAC:(NSData*)dataToHMAC withHMACKey:(NSData*)HMACKey truncation:(int)bytes{ + return [[Cryptography computeSHA256HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, bytes)]; +} + + +#pragma mark AES CBC Mode ++(NSData*)encryptCBCMode:(NSData*) dataToEncrypt withKey:(NSData*) key withIV:(NSData*) iv withVersion:(NSData*)version withHMACKey:(NSData*) hmacKey withHMACType:(TSMACType)hmacType computedHMAC:(NSData**)hmac { + /* AES256 CBC encrypt then mac + Returns nil if encryption fails + */ + size_t bufferSize = [dataToEncrypt length] + kCCBlockSizeAES128; + void* buffer = malloc(bufferSize); + + size_t bytesEncrypted = 0; + CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, + [key bytes], [key length], + [iv bytes], + [dataToEncrypt bytes], [dataToEncrypt length], + buffer, bufferSize, + &bytesEncrypted); + + if (cryptStatus == kCCSuccess){ + NSData* encryptedData= [NSData dataWithBytesNoCopy:buffer length:bytesEncrypted]; + //compute hmac of version||encrypted data||iv + NSMutableData *dataToHmac = [NSMutableData data]; + if(version!=nil) { + [dataToHmac appendData:version]; + } + [dataToHmac appendData:iv]; + [dataToHmac appendData:encryptedData]; + + if(hmacType == TSHMACSHA1Truncated10Bytes) { + *hmac = [Cryptography truncatedSHA1HMAC:dataToHmac withHMACKey:hmacKey truncation:10]; + } + else if (hmacType == TSHMACSHA256Truncated10Bytes) { + *hmac = [Cryptography truncatedSHA256HMAC:dataToHmac withHMACKey:hmacKey truncation:10]; + } + + return encryptedData; + } + free(buffer); + return nil; + +} + + + ++(NSData*) decryptCBCMode:(NSData*) dataToDecrypt withKey:(NSData*) key withIV:(NSData*) iv withVersion:(NSData*)version withHMACKey:(NSData*) hmacKey withHMACType:(TSMACType)hmacType forHMAC:(NSData *)hmac { + /* AES256 CBC encrypt then mac + + Returns nil if hmac invalid or decryption fails + */ + //verify hmac of version||encrypted data||iv + NSMutableData *dataToHmac = [NSMutableData data ]; + if(version!=nil) { + [dataToHmac appendData:version]; + } + [dataToHmac appendData:iv]; + [dataToHmac appendData:dataToDecrypt]; + + // verify hmac + NSData* ourHmacData; + if(hmacType == TSHMACSHA1Truncated10Bytes) { + ourHmacData = [Cryptography truncatedSHA1HMAC:dataToHmac withHMACKey:hmacKey truncation:10]; + } + else if (hmacType == TSHMACSHA256Truncated10Bytes) { + ourHmacData = [Cryptography truncatedSHA256HMAC:dataToHmac withHMACKey:hmacKey truncation:10]; + } + + if(hmac == nil || ![ourHmacData isEqualToData:hmac] ) { + return nil; + } + + // decrypt + size_t bufferSize = [dataToDecrypt length] + kCCBlockSizeAES128; + void* buffer = malloc(bufferSize); + + size_t bytesDecrypted = 0; + CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, + [key bytes], [key length], + [iv bytes], + [dataToDecrypt bytes], [dataToDecrypt length], + buffer, bufferSize, + &bytesDecrypted); + if (cryptStatus == kCCSuccess) { + return [NSData dataWithBytesNoCopy:buffer length:bytesDecrypted]; + } + + free(buffer); + return nil; + + +} + +#pragma mark methods which use AES CBC ++(NSData*) decryptAppleMessagePayload:(NSData*)payload withSignalingKey:(NSString*)signalingKeyString{ + unsigned char version[1]; + unsigned char iv[16]; + NSUInteger ciphertext_length = ([payload length]-10-17)*sizeof(char); + unsigned char *ciphertext = (unsigned char*)malloc(ciphertext_length); + unsigned char mac[10]; + [payload getBytes:version range:NSMakeRange(0, 1)]; + [payload getBytes:iv range:NSMakeRange(1, 16)]; + [payload getBytes:ciphertext range:NSMakeRange(17, [payload length]-10-17)]; + [payload getBytes:mac range:NSMakeRange([payload length]-10, 10)]; + + NSData* signalingKey = [NSData dataFromBase64String:signalingKeyString]; + NSData* signalingKeyAESKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(0, 32)]; + NSData* signalingKeyHMACKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(32, 20)]; + return [Cryptography decryptCBCMode:[NSData dataWithBytesNoCopy:ciphertext length:ciphertext_length freeWhenDone:YES] withKey:signalingKeyAESKeyMaterial withIV:[NSData dataWithBytes:iv length:16] withVersion:[NSData dataWithBytes:version length:1] withHMACKey:signalingKeyHMACKeyMaterial withHMACType:TSHMACSHA256Truncated10Bytes forHMAC:[NSData dataWithBytes:mac length:10]]; + +} + ++(NSData*) decryptAttachment:(NSData*) dataToDecrypt withKey:(NSData*) key { + // key: 32 byte AES key || 32 byte Hmac-SHA256 key. + NSData *encryptionKey = [key subdataWithRange:NSMakeRange(0, 32)]; + NSData *hmacKey = [key subdataWithRange:NSMakeRange(32, 32)]; + // dataToDecrypt: IV || Ciphertext || truncated MAC(IV||Ciphertext) + NSData *iv = [dataToDecrypt subdataWithRange:NSMakeRange(0, 10)]; + NSData *encryptedAttachment = [dataToDecrypt subdataWithRange:NSMakeRange(10, [dataToDecrypt length]-10-10)]; + NSData *hmac = [dataToDecrypt subdataWithRange:NSMakeRange([dataToDecrypt length]-10, 10)]; + return [Cryptography decryptCBCMode:encryptedAttachment withKey:encryptionKey withIV:iv withVersion:nil withHMACKey:hmacKey withHMACType:TSHMACSHA256Truncated10Bytes forHMAC:hmac]; +} + + + + ++(NSData*) encryptAttachment:(NSData*) attachment withRandomKey:(NSData**)key{ + // generate + // random 10 byte IV + // key: 32 byte AES key || 32 byte Hmac-SHA256 key. + // returns: IV || Ciphertext || truncated MAC(IV||Ciphertext) + NSData* iv = [Cryptography generateRandomBytes:10]; + NSData* encryptionKey = [Cryptography generateRandomBytes:32]; + NSData* hmacKey = [Cryptography generateRandomBytes:32]; + + // The concatenated key for storage + NSMutableData *outKey = [NSMutableData data]; + [outKey appendData:encryptionKey]; + [outKey appendData:hmacKey]; + *key = [NSData dataWithData:outKey]; + + NSData* computedHMAC; + NSData* ciphertext = [Cryptography encryptCBCMode:attachment withKey:encryptionKey withIV:iv withVersion:nil withHMACKey:hmacKey withHMACType:TSHMACSHA256Truncated10Bytes computedHMAC:&computedHMAC]; + + NSMutableData* encryptedAttachment = [NSMutableData data]; + [encryptedAttachment appendData:iv]; + [encryptedAttachment appendData:ciphertext]; + [encryptedAttachment appendData:computedHMAC]; + return encryptedAttachment; +} + + +#pragma mark AESCTR Mode + ++(NSData*)encryptCTRMode:(NSData*)dataToEncrypt withKeys: (TSMessageKeys*)keys { + + /* AES256 CTR encrypt then mac + Returns nil if hmac invalid or decryption fails + */ + + size_t bufferSize = [dataToEncrypt length] + kCCBlockSizeAES128; + NSMutableData * buffer = [NSMutableData dataWithLength: bufferSize]; + size_t bytesEncrypted = 0; + + // setting up cryptor + CCCryptorStatus cryptStatus; + CCCryptorRef cryptor; + cryptStatus = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR, kCCAlgorithmAES128, + ccNoPadding, [[Cryptography counterFromNumber:[NSNumber numberWithInt:keys.counter]] bytes], + [keys.cipherKey bytes], [keys.cipherKey length], + NULL, 0, 0, kCCModeOptionCTR_BE, &cryptor); + if (cryptStatus != kCCSuccess){ + return nil; + } + + cryptStatus = CCCryptorUpdate(cryptor, [dataToEncrypt bytes], [dataToEncrypt length], [buffer mutableBytes], [buffer length], &bytesEncrypted); + if (cryptStatus != kCCSuccess){ + return nil; + } + + // Returns a non-mutable copy + [buffer setLength:bytesEncrypted]; + return [buffer copy]; +} + + + ++(NSData*)decryptCTRMode:(NSData*)ciphertext withKeys:(TSMessageKeys*)keys{ + + /* AES256 CTR encrypt then mac / validate mac then decrypt + Returns nil if hmac invalid or decryption fails + */ + + // decrypt + size_t bufferSize = [ciphertext length] + kCCBlockSizeAES128; + NSMutableData * buffer = [NSMutableData dataWithLength: bufferSize]; + size_t bytesDecrypted = 0; + + // setting up cryptor + CCCryptorStatus cryptStatus; + CCCryptorRef cryptor; + + cryptStatus = CCCryptorCreateWithMode(kCCDecrypt, kCCModeCTR, kCCAlgorithmAES128, + ccNoPadding, [[Cryptography counterFromNumber:[NSNumber numberWithInt:keys.counter]] bytes], [keys.cipherKey bytes], [keys.cipherKey length], + NULL, 0, 0, kCCModeOptionCTR_BE, &cryptor); + if (cryptStatus != kCCSuccess){ + return nil; + } + + cryptStatus = CCCryptorUpdate(cryptor, [ciphertext bytes], [ciphertext length], [buffer mutableBytes], [buffer length], &bytesDecrypted); + if (cryptStatus != kCCSuccess){ + return nil; + } + + // Returns a non-mutable copy + [buffer setLength:bytesDecrypted]; + return [buffer copy]; +} + ++(NSData*) counterFromNumber:(NSNumber*)ctr { + uint32_t ctrInt = (uint32_t)[ctr intValue]; + uint8_t bytes[16] = {0} ; + bytes[3]=(uint8_t)ctrInt; + bytes[2]=(uint8_t)(ctrInt>>8); + bytes[1]=(uint8_t)(ctrInt>>16); + bytes[0]=(uint8_t)(ctrInt>>24); + NSData* counter= [NSData dataWithBytes:&bytes length:16]; + return counter; +} + +@end diff --git a/TextSecureiOS/KeychainWrapper.h b/TextSecureiOS/Security/KeychainWrapper.h similarity index 100% rename from TextSecureiOS/KeychainWrapper.h rename to TextSecureiOS/Security/KeychainWrapper.h diff --git a/TextSecureiOS/KeychainWrapper.m b/TextSecureiOS/Security/KeychainWrapper.m similarity index 81% rename from TextSecureiOS/KeychainWrapper.m rename to TextSecureiOS/Security/KeychainWrapper.m index fb71cd4..c8fe8b0 100644 --- a/TextSecureiOS/KeychainWrapper.m +++ b/TextSecureiOS/Security/KeychainWrapper.m @@ -1,19 +1,11 @@ #import "KeychainWrapper.h" #import "Constants.h" @implementation KeychainWrapper -// *** NOTE *** This class is ARC compliant - any references to CF classes must be paired with a "__bridge" statement to -// cast between Objective-C and Core Foundation Classes. WWDC 2011 Video "Introduction to Automatic Reference Counting" explains this. -// *** END NOTE *** + (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier { - - // Setup dictionary to access keychain. NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init]; - // Specify we are using a password (rather than a certificate, internet password, etc). [searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; - // Uniquely identify this keychain accessor. [searchDictionary setObject:appName forKey:(__bridge id)kSecAttrService]; - // Uniquely identify the account who will be accessing the keychain. NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding]; [searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric]; [searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount]; @@ -21,17 +13,13 @@ + (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifie return searchDictionary; } -+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier -{ ++ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier { NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier]; - // Limit search results to one. [searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; - // Specify we want NSData/CFData returned. [searchDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; - // Search. NSData *result = nil; CFTypeRef foundDict = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &foundDict); @@ -45,21 +33,20 @@ + (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier return result; } -+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier -{ ++ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier { NSData *valueData = [self searchKeychainCopyMatchingIdentifier:identifier]; if (valueData) { NSString *value = [[NSString alloc] initWithData:valueData encoding:NSUTF8StringEncoding]; return value; - } else { + } + else { return nil; } } -+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier -{ ++ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier { NSMutableDictionary *dictionary = [self setupSearchDirectoryForIdentifier:identifier]; NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding]; @@ -74,43 +61,37 @@ + (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifi // If the addition was successful, return. Otherwise, attempt to update existing key or quit (return NO). if (status == errSecSuccess) { return YES; - } else if (status == errSecDuplicateItem){ + } + else if (status == errSecDuplicateItem){ return [self updateKeychainValue:value forIdentifier:identifier]; - } else { + } + else { return NO; } } -+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier -{ ++ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier { NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier]; NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init]; NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding]; [updateDictionary setObject:valueData forKey:(__bridge id)kSecValueData]; - // Update. OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)searchDictionary, (__bridge CFDictionaryRef)updateDictionary); if (status == errSecSuccess) { return YES; - } else { + } + else { return NO; } } -+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier -{ ++ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier { NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier]; CFDictionaryRef dictionary = (__bridge CFDictionaryRef)searchDictionary; - - //Delete. SecItemDelete(dictionary); } - - - - @end diff --git a/TextSecureiOS/Security/RKCK.h b/TextSecureiOS/Security/RKCK.h new file mode 100644 index 0000000..c8ea05c --- /dev/null +++ b/TextSecureiOS/Security/RKCK.h @@ -0,0 +1,24 @@ +// +// RKCK.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/15/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSSession.h" +@class TSChainKey; +@class TSECKeyPair; + +@interface RKCK : NSObject + +@property (nonatomic,strong) NSData* RK; +@property (nonatomic,strong) TSChainKey* CK; + ++(instancetype) initWithRK:(NSData*)rootKey CK:(TSChainKey*)chainKey; ++(instancetype) initWithRootMasterKey:(NSData*)data; + +- (RKCK*)createChainWithEphemeral:(TSECKeyPair*)myEphemeral fromTheirProvideEphemeral:(NSData*)theirPublicEphemeral; + +@end \ No newline at end of file diff --git a/TextSecureiOS/Security/RKCK.m b/TextSecureiOS/Security/RKCK.m new file mode 100644 index 0000000..52a8281 --- /dev/null +++ b/TextSecureiOS/Security/RKCK.m @@ -0,0 +1,48 @@ +// +// RKCK.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/15/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "RKCK.h" +#import "TSECKeyPair.h" +#import "TSDerivedSecrets.h" +#import "TSMessagesDatabase.h" + +@implementation RKCK + ++(instancetype) initWithRK:(NSData*)rootKey CK:(TSChainKey *)chainKey{ + RKCK *rkck = [[RKCK alloc]init]; + rkck.RK = rootKey; + rkck.CK = chainKey; + return rkck; +} + ++(instancetype) initWithRootMasterKey:(NSData*)data{ + RKCK *rkck = [[RKCK alloc] init]; + TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets derivedInitialSecretsWithMasterKey:data]; + rkck.RK = derivedSecrets.cipherKey; + rkck.CK = [[TSChainKey alloc]initWithChainKeyWithKey:derivedSecrets.macKey index:0]; + return rkck; +} + ++(instancetype) initWithRootKey:(NSData*)rootKey sharedSecret:(NSData*)sharedSecret{ + RKCK *rkck = [[RKCK alloc] init]; + TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets derivedRatchetedSecretsWithSharedSecret:sharedSecret rootKey:rootKey]; + rkck.RK = derivedSecrets.cipherKey; + rkck.CK = [[TSChainKey alloc]initWithChainKeyWithKey:derivedSecrets.macKey index:0]; + return rkck; +} + +- (instancetype)createChainWithEphemeral:(TSECKeyPair*)myEphemeral fromTheirProvideEphemeral:(NSData*)theirPublicEphemeral{ + NSData *inputKeyMaterial = [myEphemeral generateSharedSecretFromPublicKey:theirPublicEphemeral]; + return [[self class]initWithRootKey:self.RK sharedSecret:inputKeyMaterial]; +} + +-(NSString*)debugDescription { + return [NSString stringWithFormat:@"RK: %@\n",self.RK]; +} + +@end diff --git a/TextSecureiOS/Security/TSAxolotlRatchet.hh b/TextSecureiOS/Security/TSAxolotlRatchet.hh new file mode 100644 index 0000000..2cd2c4a --- /dev/null +++ b/TextSecureiOS/Security/TSAxolotlRatchet.hh @@ -0,0 +1,45 @@ +// +// TSAxolotlRatchet.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/12/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +@class TSSession; +@class TSPrekey; +@class TSWhisperMessage; +@class TSMessage; +@class TSContact; +@class TSEncryptedWhisperMessage; + +@interface TSAxolotlRatchet : NSObject + +#pragma mark Encryption methods + +/** + * The encrypt method of the Ratchet does provide a convenient method to encrypt a message. + * + * @param message A TSMessageOutgoing that contains the plaintext to encrypt. + * @param session The session used for ratcheting. + * + * @return An encrypted protocol-buffer encoded TSEncryptedWhisperMessage or TSPrekeyWhisperMessage if it's the first message to be sent. + */ + ++ (TSWhisperMessage*)encryptMessage:(TSMessage*)message withSession:(TSSession*)session; + +#pragma mark DecryptionMethods + +/** + * Reverse operation of the encrypt method. + * + * @param message A protocol buffer object of type TSEncryptedWhisperMessage or TSPrekeyWhisperMessage. + * @param session The session used for ratcheting. + * + * @return + */ + ++ (TSMessage*)decryptWhisperMessage:(TSWhisperMessage*)message withSession:(TSSession *)session; + +@end \ No newline at end of file diff --git a/TextSecureiOS/Security/TSAxolotlRatchet.mm b/TextSecureiOS/Security/TSAxolotlRatchet.mm new file mode 100644 index 0000000..d6e4ea8 --- /dev/null +++ b/TextSecureiOS/Security/TSAxolotlRatchet.mm @@ -0,0 +1,343 @@ +// +// TSAxolotlRatchet.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 1/12/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSAxolotlRatchet.hh" + +#import "TSMessage.h" +#import "TSContact.h" +#import "NSData+Base64.h" +#import "TSSubmitMessageRequest.h" +#import "TSMessagesManager.h" +#import "TSKeyManager.h" +#import "Cryptography.h" +#import "TSMessage.h" +#import "TSMessagesDatabase.h" +#import "TSUserKeysDatabase.h" +#import "TSMessageSignal.hh" +#import "TSWhisperMessage.hh" +#import "TSEncryptedWhisperMessage.hh" +#import "TSPreKeyWhisperMessage.hh" +#import "TSRecipientPrekeyRequest.h" +#import "TSMessageKeys.h" +#import "TSContact.h" +#import "Constants.h" +#import "TSSession.h" +#import "TSMessageIncoming.h" +#import "TSMessageOutgoing.h" +#import "TSPrekey.h" +#import "NSData+TSKeyVersion.h" +#import "TSPushMessageContent.hh" + + + +@implementation TSAxolotlRatchet + + +#pragma mark Public methods + +// Method for incoming messages ++ (TSMessage*)decryptWhisperMessage:(TSWhisperMessage*)message withSession:(TSSession *)session{ + if ([message isKindOfClass:[TSPreKeyWhisperMessage class]]) { + + TSPreKeyWhisperMessage *preKeyWhisperMessage = (TSPreKeyWhisperMessage*)message; + + if (session.contact.identityKey && ![session.contact.identityKey isEqualToData:preKeyWhisperMessage.identityKey]) { + throw [NSException exceptionWithName:@"MITM" reason:@"Attempt to initialize a new session with new Identity Key" userInfo:nil]; + } + + if (![session isInitialized]) { + session = [self initializeSessionAsBob:session withPreKeyWhisperMessage:preKeyWhisperMessage]; + } + + return [self decryptMessage:[[TSEncryptedWhisperMessage alloc] initWithTextSecureProtocolData:preKeyWhisperMessage.message] + withSession:session]; + } + else if ([message isKindOfClass:[TSEncryptedWhisperMessage class]]) { + return [self decryptMessage: (TSEncryptedWhisperMessage*)message + withSession:session]; + } + else { + throw [NSException exceptionWithName:@"Unrecognized TSWhisperMessageType. Can't cast. This is a programmer bug." reason:@"" userInfo:@{}]; + } +} + ++ (TSMessage*)decryptMessage:(TSEncryptedWhisperMessage*)message withSession:(TSSession*)sessionRecord{ + NSData *theirEphemeral = message.ephemeralKey; + int counter = [message.counter intValue]; + + + TSChainKey *chainKey = [self getOrCreateChainKeys:sessionRecord theirEphemeral:theirEphemeral]; + + TSMessageKeys *messageKeys = [self getOrCreateMessageKeysForSession:sessionRecord + theirEphemeral:theirEphemeral + chainKey:chainKey + counter:counter]; + + NSData *cipherTextMessage = message.message; + + BOOL validHMAC = [message verifyHMAC:messageKeys.macKey]; + + if (!validHMAC) { + @throw [NSException exceptionWithName:@"Bad HMAC" reason:@"Bad HMAC!" userInfo:nil]; + } + + NSData* decryptedPushMessageContentData = [Cryptography decryptCTRMode:cipherTextMessage withKeys:messageKeys]; + TSPushMessageContent *pushMessageContent = [[TSPushMessageContent alloc] initWithData:decryptedPushMessageContentData]; + + if (pushMessageContent==nil) { + throw [NSException exceptionWithName:@"Error decrypting message" reason:@"" userInfo:nil]; + } + + + TSGroup* group = pushMessageContent.groupContext ? [[TSGroup alloc] initWithGroupContext:pushMessageContent.groupContext] : nil; + + TSMessageIncoming *incomingMessage = [[TSMessageIncoming alloc] initMessageWithContent:pushMessageContent.body + sender:sessionRecord.contact.registeredID + date:[NSDate date] + attachements:pushMessageContent.attachments + group:group + state:TSMessageStateReceived]; + + [sessionRecord removePendingPrekey]; + [TSMessagesDatabase storeSession:sessionRecord]; + + return incomingMessage; +} + ++ (TSWhisperMessage*)encryptMessage:(TSMessage*)message withSession:(TSSession*)sessionRecord{ + + if ([sessionRecord hasPendingPreKey] && sessionRecord.needsInitialization) { + [self initializeSessionAsAlice:sessionRecord]; + sessionRecord.needsInitialization = FALSE; + } + + TSChainKey *chainKey = [sessionRecord senderChainKey]; + TSMessageKeys *messageKeys = [chainKey messageKeys]; + + TSECKeyPair *senderEphemeral = [sessionRecord senderEphemeral]; + + int previousCounter = [sessionRecord PN]; + + + + TSPushMessageContent *messageContent = [[TSPushMessageContent alloc] initWithBody:message.content withAttachments:message.attachments withGroupContext:message.group.groupContext]; + NSData *ciphertextBody = [Cryptography encryptCTRMode:[messageContent getTextSecureProtocolData] withKeys:messageKeys]; + + + if (!ciphertextBody) { + throw [NSException exceptionWithName:@"Error while encrypting" reason:@"" userInfo:nil]; + } + + TSWhisperMessage *encryptedMessage; + if ([sessionRecord hasPendingPreKey]) { + encryptedMessage = [TSPreKeyWhisperMessage constructFirstMessageWithEncryptedPushMessageContent:ciphertextBody + theirPrekeyId:[NSNumber numberWithInt:sessionRecord.pendingPreKey.prekeyId] + myCurrentEphemeral:sessionRecord.pendingPreKey.ephemeralKey + myNextEphemeral:sessionRecord.senderEphemeral.publicKey + forVersion:[self currentProtocolVersion] + withHMACKey:messageKeys.macKey]; + + + } + else { + encryptedMessage = [[TSEncryptedWhisperMessage alloc] initWithEphemeralKey:senderEphemeral.publicKey + previousCounter:[NSNumber numberWithInt:previousCounter] + counter:[NSNumber numberWithInt:chainKey.index] + encryptedPushMessageContent:ciphertextBody + forVersion:[self currentProtocolVersion] + HMACKey:messageKeys.macKey]; + + + + } + [sessionRecord setSenderChainKey:[chainKey nextChainKey]]; + [TSMessagesDatabase storeSession:sessionRecord]; + return encryptedMessage; +} + ++ (TSChainKey*)getOrCreateChainKeys:(TSSession*)session theirEphemeral:(NSData*)theirEphemeral{ + + if ([session hasReceiverChain:theirEphemeral]) { + return [session receiverChainKey:theirEphemeral]; + } + else { + // Receiving chain setup + RKCK *rootKey = [RKCK initWithRK:session.rootKey CK:nil]; + TSECKeyPair *ourEphemeral = [session senderEphemeral]; + + RKCK *receiverChain= [rootKey createChainWithEphemeral:ourEphemeral + fromTheirProvideEphemeral:theirEphemeral]; + + // Sending chain setup + TSECKeyPair *ourNewSendingEphemeral = [TSECKeyPair keyPairGenerateWithPreKeyId:0]; + + + RKCK *senderChain = [receiverChain createChainWithEphemeral:ourNewSendingEphemeral fromTheirProvideEphemeral:theirEphemeral]; + [session setSenderChain:ourNewSendingEphemeral chainkey:senderChain.CK]; + + // Saving in session + [session setRootKey:senderChain.RK]; + [session addReceiverChain:theirEphemeral chainKey:receiverChain.CK]; + [session setPN:session.senderChainKey.index-1]; + return receiverChain.CK; + } +} + ++ (TSMessageKeys*)getOrCreateMessageKeysForSession:(TSSession*)session theirEphemeral:(NSData*)theirEphemeral chainKey:(TSChainKey*)chainKey counter:(int)counter{ + + if (chainKey.index > counter) { + if ([session hasMessageKeysForEphemeral:theirEphemeral counter:counter]) { + return [session removeMessageKeysForEphemeral:theirEphemeral counter:counter]; + } + else{ + throw [NSException exceptionWithName:@"Received message with old counter!" reason:@"" userInfo:@{}]; + } + } + + if (chainKey.index - counter > 500) { + throw [NSException exceptionWithName:@"Over 500 messages into the future!" reason:@"" userInfo:@{}]; + } + + while (chainKey.index < counter) { + TSMessageKeys *messageKeys = [chainKey messageKeys]; + [session setMessageKeysWithEphemeral:theirEphemeral messageKey:messageKeys]; + chainKey = chainKey.nextChainKey; + } + + [session setReceiverChainKeyWithEphemeral:theirEphemeral chainKey:[chainKey nextChainKey]]; + return [chainKey messageKeys]; +} + ++ (TSECKeyPair*)myIdentityKey{ + return [TSUserKeysDatabase identityKey]; +} + +/** + * The current version data. First 4 bits are the current version and the last 4 ones are the lowest version we support. + * + * @return Current version data + */ + ++ (NSData*)currentProtocolVersion{ + NSUInteger index = 0b00100010; + NSData *versionByte = [NSData dataWithBytes:&index length:1]; + return versionByte; +} + + +#pragma mark Private methods ++ (void) initializeSessionAsAlice:(TSSession*) sessionRecord { + // corbett refactored: + // See slide 9 http://www.slideshare.net/ChristineCorbettMora/axolotl-protocol-an-illustrated-primer + int idPrekeyUsed = sessionRecord.pendingPreKey.prekeyId; +#warning verify if previous identity key stored! + [sessionRecord clear]; + /* A,A0,B,B0 */ + TSECKeyPair *ourIdentityKey = [self myIdentityKey]; //A + TSECKeyPair *ourBaseKey = [TSECKeyPair keyPairGenerateWithPreKeyId:0]; // A0 + NSData* theirIdentityKey = sessionRecord.pendingPreKey.identityKey; // B + NSData *theirEphemeralKey = sessionRecord.pendingPreKey.ephemeralKey; // B0 + TSECKeyPair *newSendingKey = [TSECKeyPair keyPairGenerateWithPreKeyId:0]; // A1 + // Initial 3ECDH(A,A0,B,B0) + RKCK *receivingChain = [RKCK initWithRootMasterKey:[self masterKeyAlice:ourIdentityKey + ourEphemeral:ourBaseKey + theirIdentityPublicKey:theirIdentityKey + theirEphemeralPublicKey:theirEphemeralKey]]; + + RKCK* sendingChain = [receivingChain createChainWithEphemeral:newSendingKey + fromTheirProvideEphemeral:theirEphemeralKey]; + + [sessionRecord addReceiverChain:theirEphemeralKey chainKey:receivingChain.CK]; + [sessionRecord setSenderChain:newSendingKey chainkey:sendingChain.CK]; + [sessionRecord setRootKey:sendingChain.RK]; + + [sessionRecord setPendingPreKey:[[TSPrekey alloc] initWithIdentityKey:nil + ephemeral:ourBaseKey.publicKey + prekeyId:idPrekeyUsed]]; +} + ++(TSSession*) initializeSessionAsBob:(TSSession*) sessionRecord withPreKeyWhisperMessage:(TSPreKeyWhisperMessage*)preKeyWhisperMessage{ + + TSContact *contact = sessionRecord.contact; + int deviceId = sessionRecord.deviceId; + + if (!contact.identityKey) { + contact.identityKey = preKeyWhisperMessage.identityKey; + [contact save]; + [[NSNotificationCenter defaultCenter] postNotificationName:contact.registeredID object:self]; + } + else{ + if (![contact.identityKey isEqualToData:preKeyWhisperMessage.identityKey]) { + #warning we'll want to store that message to retry decrypting later if user wants to continue + throw [NSException exceptionWithName:@"IdentityKeyMismatch" reason:@"" userInfo:@{}]; + } + } + + TSPrekey *prekey = [[TSPrekey alloc] initWithIdentityKey:preKeyWhisperMessage.identityKey + ephemeral:preKeyWhisperMessage.baseKey + prekeyId:[preKeyWhisperMessage.preKeyId intValue]]; + + TSECKeyPair *ourEphemeralKey = [TSUserKeysDatabase preKeyWithId:prekey.prekeyId]; + + if (ourEphemeralKey){ + [TSMessagesDatabase deleteSession:sessionRecord]; + TSSession *newSession = [TSMessagesDatabase sessionForRegisteredId:contact.registeredID deviceId:deviceId]; + // Initial 3ECDH(A,A0,B,B0) + RKCK *sendingChain = [RKCK initWithRootMasterKey:[self masterKeyBob:[self myIdentityKey] + ourEphemeral:ourEphemeralKey + theirIdentityPublicKey:prekey.identityKey + theirEphemeralPublicKey:prekey.ephemeralKey]]; + + [newSession setSenderChain:ourEphemeralKey chainkey:sendingChain.CK]; // this will be unused + [newSession setRootKey:sendingChain.RK]; + + if (ourEphemeralKey.preKeyId != kLastResortKeyId) { + #warning Delete that preKey! + } + return newSession; + + } + else { + #warning properly do error management + /* if session exists for that contact we just go straight to decryption process. + We probably have already processed that message. */ + @throw ([NSException exceptionWithName:@"NoPrekeyWithID" reason:@"A message was received with an unknown prekey" userInfo:@{}]); + } + +} + + ++ (NSData*)masterKeyAlice:(TSECKeyPair*)ourIdentityKeyPair ourEphemeral:(TSECKeyPair*)ourEphemeralKeyPair theirIdentityPublicKey:(NSData*)theirIdentityPublicKey theirEphemeralPublicKey:(NSData*)theirEphemeralPublicKey { + NSMutableData *masterKey = [NSMutableData data]; + [masterKey appendData:[ourIdentityKeyPair generateSharedSecretFromPublicKey:theirEphemeralPublicKey]]; + [masterKey appendData:[ourEphemeralKeyPair generateSharedSecretFromPublicKey:theirIdentityPublicKey]]; + [masterKey appendData:[ourEphemeralKeyPair generateSharedSecretFromPublicKey:theirEphemeralPublicKey]]; + return masterKey; +} + ++ (NSData*)masterKeyBob:(TSECKeyPair*)ourIdentityKeyPair ourEphemeral:(TSECKeyPair*)ourEphemeralKeyPair theirIdentityPublicKey:(NSData*)theirIdentityPublicKey theirEphemeralPublicKey:(NSData*)theirEphemeralPublicKey { + NSMutableData *masterKey = [NSMutableData data]; + + if (!(ourEphemeralKeyPair && theirEphemeralPublicKey && ourIdentityKeyPair && theirIdentityPublicKey)) { + DLog(@"Some parameters of are not defined"); + } + + + + [masterKey appendData:[ourEphemeralKeyPair generateSharedSecretFromPublicKey:theirIdentityPublicKey]]; + [masterKey appendData:[ourIdentityKeyPair generateSharedSecretFromPublicKey:theirEphemeralPublicKey]]; + [masterKey appendData:[ourEphemeralKeyPair generateSharedSecretFromPublicKey:theirEphemeralPublicKey]]; + + + + return masterKey; +} + + + +@end \ No newline at end of file diff --git a/TextSecureiOS/Security/TSChainKey.h b/TextSecureiOS/Security/TSChainKey.h new file mode 100644 index 0000000..6befc50 --- /dev/null +++ b/TextSecureiOS/Security/TSChainKey.h @@ -0,0 +1,21 @@ +// +// TSChainKey.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 02/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +@class TSMessageKeys; + +@interface TSChainKey : NSObject + +@property int index; +@property(readonly)NSData *key; + +- (instancetype)initWithChainKeyWithKey:(NSData*)key index:(int)index; +- (TSMessageKeys*)messageKeys; +- (TSChainKey*)nextChainKey; + +@end \ No newline at end of file diff --git a/TextSecureiOS/Security/TSChainKey.m b/TextSecureiOS/Security/TSChainKey.m new file mode 100644 index 0000000..b7345ea --- /dev/null +++ b/TextSecureiOS/Security/TSChainKey.m @@ -0,0 +1,75 @@ +// +// TSChainKey.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 02/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSChainKey.h" +#import "TSMessageKeys.h" +#import "TSDerivedSecrets.h" +#import "Cryptography.h" +#import + +@implementation TSChainKey + + +#define kTSKeySeedLength 1 + +static uint8_t kMessageKeySeed[kTSKeySeedLength] = {01}; +static uint8_t kChainKeySeed[kTSKeySeedLength] = {02}; + +static NSString * const kChainKeyKey = @"kChainKeyKey"; +static NSString * const kChainKeyIndex = @"kChainKeyIndex"; + +- (instancetype)initWithChainKeyWithKey:(NSData*)key index:(int)index{ + self = [super init]; + if (self) { + _key = key; + _index = index; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder{ + self = [super init]; + + if (self) { + _key = [aDecoder decodeObjectForKey:kChainKeyKey]; + _index = [aDecoder decodeIntForKey:kChainKeyIndex]; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder{ + [aCoder encodeObject:self.key forKey:kChainKeyKey]; + [aCoder encodeInteger:self.index forKey:kChainKeyIndex]; +} + +- (TSMessageKeys*)messageKeys{ + NSData *inputKeyMaterial = [self getBaseMaterial:[NSData dataWithBytes:kMessageKeySeed length:kTSKeySeedLength]]; + TSDerivedSecrets *derivedSecrets = [TSDerivedSecrets derivedMessageKeysWithData:inputKeyMaterial]; + return [[TSMessageKeys alloc] initWithCipherKey:derivedSecrets.cipherKey macKey:derivedSecrets.macKey counter:self.index]; +} + +- (TSChainKey*) nextChainKey{ + NSData* nextCK = [self getBaseMaterial:[NSData dataWithBytes:kChainKeySeed length:kTSKeySeedLength]]; + return [[TSChainKey alloc] initWithChainKeyWithKey:nextCK index:self.index+1]; +} + +- (NSString*) debugDescription { + return [NSString stringWithFormat:@"CK: %@",self.key]; +} + +- (NSData*)getBaseMaterial:(NSData*)seed{ + uint8_t result[CC_SHA256_DIGEST_LENGTH] = {0}; + 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)]; +} + +@end diff --git a/TextSecureiOS/Security/TSECKeyPair.h b/TextSecureiOS/Security/TSECKeyPair.h new file mode 100644 index 0000000..7eb6974 --- /dev/null +++ b/TextSecureiOS/Security/TSECKeyPair.h @@ -0,0 +1,53 @@ +// +// TSECKeyPair.h +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + + +@interface TSECKeyPair : NSObject { + uint8_t publicKey[32]; + uint8_t privateKey[32]; + int32_t preKeyId; +} + + +/** + * Generate a Curve25519 key pair. + * @author Alban Diquet + * + * @param preKeyId The TextSecure preKeyId to assign to the newly generated key pair. See https://github.com/WhisperSystems/TextSecure/wiki/ProtocolV2 for more information. + * @return A reference to the newly generated key pair or nil if memory could not be allocated. + */ ++(instancetype) keyPairGenerateWithPreKeyId:(int32_t)preKeyId; + + +/** + * Export the key pair's public key. + * @author Alban Diquet + * + * @return The key pair's public key. + */ +-(NSData*) publicKey; + +/** + * Return the key pair's preKeyId. + * @author Alban Diquet + * + * @return The key pair's preKeyId. + */ +-(int32_t) preKeyId; + + +/** + * Compute a shared secret using the supplied third-party public key and the key pair's private key. See https://code.google.com/p/curve25519-donna/ for more information. + * @author Alban Diquet + * + * @return The shared secret. + */ +-(NSData*) generateSharedSecretFromPublicKey:(NSData*)theirPublicKey; + + +@end diff --git a/TextSecureiOS/Security/TSECKeyPair.m b/TextSecureiOS/Security/TSECKeyPair.m new file mode 100644 index 0000000..4107aba --- /dev/null +++ b/TextSecureiOS/Security/TSECKeyPair.m @@ -0,0 +1,112 @@ +// +// TSECKeyPair.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSECKeyPair.h" +#import "Cryptography.h" +#import "NSData+TSKeyVersion.h" + + + +// Used for serializing a TSECKeyPair +NSString * const TSECKeyPairPublicKey = @"TSECKeyPairPublicKey"; +NSString * const TSECKeyPairPrivateKey = @"TSECKeyPairPrivateKey"; +NSString * const TSECKeyPairPreKeyId = @"TSECKeyPairPreKeyId"; + + +extern void curve25519_donna(unsigned char *output, const unsigned char *a, const unsigned char *b); + + +@implementation TSECKeyPair + +# pragma mark Key pair generation + ++ (TSECKeyPair*)keyPairGenerateWithPreKeyId:(int32_t)prekeyId { + TSECKeyPair* keyPair =[[TSECKeyPair alloc] init]; + + keyPair->preKeyId = prekeyId; + + // Generate key pair as described in https://code.google.com/p/curve25519-donna/ + memcpy(keyPair->privateKey, [[Cryptography generateRandomBytes:32] bytes], 32); + keyPair->privateKey[0] &= 248; + keyPair->privateKey[31] &= 127; + keyPair->privateKey[31] |= 64; + + static const uint8_t basepoint[32] = {9}; + curve25519_donna(keyPair->publicKey, keyPair->privateKey, basepoint); + + return keyPair; +} + +# pragma mark Key pair usage + + +-(NSData*) publicKey { + return [NSData dataWithBytes:self->publicKey length:32]; +} + +-(int32_t) preKeyId { + return self->preKeyId; +} + + +-(NSData*) generateSharedSecretFromPublicKey:(NSData*)theirPublicKey { + unsigned char *sharedSecret = NULL; + + if ([theirPublicKey length] != 32) { + NSLog(@"Key does not contain 32 bytes"); + @throw [NSException exceptionWithName:@"Invalid argument" reason:@" The supplied public key does not contain 32 bytes" userInfo:nil]; + } + + sharedSecret = malloc(32); + if (sharedSecret == NULL) { + return nil; + } + + // Computing shared secret using our private key and the other party's public key + curve25519_donna(sharedSecret,self->privateKey, [theirPublicKey bytes]); + + return [NSData dataWithBytes:sharedSecret length:32]; +} + + +#pragma mark Key pair serialization + +-(void)encodeWithCoder:(NSCoder *)coder { + [coder encodeBytes:self->publicKey length:32 forKey:TSECKeyPairPublicKey]; + [coder encodeBytes:self->privateKey length:32 forKey:TSECKeyPairPrivateKey]; + [coder encodeInt32:self->preKeyId forKey:TSECKeyPairPreKeyId]; +} + + +-(id)initWithCoder:(NSCoder *)coder { + self = [super init]; + if (self) { + unsigned long returnedLength = 0; + const uint8_t *returnedBuffer = NULL; + // De-serialize public key + returnedBuffer = [coder decodeBytesForKey:TSECKeyPairPublicKey returnedLength:&returnedLength]; + if (returnedLength != 32) { + return nil; + } + memcpy(self->publicKey, returnedBuffer, 32); + + // De-serialize private key + returnedBuffer = [coder decodeBytesForKey:TSECKeyPairPrivateKey returnedLength:&returnedLength]; + if (returnedLength != 32) { + return nil; + } + memcpy(self->privateKey, returnedBuffer, 32); + // De-serialize preKeyId + self->preKeyId = [coder decodeInt32ForKey:TSECKeyPairPreKeyId]; + } + return self; +} + + + +@end diff --git a/TextSecureiOS/Security/TSKeyManager.h b/TextSecureiOS/Security/TSKeyManager.h new file mode 100644 index 0000000..0cb74ac --- /dev/null +++ b/TextSecureiOS/Security/TSKeyManager.h @@ -0,0 +1,47 @@ +// +// TSKeyManager.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSECKeyPair.h" + +@interface TSKeyManager : NSObject + + +#pragma mark username ++ (BOOL) storeUsernameToken:(NSString*)token; ++ (NSString*) getUsernameToken; ++ (NSNumber*) getUserDeviceId; + +#pragma mark authentication +/* + Basic auth is username:password base64 encoded where the "username" is the device's phone number in E164 format, and the "password" is a random string you generate at registration time. + What we're doing is just using the Authorization header to convey that information, since it's more REST-ish. In subsequent calls, you'll authenticate with the same Authorization header. + */ ++(NSString*) generateNewAccountAuthenticationToken; ++ (BOOL) storeAuthenticationToken:(NSString*)token; ++ (NSString*) getAuthenticationToken; +#pragma mark authorization ++ (NSString*) getAuthorizationToken; ++ (NSString*) getAuthorizationTokenFromAuthToken:(NSString*)authToken; + +#pragma mark signalingkey + +/* The signalingKey is 32 bytes of AES material (256bit AES) and 20 bytes of Hmac key material (HmacSHA1) concatenated into a 52 byte slug that is base64 encoded. + See for usage, 52 random bytes generated at init which will be used as key material for AES256 (first 32 bytes) and HmacSHA1 */ ++(NSString*) generateNewSignalingKeyToken; + ++ (BOOL) storeSignalingKeyToken:(NSString*)token; ++ (NSString*) getSignalingKeyToken; + +#pragma mark user defaults ++(void) removeAllKeychainItems; ++(BOOL) hasVerifiedPhoneNumber; + ++(NSData*) getFingerprintFromIdentityKey:(NSData*)identityKey; + +@end diff --git a/TextSecureiOS/Security/TSKeyManager.m b/TextSecureiOS/Security/TSKeyManager.m new file mode 100644 index 0000000..b3d6ae2 --- /dev/null +++ b/TextSecureiOS/Security/TSKeyManager.m @@ -0,0 +1,108 @@ +// +// TSKeyManager.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 12/1/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSKeyManager.h" +#import "Cryptography.h" +#import "KeychainWrapper.h" +#import "NSData+Base64.h" +#import "NSData+Conversion.h" +#import "TSStorageMasterKey.h" +#import "TSStorageError.h" + +@implementation TSKeyManager + +//+ (BOOL) generateCryptographyKeysForNewUser { +// [TSKeyManager generateAndStoreIdentityKey]; +// [TSKeyManager generateAndStoreNewPreKeys:70]; +// return YES; +//} + + +#pragma mark Username (Phone number) + ++ (BOOL) storeUsernameToken:(NSString*)token { + return [KeychainWrapper createKeychainValue:token forIdentifier:usernameTokenStorageId]; +} + ++ (NSString*) getUsernameToken { + return [KeychainWrapper keychainStringFromMatchingIdentifier:usernameTokenStorageId]; +} + + +#pragma mark Authentication Token ++(NSString*) generateNewAccountAuthenticationToken { + NSMutableData* authToken = [Cryptography generateRandomBytes:16]; + NSString* authTokenPrint = [[NSData dataWithData:authToken] hexadecimalString]; + return authTokenPrint; +} + ++ (BOOL) storeAuthenticationToken:(NSString*)token { + return [KeychainWrapper createKeychainValue:token forIdentifier:authenticationTokenStorageId]; +} + + ++ (NSString*) getAuthenticationToken { + return [KeychainWrapper keychainStringFromMatchingIdentifier:authenticationTokenStorageId]; +} + + +#pragma mark Authorization Token + ++ (NSString*) getAuthorizationToken { + return [self getAuthorizationTokenFromAuthToken:[TSKeyManager getAuthenticationToken]]; +} + ++ (NSString*) getAuthorizationTokenFromAuthToken:(NSString*)authToken{ + return [NSString stringWithFormat:@"%@:%@",[TSKeyManager getUsernameToken],[TSKeyManager getAuthenticationToken]]; +} + + +#pragma mark SignalingKey + + ++(NSString*) generateNewSignalingKeyToken { + /*The signalingKey is 32 bytes of AES material (256bit AES) and 20 bytes of Hmac key material (HmacSHA1) concatenated into a 52 byte slug that is base64 encoded. */ + NSMutableData* signalingKeyToken = [Cryptography generateRandomBytes:52]; + NSString* signalingKeyTokenPrint = [[NSData dataWithData:signalingKeyToken] base64EncodedString]; + return signalingKeyTokenPrint; +} + ++ (BOOL) storeSignalingKeyToken:(NSString*)token { + return [KeychainWrapper createKeychainValue:token forIdentifier:signalingTokenStorageId]; +} + ++ (NSString*) getSignalingKeyToken { + return [KeychainWrapper keychainStringFromMatchingIdentifier:signalingTokenStorageId]; +} + +#pragma mark user defaults +// Detecting if a user has a verified phone number or not can be done by looking if a phone number is stored or not. +// If a phone number is present and no basic auth key, this means that a text message with a verification code has been sent but that the user never entered it. +// If both numbers are present, we are ready to use the app. + ++(void) removeAllKeychainItems{ + [KeychainWrapper deleteItemFromKeychainWithIdentifier:signalingTokenStorageId]; + [KeychainWrapper deleteItemFromKeychainWithIdentifier:usernameTokenStorageId]; + [KeychainWrapper deleteItemFromKeychainWithIdentifier:authenticationTokenStorageId]; +} + ++(BOOL) hasVerifiedPhoneNumber{ + return ([TSKeyManager getUsernameToken] && [TSKeyManager getAuthenticationToken] && [TSStorageMasterKey wasStorageMasterKeyCreated]); +} + ++ (NSNumber*) getUserDeviceId{ +#warning not implemented! + return [NSNumber numberWithInt:1]; +} + ++(NSData*) getFingerprintFromIdentityKey:(NSData*)identityKey { + //16-byte truncated sha-256 of public key including version byte + return [Cryptography computeSHA256:identityKey truncatedToBytes:16]; +} + +@end diff --git a/TextSecureiOS/Security/TSSendingChain.h b/TextSecureiOS/Security/TSSendingChain.h new file mode 100644 index 0000000..4b60b29 --- /dev/null +++ b/TextSecureiOS/Security/TSSendingChain.h @@ -0,0 +1,21 @@ +// +// TSSendingChain.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 13/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +@class TSChainKey; +@class TSECKeyPair; + +@interface TSSendingChain : NSObject + +- (instancetype)initWithChainKey:(TSChainKey*)chainKey ephemeral:(TSECKeyPair*)ephemeral; + +@property(readonly)TSChainKey *chainKey; +@property(readonly)TSECKeyPair *ephemeral; + + +@end diff --git a/TextSecureiOS/Security/TSSendingChain.m b/TextSecureiOS/Security/TSSendingChain.m new file mode 100644 index 0000000..cf0ac9e --- /dev/null +++ b/TextSecureiOS/Security/TSSendingChain.m @@ -0,0 +1,42 @@ +// +// TSSendingChain.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 13/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSSendingChain.h" + +@implementation TSSendingChain + +static NSString* const kChainKey = @"kChainKey"; +static NSString* const kChainEphemeral = @"kChainEphemeral"; + +- (instancetype)initWithChainKey:(TSChainKey*)chainKey ephemeral:(TSECKeyPair*)ephemeral{ + self = [super init]; + + if (self) { + _chainKey = chainKey; + _ephemeral = ephemeral; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder{ + self = [super init]; + + if (self) { + _chainKey = [aDecoder decodeObjectForKey:kChainKey]; + _ephemeral = [aDecoder decodeObjectForKey:kChainEphemeral]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder{ + [aCoder encodeObject:self.chainKey forKey:kChainKey]; + [aCoder encodeObject:self.ephemeral forKey:kChainEphemeral]; +} + + +@end diff --git a/TextSecureiOS/Security/TSStorageMasterKey.h b/TextSecureiOS/Security/TSStorageMasterKey.h new file mode 100644 index 0000000..2afa640 --- /dev/null +++ b/TextSecureiOS/Security/TSStorageMasterKey.h @@ -0,0 +1,98 @@ +// +// TSStorageMasterKey.h +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#define kStorageMasterKeyWasCreated @"StorageMasterKeyWasCreated" +#define MASTER_KEY_SIZE 32 + +@interface TSStorageMasterKey : NSObject + + +#pragma mark Storage master key generation +/** + * Create a storage master key on the device which will be used to encrypted the TextSecure databases. + * @author Alban Diquet + * + * @param userPassword The password from which the master key should be derived. + * @param error. + * @return The newly generated storage master key or nil of an error occured. + */ ++(NSData*) createStorageMasterKeyWithPassword:(NSString *)userPassword error:(NSError **) error; + +/** + * Check if the storage master key has already been generated on the device. + * @author Alban Diquet + * + * @return TRUE if key has already been generated on the device. + */ ++(BOOL) wasStorageMasterKeyCreated; + + +#pragma mark Storage master key access +/** + * Unlock the storage master key. + * @author Alban Diquet + * + * @param userPassword The password from which the master key was derived on creation. + * @param error. + * @return The storage master key or nil of an error occured. + */ ++(NSData*) unlockStorageMasterKeyUsingPassword:(NSString *)userPassword error:(NSError **)error; + +/** + * Get the previously unlocked storage master key. + * @author Alban Diquet + * + * @param error. + * @return The storage master key or nil of an error occured (such as the master key being locked). + */ ++(NSData*) getStorageMasterKeyWithError:(NSError **)error; + +/** + * Change the password of the storage master key. + * @author Christoph Schenkel + * + * @param oldPassword Current master key password + * @param newPassword New master key password + */ ++(void) changeStorageMasterKeyPasswordFrom:(NSString*)oldPassword to:(NSString*)newPassword error:(NSError **)error; + +/** + * Change the password of the storage master key without providing the current password. Storage master key need to be unlocked. + * @author Christoph Schenkel + * + * @param newPassword New master key password + */ ++(void) changeStorageMasterKeyPasswordTo:(NSString*)newPassword error:(NSError **)error; + +#pragma mark Storage master key locking +/** + * Lock the storage master key, thereby requiring a call to unlockStorageMasterKeyUsingPassword:error before the key can be recovered. + * @author Alban Diquet + */ ++(void) lockStorageMasterKey; + + +/** + * Check if the storage master key is currently in a "locked" state. + * @author Alban Diquet + * + * @return TRUE if key is locked. + */ ++(BOOL) isStorageMasterKeyLocked; + + +#pragma mark Storage master key deletion +/** + * Erase the storage master key from the device. + * @author Alban Diquet + */ ++(void) eraseStorageMasterKey; + + +@end diff --git a/TextSecureiOS/Security/TSStorageMasterKey.m b/TextSecureiOS/Security/TSStorageMasterKey.m new file mode 100644 index 0000000..5088ad8 --- /dev/null +++ b/TextSecureiOS/Security/TSStorageMasterKey.m @@ -0,0 +1,208 @@ +// +// TSStorageMasterKey.m +// TextSecureiOS +// +// Created by Alban Diquet on 12/29/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSStorageMasterKey.h" +#import "Cryptography.h" +#import "RNEncryptor.h" +#import "RNDecryptor.h" +#import "KeychainWrapper.h" +#import "NSData+Base64.h" +#import "TSStorageError.h" + + + + +static uint8_t storageMasterKey[MASTER_KEY_SIZE] = {0}; +static BOOL isMasterKeyLocked = TRUE; + + +@implementation TSStorageMasterKey + + ++(NSData*) createStorageMasterKeyWithPassword:(NSString *)userPassword error:(NSError **) error { + + if ([TSStorageMasterKey wasStorageMasterKeyCreated]) { + // A master key has already been generated on this device + if (error){ + *error = [TSStorageError errorStorageKeyAlreadyCreated]; + } + return nil; + } + + NSData *masterKey = [Cryptography generateRandomBytes:36]; + if(!masterKey) { + if (error) { + *error = [TSStorageError errorStorageKeyCreationFailed]; + } + return nil; + } + + NSData *encryptedMasterKey = [RNEncryptor encryptData:masterKey withSettings:kRNCryptorAES256Settings password:userPassword error:nil]; + if(!encryptedMasterKey) { + if (error) { + *error = [TSStorageError errorStorageKeyCreationFailed]; + } + return nil; + } + + // Store the encrypted master key in the Keychain + if (![KeychainWrapper createKeychainValue:[encryptedMasterKey base64EncodedString] forIdentifier:encryptedMasterSecretKeyStorageId]) { + if (error) { + *error = [TSStorageError errorStorageKeyCreationFailed]; + } + return nil; + } + + // Store the decrypted master key in the local buffer + isMasterKeyLocked = FALSE; + memcpy(storageMasterKey, [masterKey bytes], MASTER_KEY_SIZE); + + // Update user preferences + [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:kStorageMasterKeyWasCreated]; + [[NSUserDefaults standardUserDefaults] synchronize]; + return [NSData dataWithBytesNoCopy:storageMasterKey length:MASTER_KEY_SIZE freeWhenDone:NO]; +} + + ++(BOOL) wasStorageMasterKeyCreated { + return [[NSUserDefaults standardUserDefaults] boolForKey:kStorageMasterKeyWasCreated]; +} + + ++(NSData*) unlockStorageMasterKeyUsingPassword:(NSString *)userPassword error:(NSError **)error { + + if (![TSStorageMasterKey wasStorageMasterKeyCreated]) { + // A master key has not been generated on this device yet + if (error){ + *error = [TSStorageError errorStorageKeyNotCreated]; + } + return nil; + } + + NSString *encryptedStorageMasterKey = [KeychainWrapper keychainStringFromMatchingIdentifier:encryptedMasterSecretKeyStorageId]; + if (!encryptedStorageMasterKey) { + if (error) { + *error = [TSStorageError errorStorageKeyCorrupted]; + } + return nil; + } + + NSData *masterKey = [RNDecryptor decryptData:[NSData dataFromBase64String:encryptedStorageMasterKey] withPassword:userPassword error:error]; + if (!masterKey) { + if (error && ([*error domain] == kRNCryptorErrorDomain) && ([*error code] == kRNCryptorHMACMismatch)) { + *error = [TSStorageError errorInvalidPassword]; + } + else if (error) { + *error = [TSStorageError errorStorageKeyCorrupted]; + } + return nil; + } + + isMasterKeyLocked = FALSE; + memcpy(storageMasterKey, [masterKey bytes], MASTER_KEY_SIZE); + return [NSData dataWithBytesNoCopy:storageMasterKey length:MASTER_KEY_SIZE freeWhenDone:NO]; +} + + ++(NSData*) getStorageMasterKeyWithError:(NSError **)error { + + if (![TSStorageMasterKey wasStorageMasterKeyCreated]) { + if (error) { + *error = [TSStorageError errorStorageKeyNotCreated]; + } + return nil; + } + + if (isMasterKeyLocked) { + if (error) { + *error = [TSStorageError errorStorageKeyLocked]; + } + return nil; + } + + return [NSData dataWithBytesNoCopy:storageMasterKey length:MASTER_KEY_SIZE freeWhenDone:NO]; +} + + ++(void) lockStorageMasterKey { + // Best-effort "secure" erase; may be overkill and may not work at all + // We'll also probably have pointers to decrypted DBs hanging around :( + isMasterKeyLocked = TRUE; + + // TODO: See if this actually works the way I think it works + memset(storageMasterKey, 0, MASTER_KEY_SIZE); +} + + ++(BOOL) isStorageMasterKeyLocked { + + if (isMasterKeyLocked){ + BOOL passwordNotSet = [[NSUserDefaults standardUserDefaults] boolForKey:kPasswordNotSet]; + + if (passwordNotSet) { + NSError *error = nil; + [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:@"" error:&error]; + if (isMasterKeyLocked) { + return YES; + } + return NO; + + } else{ + return YES; + } + } else{ + return NO; + } +} + + ++(void) eraseStorageMasterKey { + [[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:kStorageMasterKeyWasCreated]; + [[NSUserDefaults standardUserDefaults] synchronize]; + isMasterKeyLocked = TRUE; + memset(storageMasterKey, 0, MASTER_KEY_SIZE); + [KeychainWrapper deleteItemFromKeychainWithIdentifier:encryptedMasterSecretKeyStorageId]; +} + ++(void) changeStorageMasterKeyPasswordFrom:(NSString*)oldPassword to:(NSString*)newPassword error:(NSError **)error { + // try to unlock master key with old password + NSData *masterKey = [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:oldPassword error:error]; + if (!masterKey) { + return; + } + + [TSStorageMasterKey changeStorageMasterKeyPasswordTo:newPassword error:error]; +} + ++(void) changeStorageMasterKeyPasswordTo:(NSString*)newPassword error:(NSError **)error { + + NSData *masterKey = [TSStorageMasterKey getStorageMasterKeyWithError:error]; + if (!masterKey) { + return; + } + + // encrypt master key with new password + NSData *encryptedMasterKey = [RNEncryptor encryptData:masterKey withSettings:kRNCryptorAES256Settings password:newPassword error:error]; + if(!encryptedMasterKey) { + if (error) { + *error = [TSStorageError errorStorageKeyCreationFailed]; + } + return; + } + + // Store the encrypted master key in the Keychain + if (![KeychainWrapper updateKeychainValue:[encryptedMasterKey base64EncodedString] forIdentifier:encryptedMasterSecretKeyStorageId]) { + if (error) { + *error = [TSStorageError errorStorageKeyCreationFailed]; + } + return; + } +} + + +@end diff --git a/TextSecureiOS/Server.h b/TextSecureiOS/Server.h deleted file mode 100644 index eae0b6e..0000000 --- a/TextSecureiOS/Server.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Server.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import -#import "Constants.h" -//curl -X POST --header "Content-Length: 0" https://textsecure-gcm.appspot.com/v1/accounts/+41799624499 -//curl -X PUT -i -H "Content-Type:application/json" --data "{\"verificationCode\" : \"958525159\", \"authenticationToken\" :\"123456\" , \"gcmRegistrationId\" : \"12345678\"}" https://textsecure-gcm.appspot.com/v1/accounts/+41799624499 -@interface Server : NSObject { - -} -@property (nonatomic,strong) NSMutableData *receivedData; -@property (nonatomic,strong) NSMutableArray *requestQueue; -@property (nonatomic,strong) NSString *number; -@property (nonatomic) int currentRequest; -#pragma mark - -#pragma mark server request queue methods --(void) doNextRequest; --(void) pushSecureRequest:(NSString*) request; - --(NSURL*) createRequestURL:(NSString*)requestStr withServer:(NSString*)server withAPI:(NSString*) api; --(void) serverAuthenticatedRequest:(NSURL*)requestUrl withData:(NSData*)requestData requestType:(NSString*)method; --(void) serverEmptyPost:(NSURL*)requestUrl; --(void) serverPut:(NSURL*)requestUrl withData:(NSData*)requestData; -#pragma mark - -#pragma mark TextSecure verbs -- (NSData*) jsonDataFromDict:(NSDictionary*)parameters; --(void) doCreateAccount:(NSNotification*) notification; --(void) doVerifyAccount:(NSNotification*) notification; --(void) doSendAPN:(NSNotification *)notification; - -#pragma mark - -#pragma mark connection delegate methods --(void) connection:(NSURLConnection*)connection didReceiveData:(NSData*)data; -- (void)connectionDidFinishLoading:(NSURLConnection *)connection; -@end diff --git a/TextSecureiOS/Server.m b/TextSecureiOS/Server.m deleted file mode 100644 index 917db24..0000000 --- a/TextSecureiOS/Server.m +++ /dev/null @@ -1,208 +0,0 @@ -// -// Server.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "Server.h" -#import "Cryptography.h" -#import "NSObject+SBJSON.h" -#import "Message.h" -@implementation Server -@synthesize receivedData; -@synthesize requestQueue; -@synthesize currentRequest; --(id) init { - if(self==[super init]) { - self.requestQueue = [[NSMutableArray alloc] init]; - // how outside world tells server to serve - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doVerifyAccount:) name:@"VerifyAccount" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doCreateAccount:) name:@"CreateAccount" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSendAPN:) name:@"SendAPN" object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSendMessage:) name:@"SendMessage" object:nil]; - - } - return self; -} - --(void) doNextRequest { - if([self.requestQueue count] > 0) { - id request = [self.requestQueue lastObject]; - [self serverEmptyPost:request]; - } -} - --(NSString*) escapeRequest:(NSString*)request { - return [[request stringByReplacingOccurrencesOfString:@" " withString:@"%20"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -} - - --(void) pushSecureRequest:(NSString*) request { - NSURL* requestUrl = [self createRequestURL:request withServer:textSecureServer withAPI:textSecureAccountsAPI]; - [self.requestQueue insertObject:requestUrl atIndex:0]; - if([self.requestQueue count]==1) { - [self doNextRequest]; - } -} - - --(void) serverAuthenticatedRequest:(NSURL*)requestUrl withData:(NSData*)requestData requestType:(NSString*)method { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0]; - NSLog(@"authorization token for put %@",[NSString stringWithFormat:@"Basic %@",[Cryptography getAuthorizationToken]]); - [request setHTTPMethod:method]; - [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - [request setValue:[NSString stringWithFormat:@"Basic %@",[Cryptography getAuthorizationToken]] forHTTPHeaderField:@"Authorization"]; - [request addValue:[NSString stringWithFormat:@"%d", [requestData length]] forHTTPHeaderField:@"Content-Length"]; - [request setHTTPBody:requestData]; - self.receivedData = [[NSMutableData alloc] init]; - id urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; - if(urlConnection==nil) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"ServerError" object:self]; - } - -} - --(void) serverPut:(NSURL*)requestUrl withData:(NSData*)requestData{ - [self serverAuthenticatedRequest:requestUrl withData:requestData requestType:@"PUT"]; -} - --(void) serverPost:(NSURL*)requestUrl withData:(NSData*)requestData{ - [self serverAuthenticatedRequest:requestUrl withData:requestData requestType:@"POST"]; -} - - - - --(void) serverEmptyPost:(NSURL*)requestUrl { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0]; - [request setHTTPMethod:@"POST"]; - [request setValue:@"0" forHTTPHeaderField:@"Content-Length"]; - self.receivedData = [[NSMutableData alloc] init]; - NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; -// [urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] -// forMode:NSDefaultRunLoopMode]; -// [urlConnection start]; - - if(urlConnection==nil) { - [[NSNotificationCenter defaultCenter] postNotificationName:@"ServerError" object:self]; - } -} - - - - --(NSURL*) createRequestURL:(NSString*)requestStr withServer:(NSString*)server withAPI:(NSString*) api{ - NSLog(@"URL STring %@",[NSString stringWithFormat:@"%@/%@/%@",server,api,[self escapeRequest:requestStr]]); - return [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%@",server,api,[self escapeRequest:requestStr]]]; -} - - -#pragma mark methods -- (NSData*) jsonDataFromDict:(NSDictionary*)parameters { - NSString* jsonRequest = [parameters JSONRepresentation]; - NSLog(@"json data %@",jsonRequest); - return [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]]; -} - --(void) doCreateAccount:(NSNotification*) notification { - self.currentRequest = CREATE_ACCOUNT; - NSString* phoneNumber = [[notification userInfo] objectForKey:@"username"]; - NSLog(@"phone is %@",phoneNumber); - [Cryptography storeUsernameToken:phoneNumber]; - [self serverEmptyPost:[self createRequestURL:phoneNumber withServer:textSecureServer withAPI:textSecureAccountsAPI]]; -} - - --(void) doVerifyAccount:(NSNotification*) notification { - self.currentRequest = VERIFY_ACCOUNT; - NSString* verificationCode = [[notification userInfo] objectForKey:@"verification_code"]; - [Cryptography generateAndStoreNewAccountAuthenticationToken]; - NSDictionary *parameters = [[NSDictionary alloc] initWithObjects: - [[NSArray alloc] initWithObjects:verificationCode,[Cryptography getAuthenticationToken], nil] - forKeys:[[NSArray alloc] initWithObjects:@"verificationCode",@"authenticationToken",nil]]; - [self serverPut:[self createRequestURL:[Cryptography getUsernameToken] withServer:textSecureServer withAPI:textSecureAccountsAPI] withData:[self jsonDataFromDict:parameters]]; -} - - --(void) doSendAPN:(NSNotification *)notification { - self.currentRequest = SEND_APN; - NSString* apn = [[notification userInfo] objectForKey:@"apnRegistrationId"]; - NSDictionary *parameters = [[NSDictionary alloc] initWithObjectsAndKeys:apn,@"apnRegistrationId", nil]; - [self serverPut:[self createRequestURL:[NSString stringWithFormat:@"%@/%@",@"apn",[Cryptography getUsernameToken]] withServer:textSecureServer withAPI:textSecureAccountsAPI] withData:[self jsonDataFromDict:parameters]]; -} - --(void) doSendMessage:(NSNotification*)notification { - self.currentRequest = SEND_MESSAGE; - Message* message = [[notification userInfo] objectForKey:@"message"]; - NSDictionary *parameters = [[NSDictionary alloc] - initWithObjectsAndKeys:message.destinations,@"destinations",message.text,@"messageText",message.attachments,@"attachments", nil]; - [self serverPost:[self createRequestURL:@"" withServer:textSecureServer withAPI:textSecureMessagesAPI] withData:[self jsonDataFromDict:parameters]]; -} - - -#pragma mark - -#pragma mark connection delegate methods -// Error handling and keeping track of when the connection finishes -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { - NSLog(@"error %@",error); - [self.requestQueue removeAllObjects]; - [self doNextRequest]; - - [[NSNotificationCenter defaultCenter] postNotificationName:@"ServerError" object:self]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { - //TODO: remove this when the server is trusted - [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; -} - --(void) connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { - [self.receivedData appendData:data]; -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection{ - NSLog(@"response %@ and len %d",self.receivedData,[self.receivedData length]); - - [self.requestQueue removeLastObject]; - [self doNextRequest]; -} - - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { - // how server alerts outside world of success - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - NSDictionary *dic = [httpResponse allHeaderFields]; - - NSLog(@"response status code and headers %d %@",[httpResponse statusCode],dic); - if(self.currentRequest == CREATE_ACCOUNT) { - if([httpResponse statusCode] == 200){ - [[NSNotificationCenter defaultCenter] postNotificationName:@"SentVerification" object:self]; - } - } - else if(self.currentRequest == VERIFY_ACCOUNT) { - if([httpResponse statusCode] == 200){ - [[NSNotificationCenter defaultCenter] postNotificationName:@"VerifiedPhone" object:self]; - } - - } - else if (self.currentRequest == SEND_APN) { - if([httpResponse statusCode] == 200){ - [[NSNotificationCenter defaultCenter] postNotificationName:@"SentAPN" object:self]; - } - - } - else if (self.currentRequest == SEND_MESSAGE) { - if([httpResponse statusCode] == 200){ - [[NSNotificationCenter defaultCenter] postNotificationName:@"SentMessage" object:self]; - } - - } -} - - - - -@end diff --git a/TextSecureiOS/TextSecureiOS-Info.plist b/TextSecureiOS/TextSecureiOS-Info.plist index ae690d5..0ad5ff7 100644 --- a/TextSecureiOS/TextSecureiOS-Info.plist +++ b/TextSecureiOS/TextSecureiOS-Info.plist @@ -10,11 +10,10 @@ ${EXECUTABLE_NAME} CFBundleIconFiles - textsecure.png textsecure@2x.png CFBundleIdentifier - com.openwhispersystems.${PRODUCT_NAME:rfc1034identifier} + org.whispersystems.whisper CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -25,10 +24,23 @@ 1.0 CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleURLName + org.whispersytems.whisper + CFBundleURLSchemes + + TextSecure + + + CFBundleVersion 1.0 LSRequiresIPhoneOS + NSMainNibFile~ipad + TextSecureStoryboard UIAppFonts OpenSans-Bold.ttf @@ -42,8 +54,12 @@ OpenSans-Semibold.ttf OpenSans-SemiboldItalic.ttf + UIBackgroundModes + + remote-notification + UIMainStoryboardFile - MainStoryboard + TextSecureStoryboard UIPrerenderedIcon UIRequiredDeviceCapabilities @@ -55,5 +71,12 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + diff --git a/TextSecureiOS/TextSecureiOS-Prefix.pch b/TextSecureiOS/TextSecureiOS-Prefix.pch index 4f1430d..986c89a 100644 --- a/TextSecureiOS/TextSecureiOS-Prefix.pch +++ b/TextSecureiOS/TextSecureiOS-Prefix.pch @@ -4,11 +4,28 @@ #import -#ifndef __IPHONE_5_0 -#warning "This project uses features only available in iOS SDK 5.0 and later." +#ifndef __IPHONE_6_0 +#warning "This project uses features only available in iOS SDK 6.0 and later." #endif #ifdef __OBJC__ - #import - #import + #import + #import + #import "NSLocale+phonePrefixes.h" + #import "NSString+PhoneFormating.h" + #import "Constants.h" + #import "NSString+escape.h" + #import "TSNetworkManager.h" + #import "TSKeyManager.h" + #define _AFNETWORKING_PIN_SSL_CERTIFICATES_ + +#endif + +#define defaultNetworkErrorMessage [[[UIAlertView alloc] initWithTitle:@"Connection issue." message:@"We couldn't reach the TextSecure server." delegate:self cancelButtonTitle:@"Ok." otherButtonTitles:nil, nil]show]; + +#ifdef DEBUG + #import + #define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); +#else + #define DLog(...) /* */ #endif diff --git a/TextSecureiOS/TextSecureiOSAssets/.dropbox b/TextSecureiOS/TextSecureiOSAssets/.dropbox deleted file mode 100644 index 48332fc..0000000 --- a/TextSecureiOS/TextSecureiOSAssets/.dropbox +++ /dev/null @@ -1 +0,0 @@ -250077073 diff --git a/TextSecureiOS/TextSecureiOSAssets/Default-568h@2x.png b/TextSecureiOS/TextSecureiOSAssets/Default-568h@2x.png new file mode 100644 index 0000000..0891b7a Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/Default-568h@2x.png differ diff --git a/TextSecureiOS/Default.png b/TextSecureiOS/TextSecureiOSAssets/Default.png similarity index 100% rename from TextSecureiOS/Default.png rename to TextSecureiOS/TextSecureiOSAssets/Default.png diff --git a/TextSecureiOS/Default@2x.png b/TextSecureiOS/TextSecureiOSAssets/Default@2x.png similarity index 100% rename from TextSecureiOS/Default@2x.png rename to TextSecureiOS/TextSecureiOSAssets/Default@2x.png diff --git a/TextSecureiOS/TextSecureiOSAssets/Default_person.png b/TextSecureiOS/TextSecureiOSAssets/Default_person.png new file mode 100755 index 0000000..5a513df Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/Default_person.png differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Default_person@2x.png b/TextSecureiOS/TextSecureiOSAssets/Default_person@2x.png new file mode 100755 index 0000000..8802720 Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/Default_person@2x.png differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Bold.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Bold.ttf deleted file mode 100644 index fd79d43..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Bold.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-BoldItalic.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-BoldItalic.ttf deleted file mode 100644 index 9bc8009..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-BoldItalic.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-ExtraBold.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-ExtraBold.ttf deleted file mode 100644 index 21f6f84..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-ExtraBold.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-ExtraBoldItalic.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-ExtraBoldItalic.ttf deleted file mode 100644 index 31cb688..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-ExtraBoldItalic.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Italic.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Italic.ttf deleted file mode 100644 index c90da48..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Italic.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Light.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Light.ttf deleted file mode 100644 index 0d38189..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Light.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-LightItalic.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-LightItalic.ttf deleted file mode 100644 index 68299c4..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-LightItalic.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Regular.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Regular.ttf deleted file mode 100644 index db43334..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Regular.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Semibold.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Semibold.ttf deleted file mode 100644 index 1a7679e..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-Semibold.ttf and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-SemiboldItalic.ttf b/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-SemiboldItalic.ttf deleted file mode 100644 index 59b6d16..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/Fonts/OpenSans-SemiboldItalic.ttf and /dev/null differ diff --git "a/TextSecureiOS/TextSecureiOSAssets/Icon\r" "b/TextSecureiOS/TextSecureiOSAssets/Icon\r" deleted file mode 100644 index e69de29..0000000 diff --git a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure.png b/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure.png deleted file mode 100644 index 51269db..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure@2x.png b/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure@2x.png index b90c511..708e7b2 100644 Binary files a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure@2x.png and b/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure@2x.png differ diff --git a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure_1024 icon.psd b/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure_1024 icon.psd deleted file mode 100644 index 7b4c322..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure_1024 icon.psd and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure_1024.png b/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure_1024.png deleted file mode 100644 index eff1886..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/MainIcon/textsecure_1024.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/flags/resizeAll.sh b/TextSecureiOS/TextSecureiOSAssets/Scripts/resizeAllFlags.sh similarity index 100% rename from TextSecureiOS/TextSecureiOSAssets/flags/resizeAll.sh rename to TextSecureiOS/TextSecureiOSAssets/Scripts/resizeAllFlags.sh diff --git a/TextSecureiOS/TextSecureiOSAssets/Scripts/resizeAllRetina.sh b/TextSecureiOS/TextSecureiOSAssets/Scripts/resizeAllRetina.sh new file mode 100644 index 0000000..db9266e --- /dev/null +++ b/TextSecureiOS/TextSecureiOSAssets/Scripts/resizeAllRetina.sh @@ -0,0 +1,5 @@ +for image in `find . -name "*@2x*png"` +do + lowres=`echo $image | sed 's/@2x//g'` + convert $image -resize 50% $lowres +done diff --git a/TextSecureiOS/TextSecureiOSAssets/MainIcon/resizeIcon.sh b/TextSecureiOS/TextSecureiOSAssets/Scripts/resizeIcon.sh similarity index 100% rename from TextSecureiOS/TextSecureiOSAssets/MainIcon/resizeIcon.sh rename to TextSecureiOS/TextSecureiOSAssets/Scripts/resizeIcon.sh diff --git a/TextSecureiOS/TextSecureiOSAssets/apple_logo_animated.gif b/TextSecureiOS/TextSecureiOSAssets/apple_logo_animated.gif new file mode 100644 index 0000000..3c344bf Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/apple_logo_animated.gif differ diff --git a/TextSecureiOS/TextSecureiOSAssets/contact_mask.png b/TextSecureiOS/TextSecureiOSAssets/contact_mask.png new file mode 100755 index 0000000..04f59c3 Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/contact_mask.png differ diff --git a/TextSecureiOS/TextSecureiOSAssets/contact_mask@2x.png b/TextSecureiOS/TextSecureiOSAssets/contact_mask@2x.png new file mode 100755 index 0000000..d483016 Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/contact_mask@2x.png differ diff --git a/TextSecureiOS/TextSecureiOSAssets/padlock_prompt.png b/TextSecureiOS/TextSecureiOSAssets/padlock_prompt.png new file mode 100644 index 0000000..287628f Binary files /dev/null and b/TextSecureiOS/TextSecureiOSAssets/padlock_prompt.png differ diff --git a/TextSecureiOS/TextSecureiOSAssets/style-guide.txt b/TextSecureiOS/TextSecureiOSAssets/style-guide.txt new file mode 100644 index 0000000..95c4f02 --- /dev/null +++ b/TextSecureiOS/TextSecureiOSAssets/style-guide.txt @@ -0,0 +1,17 @@ + + +# TextSecure iOS Style Guide + +## Global Colors + +- .green: #3de838 (61, 232, 56) + +## View Controller Styles + +### Conversation + +Note: The space between the menubar and the first message card should be 30px, I missed that in the mockup. + +- Card Font: Regular 26px, #393935 (57, 57, 53); +- Menubar Title Font: Bold 48px, White; +- Timestamp Font: Regular 18px, #6390a5 (99,144,165); \ No newline at end of file diff --git a/TextSecureiOS/TextSecureiOSAssets/textsecure_1024 icon (Tyler Reinhard's conflicted copy 2013-03-24).psd b/TextSecureiOS/TextSecureiOSAssets/textsecure_1024 icon (Tyler Reinhard's conflicted copy 2013-03-24).psd deleted file mode 100644 index 5fa9e3b..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/textsecure_1024 icon (Tyler Reinhard's conflicted copy 2013-03-24).psd and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/textsecure_1024-icon-(0.2).png b/TextSecureiOS/TextSecureiOSAssets/textsecure_1024-icon-(0.2).png deleted file mode 100644 index f7aefde..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/textsecure_1024-icon-(0.2).png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_guidelines_arrow.png b/TextSecureiOS/TextSecureiOSAssets/tutorial_guidelines_arrow.png deleted file mode 100644 index 8787be0..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_guidelines_arrow.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_guidelines_arrow.psd b/TextSecureiOS/TextSecureiOSAssets/tutorial_guidelines_arrow.psd deleted file mode 100644 index 6bc20aa..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_guidelines_arrow.psd and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button.png b/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button.png deleted file mode 100644 index 13ccb67..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button.psd b/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button.psd deleted file mode 100644 index 5a85c3f..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button.psd and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button@2x.png b/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button@2x.png deleted file mode 100644 index 906eb7c..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_ok_button@2x.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_tutorial_arrow.png b/TextSecureiOS/TextSecureiOSAssets/tutorial_tutorial_arrow.png deleted file mode 100644 index 5d42a64..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_tutorial_arrow.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/tutorial_tutorial_arrow.psd b/TextSecureiOS/TextSecureiOSAssets/tutorial_tutorial_arrow.psd deleted file mode 100644 index 512ce06..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/tutorial_tutorial_arrow.psd and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-countrycode-field.png b/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-countrycode-field.png deleted file mode 100644 index 2258543..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-countrycode-field.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-gettingstarted-menubar.png b/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-gettingstarted-menubar.png deleted file mode 100644 index e2526c6..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-gettingstarted-menubar.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-phonenumber-field.png b/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-phonenumber-field.png deleted file mode 100644 index d149624..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-phonenumber-field.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-selectyourcountry-field.png b/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-selectyourcountry-field.png deleted file mode 100644 index 90997f9..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-selectyourcountry-field.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-verifybutton.png b/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-verifybutton.png deleted file mode 100644 index 8dcd7c3..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/vc-verification/textsecure-verifybutton.png and /dev/null differ diff --git a/TextSecureiOS/TextSecureiOSAssets/vc-verification/verification-viewcontroller-mockup.png b/TextSecureiOS/TextSecureiOSAssets/vc-verification/verification-viewcontroller-mockup.png deleted file mode 100644 index 115ad80..0000000 Binary files a/TextSecureiOS/TextSecureiOSAssets/vc-verification/verification-viewcontroller-mockup.png and /dev/null differ diff --git a/TextSecureiOS/UserDefaults.h b/TextSecureiOS/UserDefaults.h deleted file mode 100644 index 6284f75..0000000 --- a/TextSecureiOS/UserDefaults.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// UserDefaults.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/27/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import - -@interface UserDefaults : NSObject - -+(BOOL) hasVerifiedPhone; -+(void) markVerifiedPhone; -+(BOOL) hasSentVerification; -+(void) markSentVerification; -@end diff --git a/TextSecureiOS/UserDefaults.m b/TextSecureiOS/UserDefaults.m deleted file mode 100644 index 23d8b76..0000000 --- a/TextSecureiOS/UserDefaults.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// UserDefaults.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/27/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "UserDefaults.h" - -@implementation UserDefaults - -+(BOOL) hasVerifiedPhone { - return [[NSUserDefaults standardUserDefaults] boolForKey:@"hasVerifiedPhone"]; -} - -+(void) markVerifiedPhone { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"hasVerifiedPhone"]; - [[NSUserDefaults standardUserDefaults] synchronize]; -} - - -+(BOOL) hasSentVerification { - return [[NSUserDefaults standardUserDefaults] boolForKey:@"hasSentVerification"]; -} - -+(void) markSentVerification{ - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"hasSentVerification"]; - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -@end diff --git a/TextSecureiOS/Utility/Constants.h b/TextSecureiOS/Utility/Constants.h new file mode 100644 index 0000000..b88a923 --- /dev/null +++ b/TextSecureiOS/Utility/Constants.h @@ -0,0 +1,68 @@ +// +// Constants.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +//Colors +#import "UIColor+TextSecure.h" + +#define kLastResortKeyId 0xFFFFFF + +extern NSString* const textSecureServer; +extern NSString* const textSecureGeneralAPI; +extern NSString* const textSecureAccountsAPI; +extern NSString* const textSecureKeysAPI; + +extern NSString* const textSecureMessagesAPI; +extern NSString* const textSecureDirectoryAPI; +extern NSString* const textSecureAttachmentsAPI; +extern NSString* const appName; +extern NSString* const authenticationTokenStorageId; +extern NSString* const usernameTokenStorageId; +extern NSString* const signalingTokenStorageId; +extern NSString* const prekeyCounterStorageId; +extern NSString* const encryptedMasterSecretKeyStorageId; +extern NSString* const textSecureAttachmentsAPI; +extern NSTimeInterval const timeOutForRequests; +extern unsigned char const textSecureVersion; +extern NSString* const textSecureWebSocketAPI; +// CountryCodes.plist constants +extern NSString* const countryInfoPathInMainBundle; +extern NSString* const countryInfoKeyCountryCode; +extern NSString* const countryInfoKeyName; + +typedef NS_ENUM(NSInteger, TSGroupContextType) { + TSUnknownGroupContext = 0, + TSUpdateGroupContext = 1, + TSDeliverGroupContext = 2, + TSQuitGroupContext =3 +}; + +typedef NS_ENUM(NSInteger, TSPushMessageFlags) { + TSNoFlag = 0, + TSEndSession = 1 +}; + +typedef NS_ENUM(NSInteger, TSWhisperMessageType) { + TSUnknownMessageType =0, + TSEncryptedWhisperMessageType = 1, + TSIgnoreOnIOSWhisperMessageType=2, // on droid this is the prekey bundle message irrelevant for us + TSPreKeyWhisperMessageType = 3, + TSUnencryptedWhisperMessageType = 4, +}; + +typedef NS_ENUM(NSInteger, TSMACType) { + TSHMACSHA1Truncated10Bytes = 1, + TSHMACSHA256Truncated10Bytes = 2 + +}; + + +#define kPasswordNotSet @"PasswordNotSet" +#define kTSVersion +#define kScreenshotProtection @"screenshotProtection" +#define kDBNewMessageNotification @"db_new_message" +#define kContactIdentity @"db_new_message" diff --git a/TextSecureiOS/Utility/Constants.m b/TextSecureiOS/Utility/Constants.m new file mode 100644 index 0000000..a48c70c --- /dev/null +++ b/TextSecureiOS/Utility/Constants.m @@ -0,0 +1,35 @@ +// +// Constants.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + + +#import "Constants.h" + +NSString* const appName = @"TextSecure"; +NSString* const authenticationTokenStorageId = @"TextSecureAuthenticationToken"; +NSString* const usernameTokenStorageId = @"UsernameAuthenticationToken"; +NSString* const signalingTokenStorageId = @"SignalingTokenStorageId"; +NSString* const prekeyCounterStorageId = @"PrekeyCounterStorageId"; +NSString* const encryptedMasterSecretKeyStorageId = @"EncryptedMasterSecretKeyStorageId"; + +NSString* const textSecureWebSocketAPI = @"wss://textsecure-service.whispersystems.org/v1/websocket/"; +NSString* const textSecureServer = @"https://textsecure-service.whispersystems.org/"; + +NSString* const textSecureGeneralAPI = @"v1"; +NSString* const textSecureAccountsAPI = @"v1/accounts"; +NSString* const textSecureMessagesAPI = @"v1/messages/"; // NOTE trailing slash is important +NSString* const textSecureKeysAPI = @"v1/keys"; +NSString* const textSecureDirectoryAPI= @"v1/directory"; +NSString* const textSecureAttachmentsAPI= @"v1/attachments"; + +NSTimeInterval const timeOutForRequests = 10; +unsigned char const textSecureVersion = 0; + +// CountryCodes.plist constants +NSString* const countryInfoPathInMainBundle = @"CountryCodes"; +NSString* const countryInfoKeyCountryCode = @"country_code"; +NSString* const countryInfoKeyName = @"name"; diff --git a/TextSecureiOS/FilePath.h b/TextSecureiOS/Utility/FilePath.h similarity index 84% rename from TextSecureiOS/FilePath.h rename to TextSecureiOS/Utility/FilePath.h index 2ce2c53..7c2bf5f 100644 --- a/TextSecureiOS/FilePath.h +++ b/TextSecureiOS/Utility/FilePath.h @@ -12,4 +12,5 @@ + (NSString *)applicationDocumentsDirectory; + (NSString*)pathInDocumentsDirectory:(NSString*) fileBasename; ++ (NSString*)pathInBundleDirectory:(NSString*) fileBasename; @end diff --git a/TextSecureiOS/FilePath.m b/TextSecureiOS/Utility/FilePath.m similarity index 79% rename from TextSecureiOS/FilePath.m rename to TextSecureiOS/Utility/FilePath.m index 26c0724..a378317 100644 --- a/TextSecureiOS/FilePath.m +++ b/TextSecureiOS/Utility/FilePath.m @@ -9,8 +9,8 @@ #import "FilePath.h" @implementation FilePath + + (NSString *)applicationDocumentsDirectory { - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; return basePath; @@ -19,4 +19,9 @@ + (NSString *)applicationDocumentsDirectory { + (NSString*)pathInDocumentsDirectory:(NSString*) fileBasename { return [NSString stringWithFormat:@"%@/%@",[FilePath applicationDocumentsDirectory],fileBasename]; } + ++ (NSString*)pathInBundleDirectory:(NSString*) fileBasename { + return [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] resourcePath],fileBasename]; +} + @end diff --git a/TextSecureiOS/Utility/NSData+Base64.h b/TextSecureiOS/Utility/NSData+Base64.h new file mode 100644 index 0000000..14e2716 --- /dev/null +++ b/TextSecureiOS/Utility/NSData+Base64.h @@ -0,0 +1,7 @@ + +@interface NSData (Base64) + ++ (NSData *)dataFromBase64String:(NSString *)aString; +- (NSString *)base64EncodedString; + +@end diff --git a/TextSecureiOS/Utility/NSData+Base64.m b/TextSecureiOS/Utility/NSData+Base64.m new file mode 100644 index 0000000..fd0d06f --- /dev/null +++ b/TextSecureiOS/Utility/NSData+Base64.m @@ -0,0 +1,37 @@ + +#import "NSData+Base64.h" + + +@implementation NSData (Base64) + +// +// dataFromBase64String: +// +// Creates an NSData object containing the base64 decoded representation of +// the base64 string 'aString' +// +// Parameters: +// aString - the base64 string to decode +// +// returns the NSData representation of the base64 string +// ++ (NSData *)dataFromBase64String:(NSString *)aString +{ + return [[NSData alloc] initWithBase64EncodedString:aString options:NSDataBase64DecodingIgnoreUnknownCharacters]; +} + +// +// base64EncodedString +// +// Creates an NSString object that contains the base 64 encoding of the +// receiver's data. Lines are broken at 64 characters long. +// +// returns an NSString being the base 64 representation of the +// receiver. +// +- (NSString *)base64EncodedString +{ + return [self base64EncodedStringWithOptions:0]; +} + +@end diff --git a/TextSecureiOS/NSData+Conversion.h b/TextSecureiOS/Utility/NSData+Conversion.h similarity index 100% rename from TextSecureiOS/NSData+Conversion.h rename to TextSecureiOS/Utility/NSData+Conversion.h diff --git a/TextSecureiOS/NSData+Conversion.m b/TextSecureiOS/Utility/NSData+Conversion.m similarity index 94% rename from TextSecureiOS/NSData+Conversion.m rename to TextSecureiOS/Utility/NSData+Conversion.m index af0d3a8..a54f32a 100644 --- a/TextSecureiOS/NSData+Conversion.m +++ b/TextSecureiOS/Utility/NSData+Conversion.m @@ -18,7 +18,7 @@ -(NSString *)hexadecimalString { NSUInteger dataLength = [self length]; NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; - for (int i = 0; i < dataLength; ++i) { + for (NSUInteger i = 0; i < dataLength; ++i) { [hexString appendFormat:@"%02x", dataBuffer[i]]; } return [NSString stringWithString:hexString]; diff --git a/TextSecureiOS/Utility/NSData+TSKeyVersion.h b/TextSecureiOS/Utility/NSData+TSKeyVersion.h new file mode 100644 index 0000000..6d98743 --- /dev/null +++ b/TextSecureiOS/Utility/NSData+TSKeyVersion.h @@ -0,0 +1,16 @@ +// +// NSData+TSKeyVersion.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 17/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface NSData (TSKeyVersion) + +- (NSData*)prependVersionByte; +- (NSData*)removeVersionByte; + +@end diff --git a/TextSecureiOS/Utility/NSData+TSKeyVersion.m b/TextSecureiOS/Utility/NSData+TSKeyVersion.m new file mode 100644 index 0000000..a39e23c --- /dev/null +++ b/TextSecureiOS/Utility/NSData+TSKeyVersion.m @@ -0,0 +1,27 @@ +// +// NSData+TSKeyVersion.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 17/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "NSData+TSKeyVersion.h" + +@implementation NSData (TSKeyVersion) + +- (NSData*)prependVersionByte{ + NSMutableData *concatenatedData = [NSMutableData data]; + uint intVal = 0x05; + Byte byteData[1]; + byteData[0] = intVal; + [concatenatedData appendBytes:byteData length:1]; + [concatenatedData appendData:self]; + return [concatenatedData copy]; +} + +-(NSData *)removeVersionByte{ + return [self subdataWithRange:NSMakeRange(1, 32)]; // THIS IS CRASHING +} + +@end diff --git a/TextSecureiOS/NSString+Conversion.h b/TextSecureiOS/Utility/NSString+Conversion.h similarity index 89% rename from TextSecureiOS/NSString+Conversion.h rename to TextSecureiOS/Utility/NSString+Conversion.h index bd2ed14..84028ee 100644 --- a/TextSecureiOS/NSString+Conversion.h +++ b/TextSecureiOS/Utility/NSString+Conversion.h @@ -12,5 +12,5 @@ #pragma mark - Base 64 Conversion - (NSString *)base64Encoded; --(NSString *) rot13String; +- (NSString *)unformattedPhoneNumber; @end diff --git a/TextSecureiOS/Utility/NSString+Conversion.m b/TextSecureiOS/Utility/NSString+Conversion.m new file mode 100644 index 0000000..f7f3367 --- /dev/null +++ b/TextSecureiOS/Utility/NSString+Conversion.m @@ -0,0 +1,31 @@ +// +// NSString+Conversion.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/27/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "NSString+Conversion.h" + +// +// NSData+Conversion.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/26/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// +@implementation NSString (NSString_Conversion) + +#pragma mark - Base 64 Conversion +- (NSString *)base64Encoded { + return [[self dataUsingEncoding: NSASCIIStringEncoding] base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; +} + +- (NSString *)unformattedPhoneNumber { + NSCharacterSet *toExclude = [NSCharacterSet characterSetWithCharactersInString:@"/.()- "]; + return [[self componentsSeparatedByCharactersInSet:toExclude] componentsJoinedByString: @""]; +} + + +@end \ No newline at end of file diff --git a/TextSecureiOS/Utility/NSString+randomString.h b/TextSecureiOS/Utility/NSString+randomString.h new file mode 100644 index 0000000..8e59f23 --- /dev/null +++ b/TextSecureiOS/Utility/NSString+randomString.h @@ -0,0 +1,15 @@ +// +// NSString+randomString.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 23/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface NSString (randomString) + ++ (NSString*)genRandStringLength:(int)len; + +@end diff --git a/TextSecureiOS/Utility/NSString+randomString.m b/TextSecureiOS/Utility/NSString+randomString.m new file mode 100644 index 0000000..aadf8b5 --- /dev/null +++ b/TextSecureiOS/Utility/NSString+randomString.m @@ -0,0 +1,26 @@ +// +// NSString+randomString.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 23/03/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "NSString+randomString.h" + +@implementation NSString (randomString) + +static NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + ++ (NSString*)genRandStringLength:(int)len { + + NSMutableString *randomString = [NSMutableString stringWithCapacity: len]; + + for (int i=0; i + +@interface TSReplaceSegue : UIStoryboardSegue + +@end diff --git a/TextSecureiOS/Utility/TSReplaceSegue.m b/TextSecureiOS/Utility/TSReplaceSegue.m new file mode 100644 index 0000000..87d1cf9 --- /dev/null +++ b/TextSecureiOS/Utility/TSReplaceSegue.m @@ -0,0 +1,25 @@ +// +// TSReplaceSegue.m +// TextSecureiOS +// +// Created by Daniel Cestari on 3/12/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSReplaceSegue.h" + +// lossly based on: http://stackoverflow.com/a/21415225/452964 + +@implementation TSReplaceSegue + +- (void)perform +{ + UIViewController *sourceViewController = self.sourceViewController; + UIViewController *destinationController = self.destinationViewController; + UINavigationController *navigationController = sourceViewController.navigationController; + + [navigationController popToRootViewControllerAnimated:NO]; + [navigationController pushViewController:destinationController animated:YES]; +} + +@end diff --git a/TextSecureiOS/Utility/TSStartOverSegue.h b/TextSecureiOS/Utility/TSStartOverSegue.h new file mode 100644 index 0000000..4c9fe5e --- /dev/null +++ b/TextSecureiOS/Utility/TSStartOverSegue.h @@ -0,0 +1,13 @@ +// +// TSStartOverSegue.h +// TextSecureiOS +// +// Created by Daniel Cestari on 3/15/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSStartOverSegue : UIStoryboardSegue + +@end diff --git a/TextSecureiOS/Utility/TSStartOverSegue.m b/TextSecureiOS/Utility/TSStartOverSegue.m new file mode 100644 index 0000000..3898344 --- /dev/null +++ b/TextSecureiOS/Utility/TSStartOverSegue.m @@ -0,0 +1,24 @@ +// +// TSStartOverSegue.m +// TextSecureiOS +// +// Created by Daniel Cestari on 3/15/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSStartOverSegue.h" + +// lossly based on: http://stackoverflow.com/a/21415225/452964 + +@implementation TSStartOverSegue + +- (void)perform +{ + UIViewController *sourceViewController = self.sourceViewController; + UIViewController *destinationController = self.destinationViewController; + UINavigationController *navigationController = sourceViewController.navigationController; + + [navigationController setViewControllers:@[destinationController] animated:YES]; +} + +@end diff --git a/TextSecureiOS/VerificationViewController.h b/TextSecureiOS/VerificationViewController.h deleted file mode 100644 index 5000305..0000000 --- a/TextSecureiOS/VerificationViewController.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// VerificationViewController.h -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import -#import "Server.h" -@interface VerificationViewController : UIViewController -@property (nonatomic,strong) IBOutlet UILabel *countryCode; -@property (nonatomic,strong) IBOutlet UITextField *countryCodeInput; -@property (nonatomic,strong) IBOutlet UILabel *countryName; -@property (nonatomic,strong) IBOutlet UITextField *phoneNumber; -@property (nonatomic,strong) IBOutlet UITextField *verificationCodePart1; -@property (nonatomic,strong) IBOutlet UITextField *verificationCodePart2; -@property (nonatomic,strong) IBOutlet UIImageView *flag; -@property (nonatomic,strong) IBOutlet UIButton *verifyButton; -@property (nonatomic,strong) IBOutlet UIScrollView *scrollView; - -@property (nonatomic,strong) IBOutlet UILabel *explanationText; -@property (nonatomic,strong) IBOutlet UILabel *youPhoneNumberText; -@property (nonatomic,strong) IBOutlet UILabel *youPhoneNumberTextDescription; -@property (nonatomic,strong) IBOutlet UILabel *findCountryCodeText; -@property (nonatomic,strong) IBOutlet UILabel *findCountryCodeTextDescription; - -@property (nonatomic,strong) IBOutlet UILabel *verificationTextExplanation; -@property (nonatomic,strong) IBOutlet UILabel *verificationCompletionExplanation; - - -@property (nonatomic,strong) NSString* selectedPhoneNumber; -@property (nonatomic,strong) NSMutableDictionary* countryDict; - --(IBAction)doVerifyPhone:(id)sender; --(void) countryChosen:(NSNotification*)notification; -- (void)didReceiveMemoryWarning; --(void) registerForKeyboardNotifications; --(void)finishedVerifiedPhone:(NSNotification*)notification; --(void)finishedSendVerification:(NSNotification*)notification; --(IBAction)sentVerification:(id)sender; --(void) configureFonts; --(void)updateCountry:(NSDictionary*)countryInfo; -@end - - diff --git a/TextSecureiOS/VerificationViewController.m b/TextSecureiOS/VerificationViewController.m deleted file mode 100644 index 89c0f9e..0000000 --- a/TextSecureiOS/VerificationViewController.m +++ /dev/null @@ -1,224 +0,0 @@ -// -// VerificationViewController.m -// TextSecureiOS -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "VerificationViewController.h" - -@interface VerificationViewController () - -@end - -@implementation VerificationViewController -@synthesize phoneNumber; -@synthesize countryCode; -@synthesize countryName; - -@synthesize verificationCodePart1; -@synthesize verificationCodePart2; -@synthesize selectedPhoneNumber; -@synthesize flag; -@synthesize scrollView; -@synthesize countryCodeInput; -@synthesize verifyButton; - -@synthesize explanationText; -@synthesize youPhoneNumberText; -@synthesize youPhoneNumberTextDescription; -@synthesize findCountryCodeText; -@synthesize findCountryCodeTextDescription; -@synthesize verificationTextExplanation; -@synthesize verificationCompletionExplanation; -@synthesize countryDict; - - -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - if (self) { - // Custom initialization - - } - return self; -} - --(void) configureFonts { - /* - "OpenSans-Light", - "OpenSans-Extrabold", - OpenSans, - "OpenSans-Italic", - "OpenSansLight-Italic", - "OpenSans-Semibold", - "OpenSans-SemiboldItalic", - "OpenSans-Bold", - "OpenSans-BoldItalic", - "OpenSans-ExtraboldItalic" - */ - [countryCode setFont:[UIFont fontWithName:@"OpenSans" size:14]]; - [countryCodeInput setFont:[UIFont fontWithName:@"OpenSans" size:20]]; - [phoneNumber setFont:[UIFont fontWithName:@"OpenSans" size:20]]; - [verificationCodePart1 setFont:[UIFont fontWithName:@"OpenSans" size:20]]; - [verificationCodePart2 setFont:[UIFont fontWithName:@"OpenSans" size:20]]; - [explanationText setFont:[UIFont fontWithName:@"OpenSans" size:14]]; - [youPhoneNumberText setFont:[UIFont fontWithName:@"OpenSans" size:14]]; - [youPhoneNumberTextDescription setFont:[UIFont fontWithName:@"OpenSans" size:10]]; - [findCountryCodeText setFont:[UIFont fontWithName:@"OpenSans" size:14]]; - [findCountryCodeTextDescription setFont:[UIFont fontWithName:@"OpenSans" size:10]]; - - [verificationTextExplanation setFont:[UIFont fontWithName:@"OpenSans" size:20]]; - [verificationCompletionExplanation setFont:[UIFont fontWithName:@"OpenSans" size:20]]; - -} - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view. - self.verificationCodePart2.delegate = self; - self.verificationCodePart1.delegate = self; - self.title = selectedPhoneNumber; - self.flag.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@.png",@"us"]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(countryChosen:) name:@"CountryChosen" object:nil]; - [self registerForKeyboardNotifications]; - [self configureFonts]; - [countryCodeInput addTarget:self action:@selector(updateCountryCode:) forControlEvents:UIControlEventEditingChanged]; - self.countryDict = [[NSMutableDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"CountryCodes" ofType:@"plist"]]; - - // also key by country code - for (NSString* key in [self.countryDict allKeys]) { - // TODO: save this reoriganization - NSDictionary* data =[self.countryDict objectForKey:key]; - [self.countryDict setObject:data forKey:[data objectForKey:@"country_code"]]; - } - - -} - --(void)updateCountryCode:(id)sender { - if ([self.countryDict objectForKey:self.countryCodeInput.text]) { - NSLog(@"country found %@",[self.countryDict objectForKey:self.countryCodeInput.text]); - [self updateCountry:[self.countryDict objectForKey:self.countryCodeInput.text]]; - } -} - --(void)updateCountry:(NSDictionary*)countryInfo { - self.flag.image=[UIImage imageNamed:[NSString stringWithFormat:@"%@.png",[[countryInfo objectForKey:@"code"] lowercaseString]]]; - self.countryCode.text = [NSString stringWithFormat:@"[+%@]",[countryInfo objectForKey:@"country_code"]]; - self.countryCodeInput.text = [countryInfo objectForKey:@"country_code"]; - self.countryName.text=[countryInfo objectForKey:@"name"]; - -} - --(void) countryChosen:(NSNotification*)notification { - NSLog(@"country chosen"); - [self updateCountry:[notification userInfo]]; -} - - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - --(IBAction)doVerifyPhone:(id)sender { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishedVerifiedPhone:) name:@"VerifiedPhone" object:nil]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"VerifyAccount" object:self userInfo:[[NSDictionary alloc] initWithObjectsAndKeys:[NSString stringWithFormat:@"%@%@",verificationCodePart1.text,verificationCodePart2.text], @"verification_code", nil]]; -} - --(void)finishedVerifiedPhone:(NSNotification*)notification { - // register for push notifications - [[UIApplication sharedApplication] registerForRemoteNotificationTypes: - (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; - [self performSegueWithIdentifier:@"BeginUsingApp" sender:self]; - -} - --(void)finishedSendVerification:(NSNotification*)notification { - [self performSegueWithIdentifier:@"ConfirmVerificationCode" sender:self]; -} - --(IBAction)sentVerification:(id)sender { - self.selectedPhoneNumber = [NSString stringWithFormat:@"+%@%@",self.countryCode.text,self.phoneNumber.text]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"CreateAccount" object:self userInfo:[[NSDictionary alloc] initWithObjectsAndKeys:self.self.selectedPhoneNumber, @"username", nil]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishedSendVerification:) name:@"SentVerification" object:nil]; -} - - --(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ - if([segue.identifier isEqualToString:@"ConfirmVerificationCode"]){ - VerificationViewController *controller = (VerificationViewController *)segue.destinationViewController; - controller.selectedPhoneNumber= self.selectedPhoneNumber; - } -} - -#define MAX_LENGTH 3 // Whatever your limit is -- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { - //TODO: not called - NSUInteger newLength = (textView.text.length - range.length) + text.length; - if(newLength <= MAX_LENGTH) { - return YES; - } else { - NSUInteger emptySpace = MAX_LENGTH - (textView.text.length - range.length); - textView.text = [[[textView.text substringToIndex:range.location] - stringByAppendingString:[text substringToIndex:emptySpace]] - stringByAppendingString:[textView.text substringFromIndex:(range.location + range.length)]]; - return NO; - } -} - -// Called when the UIKeyboardDidShowNotification is sent. -- (void)keyboardWasShown:(NSNotification*)aNotification { - NSDictionary* info = [aNotification userInfo]; - CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; - UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); - scrollView.contentInset = contentInsets; - scrollView.scrollIndicatorInsets = contentInsets; - - // If verify button field is hidden by keyboard, scroll it so it's visible - // Your application might not need or want this behavior. - CGRect aRect = self.view.frame; - CGPoint aPoint; - aRect.size.height -= kbSize.height; - if(phoneNumber!=NULL) { - aPoint = self.verifyButton.frame.origin; - } - else { - aPoint = verificationCodePart1.frame.origin; - } - if (CGRectContainsPoint(aRect, aPoint )) { - // iPhone 5 hack :( TODO: figure out how to remove - float offset = 0.0; - CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size; - if (iOSDeviceScreenSize.height == 568) { - offset = -68.0; - } - CGPoint scrollPoint = CGPointMake(0.0, aPoint.y+kbSize.height+offset); - - [scrollView setContentOffset:scrollPoint animated:YES]; - } -} - - -- (void)registerForKeyboardNotifications { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWasShown:) - name:UIKeyboardDidShowNotification object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillBeHidden:) - name:UIKeyboardWillHideNotification object:nil]; - -} - - -// Called when the UIKeyboardWillHideNotification is sent -- (void)keyboardWillBeHidden:(NSNotification*)aNotification { - UIEdgeInsets contentInsets = UIEdgeInsetsZero; - scrollView.contentInset = contentInsets; - scrollView.scrollIndicatorInsets = contentInsets; -} - - -@end diff --git a/TextSecureiOS/ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.h b/TextSecureiOS/ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.h new file mode 100644 index 0000000..6bf118d --- /dev/null +++ b/TextSecureiOS/ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.h @@ -0,0 +1,23 @@ +// +// TSGroupSetupViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/8/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSGroup.h" + + +@interface TSGroupSetupViewController : UIViewController +@property(nonatomic,strong) IBOutlet UITextField *groupName; +@property(nonatomic,strong) IBOutlet UIButton *groupPhoto; +@property(nonatomic,strong) IBOutlet UIBarButtonItem *nextButton; +@property(nonatomic,strong) NSArray* whisperContacts; +@property(nonatomic,strong) TSGroup* group; +- (IBAction) setGroupPhotoPressed:(UIButton *)sender; +-(IBAction)createNonBroadcastGroup:(id)sender; +-(IBAction)createBroadcastGroup:(id)sender; +-(void)createGroup; +@end diff --git a/TextSecureiOS/ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.m b/TextSecureiOS/ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.m new file mode 100644 index 0000000..33f5215 --- /dev/null +++ b/TextSecureiOS/ViewControllers/Conversations/Group Messaging/TSGroupSetupViewController.m @@ -0,0 +1,134 @@ +// +// TSGroupSetupViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/8/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSGroupSetupViewController.h" +#import "TSMessageViewController.h" +#import "TextSecureViewController.h" +#import "Cryptography.h" +#import "TSMessageOutgoing.h" +#import "TSMessagesManager.h" +@interface TSGroupSetupViewController () + +@end + +@implementation TSGroupSetupViewController + +-(id) initWithCoder:(NSCoder *)aDecoder { + if(self = [super initWithCoder:aDecoder]) { + self.group = [[TSGroup alloc] init]; + } + return self; + +} +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view. + + self.nextButton.enabled = NO; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + +- (IBAction) setGroupPhotoPressed:(UIButton *)sender { + UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Take Photo or Video",@"Choose Existing", nil]; + [actionSheet showInView:self.view]; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; + imagePicker.delegate = self; + + imagePicker.mediaTypes = @[(NSString *) kUTTypeImage]; + + imagePicker.allowsEditing = NO; + + switch (buttonIndex) { + case 0: + imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; + break; + case 1: + imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + break; + case 2: + // cancel + return; + default: + break; + } + [self presentViewController:imagePicker animated:YES completion:nil]; + +} + + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + if([[textField.text stringByReplacingCharactersInRange:range withString:string] length]>0) { + self.nextButton.enabled = YES; + } + else { + self.nextButton.enabled = NO; + + } + return YES; +} + + +-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { + UIImage *image = info[UIImagePickerControllerOriginalImage]; + [self.groupPhoto setImage:image forState:UIControlStateNormal]; + self.groupPhoto.layer.cornerRadius =self.groupPhoto.bounds.size.width/1.6; + self.groupPhoto.layer.masksToBounds = YES; + [self dismissViewControllerAnimated:YES completion:nil]; +} + + +-(IBAction)createNonBroadcastGroup:(id)sender { + [self setupGroup]; +} + + +-(IBAction)createBroadcastGroup:(id)sender { + self.group.isBroadcastGroup = YES; + [self setupGroup]; +} + + +-(void)setupGroup { +#warning want to pick the group id generation length etc. as the droid +#warning no avatar support yet will come with attachments support + // https://github.com/WhisperSystems/TextSecure/blob/d5f04159074544d715628b870aa048993ee69b7d/src/org/thoughtcrime/securesms/util/GroupUtil.java + self.group.groupName = self.groupName.text; + self.group.groupImage = self.groupPhoto.imageView.image; + NSMutableArray *groupMembers = [NSMutableArray arrayWithArray:self.whisperContacts]; + [groupMembers addObject:[[TSContact alloc] initWithRegisteredID:[TSKeyManager getUsernameToken] relay:nil]]; + self.group.groupContext = [[TSGroupContext alloc] initWithId:[TSGroupContext createNewGroupId] withType:TSUpdateGroupContext withName:self.group.groupName withMembers:groupMembers withAvatar:nil]; + TSMessageOutgoing *message = nil; + if(self.group.isBroadcastGroup) { + message = [[TSMessageOutgoing alloc]initBroadcastMessageWithContent:@"" recipient:nil date:[NSDate date] attachements:@[] group:self.group state:TSMessageStatePendingSend]; + } + else { + message = [[TSMessageOutgoing alloc]initMessageWithContent:@"" recipient:nil date:[NSDate date] attachements:@[] group:self.group state:TSMessageStatePendingSend]; + } + [[TSMessagesManager sharedManager] scheduleMessageSend:message]; + [self performSegueWithIdentifier:@"GroupComposeMessageSegue" sender:nil]; + +} + +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if ([segue.destinationViewController isKindOfClass:[TSMessageViewController class]]) { + TSMessageViewController *vc = segue.destinationViewController; + vc.group = self.group; + } +} + +@end diff --git a/TextSecureiOS/ViewControllers/Conversations/TSMessageViewController.h b/TextSecureiOS/ViewControllers/Conversations/TSMessageViewController.h new file mode 100644 index 0000000..61e394b --- /dev/null +++ b/TextSecureiOS/ViewControllers/Conversations/TSMessageViewController.h @@ -0,0 +1,35 @@ +// +// ComposeMessageViewController.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/30/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "JSMessagesViewController.h" +#import "TSContact.h" +@class TSGroup; +@class TSMessage; +@class TSAttachment; + +/** + * ComposeMessageViewController has two constructors, one for 1-to-1 discussions and the other one for group discussions. + */ + +@interface TSMessageViewController : JSMessagesViewController + +/** + * Constructor for 1-to-1 discussions + * + * @param contact Contact to have discussion with + * + * @return A viewcontroller for displaying discussions + */ + +@property (nonatomic, retain) TSContact *contact; +@property (nonatomic, retain) TSGroup *group; +@property (nonatomic) TSWhisperMessageType messagingType; + +@end + diff --git a/TextSecureiOS/ViewControllers/Conversations/TSMessageViewController.m b/TextSecureiOS/ViewControllers/Conversations/TSMessageViewController.m new file mode 100644 index 0000000..ef91dd5 --- /dev/null +++ b/TextSecureiOS/ViewControllers/Conversations/TSMessageViewController.m @@ -0,0 +1,340 @@ + // +// ComposeMessageViewController.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/30/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageViewController.h" +#import "TSMessagesManager.h" +#import "TSContactManager.h" +#import "TSContact.h" +#import "TSMessagesDatabase.h" +#import "TSMessageOutgoing.h" +#import "TSKeyManager.h" +#import "TSAttachment.h" +#import "TSAttachmentManager.h" +#import "Cryptography.h" +#import "FilePath.h" +#import "TSGroup.h" +#import "Emoticonizer.h" +#import "TSVerifyIdentityViewController.h" + +@interface TSMessageViewController () + +@property (nonatomic, retain) NSArray *messages; + +@end + +@implementation TSMessageViewController + +- (void)reloadMessages { + if (!self.group) { + self.messages = [TSMessagesDatabase messagesWithContact:self.contact]; + } + else { + self.messages = [TSMessagesDatabase messagesForGroup:self.group]; + } + + [self.tableView reloadData]; +} + +-(void) setupThread { + self.title = [self.contact name]; + [self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)]; //scrolls to bottom +} + + + +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if ([[segue identifier] isEqualToString:@"ProfileSegue"]) { + + ((TSVerifyIdentityViewController*)segue.destinationViewController).contact = self.contact; + } +} + +- (void) dismissVC { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + [self setupThread]; + self.delegate = self; + self.dataSource = self; + if (self.group) { + if([[self.group groupName] length]>0) { + self.title = self.group.groupName; + } + else if (![self.group isBroadcastGroup]) { + self.title = @"Group message"; + } + else { + self.title = @"Broadcast message"; + } + } else { + self.messages = [TSMessagesDatabase messagesWithContact:self.contact]; + } + + [self.view setBackgroundColor:[UIColor whiteColor]]; + self.tableView.frame = CGRectMake(0, 0, self.tableView.frame.size.width, self.view.frame.size.height - 44); + + [[NSNotificationCenter defaultCenter] addObserverForName:kDBNewMessageNotification object:nil queue:nil usingBlock:^(NSNotification *note) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self reloadMessages]; + [self.tableView reloadData]; + }); + }]; + + + + + if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) { + self.edgesForExtendedLayout = UIRectEdgeNone; + } + + if (self.group) { + if([[self.group groupName] length]>0) { + self.title = self.group.groupName; + } + else if (![self.group isBroadcastGroup]) { + self.title = @"Group message"; + } + else { + self.title = @"Broadcast message"; + } + self.messages = [TSMessagesDatabase messagesForGroup:self.group]; + [[NSNotificationCenter defaultCenter] addObserverForName:[self.group.groupContext getEncodedId] object:nil queue:nil usingBlock:^(NSNotification *note) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self displayProfileOptionIfAvailable]; + }); + }]; + } + else { + self.title = [self.contact name]; + self.messages = [TSMessagesDatabase messagesWithContact:self.contact]; + [[NSNotificationCenter defaultCenter] addObserverForName:self.contact.registeredID object:nil queue:nil usingBlock:^(NSNotification *note) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.contact = [TSMessagesDatabase contactForRegisteredID:self.contact.registeredID]; + [self displayProfileOptionIfAvailable]; + }); + }]; + [self displayProfileOptionIfAvailable]; + } + //[self reloadMessages]; + +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Table view data source + +-(void) displayProfileOptionIfAvailable { + if(self.group==nil && self.contact.identityKey && !self.contact.identityKeyIsVerified) { + self.navigationItem.rightBarButtonItem.enabled=YES; + + } + else { + self.navigationItem.rightBarButtonItem.enabled = NO; + } + +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return [self.messages count]; +} + +#pragma mark - Messages view delegate + +- (JSMessageInputViewStyle)inputViewStyle{ + return JSMessageInputViewStyleFlat; +} + + + +- (UIImageView *)avatarImageViewForRowAtIndexPath:(NSIndexPath *)indexPath{ + return nil; +} + +- (void)didSendText:(NSString *)text { + + self.group.groupContext.type = TSDeliverGroupContext; + + TSMessageOutgoing *message = nil; + if(self.group.isBroadcastGroup) { + message = [[TSMessageOutgoing alloc]initBroadcastMessageWithContent:text recipient:nil date:[NSDate date] attachements:@[] group:self.group state:TSMessageStatePendingSend]; + } + else { + message = [[TSMessageOutgoing alloc]initMessageWithContent:text recipient:[self.contact registeredID] date:[NSDate date] attachements:@[] group:self.group state:TSMessageStatePendingSend]; + + } + // if(message.attachment.attachmentType!=TSAttachmentEmpty) { + // // this is asynchronous so message will only be send by messages manager when it succeeds + // [TSAttachmentManager uploadAttachment:message]; + // } + + [[TSMessagesManager sharedManager] scheduleMessageSend:message]; + + [self reloadMessages]; + + [self finishSend]; +} + +- (void)photoPressed:(UIButton *)sender { + UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Take Photo or Video",@"Choose Existing", nil]; + [actionSheet showInView:self.view]; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; + imagePicker.delegate = self; + + imagePicker.mediaTypes = @[(NSString *) kUTTypeImage, (NSString *) kUTTypeMovie]; + + imagePicker.allowsEditing = NO; + + switch (buttonIndex) { + case 0: + imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; + break; + case 1: + imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + break; + case 2: + // cancel + return; + default: + break; + } + [self presentViewController:imagePicker animated:YES completion:nil]; + +} + +-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { + + NSString *mediaType = info[UIImagePickerControllerMediaType]; + + NSData* attachmentData; + TSAttachmentType attachmentType = TSAttachmentEmpty; + if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) { + UIImage *image = info[UIImagePickerControllerOriginalImage]; + attachmentData= UIImagePNGRepresentation(image); + attachmentType = TSAttachmentPhoto; + } + else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) { + NSURL *videoURL = info[UIImagePickerControllerMediaURL]; + + attachmentData=[NSData dataWithContentsOfURL:videoURL]; + attachmentType = TSAttachmentVideo; + } + // encryption attachment data, write to file, and initialize the attachment + NSData *randomEncryptionKey; + NSData *encryptedData = [Cryptography encryptAttachment:attachmentData withRandomKey:&randomEncryptionKey]; + //NSString* filename = [[Cryptography truncatedHMAC:encryptedData withHMACKey:randomEncryptionKey truncation:10] base64EncodedStringWithOptions:0]; + //NSString* writeToFile = [FilePath pathInDocumentsDirectory:filename]; + //[encryptedData writeToFile:writeToFile atomically:YES]; + //self.attachment = [[TSAttachment alloc] initWithAttachmentDataPath:writeToFile withType:attachmentType withDecryptionKey:randomEncryptionKey]; + //size of button + [self dismissViewControllerAnimated:YES completion:nil]; +} + +-(void) reloadModel:(NSNotification*)notification { + [self.tableView reloadData]; + if([[[notification userInfo] objectForKey:@"messageType"] isEqualToString:@"send"]) { + [JSMessageSoundEffect playMessageSentSound]; + } + else { + [JSMessageSoundEffect playMessageReceivedSound]; + } +} + +- (UIImageView *)bubbleImageViewWithType:(JSBubbleMessageType)type + forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (type == JSBubbleMessageTypeIncoming) { + return [JSBubbleImageViewFactory bubbleImageViewForType:type + color:[UIColor js_bubbleLightGrayColor]]; + } else{ + + return [JSBubbleImageViewFactory bubbleImageViewForType:type + color:[UIColor js_bubbleBlueColor]]; + } +} + +- (JSBubbleMessageType)messageTypeForRowAtIndexPath:(NSIndexPath *)indexPath { + if([[[self.messages objectAtIndex:indexPath.row] senderId] isEqualToString:[TSKeyManager getUsernameToken]]) { + return JSBubbleMessageTypeOutgoing; + } + else { + return JSBubbleMessageTypeIncoming; + } +} + +- (JSMessagesViewTimestampPolicy)timestampPolicy { + return JSMessagesViewTimestampPolicyEveryThree; +} + +- (JSMessagesViewAvatarPolicy)avatarPolicy { + return JSMessagesViewAvatarPolicyNone; +} + +#pragma mark - Messages view data source +//- (BOOL) shouldHaveThumbnailForRowAtIndexPath:(NSIndexPath*)indexPath { +// TSAttachment *attachment = [[self.messages objectAtIndex:indexPath.row] attachment]; +// return attachment.attachmentType != TSAttachmentEmpty; +//} +//- (UIImage *)thumbnailForRowAtIndexPath:(NSIndexPath *)indexPath { +// TSAttachment *attachment = [[self.messages objectAtIndex:indexPath.row] attachment]; +// return [attachment getThumbnailOfSize:100]; +//} + +- (NSString *)textForRowAtIndexPath:(NSIndexPath *)indexPath { + if(![self shouldHaveSubtitleForRowAtIndexPath:indexPath]) { + TSMessage* message = [self.messages objectAtIndex:indexPath.row]; + if(message.isBroadcast) { + return [@"BROADCAST: " stringByAppendingString:[Emoticonizer emoticonizeString:[[self.messages objectAtIndex:indexPath.row] content]]]; + } + else { + return [Emoticonizer emoticonizeString:[[self.messages objectAtIndex:indexPath.row] content]]; + } + } + else { + return nil; + } +} + +- (NSDate *)timestampForRowAtIndexPath:(NSIndexPath *)indexPath { + return [(TSMessage*)[self.messages objectAtIndex:indexPath.row] timestamp]; +} + + +//- (JSMessagesViewSubtitlePolicy)subtitlePolicy{ +// return JSMessagesViewSubtitlePolicyAll; +//} + +- (NSString *)subtitleForRowAtIndexPath:(NSIndexPath *)indexPath{ + return [Emoticonizer emoticonizeString:[[self.messages objectAtIndex:indexPath.row] content]]; +} + +- (BOOL)shouldHaveSubtitleForRowAtIndexPath:(NSIndexPath *)indexPath { + TSGroupContextType meta = [[self.messages objectAtIndex:indexPath.row] metaMessage]; + return meta == TSUpdateGroupContext || meta == TSQuitGroupContext; +} + + + + + + +- (UIImage *)avatarImageForIncomingMessage { + return nil; +} + +- (UIImage *)avatarImageForOutgoingMessage { + return nil; +} + +@end diff --git a/TextSecureiOS/CountrySegue.h b/TextSecureiOS/ViewControllers/CountrySegue.h similarity index 100% rename from TextSecureiOS/CountrySegue.h rename to TextSecureiOS/ViewControllers/CountrySegue.h diff --git a/TextSecureiOS/CountrySegue.m b/TextSecureiOS/ViewControllers/CountrySegue.m similarity index 99% rename from TextSecureiOS/CountrySegue.m rename to TextSecureiOS/ViewControllers/CountrySegue.m index 3318f7d..b9b83fc 100644 --- a/TextSecureiOS/CountrySegue.m +++ b/TextSecureiOS/ViewControllers/CountrySegue.m @@ -11,7 +11,6 @@ @implementation CountrySegue - (void) perform { - UIViewController *src = (UIViewController *) self.sourceViewController; [src.navigationController popViewControllerAnimated:YES]; } diff --git a/TextSecureiOS/CountryViewController.h b/TextSecureiOS/ViewControllers/CountryViewController.h similarity index 66% rename from TextSecureiOS/CountryViewController.h rename to TextSecureiOS/ViewControllers/CountryViewController.h index 795c933..4518b53 100644 --- a/TextSecureiOS/CountryViewController.h +++ b/TextSecureiOS/ViewControllers/CountryViewController.h @@ -7,11 +7,9 @@ // #import +#import "Constants.h" @interface CountryViewController : UITableViewController - -@property (nonatomic,strong) NSDictionary* countryDict; -@property (nonatomic,strong) NSArray* countryList; - - +@property (nonatomic, strong) NSDictionary *countryDict; +@property (nonatomic, strong) NSArray *countryList; @end diff --git a/TextSecureiOS/CountryViewController.m b/TextSecureiOS/ViewControllers/CountryViewController.m similarity index 76% rename from TextSecureiOS/CountryViewController.m rename to TextSecureiOS/ViewControllers/CountryViewController.m index 56ab8b9..3707703 100644 --- a/TextSecureiOS/CountryViewController.m +++ b/TextSecureiOS/ViewControllers/CountryViewController.m @@ -7,37 +7,27 @@ // #import "CountryViewController.h" -#import "VerificationViewController.h" -@interface CountryViewController () - -@end @implementation CountryViewController -@synthesize countryList; -@synthesize countryDict; -- (id)initWithStyle:(UITableViewStyle)style -{ + +- (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; - if (self) { - // Custom initialization - } + if (!self) return nil; + return self; } -- (void)viewDidLoad -{ +- (void)viewDidLoad { [super viewDidLoad]; - - - - NSString *plistFile = [[NSBundle mainBundle] pathForResource:@"CountryCodes" ofType:@"plist"]; - - self.countryDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile]; - self.countryList = [[self.countryDict allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - // finally we export to a plist + + NSString *plistFile = [[NSBundle mainBundle] pathForResource:countryInfoPathInMainBundle ofType:@"plist"]; + + self.countryDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile]; + self.countryList = [[self.countryDict allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + // finally we export to a plist // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; - + self.navigationController.navigationBarHidden =NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; } @@ -69,12 +59,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } - - -#pragma mark - Table view delegate - - -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"CountrySegue"]) { NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; NSString* countryTitle = [self.countryList objectAtIndex:indexPath.row]; diff --git a/TextSecureiOS/ViewControllers/PasswordUnlockViewController.h b/TextSecureiOS/ViewControllers/PasswordUnlockViewController.h new file mode 100644 index 0000000..0402e24 --- /dev/null +++ b/TextSecureiOS/ViewControllers/PasswordUnlockViewController.h @@ -0,0 +1,15 @@ +// +// PasswordUnlockViewController.h +// TextSecureiOS +// +// Created by Claudiu-Vlad Ursache on 29/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +@interface PasswordUnlockViewController : UIViewController +@property (strong, nonatomic) IBOutlet UIView *pwUnderlineView; +@property (strong, nonatomic) IBOutlet UIImageView *padView; + +@end diff --git a/TextSecureiOS/ViewControllers/PasswordUnlockViewController.m b/TextSecureiOS/ViewControllers/PasswordUnlockViewController.m new file mode 100644 index 0000000..d393b06 --- /dev/null +++ b/TextSecureiOS/ViewControllers/PasswordUnlockViewController.m @@ -0,0 +1,132 @@ +// +// PasswordUnlockViewController.m +// TextSecureiOS +// +// Created by Claudiu-Vlad Ursache on 29/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "PasswordUnlockViewController.h" +#import "TSUserKeysDatabase.h" +#import "TSStorageMasterKey.h" +#import "TSStorageError.h" +#import "TSMessagesDatabase.h" +#import "TSWaitingPushMessageDatabase.h" +#import "TSSetMasterPasswordViewController.h" + +@interface PasswordUnlockViewController () +@property(nonatomic, strong) IBOutlet UITextField *passwordTextField; +@end + +@implementation PasswordUnlockViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (!self) return nil; + + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + if (![[NSUserDefaults standardUserDefaults] boolForKey:kPasswordIsAlphanumerical]) { + + self.passwordTextField.keyboardType = UIKeyboardTypeNumberPad; + + // number pad does not provide UIReturnKeyGo, so use a accessory view + UIToolbar *keyboardDoneButtonView = [[UIToolbar alloc] init]; + UIBarButtonItem *switchKeyboards = [[UIBarButtonItem alloc]initWithTitle:NSLocalizedString(@"alphanumeric", nil) style:UIBarButtonItemStyleBordered target:self action:@selector(switchKeyboardToAlphanumeric)]; + UIBarButtonItem *spacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; + UIBarButtonItem *goButton = [[UIBarButtonItem alloc] initWithTitle:@"Go" + style:UIBarButtonItemStyleDone target:self + action:@selector(doneTapped)]; + [keyboardDoneButtonView setItems:@[switchKeyboards, spacer, goButton]]; + [keyboardDoneButtonView sizeToFit]; + self.passwordTextField.inputAccessoryView = keyboardDoneButtonView; + } + + self.passwordTextField.placeholder = @"Please enter your password"; + self.passwordTextField.returnKeyType = UIReturnKeyGo; + [self.passwordTextField becomeFirstResponder]; + self.passwordTextField.delegate = self; +} + +- (IBAction)unlockPressed:(id)sender { + + NSString *password = self.passwordTextField.text; + + NSError *error = nil; + [TSStorageMasterKey unlockStorageMasterKeyUsingPassword:password error:&error]; + BOOL didUnlock = ![TSStorageMasterKey isStorageMasterKeyLocked]; + + if (didUnlock) { + self.pwUnderlineView.backgroundColor = [UIColor TSValidColor]; + [[NSNotificationCenter defaultCenter] postNotificationName:TSDatabaseDidUnlockNotification object:self]; + [self dismissViewControllerAnimated:YES completion:nil]; + + } else { + if ([[error domain] isEqualToString:TSStorageErrorDomain]) { + switch ([error code]) { + case TSStorageErrorInvalidPassword: { + [self shake:self.padView]; + self.passwordTextField.text = nil; + self.passwordTextField.placeholder = @"Please try again."; + + break; + } + default: { + // TODO: proper error handling + + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil + message:error.localizedDescription + delegate:self + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alertView show]; + + break; + } + } + } + } +} + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + + [self unlockPressed:textField]; + + return YES; +} + +#pragma mark - Keyboard Accessory View + +- (void)doneTapped { + [self unlockPressed:self.passwordTextField]; +} + +- (void)switchKeyboardToAlphanumeric { + self.passwordTextField.keyboardType = UIKeyboardTypeDefault; + self.passwordTextField.inputAccessoryView = nil; + [self.passwordTextField resignFirstResponder]; + [self.passwordTextField becomeFirstResponder]; +} + +#pragma mark - Shake if password incorrect +- (void)shake:(UIImageView*)imageView +{ + CABasicAnimation *animation = + [CABasicAnimation animationWithKeyPath:@"position"]; + [animation setDuration:0.05]; + [animation setRepeatCount:4]; + [animation setAutoreverses:YES]; + [animation setFromValue:[NSValue valueWithCGPoint: + CGPointMake([imageView center].x - 10.0f, [imageView center].y)]]; + [animation setToValue:[NSValue valueWithCGPoint: + CGPointMake([imageView center].x + 10.0f, [imageView center].y)]]; + [[imageView layer] addAnimation:animation forKey:@"position"]; +} + +@end diff --git a/TextSecureiOS/ViewControllers/SignUpStepsViewController.h b/TextSecureiOS/ViewControllers/SignUpStepsViewController.h new file mode 100644 index 0000000..4ca7634 --- /dev/null +++ b/TextSecureiOS/ViewControllers/SignUpStepsViewController.h @@ -0,0 +1,15 @@ +// +// SignUpStepsViewController.h +// TextSecureiOS +// +// Created by Dylan Bourgeois on 15/04/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "RMStepsController.h" +#import "Constants.h" + +@interface SignUpStepsViewController : RMStepsController + +@end diff --git a/TextSecureiOS/ViewControllers/SignUpStepsViewController.m b/TextSecureiOS/ViewControllers/SignUpStepsViewController.m new file mode 100644 index 0000000..a86eef6 --- /dev/null +++ b/TextSecureiOS/ViewControllers/SignUpStepsViewController.m @@ -0,0 +1,42 @@ +// +// SignUpStepsViewController.m +// TextSecureiOS +// +// Created by Dylan Bourgeois on 15/04/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "SignUpStepsViewController.h" +#import "VerificationViewController.h" +#import "VerificationCodeViewController.h" +#import "TSSocketManager.h" +#import "TSSetMasterPasswordViewController.h" + + +@implementation SignUpStepsViewController + +- (NSArray *)stepViewControllers { + [self.stepsBar setHideCancelButton:YES]; + [self.step setEnabledBarColor:[UIColor TSValidColor]]; + VerificationViewController *firstStep = [self.storyboard instantiateViewControllerWithIdentifier:@"VerificationViewController"]; + firstStep.step.title = @"Phone Number"; + + VerificationCodeViewController *secondStep = [self.storyboard instantiateViewControllerWithIdentifier:@"VerificationCodeViewController"]; + secondStep.step.title = @"Verification Code"; + + TSSetMasterPasswordViewController *thirdStep = [self.storyboard instantiateViewControllerWithIdentifier:@"TSSetMasterPasswordViewController"]; + thirdStep.step.title = @"Password"; + + return @[firstStep, secondStep, thirdStep]; +} + +- (void)finishedAllSteps { + [self performSegueWithIdentifier:@"BeginUsingApp" sender:self]; + [TSSocketManager becomeActive]; +} + +- (void)canceled { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/TextSecureiOS/ViewControllers/TSContactPickerViewController.h b/TextSecureiOS/ViewControllers/TSContactPickerViewController.h new file mode 100644 index 0000000..ba6ba80 --- /dev/null +++ b/TextSecureiOS/ViewControllers/TSContactPickerViewController.h @@ -0,0 +1,16 @@ +// +// TSContactPickerViewController.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 02/02/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSContactPickerViewController : UITableViewController + +@property (nonatomic, strong) IBOutlet UIBarButtonItem *nextButton; +@property (nonatomic) BOOL allowMultipleSelections; +-(IBAction) next; +@end diff --git a/TextSecureiOS/ViewControllers/TSContactPickerViewController.m b/TextSecureiOS/ViewControllers/TSContactPickerViewController.m new file mode 100644 index 0000000..e4ed94a --- /dev/null +++ b/TextSecureiOS/ViewControllers/TSContactPickerViewController.m @@ -0,0 +1,132 @@ +// +// TSContactPickerViewController.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 02/02/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSContactPickerViewController.h" +#import "TSContactManager.h" +#import "TSMessageViewController.h" +#import "TSContact.h" +#import "TSGroupSetupViewController.h" +#import "TSMessagesDatabase.h" +#define tableViewCellsDequeID @"TSContactCell" +@interface TSContactPickerViewController () + +@property NSArray *whisperContacts; + +@end + +@implementation TSContactPickerViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; +} + +-(void) viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + self.nextButton.enabled = YES; + self.nextButton.title = @"Group"; + self.allowMultipleSelections = NO; + [self refreshContacts]; +} + +- (void)refreshContacts +{ + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + self.title = @"Loading"; + + [TSContactManager getAllContactsIDs:^(NSArray *contacts) { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:FALSE]; + if(self.allowMultipleSelections) { + self.title = @"Pick recipients"; + } + else { + self.title = @"Pick recipient"; + } + self.whisperContacts = contacts; + [self.tableView reloadData]; + }]; +} + +#pragma mark Tableview Delegate Methods + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ + return 1; +} + +- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ + return [self.whisperContacts count]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ + UITableViewCell *cell; + cell = [tableView dequeueReusableCellWithIdentifier:tableViewCellsDequeID]; + if (!cell) { + cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:tableViewCellsDequeID]; + } + + TSContact *contact = ((TSContact *)[self.whisperContacts objectAtIndex:indexPath.row]); + cell.textLabel.text = contact.name; + cell.detailTextLabel.text = [contact labelForRegisteredNumber]; + if([[self.whisperContacts objectAtIndex:indexPath.row] isSelected]) { + cell.accessoryType = UITableViewCellAccessoryCheckmark; + } + else { + cell.accessoryType = UITableViewCellAccessoryNone; + } + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ + [[self.whisperContacts objectAtIndex:indexPath.row] reverseIsSelected]; + if(!self.allowMultipleSelections) { + self.whisperContacts=[self getSelectedContacts]; + [self performSegueWithIdentifier:@"ComposeMessageSegue" sender:self]; + } + else { + [self.tableView reloadData]; + } + +} + +-(NSArray*) getSelectedContacts { + NSPredicate *pred = [NSPredicate predicateWithFormat:@"isSelected = TRUE"]; + return [self.whisperContacts filteredArrayUsingPredicate:pred]; +} + +-(IBAction) next { + if(self.allowMultipleSelections) { + self.whisperContacts=[self getSelectedContacts]; + [self performSegueWithIdentifier:@"TSGroupSetupSegue" sender:self]; + } + else { + self.nextButton.title = @"Create"; + self.allowMultipleSelections = YES; + } +} + +-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if([segue.identifier isEqualToString:@"TSGroupSetupSegue"]) { + TSGroupSetupViewController *vc = [segue destinationViewController]; + vc.whisperContacts = [self getSelectedContacts]; + } else if ([segue.destinationViewController isKindOfClass:[TSMessageViewController class]]) { + TSMessageViewController *vc = segue.destinationViewController; + if(![TSMessagesDatabase contactForRegisteredID:self.whisperContacts.firstObject]) { + [TSMessagesDatabase storeContact:self.whisperContacts.firstObject]; + } + vc.contact = self.whisperContacts.firstObject; + } +} + + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/TextSecureiOS/ViewControllers/TSSetMasterPasswordViewController.h b/TextSecureiOS/ViewControllers/TSSetMasterPasswordViewController.h new file mode 100644 index 0000000..090c00a --- /dev/null +++ b/TextSecureiOS/ViewControllers/TSSetMasterPasswordViewController.h @@ -0,0 +1,30 @@ +// +// TSSetMasterPasswordViewController.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +#define kPasswordIsAlphanumerical @"PasswordIsAlphanumerical" + +@interface TSSetMasterPasswordViewController : UIViewController + +@property (nonatomic, copy) NSString *firstPass; +@property (nonatomic, strong) IBOutlet UITextField *pass; + +@property (strong, nonatomic) IBOutlet UIButton *nextButton; +@property (strong, nonatomic) IBOutlet UIButton *skipButton; + +@property (strong, nonatomic) IBOutlet UISwitch *alphanumericalSwitch; + +@property (nonatomic, retain) IBOutlet UILabel *instruction; + +@property (weak, nonatomic) IBOutlet UIProgressView *passwordStrengthMeterView; +@property (nonatomic, retain) IBOutlet UILabel *passwordStrengthLabel; + +-(IBAction)nextWasTapped:(id)sender; + +@end diff --git a/TextSecureiOS/ViewControllers/TSSetMasterPasswordViewController.m b/TextSecureiOS/ViewControllers/TSSetMasterPasswordViewController.m new file mode 100755 index 0000000..aa8b0c7 --- /dev/null +++ b/TextSecureiOS/ViewControllers/TSSetMasterPasswordViewController.m @@ -0,0 +1,243 @@ +// +// TSSetMasterPasswordViewController.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSSetMasterPasswordViewController.h" +#import "TSStorageError.h" +#import "TSRegisterPrekeysRequest.h" +#import "TSUserKeysDatabase.h" +#import "TSStorageMasterKey.h" +#import "TSMessagesDatabase.h" +#import "TSWaitingPushMessageDatabase.h" +#import "TSECKeyPair.h" +#import "RMStepsController.h" +#import + + +#define pickPassword @"Pick your password" +#define reenterPassword @"Please re-enter your password" + + +@interface TSSetMasterPasswordViewController () +@property (readwrite, nonatomic, strong) NJOPasswordValidator *lenientValidator; + +@end + +@implementation TSSetMasterPasswordViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // create validator with custom rule because +standardValidator has a minimum length of 6 + self.lenientValidator = [NJOPasswordValidator validatorWithRules:@[[NJOLengthRule ruleWithRange:NSMakeRange(1, 128)]]]; + + self.nextButton.enabled = NO; + + self.pass.delegate = self; + + self.instruction.text = pickPassword; + self.navigationController.navigationBarHidden = YES; + +} + +- (void) viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + [self.pass becomeFirstResponder]; +} + + +- (IBAction) nextWasTapped:(id)sender{ + if (self.firstPass == nil) { + self.firstPass = self.pass.text; + self.instruction.text = reenterPassword; + self.pass.text = @""; + self.nextButton.enabled = NO; + self.passwordStrengthLabel.text = @""; + self.pass.keyboardType = UIKeyboardTypeAlphabet; + self.passwordStrengthMeterView.progress = 0.f; + self.passwordStrengthMeterView.tintColor = [UIColor TSInvalidColor]; + + } else { + if ([self.pass.text isEqualToString:self.firstPass]) { + [self setupDatabase]; + // remember state to present the right keyboard to the user + [[NSUserDefaults standardUserDefaults] setBool:self.alphanumericalSwitch.on forKey:kPasswordIsAlphanumerical]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } else{ + UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Passwords don't match" message:@"Both entered passwords don't match. Please try again." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alert show]; + self.firstPass = nil; + self.pass.text = @""; + self.passwordStrengthMeterView.progress = 0.f; + self.passwordStrengthMeterView.tintColor = [UIColor TSInvalidColor]; + [self.pass becomeFirstResponder]; + self.instruction.text = pickPassword; + } + } +} + +- (IBAction)skipWasTapped:(id)sender { + // TODO: improve message to help user with making an informed decision + // TODO: decide whether an implementation of a block based UI-AlertView should be included as Pod + NSString *message = NSLocalizedString(@"If no master password is set, your database is only encrypted by iOS Data Protection. Do you want to proceed without setting a password?", nil); + [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Data encryption", nil) message:message + delegate:self cancelButtonTitle:NSLocalizedString(@"No", nil) otherButtonTitles:NSLocalizedString(@"Yes", nil), nil] show]; +} + +- (IBAction)pinSwitchHasChanged:(UISwitch *)sender { + if (sender.on) { + self.pass.keyboardType = UIKeyboardTypeDefault; + } else { + self.pass.keyboardType = UIKeyboardTypeNumberPad; + } + [self.pass resignFirstResponder]; + [self.pass becomeFirstResponder]; +} + +- (void) setupDatabase { + NSError *error = nil; + + + // Create and store the storage master key from the user's password so we can then create DBs + if (![TSStorageMasterKey createStorageMasterKeyWithPassword:self.pass.text error:&error]) { + @throw [NSException exceptionWithName:@"Storage master key creation failed" reason:[error localizedDescription] userInfo:nil]; + } + + // Create the messages DB + if(![TSMessagesDatabase databaseCreateWithError:&error]) { + @throw [NSException exceptionWithName:@"Initial setup of messages DB failed" reason:[error localizedDescription] userInfo:nil]; + } + + // Create the user keys DB and generate the user's identity key and prekeys + if (![TSUserKeysDatabase databaseCreateUserKeysWithError:&error]) { + @throw [NSException exceptionWithName:@"Initial setup of cryptography keys failed" reason:[error localizedDescription] userInfo:nil]; + } + + if(![TSWaitingPushMessageDatabase databaseCreateWaitingPushMessageDatabaseWithError:&error]) { + @throw [NSException exceptionWithName:@"Initial setup of waiting push message database failed" reason:[error localizedDescription] userInfo:nil]; + } + + // Send the user's newly generated keys to the API + // TODO: Error handling & retry if network error + + NSArray *preKeys = [TSUserKeysDatabase allPreKeys]; + TSECKeyPair *identityKey = [TSUserKeysDatabase identityKey]; + + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRegisterPrekeysRequest alloc] initWithPrekeyArray:preKeys identityKey:identityKey] success:^(AFHTTPRequestOperation *operation, id responseObject) { + switch (operation.response.statusCode) { + case 200: + case 204: + DLog(@"Device registered prekeys"); + break; + + default: + DLog(@"Issue registering prekeys response %zd, %@",operation.response.statusCode,operation.response.description); +#warning Add error handling if not able to send the prekeys + break; + } + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { +#warning Add error handling if not able to send the token + DLog(@"failure %zd, %@",operation.response.statusCode,operation.response.description); + }]; + + [self performSegueWithIdentifier:@"BeginUsingApp" sender:self]; +} + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + + // What's the password field going to contain if we let this change occur? + NSString *newPass = [textField.text stringByReplacingCharactersInRange:range withString:string]; + + // Update password strenght for the new password + [self updatePasswordStrength:self forPassword:newPass]; + + if (newPass.length > 0) { + self.nextButton.enabled = YES; + } else { + self.nextButton.enabled = NO; + } + + return YES; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return YES; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +#pragma mark - Password strength +- (void)updatePasswordStrength:(id)sender forPassword:(NSString*)password { + + if ([password length] == 0) { + self.passwordStrengthMeterView.progress = 0.0f; + self.passwordStrengthLabel.text = NSLocalizedString(@"Invalid Password", nil) ; + } else { + NJOPasswordStrength strength = [NJOPasswordStrengthEvaluator strengthOfPassword:password]; + self.passwordStrengthLabel.text = [NJOPasswordStrengthEvaluator localizedStringForPasswordStrength:strength]; + if ([self.lenientValidator validatePassword:password failingRules:nil]) { + switch (strength) { + case NJOVeryWeakPasswordStrength: + self.passwordStrengthMeterView.progress = 0.1f; + self.passwordStrengthMeterView.tintColor = [UIColor TSInvalidColor]; + break; + case NJOWeakPasswordStrength: + self.passwordStrengthMeterView.progress = 0.25f; + self.passwordStrengthMeterView.tintColor = [UIColor TSOrangeWarningColor]; + break; + case NJOReasonablePasswordStrength: + self.passwordStrengthMeterView.progress = 0.5f; + self.passwordStrengthMeterView.tintColor = [UIColor TSYellowWarningColor]; + break; + case NJOStrongPasswordStrength: + self.passwordStrengthMeterView.progress = 0.75f; + self.passwordStrengthMeterView.tintColor = [UIColor TSValidColor]; + break; + case NJOVeryStrongPasswordStrength: + self.passwordStrengthMeterView.progress = 1.0f; + self.passwordStrengthMeterView.tintColor = [UIColor TSValidColor]; + break; + } + + } else { + self.passwordStrengthLabel.text = NSLocalizedString(@"Invalid Password", nil); + self.passwordStrengthMeterView.progress = 0.1f; + self.passwordStrengthMeterView.tintColor = [UIColor redColor]; + } + } +} + +#pragma mark - AlertView delegate + +-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { + + if ([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:NSLocalizedString(@"Yes", nil)]) { + self.pass.text = @""; + [self setupDatabase]; + + // remember state so user does not need to unlock the app + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kPasswordNotSet]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + + +} + +@end diff --git a/TextSecureiOS/ViewControllers/TSSettingsViewController.h b/TextSecureiOS/ViewControllers/TSSettingsViewController.h new file mode 100644 index 0000000..d99bcb6 --- /dev/null +++ b/TextSecureiOS/ViewControllers/TSSettingsViewController.h @@ -0,0 +1,13 @@ +// +// TSSettingsViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/16/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "IASKAppSettingsViewController.h" + +@interface TSSettingsViewController : IASKAppSettingsViewController +@end diff --git a/TextSecureiOS/ViewControllers/TSSettingsViewController.m b/TextSecureiOS/ViewControllers/TSSettingsViewController.m new file mode 100644 index 0000000..7ca66e2 --- /dev/null +++ b/TextSecureiOS/ViewControllers/TSSettingsViewController.m @@ -0,0 +1,16 @@ +// +// TSSettingsViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/16/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSSettingsViewController.h" + +@implementation TSSettingsViewController + +#warning screenshot protection and lock database methods in UI but their effects not implemented +/* to implement add e.g. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBasedOnUserSettings) name:kIASKAppSettingChanged object:nil]; */ + +@end diff --git a/TextSecureiOS/ViewControllers/TextSecureViewController.h b/TextSecureiOS/ViewControllers/TextSecureViewController.h new file mode 100644 index 0000000..f9b61bb --- /dev/null +++ b/TextSecureiOS/ViewControllers/TextSecureViewController.h @@ -0,0 +1,23 @@ +// +// TextSecureViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/30/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import +#import +#import +#import + +@interface TextSecureViewController : UIViewController +@property (nonatomic, strong) IBOutlet UITableView *tableView; +@property (nonatomic, strong) IBOutlet UIBarButtonItem *composeBarButtonItem; + +- (IBAction) Edit:(id)sender; + +- (IBAction) composeMessage; +@end + diff --git a/TextSecureiOS/ViewControllers/TextSecureViewController.m b/TextSecureiOS/ViewControllers/TextSecureViewController.m new file mode 100644 index 0000000..41bfe65 --- /dev/null +++ b/TextSecureiOS/ViewControllers/TextSecureViewController.m @@ -0,0 +1,243 @@ +// +// TextSecureViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TextSecureViewController.h" +#import "Cryptography.h" +#import "TSMessagesDatabase.h" +#import +#import "NSString+Conversion.h" +#import "TSContactManager.h" +#import "TSContact.h" +#import "TSMessage.h" +#import "TSMessageViewController.h" +#import "TSMessageConversationCell.h" +#import "PasswordUnlockViewController.h" +#import "TSStorageMasterKey.h" +#import "TSContactPickerViewController.h" +#import "TSConversation.h" +#import "TSGroupSetupViewController.h" +#import "TSSetMasterPasswordViewController.h" + +static NSString *kCellIdentifier = @"CellIdentifier"; + +static NSString *kThreadTitleKey = @"kThreadTitleKey"; +static NSString *kThreadDateKey = @"kThreadDateKey"; +static NSString *kThreadMessageKey = @"kThreadMessageKey"; +static NSString *kThreadImageKey = @"kThreadImageKey"; + +@interface TextSecureViewController() + +@property (nonatomic, strong) IBOutlet UISearchBar *searchBar; +@property (nonatomic, strong) UIBarButtonItem *settingsBarButtonItem; +//@property (nonatomic, strong) UIView *searchBarCoverView; +@property (nonatomic, strong) NSArray *conversations; + +@end + +@implementation TextSecureViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"Messages"; + self.navigationController.navigationBarHidden = NO; + +#warning // FETCH CONVERSATIONS WITH COMPLETION BLOCK + + UIEdgeInsets inset = UIEdgeInsetsMake(44, 0, 0, 0); + self.tableView.contentInset = inset; + [self.tableView registerClass:[TSMessageConversationCell class] forCellReuseIdentifier:kCellIdentifier]; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; + self.tableView.separatorColor = [UIColor lightGrayColor]; + + [[NSNotificationCenter defaultCenter] addObserverForName:kDBNewMessageNotification object:nil queue:nil usingBlock:^(NSNotification *note) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSLog(@"Refreshing"); + self.conversations = [TSMessagesDatabase conversations]; + [self.tableView reloadData]; + }); + }]; +} + +-(void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + self.conversations = [TSMessagesDatabase conversations]; + [self.tableView reloadData]; + + self.navigationController.navigationBarHidden = NO; + + if([TSKeyManager hasVerifiedPhoneNumber] && [TSMessagesDatabase databaseWasCreated] && [TSStorageMasterKey isStorageMasterKeyLocked]) { + [self performSegueWithIdentifier:@"PasswordUnlockSegue" sender:self]; + + } else if([TSKeyManager hasVerifiedPhoneNumber] == NO) { + [self performSegueWithIdentifier:@"ObtainVerificationCode" sender:self]; + } +} + +- (IBAction)composeMessage { + [self presentViewController:[[UINavigationController alloc] initWithRootViewController:[[TSContactPickerViewController alloc]initWithNibName:nil bundle:nil]] animated:YES completion:nil]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - UITableViewDataSource methods + +- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; + [dateFormatter setTimeStyle:NSDateFormatterNoStyle]; + UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:kCellIdentifier]; + + if ([cell isKindOfClass:[TSMessageConversationCell class]]) { + + TSConversation *conversation = [self.conversations objectAtIndex:indexPath.row]; + + TSMessageConversationCell *threadCell = (TSMessageConversationCell *)cell; + if(conversation.contact!=nil) { + threadCell.titleLabel.text = [conversation.contact name]; + threadCell.timestampLabel.text = [dateFormatter stringFromDate:conversation.lastMessageDate]; + threadCell.conversationPreviewLabel.text = [conversation lastMessage]; + } + else { + threadCell.titleLabel.text = [conversation.group groupName]; + threadCell.timestampLabel.text = [dateFormatter stringFromDate:conversation.lastMessageDate]; + threadCell.conversationPreviewLabel.text = [conversation lastMessage]; + } + + UIImage *disclosureIndicatorImage = [[UIImage imageNamed:@"disclosure_indicator"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + threadCell.disclosureImageView.image = disclosureIndicatorImage; + + NSMutableArray *rightUtilityButtons = [[NSMutableArray alloc] init]; + UIColor *deleteButtonColor = [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f]; + [rightUtilityButtons sw_addUtilityButtonWithColor:deleteButtonColor title:@"Delete"]; + + threadCell.rightUtilityButtons = rightUtilityButtons; + threadCell.delegate = self; + } + + return cell; +} + +- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section{ + if([TSMessagesDatabase databaseWasCreated]) { + // don't display until db is unlocked (we have "dummy data" right now, but this better mimics UX behavior) + return [self.conversations count]; + } + else { + return 0; + } +} + +#pragma mark - UITableViewDelegate methods + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 74.0f; +} + +- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { + if(self.editing == NO || !indexPath) { + return UITableViewCellEditingStyleNone; + } + else { + return UITableViewCellEditingStyleDelete; + } +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { + if (editingStyle == UITableViewCellEditingStyleDelete) { + // Delete row + [self Edit:self]; + } + else if (editingStyle == UITableViewCellEditingStyleInsert) { + [self Edit:self]; + } +} + +-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + [self performSegueWithIdentifier:@"NewMessageOnThreadSegue" sender:self]; +} + + +-(void) reloadModel:(NSNotification*)notification { + [self.tableView reloadData]; +} + + +- (IBAction) Edit:(id)sender { + if(self.editing) { + [super setEditing:NO animated:NO]; + [self.tableView setEditing:NO animated:NO]; + [self.tableView reloadData]; + + } + else { + [super setEditing:YES animated:YES]; + [self.tableView setEditing:YES animated:YES]; + [self.tableView reloadData]; + } +} + +#pragma mark - SWTableViewCellDelegate + +- (void)swipeableTableViewCell:(TSMessageConversationCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index{ + + dataBaseUpdateCompletionBlock block = ^(BOOL success) { + if (success) { + NSMutableArray *removalArray = [self.conversations mutableCopy]; + [removalArray removeObjectAtIndex:index]; + self.conversations = [removalArray copy]; + [self swipeableTableViewCell:cell scrollingToState:kCellStateCenter]; + [self.tableView deleteRowsAtIndexPaths:@[[self.tableView indexPathForCell:cell]] withRowAnimation:UITableViewRowAnimationAutomatic]; + } else{ + UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"An unexpected error occured" message:@"An error occured while trying to delete that message. Please try again and if it persists, please report it." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; + [alertView show]; + } + }; +#ifdef DEBUG + [TSMessagesDatabase deleteMessagesAndSessionsForConversation:[self.conversations objectAtIndex:index] completion:block]; +#else + [TSMessagesDatabase deleteMessagesForConversation:[self.conversations objectAtIndex:index] completion:block]; +#endif +} + +// This SWTableViewCell delegate method is still buggy and doesn't represent the exact state of the cell, +// e.g. when the right utility buttons are not set. +// TODO: Fix bugs in SWTableViewCell +- (void)swipeableTableViewCell:(SWTableViewCell *)cell scrollingToState:(SWCellState)state { + BOOL isEnteringEditingMode = (state == kCellStateRight); + [self animateEnteringEditingMode:isEnteringEditingMode]; +} + +- (void)animateEnteringEditingMode:(BOOL)isEditing { + if (!isEditing) { + self.composeBarButtonItem.enabled = YES; + } else { + self.composeBarButtonItem.enabled = NO; + } +} + + +-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if([segue.identifier isEqualToString:@"NewMessageOnThreadSegue"]) { + TSMessageViewController *mvc = [segue destinationViewController]; + if([sender respondsToSelector:@selector(group)]) { + mvc.group = [sender performSelector:@selector(group)]; + } + NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow]; + TSConversation* conversation = [self.conversations objectAtIndex:selectedIndexPath.row]; + mvc.contact=conversation.contact; + mvc.group=conversation.group; + } + +} + + +@end diff --git a/TextSecureiOS/ViewControllers/ThreadViewController.m b/TextSecureiOS/ViewControllers/ThreadViewController.m new file mode 100644 index 0000000..e553865 --- /dev/null +++ b/TextSecureiOS/ViewControllers/ThreadViewController.m @@ -0,0 +1,57 @@ +// +// ViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "ThreadViewController.h" +#import "Message.h" +@implementation ThreadViewController + + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"Conversation"; +} + +-(void) reloadModel:(NSNotification*)notification { + // TODO: make this be threads +#warning get messages from the message db + [self.tableView reloadData]; +} + + + + + + +// for custom designed cells +- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath { + // for custom designed cells + // TODO: actually figure out if message was sent or recieved + Message* message = [self.messages objectAtIndex:indexPath.row]; + UITableViewCell *cell; + if(indexPath.row %2 == 0) { + cell = [tv dequeueReusableCellWithIdentifier:@"TextSecureThreadSent"]; + } + else { + cell = [tv dequeueReusableCellWithIdentifier:@"TextSecureThreadReceived"]; + } + UILabel *messageLabel = (UILabel *)[cell viewWithTag:1]; + UILabel *dateLabel = (UILabel *)[cell viewWithTag:2]; + messageLabel.text = message.text; + [messageLabel setFont:[UIFont fontWithName:@"OpenSans" size:12]]; + NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"HH:mm"]; + NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; + dateLabel.text = dateString; + [dateLabel setFont:[UIFont fontWithName:@"OpenSans" size:12]]; + return cell; +} + + + + +@end diff --git a/TextSecureiOS/ViewControllers/VerificationCodeViewController.h b/TextSecureiOS/ViewControllers/VerificationCodeViewController.h new file mode 100644 index 0000000..e997465 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerificationCodeViewController.h @@ -0,0 +1,27 @@ +// +// VerificationCodeViewController.h +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "NBAsYouTypeFormatter.h" + +@interface VerificationCodeViewController : UIViewController + +@property (nonatomic,strong) IBOutlet UITextField *verificationCode_part1; +@property (nonatomic,strong) IBOutlet UITextField *verificationCode_part2; +@property (strong, nonatomic) IBOutlet UILabel *smsToNumberLabel; + +@property (nonatomic, strong) IBOutlet UIButton *sendAuthenticatedRequest; +@property (strong, nonatomic) IBOutlet UIView *underlineView1; +@property (strong, nonatomic) IBOutlet UIView *underlineView2; + +@property (nonatomic, retain) NBAsYouTypeFormatter *numberFormatter; + +@property (nonatomic, copy) NSString *phoneNumber; +@property (nonatomic, copy) NSString *basicAuthCode; + +@end diff --git a/TextSecureiOS/ViewControllers/VerificationCodeViewController.m b/TextSecureiOS/ViewControllers/VerificationCodeViewController.m new file mode 100644 index 0000000..ad1986e --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerificationCodeViewController.m @@ -0,0 +1,130 @@ +// +// VerificationCodeViewController.m +// TextSecureiOS +// +// Created by Frederic Jacobs on 10/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "VerificationCodeViewController.h" +#import "TSServerCodeVerificationRequest.h" +#import "TSKeyManager.h" +#import "RMStepsController.h" + +@interface VerificationCodeViewController () + +@end + +@implementation VerificationCodeViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. + + self.verificationCode_part1.delegate = self; + self.verificationCode_part2.delegate = self; + self.sendAuthenticatedRequest.enabled=NO; + + self.smsToNumberLabel.text = [TSKeyManager getUsernameToken]; + + self.navigationController.navigationBarHidden = YES; +} + +- (void) viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + self.verificationCode_part1.text=@""; + self.verificationCode_part2.text=@""; + self.sendAuthenticatedRequest.enabled = NO; + [self.verificationCode_part1 becomeFirstResponder]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +#pragma mark UITextFieldDelegateMethod + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + + if (textField == self.verificationCode_part1 && ![string isEqualToString:@""] && range.location == 2 && string.length == 1) { + self.underlineView1.backgroundColor = [UIColor TSValidColor]; + [self.verificationCode_part2 becomeFirstResponder]; + self.verificationCode_part1.text = [self.verificationCode_part1.text stringByAppendingString:string]; + return NO; + } else if (textField == self.verificationCode_part2 && ![string isEqualToString:@""] && range.location == 2 && string.length == 1){ + self.underlineView2.backgroundColor = [UIColor TSValidColor]; + self.verificationCode_part2.text = [self.verificationCode_part2.text stringByAppendingString:string]; + [self.verificationCode_part2 resignFirstResponder]; + self.sendAuthenticatedRequest.enabled = YES; + return NO; + } + self.sendAuthenticatedRequest.enabled = NO; + return YES; +} + +#pragma mark Code verification + +-(IBAction)doVerifyPhone:(id)sender { + + NSString* verificationCode = [_verificationCode_part1.text stringByAppendingString:_verificationCode_part2.text]; + + NSString *authToken = [TSKeyManager generateNewAccountAuthenticationToken]; + NSString *signalingKey = [TSKeyManager generateNewSignalingKeyToken]; + + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSServerCodeVerificationRequest alloc] initWithVerificationCode:verificationCode signalingKey:signalingKey authToken:authToken] success:^(AFHTTPRequestOperation *operation, id responseObject){ + + switch (operation.response.statusCode) { + case 204: + + [TSKeyManager storeSignalingKeyToken:signalingKey]; + [TSKeyManager storeAuthenticationToken:authToken]; + + // Perform the APN registration + + [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; + _verificationCode_part1.text=@""; + _verificationCode_part2.text=@""; + [self.stepsController showNextStep]; + + break; + + default: + [[[UIAlertView alloc]initWithTitle:@"Can't verify" message:@"An unknown error occured. Pleasy try again." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show]; + DLog(@"Verification operation failed with response: %@ and response code : %li", responseObject, (long)operation.response.statusCode); + break; + } + + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if (operation.response.statusCode == 403) { + [[[UIAlertView alloc]initWithTitle:@"Wrong code" message:@"The entered code doesn't appear to match the one on our servers. Try entering it again." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show]; + } else{ + defaultNetworkErrorMessage + } + DLog(@"Verification operation request failed with error: %@", error); + }]; +} + +-(IBAction)doRequestPhoneVerification:(id)sender { + + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRequestVerificationCodeRequest alloc] initRequestForPhoneNumber:[TSKeyManager getUsernameToken] transport:kPhoneNumberVerification] success:^(AFHTTPRequestOperation *operation, id responseObject){ + + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Verification call" message:@"A verification call was requested." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; + [alertView show]; + + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + [[[UIAlertView alloc]initWithTitle:@"Sorry we had an issue with this request" message:@"Read Dlog" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show]; + }]; +} + +@end diff --git a/TextSecureiOS/ViewControllers/VerificationViewController.h b/TextSecureiOS/ViewControllers/VerificationViewController.h new file mode 100644 index 0000000..630606b --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerificationViewController.h @@ -0,0 +1,37 @@ +// +// VerificationViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import +#import "Constants.h" +#import "NBAsYouTypeFormatter.h" +#import "NBPhoneNumberUtil.h" +#import "NBPhoneNumber.h" + + +@interface VerificationViewController : UIViewController +@property (nonatomic,strong) IBOutlet UITextField *countryCodeInput; +@property (nonatomic,strong) IBOutlet UILabel *countryName; +@property (nonatomic,strong) IBOutlet UITextField *phoneNumber; + +@property (nonatomic, strong) IBOutlet UILabel *explanationText; +@property (nonatomic, retain) NBAsYouTypeFormatter *numberFormatter; +@property (strong, nonatomic) IBOutlet UIButton *countryButton; + +@property (strong, nonatomic) IBOutlet UIButton *sendVerificationButton; +@property (nonatomic, strong) IBOutlet UIBarButtonItem *nextButton; +@property (strong, nonatomic) IBOutlet UIView *underlineNumberView; +@property (strong, nonatomic) IBOutlet UIView *underlineCountryCodeView; + +@property (nonatomic, copy) NSString *selectedPhoneNumber; + +-(void) countryChosen:(NSNotification*)notification; +- (void)didReceiveMemoryWarning; +-(void)updateCountry:(NSDictionary*)countryInfo; + +-(IBAction)sendVerification:(id)sender; +@end \ No newline at end of file diff --git a/TextSecureiOS/ViewControllers/VerificationViewController.m b/TextSecureiOS/ViewControllers/VerificationViewController.m new file mode 100644 index 0000000..87c9531 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerificationViewController.m @@ -0,0 +1,366 @@ +// +// VerificationViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/24/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "VerificationViewController.h" +#import "TSKeyManager.h" +#import "RMStepsController.h" + +@interface VerificationViewController () + +@property (strong, nonatomic) IBOutlet UIScrollView *scrollView; + +@property (strong, nonatomic) NSString *preservedCountryCodeText; +@property (nonatomic) BOOL userSelectedCountry; + +@end + +@implementation VerificationViewController +// Note there are so many IBOutlets to support easy Localizable.strings localization +// and customization of fonts. Hopefully this will be made easier in the future iOS dev +// suite to be done entirely via Storyboards and not via code. If anyone has a cleaner way +// of doing this, please go ahead. +@synthesize phoneNumber; + +@synthesize countryName; +@synthesize countryCodeInput; +@synthesize explanationText; + +#pragma mark View Controller Methods + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the views + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(countryChosen:) name:@"CountryChosen" object:nil]; + [countryCodeInput addTarget:self action:@selector(updateCountryCode:) forControlEvents:UIControlEventEditingChanged]; + + self.nextButton.enabled = NO; + self.sendVerificationButton.enabled = NO; + self.navigationController.navigationBarHidden = YES; + + [self setLocaleCountry]; + + + // Hold off on triggering the keyboard on a small screen because it'll scroll the text up. + CGSize screenSize = [[UIScreen mainScreen] bounds].size; + + if (screenSize.height >= 568) { + [self.phoneNumber becomeFirstResponder]; + } else { + // sign up to be notified about the keyboard but only on small screens + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; + } +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + self.navigationController.navigationBarHidden = YES; +} + +- (void) viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + // If user comes back to this page, make him re-enter all data. + [TSKeyManager removeAllKeychainItems]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark keyboard notifications + +- (void) keyboardWasShown:(NSNotification *)notification +{ + NSDictionary *info = [notification userInfo]; + CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0); + self.scrollView.contentInset = contentInsets; + self.scrollView.scrollIndicatorInsets = contentInsets; + + [UIView animateKeyframesWithDuration:1.2 delay:0 options:UIViewKeyframeAnimationOptionLayoutSubviews animations:^{ + self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.scrollView.contentSize.height - kbSize.height); + [self.explanationText setHidden:YES]; + [self.countryName setFrame:CGRectMake(self.countryName.frame.origin.x, self.countryName.frame.origin.y-30, self.countryName.frame.size.width, self.countryName.frame.size.height)]; + [self.countryButton setFrame:CGRectMake(self.countryButton.frame.origin.x, self.countryButton.frame.origin.y-30, self.countryButton.frame.size.width, self.countryButton.frame.size.height)]; + [self.sendVerificationButton setFrame:CGRectMake(self.sendVerificationButton.frame.origin.x, self.sendVerificationButton.frame.origin.y-30, self.sendVerificationButton.frame.size.width, self.sendVerificationButton.frame.size.height)]; + [self.underlineCountryCodeView setFrame:CGRectMake(self.underlineCountryCodeView.frame.origin.x, self.underlineCountryCodeView.frame.origin.y-10, self.underlineCountryCodeView.frame.size.width, self.underlineCountryCodeView.frame.size.height)]; + [self.underlineNumberView setFrame:CGRectMake(self.underlineNumberView.frame.origin.x, self.underlineNumberView.frame.origin.y-10, self.underlineNumberView.frame.size.width, self.underlineNumberView.frame.size.height)]; + + } completion:nil]; +} + +- (void) keyboardWillBeHidden:(NSNotification *)notification +{ + UIEdgeInsets contentInsets = UIEdgeInsetsZero; + self.scrollView.contentInset = contentInsets; + self.scrollView.scrollIndicatorInsets = contentInsets; + [UIView animateKeyframesWithDuration:1.2 delay:0 options:UIViewKeyframeAnimationOptionLayoutSubviews animations:^{ + self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.scrollView.contentSize.height + [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height); + [self.explanationText setHidden:NO]; + [self.countryName setFrame:CGRectMake(self.countryName.frame.origin.x, self.countryName.frame.origin.y+30, self.countryName.frame.size.width, self.countryName.frame.size.height)]; + [self.countryButton setFrame:CGRectMake(self.countryButton.frame.origin.x, self.countryButton.frame.origin.y+30, self.countryButton.frame.size.width, self.countryButton.frame.size.height)]; + [self.sendVerificationButton setFrame:CGRectMake(self.sendVerificationButton.frame.origin.x, self.sendVerificationButton.frame.origin.y+30, self.sendVerificationButton.frame.size.width, self.sendVerificationButton.frame.size.height)]; + [self.underlineCountryCodeView setFrame:CGRectMake(self.underlineCountryCodeView.frame.origin.x, self.underlineCountryCodeView.frame.origin.y+10, self.underlineCountryCodeView.frame.size.width, self.underlineCountryCodeView.frame.size.height)]; + [self.underlineNumberView setFrame:CGRectMake(self.underlineNumberView.frame.origin.x, self.underlineNumberView.frame.origin.y+10, self.underlineNumberView.frame.size.width, self.underlineNumberView.frame.size.height)]; + } completion:nil]; +} + + +#pragma mark Phone number formatting +// Based on the user's locale we are guessing what his country code would be. + +-(void) initNumberFormatter{ + // The first character is '+'. + NSAssert(self.countryCodeInput.text.length > 1, @"Cannot initialize numberFormatter without a country code."); + + self.numberFormatter = [[NBAsYouTypeFormatter alloc]initWithRegionCode:[NSLocale localizedCodeNameForPhonePrefix:[self.countryCodeInput.text removeAllFormattingButNumbers]]]; + + NSString *charString = [[countryCodeInput.text removeAllFormattingButNumbers] prependPlus]; + + for (NSUInteger i = 0; i < charString.length; i++) { + [self.numberFormatter inputDigit:[charString substringWithRange:NSMakeRange(i, 1)]]; + } +} + +-(void)setLocaleCountry{ + DLog(@"Setting Locale to : %@", [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]); + + self.countryCodeInput.text = [NSLocale currentCountryPhonePrefix]; + [self updateCountryCode:nil]; + self.userSelectedCountry = FALSE; +} + +-(void)updateCountryCode:(id)sender { + NSString *enteredCountryCode = self.countryCodeInput.text; + enteredCountryCode = [enteredCountryCode removeAllFormattingButNumbers]; + + [self updateCountry:@{countryInfoKeyCountryCode:enteredCountryCode, countryInfoKeyName:[[NSLocale currentLocale] displayNameForKey:NSLocaleCountryCode value:[NSLocale localizedCodeNameForPhonePrefix:enteredCountryCode]]}]; +} + +-(void)updateCountry:(NSDictionary*)countryInfo { + self.countryCodeInput.text = [[countryInfo objectForKey:countryInfoKeyCountryCode] prependPlus]; + self.countryName.text=[countryInfo objectForKey:countryInfoKeyName]; +} + +-(void) countryChosen:(NSNotification*)notification { + [self updateCountry:[notification userInfo]]; + self.userSelectedCountry = TRUE; +} + +#pragma mark - Verification Action + +-(IBAction)sendVerification:(id)sender { + self.nextButton.enabled = NO; + self.sendVerificationButton.enabled = NO; + + self.selectedPhoneNumber = [NSString stringWithFormat:@"%@%@",self.countryCodeInput.text,[self.phoneNumber.text removeAllFormattingButNumbers]]; + [[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRequestVerificationCodeRequest alloc] initRequestForPhoneNumber:self.selectedPhoneNumber transport:kSMSVerification] success:^(AFHTTPRequestOperation *operation, id responseObject){ + + [TSKeyManager storeUsernameToken:self.selectedPhoneNumber]; + + [TSKeyManager generateNewAccountAuthenticationToken]; + [TSKeyManager generateNewSignalingKeyToken]; + + [self.stepsController showNextStep]; + + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"Operation: %@ and error: %@", operation, error); + [[[UIAlertView alloc]initWithTitle:@"Sorry we had an issue with this request" message:@"Read Dlog" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show]; + }]; +} + +#pragma mark Formatted Number String processing + +- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{ + + if ([textField isEqual:self.countryCodeInput]) { + if (self.countryCodeInput.text.length > 1) { + self.preservedCountryCodeText = self.countryCodeInput.text; + } + // If the user just selected a country, then focus the phoneNumber + // UITextInput. + if (self.userSelectedCountry) { + [self.phoneNumber becomeFirstResponder]; + } + else { + self.countryCodeInput.text = @"+"; + [self updateCountryCode:nil]; + } + } else if ([textField isEqual:self.phoneNumber]) { + // restorePreservedCountryCodeText: works around a race condition + // between textFieldShouldBeginEditing: and textFieldDidEndEditing: + [self restorePreservedCountryCodeText]; + [self initNumberFormatter]; + } + // Only prevent emptying the countryCodeInput on the first selection of a + // text field. + self.userSelectedCountry = FALSE; + + return YES; +} + +- (BOOL)textField:(UITextView *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ + + if ([textField isEqual:self.countryCodeInput]) { + if(![self isValidCountryPrefix:[textField.text stringByReplacingCharactersInRange:range withString:string]]) { + return YES; + } else { + textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string]; + [self.phoneNumber becomeFirstResponder]; + [self updateCountryCode:nil]; + return NO; + } + + } else if ([textField isEqual:self.phoneNumber]){ + + // A character is deleted. We rebuild the formatter with one fewer char + [self initNumberFormatter]; + + NSString *formattedString; + + NSString *nonFormattedstring = [self.phoneNumber.text removeAllFormattingButNumbers]; + + // The last added character might not be at the end of the string + + NSUInteger loopLength = nonFormattedstring.length+1; + + if ([string isEqualToString:@""]) { + loopLength = nonFormattedstring.length; + } + + for (NSUInteger i = 0; i < loopLength; i++) { + if (i != ([self location:range.location ofCleanedStringOf:self.phoneNumber.text])) { + formattedString = [self.numberFormatter inputDigit:[nonFormattedstring substringWithRange:NSMakeRange(i, 1)]]; + } else { + // if we are at the replace or add position, we need to evaluate what to do + + if ([string isEqualToString:@""]) { + // This is the character added to remove chars, there is nothing to do + } + else{ + formattedString = [self.numberFormatter inputDigit:string]; + } + } + } + + self.phoneNumber.text = [self cleanPrefixOfString:formattedString]; + + + // We detect if the number is a valid number. If it is, we show the next button. + + NSError *error = nil; + + NBPhoneNumber *number = [[NBPhoneNumberUtil sharedInstance] parse:[self.countryCodeInput.text stringByAppendingString:self.phoneNumber.text] defaultRegion:[NSLocale localizedCodeNameForPhonePrefix:self.countryCodeInput.text] error:&error]; + + if (error == nil && [[NBPhoneNumberUtil sharedInstance] isValidNumber:number]) { + self.sendVerificationButton.enabled = TRUE; + self.underlineNumberView.backgroundColor = [UIColor TSValidColor]; + self.underlineCountryCodeView.backgroundColor = [UIColor TSValidColor]; + + } else{ + self.sendVerificationButton.enabled = FALSE; + self.underlineNumberView.backgroundColor = [UIColor TSBlueBarColorWithAlpha]; + self.underlineCountryCodeView.backgroundColor = [UIColor TSBlueBarColorWithAlpha]; + } + + return NO; + + } else { + return YES; + } +} + +// The parsing library needs to see the phone number with the prefix, we do show it without it. +// This ugly method clean the prefix so that phone number fields is parsed but without prefix. + +-(NSString*) cleanPrefixOfString:(NSString*)formattedText{ + + NSMutableArray *prefix = [NSMutableArray array]; + NSString *prefixString = [[countryCodeInput.text removeAllFormattingButNumbers] prependPlus]; + + for (NSUInteger i = 0; i < prefixString.length; i++) { + [prefix addObject:[prefixString substringWithRange:NSMakeRange(i, 1)]]; + } + + NSUInteger lastCharLoc = 0; + + for (NSUInteger i = 0; i < formattedText.length; i++) { + if ([[formattedText substringWithRange:NSMakeRange(i, 1)] isEqualToString:[prefix firstObject]]) { + [prefix removeObjectAtIndex:0]; + + if (prefix.count == 0) { + lastCharLoc = i; + } + } + } + + if (lastCharLoc < formattedText.length) { + if (!isnumber([formattedText characterAtIndex:(lastCharLoc+1)])) { + lastCharLoc++; + } + } + + return [formattedText substringWithRange:NSMakeRange(lastCharLoc+1, formattedText.length-(lastCharLoc+1))]; +} + +-(NSUInteger) location:(NSUInteger)loc ofCleanedStringOf:(NSString*)string { + NSString *cleanedString = [string removeAllFormattingButNumbers]; + + NSMutableArray *prefix = [NSMutableArray array]; + + for (NSUInteger i = 0; i < cleanedString.length; i++) { + [prefix addObject:[cleanedString substringWithRange:NSMakeRange(i, 1)]]; + } + + NSUInteger cleanedStringIndex = 0; + + for (NSUInteger i = 0; i < loc; i++) { + if ([[string substringWithRange:NSMakeRange(i, 1)] isEqualToString:[prefix objectAtIndex:cleanedStringIndex]]) { + cleanedStringIndex++; + } + } + return cleanedStringIndex; +} + +-(void)textFieldDidEndEditing:(UITextField *)textField +{ + if ([self.countryCodeInput isEqual:textField]) { + [self restorePreservedCountryCodeText]; + } +} + +-(void)restorePreservedCountryCodeText +{ + if (self.preservedCountryCodeText != nil && ([self.countryCodeInput.text isEqualToString:@"+"] || [self.countryCodeInput.text isEqualToString:@""])) { + self.countryCodeInput.text = self.preservedCountryCodeText; + self.preservedCountryCodeText = nil; + [self updateCountryCode:nil]; + } +} + +#pragma mark Verify country code + +-(BOOL)isValidCountryPrefix:(NSString*)diallingCode{ + if (![[NSLocale localizedCodeNameForPhonePrefix:[diallingCode removeAllFormattingButNumbers]] isEqualToString:@"ZZ"]) { + return YES; + } else{ + return NO; + } +} + +#pragma mark Memory allocations + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/TextSecureiOS/ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.h b/TextSecureiOS/ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.h new file mode 100644 index 0000000..7ff8990 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.h @@ -0,0 +1,14 @@ +// +// TSPresentIdentityQRCodeViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/30/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSPresentIdentityQRCodeViewController : UIViewController +@property(nonatomic,strong) IBOutlet UIImageView* qrCodeView; +@property(nonatomic,strong) NSData* identityKey; +@end diff --git a/TextSecureiOS/ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.m b/TextSecureiOS/ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.m new file mode 100644 index 0000000..2b47697 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerifyIdentity/TSPresentIdentityQRCodeViewController.m @@ -0,0 +1,91 @@ +// +// TSPresentIdentityQRCodeViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/30/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSPresentIdentityQRCodeViewController.h" +#import "NSData+Base64.h" +@interface TSPresentIdentityQRCodeViewController () + +@end + +@implementation TSPresentIdentityQRCodeViewController + + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + + +- (void) viewDidLoad{ + [super viewDidLoad]; + self.title = @"Your Identity Key"; + + CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; + + // NSLog(@"filterAttributes:%@", filter.attributes); + + [filter setDefaults]; + [filter setValue:[[self.identityKey base64EncodedString] dataUsingEncoding:NSUTF8StringEncoding] forKey:@"inputMessage"]; + + CIImage *outputImage = [filter outputImage]; + + CIContext *context = [CIContext contextWithOptions:nil]; + CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]]; + + UIImage *image = [UIImage imageWithCGImage:cgImage scale:1. orientation:UIImageOrientationUp]; + + // Resize without interpolating + UIImage *resized = [self resizeImage:image withQuality:kCGInterpolationNone rate:5.0]; + + self.qrCodeView.image = resized; + + CGImageRelease(cgImage); +} + + + +#pragma mark - Private + +- (UIImage *)resizeImage:(UIImage *)image withQuality:(CGInterpolationQuality)quality rate:(CGFloat)rate { + UIImage *resized = nil; + CGFloat width = image.size.width * rate; + CGFloat height = image.size.height * rate; + + UIGraphicsBeginImageContext(CGSizeMake(width, height)); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetInterpolationQuality(context, quality); + [image drawInRect:CGRectMake(0, 0, width, height)]; + resized = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return resized; +} + + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender +{ + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/TextSecureiOS/ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.h b/TextSecureiOS/ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.h new file mode 100644 index 0000000..e9321a6 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.h @@ -0,0 +1,22 @@ +// +// TSScanIdentityBarcodeViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/29/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import +@interface TSScanIdentityBarcodeViewController : UIViewController + +@property(nonatomic,strong) AVCaptureSession *session; +@property(nonatomic,strong) AVCaptureDevice *device; +@property(nonatomic,strong) AVCaptureDeviceInput *input; +@property(nonatomic,strong) AVCaptureMetadataOutput *output; +@property(nonatomic,strong) AVCaptureVideoPreviewLayer *prevLayer; + +@property(nonatomic,strong) UIView *highlightView; +@property(nonatomic,strong) UILabel *label; +@property(nonatomic,strong) NSData *identityKey; +@end diff --git a/TextSecureiOS/ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.m b/TextSecureiOS/ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.m new file mode 100644 index 0000000..0340113 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerifyIdentity/TSScanIdentityBarcodeViewController.m @@ -0,0 +1,140 @@ +// +// TSScanIdentityBarcodeViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/29/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSScanIdentityBarcodeViewController.h" +#import "TSVerifyIdentityViewController.h" +#import "NSData+Base64.h" + + + +@implementation TSScanIdentityBarcodeViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + self.title = @"Scan key"; + + self.highlightView = [[UIView alloc] init]; + self.highlightView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleBottomMargin; + self.highlightView.layer.borderColor = [UIColor greenColor].CGColor; + self.highlightView.layer.borderWidth = 4; + [self.view addSubview:self.highlightView]; + + self.label = [[UILabel alloc] init]; + self.label.frame = CGRectMake(0, self.view.bounds.size.height - 40, self.view.bounds.size.width, 40); + self.label.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; + self.label.backgroundColor = [UIColor colorWithWhite:0.15 alpha:0.65]; + self.label.textColor = [UIColor whiteColor]; + self.label.textAlignment = NSTextAlignmentCenter; + self.label.text = @"(none)"; + [self.view addSubview:self.label]; + + self.session = [[AVCaptureSession alloc] init]; + self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + NSError *error = nil; + + self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error]; + if (self.input) { + [self.session addInput:self.input]; + } else { + NSLog(@"Error: %@", error); + } + + self.output = [[AVCaptureMetadataOutput alloc] init]; + [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; + [self.session addOutput:self.output]; + + self.output.metadataObjectTypes = [self.output availableMetadataObjectTypes]; + + self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; + self.prevLayer.frame = self.view.bounds; + self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + [self.view.layer addSublayer:self.prevLayer]; + + [self.session startRunning]; + + [self.view bringSubviewToFront:self.highlightView]; + [self.view bringSubviewToFront:self.label]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { + CGRect highlightViewRect = CGRectZero; + AVMetadataMachineReadableCodeObject *barCodeObject; + NSString *detectionString = nil; + NSArray *barCodeTypes = @[AVMetadataObjectTypeQRCode]; + + for (AVMetadataObject *metadata in metadataObjects) { + NSLog(@"metadata %@",metadata); + for (NSString *type in barCodeTypes) { + if ([metadata.type isEqualToString:type]) { + barCodeObject = (AVMetadataMachineReadableCodeObject *)[self.prevLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject *)metadata]; + highlightViewRect = barCodeObject.bounds; + detectionString = [(AVMetadataMachineReadableCodeObject *)metadata stringValue]; + break; + } + } + if (detectionString != nil) { + self.label.text = detectionString; + NSData* detectionData = [NSData dataFromBase64String:detectionString]; +#warning do something with this fact; update the contact as verified + if([detectionData isEqualToData:self.identityKey]) { + + self.label.text = @"verified!"; + + } + else { + self.label.text = @"identity keys do not match"; + + } + [self.session stopRunning]; + break; + } + else { + self.label.text = @"searching..."; + } + } + if([self.label.text isEqualToString:@"verified!"]) { + [self performSegueWithIdentifier:@"IdentityKeyWasVerified" sender:self]; + } + + self.highlightView.frame = highlightViewRect; +} + +/* + #pragma mark - Navigation + + + + // In a storyboard-based application, you will often want to do a little preparation before navigation + - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender + { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. + } + */ +@end + + + + diff --git a/TextSecureiOS/ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.h b/TextSecureiOS/ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.h new file mode 100644 index 0000000..ddea463 --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.h @@ -0,0 +1,19 @@ +// +// TSVerifyIdentityViewController.h +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/29/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import "TSContact.h" +@interface TSVerifyIdentityViewController : UIViewController + +@property (nonatomic,strong) IBOutlet UILabel *theirIdentity; +@property (nonatomic,strong) IBOutlet UILabel *myIdentity; +@property (nonatomic,strong) IBOutlet UILabel *identityVerifiedLabel; +@property (nonatomic,strong) TSContact* contact; + +-(IBAction)markManuallyVerified:(id)sender; +@end diff --git a/TextSecureiOS/ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.m b/TextSecureiOS/ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.m new file mode 100644 index 0000000..1ed0a9d --- /dev/null +++ b/TextSecureiOS/ViewControllers/VerifyIdentity/TSVerifyIdentityViewController.m @@ -0,0 +1,106 @@ +// +// TSVerifyIdentityViewController.m +// TextSecureiOS +// +// Created by Christine Corbett Moran on 3/29/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSVerifyIdentityViewController.h" +#import "TSUserKeysDatabase.h" +#import "TSPresentIdentityQRCodeViewController.h" +#import "NSData+Conversion.h" + + +@interface TSVerifyIdentityViewController () + +@end + +@implementation TSVerifyIdentityViewController + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = self.contact.name; + self.theirIdentity.text = [self getFingerprintForDisplay:[self getTheirIdentityKey] ]; + self.myIdentity.text = [self getFingerprintForDisplay:[self getMyIdentityKey]]; + [self displayVerificationStatus]; +} + + +-(void) displayVerificationStatus { + self.identityVerifiedLabel.text = self.contact.identityKeyIsVerified ? @"Identity Verified" : @"Identity Not Verified"; +} +-(NSData*) getMyIdentityKey { + return [[TSUserKeysDatabase identityKey] publicKey]; +} + + +-(NSData*) getTheirIdentityKey { + return self.contact.identityKey; +} + + +-(NSString*) getFingerprintForDisplay:(NSData*)identityKey { + // idea here is to insert a space every two characters. there is probably a cleverer/more native way to do this. + + NSString* fingerprint = [[TSKeyManager getFingerprintFromIdentityKey:identityKey] hexadecimalString]; + __block NSString* formattedFingerprint = @""; + + + [fingerprint enumerateSubstringsInRange:NSMakeRange(0, [fingerprint length]) + options:NSStringEnumerationByComposedCharacterSequences + usingBlock: + ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + if (substringRange.location % 2 != 0 && substringRange.location != [fingerprint length]-1) { + substring = [substring stringByAppendingString:@" "]; + } + formattedFingerprint = [formattedFingerprint stringByAppendingString:substring]; + }]; + return formattedFingerprint; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + +#pragma mark - Navigation + + +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + [segue.destinationViewController setIdentityKey:[self getMyIdentityKey]]; + if([[segue identifier] isEqualToString:@"GetMyKeyScannedSegue"]){ + [segue.destinationViewController setIdentityKey:[self getMyIdentityKey]]; + } + else if([[segue identifier] isEqualToString:@"ScanTheirKeySegue"]){ + [segue.destinationViewController setIdentityKey:[self getTheirIdentityKey]]; + } + +} + + +- (IBAction)identityKeyWasVerified:(UIStoryboardSegue *)segue { + [self markManuallyVerified:self]; + + +} + + + +-(IBAction)markManuallyVerified:(id)sender { + self.contact.identityKeyIsVerified = YES; + [self.contact save]; + [self displayVerificationStatus]; +} + + +@end diff --git a/TextSecureiOS/Views/Cells/TSMessageConversationCell.h b/TextSecureiOS/Views/Cells/TSMessageConversationCell.h new file mode 100644 index 0000000..15574c0 --- /dev/null +++ b/TextSecureiOS/Views/Cells/TSMessageConversationCell.h @@ -0,0 +1,18 @@ +// +// TSMessageThreadCell.h +// TextSecureiOS +// +// Created by Claudiu-Vlad Ursache on 26/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import + +@interface TSMessageConversationCell : SWTableViewCell + +@property(nonatomic, readwrite, strong) UILabel *titleLabel; +@property(nonatomic, readwrite, strong) UILabel *timestampLabel; +@property(nonatomic, readwrite, strong) UILabel *conversationPreviewLabel; +@property(nonatomic, readwrite, strong) UIImageView *disclosureImageView; + +@end diff --git a/TextSecureiOS/Views/Cells/TSMessageConversationCell.m b/TextSecureiOS/Views/Cells/TSMessageConversationCell.m new file mode 100644 index 0000000..37dff52 --- /dev/null +++ b/TextSecureiOS/Views/Cells/TSMessageConversationCell.m @@ -0,0 +1,74 @@ +// +// TSMessageThreadCell.m +// TextSecureiOS +// +// Created by Claudiu-Vlad Ursache on 26/12/13. +// Copyright (c) 2013 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageConversationCell.h" + +@implementation TSMessageConversationCell + +- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (!self) return nil; + + self.opaque = YES; + + UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + titleLabel.textColor = [UIColor blackColor]; + titleLabel.font = [UIFont boldSystemFontOfSize:15.0f]; + self.titleLabel = titleLabel; + + UILabel *timestampLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + timestampLabel.textColor = [UIColor lightGrayColor]; + timestampLabel.font = [UIFont systemFontOfSize:10.0f]; + self.timestampLabel = timestampLabel; + + UILabel *threadPreviewLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + threadPreviewLabel.textColor = [UIColor lightGrayColor]; + threadPreviewLabel.numberOfLines = 2; + threadPreviewLabel.font = [UIFont systemFontOfSize:14.0f]; + self.conversationPreviewLabel = threadPreviewLabel; + + UIImageView *disclosureImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; + disclosureImageView.tintColor = [UIColor lightGrayColor]; + self.disclosureImageView = disclosureImageView; + + [self.contentView addSubview:self.timestampLabel]; + [self.contentView addSubview:self.titleLabel]; + [self.contentView addSubview:self.disclosureImageView]; + [self.contentView addSubview:self.conversationPreviewLabel]; + + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGRect contentViewBounds = self.contentView.bounds; + + CGFloat yPadding = 5.0f; + CGFloat leftPadding = 10.0f; + CGFloat rightPadding = 10.0f; + CGFloat topElementHeight = 25.0f; + + CGSize disclosureImageViewSize = CGSizeMake(topElementHeight, topElementHeight); + self.disclosureImageView.frame = CGRectMake(CGRectGetWidth(contentViewBounds) - disclosureImageViewSize.width, yPadding, disclosureImageViewSize.width, disclosureImageViewSize.height); + + CGSize timestampLabelSize = CGSizeMake(60, topElementHeight); + self.timestampLabel.frame = CGRectMake(CGRectGetWidth(contentViewBounds) - timestampLabelSize.width - disclosureImageViewSize.width, yPadding, timestampLabelSize.width, timestampLabelSize.height); + + CGSize titleLabelSize = CGSizeMake(CGRectGetWidth(contentViewBounds) - disclosureImageViewSize.width - timestampLabelSize.width, topElementHeight); + self.titleLabel.frame = CGRectMake(leftPadding, yPadding, titleLabelSize.width, titleLabelSize.height); + + CGSize threadPreviewLabelSize = CGSizeMake(CGRectGetWidth(contentViewBounds), CGRectGetHeight(contentViewBounds) - 2*yPadding - CGRectGetHeight(self.titleLabel.frame)); + self.conversationPreviewLabel.frame = CGRectMake(leftPadding, CGRectGetMaxY(self.titleLabel.frame), threadPreviewLabelSize.width - rightPadding - leftPadding, threadPreviewLabelSize.height); +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; +} + +@end diff --git a/TextSecureiOS/com.apple.TimeMachine.plist b/TextSecureiOS/com.apple.TimeMachine.plist deleted file mode 100644 index 0906430..0000000 Binary files a/TextSecureiOS/com.apple.TimeMachine.plist and /dev/null differ diff --git a/TextSecureiOS/en.lproj/MainStoryboard.storyboard b/TextSecureiOS/en.lproj/MainStoryboard.storyboard deleted file mode 100644 index 5d7d832..0000000 --- a/TextSecureiOS/en.lproj/MainStoryboard.storyboard +++ /dev/null @@ -1,589 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TextSecureiOS/en.lproj/TextSecureStoryboard.storyboard b/TextSecureiOS/en.lproj/TextSecureStoryboard.storyboard new file mode 100644 index 0000000..4a1163c --- /dev/null +++ b/TextSecureiOS/en.lproj/TextSecureStoryboard.storyboard @@ -0,0 +1,853 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TextSecureiOS/gcm.textsecure.whispersystems.org.cer b/TextSecureiOS/gcm.textsecure.whispersystems.org.cer new file mode 100644 index 0000000..6ba9981 Binary files /dev/null and b/TextSecureiOS/gcm.textsecure.whispersystems.org.cer differ diff --git a/TextSecureiOS/main.m b/TextSecureiOS/main.m index 2545e84..6d604a5 100644 --- a/TextSecureiOS/main.m +++ b/TextSecureiOS/main.m @@ -1,3 +1,5 @@ + + // // main.m // TextSecureiOS diff --git a/TextSecureiOSTests/TextSecureiOSTests.h b/TextSecureiOSTests/TextSecureiOSTests.h deleted file mode 100644 index 854d1b0..0000000 --- a/TextSecureiOSTests/TextSecureiOSTests.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// TextSecureiOSTests.h -// TextSecureiOSTests -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import - -@interface TextSecureiOSTests : SenTestCase - -@end diff --git a/TextSecureiOSTests/TextSecureiOSTests.m b/TextSecureiOSTests/TextSecureiOSTests.m deleted file mode 100644 index ad6c279..0000000 --- a/TextSecureiOSTests/TextSecureiOSTests.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// TextSecureiOSTests.m -// TextSecureiOSTests -// -// Created by Christine Corbett Moran on 3/24/13. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "TextSecureiOSTests.h" - -@implementation TextSecureiOSTests - -- (void)setUp -{ - [super setUp]; - - // Set-up code here. -} - -- (void)tearDown -{ - // Tear-down code here. - - [super tearDown]; -} - -- (void)testExample -{ - STFail(@"Unit tests are not implemented yet in TextSecureiOSTests"); -} - -@end