feat: handling responses and using new updated params

This commit is contained in:
jubb 2021-06-21 15:48:42 +10:00
parent fdc042e6d4
commit 05b0e5f308
3 changed files with 69 additions and 61 deletions

View File

@ -76,30 +76,37 @@ class ClearAllDataDialog(val deleteNetworkMessages: Boolean) : DialogFragment()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Loki", "Failed to force sync", e) Log.e("Loki", "Failed to force sync", e)
withContext(Dispatchers.Main) {
updateUI(false)
}
} }
} else { } else {
// finish // finish
val promises = SnodeAPI.deleteAllMessages(requireContext()).get() val promises = try {
SnodeAPI.deleteAllMessages(requireContext()).get()
} catch (e: Exception) {
null
}
val rawResponses = promises.map { val rawResponses = promises?.map {
try { try {
it.get() it.get()
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
} } ?: listOf(null)
// TODO: process the responses here // TODO: process the responses here
if (rawResponses.any { it == null || it["failed"] as? Boolean == true }) { if (rawResponses.all { it != null }) {
// didn't succeed (at least one)
withContext(Dispatchers.Main) {
updateUI(false)
}
} else {
// don't force sync because all the messages are deleted? // don't force sync because all the messages are deleted?
ApplicationContext.getInstance(context).clearAllData(false) ApplicationContext.getInstance(context).clearAllData(false)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
dismiss() dismiss()
} }
} else if (rawResponses.any { it == null || it["failed"] as? Boolean == true }) {
// didn't succeed (at least one)
withContext(Dispatchers.Main) {
updateUI(false)
}
} }
} }
} }

View File

