mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-20 20:28:42 +00:00
Add Session Id blinding (#862)
* feat: Add Session Id blinding
Including modified version of lazysodium-android to expose missing libsodium functions, we could build from a fork which we still need to setup.
* Add v4 onion request handling
* Update SOGS signature construction
* Fix SOGS signature construction
* Update onion request
* Update signature data
* Keep path prefixes for v4 endpoints
* Update SOGS signature message
* Rename to remove api version suffix
* Update onion response parsing
* Refactor file download paths
* Implement request batching
* Refactor batch response handling
* Handle batch endpoint responses
* Update batch endpoint responses
* Update attachment download handling
* Handle file downloads
* Handle inbox messages
* Fix issue with file downloads
* Preserve image bytearray encoding
* Refactor
* Open group message requests
* Check id blinding in user detail bottom sheet rather
* Message validation refactor
* Cache last inbox/outbox server ids
* Update message encryption/decryption
* Refactor
* Refactor
* Bypass user details bottom sheet in open groups for blinded session ids
* Fix capabilities call auth
* Refactor
* Revert default server details
* Update sodium dependency to forked repo
* Fix attachment upload
* Revert "Update sodium dependency to forked repo"
This reverts commit c7db9529f9
.
* Add signed sodium lib
* Update contact id truncation and mention logic
* Open group inbox messaging fix
* Refactor
* Update blinded id check
* Fix open group message sends
* Fix crash on open group direct message send
* Direct message refactor
* Direct message encrypt/decrypt fixes
* Use updated curve25519 version
* Updated lazysodium dependency
* Update encryption/decryption calls
* Handle direct message parse errors
* Minor refactor
* Existing chat refactor
* Update encryption & decryption parameters
* Fix authenticated ciphertext size
* Set direct message sync target
* Update direct message thread lookup
* Add blinded id mapping table
* Add blinded id mapping table
* Update threads after sends
* Update open group message timestamp handling
* Filter unblinded contacts
* Format blinded id mentions
* Add message deleted field
* Hide open group inbox id
* Update message request response handling
* Update message request response sender handling
* Fix mentions of blinded ids
* Handle open group poll failure
* fix: add log for failed open group onion request, add decoding body for blinding required error at destination
* fix: change the error check
* Persist group members
* Reschedule polling after capabilities update
* Retry on other exceptions
* Minor refactor
* Open group profile fix
* Group member db schema update
* Fix ban request key
* Update ban response type
* Ban endpoint updates
* Ban endpoint updates
* Delete messages
Co-authored-by: charles <charles@oxen.io>
Co-authored-by: jubb <hjubb@users.noreply.github.com>
This commit is contained in:
@@ -74,26 +74,26 @@ object HTTP {
|
||||
/**
|
||||
* Sync. Don't call from the main thread.
|
||||
*/
|
||||
fun execute(verb: Verb, url: String, timeout: Long = HTTP.timeout, useSeedNodeConnection: Boolean = false): Map<*, *> {
|
||||
fun execute(verb: Verb, url: String, timeout: Long = HTTP.timeout, useSeedNodeConnection: Boolean = false): ByteArray {
|
||||
return execute(verb = verb, url = url, body = null, timeout = timeout, useSeedNodeConnection = useSeedNodeConnection)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync. Don't call from the main thread.
|
||||
*/
|
||||
fun execute(verb: Verb, url: String, parameters: Map<String, Any>?, timeout: Long = HTTP.timeout, useSeedNodeConnection: Boolean = false): Map<*, *> {
|
||||
if (parameters != null) {
|
||||
fun execute(verb: Verb, url: String, parameters: Map<String, Any>?, timeout: Long = HTTP.timeout, useSeedNodeConnection: Boolean = false): ByteArray {
|
||||
return if (parameters != null) {
|
||||
val body = JsonUtil.toJson(parameters).toByteArray()
|
||||
return execute(verb = verb, url = url, body = body, timeout = timeout, useSeedNodeConnection = useSeedNodeConnection)
|
||||
execute(verb = verb, url = url, body = body, timeout = timeout, useSeedNodeConnection = useSeedNodeConnection)
|
||||
} else {
|
||||
return execute(verb = verb, url = url, body = null, timeout = timeout, useSeedNodeConnection = useSeedNodeConnection)
|
||||
execute(verb = verb, url = url, body = null, timeout = timeout, useSeedNodeConnection = useSeedNodeConnection)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync. Don't call from the main thread.
|
||||
*/
|
||||
fun execute(verb: Verb, url: String, body: ByteArray?, timeout: Long = HTTP.timeout, useSeedNodeConnection: Boolean = false): Map<*, *> {
|
||||
fun execute(verb: Verb, url: String, body: ByteArray?, timeout: Long = HTTP.timeout, useSeedNodeConnection: Boolean = false): ByteArray {
|
||||
val request = Request.Builder().url(url)
|
||||
.removeHeader("User-Agent").addHeader("User-Agent", "WhatsApp") // Set a fake value
|
||||
.removeHeader("Accept-Language").addHeader("Accept-Language", "en-us") // Set a fake value
|
||||
@@ -109,14 +109,13 @@ object HTTP {
|
||||
}
|
||||
lateinit var response: Response
|
||||
try {
|
||||
val connection: OkHttpClient
|
||||
if (timeout != HTTP.timeout) { // Custom timeout
|
||||
val connection = if (timeout != HTTP.timeout) { // Custom timeout
|
||||
if (useSeedNodeConnection) {
|
||||
throw IllegalStateException("Setting a custom timeout is only allowed for requests to snodes.")
|
||||
}
|
||||
connection = getDefaultConnection(timeout)
|
||||
getDefaultConnection(timeout)
|
||||
} else {
|
||||
connection = if (useSeedNodeConnection) seedNodeConnection else defaultConnection
|
||||
if (useSeedNodeConnection) seedNodeConnection else defaultConnection
|
||||
}
|
||||
response = connection.newCall(request.build()).execute()
|
||||
} catch (exception: Exception) {
|
||||
@@ -124,14 +123,9 @@ object HTTP {
|
||||
// Override the actual error so that we can correctly catch failed requests in OnionRequestAPI
|
||||
throw HTTPRequestFailedException(0, null)
|
||||
}
|
||||
when (val statusCode = response.code()) {
|
||||
return when (val statusCode = response.code()) {
|
||||
200 -> {
|
||||
val bodyAsString = response.body()?.string() ?: throw Exception("An error occurred.")
|
||||
try {
|
||||
return JsonUtil.fromJson(bodyAsString, Map::class.java)
|
||||
} catch (exception: Exception) {
|
||||
return mapOf( "result" to bodyAsString)
|
||||
}
|
||||
response.body()?.bytes() ?: throw Exception("An error occurred.")
|
||||
}
|
||||
else -> {
|
||||
Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.")
|
||||
|
@@ -0,0 +1,15 @@
|
||||
package org.session.libsignal.utilities
|
||||
|
||||
enum class IdPrefix(val value: String) {
|
||||
STANDARD("05"), BLINDED("15"), UN_BLINDED("00");
|
||||
|
||||
companion object {
|
||||
fun fromValue(rawValue: String): IdPrefix? = when(rawValue.take(2)) {
|
||||
STANDARD.value -> STANDARD
|
||||
BLINDED.value -> BLINDED
|
||||
UN_BLINDED.value -> UN_BLINDED
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package org.session.libsignal.utilities;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@@ -30,6 +31,10 @@ public class JsonUtil {
|
||||
return fromJson(new String(serialized), clazz);
|
||||
}
|
||||
|
||||
public static <T> T fromJson(String serialized, TypeReference<T> typeReference) throws IOException {
|
||||
return objectMapper.readValue(serialized, typeReference);
|
||||
}
|
||||
|
||||
public static <T> T fromJson(String serialized, Class<T> clazz) throws IOException {
|
||||
return objectMapper.readValue(serialized, clazz);
|
||||
}
|
||||
|
@@ -1,12 +1,10 @@
|
||||
package org.session.libsignal.utilities
|
||||
|
||||
import org.session.libsignal.utilities.Hex
|
||||
|
||||
fun String.removing05PrefixIfNeeded(): String {
|
||||
return if (length == 66) removePrefix("05") else this
|
||||
fun String.removingIdPrefixIfNeeded(): String {
|
||||
return if (length == 66 && IdPrefix.fromValue(this) != null) removeRange(0..1) else this
|
||||
}
|
||||
|
||||
fun ByteArray.removing05PrefixIfNeeded(): ByteArray {
|
||||
val string = Hex.toStringCondensed(this).removing05PrefixIfNeeded()
|
||||
fun ByteArray.removingIdPrefixIfNeeded(): ByteArray {
|
||||
val string = Hex.toStringCondensed(this).removingIdPrefixIfNeeded()
|
||||
return Hex.fromStringCondensed(string)
|
||||
}
|
||||
|
@@ -10,9 +10,9 @@ object PublicKeyValidation {
|
||||
@JvmStatic
|
||||
fun isValid(candidate: String, expectedLength: Int, isPrefixRequired: Boolean): Boolean {
|
||||
val hexCharacters = "0123456789ABCDEF".toSet()
|
||||
val isValidHexEncoding = hexCharacters.containsAll(candidate.toUpperCase().toSet())
|
||||
val isValidHexEncoding = hexCharacters.containsAll(candidate.uppercase().toSet())
|
||||
val hasValidLength = candidate.length == expectedLength
|
||||
val hasValidPrefix = if (isPrefixRequired) candidate.startsWith("05") else true
|
||||
val hasValidPrefix = if (isPrefixRequired) IdPrefix.fromValue(candidate) != null else true
|
||||
return isValidHexEncoding && hasValidLength && hasValidPrefix
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user