mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			192 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			192 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import Foundation
 | |
| import GRDB
 | |
| import SessionUtilitiesKit
 | |
| 
 | |
| import Quick
 | |
| import Nimble
 | |
| 
 | |
| @testable import SessionMessagingKit
 | |
| 
 | |
| class CryptoSMKSpec: QuickSpec {
 | |
|     override class func spec() {
 | |
|         // MARK: Configuration
 | |
|         
 | |
|         @TestState var crypto: Crypto! = Crypto()
 | |
|         @TestState var mockCrypto: MockCrypto! = MockCrypto()
 | |
|         @TestState var dependencies: Dependencies! = Dependencies(storage: nil, crypto: crypto)
 | |
|         @TestState var mockStorage: Storage! = {
 | |
|             let result = SynchronousStorage(
 | |
|                 customWriter: try! DatabaseQueue(),
 | |
|                 migrationTargets: [
 | |
|                     SNUtilitiesKit.self,
 | |
|                     SNMessagingKit.self
 | |
|                 ],
 | |
|                 initialData: { db in
 | |
|                     try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db)
 | |
|                     try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db)
 | |
|                 },
 | |
|                 using: dependencies
 | |
|             )
 | |
|             dependencies.storage = result
 | |
|             
 | |
|             return result
 | |
|         }()
 | |
|         
 | |
|         // MARK: - Crypto for SessionMessagingKit
 | |