@ -312,41 +312,35 @@ object SnodeAPI {
} }
} }
/** Deletes all messages owned by the given pubkey on this SN and broadcasts the delete request to
* all other swarm members.
* Returns dict of:
* - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:
* - "failed" and other failure keys -- see `recursive`.
* - "deleted": hashes of deleted messages.
* - "signature": signature of ( PUBKEY_HEX || TIMESTAMP || DELETEDHASH[0] || ... || DELETEDHASH[N] ), signed
* by the node's ed25519 pubkey.
*/
fun deleteAllMessages(context: Context): Promise<Set<RawResponsePromise>, Exception> { fun deleteAllMessages(context: Context): Promise<Set<RawResponsePromise>, Exception> {
return retryIfNeeded(1) { return retryIfNeeded(maxRetryCount) {
// considerations: timestamp off in retrying logic, not being able to re-sign with latest timestamp? do we just not retry this as it will be synchronous // considerations: timestamp off in retrying logic, not being able to re-sign with latest timestamp? do we just not retry this as it will be synchronous
val ed = KeyPairUtilities.getUserED25519KeyPair(context) ?: return@retryIfNeeded Promise.ofFail(Error.Generic) val userED25519KeyPair = KeyPairUtilities.getUserED25519KeyPair(context) ?: return@retryIfNeeded Promise.ofFail(Error.Generic)
val xPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey
val userKeyPair = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair()
val userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.Generic) val userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() ?: return@retryIfNeeded Promise.ofFail(Error.Generic)
val destination = if (useTestnet) userPublicKey.removing05PrefixIfNeeded() else userPublicKey val destination = if (useTestnet) userPublicKey.removing05PrefixIfNeeded() else userPublicKey
getTargetSnodes(destination).map { swarm -> getSwarm(destination).map { swarm ->
swarm.map { snode -> val promise = swarm.first().let { snode ->
retryIfNeeded(1) { retryIfNeeded(maxRetryCount) {
getNetworkTime(snode).bind { (_, timestamp) -> getNetworkTime(snode).bind { (_, timestamp) ->
val signature = ByteArray(Sign.BYTES) val signature = ByteArray(Sign.BYTES)
val data = Snode.Method.DeleteAll.rawValue.toByteArray() + timestamp.toString().toByteArray() val data = (Snode.Method.DeleteAll.rawValue + timestamp.toString()).toByteArray()
val signed = sodium.cryptoSignDetached(signature, data, data.size.toLong(), xPrivateKey.serialize()) sodium.cryptoSignDetached(signature, data, data.size.toLong(), userED25519KeyPair.secretKey.asBytes)
val deleteMessage = SnodeDeleteMessage(userPublicKey, timestamp, Base64.encodeBytes(signature)) val deleteMessageParams = mapOf(
val parameters = deleteMessage.toJSON() "pubkey" to userPublicKey,
invoke(Snode.Method.DeleteAll, snode, destination, parameters).fail { e -> "pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString,
"timestamp" to timestamp,
"signature" to Base64.encodeBytes(signature)
)
invoke(Snode.Method.DeleteAll, snode, destination, deleteMessageParams).map { rawResponse -> parseDeletions(timestamp, rawResponse) }.fail { e ->
Log.e("Loki", "Failed to clear data",e) Log.e("Loki", "Failed to clear data",e)
} }
} }
} }
}.toSet() }
setOf(promise)
} }
} }
} }
@ -433,6 +427,43 @@ object SnodeAPI {
} }
} }
} }
@Suppress("UNCHECKED_CAST")
private fun parseDeletions(timestamp: Long, rawResponse: RawResponse): Map<String, Any> {
val swarms = rawResponse["swarms"] as? Map<String,Any> ?: return mapOf()
val swarmResponsesValid = swarms.mapNotNull { (nodePubKeyHex, rawMap) ->
val map = rawMap as? Map<String,Any> ?: return@mapNotNull null
/** Deletes all messages owned by the given pubkey on this SN and broadcasts the delete request to
* all other swarm members.
* Returns dict of:
* - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:
* - "failed" and other failure keys -- see `recursive`.
* - "deleted": hashes of deleted messages.
* - "signature": signature of ( PUBKEY_HEX || TIMESTAMP || DELETEDHASH[0] || ... || DELETEDHASH[N] ), signed
* by the node's ed25519 pubkey.
*/
// failure
val failed = map["failed"] as? Boolean ?: false
val code = map["code"] as? String
val reason = map["reason"] as? String
nodePubKeyHex to if (failed) {
// return error probs
false
} else {
// success
val deleted = map["deleted"] as List<String> // list of deleted hashes
Log.d("Loki", "node $nodePubKeyHex deleted ${deleted.size} messages")
val signature = map["signature"] as String
val nodePubKeyBytes = Hex.fromStringCondensed(nodePubKeyHex)
// signature of ( PUBKEY_HEX || TIMESTAMP || DELETEDHASH[0] || ... || DELETEDHASH[N] )
val message = (signature + timestamp + deleted.fold("") { a, v -> a+v }).toByteArray()
sodium.cryptoSignVerifyDetached(Base64.decode(signature), message, message.size, nodePubKeyBytes)
}
}
return swarmResponsesValid.toMap()
}
// endregion // endregion
// Error Handling // Error Handling

View File

@ -1,30 +0,0 @@
package org.session.libsession.snode
import org.session.libsignal.utilities.removing05PrefixIfNeeded
data class SnodeDeleteMessage(
/**
* The hex encoded public key of the user.
*/
val pubKey: String,
/**
* The timestamp at which this request was initiated, in milliseconds since unix epoch.
* Must be within Must be within ±60s of the current time.
* (For clients it is recommended to retrieve a timestamp via `info` first, to avoid client time sync issues).
*/
val timestamp: Long,
/**
* a Base64-encoded signature of ( "delete_all" || timestamp ), signed by the pubKey
*/
val signature: String,
) {
internal fun toJSON(): Map<String, Any> {
return mapOf(
"pubkey" to pubKey,
"timestamp" to timestamp,
"signature" to signature
)
}
}