Receiving and decoding private Zaps

Private zaps get decoded correctly.

TODO: Update events or at least notifications in Damus to replace anon sender with the one from decrypted message. Bonus: Show Content of the Zap Message
This commit is contained in:
Believethehype
2023-04-17 20:10:36 +02:00
parent ff490a2b14
commit 5e6e2835ca
6 changed files with 51 additions and 32 deletions

View File

@@ -7,6 +7,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.vitorpamplona.amethyst.service.model.*
import com.vitorpamplona.amethyst.service.relays.Relay
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
import fr.acinq.secp256k1.Hex
import kotlinx.coroutines.*
import nostr.postr.toNpub
@@ -622,6 +623,27 @@ object LocalCache {
}
}
fun checkPrivateZap(zaprequest: Event): Event {
var anonTag = zaprequest.tags.firstOrNull { t -> t.count() >= 2 && t[0] == "anon" }
if (anonTag != null && anonTag.size > 1) {
var encnote = anonTag?.elementAt(1)
if (encnote != null && encnote != "") {
try {
val loggedInUserHex = NotificationFeedFilter.account.loggedIn.privKey!! // Replace without Filter
var note = LnZapRequestEvent.decrypt_privatezap_message(encnote, loggedInUserHex, zaprequest.pubKey.toByteArray())
var decryptedEvent = Event.fromJson(note)
if (decryptedEvent.kind == 9733) {
zaprequest.pubKey = decryptedEvent.pubKey
zaprequest.content = decryptedEvent.content
// return decryptedEvent
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
return zaprequest
}
fun consume(event: LnZapRequestEvent) {
val note = getOrCreateNote(event.id)

View File

@@ -15,11 +15,11 @@ import java.util.*
open class Event(
val id: HexKey,
@SerializedName("pubkey") val pubKey: HexKey,
@SerializedName("pubkey") var pubKey: HexKey,
@SerializedName("created_at") val createdAt: Long,
val kind: Int,
val tags: List<List<String>>,
val content: String,
var content: String,
val sig: HexKey
) : EventInterface {
override fun id(): HexKey = id

View File

@@ -42,10 +42,9 @@ class LnZapEvent(
null
}
}
override fun message(): String {
return message
override fun content(): String {
return content
}
val message = content
override fun containedPost(): Event? = try {
description()?.ifBlank { null }?.let {

View File

@@ -18,5 +18,4 @@ interface LnZapEventInterface : EventInterface {
fun containedPost(): Event?
fun message(): String
}

View File

@@ -6,6 +6,7 @@ import nostr.postr.Utils
import java.nio.charset.Charset
import java.security.SecureRandom
import java.util.*
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
@@ -52,21 +53,21 @@ class LnZapRequestEvent(
privkey = Utils.privkeyCreate()
pubKey = Utils.pubkeyCreate(privkey).toHexKey()
} else if (zapType == LnZapEvent.ZapType.PRIVATE) {
var enc_prkey = create_private_key(privateKey, originalNote.id(), createdAt)
var encryptionprkey = create_private_key(privateKey.toHexKey(), originalNote.id(), createdAt)
var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson()
var privreq = encrypt_privatezap_message(noteJson, enc_prkey, originalNote.pubKey().toByteArray())
var privreq = encrypt_privatezap_message(noteJson, encryptionprkey, originalNote.pubKey().toByteArray())
tags = tags + listOf(listOf("anon", privreq))
content = ""
privkey = enc_prkey
pubKey = Utils.pubkeyCreate(enc_prkey).toHexKey()
privkey = encryptionprkey
pubKey = Utils.pubkeyCreate(encryptionprkey).toHexKey()
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = Utils.sign(id, privkey)
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
}
fun create_private_key(privkey: ByteArray, id: String, createdAt: Long): ByteArray {
var str = privkey.toHexKey() + id + createdAt.toString()
fun create_private_key(privkey: String, id: String, createdAt: Long): ByteArray {
var str = privkey + id + createdAt.toString()
var strbyte = str.toByteArray(Charset.forName("utf-8"))
return sha256.digest(strbyte)
}
@@ -79,10 +80,10 @@ class LnZapRequestEvent(
val keySpec = SecretKeySpec(sharedSecret, "AES")
val ivSpec = IvParameterSpec(iv)
var utf8_message = msg.toByteArray(Charset.forName("utf-8"))
var utf8message = msg.toByteArray(Charset.forName("utf-8"))
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
val encryptedMsg = cipher.doFinal(utf8_message)
val encryptedMsg = cipher.doFinal(utf8message)
val encryptedMsgBech32 = Bech32.encode("pzap", Bech32.eight2five(encryptedMsg), Bech32.Encoding.Bech32)
val ivBech32 = Bech32.encode("iv", Bech32.eight2five(iv), Bech32.Encoding.Bech32)
@@ -92,24 +93,26 @@ class LnZapRequestEvent(
fun decrypt_privatezap_message(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
var sharedSecret = Utils.getSharedSecret(privkey, pubkey)
if (sharedSecret.size != 16 && sharedSecret.size != 32) {
throw IllegalArgumentException("Invalid shared secret size")
}
val parts = msg.split("_")
if (parts.size != 2) {
throw IllegalArgumentException("Invalid message format")
}
val iv = parts[1].run { Bech32.decode(this) }
val encryptedMsg = parts.first().run { Bech32.decode(this) }
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(Bech32.five2eight(iv.second, 0)))
return String(cipher.doFinal(Bech32.five2eight(encryptedMsg.second, 0)))
}
fun test_decrypt(privateKey: ByteArray, event: Event): String {
val anonTag = event.tags.firstOrNull { t -> t.count() >= 2 && t[0] == "anon" }
var encnote = anonTag?.elementAt(1)
if (encnote != null) {
var enc_prkey2 = create_private_key(privateKey, event.id(), event.createdAt)
return decrypt_privatezap_message(encnote, enc_prkey2, event.pubKey().toByteArray())
} else {
return ""
try {
val decryptedMsgBytes = cipher.doFinal(Bech32.five2eight(encryptedMsg.second, 0))
return String(decryptedMsgBytes)
} catch (ex: BadPaddingException) {
throw IllegalArgumentException("Bad padding")
}
}
fun create(
userHex: String,
relays: Set<String>,
@@ -130,7 +133,7 @@ class LnZapRequestEvent(
pubKey = Utils.pubkeyCreate(privkey).toHexKey()
tags = tags + listOf(listOf("anon", ""))
} else if (zapType == LnZapEvent.ZapType.PRIVATE) {
var enc_prkey = create_private_key(privateKey, userHex, createdAt)
var enc_prkey = create_private_key(privateKey.toHexKey(), userHex, createdAt)
var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson()
var privreq = encrypt_privatezap_message(noteJson, enc_prkey, userHex.toByteArray())
tags = tags + listOf(listOf("anon", privreq))

View File

@@ -8,13 +8,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.LocalCacheState
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.service.model.LnZapEvent
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
import com.vitorpamplona.amethyst.service.model.ReactionEvent
import com.vitorpamplona.amethyst.service.model.RepostEvent
import com.vitorpamplona.amethyst.service.model.*
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
@@ -88,6 +82,8 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
if (zappedPost != null) {
val zapRequest = zappedPost.zaps.filter { it.value == zapEvent }.keys.firstOrNull()
if (zapRequest != null) {
// var newZapRequestEvent = LocalCache.checkPrivateZap(zapRequest.event as Event)
// zapRequest.event = newZapRequestEvent
zapsPerEvent.getOrPut(zappedPost, { mutableMapOf() }).put(zapRequest, zapEvent)
}
} else {