|         describe("Crypto for SessionMessagingKit") {
 | |
|             // MARK: -- can convert an ed25519 public key into an x25519 public key
 | |
|             it("can convert an ed25519 public key into an x25519 public key") {
 | |
|                 let result = crypto.generate(.x25519(ed25519Pubkey: Array(Data(hex: TestConstants.edPublicKey))))
 | |
| 
 | |
|                 expect(result?.toHexString())
 | |
|                     .to(equal("88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
 | |
|             }
 | |
| 
 | |
|             // MARK: -- can convert an ed25519 private key into an x25519 private key
 | |
|             it("can convert an ed25519 private key into an x25519 private key") {
 | |
|                 let result = crypto.generate(.x25519(ed25519Seckey: Array(Data(hex: TestConstants.edSecretKey))))
 | |
| 
 | |
|                 expect(result?.toHexString())
 | |
|                     .to(equal("30d796c1ddb4dc455fd998a98aa275c247494a9a7bde9c1fee86ae45cd585241"))
 | |
|             }
 | |
| 
 | |
|             // MARK: -- when generating a hash
 | |
|             describe("when generating a hash") {
 | |
|                 // MARK: ------ generates a hash correctly
 | |
|                 it("generates a hash correctly") {
 | |
|                     let result = crypto.generate(.hash(message: "TestMessage".bytes, key: "Key".bytes, length: 32))
 | |
|                     expect(result).toNot(beNil())
 | |
|                     expect(result?.count).to(equal(32))
 | |
|                     expect(result?.toHexString())
 | |
|                         .to(equal("4bb38525401d48349990f8e018aeeb3c68f9469babf4de9d3f08d960c7ae2721"))
 | |
|                 }
 | |
| 
 | |
|                 // MARK: ------ generates a hash correctly with no key
 | |
|                 it("generates a hash correctly with no key") {
 | |
|                     let result = crypto.generate(.hash(message: "TestMessage".bytes, key: nil, length: 32))
 | |
|                     expect(result).toNot(beNil())
 | |
|                     expect(result?.count).to(equal(32))
 | |
|                     expect(result?.toHexString())
 | |
|                         .to(equal("2a48a12262e4548afb97fe2b04a912a02297d451169ee7ef2d01a28ea20286ab"))
 | |
|                 }
 | |
| 
 | |
|                 // MARK: ------ fails if given invalid options
 | |
|                 it("fails if given invalid options") {
 | |
|                     // Max length 64
 | |
|                     expect(crypto.generate(.hash(message: "TestMessage".bytes, key: nil, length: 65))).to(beNil())
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // MARK: -- when encrypting with the session protocol
 | |
|             context("when encrypting with the session protocol") {
 | |
|                 // MARK: ---- can encrypt correctly
 | |
|                 it("can encrypt correctly") {
 | |
|                     let result: Data? = mockStorage.read { db in
 | |
|                         try crypto.tryGenerate(
 | |
|                             .ciphertextWithSessionProtocol(
 | |
|                                 db,
 | |
|                                 plaintext: "TestMessage".data(using: .utf8)!,
 | |
|                                 destination: .contact(publicKey: "05\(TestConstants.publicKey)"),
 | |
|                                 using: dependencies
 | |
|                             )
 | |
|                         )
 | |
|                     }
 | |
| 
 | |
|                     // 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))
 | |
|                 }
 | |
| 
 | |
|                 // MARK: ---- throws an error if there is no ed25519 keyPair
 | |
|                 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)
 | |
|                     }
 | |
| 
 | |
|                     mockStorage.read { db in
 | |
|                         expect {
 | |
|                             try crypto.tryGenerate(
 | |
|                                 .ciphertextWithSessionProtocol(
 | |
|                                     db,
 | |
|                                     plaintext: "TestMessage".data(using: .utf8)!,
 | |
|                                     destination: .contact(publicKey: "05\(TestConstants.publicKey)"),
 | |
|                                     using: dependencies
 | |
|                                 )
 | |
|                             )
 | |
|                         }
 | |
|                         .to(throwError(MessageSenderError.noUserED25519KeyPair))
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // MARK: -- when decrypting with the session protocol
 | |
|             context("when decrypting with the session protocol") {
 | |
|                 // MARK: ---- successfully decrypts a message
 | |
|                 it("successfully decrypts a message") {
 | |
|                     let result = mockStorage.read { db in
 | |
|                         crypto.generate(
 | |
|                             .plaintextWithSessionProtocol(
 | |
|                                 db,
 | |
|                                 ciphertext: Data(
 | |
|                                     base64Encoded: "SRP0eBUWh4ez6ppWjUs5/Wph5fhnPRgB5zsWWnTz+FBAw/YI3oS2pDpIfyetMTbU" +
 | |
|                                     "sFMhE5G4PbRtQFey1hsxLl221Qivc3ayaX2Mm/X89Dl8e45BC+Lb/KU9EdesxIK4pVgYXs9XrMtX3v8" +
 | |
|                                     "dt0eBaXneOBfr7qB8pHwwMZjtkOu1ED07T9nszgbWabBphUfWXe2U9K3PTRisSCI="
 | |
|                                 )!,
 | |
|                                 using: dependencies
 | |
|                             )
 | |
|                         )
 | |
|                     }
 | |
| 
 | |
|                     expect(String(data: (result?.plaintext ?? Data()), encoding: .utf8)).to(equal("TestMessage"))
 | |
|                     expect(result?.senderSessionIdHex)
 | |
|                         .to(equal("0588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
 | |
|                 }
 | |
| 
 | |
|                 // MARK: ---- throws an error if there is no ed25519 keyPair
 | |
|                 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)
 | |
|                     }
 | |
| 
 | |
|                     mockStorage.read { db in
 | |
|                         expect {
 | |
|                             try crypto.tryGenerate(
 | |
|                                 .plaintextWithSessionProtocol(
 | |
|                                     db,
 | |
|                                     ciphertext: Data(
 | |
|                                         base64Encoded: "SRP0eBUWh4ez6ppWjUs5/Wph5fhnPRgB5zsWWnTz+FBAw/YI3oS2pDpIfyetMTbU" +
 | |
|                                         "sFMhE5G4PbRtQFey1hsxLl221Qivc3ayaX2Mm/X89Dl8e45BC+Lb/KU9EdesxIK4pVgYXs9XrMtX3v8" +
 | |
|                                         "dt0eBaXneOBfr7qB8pHwwMZjtkOu1ED07T9nszgbWabBphUfWXe2U9K3PTRisSCI="
 | |
|                                     )!,
 | |
|                                     using: dependencies
 | |
|                                 )
 | |
|                             )
 | |
|                         }
 | |
|                         .to(throwError(MessageSenderError.noUserED25519KeyPair))
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 // MARK: ---- throws an error if the ciphertext is too short
 | |
|                 it("throws an error if the ciphertext is too short") {
 | |
|                     mockStorage.read { db in
 | |
|                         expect {
 | |
|                             try crypto.tryGenerate(
 | |
|                                 .plaintextWithSessionProtocol(
 | |
|                                     db,
 | |
|                                     ciphertext: Data([1, 2, 3]),
 | |
|                                     using: dependencies
 | |
|                                 )
 | |
|                             )
 | |
|                         }
 | |
|                         .to(throwError(MessageReceiverError.decryptionFailed))
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |