mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Cleaned up the Dependencies so that tests can run synchronously without having to custom set queues as much Sorted out the crypto and network dependencies to avoid needing weird dependency inheritance Fixed the flaky tests so they are no longer flaky Fixed some unexpected JobRunner behaviours Updated the CI config to use a local build directory for derivedData (now works with build tweaks)
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)
|
|
}
|
|
}
|