From ae23266058f78f33aefe500ea015eff116692985 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 26 May 2021 16:34:08 +1000 Subject: [PATCH] wip: implement ons name --- .../libsession/snode/OnionRequestAPI.kt | 2 +- .../org/session/libsession/snode/SnodeAPI.kt | 58 ++++++++++++++++++- .../org/session/libsignal/utilities/Snode.kt | 3 +- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index 4f6aca389..a188b4a5a 100644 --- a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -430,7 +430,7 @@ object OnionRequestAPI { /** * Sends an onion request to `snode`. Builds new paths as needed. */ - internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String): Promise, Exception> { + internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String? = null): Promise, Exception> { val payload = mapOf( "method" to method.rawValue, "params" to parameters ) return sendOnionRequest(Destination.Snode(snode), payload).recover { exception -> val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 3f9115572..429b2d0e4 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -3,6 +3,10 @@ package org.session.libsession.snode import android.os.Build +import com.goterl.lazysodium.LazySodiumAndroid +import com.goterl.lazysodium.SodiumAndroid +import com.goterl.lazysodium.interfaces.PwHash +import com.goterl.lazysodium.interfaces.SecretBox import nl.komponents.kovenant.* import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.map @@ -17,10 +21,14 @@ import org.session.libsignal.utilities.prettifiedDescription import org.session.libsignal.utilities.removing05PrefixIfNeeded import org.session.libsignal.utilities.retryIfNeeded import org.session.libsignal.utilities.* +import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Log import java.security.SecureRandom +import java.util.* object SnodeAPI { + private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) } + private val database: LokiAPIDatabaseProtocol get() = SnodeModule.shared.storage private val broadcaster: Broadcaster @@ -54,10 +62,14 @@ object SnodeAPI { internal sealed class Error(val description: String) : Exception(description) { object Generic : Error("An error occurred.") object ClockOutOfSync : Error("Your clock is out of sync with the Service Node network.") + // ONS + object DecryptionFailed : Error("Couldn't decrypt ONS name.") + object HashingFailed : Error("Couldn't compute ONS name hash.") + object ValidationFailed : Error("ONS name validation failed.") } // Internal API - internal fun invoke(method: Snode.Method, snode: Snode, publicKey: String, parameters: Map): RawResponsePromise { + internal fun invoke(method: Snode.Method, snode: Snode, publicKey: String? = null, parameters: Map): RawResponsePromise { val url = "${snode.address}:${snode.port}/storage_rpc/v1" if (useOnionRequests) { return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey) @@ -153,6 +165,50 @@ object SnodeAPI { } // Public API + fun getSessionIDFor(onsName: String): Promise { + val validationCount = 3 + val sessionIDByteCount = 33 + // Hash the ONS name using BLAKE2b + val name = onsName.toLowerCase(Locale.ENGLISH) + val nameHash = sodium.cryptoGenericHash(name) + val base64EncodedNameHash = nameHash + // Ask 3 different snodes for the Session ID associated with the given name hash + val parameters = mapOf( + "endpoint" to "ons_resolve", + "params" to mapOf( "type" to 0, "name_hash" to base64EncodedNameHash ) + ) + val promises = (0..validationCount).map { + getRandomSnode().bind { snode -> + invoke(Snode.Method.OxenDaemonRPCCall, snode, null, parameters) + } + } + val deferred = deferred() + val promise = deferred.promise + all(promises).success { results -> + val sessionIDs = mutableListOf() + for (json in results) { + val intermediate = json["result"] as? Map<*, *> + val hexEncodedCiphertext = intermediate?.get("encrypted_value") as? String + if (hexEncodedCiphertext != null) { + val ciphertext = Hex.fromStringCondensed(hexEncodedCiphertext) + val isArgon2Based = (intermediate["nonce"] == null) + if (isArgon2Based) { + // Handle old Argon2-based encryption used before HF16 + val salt = ByteArray(PwHash.SALTBYTES) + val key = sodium.cryptoPwHash(name, SecretBox.KEYBYTES, salt, PwHash.OPSLIMIT_MODERATE, PwHash.MEMLIMIT_MODERATE, PwHash.Alg.PWHASH_ALG_ARGON2ID13) + val nonce = ByteArray(SecretBox.NONCEBYTES) + val sessionID = sodium.cryptoSecretBoxOpenEasy(ciphertext, nonce, key) + } else { + + } + } else { + deferred.reject(Error.Generic) + } + } + } + return promise + } + fun getTargetSnodes(publicKey: String): Promise, Exception> { // SecureRandom() should be cryptographically secure return getSwarm(publicKey).map { it.shuffled(SecureRandom()).take(targetSwarmSnodeCount) } diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt index af4a1f694..92c30095e 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt +++ b/libsignal/src/main/java/org/session/libsignal/utilities/Snode.kt @@ -6,7 +6,8 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?) { public enum class Method(val rawValue: String) { GetSwarm("get_snodes_for_pubkey"), GetMessages("retrieve"), - SendMessage("store") + SendMessage("store"), + OxenDaemonRPCCall("oxend_request") } data class KeySet(val ed25519Key: String, val x25519Key: String)