mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-23 01:51:30 +00:00
Update SOGS signature message
This commit is contained in:
parent
52b222cf8e
commit
0674e8090d
@ -9,19 +9,22 @@ import org.junit.Test
|
|||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
|
import org.session.libsignal.utilities.Hex
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class SodiumUtilitiesTest {
|
class SodiumUtilitiesTest {
|
||||||
|
|
||||||
private val serverPublicKey = "c3b3c6f32f0ab5a57f853cc4f30f5da7fda5624b0c77b3fb0829de562ada081d"
|
private val serverPublicKey = "c3b3c6f32f0ab5a57f853cc4f30f5da7fda5624b0c77b3fb0829de562ada081d"
|
||||||
private val pubKey = Key.fromHexString("bac6e71efd7dfa4a83c98ed24f254ab2c267f9ccdb172a5280a0444ad24e89cc")
|
private val edKeyPair = KeyPair(
|
||||||
private val secKey = Key.fromHexString("c010d89eccbaf5d1c6d19df766c6eedf965d4a28a56f87c9fc819edb59896dd9")
|
Key.fromHexString("bac6e71efd7dfa4a83c98ed24f254ab2c267f9ccdb172a5280a0444ad24e89cc"),
|
||||||
|
Key.fromHexString("c010d89eccbaf5d1c6d19df766c6eedf965d4a28a56f87c9fc819edb59896dd9")
|
||||||
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun blindedKeyPair() {
|
fun blindedKeyPair() {
|
||||||
val blindedKey = "98932d4bccbe595a8789d7eb1629cefc483a0eaddc7e20e8fe5c771efafd9af5"
|
val blindedKey = "98932d4bccbe595a8789d7eb1629cefc483a0eaddc7e20e8fe5c771efafd9af5"
|
||||||
|
|
||||||
val keyPair = SodiumUtilities.blindedKeyPair(serverPublicKey, KeyPair(pubKey, secKey))!!
|
val keyPair = SodiumUtilities.blindedKeyPair(serverPublicKey, edKeyPair)!!
|
||||||
|
|
||||||
assertThat(keyPair.publicKey.asHexString.lowercase(), equalTo(blindedKey))
|
assertThat(keyPair.publicKey.asHexString.lowercase(), equalTo(blindedKey))
|
||||||
}
|
}
|
||||||
@ -29,18 +32,18 @@ class SodiumUtilitiesTest {
|
|||||||
@Test
|
@Test
|
||||||
fun sogsSignature() {
|
fun sogsSignature() {
|
||||||
val expectedSignature = "gYqpWZX6fnF4Gb2xQM3xaXs0WIYEI49+B8q4mUUEg8Rw0ObaHUWfoWjMHMArAtP9QlORfiydsKWz1o6zdPVeCQ="
|
val expectedSignature = "gYqpWZX6fnF4Gb2xQM3xaXs0WIYEI49+B8q4mUUEg8Rw0ObaHUWfoWjMHMArAtP9QlORfiydsKWz1o6zdPVeCQ="
|
||||||
val keyPair = SodiumUtilities.blindedKeyPair(serverPublicKey, KeyPair(pubKey, secKey))!!
|
val blindedKeyPair = SodiumUtilities.blindedKeyPair(serverPublicKey, edKeyPair)!!
|
||||||
|
|
||||||
val message = serverPublicKey.toByteArray()
|
val message = Hex.fromStringCondensed(serverPublicKey)
|
||||||
.plus(Base64.decode("CdB5nyKVmQGCw6s0Bvv8Ww=="))
|
.plus(Base64.decode("CdB5nyKVmQGCw6s0Bvv8Ww=="))
|
||||||
.plus("1642472103".toByteArray(Charsets.US_ASCII))
|
.plus("1642472103".toByteArray())
|
||||||
.plus("GET".toByteArray())
|
.plus("GET".toByteArray())
|
||||||
.plus("/room/sudoku/messages/recent?limit=25".toByteArray())
|
.plus("/room/sudoku/messages/recent?limit=25".toByteArray())
|
||||||
val signature = Base64.encodeBytes(SodiumUtilities.sogsSignature(
|
val signature = Base64.encodeBytes(SodiumUtilities.sogsSignature(
|
||||||
message,
|
message,
|
||||||
secKey.asBytes,
|
edKeyPair.secretKey.asBytes,
|
||||||
keyPair.secretKey.asBytes,
|
blindedKeyPair.secretKey.asBytes,
|
||||||
keyPair.publicKey.asBytes
|
blindedKeyPair.publicKey.asBytes
|
||||||
)!!)
|
)!!)
|
||||||
|
|
||||||
assertThat(signature, equalTo(expectedSignature))
|
assertThat(signature, equalTo(expectedSignature))
|
||||||
|
@ -167,7 +167,7 @@ object OpenGroupApiV4 {
|
|||||||
bodyHash = parameterHash
|
bodyHash = parameterHash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val messageBytes = publicKey.toByteArray()
|
val messageBytes = Hex.fromStringCondensed(publicKey)
|
||||||
.plus(nonce)
|
.plus(nonce)
|
||||||
.plus("$timestamp".toByteArray(Charsets.US_ASCII))
|
.plus("$timestamp".toByteArray(Charsets.US_ASCII))
|
||||||
.plus(request.verb.rawValue.toByteArray())
|
.plus(request.verb.rawValue.toByteArray())
|
||||||
|
@ -3,6 +3,7 @@ package org.session.libsession.messaging.utilities
|
|||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
import com.goterl.lazysodium.LazySodiumAndroid
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
import com.goterl.lazysodium.interfaces.GenericHash
|
import com.goterl.lazysodium.interfaces.GenericHash
|
||||||
|
import com.goterl.lazysodium.interfaces.Hash
|
||||||
import com.goterl.lazysodium.utils.Key
|
import com.goterl.lazysodium.utils.Key
|
||||||
import com.goterl.lazysodium.utils.KeyPair
|
import com.goterl.lazysodium.utils.KeyPair
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
@ -12,11 +13,11 @@ import kotlin.experimental.inv
|
|||||||
object SodiumUtilities {
|
object SodiumUtilities {
|
||||||
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
private val sodium by lazy { LazySodiumAndroid(SodiumAndroid()) }
|
||||||
|
|
||||||
private const val scalarLength: Int = 32 // crypto_core_ed25519_scalarbytes
|
private const val SCALAR_LENGTH: Int = 32 // crypto_core_ed25519_scalarbytes
|
||||||
private const val noClampLength: Int = 32 // crypto_scalarmult_ed25519_bytes
|
private const val NO_CLAMP_LENGTH: Int = 32 // crypto_scalarmult_ed25519_bytes
|
||||||
private const val scalarMultLength: Int = 32 // crypto_scalarmult_bytes
|
private const val SCALAR_MULT_LENGTH: Int = 32 // crypto_scalarmult_bytes
|
||||||
private const val publicKeyLength: Int = 32 // crypto_scalarmult_bytes
|
private const val PUBLIC_KEY_LENGTH: Int = 32 // crypto_scalarmult_bytes
|
||||||
private const val secretKeyLength: Int = 64 //crypto_sign_secretkeybytes
|
private const val SECRET_KEY_LENGTH: Int = 64 //crypto_sign_secretkeybytes
|
||||||
|
|
||||||
/* 64-byte blake2b hash then reduce to get the blinding factor */
|
/* 64-byte blake2b hash then reduce to get the blinding factor */
|
||||||
private fun generateBlindingFactor(serverPublicKey: String): ByteArray? {
|
private fun generateBlindingFactor(serverPublicKey: String): ByteArray? {
|
||||||
@ -27,7 +28,7 @@ object SodiumUtilities {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// Reduce the server public key into an ed25519 scalar (`k`)
|
// Reduce the server public key into an ed25519 scalar (`k`)
|
||||||
val x25519PublicKey = ByteArray(scalarLength)
|
val x25519PublicKey = ByteArray(SCALAR_LENGTH)
|
||||||
sodium.cryptoCoreEd25519ScalarReduce(x25519PublicKey, serverPubKeyHash)
|
sodium.cryptoCoreEd25519ScalarReduce(x25519PublicKey, serverPubKeyHash)
|
||||||
return if (x25519PublicKey.any { it.toInt() != 0 }) {
|
return if (x25519PublicKey.any { it.toInt() != 0 }) {
|
||||||
x25519PublicKey
|
x25519PublicKey
|
||||||
@ -42,7 +43,7 @@ object SodiumUtilities {
|
|||||||
*/
|
*/
|
||||||
private fun generatePrivateKeyScalar(secretKey: ByteArray): ByteArray? {
|
private fun generatePrivateKeyScalar(secretKey: ByteArray): ByteArray? {
|
||||||
// a = s.to_curve25519_private_key().encode()
|
// a = s.to_curve25519_private_key().encode()
|
||||||
val aBytes = ByteArray(scalarMultLength)
|
val aBytes = ByteArray(SCALAR_MULT_LENGTH)
|
||||||
return if (sodium.convertSecretKeyEd25519ToCurve25519(aBytes, secretKey)) {
|
return if (sodium.convertSecretKeyEd25519ToCurve25519(aBytes, secretKey)) {
|
||||||
aBytes
|
aBytes
|
||||||
} else null
|
} else null
|
||||||
@ -55,11 +56,11 @@ object SodiumUtilities {
|
|||||||
val kBytes = generateBlindingFactor(serverPublicKey)
|
val kBytes = generateBlindingFactor(serverPublicKey)
|
||||||
val aBytes = generatePrivateKeyScalar(edKeyPair.secretKey.asBytes)
|
val aBytes = generatePrivateKeyScalar(edKeyPair.secretKey.asBytes)
|
||||||
// Generate the blinded key pair `ka`, `kA`
|
// Generate the blinded key pair `ka`, `kA`
|
||||||
val kaBytes = ByteArray(secretKeyLength)
|
val kaBytes = ByteArray(SECRET_KEY_LENGTH)
|
||||||
sodium.cryptoCoreEd25519ScalarMul(kaBytes, kBytes, aBytes)
|
sodium.cryptoCoreEd25519ScalarMul(kaBytes, kBytes, aBytes)
|
||||||
if (kaBytes.all { it.toInt() == 0 }) return null
|
if (kaBytes.all { it.toInt() == 0 }) return null
|
||||||
|
|
||||||
val kABytes = ByteArray(publicKeyLength)
|
val kABytes = ByteArray(PUBLIC_KEY_LENGTH)
|
||||||
return if (sodium.cryptoScalarMultE25519BaseNoClamp(kABytes, kaBytes)) {
|
return if (sodium.cryptoScalarMultE25519BaseNoClamp(kABytes, kaBytes)) {
|
||||||
KeyPair(Key.fromBytes(kABytes), Key.fromBytes(kaBytes))
|
KeyPair(Key.fromBytes(kABytes), Key.fromBytes(kaBytes))
|
||||||
} else {
|
} else {
|
||||||
@ -79,44 +80,54 @@ object SodiumUtilities {
|
|||||||
blindedPublicKey: ByteArray /*kA*/
|
blindedPublicKey: ByteArray /*kA*/
|
||||||
): ByteArray? {
|
): ByteArray? {
|
||||||
// H_rh = sha512(s.encode()).digest()[32:]
|
// H_rh = sha512(s.encode()).digest()[32:]
|
||||||
val h_rh = ByteArray(GenericHash.BYTES)
|
val digest = ByteArray(Hash.SHA512_BYTES)
|
||||||
if (!sodium.cryptoHashSha512(h_rh, secretKey, secretKey.size.toLong())) return null
|
val h_rh = if (sodium.cryptoHashSha512(digest, secretKey, secretKey.size.toLong())) {
|
||||||
|
digest.takeLast(32).toByteArray()
|
||||||
|
} else return null
|
||||||
|
|
||||||
// r = salt.crypto_core_ed25519_scalar_reduce(sha512_multipart(H_rh, kA, message_parts))
|
// r = salt.crypto_core_ed25519_scalar_reduce(sha512_multipart(H_rh, kA, message_parts))
|
||||||
val combinedData = h_rh + blindedPublicKey + message
|
val rHash = sha512Multipart(listOf(h_rh, blindedPublicKey, message)) ?: return null
|
||||||
val combinedHash = ByteArray(GenericHash.BYTES)
|
val r = ByteArray(SCALAR_LENGTH)
|
||||||
if (!sodium.cryptoHashSha512(combinedHash, combinedData, combinedData.size.toLong())) return null
|
sodium.cryptoCoreEd25519ScalarReduce(r, rHash)
|
||||||
val rHash = ByteArray(scalarLength)
|
if (r.all { it.toInt() == 0 }) return null
|
||||||
sodium.cryptoCoreEd25519ScalarReduce(rHash, combinedHash)
|
|
||||||
if (rHash.all { it.toInt() == 0 }) return null
|
|
||||||
|
|
||||||
// sig_R = salt.crypto_scalarmult_ed25519_base_noclamp(r)
|
// sig_R = salt.crypto_scalarmult_ed25519_base_noclamp(r)
|
||||||
val sig_R = ByteArray(noClampLength)
|
val sig_R = ByteArray(NO_CLAMP_LENGTH)
|
||||||
if (!sodium.cryptoScalarMultE25519BaseNoClamp(sig_R, rHash)) return null
|
if (!sodium.cryptoScalarMultE25519BaseNoClamp(sig_R, r)) return null
|
||||||
|
|
||||||
// HRAM = salt.crypto_core_ed25519_scalar_reduce(sha512_multipart(sig_R, kA, message_parts))
|
// HRAM = salt.crypto_core_ed25519_scalar_reduce(sha512_multipart(sig_R, kA, message_parts))
|
||||||
val hRamData = sig_R + blindedPublicKey + message
|
val hRamHash = sha512Multipart(listOf(sig_R, blindedPublicKey, message)) ?: return null
|
||||||
val hRamHash = ByteArray(scalarLength)
|
val hRam = ByteArray(SCALAR_LENGTH)
|
||||||
if (!sodium.cryptoHashSha512(hRamHash, hRamData, hRamData.size.toLong())) return null
|
|
||||||
val hRam = ByteArray(scalarLength)
|
|
||||||
sodium.cryptoCoreEd25519ScalarReduce(hRam, hRamHash)
|
sodium.cryptoCoreEd25519ScalarReduce(hRam, hRamHash)
|
||||||
if (hRam.all { it.toInt() == 0 }) return null
|
if (hRam.all { it.toInt() == 0 }) return null
|
||||||
|
|
||||||
// sig_s = salt.crypto_core_ed25519_scalar_add(r, salt.crypto_core_ed25519_scalar_mul(HRAM, ka))
|
// sig_s = salt.crypto_core_ed25519_scalar_add(r, salt.crypto_core_ed25519_scalar_mul(HRAM, ka))
|
||||||
val sig_sMul = ByteArray(scalarLength)
|
val sig_sMul = ByteArray(SCALAR_LENGTH)
|
||||||
val sig_s = ByteArray(scalarLength)
|
val sig_s = ByteArray(SCALAR_LENGTH)
|
||||||
sodium.cryptoCoreEd25519ScalarMul(sig_sMul, hRam, blindedSecretKey)
|
sodium.cryptoCoreEd25519ScalarMul(sig_sMul, hRam, blindedSecretKey)
|
||||||
if (sig_sMul.any { it.toInt() != 0 }) {
|
if (sig_sMul.any { it.toInt() != 0 }) {
|
||||||
sodium.cryptoCoreEd25519ScalarAdd(sig_s, rHash, sig_sMul)
|
sodium.cryptoCoreEd25519ScalarAdd(sig_s, r, sig_sMul)
|
||||||
if (sig_s.all { it.toInt() == 0 }) return null
|
if (sig_s.all { it.toInt() == 0 }) return null
|
||||||
} else return null
|
} else return null
|
||||||
|
|
||||||
return sig_R + sig_s
|
return sig_R + sig_s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sha512Multipart(parts: List<ByteArray>): ByteArray? {
|
||||||
|
val state = Hash.State512()
|
||||||
|
sodium.cryptoHashSha512Init(state)
|
||||||
|
parts.forEach {
|
||||||
|
sodium.cryptoHashSha512Update(state, it, it.size.toLong())
|
||||||
|
}
|
||||||
|
val finalHash = ByteArray(Hash.SHA512_BYTES)
|
||||||
|
return if (sodium.cryptoHashSha512Final(state, finalHash)) {
|
||||||
|
finalHash
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
/* Combines two keys (`kA`) */
|
/* Combines two keys (`kA`) */
|
||||||
private fun combineKeys(lhsKey: ByteArray, rhsKey: ByteArray): ByteArray? {
|
private fun combineKeys(lhsKey: ByteArray, rhsKey: ByteArray): ByteArray? {
|
||||||
val kA = ByteArray(noClampLength)
|
val kA = ByteArray(NO_CLAMP_LENGTH)
|
||||||
return if (sodium.cryptoScalarMultE25519NoClamp(kA, lhsKey, rhsKey)) {
|
return if (sodium.cryptoScalarMultE25519NoClamp(kA, lhsKey, rhsKey)) {
|
||||||
kA
|
kA
|
||||||
} else null
|
} else null
|
||||||
|
Loading…
x
Reference in New Issue
Block a user