wip: implement ons name

This commit is contained in:
ryanzhao 2021-05-26 16:34:08 +10:00
parent ad51bbb847
commit ae23266058
3 changed files with 60 additions and 3 deletions

View File

@ -430,7 +430,7 @@ object OnionRequestAPI {
/** /**
* Sends an onion request to `snode`. Builds new paths as needed. * Sends an onion request to `snode`. Builds new paths as needed.
*/ */
internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String): Promise<Map<*, *>, Exception> { internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String? = null): Promise<Map<*, *>, Exception> {
val payload = mapOf( "method" to method.rawValue, "params" to parameters ) val payload = mapOf( "method" to method.rawValue, "params" to parameters )
return sendOnionRequest(Destination.Snode(snode), payload).recover { exception -> return sendOnionRequest(Destination.Snode(snode), payload).recover { exception ->
val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException

View File

@ -3,6 +3,10 @@
package org.session.libsession.snode package org.session.libsession.snode
import android.os.Build 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.*
import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map 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.removing05PrefixIfNeeded
import org.session.libsignal.utilities.retryIfNeeded import org.session.libsignal.utilities.retryIfNeeded
import org.session.libsignal.utilities.* import org.session.libsignal.utilities.*
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import java.security.SecureRandom import java.security.SecureRandom
import java.util.*
object SnodeAPI { object SnodeAPI {
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
private val database: LokiAPIDatabaseProtocol private val database: LokiAPIDatabaseProtocol
get() = SnodeModule.shared.storage get() = SnodeModule.shared.storage
private val broadcaster: Broadcaster private val broadcaster: Broadcaster
@ -54,10 +62,14 @@ object SnodeAPI {
internal sealed class Error(val description: String) : Exception(description) { internal sealed class Error(val description: String) : Exception(description) {
object Generic : Error("An error occurred.") object Generic : Error("An error occurred.")
object ClockOutOfSync : Error("Your clock is out of sync with the Service Node network.") 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 API
internal fun invoke(method: Snode.Method, snode: Snode, publicKey: String, parameters: Map<String, String>): RawResponsePromise { internal fun invoke(method: Snode.Method, snode: Snode, publicKey: String? = null, parameters: Map<String, Any>): RawResponsePromise {
val url = "${snode.address}:${snode.port}/storage_rpc/v1" val url = "${snode.address}:${snode.port}/storage_rpc/v1"
if (useOnionRequests) { if (useOnionRequests) {
return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey) return OnionRequestAPI.sendOnionRequest(method, parameters, snode, publicKey)
@ -153,6 +165,50 @@ object SnodeAPI {
} }
// Public API // Public API
fun getSessionIDFor(onsName: String): Promise<String, Exception> {
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<String, Exception>()
val promise = deferred.promise
all(promises).success { results ->
val sessionIDs = mutableListOf<String>()
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<List<Snode>, Exception> { fun getTargetSnodes(publicKey: String): Promise<List<Snode>, Exception> {
// SecureRandom() should be cryptographically secure // SecureRandom() should be cryptographically secure
return getSwarm(publicKey).map { it.shuffled(SecureRandom()).take(targetSwarmSnodeCount) } return getSwarm(publicKey).map { it.shuffled(SecureRandom()).take(targetSwarmSnodeCount) }

View File

@ -6,7 +6,8 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?) {
public enum class Method(val rawValue: String) { public enum class Method(val rawValue: String) {
GetSwarm("get_snodes_for_pubkey"), GetSwarm("get_snodes_for_pubkey"),
GetMessages("retrieve"), GetMessages("retrieve"),
SendMessage("store") SendMessage("store"),
OxenDaemonRPCCall("oxend_request")
} }
data class KeySet(val ed25519Key: String, val x25519Key: String) data class KeySet(val ed25519Key: String, val x25519Key: String)