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.
		
		
		
		
		
			
		
			
				
	
	
		
			139 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			139 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import Foundation
 | |
| import GRDB
 | |
| import Sodium
 | |
| import SessionUtilitiesKit
 | |
| 
 | |
| extension MessageReceiver {
 | |
|     internal static func decryptWithSessionProtocol(
 | |
|         ciphertext: Data,
 | |
|         using x25519KeyPair: KeyPair,
 | |
|         using dependencies: Dependencies = Dependencies()
 | |
|     ) throws -> (plaintext: Data, senderX25519PublicKey: String) {
 | |
|         let recipientX25519PrivateKey: Bytes = x25519KeyPair.secretKey
 | |
|         let recipientX25519PublicKey: Bytes = x25519KeyPair.publicKey
 | |
|         let signatureSize: Int = dependencies.crypto.size(.signature)
 | |
|         let ed25519PublicKeySize: Int = dependencies.crypto.size(.publicKey)
 | |
|         
 | |
|         // 1. ) Decrypt the message
 | |
|         guard
 | |
|             let plaintextWithMetadata = try? dependencies.crypto.perform(
 | |
|                 .open(
 | |
|                     anonymousCipherText: Bytes(ciphertext),
 | |
|                     recipientPublicKey: Box.PublicKey(Bytes(recipientX25519PublicKey)),
 | |
|                     recipientSecretKey: Bytes(recipientX25519PrivateKey)
 | |
|                 )
 | |
|             ),
 | |
|             plaintextWithMetadata.count > (signatureSize + ed25519PublicKeySize)
 | |
|         else {
 | |
|             throw MessageReceiverError.decryptionFailed
 | |
|         }
 | |
|         
 | |
|         // 2. ) Get the message parts
 | |
|         let signature = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - signatureSize ..< plaintextWithMetadata.count])
 | |
|         let senderED25519PublicKey = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize) ..< plaintextWithMetadata.count - signatureSize])
 | |
|         let plaintext = Bytes(plaintextWithMetadata[0..<plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize)])
 | |
|         
 | |
|         // 3. ) Verify the signature
 | |
|         let verificationData = plaintext + senderED25519PublicKey + recipientX25519PublicKey
 | |
|         
 | |
|         guard
 | |
|             dependencies.crypto.verify(
 | |
|                 .signature(message: verificationData, publicKey: senderED25519PublicKey, signature: signature)
 | |
|             )
 | |
|         else { throw MessageReceiverError.invalidSignature }
 | |
|         
 | |
|         // 4. ) Get the sender's X25519 public key
 | |
|         guard
 | |
|             let senderX25519PublicKey = try? dependencies.crypto.perform(
 | |
|                 .toX25519(ed25519PublicKey: senderED25519PublicKey)
 | |
|             )
 | |
|         else { throw MessageReceiverError.decryptionFailed }
 | |
|         
 | |
|         return (Data(plaintext), SessionId(.standard, publicKey: senderX25519PublicKey).hexString)
 | |
|     }
 | |
|     
 | |
