[WIP] Updated to the latest libSession and started plugging in

Updated the updated group messages encryption/decryption logic to work with the latest libSession
Disabled manual PN triggering for updated group messages
This commit is contained in:
Morgan Pretty 2023-09-26 17:35:19 +10:00
parent 4f10277a48
commit a48327f6f6
7 changed files with 86 additions and 16 deletions

@ -1 +1 @@
Subproject commit e21302b598b5dde44fd72566d8b89d4d41cbb9ce
Subproject commit 916a47fcd347332256b744457362639c405aebef

View File

@ -21,6 +21,9 @@ public extension Message {
public var defaultNamespace: SnodeAPI.Namespace? {
switch self {
case .contact: return .`default`
case .closedGroup(let key) where SessionId.Prefix(from: key) == .group:
return .groupMessages
case .closedGroup: return .legacyClosedGroup
default: return nil
}

View File

@ -36,7 +36,7 @@ public enum MessageReceiver {
)
case (_, .community(let openGroupId, let messageSender, let timestamp, let messageServerId)):
plaintext = data
plaintext = data.removePadding() // Remove the padding
sender = messageSender
sentTimestamp = UInt64(floor(timestamp * 1000)) // Convert to ms for database consistency
openGroupServerMessageId = UInt64(messageServerId)
@ -62,6 +62,7 @@ public enum MessageReceiver {
using: dependencies
)
plaintext = plaintext.removePadding() // Remove the padding
sentTimestamp = UInt64(floor(timestamp * 1000)) // Convert to ms for database consistency
openGroupServerMessageId = UInt64(messageServerId)
threadVariant = .contact
@ -85,6 +86,7 @@ public enum MessageReceiver {
ciphertext: ciphertext,
using: userX25519KeyPair
)
plaintext = plaintext.removePadding() // Remove the padding
sentTimestamp = envelope.timestamp
openGroupServerMessageId = nil
threadVariant = .contact
@ -105,13 +107,16 @@ public enum MessageReceiver {
)
guard
let envelope: SNProtoEnvelope = try? MessageWrapper.unwrap(data: plaintextEnvelope),
let envelope: SNProtoEnvelope = try? MessageWrapper.unwrap(
data: plaintextEnvelope,
includesWebSocketMessage: false
),
let envelopeContent: Data = envelope.content
else {
SNLog("Failed to unwrap data for message from 'default' namespace.")
throw MessageReceiverError.invalidMessage
}
plaintext = envelopeContent
plaintext = envelopeContent // Padding already removed for updated groups
sentTimestamp = envelope.timestamp
openGroupServerMessageId = nil
threadVariant = .group
@ -158,6 +163,7 @@ public enum MessageReceiver {
}
(plaintext, sender) = try decrypt(keyPairs: encryptionKeyPairs)
plaintext = plaintext.removePadding() // Remove the padding
sentTimestamp = envelope.timestamp
openGroupServerMessageId = nil
threadVariant = .legacyGroup
@ -175,7 +181,7 @@ public enum MessageReceiver {
}
}
let proto: SNProtoContent = try Result(SNProtoContent.parseData(plaintext.removePadding()))
let proto: SNProtoContent = try Result(SNProtoContent.parseData(plaintext))
.onFailure { SNLog("Couldn't parse proto due to error: \($0).") }
.successOrThrow()
let message: Message = try Message.createMessageFrom(proto, sender: sender)

View File

@ -179,11 +179,6 @@ public final class MessageSender {
.successOrThrow()
.base64EncodedString()
// Config messages should be sent directly rather than via this method
case (.contact, _): throw MessageSenderError.invalidConfigMessageHandling
case (.closedGroup(let groupPublicKey), _) where SessionId.Prefix(from: groupPublicKey) == .group:
throw MessageSenderError.invalidConfigMessageHandling
// Updated group messages should be wrapped _before_ encrypting
case (.closedGroup(let groupPublicKey), .groupMessages) where SessionId.Prefix(from: groupPublicKey) == .group:
return try SessionUtil
@ -204,6 +199,11 @@ public final class MessageSender {
)
.base64EncodedString()
// Config messages should be sent directly rather than via this method
case (.contact, _): throw MessageSenderError.invalidConfigMessageHandling
case (.closedGroup(let groupPublicKey), _) where SessionId.Prefix(from: groupPublicKey) == .group:
throw MessageSenderError.invalidConfigMessageHandling
// Legacy groups used a `05` prefix
case (.closedGroup(let groupPublicKey), _):
guard let encryptionKeyPair: ClosedGroupKeyPair = try? ClosedGroupKeyPair.fetchLatestKeyPair(db, threadId: groupPublicKey) else {
@ -262,6 +262,13 @@ public final class MessageSender {
details: NotifyPushServerJob.Details(message: snodeMessage)
)
let shouldNotify: Bool = {
// New groups only run via the updated push server so don't notify
switch destination {
case .closedGroup(let key) where SessionId.Prefix(from: key) == .group:
return false
default: break
}
switch updatedMessage {
case is VisibleMessage, is UnsendRequest: return !isSyncMessage
case let callMessage as CallMessage:

View File

@ -327,23 +327,31 @@ internal extension SessionUtil {
guard case .groupKeys(let conf, _, _) = config else { throw SessionUtilError.invalidConfigObject }
var ciphertext: [UInt8] = Array(ciphertext)
var cSessionId: [CChar] = [CChar](repeating: 0, count: 67)
var maybePlaintext: UnsafeMutablePointer<UInt8>? = nil
var plaintextLen: Int = 0
groups_keys_decrypt_message(
let didDecrypt: Bool = groups_keys_decrypt_message(
conf,
&ciphertext,
ciphertext.count,
&cSessionId,
&maybePlaintext,
&plaintextLen
)
// If we got a reported failure then just stop here
guard didDecrypt else { throw MessageReceiverError.decryptionFailed }
// We need to manually free 'maybePlaintext' upon a successful decryption
defer { maybePlaintext?.deallocate() }
guard
plaintextLen > 0,
let plaintext: Data = maybePlaintext
.map({ Data(bytes: $0, count: plaintextLen) })
else { throw MessageReceiverError.decryptionFailed }
return (plaintext, "")
return (plaintext, String(cString: cSessionId))
} ?? { throw MessageReceiverError.decryptionFailed }()
}
}

View File

@ -80,11 +80,18 @@ public enum MessageWrapper {
}
/// - Note: `data` shouldn't be base 64 encoded.
public static func unwrap(data: Data) throws -> SNProtoEnvelope {
public static func unwrap(
data: Data,
includesWebSocketMessage: Bool = true
) throws -> SNProtoEnvelope {
do {
let webSocketMessage = try WebSocketProtoWebSocketMessage.parseData(data)
let envelope = webSocketMessage.request!.body!
return try SNProtoEnvelope.parseData(envelope)
let envelopeData: Data = try {
guard includesWebSocketMessage else { return data }
let webSocketMessage = try WebSocketProtoWebSocketMessage.parseData(data)
return webSocketMessage.request!.body!
}()
return try SNProtoEnvelope.parseData(envelopeData)
} catch let error {
SNLog("Failed to unwrap data: \(error).")
throw Error.failedToUnwrapData

View File

@ -1805,6 +1805,20 @@ fileprivate extension LibSessionSpec {
// MARK: - GROUP_INFO
fileprivate extension LibSessionSpec {
/// This function can be used to regenerate the hard-coded `keysDump` value if needed due to `libSession` changes
/// resulting in the dump changing
private static func generateKeysDump(for keysConf: UnsafeMutablePointer<config_group_keys>?) throws -> String {
var dumpResult: UnsafeMutablePointer<UInt8>? = nil
var dumpResultLen: Int = 0
try CExceptionHelper.performSafely {
groups_keys_dump(keysConf, &dumpResult, &dumpResultLen)
}
let dumpData: Data = Data(bytes: dumpResult!, count: dumpResultLen)
dumpResult?.deallocate()
return dumpData.toHexString()
}
class func groupInfoSpec() {
context("GROUP_INFO") {
// MARK: -- generates config correctly
@ -1814,8 +1828,24 @@ fileprivate extension LibSessionSpec {
hex: "0123456789abcdef0123456789abcdeffedcba9876543210fedcba9876543210"
)
// Since we can't test the group without encryption keys and the C API doesn't have
// a way to manually provide encryption keys we needed to create a dump with valid
// key data and load that in so we can test the other cases, this dump contains a
// single admin member and a single encryption key
let keysDump: Data = Data(hex: "64363a6163746976656c65343a6b6579736c6565")
let cachedKeysDump: (data: UnsafePointer<UInt8>, length: Int)? = keysDump.withUnsafeBytes { unsafeBytes in
return unsafeBytes.baseAddress.map {
(
$0.assumingMemoryBound(to: UInt8.self),
unsafeBytes.count
)
}
}
// FIXME: Would be good to move these into the libSession-util instead of using Sodium separately
let identity = try! Identity.generate(from: userSeed)
let keyPair: KeyPair = Crypto().generate(.ed25519KeyPair(seed: seed))!
var userEdSK: [UInt8] = identity.ed25519KeyPair.secretKey
var edPK: [UInt8] = keyPair.publicKey
var edSK: [UInt8] = keyPair.secretKey
@ -1828,9 +1858,18 @@ fileprivate extension LibSessionSpec {
var conf: UnsafeMutablePointer<config_object>? = nil
expect(groups_info_init(&conf, &edPK, &edSK, nil, 0, &error)).to(equal(0))
var membersConf: UnsafeMutablePointer<config_object>? = nil
expect(groups_members_init(&membersConf, &edPK, &edSK, nil, 0, &error)).to(equal(0))
var keysConf1: UnsafeMutablePointer<config_group_keys>? = nil
expect(groups_keys_init(&keysConf1, &userEdSK, &edPK, &edSK, conf, membersConf, cachedKeysDump?.data, (cachedKeysDump?.length ?? 0), &error)).to(equal(0))
var conf2: UnsafeMutablePointer<config_object>? = nil
expect(groups_info_init(&conf2, &edPK, &edSK, nil, 0, &error)).to(equal(0))
var keysConf2: UnsafeMutablePointer<config_group_keys>? = nil
expect(groups_keys_init(&keysConf2, &userEdSK, &edPK, &edSK, conf2, membersConf, cachedKeysDump?.data, (cachedKeysDump?.length ?? 0), &error)).to(equal(0))
expect(groups_info_set_name(conf, "GROUP Name")).to(equal(0))
expect(config_needs_push(conf)).to(beTrue())
expect(config_needs_dump(conf)).to(beTrue())