Implement SSK protocol changes

This commit is contained in:
nielsandriesse 2020-09-14 15:16:51 +10:00
parent c9cc1ee64e
commit c91bc8b7f3
13 changed files with 365 additions and 53 deletions

2
Pods

@ -1 +1 @@
Subproject commit c89ab147afd32d3dde1e249070dd544abcf9c1c5
Subproject commit 688635bfffb46fddf0170e2e611aee261474a67d

View File

@ -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;

View File

@ -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<EncryptionResult> {
@ -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)

View File

@ -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..<Int(ivSize)]
let ciphertext = ivAndCiphertext[Int(ivSize)...]
let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= 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

View File

@ -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..<Int(EncryptionUtilities.ivSize)]
let ciphertext = ivAndCiphertext[Int(EncryptionUtilities.ivSize)...]
let gcm = GCM(iv: iv.bytes, tagLength: Int(EncryptionUtilities.gcmTagSize), mode: .combined)
let aes = try AES(key: symmetricKey.bytes, blockMode: gcm, padding: .noPadding)
return Data(try aes.decrypt(ciphertext.bytes))
}
}

View File

@ -0,0 +1,38 @@
import CryptoSwift
internal typealias EncryptionResult = (ciphertext: Data, symmetricKey: Data, ephemeralPublicKey: Data)
enum EncryptionUtilities {
internal static let gcmTagSize: UInt = 16
internal static let ivSize: UInt = 12
/// - Note: Sync. Don't call from the main thread.
internal static func encrypt(_ plaintext: Data, usingAESGCMWithSymmetricKey symmetricKey: Data) throws -> 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)
}
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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];

View File

@ -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 {

View File

@ -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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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 = [

View File

@ -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.
};