// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import Foundation import SessionUtilitiesKit public final class UnrevokeSubaccountResponse: SnodeRecursiveResponse {} // MARK: - ValidatableResponse extension UnrevokeSubaccountResponse: ValidatableResponse { typealias ValidationData = (subaccountsToUnrevoke: [[UInt8]], timestampMs: UInt64) typealias ValidationResponse = Bool /// All responses in the swarm must be valid internal static var requiredSuccessfulResponses: Int { -1 } internal func validResultMap( swarmPublicKey: String, validationData: (subaccountsToUnrevoke: [[UInt8]], timestampMs: UInt64), using dependencies: Dependencies ) throws -> [String: Bool] { let validationMap: [String: Bool] = try swarm.reduce(into: [:]) { result, next in guard !next.value.failed, let signatureBase64: String = next.value.signatureBase64, let encodedSignature: Data = Data(base64Encoded: signatureBase64) else { if let reason: String = next.value.reason, let statusCode: Int = next.value.code { SNLog("Couldn't revoke subaccount from: \(next.key) due to error: \(reason) (\(statusCode)).") } else { SNLog("Couldn't revoke subaccount from: \(next.key).") } return } /// Signature of `( PUBKEY_HEX || timestamp || SUBACCOUNT_TOKEN_BYTES... )` where `SUBACCOUNT_TOKEN_BYTES` is the /// requested subaccount token for revocation let verificationBytes: [UInt8] = swarmPublicKey.bytes .appending(contentsOf: "\(validationData.timestampMs)".data(using: .ascii)?.bytes) .appending(contentsOf: Array(validationData.subaccountsToUnrevoke.joined())) let isValid: Bool = dependencies[singleton: .crypto].verify( .signature( message: verificationBytes, publicKey: Data(hex: next.key).bytes, signature: encodedSignature.bytes ) ) // If the update signature is invalid then we want to fail here guard isValid else { throw SnodeAPIError.signatureVerificationFailed } result[next.key] = isValid } return try Self.validated(map: validationMap) } }