Implement SSK protocol changes
This commit is contained in:
parent
c9cc1ee64e
commit
c91bc8b7f3
2
Pods
2
Pods
|
@ -1 +1 @@
|
|||
Subproject commit c89ab147afd32d3dde1e249070dd544abcf9c1c5
|
||||
Subproject commit 688635bfffb46fddf0170e2e611aee261474a67d
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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.
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue