mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-27 02:07:42 +00:00
fix: Authenticate all Open Group API calls
* Use unblinded authentication when we have `capabilities` data for the open group server we are sending the request to but don't have the `blind` capability * Use blinded authentication when we haven't gotten any `capabilities` for an open group server, or if we have `capabilities` and the server has the `blind` capability
This commit is contained in:
parent
1dbcffe40b
commit
31d388e00b
@ -41,7 +41,7 @@ class BackgroundGroupAddJob(val joinUrl: String): Job {
|
|||||||
}
|
}
|
||||||
// get image
|
// get image
|
||||||
storage.setOpenGroupPublicKey(openGroup.server, openGroup.serverPublicKey)
|
storage.setOpenGroupPublicKey(openGroup.server, openGroup.serverPublicKey)
|
||||||
val (capabilities, info) = OpenGroupApi.getCapabilitiesAndRoomInfo(openGroup.room, openGroup.server, false).get()
|
val (capabilities, info) = OpenGroupApi.getCapabilitiesAndRoomInfo(openGroup.room, openGroup.server).get()
|
||||||
storage.setServerCapabilities(openGroup.server, capabilities.capabilities)
|
storage.setServerCapabilities(openGroup.server, capabilities.capabilities)
|
||||||
val imageId = info.imageId
|
val imageId = info.imageId
|
||||||
storage.addOpenGroup(openGroup.joinUrl())
|
storage.addOpenGroup(openGroup.joinUrl())
|
||||||
|
@ -255,7 +255,6 @@ object OpenGroupApi {
|
|||||||
val queryParameters: Map<String, String> = mapOf(),
|
val queryParameters: Map<String, String> = mapOf(),
|
||||||
val parameters: Any? = null,
|
val parameters: Any? = null,
|
||||||
val headers: Map<String, String> = mapOf(),
|
val headers: Map<String, String> = mapOf(),
|
||||||
val isAuthRequired: Boolean = true,
|
|
||||||
val body: ByteArray? = null,
|
val body: ByteArray? = null,
|
||||||
/**
|
/**
|
||||||
* Always `true` under normal circumstances. You might want to disable
|
* Always `true` under normal circumstances. You might want to disable
|
||||||
@ -301,73 +300,71 @@ object OpenGroupApi {
|
|||||||
?: return Promise.ofFail(Error.NoEd25519KeyPair)
|
?: return Promise.ofFail(Error.NoEd25519KeyPair)
|
||||||
val urlRequest = urlBuilder.toString()
|
val urlRequest = urlBuilder.toString()
|
||||||
val headers = request.headers.toMutableMap()
|
val headers = request.headers.toMutableMap()
|
||||||
if (request.isAuthRequired) {
|
val nonce = sodium.nonce(16)
|
||||||
val nonce = sodium.nonce(16)
|
val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
|
||||||
val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
|
var pubKey = ""
|
||||||
var pubKey = ""
|
var signature = ByteArray(Sign.BYTES)
|
||||||
var signature = ByteArray(Sign.BYTES)
|
var bodyHash = ByteArray(0)
|
||||||
var bodyHash = ByteArray(0)
|
if (request.parameters != null) {
|
||||||
if (request.parameters != null) {
|
val parameterBytes = JsonUtil.toJson(request.parameters).toByteArray()
|
||||||
val parameterBytes = JsonUtil.toJson(request.parameters).toByteArray()
|
val parameterHash = ByteArray(GenericHash.BYTES_MAX)
|
||||||
val parameterHash = ByteArray(GenericHash.BYTES_MAX)
|
if (sodium.cryptoGenericHash(
|
||||||
if (sodium.cryptoGenericHash(
|
parameterHash,
|
||||||
parameterHash,
|
parameterHash.size,
|
||||||
parameterHash.size,
|
parameterBytes,
|
||||||
parameterBytes,
|
parameterBytes.size.toLong()
|
||||||
parameterBytes.size.toLong()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
bodyHash = parameterHash
|
|
||||||
}
|
|
||||||
} else if (request.body != null) {
|
|
||||||
val byteHash = ByteArray(GenericHash.BYTES_MAX)
|
|
||||||
if (sodium.cryptoGenericHash(
|
|
||||||
byteHash,
|
|
||||||
byteHash.size,
|
|
||||||
request.body,
|
|
||||||
request.body.size.toLong()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
bodyHash = byteHash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val messageBytes = Hex.fromStringCondensed(publicKey)
|
|
||||||
.plus(nonce)
|
|
||||||
.plus("$timestamp".toByteArray(Charsets.US_ASCII))
|
|
||||||
.plus(request.verb.rawValue.toByteArray())
|
|
||||||
.plus("/${request.endpoint.value}".toByteArray())
|
|
||||||
.plus(bodyHash)
|
|
||||||
if (serverCapabilities.contains(Capability.BLIND.name.lowercase())) {
|
|
||||||
SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair ->
|
|
||||||
pubKey = SessionId(
|
|
||||||
IdPrefix.BLINDED,
|
|
||||||
keyPair.publicKey.asBytes
|
|
||||||
).hexString
|
|
||||||
|
|
||||||
signature = SodiumUtilities.sogsSignature(
|
|
||||||
messageBytes,
|
|
||||||
ed25519KeyPair.secretKey.asBytes,
|
|
||||||
keyPair.secretKey.asBytes,
|
|
||||||
keyPair.publicKey.asBytes
|
|
||||||
) ?: return Promise.ofFail(Error.SigningFailed)
|
|
||||||
} ?: return Promise.ofFail(Error.SigningFailed)
|
|
||||||
} else {
|
|
||||||
pubKey = SessionId(
|
|
||||||
IdPrefix.UN_BLINDED,
|
|
||||||
ed25519KeyPair.publicKey.asBytes
|
|
||||||
).hexString
|
|
||||||
sodium.cryptoSignDetached(
|
|
||||||
signature,
|
|
||||||
messageBytes,
|
|
||||||
messageBytes.size.toLong(),
|
|
||||||
ed25519KeyPair.secretKey.asBytes
|
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
|
bodyHash = parameterHash
|
||||||
|
}
|
||||||
|
} else if (request.body != null) {
|
||||||
|
val byteHash = ByteArray(GenericHash.BYTES_MAX)
|
||||||
|
if (sodium.cryptoGenericHash(
|
||||||
|
byteHash,
|
||||||
|
byteHash.size,
|
||||||
|
request.body,
|
||||||
|
request.body.size.toLong()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
bodyHash = byteHash
|
||||||
}
|
}
|
||||||
headers["X-SOGS-Nonce"] = encodeBytes(nonce)
|
|
||||||
headers["X-SOGS-Timestamp"] = "$timestamp"
|
|
||||||
headers["X-SOGS-Pubkey"] = pubKey
|
|
||||||
headers["X-SOGS-Signature"] = encodeBytes(signature)
|
|
||||||
}
|
}
|
||||||
|
val messageBytes = Hex.fromStringCondensed(publicKey)
|
||||||
|
.plus(nonce)
|
||||||
|
.plus("$timestamp".toByteArray(Charsets.US_ASCII))
|
||||||
|
.plus(request.verb.rawValue.toByteArray())
|
||||||
|
.plus("/${request.endpoint.value}".toByteArray())
|
||||||
|
.plus(bodyHash)
|
||||||
|
if (serverCapabilities.isEmpty() || serverCapabilities.contains(Capability.BLIND.name.lowercase())) {
|
||||||
|
SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair ->
|
||||||
|
pubKey = SessionId(
|
||||||
|
IdPrefix.BLINDED,
|
||||||
|
keyPair.publicKey.asBytes
|
||||||
|
).hexString
|
||||||
|
|
||||||
|
signature = SodiumUtilities.sogsSignature(
|
||||||
|
messageBytes,
|
||||||
|
ed25519KeyPair.secretKey.asBytes,
|
||||||
|
keyPair.secretKey.asBytes,
|
||||||
|
keyPair.publicKey.asBytes
|
||||||
|
) ?: return Promise.ofFail(Error.SigningFailed)
|
||||||
|
} ?: return Promise.ofFail(Error.SigningFailed)
|
||||||
|
} else {
|
||||||
|
pubKey = SessionId(
|
||||||
|
IdPrefix.UN_BLINDED,
|
||||||
|
ed25519KeyPair.publicKey.asBytes
|
||||||
|
).hexString
|
||||||
|
sodium.cryptoSignDetached(
|
||||||
|
signature,
|
||||||
|
messageBytes,
|
||||||
|
messageBytes.size.toLong(),
|
||||||
|
ed25519KeyPair.secretKey.asBytes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
headers["X-SOGS-Nonce"] = encodeBytes(nonce)
|
||||||
|
headers["X-SOGS-Timestamp"] = "$timestamp"
|
||||||
|
headers["X-SOGS-Pubkey"] = pubKey
|
||||||
|
headers["X-SOGS-Signature"] = encodeBytes(signature)
|
||||||
|
|
||||||
val requestBuilder = okhttp3.Request.Builder()
|
val requestBuilder = okhttp3.Request.Builder()
|
||||||
.url(urlRequest)
|
.url(urlRequest)
|
||||||
@ -794,16 +791,14 @@ object OpenGroupApi {
|
|||||||
|
|
||||||
private fun sequentialBatch(
|
private fun sequentialBatch(
|
||||||
server: String,
|
server: String,
|
||||||
requests: MutableList<BatchRequestInfo<*>>,
|
requests: MutableList<BatchRequestInfo<*>>
|
||||||
authRequired: Boolean = true
|
|
||||||
): Promise<List<BatchResponse<*>>, Exception> {
|
): Promise<List<BatchResponse<*>>, Exception> {
|
||||||
val request = Request(
|
val request = Request(
|
||||||
verb = POST,
|
verb = POST,
|
||||||
room = null,
|
room = null,
|
||||||
server = server,
|
server = server,
|
||||||
endpoint = Endpoint.Sequence,
|
endpoint = Endpoint.Sequence,
|
||||||
parameters = requests.map { it.request },
|
parameters = requests.map { it.request }
|
||||||
isAuthRequired = authRequired
|
|
||||||
)
|
)
|
||||||
return getBatchResponseJson(request, requests)
|
return getBatchResponseJson(request, requests)
|
||||||
}
|
}
|
||||||
@ -904,7 +899,7 @@ object OpenGroupApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCapabilities(server: String): Promise<Capabilities, Exception> {
|
fun getCapabilities(server: String): Promise<Capabilities, Exception> {
|
||||||
val request = Request(verb = GET, room = null, server = server, endpoint = Endpoint.Capabilities, isAuthRequired = false)
|
val request = Request(verb = GET, room = null, server = server, endpoint = Endpoint.Capabilities)
|
||||||
return getResponseBody(request).map { response ->
|
return getResponseBody(request).map { response ->
|
||||||
JsonUtil.fromJson(response, Capabilities::class.java)
|
JsonUtil.fromJson(response, Capabilities::class.java)
|
||||||
}
|
}
|
||||||
@ -912,8 +907,7 @@ object OpenGroupApi {
|
|||||||
|
|
||||||
fun getCapabilitiesAndRoomInfo(
|
fun getCapabilitiesAndRoomInfo(
|
||||||
room: String,
|
room: String,
|
||||||
server: String,
|
server: String
|
||||||
authRequired: Boolean = true
|
|
||||||
): Promise<Pair<Capabilities, RoomInfo>, Exception> {
|
): Promise<Pair<Capabilities, RoomInfo>, Exception> {
|
||||||
val requests = mutableListOf<BatchRequestInfo<*>>(
|
val requests = mutableListOf<BatchRequestInfo<*>>(
|
||||||
BatchRequestInfo(
|
BatchRequestInfo(
|
||||||
@ -933,7 +927,7 @@ object OpenGroupApi {
|
|||||||
responseType = object : TypeReference<RoomInfo>(){}
|
responseType = object : TypeReference<RoomInfo>(){}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return sequentialBatch(server, requests, authRequired).map {
|
return sequentialBatch(server, requests).map {
|
||||||
val capabilities = it.firstOrNull()?.body as? Capabilities ?: throw Error.ParsingFailed
|
val capabilities = it.firstOrNull()?.body as? Capabilities ?: throw Error.ParsingFailed
|
||||||
val roomInfo = it.lastOrNull()?.body as? RoomInfo ?: throw Error.ParsingFailed
|
val roomInfo = it.lastOrNull()?.body as? RoomInfo ?: throw Error.ParsingFailed
|
||||||
capabilities to roomInfo
|
capabilities to roomInfo
|
||||||
|
Loading…
x
Reference in New Issue
Block a user