From c91bc8b7f38a83ef068e2de11b80cb2303b98f19 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Mon, 14 Sep 2020 15:16:51 +1000 Subject: [PATCH] Implement SSK protocol changes --- Pods | 2 +- SignalServiceKit/protobuf/SignalService.proto | 7 ++ .../OnionRequestAPI+Encryption.swift | 32 +---- .../API/Onion Requests/OnionRequestAPI.swift | 12 +- .../{ => To Do}/PublicChatManager.swift | 0 .../API/Utilities/DecryptionUtilities.swift | 18 +++ .../API/Utilities/EncryptionUtilities.swift | 38 ++++++ .../Closed Groups/ClosedGroupUtilities.swift | 70 +++++++++++ .../src/Messages/OWSMessageDecrypter.m | 23 +++- .../src/Messages/OWSMessageSender.m | 35 ++++-- .../src/Protos/Generated/SSKProto.swift | 112 ++++++++++++++++++ .../Protos/Generated/SignalService.pb.swift | 68 +++++++++++ SignalServiceKit/src/TSConstants.h | 1 + 13 files changed, 365 insertions(+), 53 deletions(-) rename SignalServiceKit/src/Loki/API/Open Groups/{ => To Do}/PublicChatManager.swift (100%) create mode 100644 SignalServiceKit/src/Loki/API/Utilities/DecryptionUtilities.swift create mode 100644 SignalServiceKit/src/Loki/API/Utilities/EncryptionUtilities.swift create mode 100644 SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUtilities.swift diff --git a/Pods b/Pods index c89ab147a..688635bff 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit c89ab147afd32d3dde1e249070dd544abcf9c1c5 +Subproject commit 688635bfffb46fddf0170e2e611aee261474a67d diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index b2db53872..235537b11 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -132,6 +132,13 @@ message CallMessage { optional bytes profileKey = 6; } +message ClosedGroupCiphertextMessageWrapper { + // @required + optional bytes ciphertext = 1; + // @required + optional bytes ephemeralPublicKey = 2; +} + message DataMessage { enum Flags { END_SESSION = 1; diff --git a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift index 832c6f0e5..a56a0bab8 100644 --- a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift +++ b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI+Encryption.swift @@ -2,32 +2,6 @@ import CryptoSwift import PromiseKit extension OnionRequestAPI { - internal static let gcmTagSize: UInt = 16 - internal static let ivSize: UInt = 12 - - internal typealias EncryptionResult = (ciphertext: Data, symmetricKey: Data, ephemeralPublicKey: Data) - - /// - Note: Sync. Don't call from the main thread. - private static func encrypt(_ plaintext: Data, usingAESGCMWithSymmetricKey symmetricKey: Data) throws -> Data { - guard !Thread.isMainThread else { preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") } - guard let iv = Data.getSecureRandomData(ofSize: ivSize) else { throw Error.randomDataGenerationFailed } - let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) - let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .noPadding) - let ciphertext = try aes.encrypt(plaintext.bytes) - return iv + Data(bytes: ciphertext) - } - - /// - Note: Sync. Don't call from the main thread. - private static func encrypt(_ plaintext: Data, using hexEncodedX25519PublicKey: String) throws -> EncryptionResult { - guard !Thread.isMainThread else { preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") } - let x25519PublicKey = Data(hex: hexEncodedX25519PublicKey) - let ephemeralKeyPair = Curve25519.generateKeyPair() - let ephemeralSharedSecret = try Curve25519.generateSharedSecret(fromPublicKey: x25519PublicKey, privateKey: ephemeralKeyPair.privateKey) - let salt = "LOKI" - let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) - let ciphertext = try encrypt(plaintext, usingAESGCMWithSymmetricKey: Data(bytes: symmetricKey)) - return (ciphertext, Data(bytes: symmetricKey), ephemeralKeyPair.publicKey) - } /// Encrypts `payload` for `destination` and returns the result. Use this to build the core of an onion request. internal static func encrypt(_ payload: JSON, for destination: Destination) -> Promise { @@ -44,11 +18,11 @@ extension OnionRequestAPI { let wrapper: JSON = [ "body" : payloadAsString, "headers" : "" ] guard JSONSerialization.isValidJSONObject(wrapper) else { return seal.reject(HTTP.Error.invalidJSON) } let plaintext = try JSONSerialization.data(withJSONObject: wrapper, options: [ .fragmentsAllowed ]) - let result = try encrypt(plaintext, using: snodeX25519PublicKey) + let result = try EncryptionUtilities.encrypt(plaintext, using: snodeX25519PublicKey) seal.fulfill(result) case .server(_, let serverX25519PublicKey): let plaintext = try JSONSerialization.data(withJSONObject: payload, options: [ .fragmentsAllowed ]) - let result = try encrypt(plaintext, using: serverX25519PublicKey) + let result = try EncryptionUtilities.encrypt(plaintext, using: serverX25519PublicKey) seal.fulfill(result) } } catch (let error) { @@ -83,7 +57,7 @@ extension OnionRequestAPI { do { guard JSONSerialization.isValidJSONObject(parameters) else { return seal.reject(HTTP.Error.invalidJSON) } let plaintext = try JSONSerialization.data(withJSONObject: parameters, options: [ .fragmentsAllowed ]) - let result = try encrypt(plaintext, using: x25519PublicKey) + let result = try EncryptionUtilities.encrypt(plaintext, using: x25519PublicKey) seal.fulfill(result) } catch (let error) { seal.reject(error) diff --git a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift index 91aa19389..c4188cbc0 100644 --- a/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift +++ b/SignalServiceKit/src/Loki/API/Onion Requests/OnionRequestAPI.swift @@ -21,12 +21,11 @@ public enum OnionRequestAPI { } // MARK: Error - public enum Error : LocalizedError { + public enum Error : LocalizedError { case httpRequestFailedAtDestination(statusCode: UInt, json: JSON) case insufficientSnodes case invalidURL case missingSnodeVersion - case randomDataGenerationFailed case snodePublicKeySetMissing case unsupportedSnodeVersion(String) @@ -36,7 +35,6 @@ public enum OnionRequestAPI { case .insufficientSnodes: return "Couldn't find enough snodes to build a path." case .invalidURL: return "Invalid URL" case .missingSnodeVersion: return "Missing snode version." - case .randomDataGenerationFailed: return "Couldn't generate random data." case .snodePublicKeySetMissing: return "Missing snode public key set." case .unsupportedSnodeVersion(let version): return "Unsupported snode version: \(version)." } @@ -297,13 +295,9 @@ public enum OnionRequestAPI { let destinationSymmetricKey = intermediate.destinationSymmetricKey HTTP.execute(.post, url, parameters: parameters).done2 { rawResponse in guard let json = rawResponse as? JSON, let base64EncodedIVAndCiphertext = json["result"] as? String, - let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= ivSize else { return seal.reject(HTTP.Error.invalidJSON) } - let iv = ivAndCiphertext[0..= EncryptionUtilities.ivSize else { return seal.reject(HTTP.Error.invalidJSON) } do { - let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) - let aes = try AES(key: destinationSymmetricKey.bytes, blockMode: gcm, padding: .noPadding) - let data = Data(try aes.decrypt(ciphertext.bytes)) + let data = try DecryptionUtilities.decrypt(ivAndCiphertext, usingAESGCMWithSymmetricKey: destinationSymmetricKey) guard let json = try JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON, let statusCode = json["status"] as? Int else { return seal.reject(HTTP.Error.invalidJSON) } if statusCode == 406 { // Clock out of sync diff --git a/SignalServiceKit/src/Loki/API/Open Groups/PublicChatManager.swift b/SignalServiceKit/src/Loki/API/Open Groups/To Do/PublicChatManager.swift similarity index 100% rename from SignalServiceKit/src/Loki/API/Open Groups/PublicChatManager.swift rename to SignalServiceKit/src/Loki/API/Open Groups/To Do/PublicChatManager.swift diff --git a/SignalServiceKit/src/Loki/API/Utilities/DecryptionUtilities.swift b/SignalServiceKit/src/Loki/API/Utilities/DecryptionUtilities.swift new file mode 100644 index 000000000..974451235 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/Utilities/DecryptionUtilities.swift @@ -0,0 +1,18 @@ +import CryptoSwift + +enum DecryptionUtilities { + + /// - Note: Sync. Don't call from the main thread. + internal static func decrypt(_ ivAndCiphertext: Data, usingAESGCMWithSymmetricKey symmetricKey: Data) throws -> Data { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call decrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") + #endif + } + let iv = ivAndCiphertext[0.. Data { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.") + #endif + } + let iv = Data.getSecureRandomData(ofSize: ivSize)! + let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) + let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .noPadding) + let ciphertext = try aes.encrypt(plaintext.bytes) + return iv + Data(bytes: ciphertext) + } + + /// - Note: Sync. Don't call from the main thread. + internal static func encrypt(_ plaintext: Data, using hexEncodedX25519PublicKey: String) throws -> EncryptionResult { + if Thread.isMainThread { + #if DEBUG + preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.") + #endif + } + let x25519PublicKey = Data(hex: hexEncodedX25519PublicKey) + let ephemeralKeyPair = Curve25519.generateKeyPair() + let ephemeralSharedSecret = try Curve25519.generateSharedSecret(fromPublicKey: x25519PublicKey, privateKey: ephemeralKeyPair.privateKey) + let salt = "LOKI" + let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) + let ciphertext = try encrypt(plaintext, usingAESGCMWithSymmetricKey: Data(bytes: symmetricKey)) + return (ciphertext, Data(bytes: symmetricKey), ephemeralKeyPair.publicKey) + } +} diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUtilities.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUtilities.swift new file mode 100644 index 000000000..7a78e57b5 --- /dev/null +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/ClosedGroupUtilities.swift @@ -0,0 +1,70 @@ +import CryptoSwift +import SessionMetadataKit + +@objc(LKClosedGroupUtilities) +public final class ClosedGroupUtilities : NSObject { + private static let gcmTagSize: UInt = 16 + private static let ivSize: UInt = 12 + + @objc(LKSSKDecryptionError) + public class SSKDecryptionError : NSError { // Not called `Error` for Obj-C interoperablity + + @objc public static let invalidGroupPublicKey = SSKDecryptionError(domain: "SSKErrorDomain", code: 1, userInfo: [ NSLocalizedDescriptionKey : "Invalid group public key." ]) + @objc public static let noData = SSKDecryptionError(domain: "SSKErrorDomain", code: 2, userInfo: [ NSLocalizedDescriptionKey : "Received an empty envelope." ]) + @objc public static let noGroupPrivateKey = SSKDecryptionError(domain: "SSKErrorDomain", code: 3, userInfo: [ NSLocalizedDescriptionKey : "Missing group private key." ]) + } + + @objc(encryptData:usingGroupPublicKey:transaction:error:) + public static func encrypt(data: Data, groupPublicKey: String, transaction: YapDatabaseReadWriteTransaction) throws -> Data { + // 1. ) Encrypt the data with the user's sender key + guard let userPublicKey = OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey else { + throw SMKError.assertionError(description: "[Loki] Couldn't find user key pair.") + } + let ciphertextAndKeyIndex = try SharedSenderKeysImplementation.shared.encrypt(data, forGroupWithPublicKey: groupPublicKey, + senderPublicKey: userPublicKey, protocolContext: transaction) + let ivAndCiphertext = ciphertextAndKeyIndex[0] as! Data + let keyIndex = ciphertextAndKeyIndex[1] as! UInt + let encryptedMessage = ClosedGroupCiphertextMessage(_throws_withIVAndCiphertext: ivAndCiphertext, senderPublicKey: Data(hex: userPublicKey), keyIndex: UInt32(keyIndex)) + // 2. ) Encrypt the result for the group's public key to hide the sender public key and key index + let (ciphertext, _, ephemeralPublicKey) = try EncryptionUtilities.encrypt(encryptedMessage.serialized, using: groupPublicKey.removing05PrefixIfNeeded()) + // 3. ) Wrap the result + return try SSKProtoClosedGroupCiphertextMessageWrapper.builder(ciphertext: ciphertext, ephemeralPublicKey: ephemeralPublicKey).build().serializedData() + } + + @objc(decryptEnvelope:transaction:error:) + public static func decrypt(envelope: SSKProtoEnvelope, transaction: YapDatabaseReadWriteTransaction) throws -> [Any] { + let (plaintext, senderPublicKey) = try decrypt(envelope: envelope, transaction: transaction) + return [ plaintext, senderPublicKey ] + } + + public static func decrypt(envelope: SSKProtoEnvelope, transaction: YapDatabaseReadWriteTransaction) throws -> (plaintext: Data, senderPublicKey: String) { + // 1. ) Check preconditions + guard let groupPublicKey = envelope.source, SharedSenderKeysImplementation.shared.isClosedGroup(groupPublicKey) else { + throw SSKDecryptionError.invalidGroupPublicKey + } + guard let data = envelope.content else { + throw SSKDecryptionError.noData + } + guard let hexEncodedGroupPrivateKey = Storage.getClosedGroupPrivateKey(for: groupPublicKey) else { + throw SSKDecryptionError.noGroupPrivateKey + } + let groupPrivateKey = Data(hex: hexEncodedGroupPrivateKey) + // 2. ) Parse the wrapper + let wrapper = try SSKProtoClosedGroupCiphertextMessageWrapper.parseData(data) + let ivAndCiphertext = wrapper.ciphertext + let ephemeralPublicKey = wrapper.ephemeralPublicKey + // 3. ) Decrypt the data inside + let ephemeralSharedSecret = try Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) + let salt = "LOKI" + let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes) + let closedGroupCiphertextMessageAsData = try DecryptionUtilities.decrypt(ivAndCiphertext, usingAESGCMWithSymmetricKey: Data(symmetricKey)) + // 4. ) Parse the closed group ciphertext message + let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData) + let senderPublicKey = closedGroupCiphertextMessage.senderPublicKey.toHexString() + // 5. ) Use the info inside the closed group ciphertext message to decrypt the actual message content + let plaintext = try SharedSenderKeysImplementation.shared.decrypt(closedGroupCiphertextMessage.ivAndCiphertext, forGroupWithPublicKey: groupPublicKey, + senderPublicKey: senderPublicKey, keyIndex: UInt(closedGroupCiphertextMessage.keyIndex), protocolContext: transaction) + // 6. ) Return + return (plaintext, senderPublicKey) + } +} diff --git a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m index 9c676640e..841521299 100644 --- a/SignalServiceKit/src/Messages/OWSMessageDecrypter.m +++ b/SignalServiceKit/src/Messages/OWSMessageDecrypter.m @@ -283,7 +283,28 @@ NSError *EnsureDecryptError(NSError *_Nullable error, NSString *fallbackErrorDes // Return to avoid double-acknowledging. return; } - case SSKProtoEnvelopeTypeClosedGroupCiphertext: // Loki: Fall through + case SSKProtoEnvelopeTypeClosedGroupCiphertext: { + [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + NSError *error = nil; + NSArray *plaintextAndSenderPublicKey = [LKClosedGroupUtilities decryptEnvelope:envelope transaction:transaction error:&error]; + if (error != nil) { return failureBlock(); } + NSData *plaintext = plaintextAndSenderPublicKey[0]; + NSString *senderPublicKey = plaintextAndSenderPublicKey[1]; + SSKProtoEnvelopeBuilder *newEnvelope = [envelope asBuilder]; + [newEnvelope setSource:senderPublicKey]; + NSData *newEnvelopeAsData = [newEnvelope buildSerializedDataAndReturnError:&error]; + if (error != nil) { return failureBlock(); } + NSString *userPublicKey = [OWSIdentityManager.sharedManager.identityKeyPair hexEncodedPublicKey]; + if ([senderPublicKey isEqual:userPublicKey]) { return failureBlock(); } + OWSMessageDecryptResult *result = [OWSMessageDecryptResult resultWithEnvelopeData:newEnvelopeAsData + plaintextData:[plaintext removePadding] + source:senderPublicKey + sourceDevice:OWSDevicePrimaryDeviceId + isUDMessage:NO]; + successBlock(result, transaction); + } error:nil]; + return; + } case SSKProtoEnvelopeTypeUnidentifiedSender: { [self decryptUnidentifiedSender:envelope successBlock:^(OWSMessageDecryptResult *result, YapDatabaseReadWriteTransaction *transaction) { diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 2d980a534..f2222cad5 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -976,7 +976,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (messageSend.isUDSend) { hasValidMessageType = [messageType isEqualToNumber:@(TSUnidentifiedSenderMessageType)]; } else { - NSArray *validMessageTypes = @[ @(TSEncryptedWhisperMessageType), @(TSPreKeyWhisperMessageType), @(TSFallbackMessageType) ]; + NSArray *validMessageTypes = @[ @(TSEncryptedWhisperMessageType), @(TSPreKeyWhisperMessageType), @(TSFallbackMessageType), @(TSClosedGroupCiphertextMessageType) ]; hasValidMessageType = [validMessageTypes containsObject:messageType]; } @@ -1089,24 +1089,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } NSDictionary *signalMessageInfo = deviceMessages.firstObject; SSKProtoEnvelopeType type = ((NSNumber *)signalMessageInfo[@"type"]).integerValue; - if (isSSKBasedClosedGroup) { - type = SSKProtoEnvelopeTypeClosedGroupCiphertext; - } + uint32_t senderDeviceID = (type == SSKProtoEnvelopeTypeUnidentifiedSender) ? 0 : OWSDevicePrimaryDeviceId; + NSString *content = signalMessageInfo[@"content"]; + NSString *recipientID = signalMessageInfo[@"destination"]; + uint64_t ttl = ((NSNumber *)signalMessageInfo[@"ttl"]).unsignedIntegerValue; + BOOL isPing = ((NSNumber *)signalMessageInfo[@"isPing"]).boolValue; uint64_t timestamp = message.timestamp; NSString *senderID; - if (isSSKBasedClosedGroup) { - senderID = [LKGroupUtilities getDecodedGroupID:((TSGroupThread *)messageSend.thread).groupModel.groupId]; + if (type == SSKProtoEnvelopeTypeClosedGroupCiphertext) { + senderID = recipientID; } else if (type == SSKProtoEnvelopeTypeUnidentifiedSender) { senderID = @""; } else { senderID = userPublicKey; [LKLogger print:@"[Loki] Non-UD send"]; } - uint32_t senderDeviceID = type == SSKProtoEnvelopeTypeUnidentifiedSender ? 0 : OWSDevicePrimaryDeviceId; - NSString *content = signalMessageInfo[@"content"]; - NSString *recipientID = signalMessageInfo[@"destination"]; - uint64_t ttl = ((NSNumber *)signalMessageInfo[@"ttl"]).unsignedIntegerValue; - BOOL isPing = ((NSNumber *)signalMessageInfo[@"isPing"]).boolValue; LKSignalMessage *signalMessage = [[LKSignalMessage alloc] initWithType:type timestamp:timestamp senderID:senderID senderDeviceID:senderDeviceID content:content recipientID:recipientID ttl:ttl isPing:isPing]; [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { if (!message.skipSave) { @@ -1396,7 +1393,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (isSessionRequired) { BOOL hasSession = [self throws_ensureRecipientHasSessionForMessageSend:messageSend recipientID:recipientID deviceId:@(OWSDevicePrimaryDeviceId)]; - // Loki: Remove this when we have shared sender keys + // Loki: Remove this when shared sender keys has been widely rolled out // ======== if (!hasSession && [LKSessionManagementProtocol shouldIgnoreMissingPreKeyBundleExceptionForMessage:messageSend.message to:recipientID]) { return [NSDictionary new]; @@ -1523,7 +1520,19 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSData *_Nullable serializedMessage; TSWhisperMessageType messageType; - if (messageSend.isUDSend) { + if ([LKSharedSenderKeysImplementation.shared isClosedGroup:recipientID]) { + NSError *error; + serializedMessage = [LKClosedGroupUtilities encryptData:plainText.paddedMessageBody usingGroupPublicKey:recipientID transaction:transaction error:&error]; + + if (error != nil) { + OWSFailDebug(@"Couldn't encrypt message for SSK based closed group due to error: %@.", error); + return nil; + } + + messageType = TSClosedGroupCiphertextMessageType; + + messageSend.udAccess = nil; + } else if (messageSend.isUDSend) { NSError *error; LKSessionResetImplementation *sessionResetImplementation = [LKSessionResetImplementation new]; diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index a4d2ec66e..6b7e07e7f 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -1710,6 +1710,118 @@ extension SSKProtoCallMessage.SSKProtoCallMessageBuilder { #endif +// MARK: - SSKProtoClosedGroupCiphertextMessageWrapper + +@objc public class SSKProtoClosedGroupCiphertextMessageWrapper: NSObject { + + // MARK: - SSKProtoClosedGroupCiphertextMessageWrapperBuilder + + @objc public class func builder(ciphertext: Data, ephemeralPublicKey: Data) -> SSKProtoClosedGroupCiphertextMessageWrapperBuilder { + return SSKProtoClosedGroupCiphertextMessageWrapperBuilder(ciphertext: ciphertext, ephemeralPublicKey: ephemeralPublicKey) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SSKProtoClosedGroupCiphertextMessageWrapperBuilder { + let builder = SSKProtoClosedGroupCiphertextMessageWrapperBuilder(ciphertext: ciphertext, ephemeralPublicKey: ephemeralPublicKey) + return builder + } + + @objc public class SSKProtoClosedGroupCiphertextMessageWrapperBuilder: NSObject { + + private var proto = SignalServiceProtos_ClosedGroupCiphertextMessageWrapper() + + @objc fileprivate override init() {} + + @objc fileprivate init(ciphertext: Data, ephemeralPublicKey: Data) { + super.init() + + setCiphertext(ciphertext) + setEphemeralPublicKey(ephemeralPublicKey) + } + + @objc public func setCiphertext(_ valueParam: Data) { + proto.ciphertext = valueParam + } + + @objc public func setEphemeralPublicKey(_ valueParam: Data) { + proto.ephemeralPublicKey = valueParam + } + + @objc public func build() throws -> SSKProtoClosedGroupCiphertextMessageWrapper { + return try SSKProtoClosedGroupCiphertextMessageWrapper.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SSKProtoClosedGroupCiphertextMessageWrapper.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SignalServiceProtos_ClosedGroupCiphertextMessageWrapper + + @objc public let ciphertext: Data + + @objc public let ephemeralPublicKey: Data + + private init(proto: SignalServiceProtos_ClosedGroupCiphertextMessageWrapper, + ciphertext: Data, + ephemeralPublicKey: Data) { + self.proto = proto + self.ciphertext = ciphertext + self.ephemeralPublicKey = ephemeralPublicKey + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SSKProtoClosedGroupCiphertextMessageWrapper { + let proto = try SignalServiceProtos_ClosedGroupCiphertextMessageWrapper(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SignalServiceProtos_ClosedGroupCiphertextMessageWrapper) throws -> SSKProtoClosedGroupCiphertextMessageWrapper { + guard proto.hasCiphertext else { + throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: ciphertext") + } + let ciphertext = proto.ciphertext + + guard proto.hasEphemeralPublicKey else { + throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: ephemeralPublicKey") + } + let ephemeralPublicKey = proto.ephemeralPublicKey + + // MARK: - Begin Validation Logic for SSKProtoClosedGroupCiphertextMessageWrapper - + + // MARK: - End Validation Logic for SSKProtoClosedGroupCiphertextMessageWrapper - + + let result = SSKProtoClosedGroupCiphertextMessageWrapper(proto: proto, + ciphertext: ciphertext, + ephemeralPublicKey: ephemeralPublicKey) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SSKProtoClosedGroupCiphertextMessageWrapper { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SSKProtoClosedGroupCiphertextMessageWrapper.SSKProtoClosedGroupCiphertextMessageWrapperBuilder { + @objc public func buildIgnoringErrors() -> SSKProtoClosedGroupCiphertextMessageWrapper? { + return try! self.build() + } +} + +#endif + // MARK: - SSKProtoDataMessageQuoteQuotedAttachment @objc public class SSKProtoDataMessageQuoteQuotedAttachment: NSObject { diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index a78457606..ac386523f 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -729,6 +729,39 @@ struct SignalServiceProtos_CallMessage { fileprivate var _profileKey: Data? = nil } +struct SignalServiceProtos_ClosedGroupCiphertextMessageWrapper { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var ciphertext: Data { + get {return _ciphertext ?? SwiftProtobuf.Internal.emptyData} + set {_ciphertext = newValue} + } + /// Returns true if `ciphertext` has been explicitly set. + var hasCiphertext: Bool {return self._ciphertext != nil} + /// Clears the value of `ciphertext`. Subsequent reads from it will return its default value. + mutating func clearCiphertext() {self._ciphertext = nil} + + /// @required + var ephemeralPublicKey: Data { + get {return _ephemeralPublicKey ?? SwiftProtobuf.Internal.emptyData} + set {_ephemeralPublicKey = newValue} + } + /// Returns true if `ephemeralPublicKey` has been explicitly set. + var hasEphemeralPublicKey: Bool {return self._ephemeralPublicKey != nil} + /// Clears the value of `ephemeralPublicKey`. Subsequent reads from it will return its default value. + mutating func clearEphemeralPublicKey() {self._ephemeralPublicKey = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _ciphertext: Data? = nil + fileprivate var _ephemeralPublicKey: Data? = nil +} + struct SignalServiceProtos_DataMessage { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -3398,6 +3431,41 @@ extension SignalServiceProtos_CallMessage.Hangup: SwiftProtobuf.Message, SwiftPr } } +extension SignalServiceProtos_ClosedGroupCiphertextMessageWrapper: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".ClosedGroupCiphertextMessageWrapper" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "ciphertext"), + 2: .same(proto: "ephemeralPublicKey"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularBytesField(value: &self._ciphertext) + case 2: try decoder.decodeSingularBytesField(value: &self._ephemeralPublicKey) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._ciphertext { + try visitor.visitSingularBytesField(value: v, fieldNumber: 1) + } + if let v = self._ephemeralPublicKey { + try visitor.visitSingularBytesField(value: v, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SignalServiceProtos_ClosedGroupCiphertextMessageWrapper, rhs: SignalServiceProtos_ClosedGroupCiphertextMessageWrapper) -> Bool { + if lhs._ciphertext != rhs._ciphertext {return false} + if lhs._ephemeralPublicKey != rhs._ephemeralPublicKey {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".DataMessage" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ diff --git a/SignalServiceKit/src/TSConstants.h b/SignalServiceKit/src/TSConstants.h index 61844b945..8b7629190 100644 --- a/SignalServiceKit/src/TSConstants.h +++ b/SignalServiceKit/src/TSConstants.h @@ -14,6 +14,7 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) { TSPreKeyWhisperMessageType = 3, TSUnencryptedWhisperMessageType = 4, TSUnidentifiedSenderMessageType = 6, + TSClosedGroupCiphertextMessageType = 7, TSFallbackMessageType = 101 // Loki: Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request. };