// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import Foundation import GRDB import Sodium import SessionUtilitiesKit import Quick import Nimble @testable import SessionMessagingKit class MessageSenderEncryptionSpec: QuickSpec { // MARK: - Spec override func spec() { var mockStorage: Storage! var mockBox: MockBox! var mockSign: MockSign! var mockNonce24Generator: MockNonce24Generator! var dependencies: SMKDependencies! describe("a MessageSender") { beforeEach { mockStorage = Storage( customWriter: try! DatabaseQueue(), customMigrations: [ SNUtilitiesKit.migrations(), SNMessagingKit.migrations() ] ) mockBox = MockBox() mockSign = MockSign() mockNonce24Generator = MockNonce24Generator() dependencies = SMKDependencies( storage: mockStorage, box: mockBox, sign: mockSign, nonceGenerator24: mockNonce24Generator ) mockStorage.write { db in try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db) try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db) } mockNonce24Generator .when { $0.nonce() } .thenReturn(Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!.bytes) } context("when encrypting with the session protocol") { beforeEach { mockBox.when { $0.seal(message: anyArray(), recipientPublicKey: anyArray()) }.thenReturn([1, 2, 3]) mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn([]) } it("can encrypt correctly") { let result = try? MessageSender.encryptWithSessionProtocol( "TestMessage".data(using: .utf8)!, for: "05\(TestConstants.publicKey)", using: SMKDependencies(storage: mockStorage) ) // Note: A Nonce is used for this so we can't compare the exact value when not mocked expect(result).toNot(beNil()) expect(result?.count).to(equal(155)) } it("returns the correct value when mocked") { let result = try? MessageSender.encryptWithSessionProtocol( "TestMessage".data(using: .utf8)!, for: "05\(TestConstants.publicKey)", using: dependencies ) expect(result?.bytes).to(equal([1, 2, 3])) } it("throws an error if there is no ed25519 keyPair") { mockStorage.write { db in _ = try Identity.filter(id: .ed25519PublicKey).deleteAll(db) _ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db) } expect { try MessageSender.encryptWithSessionProtocol( "TestMessage".data(using: .utf8)!, for: "05\(TestConstants.publicKey)", using: dependencies ) } .to(throwError(MessageSenderError.noUserED25519KeyPair)) } it("throws an error if the signature generation fails") { mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn(nil) expect { try MessageSender.encryptWithSessionProtocol( "TestMessage".data(using: .utf8)!, for: "05\(TestConstants.publicKey)", using: dependencies ) } .to(throwError(MessageSenderError.signingFailed)) } it("throws an error if the encryption fails") { mockBox.when { $0.seal(message: anyArray(), recipientPublicKey: anyArray()) }.thenReturn(nil) expect { try MessageSender.encryptWithSessionProtocol( "TestMessage".data(using: .utf8)!, for: "05\(TestConstants.publicKey)", using: dependencies ) } .to(throwError(MessageSenderError.encryptionFailed)) } } context("when encrypting with the blinded session protocol") { it("successfully encrypts") { let result = try? MessageSender.encryptWithSessionBlindingProtocol( "TestMessage".data(using: .utf8)!, for: "15\(TestConstants.blindedPublicKey)", openGroupPublicKey: TestConstants.serverPublicKey, using: dependencies ) expect(result?.toHexString()) .to(equal( "00db16b6687382811d69875a5376f66acad9c49fe5e26bcf770c7e6e9c230299" + "f61b315299dd1fa700dd7f34305c0465af9e64dc791d7f4123f1eeafa5b4d48b" + "3ade4f4b2a2764762e5a2c7900f254bd91633b43" )) } it("includes a version at the start of the encrypted value") { let result = try? MessageSender.encryptWithSessionBlindingProtocol( "TestMessage".data(using: .utf8)!, for: "15\(TestConstants.blindedPublicKey)", openGroupPublicKey: TestConstants.serverPublicKey, using: dependencies ) expect(result?.toHexString().prefix(2)).to(equal("00")) } it("includes the nonce at the end of the encrypted value") { let maybeResult = try? MessageSender.encryptWithSessionBlindingProtocol( "TestMessage".data(using: .utf8)!, for: "15\(TestConstants.blindedPublicKey)", openGroupPublicKey: TestConstants.serverPublicKey, using: dependencies ) let result: [UInt8] = (maybeResult?.bytes ?? []) let nonceBytes: [UInt8] = Array(result[max(0, (result.count - 24))..