diff --git a/Podfile b/Podfile index 90011102c..0c18e171f 100644 --- a/Podfile +++ b/Podfile @@ -69,6 +69,9 @@ target 'Signal' do project 'Signal' shared_pods pod 'SSZipArchive', :inhibit_warnings => true + + # Loki + pod 'GCDWebServer', '~> 3.0' target 'SignalTests' do inherit! :search_paths diff --git a/Podfile.lock b/Podfile.lock index 7ea048b0d..08ccc0a45 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -36,6 +36,9 @@ PODS: - Curve25519Kit/Tests (2.1.0): - CocoaLumberjack - SignalCoreKit + - GCDWebServer (3.5.2): + - GCDWebServer/Core (= 3.5.2) + - GCDWebServer/Core (3.5.2) - GRKOpenSSLFramework (1.0.2.12) - HKDFKit (0.0.4): - CocoaLumberjack @@ -194,6 +197,7 @@ DEPENDENCIES: - CryptoSwift - Curve25519Kit (from `https://github.com/signalapp/Curve25519Kit`) - Curve25519Kit/Tests (from `https://github.com/signalapp/Curve25519Kit`) + - GCDWebServer (~> 3.0) - GRKOpenSSLFramework (from `https://github.com/signalapp/GRKOpenSSLFramework`) - HKDFKit (from `https://github.com/signalapp/HKDFKit.git`) - HKDFKit/Tests (from `https://github.com/signalapp/HKDFKit.git`) @@ -218,6 +222,7 @@ SPEC REPOS: - AFNetworking - CocoaLumberjack - CryptoSwift + - GCDWebServer - libPhoneNumber-iOS - PromiseKit - PureLayout @@ -289,6 +294,7 @@ SPEC CHECKSUMS: CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947 CryptoSwift: d81eeaa59dc5a8d03720fe919a6fd07b51f7439f Curve25519Kit: b3e77b7152ebe95fee2b3fb6c955449492bc14f7 + GCDWebServer: ead88cd14596dd4eae4f5830b8877c87c8728990 GRKOpenSSLFramework: 8a3735ad41e7dc1daff460467bccd32ca5d6ae3e HKDFKit: 3b6dbbb9d59c221cc6c52c3aa915700cbf24e376 libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa @@ -299,7 +305,7 @@ SPEC CHECKSUMS: SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SignalCoreKit: c2d8132cdedb95d35eb2f8ae7eac0957695d0a8b SignalMetadataKit: 6fa5e9a53c7f104568662521a2f3874672ff7a02 - SignalServiceKit: 54fde76bc75b63bd5e9088acdbd3be0ff7e154d0 + SignalServiceKit: 7f97a29c42328c02ae95f94cce08aa8bc92ab258 SQLCipher: efbdb52cdbe340bcd892b1b14297df4e07241b7f SSZipArchive: 8e859da2520142e09166bc9161967db296e9d02f Starscream: ef3ece99d765eeccb67de105bfa143f929026cf5 @@ -307,6 +313,6 @@ SPEC CHECKSUMS: YapDatabase: b418a4baa6906e8028748938f9159807fd039af4 YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 -PODFILE CHECKSUM: beb49888a2355e89f15280fcfa5105ddc59a5c0a +PODFILE CHECKSUM: 708b5bc644a63e9bc55c409ac292c88c8ee78fad COCOAPODS: 1.5.3 diff --git a/Pods b/Pods index 44417f949..62d4d97c7 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 44417f9493b8c89b45c1acb72e538d7cd773ebd7 +Subproject commit 62d4d97c76c956b32957690d324145c18adf3279 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 9a8604f0a..c2bf82e68 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */; }; 2AE2882E4C2B96BFFF9EE27C /* Pods_SignalShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */; }; 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; }; 34074F61203D0CBE004596AE /* OWSSounds.m in Sources */ = {isa = PBXBuildFile; fileRef = 34074F5F203D0CBD004596AE /* OWSSounds.m */; }; @@ -654,6 +655,7 @@ 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; + 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiP2PServer.swift; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = ""; }; 3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareButtonsView.h; sourceTree = ""; }; @@ -2606,6 +2608,7 @@ B821F2F72272CED3002C88C0 /* OnboardingAccountDetailsViewController.swift */, B821F2F92272CEEE002C88C0 /* OnboardingKeyPairViewController.swift */, B8439519228510FB000563FE /* Poller.swift */, + 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, ); path = Loki; sourceTree = ""; @@ -3257,6 +3260,7 @@ "${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework", "${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework", "${BUILT_PRODUCTS_DIR}/Curve25519Kit/Curve25519Kit.framework", + "${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework", "${PODS_ROOT}/GRKOpenSSLFramework/OpenSSL-iOS/bin/openssl.framework", "${BUILT_PRODUCTS_DIR}/HKDFKit/HKDFKit.framework", "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", @@ -3282,6 +3286,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptoSwift.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Curve25519Kit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GCDWebServer.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HKDFKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", @@ -3639,6 +3644,7 @@ 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */, 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */, + 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */, 349ED990221B0194008045B0 /* Onboarding2FAViewController.swift in Sources */, 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */, 340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 79957c086..5f9be61a3 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -63,6 +63,7 @@ static NSTimeInterval launchStartedAt; @property (nonatomic) BOOL hasInitialRootViewController; @property (nonatomic) BOOL areVersionMigrationsComplete; @property (nonatomic) BOOL didAppLaunchFail; +@property (nonatomic) LKP2PServer *lokiP2PServer; // Loki @end @@ -187,6 +188,8 @@ static NSTimeInterval launchStartedAt; OWSLogInfo(@"applicationWillTerminate."); [DDLog flushLog]; + + if (self.lokiP2PServer) { [self.lokiP2PServer stop]; } } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -307,7 +310,25 @@ static NSTimeInterval launchStartedAt; OWSLogInfo(@"application: didFinishLaunchingWithOptions completed."); [OWSAnalytics appLaunchDidBegin]; - + + // Loki + self.lokiP2PServer = [LKP2PServer new]; + + // We try to bind to 8081, if we can't then we just fallback to any random port + NSArray *ports = @[ @8081, @0 ]; + for (NSNumber *port in ports) { + if (self.lokiP2PServer.isRunning) { break; } + BOOL isStarted = [self.lokiP2PServer startOnPort:port.unsignedIntegerValue]; + if (isStarted) { + OWSLogInfo(@"[Loki] Started server at %@.", self.lokiP2PServer.serverURL); + break; + } + } + + if (!self.lokiP2PServer.isRunning) { + OWSLogWarn(@"[Loki] Failed to start P2P server."); + } + return YES; } diff --git a/Signal/src/Loki/LokiP2PServer.swift b/Signal/src/Loki/LokiP2PServer.swift new file mode 100644 index 000000000..759b28079 --- /dev/null +++ b/Signal/src/Loki/LokiP2PServer.swift @@ -0,0 +1,100 @@ +import GCDWebServer + +private extension GCDWebServerResponse { + + convenience init(statusCode: E) where E.RawValue == Int { + self.init(statusCode: statusCode.rawValue) + } +} + +private extension GCDWebServerDataRequest { + var truncatedContentType: String? { + guard let contentType = contentType else { return nil } + guard let substring = contentType.split(separator: ";").first else { return contentType } + return String(substring) + } + + // GCDWebServerDataRequest already provides this implementation + // However it will abort when running in DEBUG, we don't want that so we just override it with a version which doesn't abort + var jsonObject: JSON? { + let acceptedMimeTypes = [ "application/json", "text/json", "text/javascript" ] + guard let mimeType = truncatedContentType, acceptedMimeTypes.contains(mimeType) else { return nil } + + do { + let object = try JSONSerialization.jsonObject(with: data, options: .allowFragments) + return object as? JSON + } catch let error { + Logger.debug("[Loki] Failed to serialize JSON: \(error).") + } + + return nil + } +} + +@objc(LKP2PServer) +final class LokiP2PServer : NSObject { + + private enum StatusCode : Int { + case ok = 200 + case badRequest = 400 + case notFound = 404 + case methodNotAllowed = 405 + case internalServerError = 500 + } + + private lazy var webServer: GCDWebServer = { + let webServer = GCDWebServer() + + // Don't allow specific methods + let invalidMethodProcessBlock: (GCDWebServerRequest) -> GCDWebServerResponse? = { _ in + return GCDWebServerResponse(statusCode: StatusCode.methodNotAllowed) + } + + let invalidMethods = [ "GET", "PUT", "DELETE" ] + for method in invalidMethods { + webServer.addDefaultHandler(forMethod: method, request: GCDWebServerRequest.self, processBlock: invalidMethodProcessBlock) + } + + // By default send 404 for any path + webServer.addDefaultHandler(forMethod: "POST", request: GCDWebServerRequest.self, processBlock: { _ in + return GCDWebServerResponse(statusCode: StatusCode.notFound) + }) + + // Handle our specific storage path + webServer.addHandler(forMethod: "POST", path: "/v1/storage_rpc", request: GCDWebServerDataRequest.self, processBlock: { request in + // Make sure we were sent a good request + guard let dataRequest = request as? GCDWebServerDataRequest, let json = dataRequest.jsonObject else { + return GCDWebServerResponse(statusCode: StatusCode.badRequest) + } + + // Only allow the store method + guard let method = json["method"] as? String, method == "store" else { + return GCDWebServerResponse(statusCode: StatusCode.notFound) + } + + // Make sure we have the data + guard let params = json["params"] as? [String: String], let data = params["data"] else { + return GCDWebServerResponse(statusCode: StatusCode.badRequest) + } + + // Pass it off to the message handler + LokiP2PMessageHandler.shared.handleReceivedMessage(base64EncodedData: data) + + // Send a response back + return GCDWebServerResponse(statusCode: StatusCode.ok.rawValue) + }) + + return webServer + }() + + @objc public var serverURL: URL? { return webServer.serverURL } + @objc public var isRunning: Bool { return webServer.isRunning } + + @objc @discardableResult func start(onPort port: UInt) -> Bool { + guard !webServer.isRunning else { return false } + webServer.start(withPort: port, bonjourName: nil) + return webServer.isRunning + } + + @objc func stop() { webServer.stop() } +} diff --git a/SignalServiceKit.podspec b/SignalServiceKit.podspec index d70992803..7e4126d02 100644 --- a/SignalServiceKit.podspec +++ b/SignalServiceKit.podspec @@ -36,7 +36,7 @@ An Objective-C library for communicating with the Signal messaging service. s.prefix_header_file = 'SignalServiceKit/src/TSPrefix.h' s.xcconfig = { 'OTHER_CFLAGS' => '$(inherited) -DSQLITE_HAS_CODEC' } - s.resources = ["SignalServiceKit/Resources/Certificates/*"] + s.resources = ["SignalServiceKit/Resources/Certificates/*", "SignalServiceKit/src/Loki/Mnemonic/*.txt"] s.dependency 'Curve25519Kit' s.dependency 'CocoaLumberjack' diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index bc17df0dc..ce2e45d2e 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -38,6 +38,10 @@ message Envelope { optional string serverGuid = 9; // We may eventually want to make this required. optional uint64 serverTimestamp = 10; + + // Loki: This field is only here as a helper + // It shouldn't be set when sending a message + optional bool isPtpMessage = 999; } message TypingMessage { diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift b/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift index 1c3aa29d9..021348535 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift @@ -7,7 +7,7 @@ public extension LokiAPI { let destination: String /// The content of the message. let data: LosslessStringConvertible - /// The time to live for the message. + /// The time to live for the message in seconds. let ttl: UInt64 /// When the proof of work was calculated, if applicable. /// @@ -32,10 +32,11 @@ public extension LokiAPI { return Promise { seal in DispatchQueue.global(qos: .default).async { do { - let wrappedMessage = try wrap(message: signalMessage, timestamp: timestamp) + let wrappedMessage = try LokiMessageWrapper.wrap(message: signalMessage, timestamp: timestamp) let data = wrappedMessage.base64EncodedString() let destination = signalMessage["destination"] as! String - let ttl = LokiAPI.defaultMessageTTL + var ttl = LokiAPI.defaultMessageTTL + if let messageTTL = signalMessage["ttl"] as? UInt, messageTTL > 0 { ttl = UInt64(messageTTL) } if isPoWRequired { // The storage server takes a time interval in milliseconds let now = NSDate.ows_millisecondTimeStamp() diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index f5e191993..826c41bdd 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -148,7 +148,7 @@ import PromiseKit Logger.warn("[Loki] Failed to decode data for message: \(rawMessage).") return nil } - guard let envelope = try? unwrap(data: data) else { + guard let envelope = try? LokiMessageWrapper.unwrap(data: data) else { Logger.warn("[Loki] Failed to unwrap data for message: \(rawMessage).") return nil } diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Wrapping.swift b/SignalServiceKit/src/Loki/API/LokiMessageWrapper.swift similarity index 94% rename from SignalServiceKit/src/Loki/API/LokiAPI+Wrapping.swift rename to SignalServiceKit/src/Loki/API/LokiMessageWrapper.swift index 255a5652e..03ae7dc70 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+Wrapping.swift +++ b/SignalServiceKit/src/Loki/API/LokiMessageWrapper.swift @@ -1,7 +1,7 @@ -extension LokiAPI { +public enum LokiMessageWrapper { - enum WrappingError : LocalizedError { + public enum WrappingError : LocalizedError { case failedToWrapData case failedToWrapMessageInEnvelope case failedToWrapEnvelopeInWebSocketMessage @@ -25,7 +25,7 @@ extension LokiAPI { /// - timestamp: The original message timestamp (`TSOutgoingMessage.timestamp`). /// - Returns: The wrapped message data. /// - Throws: A `WrappingError` if something went wrong. - static func wrap(message: SignalMessage, timestamp: UInt64) throws -> Data { + public static func wrap(message: SignalMessage, timestamp: UInt64) throws -> Data { do { let envelope = try createEnvelope(around: message, timestamp: timestamp) let webSocketMessage = try createWebSocketMessage(around: envelope) @@ -81,7 +81,7 @@ extension LokiAPI { /// - Parameter data: The data from the storage server (not base 64 encoded). /// - Returns: An `SSKProtoEnvelope` object. /// - Throws: A `WrappingError` if something went wrong. - static func unwrap(data: Data) throws -> SSKProtoEnvelope { + public static func unwrap(data: Data) throws -> SSKProtoEnvelope { do { let webSocketMessage = try WebSocketProtoWebSocketMessage.parseData(data) let envelope = webSocketMessage.request!.body! diff --git a/SignalServiceKit/src/Loki/API/LokiP2PMessageHandler.swift b/SignalServiceKit/src/Loki/API/LokiP2PMessageHandler.swift new file mode 100644 index 000000000..593bad7fb --- /dev/null +++ b/SignalServiceKit/src/Loki/API/LokiP2PMessageHandler.swift @@ -0,0 +1,33 @@ + +public final class LokiP2PMessageHandler { + private let messageReceiver = SSKEnvironment.shared.messageReceiver + + // MARK: Initialization + public static let shared = LokiP2PMessageHandler() + + private init() { } + + // MARK: General + public func handleReceivedMessage(base64EncodedData: String) { + guard let data = Data(base64Encoded: base64EncodedData) else { + Logger.warn("[Loki] Failed to decode data for P2P message.") + return + } + guard let envelope = try? LokiMessageWrapper.unwrap(data: data) else { + Logger.warn("[Loki] Failed to unwrap data for P2P message.") + return + } + // We need to set the P2P field on the envelope + let builder = envelope.asBuilder() + builder.setIsPtpMessage(true) + // Send it to the message receiver + do { + let newEnvelope = try builder.build() + let envelopeData = try newEnvelope.serializedData() + messageReceiver.handleReceivedEnvelopeData(envelopeData) + } catch let error { + Logger.warn("[Loki] Something went wrong during proto conversion: \(error).") + } + } + +} diff --git a/SignalServiceKit/src/Loki/API/SignalMessage.swift b/SignalServiceKit/src/Loki/API/SignalMessage.swift index 11d7eec95..f543fe1df 100644 --- a/SignalServiceKit/src/Loki/API/SignalMessage.swift +++ b/SignalServiceKit/src/Loki/API/SignalMessage.swift @@ -1,2 +1,3 @@ +// This is basically OWSMessageServiceParams public typealias SignalMessage = [String:Any] diff --git a/SignalServiceKit/src/Loki/Crypto/Mnemonic/Mnemonic.swift b/SignalServiceKit/src/Loki/Crypto/Mnemonic.swift similarity index 100% rename from SignalServiceKit/src/Loki/Crypto/Mnemonic/Mnemonic.swift rename to SignalServiceKit/src/Loki/Crypto/Mnemonic.swift diff --git a/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.h b/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.h index ad261ebf9..b966b96c4 100644 --- a/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.h +++ b/SignalServiceKit/src/Loki/Crypto/SessionCipher+Loki.h @@ -1,6 +1,6 @@ // Loki: Refer to Docs/SessionReset.md for explanations -#import "SessionCipher.h" +#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalServiceKit/src/Loki/Messages/LKFriendRequestMessage.m b/SignalServiceKit/src/Loki/Messages/LKFriendRequestMessage.m index eed1204c2..cddf75886 100644 --- a/SignalServiceKit/src/Loki/Messages/LKFriendRequestMessage.m +++ b/SignalServiceKit/src/Loki/Messages/LKFriendRequestMessage.m @@ -1,11 +1,14 @@ #import "LKFriendRequestMessage.h" #import "OWSPrimaryStorage+Loki.h" +#import "NSDate+OWS.h" #import "SignalRecipient.h" #import @implementation LKFriendRequestMessage --(BOOL)isFriendRequest { return YES; } +- (BOOL)isFriendRequest { return YES; } + +- (uint)ttl { return 4 * kDayInterval; } // Friend requests should stay for the longest on the storage server - (SSKProtoContentBuilder *)contentBuilder:(SignalRecipient *)recipient { SSKProtoContentBuilder *contentBuilder = [super contentBuilder:recipient]; diff --git a/SignalServiceKit/src/Loki/Messages/SSKProtoPrekeyBundleMessage+Loki.swift b/SignalServiceKit/src/Loki/Messages/SSKProtoPrekeyBundleMessage+Loki.swift index e907709b0..a951dfd00 100644 --- a/SignalServiceKit/src/Loki/Messages/SSKProtoPrekeyBundleMessage+Loki.swift +++ b/SignalServiceKit/src/Loki/Messages/SSKProtoPrekeyBundleMessage+Loki.swift @@ -1,7 +1,7 @@ @objc public extension SSKProtoPrekeyBundleMessage { - @objc public class func builder(fromPreKeyBundle preKeyBundle: PreKeyBundle) -> SSKProtoPrekeyBundleMessageBuilder { + @objc public static func builder(fromPreKeyBundle preKeyBundle: PreKeyBundle) -> SSKProtoPrekeyBundleMessageBuilder { let builder = self.builder() builder.setIdentityKey(preKeyBundle.identityKey) diff --git a/SignalServiceKit/src/Loki/Crypto/Mnemonic/english.txt b/SignalServiceKit/src/Loki/Mnemonic/english.txt similarity index 100% rename from SignalServiceKit/src/Loki/Crypto/Mnemonic/english.txt rename to SignalServiceKit/src/Loki/Mnemonic/english.txt diff --git a/SignalServiceKit/src/Loki/Crypto/Mnemonic/japanese.txt b/SignalServiceKit/src/Loki/Mnemonic/japanese.txt similarity index 100% rename from SignalServiceKit/src/Loki/Crypto/Mnemonic/japanese.txt rename to SignalServiceKit/src/Loki/Mnemonic/japanese.txt diff --git a/SignalServiceKit/src/Loki/Crypto/Mnemonic/portuguese.txt b/SignalServiceKit/src/Loki/Mnemonic/portuguese.txt similarity index 100% rename from SignalServiceKit/src/Loki/Crypto/Mnemonic/portuguese.txt rename to SignalServiceKit/src/Loki/Mnemonic/portuguese.txt diff --git a/SignalServiceKit/src/Loki/Crypto/Mnemonic/spanish.txt b/SignalServiceKit/src/Loki/Mnemonic/spanish.txt similarity index 100% rename from SignalServiceKit/src/Loki/Crypto/Mnemonic/spanish.txt rename to SignalServiceKit/src/Loki/Mnemonic/spanish.txt diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h index 93adadaba..eca1b47e2 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.h @@ -141,9 +141,12 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) { @property (nonatomic, readonly) BOOL isOnline; -// Loki: Bool to indicate if proof of work is being calculated for this message +/// Loki: Bool to indicate if proof of work is being calculated for this message @property (atomic, readonly) BOOL isCalculatingPoW; +/// Loki: Time to live for the message in seconds +@property (nonatomic, readonly) uint ttl; + /** * The data representation of this message, to be encrypted, before being sent. */ diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index eed46d2b5..7c8fd30ff 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -1154,6 +1154,12 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt return [result copy]; } +- (uint)ttl { + // Time to live for all messages (except friend request messages) should be 1 day + // TODO: Change this to return a value that the user chose + return 1 * kDayInterval; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 8ec448685..32299d3b1 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1773,6 +1773,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; SignalRecipient *recipient = messageSend.recipient; NSString *recipientId = recipient.recipientId; + TSOutgoingMessage *message = messageSend.message; FallBackSessionCipher *cipher = [[FallBackSessionCipher alloc] initWithRecipientId:recipientId identityKeyStore:self.identityManager]; @@ -1790,7 +1791,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; content:serializedMessage isSilent:false isOnline:false - registrationId:0]; + registrationId:0 + ttl:message.ttl]; NSError *error; NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error]; @@ -1884,11 +1886,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; content:serializedMessage isSilent:isSilent isOnline:isOnline - registrationId:[cipher throws_remoteRegistrationId:transaction]]; + registrationId:[cipher throws_remoteRegistrationId:transaction] + ttl:message.ttl]; NSError *error; NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error]; - + if (error) { OWSProdError([OWSAnalyticsEvents messageSendErrorCouldNotSerializeMessageJson]); return nil; diff --git a/SignalServiceKit/src/Messages/OWSMessageServiceParams.h b/SignalServiceKit/src/Messages/OWSMessageServiceParams.h index a2e1d9d3f..3a8dc25c9 100644 --- a/SignalServiceKit/src/Messages/OWSMessageServiceParams.h +++ b/SignalServiceKit/src/Messages/OWSMessageServiceParams.h @@ -24,13 +24,17 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL silent; @property (nonatomic, readonly) BOOL online; +// Loki: Message ttl +@property (nonatomic, readonly) uint ttl; + - (instancetype)initWithType:(TSWhisperMessageType)type recipientId:(NSString *)destination device:(int)deviceId content:(NSData *)content isSilent:(BOOL)isSilent isOnline:(BOOL)isOnline - registrationId:(int)registrationId; + registrationId:(int)registrationId + ttl:(uint)ttl; @end diff --git a/SignalServiceKit/src/Messages/OWSMessageServiceParams.m b/SignalServiceKit/src/Messages/OWSMessageServiceParams.m index a2a66121c..cfd88bd3e 100644 --- a/SignalServiceKit/src/Messages/OWSMessageServiceParams.m +++ b/SignalServiceKit/src/Messages/OWSMessageServiceParams.m @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN isSilent:(BOOL)isSilent isOnline:(BOOL)isOnline registrationId:(int)registrationId + ttl:(uint)ttl { self = [super init]; @@ -36,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN _content = [content base64EncodedString]; _silent = isSilent; _online = isOnline; + _ttl = ttl; return self; } diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index e01692b52..5fa154cd9 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -80,6 +80,9 @@ public enum SSKProtoError: Error { if hasServerTimestamp { builder.setServerTimestamp(serverTimestamp) } + if hasIsPtpMessage { + builder.setIsPtpMessage(isPtpMessage) + } return builder } @@ -132,6 +135,10 @@ public enum SSKProtoError: Error { proto.serverTimestamp = valueParam } + @objc public func setIsPtpMessage(_ valueParam: Bool) { + proto.isPtpMessage = valueParam + } + @objc public func build() throws -> SSKProtoEnvelope { return try SSKProtoEnvelope.parseProto(proto) } @@ -211,6 +218,13 @@ public enum SSKProtoError: Error { return proto.hasServerTimestamp } + @objc public var isPtpMessage: Bool { + return proto.isPtpMessage + } + @objc public var hasIsPtpMessage: Bool { + return proto.hasIsPtpMessage + } + private init(proto: SignalServiceProtos_Envelope, type: SSKProtoEnvelopeType, timestamp: UInt64) { diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index faa449417..b5e1f4b26 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -119,6 +119,17 @@ struct SignalServiceProtos_Envelope { /// Clears the value of `serverTimestamp`. Subsequent reads from it will return its default value. mutating func clearServerTimestamp() {self._serverTimestamp = nil} + /// Loki: This field is only here as a helper + /// It shouldn't be set when sending a message + var isPtpMessage: Bool { + get {return _isPtpMessage ?? false} + set {_isPtpMessage = newValue} + } + /// Returns true if `isPtpMessage` has been explicitly set. + var hasIsPtpMessage: Bool {return self._isPtpMessage != nil} + /// Clears the value of `isPtpMessage`. Subsequent reads from it will return its default value. + mutating func clearIsPtpMessage() {self._isPtpMessage = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() enum TypeEnum: SwiftProtobuf.Enum { @@ -175,6 +186,7 @@ struct SignalServiceProtos_Envelope { fileprivate var _content: Data? = nil fileprivate var _serverGuid: String? = nil fileprivate var _serverTimestamp: UInt64? = nil + fileprivate var _isPtpMessage: Bool? = nil } #if swift(>=4.2) @@ -437,6 +449,7 @@ struct SignalServiceProtos_LokiAddressMessage { // methods supported on all messages. /// The naming is a bit different from desktop because of swift auto generation + /// It doesn't like p2p much :( var ptpAddress: String { get {return _ptpAddress ?? String()} set {_ptpAddress = newValue} @@ -2473,6 +2486,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me 8: .same(proto: "content"), 9: .same(proto: "serverGuid"), 10: .same(proto: "serverTimestamp"), + 999: .same(proto: "isPtpMessage"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2487,6 +2501,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me case 8: try decoder.decodeSingularBytesField(value: &self._content) case 9: try decoder.decodeSingularStringField(value: &self._serverGuid) case 10: try decoder.decodeSingularUInt64Field(value: &self._serverTimestamp) + case 999: try decoder.decodeSingularBoolField(value: &self._isPtpMessage) default: break } } @@ -2520,6 +2535,9 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me if let v = self._serverTimestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 10) } + if let v = self._isPtpMessage { + try visitor.visitSingularBoolField(value: v, fieldNumber: 999) + } try unknownFields.traverse(visitor: &visitor) } @@ -2533,6 +2551,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me if lhs._content != rhs._content {return false} if lhs._serverGuid != rhs._serverGuid {return false} if lhs._serverTimestamp != rhs._serverTimestamp {return false} + if lhs._isPtpMessage != rhs._isPtpMessage {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true }