// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. import Foundation import GRDB import SessionSnodeKit import SessionUtilitiesKit // MARK: - Authentication Types public extension Authentication { /// Used for when interacting as the current user struct standard: AuthenticationMethod { public let sessionId: SessionId public let ed25519KeyPair: KeyPair public var info: Info { .standard(sessionId: sessionId, ed25519KeyPair: ed25519KeyPair) } public init(sessionId: SessionId, ed25519KeyPair: KeyPair) { self.sessionId = sessionId self.ed25519KeyPair = ed25519KeyPair } // MARK: - SignatureGenerator public func generateSignature(with verificationBytes: [UInt8], using dependencies: Dependencies) throws -> Authentication.Signature { return try dependencies[singleton: .crypto].tryGenerate( .signature(message: verificationBytes, ed25519SecretKey: ed25519KeyPair.secretKey) ) } } /// Used for when interacting as a group admin struct groupAdmin: AuthenticationMethod { public let groupSessionId: SessionId public let ed25519SecretKey: [UInt8] public var info: Info { .groupAdmin(groupSessionId: groupSessionId, ed25519SecretKey: ed25519SecretKey) } public init(groupSessionId: SessionId, ed25519SecretKey: [UInt8]) { self.groupSessionId = groupSessionId self.ed25519SecretKey = ed25519SecretKey } // MARK: - SignatureGenerator public func generateSignature(with verificationBytes: [UInt8], using dependencies: Dependencies) throws -> Authentication.Signature { return try dependencies[singleton: .crypto].tryGenerate( .signature(message: verificationBytes, ed25519SecretKey: ed25519SecretKey) ) } } /// Used for when interacting as a group member struct groupMember: AuthenticationMethod { public let groupSessionId: SessionId public let authData: Data public var info: Info { .groupMember(groupSessionId: groupSessionId, authData: authData) } public init(groupSessionId: SessionId, authData: Data) { self.groupSessionId = groupSessionId self.authData = authData } // MARK: - SignatureGenerator public func generateSignature(with verificationBytes: [UInt8], using dependencies: Dependencies) throws -> Authentication.Signature { return try dependencies.mutate(cache: .libSession) { cache in try dependencies[singleton: .crypto].tryGenerate( .signatureSubaccount( config: cache.config(for: .groupKeys, sessionId: groupSessionId), verificationBytes: verificationBytes, memberAuthData: authData ) ) } } } } // MARK: - Convenience fileprivate struct GroupAuthData: Codable, FetchableRecord { let groupIdentityPrivateKey: Data? let authData: Data? } public extension Authentication { static func with( _ db: Database, swarmPublicKey: String, using dependencies: Dependencies ) throws -> AuthenticationMethod { switch try? SessionId(from: swarmPublicKey) { case .some(let sessionId) where sessionId.prefix == .standard: guard let keyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db) else { throw SnodeAPIError.noKeyPair } return Authentication.standard(sessionId: sessionId, ed25519KeyPair: keyPair) case .some(let sessionId) where sessionId.prefix == .group: let authData: GroupAuthData? = try? ClosedGroup .filter(id: swarmPublicKey) .select(.authData, .groupIdentityPrivateKey) .asRequest(of: GroupAuthData.self) .fetchOne(db) switch (authData?.groupIdentityPrivateKey, authData?.authData) { case (.some(let privateKey), _): return Authentication.groupAdmin( groupSessionId: sessionId, ed25519SecretKey: Array(privateKey) ) case (_, .some(let authData)): return Authentication.groupMember( groupSessionId: sessionId, authData: authData ) default: throw SnodeAPIError.invalidAuthentication } default: throw SnodeAPIError.invalidAuthentication } } }