Refactoring to clarify the use of crypto functions
This commit is contained in:
@@ -24,7 +24,7 @@ class CryptoUtilsTest {
|
|||||||
val privateKey = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561"
|
val privateKey = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561"
|
||||||
val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f"
|
val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f"
|
||||||
|
|
||||||
val key = CryptoUtils.getSharedSecretXChaCha(privateKey = privateKey.hexToByteArray(), pubKey = publicKey.hexToByteArray())
|
val key = CryptoUtils.getSharedSecretNIP24(privateKey = privateKey.hexToByteArray(), pubKey = publicKey.hexToByteArray())
|
||||||
|
|
||||||
assertEquals("577c966f499dddd8e8dcc34e8f352e283cc177e53ae372794947e0b8ede7cfd8", key.toHexKey())
|
assertEquals("577c966f499dddd8e8dcc34e8f352e283cc177e53ae372794947e0b8ede7cfd8", key.toHexKey())
|
||||||
}
|
}
|
||||||
@@ -34,8 +34,8 @@ class CryptoUtilsTest {
|
|||||||
val sender = KeyPair()
|
val sender = KeyPair()
|
||||||
val receiver = KeyPair()
|
val receiver = KeyPair()
|
||||||
|
|
||||||
val sharedSecret1 = CryptoUtils.getSharedSecretXChaCha(sender.privKey!!, receiver.pubKey)
|
val sharedSecret1 = CryptoUtils.getSharedSecretNIP24(sender.privKey!!, receiver.pubKey)
|
||||||
val sharedSecret2 = CryptoUtils.getSharedSecretXChaCha(receiver.privKey!!, sender.pubKey)
|
val sharedSecret2 = CryptoUtils.getSharedSecretNIP24(receiver.privKey!!, sender.pubKey)
|
||||||
|
|
||||||
assertEquals(sharedSecret1.toHexKey(), sharedSecret2.toHexKey())
|
assertEquals(sharedSecret1.toHexKey(), sharedSecret2.toHexKey())
|
||||||
|
|
||||||
@@ -53,8 +53,8 @@ class CryptoUtilsTest {
|
|||||||
val privateKey = CryptoUtils.privkeyCreate()
|
val privateKey = CryptoUtils.privkeyCreate()
|
||||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
|
|
||||||
val encrypted = CryptoUtils.encrypt(msg, privateKey, publicKey)
|
val encrypted = CryptoUtils.encryptNIP04(msg, privateKey, publicKey)
|
||||||
val decrypted = CryptoUtils.decrypt(encrypted, privateKey, publicKey)
|
val decrypted = CryptoUtils.decryptNIP04(encrypted, privateKey, publicKey)
|
||||||
|
|
||||||
assertEquals(msg, decrypted)
|
assertEquals(msg, decrypted)
|
||||||
}
|
}
|
||||||
@@ -66,8 +66,8 @@ class CryptoUtilsTest {
|
|||||||
val privateKey = CryptoUtils.privkeyCreate()
|
val privateKey = CryptoUtils.privkeyCreate()
|
||||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
|
|
||||||
val encrypted = CryptoUtils.encryptXChaCha(msg, privateKey, publicKey)
|
val encrypted = CryptoUtils.encryptNIP24(msg, privateKey, publicKey)
|
||||||
val decrypted = CryptoUtils.decryptXChaCha(encrypted, privateKey, publicKey)
|
val decrypted = CryptoUtils.decryptNIP24(encrypted, privateKey, publicKey)
|
||||||
|
|
||||||
assertEquals(msg, decrypted)
|
assertEquals(msg, decrypted)
|
||||||
}
|
}
|
||||||
@@ -78,10 +78,10 @@ class CryptoUtilsTest {
|
|||||||
|
|
||||||
val privateKey = CryptoUtils.privkeyCreate()
|
val privateKey = CryptoUtils.privkeyCreate()
|
||||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
val sharedSecret = CryptoUtils.getSharedSecret(privateKey, publicKey)
|
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privateKey, publicKey)
|
||||||
|
|
||||||
val encrypted = CryptoUtils.encrypt(msg, sharedSecret)
|
val encrypted = CryptoUtils.encryptNIP04(msg, sharedSecret)
|
||||||
val decrypted = CryptoUtils.decrypt(encrypted, sharedSecret)
|
val decrypted = CryptoUtils.decryptNIP04(encrypted, sharedSecret)
|
||||||
|
|
||||||
assertEquals(msg, decrypted)
|
assertEquals(msg, decrypted)
|
||||||
}
|
}
|
||||||
@@ -92,10 +92,10 @@ class CryptoUtilsTest {
|
|||||||
|
|
||||||
val privateKey = CryptoUtils.privkeyCreate()
|
val privateKey = CryptoUtils.privkeyCreate()
|
||||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
val sharedSecret = CryptoUtils.getSharedSecretXChaCha(privateKey, publicKey)
|
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privateKey, publicKey)
|
||||||
|
|
||||||
val encrypted = CryptoUtils.encryptXChaCha(msg, sharedSecret)
|
val encrypted = CryptoUtils.encryptNIP24(msg, sharedSecret)
|
||||||
val decrypted = CryptoUtils.decryptXChaCha(encrypted, sharedSecret)
|
val decrypted = CryptoUtils.decryptNIP24(encrypted, sharedSecret)
|
||||||
|
|
||||||
assertEquals(msg, decrypted)
|
assertEquals(msg, decrypted)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.vitorpamplona.amethyst
|
package com.vitorpamplona.amethyst
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.vitorpamplona.amethyst.model.HexKey
|
||||||
import com.vitorpamplona.amethyst.model.hexToByteArray
|
import com.vitorpamplona.amethyst.model.hexToByteArray
|
||||||
import com.vitorpamplona.amethyst.model.toHexKey
|
import com.vitorpamplona.amethyst.model.toHexKey
|
||||||
import com.vitorpamplona.amethyst.service.CryptoUtils
|
import com.vitorpamplona.amethyst.service.CryptoUtils
|
||||||
@@ -8,6 +9,7 @@ import com.vitorpamplona.amethyst.service.KeyPair
|
|||||||
import com.vitorpamplona.amethyst.service.model.ChatMessageEvent
|
import com.vitorpamplona.amethyst.service.model.ChatMessageEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.Event
|
import com.vitorpamplona.amethyst.service.model.Event
|
||||||
import com.vitorpamplona.amethyst.service.model.GiftWrapEvent
|
import com.vitorpamplona.amethyst.service.model.GiftWrapEvent
|
||||||
|
import com.vitorpamplona.amethyst.service.model.Gossip
|
||||||
import com.vitorpamplona.amethyst.service.model.NIP24Factory
|
import com.vitorpamplona.amethyst.service.model.NIP24Factory
|
||||||
import com.vitorpamplona.amethyst.service.model.SealedGossipEvent
|
import com.vitorpamplona.amethyst.service.model.SealedGossipEvent
|
||||||
import com.vitorpamplona.amethyst.service.relays.Client
|
import com.vitorpamplona.amethyst.service.relays.Client
|
||||||
@@ -34,18 +36,6 @@ class GiftWrapEventTest {
|
|||||||
sender.privKey!!
|
sender.privKey!!
|
||||||
)
|
)
|
||||||
|
|
||||||
events.forEach {
|
|
||||||
if (it.recipientPubKey() == sender.pubKey.toHexKey()) {
|
|
||||||
println(sender.privKey!!.toHexKey())
|
|
||||||
println(it.toJson())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it.recipientPubKey() == receiver.pubKey.toHexKey()) {
|
|
||||||
println(receiver.privKey!!.toHexKey())
|
|
||||||
println(it.toJson())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate Receiver
|
// Simulate Receiver
|
||||||
val eventsReceiverGets = events.filter { it.isTaggedUser(receiver.pubKey.toHexKey()) }
|
val eventsReceiverGets = events.filter { it.isTaggedUser(receiver.pubKey.toHexKey()) }
|
||||||
eventsReceiverGets.forEach {
|
eventsReceiverGets.forEach {
|
||||||
@@ -424,23 +414,11 @@ class GiftWrapEventTest {
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
val privateKey = "de6152a85a0dea3b09a08a6f8139a314d498a7b52f7e5c28858b64270abd4c70".hexToByteArray()
|
val privateKey = "de6152a85a0dea3b09a08a6f8139a314d498a7b52f7e5c28858b64270abd4c70"
|
||||||
val wrap = Event.fromJson(json, Client.lenient) as GiftWrapEvent
|
val gossip = unwrapUnsealGossip(json, privateKey)
|
||||||
|
|
||||||
wrap.checkSignature()
|
assertNotNull(gossip)
|
||||||
|
assertEquals("Hola, que tal?", gossip?.content)
|
||||||
assertEquals(CryptoUtils.pubkeyCreate(privateKey).toHexKey(), wrap.recipientPubKey())
|
|
||||||
|
|
||||||
val event = wrap.unwrap(privateKey)
|
|
||||||
assertNotNull(event)
|
|
||||||
|
|
||||||
if (event is SealedGossipEvent) {
|
|
||||||
val innerData = event.unseal(privateKey)
|
|
||||||
assertNotNull(innerData)
|
|
||||||
assertEquals("Hola, que tal?", innerData?.content)
|
|
||||||
} else {
|
|
||||||
fail("Inner event is not a Sealed Gossip")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -462,23 +440,11 @@ class GiftWrapEventTest {
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
val privateKey = "409ff7654141eaa16cd2161fe5bd127aeaef71f270c67587474b78998a8e3533".hexToByteArray()
|
val privateKey = "409ff7654141eaa16cd2161fe5bd127aeaef71f270c67587474b78998a8e3533"
|
||||||
val wrap = Event.fromJson(json, Client.lenient) as GiftWrapEvent
|
val gossip = unwrapUnsealGossip(json, privateKey)
|
||||||
|
|
||||||
wrap.checkSignature()
|
assertNotNull(gossip)
|
||||||
|
assertEquals("Hola, que tal?", gossip?.content)
|
||||||
assertEquals(CryptoUtils.pubkeyCreate(privateKey).toHexKey(), wrap.recipientPubKey())
|
|
||||||
|
|
||||||
val event = wrap.unwrap(privateKey)
|
|
||||||
assertNotNull(event)
|
|
||||||
|
|
||||||
if (event is SealedGossipEvent) {
|
|
||||||
val innerData = event.unseal(privateKey)
|
|
||||||
assertNotNull(innerData)
|
|
||||||
assertEquals("Hola, que tal?", innerData?.content)
|
|
||||||
} else {
|
|
||||||
fail("Inner event is not a Sealed Gossip")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -503,24 +469,11 @@ class GiftWrapEventTest {
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771".hexToByteArray()
|
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771"
|
||||||
val wrap = Event.fromJson(json, Client.lenient) as GiftWrapEvent
|
val gossip = unwrapUnsealGossip(json, privateKey)
|
||||||
|
|
||||||
wrap.checkSignature()
|
assertNotNull(gossip)
|
||||||
|
assertEquals("test", gossip?.content)
|
||||||
assertEquals(CryptoUtils.pubkeyCreate(privateKey).toHexKey(), wrap.recipientPubKey())
|
|
||||||
|
|
||||||
val event = wrap.unwrap(privateKey)
|
|
||||||
assertNotNull(event)
|
|
||||||
|
|
||||||
if (event is SealedGossipEvent) {
|
|
||||||
val innerData = event.unseal(privateKey)
|
|
||||||
assertNotNull(innerData)
|
|
||||||
assertEquals("test", innerData?.content)
|
|
||||||
} else {
|
|
||||||
println(event?.toJson())
|
|
||||||
fail()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -542,28 +495,35 @@ class GiftWrapEventTest {
|
|||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771".hexToByteArray()
|
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771"
|
||||||
val wrap = Event.fromJson(json, Client.lenient) as GiftWrapEvent
|
|
||||||
|
|
||||||
|
val gossip = unwrapUnsealGossip(json, privateKey)
|
||||||
|
|
||||||
|
assertEquals("asdfasdfasdf", gossip?.content)
|
||||||
|
assertEquals(0L, gossip?.createdAt)
|
||||||
|
assertEquals("827ba09d32ab81d62c60f657b350198c8aaba84372dab9ad3f4f6b8b7274b707", gossip?.id)
|
||||||
|
assertEquals(14, gossip?.kind)
|
||||||
|
assertEquals("subject", gossip?.tags?.firstOrNull()?.get(0))
|
||||||
|
assertEquals("test", gossip?.tags?.firstOrNull()?.get(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unwrapUnsealGossip(json: String, privateKey: HexKey): Gossip? {
|
||||||
|
val pkBytes = privateKey.hexToByteArray()
|
||||||
|
|
||||||
|
val wrap = Event.fromJson(json, Client.lenient) as GiftWrapEvent
|
||||||
wrap.checkSignature()
|
wrap.checkSignature()
|
||||||
|
|
||||||
assertEquals(CryptoUtils.pubkeyCreate(privateKey).toHexKey(), wrap.recipientPubKey())
|
assertEquals(CryptoUtils.pubkeyCreate(pkBytes).toHexKey(), wrap.recipientPubKey())
|
||||||
|
|
||||||
val event = wrap.unwrap(privateKey)
|
val event = wrap.unwrap(pkBytes)
|
||||||
assertNotNull(event)
|
assertNotNull(event)
|
||||||
|
|
||||||
if (event is SealedGossipEvent) {
|
return if (event is SealedGossipEvent) {
|
||||||
val innerData = event.unseal(privateKey)
|
return event.unseal(pkBytes)
|
||||||
assertNotNull(innerData)
|
|
||||||
assertEquals("asdfasdfasdf", innerData?.content)
|
|
||||||
assertEquals(0L, innerData?.createdAt)
|
|
||||||
assertEquals("827ba09d32ab81d62c60f657b350198c8aaba84372dab9ad3f4f6b8b7274b707", innerData?.id)
|
|
||||||
assertEquals(14, innerData?.kind)
|
|
||||||
assertEquals("subject", innerData?.tags?.firstOrNull()?.get(0))
|
|
||||||
assertEquals("test", innerData?.tags?.firstOrNull()?.get(1))
|
|
||||||
} else {
|
} else {
|
||||||
println(event?.toJson())
|
println(event?.toJson())
|
||||||
fail()
|
fail("Event is not a Sealed Gossip")
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package com.vitorpamplona.amethyst.service
|
package com.vitorpamplona.amethyst.service
|
||||||
|
|
||||||
import com.goterl.lazysodium.LazySodium
|
|
||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
|
||||||
import com.goterl.lazysodium.Sodium
|
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
import com.goterl.lazysodium.utils.Base64MessageEncoder
|
|
||||||
import com.goterl.lazysodium.utils.Key
|
import com.goterl.lazysodium.utils.Key
|
||||||
import fr.acinq.secp256k1.Hex
|
import fr.acinq.secp256k1.Hex
|
||||||
import fr.acinq.secp256k1.Secp256k1
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
@@ -34,86 +30,6 @@ object CryptoUtils {
|
|||||||
fun sign(data: ByteArray, privKey: ByteArray): ByteArray =
|
fun sign(data: ByteArray, privKey: ByteArray): ByteArray =
|
||||||
secp256k1.signSchnorr(data, privKey, null)
|
secp256k1.signSchnorr(data, privKey, null)
|
||||||
|
|
||||||
fun encrypt(msg: String, privateKey: ByteArray, pubKey: ByteArray): String {
|
|
||||||
val sharedSecret = getSharedSecret(privateKey, pubKey)
|
|
||||||
return encrypt(msg, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encrypt(msg: String, sharedSecret: ByteArray): String {
|
|
||||||
val iv = ByteArray(16)
|
|
||||||
random.nextBytes(iv)
|
|
||||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(iv))
|
|
||||||
val ivBase64 = Base64.getEncoder().encodeToString(iv)
|
|
||||||
val encryptedMsg = cipher.doFinal(msg.toByteArray())
|
|
||||||
val encryptedMsgBase64 = Base64.getEncoder().encodeToString(encryptedMsg)
|
|
||||||
return "$encryptedMsgBase64?iv=$ivBase64"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrypt(msg: String, privateKey: ByteArray, pubKey: ByteArray): String {
|
|
||||||
val sharedSecret = getSharedSecret(privateKey, pubKey)
|
|
||||||
return decrypt(msg, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrypt(msg: String, sharedSecret: ByteArray): String {
|
|
||||||
val parts = msg.split("?iv=")
|
|
||||||
val iv = parts[1].run { Base64.getDecoder().decode(this) }
|
|
||||||
val encryptedMsg = parts.first().run { Base64.getDecoder().decode(this) }
|
|
||||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(iv))
|
|
||||||
return String(cipher.doFinal(encryptedMsg))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encryptXChaCha(msg: String, privateKey: ByteArray, pubKey: ByteArray): EncryptedInfo {
|
|
||||||
val sharedSecret = getSharedSecretXChaCha(privateKey, pubKey)
|
|
||||||
return encryptXChaCha(msg, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encryptXChaCha(msg: String, sharedSecret: ByteArray): EncryptedInfo {
|
|
||||||
val lazySodium = LazySodiumAndroid(SodiumAndroid(), Base64MessageEncoder())
|
|
||||||
|
|
||||||
val key = Key.fromBytes(sharedSecret)
|
|
||||||
|
|
||||||
val nonce: ByteArray = lazySodium.nonce(24)
|
|
||||||
val messageBytes: ByteArray = msg.toByteArray()
|
|
||||||
|
|
||||||
val cipher = lazySodium.cryptoStreamXChaCha20Xor(
|
|
||||||
messageBytes = messageBytes,
|
|
||||||
nonce = nonce,
|
|
||||||
key = key
|
|
||||||
)
|
|
||||||
|
|
||||||
val cipherBase64 = Base64.getEncoder().encodeToString(cipher)
|
|
||||||
val nonceBase64 = Base64.getEncoder().encodeToString(nonce)
|
|
||||||
|
|
||||||
return EncryptedInfo(
|
|
||||||
ciphertext = cipherBase64,
|
|
||||||
nonce = nonceBase64,
|
|
||||||
v = Nip44Version.XChaCha20.versionCode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decryptXChaCha(encryptedInfo: EncryptedInfo, privateKey: ByteArray, pubKey: ByteArray): String? {
|
|
||||||
val sharedSecret = getSharedSecretXChaCha(privateKey, pubKey)
|
|
||||||
return decryptXChaCha(encryptedInfo, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decryptXChaCha(encryptedInfo: EncryptedInfo, sharedSecret: ByteArray): String? {
|
|
||||||
val lazySodium = LazySodiumAndroid(SodiumAndroid(), Base64MessageEncoder())
|
|
||||||
|
|
||||||
val key = Key.fromBytes(sharedSecret)
|
|
||||||
val nonceBytes = Base64.getDecoder().decode(encryptedInfo.nonce)
|
|
||||||
val messageBytes = Base64.getDecoder().decode(encryptedInfo.ciphertext)
|
|
||||||
|
|
||||||
val cipher = lazySodium.cryptoStreamXChaCha20Xor(
|
|
||||||
messageBytes = messageBytes,
|
|
||||||
nonce = nonceBytes,
|
|
||||||
key = key
|
|
||||||
)
|
|
||||||
|
|
||||||
return cipher?.decodeToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun verifySignature(
|
fun verifySignature(
|
||||||
signature: ByteArray,
|
signature: ByteArray,
|
||||||
hash: ByteArray,
|
hash: ByteArray,
|
||||||
@@ -130,100 +46,87 @@ object CryptoUtils {
|
|||||||
/**
|
/**
|
||||||
* @return 32B shared secret
|
* @return 32B shared secret
|
||||||
*/
|
*/
|
||||||
fun getSharedSecret(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
|
fun getSharedSecretNIP04(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
|
||||||
secp256k1.pubKeyTweakMul(Hex.decode("02") + pubKey, privateKey).copyOfRange(1, 33)
|
secp256k1.pubKeyTweakMul(Hex.decode("02") + pubKey, privateKey).copyOfRange(1, 33)
|
||||||
|
|
||||||
|
fun encryptNIP04(msg: String, privateKey: ByteArray, pubKey: ByteArray): String {
|
||||||
|
val sharedSecret = getSharedSecretNIP04(privateKey, pubKey)
|
||||||
|
return encryptNIP04(msg, sharedSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encryptNIP04(msg: String, sharedSecret: ByteArray): String {
|
||||||
|
val iv = ByteArray(16)
|
||||||
|
random.nextBytes(iv)
|
||||||
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(iv))
|
||||||
|
val ivBase64 = Base64.getEncoder().encodeToString(iv)
|
||||||
|
val encryptedMsg = cipher.doFinal(msg.toByteArray())
|
||||||
|
val encryptedMsgBase64 = Base64.getEncoder().encodeToString(encryptedMsg)
|
||||||
|
return "$encryptedMsgBase64?iv=$ivBase64"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptNIP04(msg: String, privateKey: ByteArray, pubKey: ByteArray): String {
|
||||||
|
val sharedSecret = getSharedSecretNIP04(privateKey, pubKey)
|
||||||
|
return decryptNIP04(msg, sharedSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptNIP04(msg: String, sharedSecret: ByteArray): String {
|
||||||
|
val parts = msg.split("?iv=")
|
||||||
|
val iv = parts[1].run { Base64.getDecoder().decode(this) }
|
||||||
|
val encryptedMsg = parts.first().run { Base64.getDecoder().decode(this) }
|
||||||
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(iv))
|
||||||
|
return String(cipher.doFinal(encryptedMsg))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encryptNIP24(msg: String, privateKey: ByteArray, pubKey: ByteArray): EncryptedInfo {
|
||||||
|
val sharedSecret = getSharedSecretNIP24(privateKey, pubKey)
|
||||||
|
return encryptNIP24(msg, sharedSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encryptNIP24(msg: String, sharedSecret: ByteArray): EncryptedInfo {
|
||||||
|
val nonce = ByteArray(24)
|
||||||
|
random.nextBytes(nonce)
|
||||||
|
|
||||||
|
val cipher = SodiumAndroid().cryptoStreamXChaCha20Xor(
|
||||||
|
messageBytes = msg.toByteArray(),
|
||||||
|
nonce = nonce,
|
||||||
|
key = Key.fromBytes(sharedSecret)
|
||||||
|
)
|
||||||
|
|
||||||
|
val cipherBase64 = Base64.getEncoder().encodeToString(cipher)
|
||||||
|
val nonceBase64 = Base64.getEncoder().encodeToString(nonce)
|
||||||
|
|
||||||
|
return EncryptedInfo(
|
||||||
|
ciphertext = cipherBase64,
|
||||||
|
nonce = nonceBase64,
|
||||||
|
v = Nip44Version.XChaCha20.versionCode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptNIP24(encryptedInfo: EncryptedInfo, privateKey: ByteArray, pubKey: ByteArray): String? {
|
||||||
|
val sharedSecret = getSharedSecretNIP24(privateKey, pubKey)
|
||||||
|
return decryptNIP24(encryptedInfo, sharedSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptNIP24(encryptedInfo: EncryptedInfo, sharedSecret: ByteArray): String? {
|
||||||
|
return SodiumAndroid().cryptoStreamXChaCha20Xor(
|
||||||
|
messageBytes = Base64.getDecoder().decode(encryptedInfo.ciphertext),
|
||||||
|
nonce = Base64.getDecoder().decode(encryptedInfo.nonce),
|
||||||
|
key = Key.fromBytes(sharedSecret)
|
||||||
|
)?.decodeToString()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 32B shared secret
|
* @return 32B shared secret
|
||||||
*/
|
*/
|
||||||
fun getSharedSecretXChaCha(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
|
fun getSharedSecretNIP24(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
|
||||||
sha256(secp256k1.pubKeyTweakMul(Hex.decode("02") + pubKey, privateKey).copyOfRange(1, 33))
|
sha256(secp256k1.pubKeyTweakMul(Hex.decode("02") + pubKey, privateKey).copyOfRange(1, 33))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Int.toByteArray(): ByteArray {
|
|
||||||
val bytes = ByteArray(4)
|
|
||||||
(0..3).forEach {
|
|
||||||
bytes[3 - it] = ((this ushr (8 * it)) and 0xFFFF).toByte()
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
data class EncryptedInfo(val ciphertext: String, val nonce: String, val v: Int)
|
data class EncryptedInfo(val ciphertext: String, val nonce: String, val v: Int)
|
||||||
|
|
||||||
enum class Nip44Version(val versionCode: Int) {
|
enum class Nip44Version(val versionCode: Int) {
|
||||||
Reserved(0),
|
Reserved(0),
|
||||||
XChaCha20(1)
|
XChaCha20(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Sodium.crypto_stream_xchacha20_xor_ic(
|
|
||||||
cipher: ByteArray,
|
|
||||||
message: ByteArray,
|
|
||||||
messageLen: Long,
|
|
||||||
nonce: ByteArray,
|
|
||||||
ic: Long,
|
|
||||||
key: ByteArray
|
|
||||||
): Int {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* unsigned char k2[crypto_core_hchacha20_OUTPUTBYTES];
|
|
||||||
|
|
||||||
crypto_core_hchacha20(k2, n, k, NULL);
|
|
||||||
return crypto_stream_chacha20_xor_ic(
|
|
||||||
c, m, mlen, n + crypto_core_hchacha20_INPUTBYTES, ic, k2);
|
|
||||||
*/
|
|
||||||
|
|
||||||
val k2 = ByteArray(32)
|
|
||||||
|
|
||||||
val nonceChaCha = nonce.drop(16).toByteArray()
|
|
||||||
assert(nonceChaCha.size == 8)
|
|
||||||
|
|
||||||
crypto_core_hchacha20(k2, nonce, key, null)
|
|
||||||
return crypto_stream_chacha20_xor_ic(
|
|
||||||
cipher,
|
|
||||||
message,
|
|
||||||
messageLen,
|
|
||||||
nonceChaCha,
|
|
||||||
ic,
|
|
||||||
k2
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Sodium.crypto_stream_xchacha20_xor(
|
|
||||||
cipher: ByteArray,
|
|
||||||
message: ByteArray,
|
|
||||||
messageLen: Long,
|
|
||||||
nonce: ByteArray,
|
|
||||||
key: ByteArray
|
|
||||||
): Int {
|
|
||||||
return crypto_stream_xchacha20_xor_ic(cipher, message, messageLen, nonce, 0, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun LazySodium.cryptoStreamXChaCha20Xor(
|
|
||||||
cipher: ByteArray,
|
|
||||||
message: ByteArray,
|
|
||||||
messageLen: Long,
|
|
||||||
nonce: ByteArray,
|
|
||||||
key: ByteArray
|
|
||||||
): Boolean {
|
|
||||||
require(!(messageLen < 0 || messageLen > message.size)) { "messageLen out of bounds: $messageLen" }
|
|
||||||
return successful(
|
|
||||||
getSodium().crypto_stream_xchacha20_xor(
|
|
||||||
cipher,
|
|
||||||
message,
|
|
||||||
messageLen,
|
|
||||||
nonce,
|
|
||||||
key
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun LazySodium.cryptoStreamXChaCha20Xor(
|
|
||||||
messageBytes: ByteArray,
|
|
||||||
nonce: ByteArray,
|
|
||||||
key: Key
|
|
||||||
): ByteArray? {
|
|
||||||
val mLen = messageBytes.size
|
|
||||||
val cipher = ByteArray(mLen)
|
|
||||||
val sucessful = cryptoStreamXChaCha20Xor(cipher, messageBytes, mLen.toLong(), nonce, key.asBytes)
|
|
||||||
return if (sucessful) cipher else null
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.vitorpamplona.amethyst.service
|
||||||
|
|
||||||
|
import com.goterl.lazysodium.Sodium
|
||||||
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
|
import com.goterl.lazysodium.utils.Key
|
||||||
|
|
||||||
|
fun Sodium.crypto_stream_xchacha20_xor_ic(
|
||||||
|
cipher: ByteArray,
|
||||||
|
message: ByteArray,
|
||||||
|
messageLen: Long,
|
||||||
|
nonce: ByteArray,
|
||||||
|
ic: Long,
|
||||||
|
key: ByteArray
|
||||||
|
): Int {
|
||||||
|
/**
|
||||||
|
* C++ Code:
|
||||||
|
*
|
||||||
|
* unsigned char k2[crypto_core_hchacha20_OUTPUTBYTES];
|
||||||
|
* crypto_core_hchacha20(k2, n, k, NULL);
|
||||||
|
* return crypto_stream_chacha20_xor_ic(
|
||||||
|
* c, m, mlen, n + crypto_core_hchacha20_INPUTBYTES, ic, k2);
|
||||||
|
*/
|
||||||
|
val k2 = ByteArray(32)
|
||||||
|
|
||||||
|
val nonceChaCha = nonce.drop(16).toByteArray()
|
||||||
|
assert(nonceChaCha.size == 8)
|
||||||
|
|
||||||
|
crypto_core_hchacha20(k2, nonce, key, null)
|
||||||
|
return crypto_stream_chacha20_xor_ic(
|
||||||
|
cipher,
|
||||||
|
message,
|
||||||
|
messageLen,
|
||||||
|
nonceChaCha,
|
||||||
|
ic,
|
||||||
|
k2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Sodium.crypto_stream_xchacha20_xor(
|
||||||
|
cipher: ByteArray,
|
||||||
|
message: ByteArray,
|
||||||
|
messageLen: Long,
|
||||||
|
nonce: ByteArray,
|
||||||
|
key: ByteArray
|
||||||
|
): Int {
|
||||||
|
return crypto_stream_xchacha20_xor_ic(cipher, message, messageLen, nonce, 0, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SodiumAndroid.cryptoStreamXChaCha20Xor(
|
||||||
|
cipher: ByteArray,
|
||||||
|
message: ByteArray,
|
||||||
|
messageLen: Long,
|
||||||
|
nonce: ByteArray,
|
||||||
|
key: ByteArray
|
||||||
|
): Boolean {
|
||||||
|
require(!(messageLen < 0 || messageLen > message.size)) { "messageLen out of bounds: $messageLen" }
|
||||||
|
return crypto_stream_xchacha20_xor(
|
||||||
|
cipher,
|
||||||
|
message,
|
||||||
|
messageLen,
|
||||||
|
nonce,
|
||||||
|
key
|
||||||
|
) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SodiumAndroid.cryptoStreamXChaCha20Xor(
|
||||||
|
messageBytes: ByteArray,
|
||||||
|
nonce: ByteArray,
|
||||||
|
key: Key
|
||||||
|
): ByteArray? {
|
||||||
|
val mLen = messageBytes.size
|
||||||
|
val cipher = ByteArray(mLen)
|
||||||
|
val sucessful = cryptoStreamXChaCha20Xor(cipher, messageBytes, mLen.toLong(), nonce, key.asBytes)
|
||||||
|
return if (sucessful) cipher else null
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import com.vitorpamplona.amethyst.model.hexToByteArray
|
|||||||
import com.vitorpamplona.amethyst.model.toHexKey
|
import com.vitorpamplona.amethyst.model.toHexKey
|
||||||
import com.vitorpamplona.amethyst.service.bechToBytes
|
import com.vitorpamplona.amethyst.service.bechToBytes
|
||||||
import com.vitorpamplona.amethyst.service.nip19.Tlv
|
import com.vitorpamplona.amethyst.service.nip19.Tlv
|
||||||
import com.vitorpamplona.amethyst.service.toByteArray
|
import com.vitorpamplona.amethyst.service.nip19.toByteArray
|
||||||
import com.vitorpamplona.amethyst.service.toNAddress
|
import com.vitorpamplona.amethyst.service.toNAddress
|
||||||
import fr.acinq.secp256k1.Hex
|
import fr.acinq.secp256k1.Hex
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ abstract class GeneralListEvent(
|
|||||||
if (content.isBlank()) return null
|
if (content.isBlank()) return null
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecret(privKey, pubKey.hexToByteArray())
|
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey.hexToByteArray())
|
||||||
|
|
||||||
return CryptoUtils.decrypt(content, sharedSecret)
|
return CryptoUtils.decryptNIP04(content, sharedSecret)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w("GeneralList", "Error decrypting the message ${e.message} for ${dTag()}")
|
Log.w("GeneralList", "Error decrypting the message ${e.message} for ${dTag()}")
|
||||||
null
|
null
|
||||||
@@ -91,7 +91,7 @@ abstract class GeneralListEvent(
|
|||||||
}
|
}
|
||||||
val msg = gson.toJson(privTags)
|
val msg = gson.toJson(privTags)
|
||||||
|
|
||||||
return CryptoUtils.encrypt(
|
return CryptoUtils.encryptNIP04(
|
||||||
msg,
|
msg,
|
||||||
privateKey,
|
privateKey,
|
||||||
pubKey
|
pubKey
|
||||||
@@ -102,7 +102,7 @@ abstract class GeneralListEvent(
|
|||||||
privateTags: List<List<String>>? = null,
|
privateTags: List<List<String>>? = null,
|
||||||
privateKey: ByteArray
|
privateKey: ByteArray
|
||||||
): String {
|
): String {
|
||||||
return CryptoUtils.encrypt(
|
return CryptoUtils.encryptNIP04(
|
||||||
msg = gson.toJson(privateTags),
|
msg = gson.toJson(privateTags),
|
||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
pubKey = CryptoUtils.pubkeyCreate(privateKey)
|
pubKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ class GiftWrapEvent(
|
|||||||
if (content.isBlank()) return null
|
if (content.isBlank()) return null
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecretXChaCha(privKey, pubKey.hexToByteArray())
|
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privKey, pubKey.hexToByteArray())
|
||||||
|
|
||||||
val toDecrypt = gson.fromJson<EncryptedInfo>(
|
val toDecrypt = gson.fromJson<EncryptedInfo>(
|
||||||
content,
|
content,
|
||||||
EncryptedInfo::class.java
|
EncryptedInfo::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
return CryptoUtils.decryptXChaCha(toDecrypt, sharedSecret)
|
return CryptoUtils.decryptNIP24(toDecrypt, sharedSecret)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w("GeneralList", "Error decrypting the message ${e.message}")
|
Log.w("GeneralList", "Error decrypting the message ${e.message}")
|
||||||
null
|
null
|
||||||
@@ -65,10 +65,10 @@ class GiftWrapEvent(
|
|||||||
createdAt: Long = TimeUtils.now()
|
createdAt: Long = TimeUtils.now()
|
||||||
): GiftWrapEvent {
|
): GiftWrapEvent {
|
||||||
val privateKey = CryptoUtils.privkeyCreate() // GiftWrap is always a random key
|
val privateKey = CryptoUtils.privkeyCreate() // GiftWrap is always a random key
|
||||||
val sharedSecret = CryptoUtils.getSharedSecretXChaCha(privateKey, recipientPubKey.hexToByteArray())
|
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privateKey, recipientPubKey.hexToByteArray())
|
||||||
|
|
||||||
val content = gson.toJson(
|
val content = gson.toJson(
|
||||||
CryptoUtils.encryptXChaCha(
|
CryptoUtils.encryptNIP24(
|
||||||
gson.toJson(event),
|
gson.toJson(event),
|
||||||
sharedSecret
|
sharedSecret
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-3
@@ -35,9 +35,9 @@ class LnZapPaymentRequestEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecret(privKey, pubkey)
|
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubkey)
|
||||||
|
|
||||||
val jsonText = CryptoUtils.decrypt(content, sharedSecret)
|
val jsonText = CryptoUtils.decryptNIP04(content, sharedSecret)
|
||||||
|
|
||||||
val payInvoiceMethod = gson.fromJson(jsonText, Request::class.java)
|
val payInvoiceMethod = gson.fromJson(jsonText, Request::class.java)
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class LnZapPaymentRequestEvent(
|
|||||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey)
|
val pubKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
val serializedRequest = gson.toJson(PayInvoiceMethod.create(lnInvoice))
|
val serializedRequest = gson.toJson(PayInvoiceMethod.create(lnInvoice))
|
||||||
|
|
||||||
val content = CryptoUtils.encrypt(
|
val content = CryptoUtils.encryptNIP04(
|
||||||
serializedRequest,
|
serializedRequest,
|
||||||
privateKey,
|
privateKey,
|
||||||
walletServicePubkey.hexToByteArray()
|
walletServicePubkey.hexToByteArray()
|
||||||
|
|||||||
+2
-2
@@ -30,9 +30,9 @@ class LnZapPaymentResponseEvent(
|
|||||||
|
|
||||||
private fun decrypt(privKey: ByteArray, pubKey: ByteArray): String? {
|
private fun decrypt(privKey: ByteArray, pubKey: ByteArray): String? {
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecret(privKey, pubKey)
|
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey)
|
||||||
|
|
||||||
val retVal = CryptoUtils.decrypt(content, sharedSecret)
|
val retVal = CryptoUtils.decryptNIP04(content, sharedSecret)
|
||||||
|
|
||||||
if (retVal.startsWith(PrivateDmEvent.nip18Advertisement)) {
|
if (retVal.startsWith(PrivateDmEvent.nip18Advertisement)) {
|
||||||
retVal.substring(16)
|
retVal.substring(16)
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ class LnZapRequestEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
||||||
var sharedSecret = CryptoUtils.getSharedSecret(privkey, pubkey)
|
var sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||||
val iv = ByteArray(16)
|
val iv = ByteArray(16)
|
||||||
SecureRandom().nextBytes(iv)
|
SecureRandom().nextBytes(iv)
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ class LnZapRequestEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun decryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
private fun decryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
||||||
var sharedSecret = CryptoUtils.getSharedSecret(privkey, pubkey)
|
var sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||||
if (sharedSecret.size != 16 && sharedSecret.size != 32) {
|
if (sharedSecret.size != 16 && sharedSecret.size != 32) {
|
||||||
throw IllegalArgumentException("Invalid shared secret size")
|
throw IllegalArgumentException("Invalid shared secret size")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ class MuteListEvent(
|
|||||||
|
|
||||||
fun plainContent(privKey: ByteArray): String? {
|
fun plainContent(privKey: ByteArray): String? {
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecret(privKey, pubKey.hexToByteArray())
|
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey.hexToByteArray())
|
||||||
|
|
||||||
return CryptoUtils.decrypt(content, sharedSecret)
|
return CryptoUtils.decryptNIP04(content, sharedSecret)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w("BookmarkList", "Error decrypting the message ${e.message}")
|
Log.w("BookmarkList", "Error decrypting the message ${e.message}")
|
||||||
null
|
null
|
||||||
@@ -87,7 +87,7 @@ class MuteListEvent(
|
|||||||
}
|
}
|
||||||
val msg = gson.toJson(privTags)
|
val msg = gson.toJson(privTags)
|
||||||
|
|
||||||
val content = CryptoUtils.encrypt(
|
val content = CryptoUtils.encryptNIP04(
|
||||||
msg,
|
msg,
|
||||||
privateKey,
|
privateKey,
|
||||||
pubKey
|
pubKey
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class PrivateDmEvent(
|
|||||||
|
|
||||||
fun plainContent(privKey: ByteArray, pubKey: ByteArray): String? {
|
fun plainContent(privKey: ByteArray, pubKey: ByteArray): String? {
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecret(privKey, pubKey)
|
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey)
|
||||||
|
|
||||||
val retVal = CryptoUtils.decrypt(content, sharedSecret)
|
val retVal = CryptoUtils.decryptNIP04(content, sharedSecret)
|
||||||
|
|
||||||
if (retVal.startsWith(nip18Advertisement)) {
|
if (retVal.startsWith(nip18Advertisement)) {
|
||||||
retVal.substring(16)
|
retVal.substring(16)
|
||||||
@@ -89,7 +89,7 @@ class PrivateDmEvent(
|
|||||||
zapRaiserAmount: Long?,
|
zapRaiserAmount: Long?,
|
||||||
geohash: String? = null
|
geohash: String? = null
|
||||||
): PrivateDmEvent {
|
): PrivateDmEvent {
|
||||||
val content = CryptoUtils.encrypt(
|
val content = CryptoUtils.encryptNIP04(
|
||||||
if (advertiseNip18) { nip18Advertisement } else { "" } + msg,
|
if (advertiseNip18) { nip18Advertisement } else { "" } + msg,
|
||||||
privateKey,
|
privateKey,
|
||||||
recipientPubKey
|
recipientPubKey
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ class SealedGossipEvent(
|
|||||||
if (content.isBlank()) return null
|
if (content.isBlank()) return null
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecretXChaCha(privKey, pubKey.hexToByteArray())
|
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privKey, pubKey.hexToByteArray())
|
||||||
|
|
||||||
val toDecrypt = gson.fromJson<EncryptedInfo>(
|
val toDecrypt = gson.fromJson<EncryptedInfo>(
|
||||||
content,
|
content,
|
||||||
EncryptedInfo::class.java
|
EncryptedInfo::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
return CryptoUtils.decryptXChaCha(toDecrypt, sharedSecret)
|
return CryptoUtils.decryptNIP24(toDecrypt, sharedSecret)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w("GeneralList", "Error decrypting the message ${e.message}")
|
Log.w("GeneralList", "Error decrypting the message ${e.message}")
|
||||||
null
|
null
|
||||||
@@ -71,10 +71,10 @@ class SealedGossipEvent(
|
|||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
createdAt: Long = TimeUtils.now()
|
createdAt: Long = TimeUtils.now()
|
||||||
): SealedGossipEvent {
|
): SealedGossipEvent {
|
||||||
val sharedSecret = CryptoUtils.getSharedSecretXChaCha(privateKey, encryptTo.hexToByteArray())
|
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privateKey, encryptTo.hexToByteArray())
|
||||||
|
|
||||||
val content = gson.toJson(
|
val content = gson.toJson(
|
||||||
CryptoUtils.encryptXChaCha(
|
CryptoUtils.encryptNIP24(
|
||||||
gson.toJson(gossip),
|
gson.toJson(gossip),
|
||||||
sharedSecret
|
sharedSecret
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.runtime.Immutable
|
|||||||
import com.vitorpamplona.amethyst.model.hexToByteArray
|
import com.vitorpamplona.amethyst.model.hexToByteArray
|
||||||
import com.vitorpamplona.amethyst.model.toHexKey
|
import com.vitorpamplona.amethyst.model.toHexKey
|
||||||
import com.vitorpamplona.amethyst.service.bechToBytes
|
import com.vitorpamplona.amethyst.service.bechToBytes
|
||||||
import com.vitorpamplona.amethyst.service.toByteArray
|
|
||||||
import com.vitorpamplona.amethyst.service.toNEvent
|
import com.vitorpamplona.amethyst.service.toNEvent
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@@ -170,3 +169,11 @@ object Nip19 {
|
|||||||
return fullArray.toNEvent()
|
return fullArray.toNEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Int.toByteArray(): ByteArray {
|
||||||
|
val bytes = ByteArray(4)
|
||||||
|
(0..3).forEach {
|
||||||
|
bytes[3 - it] = ((this ushr (8 * it)) and 0xFFFF).toByte()
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user