|     internal static func decryptWithSessionBlindingProtocol(
 | |
|         data: Data,
 | |
|         isOutgoing: Bool,
 | |
|         otherBlindedPublicKey: String,
 | |
|         with openGroupPublicKey: String,
 | |
|         userEd25519KeyPair: KeyPair,
 | |
|         using dependencies: Dependencies = Dependencies()
 | |
|     ) throws -> (plaintext: Data, senderX25519PublicKey: String) {
 | |
|         /// Ensure the data is at least long enough to have the required components
 | |
|         guard
 | |
|             data.count > (dependencies.crypto.size(.nonce24) + 2),
 | |
|             let blindedKeyPair = dependencies.crypto.generate(
 | |
|                 .blindedKeyPair(serverPublicKey: openGroupPublicKey, edKeyPair: userEd25519KeyPair, using: dependencies)
 | |
|             )
 | |
|         else { throw MessageReceiverError.decryptionFailed }
 | |
| 
 | |
|         /// Step one: calculate the shared encryption key, receiving from A to B
 | |
|         let otherKeyBytes: Bytes = Data(hex: otherBlindedPublicKey.removingIdPrefixIfNeeded()).bytes
 | |
|         let kA: Bytes = (isOutgoing ? blindedKeyPair.publicKey : otherKeyBytes)
 | |
|         guard
 | |
|             let dec_key: Bytes = try? dependencies.crypto.perform(
 | |
|                 .sharedBlindedEncryptionKey(
 | |
|                     secretKey: userEd25519KeyPair.secretKey,
 | |
|                     otherBlindedPublicKey: otherKeyBytes,
 | |
|                     fromBlindedPublicKey: kA,
 | |
|                     toBlindedPublicKey: (isOutgoing ? otherKeyBytes : blindedKeyPair.publicKey),
 | |
|                     using: dependencies
 | |
|                 )
 | |
|             )
 | |
|         else { throw MessageReceiverError.decryptionFailed }
 | |
|         
 | |
|         /// v, ct, nc = data[0], data[1:-24], data[-24:]
 | |
|         let version: UInt8 = data.bytes[0]
 | |
|         let ciphertext: Bytes = Bytes(data.bytes[1..<(data.count - dependencies.crypto.size(.nonce24))])
 | |
|         let nonce: Bytes = Bytes(data.bytes[(data.count - dependencies.crypto.size(.nonce24))..<data.count])
 | |
| 
 | |
|         /// Make sure our encryption version is okay
 | |
|         guard version == 0 else { throw MessageReceiverError.decryptionFailed }
 | |
| 
 | |
|         /// Decrypt
 | |
|         guard
 | |
|             let innerBytes: Bytes = try? dependencies.crypto.perform(
 | |
|                 .decryptAeadXChaCha20(
 | |
|                     authenticatedCipherText: ciphertext,
 | |
|                     secretKey: dec_key,
 | |
|                     nonce: nonce
 | |
|                 )
 | |
|             )
 | |
|         else { throw MessageReceiverError.decryptionFailed }
 | |
|         
 | |
|         /// Ensure the length is correct
 | |
|         guard innerBytes.count > dependencies.crypto.size(.publicKey) else { throw MessageReceiverError.decryptionFailed }
 | |
| 
 | |
|         /// Split up: the last 32 bytes are the sender's *unblinded* ed25519 key
 | |
|         let plaintext: Bytes = Bytes(innerBytes[
 | |
|             0...(innerBytes.count - 1 - dependencies.crypto.size(.publicKey))
 | |
|         ])
 | |
|         let sender_edpk: Bytes = Bytes(innerBytes[
 | |
|             (innerBytes.count - dependencies.crypto.size(.publicKey))...(innerBytes.count - 1)
 | |
|         ])
 | |
|         
 | |
|         /// Verify that the inner sender_edpk (A) yields the same outer kA we got with the message
 | |
|         guard
 | |
|             let blindingFactor: Bytes = try? dependencies.crypto.perform(
 | |
|                 .generateBlindingFactor(serverPublicKey: openGroupPublicKey, using: dependencies)
 | |
|             ),
 | |
|             let sharedSecret: Bytes = try? dependencies.crypto.perform(
 | |
|                 .combineKeys(lhsKeyBytes: blindingFactor, rhsKeyBytes: sender_edpk)
 | |
|             ),
 | |
|             kA == sharedSecret
 | |
|         else { throw MessageReceiverError.invalidSignature }
 | |
|         
 | |
|         /// Get the sender's X25519 public key
 | |
|         guard
 | |
|             let senderSessionIdBytes: Bytes = try? dependencies.crypto.perform(
 | |
|                 .toX25519(ed25519PublicKey: sender_edpk)
 | |
|             )
 | |
|         else { throw MessageReceiverError.decryptionFailed }
 | |
|         
 | |
|         return (Data(plaintext), SessionId(.standard, publicKey: senderSessionIdBytes).hexString)
 | |
|     }
 | |
| }
 |