diff --git a/Session/Closed Groups/NewClosedGroupVC.swift b/Session/Closed Groups/NewClosedGroupVC.swift index c71579e3b..b864a1bdd 100644 --- a/Session/Closed Groups/NewClosedGroupVC.swift +++ b/Session/Closed Groups/NewClosedGroupVC.swift @@ -332,7 +332,7 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate ModalActivityIndicatorViewController.present(fromViewController: navigationController!) { [weak self] _ in MessageSender // .createLegacyClosedGroup(name: name, members: selectedProfiles.map { $0.0 }.asSet()) - .createGroup(name: name, description: nil, displayPicture: nil, members: selectedProfiles) + .createGroup(name: name, description: nil, displayPictureData: nil, members: selectedProfiles) .subscribe(on: DispatchQueue.global(qos: .userInitiated)) .receive(on: DispatchQueue.main) .sinkUntilComplete( diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+Groups.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+Groups.swift index 58bb18aa9..3c1f441df 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+Groups.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+Groups.swift @@ -20,34 +20,42 @@ extension MessageSender { public static func createGroup( name: String, description: String?, - displayPicture: SignalAttachment?, + displayPictureData: Data?, members: [(String, Profile?)], using dependencies: Dependencies = Dependencies() ) -> AnyPublisher { + typealias ImageUploadResponse = (downloadUrl: String, fileName: String, encryptionKey: Data) + return Just(()) .setFailureType(to: Error.self) - .flatMap { _ -> AnyPublisher<(url: String, filename: String, encryptionKey: Data)?, Error> in - guard let displayPicture: SignalAttachment = displayPicture else { + .flatMap { _ -> AnyPublisher in + guard let displayPictureData: Data = displayPictureData else { return Just(nil) .setFailureType(to: Error.self) .eraseToAnyPublisher() } - // TODO: Upload group image first - return Just(nil) - .setFailureType(to: Error.self) - .eraseToAnyPublisher() + + return Deferred { + Future { resolver in + DisplayPictureManager.prepareAndUploadDisplayPicture( + queue: DispatchQueue.global(qos: .userInitiated), + imageData: displayPictureData, + success: { resolver(Result.success($0)) }, + failure: { resolver(Result.failure($0)) }, + using: dependencies + ) + } + }.eraseToAnyPublisher() } .flatMap { displayPictureInfo -> AnyPublisher in dependencies[singleton: .storage].writePublisher(using: dependencies) { db -> PreparedGroupData in // Create and cache the libSession entries - let userSessionId: SessionId = getUserSessionId(db, using: dependencies) - let currentUserProfile: Profile = Profile.fetchOrCreateCurrentUser(db, using: dependencies) let createdInfo: SessionUtil.CreatedGroupInfo = try SessionUtil.createGroup( db, name: name, description: description, - displayPictureUrl: displayPictureInfo?.url, - displayPictureFilename: displayPictureInfo?.filename, + displayPictureUrl: displayPictureInfo?.downloadUrl, + displayPictureFilename: displayPictureInfo?.fileName, displayPictureEncryptionKey: displayPictureInfo?.encryptionKey, members: members, using: dependencies @@ -402,9 +410,14 @@ extension MessageSender { } /// Generate the data needed to send the new members invitations to the group - let memberJobData: [(id: String, profile: Profile?, jobDetails: GroupInviteMemberJob.Details)] = try members + let memberJobData: [(id: String, profile: Profile?, jobDetails: GroupInviteMemberJob.Details, subaccountToken: [UInt8])] = try members .map { id, profile in // Generate authData for the newly added member + let subaccountToken: [UInt8] = try SessionUtil.generateSubaccountToken( + groupSessionId: sessionId, + memberId: id, + using: dependencies + ) let memberAuthInfo: Authentication.Info = try SessionUtil.generateAuthData( groupSessionId: sessionId, memberId: id, @@ -415,7 +428,7 @@ extension MessageSender { authInfo: memberAuthInfo ) - return (id, profile, inviteDetails) + return (id, profile, inviteDetails, subaccountToken) } /// Unrevoke the newly added members just in case they had previously gotten their access to the group @@ -427,9 +440,9 @@ extension MessageSender { try? SnodeAPI .preparedBatch( db, - requests: try memberJobDataChunk.map { id, _, jobDetails in + requests: try memberJobDataChunk.map { id, _, _, subaccountToken in try SnodeAPI.preparedUnrevokeSubaccount( - subaccountToUnrevoke: jobDetails.memberAuthData.toHexString(), + subaccountToUnrevoke: subaccountToken, authMethod: Authentication.groupAdmin( groupSessionId: sessionId, ed25519SecretKey: Array(groupIdentityPrivateKey) @@ -447,7 +460,7 @@ extension MessageSender { } /// Make the required changes for each added member - try memberJobData.forEach { id, profile, inviteJobDetails in + try memberJobData.forEach { id, profile, inviteJobDetails, _ in /// Add the member to the database try GroupMember( groupId: sessionId.hexString, @@ -518,6 +531,11 @@ extension MessageSender { .fetchOne(db) else { throw MessageSenderError.invalidClosedGroupUpdate } + let subaccountToken: [UInt8] = try SessionUtil.generateSubaccountToken( + groupSessionId: sessionId, + memberId: memberId, + using: dependencies + ) let inviteDetails: GroupInviteMemberJob.Details = try GroupInviteMemberJob.Details( memberSessionIdHexString: memberId, authInfo: try SessionUtil.generateAuthData( @@ -531,7 +549,7 @@ extension MessageSender { /// unrevoke request when initially added them failed (fire-and-forget this request, we don't want it to be blocking) try SnodeAPI .preparedUnrevokeSubaccount( - subaccountToUnrevoke: inviteDetails.memberAuthData.toHexString(), + subaccountToUnrevoke: subaccountToken, authMethod: Authentication.groupAdmin( groupSessionId: sessionId, ed25519SecretKey: Array(groupIdentityPrivateKey) @@ -658,16 +676,15 @@ extension MessageSender { requests: memberIdsChunk.compactMap { id -> HTTP.PreparedRequest? in // Generate authData for the removed member guard - let memberAuthInfo: Authentication.Info = try? SessionUtil.generateAuthData( + let subaccountToken: [UInt8] = try? SessionUtil.generateSubaccountToken( groupSessionId: groupSessionId, memberId: id, using: dependencies - ), - case .groupMember(_, let memberAuthData) = memberAuthInfo + ) else { return nil } return try? SnodeAPI.preparedRevokeSubaccount( - subaccountToRevoke: memberAuthData.toHexString(), + subaccountToRevoke: subaccountToken, authMethod: Authentication.groupAdmin( groupSessionId: groupSessionId, ed25519SecretKey: Array(groupIdentityPrivateKey) diff --git a/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+GroupKeys.swift b/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+GroupKeys.swift index 5b2cb8e6a..49f66ada9 100644 --- a/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+GroupKeys.swift +++ b/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+GroupKeys.swift @@ -91,6 +91,22 @@ internal extension SessionUtil { } ?? { throw SessionUtilError.invalidConfigObject }() } + static func generateSubaccountToken( + groupSessionId: SessionId, + memberId: String, + using dependencies: Dependencies + ) throws -> [UInt8] { + try dependencies[singleton: .crypto].perform( + .subaccountToken( + config: dependencies[cache: .sessionUtil] + .config(for: .groupKeys, sessionId: groupSessionId) + .wrappedValue, + groupSessionId: groupSessionId, + memberId: memberId + ) + ) + } + static func generateAuthData( groupSessionId: SessionId, memberId: String, diff --git a/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+UserGroups.swift b/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+UserGroups.swift index 8d09ce1bd..f27719333 100644 --- a/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+UserGroups.swift +++ b/SessionMessagingKit/SessionUtil/Config Handling/SessionUtil+UserGroups.swift @@ -1238,7 +1238,7 @@ extension SessionUtil { ) \(lastKeyPair, asSubquery: true) ON \(lastKeyPair[.threadId]) = \(closedGroup[.threadId]) LEFT JOIN \(disappearingConfig) ON \(disappearingConfig[.threadId]) = \(closedGroup[.threadId]) - WHERE \(SQL("\(closedGroup[.threadId]) LIKE '\(SessionId.Prefix.standard)%'")) + WHERE \(closedGroup[.threadId]) LIKE '\(SessionId.Prefix.standard)%' """ let legacyGroupInfoNoMembers: [LegacyGroupInfo] = try request diff --git a/SessionMessagingKit/SessionUtil/Utilities/Crypto+SessionUtil.swift b/SessionMessagingKit/SessionUtil/Utilities/Crypto+SessionUtil.swift index c9d746cb4..85de36069 100644 --- a/SessionMessagingKit/SessionUtil/Utilities/Crypto+SessionUtil.swift +++ b/SessionMessagingKit/SessionUtil/Utilities/Crypto+SessionUtil.swift @@ -4,6 +4,32 @@ import Foundation import SessionUtil import SessionUtilitiesKit +public extension Crypto.Action { + static func subaccountToken( + config: SessionUtil.Config?, + groupSessionId: SessionId, + memberId: String + ) -> Crypto.Action { + return Crypto.Action( + id: "subaccountToken", + args: [config, groupSessionId, memberId] + ) { + guard case .groupKeys(let conf, _, _) = config else { throw SessionUtilError.invalidConfigObject } + + var cMemberId: [CChar] = memberId.cArray + var tokenData: [UInt8] = [UInt8](repeating: 0, count: SessionUtil.sizeSubaccountBytes) + + guard groups_keys_swarm_subaccount_token( + conf, + &cMemberId, + &tokenData + ) else { throw SessionUtilError.failedToMakeSubAccountInGroup } + + return tokenData + } + } +} + public extension Crypto.AuthenticationInfo { static func memberAuthData( config: SessionUtil.Config?, diff --git a/SessionMessagingKit/Utilities/DisplayPictureManager.swift b/SessionMessagingKit/Utilities/DisplayPictureManager.swift index 78e7f85b4..37393ccac 100644 --- a/SessionMessagingKit/Utilities/DisplayPictureManager.swift +++ b/SessionMessagingKit/Utilities/DisplayPictureManager.swift @@ -244,14 +244,14 @@ public struct DisplayPictureManager { public static func prepareAndUploadDisplayPicture( queue: DispatchQueue, imageData: Data, - success: @escaping ((downloadUrl: String, fileName: String, profileKey: Data)) -> (), + success: @escaping ((downloadUrl: String, fileName: String, encryptionKey: Data)) -> (), failure: ((DisplayPictureError) -> ())? = nil, using dependencies: Dependencies ) { queue.async { // If the profile avatar was updated or removed then encrypt with a new profile key // to ensure that other users know that our profile picture was updated - let newProfileKey: Data + let newEncryptionKey: Data let finalImageData: Data let fileExtension: String @@ -306,7 +306,9 @@ public struct DisplayPictureManager { return data }() - newProfileKey = try Randomness.generateRandomBytes(numberBytes: DisplayPictureManager.aes256KeyByteLength) + newEncryptionKey = try Randomness.generateRandomBytes( + numberBytes: DisplayPictureManager.aes256KeyByteLength + ) fileExtension = { switch guessedFormat { case .gif: return "gif" // stringlint:disable @@ -338,7 +340,7 @@ public struct DisplayPictureManager { } // Encrypt the avatar for upload - guard let encryptedData: Data = DisplayPictureManager.encryptData(data: finalImageData, key: newProfileKey) else { + guard let encryptedData: Data = DisplayPictureManager.encryptData(data: finalImageData, key: newEncryptionKey) else { SNLog("Updating service with profile failed.") failure?(.encryptionFailed) return @@ -373,7 +375,7 @@ public struct DisplayPictureManager { dependencies.mutate(cache: .displayPicture) { $0.imageData[fileName] = finalImageData } SNLog("Successfully uploaded avatar image.") - success((downloadUrl, fileName, newProfileKey)) + success((downloadUrl, fileName, newEncryptionKey)) } ) } diff --git a/SessionMessagingKitTests/Sending & Receiving/MessageSenderGroupsSpec.swift b/SessionMessagingKitTests/Sending & Receiving/MessageSenderGroupsSpec.swift index 502d53589..5ea2685f4 100644 --- a/SessionMessagingKitTests/Sending & Receiving/MessageSenderGroupsSpec.swift +++ b/SessionMessagingKitTests/Sending & Receiving/MessageSenderGroupsSpec.swift @@ -172,7 +172,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -199,7 +199,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "Test", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -226,7 +226,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -253,7 +253,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -286,7 +286,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -361,7 +361,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -395,7 +395,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -413,7 +413,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], @@ -442,7 +442,7 @@ class MessageSenderGroupsSpec: QuickSpec { .createGroup( name: "TestGroupName", description: nil, - displayPicture: nil, + displayPictureData: nil, members: [ ("051111111111111111111111111111111111111111111111111111111111111111", nil) ], diff --git a/SessionSnodeKit/Models/RevokeSubaccountRequest.swift b/SessionSnodeKit/Models/RevokeSubaccountRequest.swift index 3a7bb2af1..c8be2ce10 100644 --- a/SessionSnodeKit/Models/RevokeSubaccountRequest.swift +++ b/SessionSnodeKit/Models/RevokeSubaccountRequest.swift @@ -9,24 +9,29 @@ extension SnodeAPI { case subaccountToRevoke = "revoke" } - let subaccountToRevoke: String + let subaccountToRevoke: [UInt8] override var verificationBytes: [UInt8] { - /// Ed25519 signature of `("revoke_subaccount" || subaccount)`; this signs the subkey tag, + /// Ed25519 signature of `("revoke_subaccount" || timestamp || SUBACCOUNT_TAG_BYTES)`; this signs the subkey tag, /// using `pubkey` to sign. Must be base64 encoded for json requests; binary for OMQ requests. SnodeAPI.Endpoint.revokeSubaccount.path.bytes - .appending(contentsOf: subaccountToRevoke.bytes) + .appending(contentsOf: timestampMs.map { "\($0)" }?.data(using: .ascii)?.bytes) + .appending(contentsOf: subaccountToRevoke) } // MARK: - Init public init( - subaccountToRevoke: String, - authMethod: AuthenticationMethod + subaccountToRevoke: [UInt8], + authMethod: AuthenticationMethod, + timestampMs: UInt64 ) { self.subaccountToRevoke = subaccountToRevoke - super.init(authMethod: authMethod) + super.init( + authMethod: authMethod, + timestampMs: timestampMs + ) } // MARK: - Coding @@ -34,7 +39,8 @@ extension SnodeAPI { override public func encode(to encoder: Encoder) throws { var container: KeyedEncodingContainer = encoder.container(keyedBy: CodingKeys.self) - try container.encode(subaccountToRevoke, forKey: .subaccountToRevoke) + /// The `subaccountToRevoke` should be sent as a hex string + try container.encode(subaccountToRevoke.toHexString(), forKey: .subaccountToRevoke) try super.encode(to: encoder) } diff --git a/SessionSnodeKit/Models/RevokeSubaccountResponse.swift b/SessionSnodeKit/Models/RevokeSubaccountResponse.swift index 0bdb034c4..b734d8996 100644 --- a/SessionSnodeKit/Models/RevokeSubaccountResponse.swift +++ b/SessionSnodeKit/Models/RevokeSubaccountResponse.swift @@ -9,7 +9,7 @@ public class RevokeSubaccountResponse: SnodeRecursiveResponse {} // MARK: - ValidatableResponse extension RevokeSubaccountResponse: ValidatableResponse { - typealias ValidationData = String + typealias ValidationData = (subaccountToRevoke: [UInt8], timestampMs: UInt64) typealias ValidationResponse = Bool /// All responses in the swarm must be valid @@ -17,7 +17,7 @@ extension RevokeSubaccountResponse: ValidatableResponse { internal func validResultMap( publicKey: String, - validationData: String, + validationData: (subaccountToRevoke: [UInt8], timestampMs: UInt64), using dependencies: Dependencies ) throws -> [String: Bool] { let validationMap: [String: Bool] = try swarm.reduce(into: [:]) { result, next in @@ -35,10 +35,11 @@ extension RevokeSubaccountResponse: ValidatableResponse { return } - /// Signature of `( PUBKEY_HEX || SUBACCOUNT_TAG_BYTES )` where `SUBACCOUNT_TAG_BYTES` is the + /// Signature of `( PUBKEY_HEX || timestamp || SUBACCOUNT_TAG_BYTES )` where `SUBACCOUNT_TAG_BYTES` is the /// requested subkey tag for revocation let verificationBytes: [UInt8] = publicKey.bytes - .appending(contentsOf: validationData.bytes) + .appending(contentsOf: "\(validationData.timestampMs)".data(using: .ascii)?.bytes) + .appending(contentsOf: validationData.subaccountToRevoke) let isValid: Bool = dependencies[singleton: .crypto].verify( .signature( diff --git a/SessionSnodeKit/Models/SendMessageResponse.swift b/SessionSnodeKit/Models/SendMessageResponse.swift index 14b42cceb..7425cdb07 100644 --- a/SessionSnodeKit/Models/SendMessageResponse.swift +++ b/SessionSnodeKit/Models/SendMessageResponse.swift @@ -102,7 +102,6 @@ extension SendMessagesResponse: ValidatableResponse { /// Signature of `hash` signed by the node's ed25519 pubkey let verificationBytes: [UInt8] = hash.bytes - result[next.key] = dependencies[singleton: .crypto].verify( .signature( message: verificationBytes, diff --git a/SessionSnodeKit/Models/UnrevokeSubaccountRequest.swift b/SessionSnodeKit/Models/UnrevokeSubaccountRequest.swift index 26a6b1dba..c72939510 100644 --- a/SessionSnodeKit/Models/UnrevokeSubaccountRequest.swift +++ b/SessionSnodeKit/Models/UnrevokeSubaccountRequest.swift @@ -9,24 +9,29 @@ extension SnodeAPI { case subaccountToUnrevoke = "unrevoke" } - let subaccountToUnrevoke: String + let subaccountToUnrevoke: [UInt8] override var verificationBytes: [UInt8] { - /// Ed25519 signature of `("unrevoke_subaccount" || subaccount)`; this signs the subkey tag, + /// Ed25519 signature of `("unrevoke_subaccount" || timestamp || subaccount)`; this signs the subkey tag, /// using `pubkey` to sign. Must be base64 encoded for json requests; binary for OMQ requests. SnodeAPI.Endpoint.unrevokeSubaccount.path.bytes - .appending(contentsOf: subaccountToUnrevoke.bytes) + .appending(contentsOf: timestampMs.map { "\($0)" }?.data(using: .ascii)?.bytes) + .appending(contentsOf: subaccountToUnrevoke) } // MARK: - Init public init( - subaccountToUnrevoke: String, - authMethod: AuthenticationMethod + subaccountToUnrevoke: [UInt8], + authMethod: AuthenticationMethod, + timestampMs: UInt64 ) { self.subaccountToUnrevoke = subaccountToUnrevoke - super.init(authMethod: authMethod) + super.init( + authMethod: authMethod, + timestampMs: timestampMs + ) } // MARK: - Coding @@ -34,7 +39,8 @@ extension SnodeAPI { override public func encode(to encoder: Encoder) throws { var container: KeyedEncodingContainer = encoder.container(keyedBy: CodingKeys.self) - try container.encode(subaccountToUnrevoke, forKey: .subaccountToUnrevoke) + /// The `subaccountToRevoke` should be sent as a hex string + try container.encode(subaccountToUnrevoke.toHexString(), forKey: .subaccountToUnrevoke) try super.encode(to: encoder) } diff --git a/SessionSnodeKit/Models/UnrevokeSubaccountResponse.swift b/SessionSnodeKit/Models/UnrevokeSubaccountResponse.swift index 6280705b1..2846e29f7 100644 --- a/SessionSnodeKit/Models/UnrevokeSubaccountResponse.swift +++ b/SessionSnodeKit/Models/UnrevokeSubaccountResponse.swift @@ -9,7 +9,7 @@ public class UnrevokeSubaccountResponse: SnodeRecursiveResponse // MARK: - ValidatableResponse extension UnrevokeSubaccountResponse: ValidatableResponse { - typealias ValidationData = String + typealias ValidationData = (subaccountToUnrevoke: [UInt8], timestampMs: UInt64) typealias ValidationResponse = Bool /// All responses in the swarm must be valid @@ -17,7 +17,7 @@ extension UnrevokeSubaccountResponse: ValidatableResponse { internal func validResultMap( publicKey: String, - validationData: String, + validationData: (subaccountToUnrevoke: [UInt8], timestampMs: UInt64), using dependencies: Dependencies ) throws -> [String: Bool] { let validationMap: [String: Bool] = try swarm.reduce(into: [:]) { result, next in @@ -35,10 +35,11 @@ extension UnrevokeSubaccountResponse: ValidatableResponse { return } - /// Signature of `( PUBKEY_HEX || SUBKEY_TAG_BYTES )` where `SUBKEY_TAG_BYTES` is the + /// Signature of `( PUBKEY_HEX || timestamp || SUBKEY_TAG_BYTES )` where `SUBKEY_TAG_BYTES` is the /// requested subkey tag for revocation let verificationBytes: [UInt8] = publicKey.bytes - .appending(contentsOf: validationData.bytes) + .appending(contentsOf: "\(validationData.timestampMs)".data(using: .ascii)?.bytes) + .appending(contentsOf: validationData.subaccountToUnrevoke) let isValid: Bool = dependencies[singleton: .crypto].verify( .signature( diff --git a/SessionSnodeKit/Networking/SnodeAPI.swift b/SessionSnodeKit/Networking/SnodeAPI.swift index 6a1be6982..8617e2e05 100644 --- a/SessionSnodeKit/Networking/SnodeAPI.swift +++ b/SessionSnodeKit/Networking/SnodeAPI.swift @@ -658,10 +658,12 @@ public final class SnodeAPI { } public static func preparedRevokeSubaccount( - subaccountToRevoke: String, + subaccountToRevoke: [UInt8], authMethod: AuthenticationMethod, using dependencies: Dependencies = Dependencies() ) throws -> HTTP.PreparedRequest { + let timestampMs: UInt64 = UInt64(SnodeAPI.currentOffsetTimestampMs(using: dependencies)) + return try SnodeAPI .prepareRequest( request: Request( @@ -669,7 +671,8 @@ public final class SnodeAPI { publicKey: authMethod.sessionId.hexString, body: RevokeSubaccountRequest( subaccountToRevoke: subaccountToRevoke, - authMethod: authMethod + authMethod: authMethod, + timestampMs: timestampMs ) ), responseType: RevokeSubaccountResponse.self @@ -677,7 +680,7 @@ public final class SnodeAPI { .tryMap { _, response -> Void in try response.validateResultMap( publicKey: authMethod.sessionId.hexString, - validationData: subaccountToRevoke, + validationData: (subaccountToRevoke, timestampMs), using: dependencies ) @@ -686,10 +689,12 @@ public final class SnodeAPI { } public static func preparedUnrevokeSubaccount( - subaccountToUnrevoke: String, + subaccountToUnrevoke: [UInt8], authMethod: AuthenticationMethod, using dependencies: Dependencies = Dependencies() ) throws -> HTTP.PreparedRequest { + let timestampMs: UInt64 = UInt64(SnodeAPI.currentOffsetTimestampMs(using: dependencies)) + return try SnodeAPI .prepareRequest( request: Request( @@ -697,7 +702,8 @@ public final class SnodeAPI { publicKey: authMethod.sessionId.hexString, body: UnrevokeSubaccountRequest( subaccountToUnrevoke: subaccountToUnrevoke, - authMethod: authMethod + authMethod: authMethod, + timestampMs: timestampMs ) ), responseType: UnrevokeSubaccountResponse.self @@ -705,7 +711,7 @@ public final class SnodeAPI { .tryMap { _, response -> Void in try response.validateResultMap( publicKey: authMethod.sessionId.hexString, - validationData: subaccountToUnrevoke, + validationData: (subaccountToUnrevoke, timestampMs), using: dependencies ) diff --git a/SessionUtilitiesKit/Database/Utilities/SQLInterpolation+Utilities.swift b/SessionUtilitiesKit/Database/Utilities/SQLInterpolation+Utilities.swift index 1738f8408..4ec54825f 100644 --- a/SessionUtilitiesKit/Database/Utilities/SQLInterpolation+Utilities.swift +++ b/SessionUtilitiesKit/Database/Utilities/SQLInterpolation+Utilities.swift @@ -77,6 +77,6 @@ public extension SQLInterpolation { /// let request: SQLRequest = "SELECT * FROM \(user) WHERE \(user[.id]) LIKE '\(SessionId.Prefix.standard)%'" @_disfavoredOverload mutating func appendInterpolation(_ idPrefix: SessionId.Prefix) { - appendLiteral("\(SQL(stringLiteral: "\(idPrefix.rawValue)"))") + appendLiteral(idPrefix.rawValue) } } diff --git a/SessionUtilitiesKit/Dependency Injection/Dependencies.swift b/SessionUtilitiesKit/Dependency Injection/Dependencies.swift index 6da01c60b..0149db841 100644 --- a/SessionUtilitiesKit/Dependency Injection/Dependencies.swift +++ b/SessionUtilitiesKit/Dependency Injection/Dependencies.swift @@ -56,6 +56,7 @@ public class Dependencies { /// in `Dependencies.cacheInstances` so that we can be reliably certail we aren't accessing some /// random instance that will go out of memory as soon as the mutation is completed getValueSettingIfNull(cache: cache, &Dependencies.cacheInstances) + let cacheWrapper: Atomic = ( Dependencies.cacheInstances.wrappedValue[cache.identifier] ?? Atomic(cache.mutableInstance(cache.createInstance(self))) // Should never be called