implement get session id from ons name api

This commit is contained in:
Ryan ZHAO 2021-05-27 16:23:15 +10:00
parent 0e89d88459
commit 592825dcc6

View File

@ -5,26 +5,24 @@ package org.session.libsession.snode
import android.os.Build import android.os.Build
import com.goterl.lazysodium.LazySodiumAndroid import com.goterl.lazysodium.LazySodiumAndroid
import com.goterl.lazysodium.SodiumAndroid import com.goterl.lazysodium.SodiumAndroid
import com.goterl.lazysodium.exceptions.SodiumException
import com.goterl.lazysodium.interfaces.AEAD
import com.goterl.lazysodium.interfaces.GenericHash
import com.goterl.lazysodium.interfaces.PwHash import com.goterl.lazysodium.interfaces.PwHash
import com.goterl.lazysodium.interfaces.SecretBox import com.goterl.lazysodium.interfaces.SecretBox
import com.goterl.lazysodium.utils.Key
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
import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.messaging.utilities.MessageWrapper
import org.session.libsignal.crypto.getRandomElement import org.session.libsignal.crypto.getRandomElement
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Snode
import org.session.libsignal.utilities.HTTP
import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.database.LokiAPIDatabaseProtocol
import org.session.libsignal.utilities.Broadcaster import org.session.libsignal.protos.SignalServiceProtos
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.*
import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Log
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
import javax.crypto.AEADBadTagException
object SnodeAPI { object SnodeAPI {
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) } private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
@ -166,12 +164,19 @@ object SnodeAPI {
// Public API // Public API
fun getSessionIDFor(onsName: String): Promise<String, Exception> { fun getSessionIDFor(onsName: String): Promise<String, Exception> {
val deferred = deferred<String, Exception>()
val promise = deferred.promise
val validationCount = 3 val validationCount = 3
val sessionIDByteCount = 33 val sessionIDByteCount = 33
// Hash the ONS name using BLAKE2b // Hash the ONS name using BLAKE2b
val name = onsName.toLowerCase(Locale.ENGLISH) val onsName = onsName.toLowerCase(Locale.ENGLISH)
val nameHash = sodium.cryptoGenericHash(name) val nameAsData = onsName.toByteArray()
val base64EncodedNameHash = nameHash val nameHash = ByteArray(GenericHash.BYTES)
if (!sodium.cryptoGenericHash(nameHash, nameHash.size, nameAsData, nameAsData.size.toLong())) {
deferred.reject(Error.HashingFailed)
return promise
}
val base64EncodedNameHash = Base64.encodeBytes(nameHash)
// Ask 3 different snodes for the Session ID associated with the given name hash // Ask 3 different snodes for the Session ID associated with the given name hash
val parameters = mapOf( val parameters = mapOf(
"endpoint" to "ons_resolve", "endpoint" to "ons_resolve",
@ -182,29 +187,63 @@ object SnodeAPI {
invoke(Snode.Method.OxenDaemonRPCCall, snode, null, parameters) invoke(Snode.Method.OxenDaemonRPCCall, snode, null, parameters)
} }
} }
val deferred = deferred<String, Exception>()
val promise = deferred.promise
all(promises).success { results -> all(promises).success { results ->
val sessionIDs = mutableListOf<String>() val sessionIDs = mutableListOf<String>()
for (json in results) { for (json in results) {
val intermediate = json["result"] as? Map<*, *> val intermediate = json["result"] as? Map<*, *>
val hexEncodedCiphertext = intermediate?.get("encrypted_value") as? String val hexEncodedCiphertext = intermediate?.get("encrypted_value") as? String
if (hexEncodedCiphertext != null) { if (hexEncodedCiphertext != null) {
val ciphertext = Hex.fromStringCondensed(hexEncodedCiphertext)
val isArgon2Based = (intermediate["nonce"] == null) val isArgon2Based = (intermediate["nonce"] == null)
if (isArgon2Based) { if (isArgon2Based) {
// Handle old Argon2-based encryption used before HF16 // Handle old Argon2-based encryption used before HF16
val salt = ByteArray(PwHash.SALTBYTES) 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 key: String
val nonce = ByteArray(SecretBox.NONCEBYTES) val nonce = ByteArray(SecretBox.NONCEBYTES)
val sessionID = sodium.cryptoSecretBoxOpenEasy(ciphertext, nonce, key) val sessionID: String
try {
key = sodium.cryptoPwHash(onsName, SecretBox.KEYBYTES, salt, PwHash.OPSLIMIT_MODERATE, PwHash.MEMLIMIT_MODERATE, PwHash.Alg.PWHASH_ALG_ARGON2ID13)
} catch (e: SodiumException) {
deferred.reject(Error.HashingFailed)
return@success
}
try {
sessionID = sodium.cryptoSecretBoxOpenEasy(hexEncodedCiphertext, nonce, Key.fromHexString(key))
} catch (e: SodiumException) {
deferred.reject(Error.DecryptionFailed)
return@success
}
sessionIDs.add(sessionID)
} else { } else {
val hexEncodedNonce = intermediate["nonce"] as? String
if (hexEncodedNonce == null) {
deferred.reject(Error.Generic)
return@success
}
val nonce = Hex.fromStringCondensed(hexEncodedNonce)
val key = ByteArray(GenericHash.BYTES)
if (!sodium.cryptoGenericHash(key, key.size, nameAsData, nameAsData.size.toLong(), nameHash, nameHash.size)) {
deferred.reject(Error.HashingFailed)
return@success
}
val sessionID: String
try {
sessionID = sodium.decrypt(hexEncodedCiphertext, null, nonce, Key.fromBytes(key), AEAD.Method.CHACHA20_POLY1305_IETF)
} catch (e: Exception) {
deferred.reject(Error.DecryptionFailed)
return@success
}
sessionIDs.add(sessionID)
} }
} else { } else {
deferred.reject(Error.Generic) deferred.reject(Error.Generic)
return@success
} }
} }
if (sessionIDs.size == validationCount && sessionIDs.toSet().size == 1) {
deferred.resolve(sessionIDs.first())
} else {
deferred.reject(Error.ValidationFailed)
}
} }
return promise return promise
} }