From ff490a2b14ac21b5570c9d4020afbe4e105a8f57 Mon Sep 17 00:00:00 2001 From: Believethehype Date: Thu, 13 Apr 2023 00:15:36 +0200 Subject: [PATCH 1/7] Send Private Zaps --- .../amethyst/service/model/LnZapEvent.kt | 2 +- .../service/model/LnZapRequestEvent.kt | 80 +++++++++++++++++-- .../amethyst/ui/note/ZapCustomDialog.kt | 1 + 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt index 1812fb803..9f3921c67 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt @@ -72,7 +72,7 @@ class LnZapEvent( enum class ZapType() { PUBLIC, - PRIVATE, // not yet implemented + PRIVATE, ANONYMOUS, NONZAP } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt index 34fddc02a..660e27d9a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt @@ -1,9 +1,14 @@ package com.vitorpamplona.amethyst.service.model -import com.vitorpamplona.amethyst.model.HexKey -import com.vitorpamplona.amethyst.model.toHexKey +import com.vitorpamplona.amethyst.model.* +import nostr.postr.Bech32 import nostr.postr.Utils -import java.util.Date +import java.nio.charset.Charset +import java.security.SecureRandom +import java.util.* +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec class LnZapRequestEvent( id: HexKey, @@ -28,7 +33,7 @@ class LnZapRequestEvent( zapType: LnZapEvent.ZapType, createdAt: Long = Date().time / 1000 ): LnZapRequestEvent { - val content = message + var content = message var privkey = privateKey var pubKey = Utils.pubkeyCreate(privateKey).toHexKey() var tags = listOf( @@ -46,12 +51,65 @@ class LnZapRequestEvent( tags = tags + listOf(listOf("anon", "")) 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 noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() + var privreq = encrypt_privatezap_message(noteJson, enc_prkey, originalNote.pubKey().toByteArray()) + tags = tags + listOf(listOf("anon", privreq)) + content = "" + privkey = enc_prkey + pubKey = Utils.pubkeyCreate(enc_prkey).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() + var strbyte = str.toByteArray(Charset.forName("utf-8")) + return sha256.digest(strbyte) + } + + fun encrypt_privatezap_message(msg: String, privkey: ByteArray, pubkey: ByteArray): String { + var sharedSecret = Utils.getSharedSecret(privkey, pubkey) + val iv = ByteArray(16) + SecureRandom().nextBytes(iv) + + val keySpec = SecretKeySpec(sharedSecret, "AES") + val ivSpec = IvParameterSpec(iv) + + var utf8_message = 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 encryptedMsgBech32 = Bech32.encode("pzap", Bech32.eight2five(encryptedMsg), Bech32.Encoding.Bech32) + val ivBech32 = Bech32.encode("iv", Bech32.eight2five(iv), Bech32.Encoding.Bech32) + + return encryptedMsgBech32 + "_" + ivBech32 + } + + fun decrypt_privatezap_message(msg: String, privkey: ByteArray, pubkey: ByteArray): String { + var sharedSecret = Utils.getSharedSecret(privkey, pubkey) + val parts = msg.split("_") + 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 "" + } + } fun create( userHex: String, relays: Set, @@ -60,7 +118,7 @@ class LnZapRequestEvent( zapType: LnZapEvent.ZapType, createdAt: Long = Date().time / 1000 ): LnZapRequestEvent { - val content = message + var content = message var privkey = privateKey var pubKey = Utils.pubkeyCreate(privateKey).toHexKey() var tags = listOf( @@ -68,18 +126,24 @@ class LnZapRequestEvent( listOf("relays") + relays ) if (zapType == LnZapEvent.ZapType.ANONYMOUS) { - tags = tags + listOf(listOf("anon", "")) privkey = Utils.privkeyCreate() 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 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)) + content = "" + privkey = enc_prkey + pubKey = Utils.pubkeyCreate(enc_prkey).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()) } } } - /* { "pubkey": "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt index 5fe601a6a..6239a0abb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt @@ -67,6 +67,7 @@ fun ZapCustomDialog(onClose: () -> Unit, account: Account, accountViewModel: Acc val zapTypes = listOf( Pair(LnZapEvent.ZapType.PUBLIC, "Public"), + Pair(LnZapEvent.ZapType.PRIVATE, "Private"), Pair(LnZapEvent.ZapType.ANONYMOUS, "Anonymous"), Pair(LnZapEvent.ZapType.NONZAP, "Non-Zap") ) From 5e6e2835ca0087c666c6a38b430230d1a126d27b Mon Sep 17 00:00:00 2001 From: Believethehype Date: Mon, 17 Apr 2023 20:10:36 +0200 Subject: [PATCH 2/7] 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 --- .../amethyst/model/LocalCache.kt | 22 ++++++++++ .../amethyst/service/model/Event.kt | 4 +- .../amethyst/service/model/LnZapEvent.kt | 5 +-- .../service/model/LnZapEventInterface.kt | 1 - .../service/model/LnZapRequestEvent.kt | 41 ++++++++++--------- .../amethyst/ui/screen/CardFeedViewModel.kt | 10 ++--- 6 files changed, 51 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index cef63d463..be45ab09d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt index 818a2d3b2..b3e7dc182 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt @@ -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>, - val content: String, + var content: String, val sig: HexKey ) : EventInterface { override fun id(): HexKey = id diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt index 9f3921c67..eaa82509a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt @@ -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 { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt index b69e02781..b60fcd5b1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt @@ -18,5 +18,4 @@ interface LnZapEventInterface : EventInterface { fun containedPost(): Event? - fun message(): String } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt index 660e27d9a..a3b3b6285 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt @@ -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, @@ -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)) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index 3c83d0945..5cb55bb81 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -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) : 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 { From 7a60a6804a21d484b2aee15ee196b678d94f9fb8 Mon Sep 17 00:00:00 2001 From: Believethehype Date: Mon, 17 Apr 2023 20:14:17 +0200 Subject: [PATCH 3/7] Update LnZapEventInterface.kt --- .../vitorpamplona/amethyst/service/model/LnZapEventInterface.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt index b60fcd5b1..934dba55b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEventInterface.kt @@ -17,5 +17,4 @@ interface LnZapEventInterface : EventInterface { fun amount(): BigDecimal? fun containedPost(): Event? - } From 1d23e87bd952b10b1a04c788615422a6ba23f51e Mon Sep 17 00:00:00 2001 From: Believethehype Date: Sat, 22 Apr 2023 05:32:48 +0200 Subject: [PATCH 4/7] refactoring --- .../amethyst/model/LocalCache.kt | 33 +++++++------------ .../amethyst/service/model/Event.kt | 4 +-- .../service/model/LnZapRequestEvent.kt | 33 +++++++++++++++---- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 9385bddba..e0771efb1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -4,10 +4,10 @@ import android.util.Log import androidx.lifecycle.LiveData import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.vitorpamplona.amethyst.service.NostrAccountDataSource.account import com.vitorpamplona.amethyst.service.model.* import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.ui.components.BundledInsert -import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter import fr.acinq.secp256k1.Hex import kotlinx.coroutines.* import nostr.postr.toNpub @@ -595,6 +595,16 @@ object LocalCache { fun consume(event: LnZapEvent) { val note = getOrCreateNote(event.id) + var decryptedContent = LnZapRequestEvent.checkForPrivateZap(event.zapRequest!!, account.loggedIn.privKey!!) + if (decryptedContent != null) { + Log.e( + "DC", + "Decrypted Event: Sender: {${decryptedContent.pubKey}}, Message: {${decryptedContent.content}} " + + // TODO Update Notification with this Sender and Message + ) + } + // Already processed this event. if (note.event != null) return @@ -627,27 +637,6 @@ object LocalCache { refreshObservers(note) } - 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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt index 85a47e1c9..6d857328c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/Event.kt @@ -15,11 +15,11 @@ import java.util.* open class Event( val id: HexKey, - @SerializedName("pubkey") var pubKey: HexKey, + @SerializedName("pubkey") val pubKey: HexKey, @SerializedName("created_at") val createdAt: Long, val kind: Int, val tags: List>, - var content: String, + val content: String, val sig: HexKey ) : EventInterface { override fun id(): HexKey = id diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt index c08e0b1f6..e541f7382 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt @@ -55,9 +55,9 @@ class LnZapRequestEvent( privkey = Utils.privkeyCreate() pubKey = Utils.pubkeyCreate(privkey).toHexKey() } else if (zapType == LnZapEvent.ZapType.PRIVATE) { - var encryptionprkey = create_private_key(privateKey.toHexKey(), originalNote.id(), createdAt) + var encryptionprkey = createPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt) var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() - var privreq = encrypt_privatezap_message(noteJson, encryptionprkey, originalNote.pubKey().toByteArray()) + var privreq = encryptPrivateZapMessage(noteJson, encryptionprkey, originalNote.pubKey().toByteArray()) tags = tags + listOf(listOf("anon", privreq)) content = "" privkey = encryptionprkey @@ -68,13 +68,13 @@ class LnZapRequestEvent( return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) } - fun create_private_key(privkey: String, id: String, createdAt: Long): ByteArray { + fun createPrivateKey(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) } - fun encrypt_privatezap_message(msg: String, privkey: ByteArray, pubkey: ByteArray): String { + fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String { var sharedSecret = Utils.getSharedSecret(privkey, pubkey) val iv = ByteArray(16) SecureRandom().nextBytes(iv) @@ -93,7 +93,7 @@ class LnZapRequestEvent( return encryptedMsgBech32 + "_" + ivBech32 } - fun decrypt_privatezap_message(msg: String, privkey: ByteArray, pubkey: ByteArray): String { + fun decryptPrivateZapMessage(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") @@ -115,6 +115,25 @@ class LnZapRequestEvent( } } + fun checkForPrivateZap(zaprequest: Event, loggedInUserPrivKey: ByteArray): 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 { + var note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, zaprequest.pubKey.toByteArray()) + var decryptedEvent = fromJson(note) + if (decryptedEvent.kind == 9733) { + return decryptedEvent + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + return null + } + fun create( userHex: String, relays: Set, @@ -135,9 +154,9 @@ 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.toHexKey(), userHex, createdAt) + var enc_prkey = createPrivateKey(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()) + var privreq = encryptPrivateZapMessage(noteJson, enc_prkey, userHex.toByteArray()) tags = tags + listOf(listOf("anon", privreq)) content = "" privkey = enc_prkey From 12a3cd5113f0ff5143555d18f317f5ab28fe62da Mon Sep 17 00:00:00 2001 From: Believethehype Date: Sat, 22 Apr 2023 05:37:52 +0200 Subject: [PATCH 5/7] Update LocalCache.kt --- .../main/java/com/vitorpamplona/amethyst/model/LocalCache.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index e0771efb1..140a30849 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -599,7 +599,7 @@ object LocalCache { if (decryptedContent != null) { Log.e( "DC", - "Decrypted Event: Sender: {${decryptedContent.pubKey}}, Message: {${decryptedContent.content}} " + "Decrypted Content from Anon Tag: Sender: {${decryptedContent.pubKey}}, Message: {${decryptedContent.content}} " // TODO Update Notification with this Sender and Message ) From bd64a7de63f90e524e1bfd069a5a727f2248bb12 Mon Sep 17 00:00:00 2001 From: Believethehype Date: Sat, 22 Apr 2023 05:50:38 +0200 Subject: [PATCH 6/7] clean up --- .../service/model/LnZapRequestEvent.kt | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt index e541f7382..221d470dd 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt @@ -55,20 +55,53 @@ class LnZapRequestEvent( privkey = Utils.privkeyCreate() pubKey = Utils.pubkeyCreate(privkey).toHexKey() } else if (zapType == LnZapEvent.ZapType.PRIVATE) { - var encryptionprkey = createPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt) + var encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt) var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() - var privreq = encryptPrivateZapMessage(noteJson, encryptionprkey, originalNote.pubKey().toByteArray()) - tags = tags + listOf(listOf("anon", privreq)) - content = "" - privkey = encryptionprkey - pubKey = Utils.pubkeyCreate(encryptionprkey).toHexKey() + var encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, originalNote.pubKey().toByteArray()) + tags = tags + listOf(listOf("anon", encryptedContent)) + content = "" // make sure public content is empty, as the content is encrypted + privkey = encryptionPrivateKey // sign event with generated privkey + pubKey = Utils.pubkeyCreate(encryptionPrivateKey).toHexKey() // updated event with according pubkey } 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 createPrivateKey(privkey: String, id: String, createdAt: Long): ByteArray { + fun create( + userHex: String, + relays: Set, + privateKey: ByteArray, + message: String, + zapType: LnZapEvent.ZapType, + createdAt: Long = Date().time / 1000 + ): LnZapRequestEvent { + var content = message + var privkey = privateKey + var pubKey = Utils.pubkeyCreate(privateKey).toHexKey() + var tags = listOf( + listOf("p", userHex), + listOf("relays") + relays + ) + if (zapType == LnZapEvent.ZapType.ANONYMOUS) { + privkey = Utils.privkeyCreate() + pubKey = Utils.pubkeyCreate(privkey).toHexKey() + tags = tags + listOf(listOf("anon", "")) + } else if (zapType == LnZapEvent.ZapType.PRIVATE) { + var enc_prkey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt) + var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() + var privreq = encryptPrivateZapMessage(noteJson, enc_prkey, userHex.toByteArray()) + tags = tags + listOf(listOf("anon", privreq)) + content = "" + privkey = enc_prkey + pubKey = Utils.pubkeyCreate(enc_prkey).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 createEncryptionPrivateKey(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) @@ -116,13 +149,13 @@ class LnZapRequestEvent( } fun checkForPrivateZap(zaprequest: Event, loggedInUserPrivKey: ByteArray): Event? { - var anonTag = zaprequest.tags.firstOrNull { t -> t.count() >= 2 && t[0] == "anon" } + val anonTag = zaprequest.tags.firstOrNull { t -> t.count() >= 2 && t[0] == "anon" } if (anonTag != null && anonTag.size > 1) { - var encnote = anonTag?.elementAt(1) + val encnote = anonTag?.elementAt(1) if (encnote != null && encnote != "") { try { - var note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, zaprequest.pubKey.toByteArray()) - var decryptedEvent = fromJson(note) + val note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, zaprequest.pubKey.toByteArray()) + val decryptedEvent = fromJson(note) if (decryptedEvent.kind == 9733) { return decryptedEvent } @@ -133,39 +166,6 @@ class LnZapRequestEvent( } return null } - - fun create( - userHex: String, - relays: Set, - privateKey: ByteArray, - message: String, - zapType: LnZapEvent.ZapType, - createdAt: Long = Date().time / 1000 - ): LnZapRequestEvent { - var content = message - var privkey = privateKey - var pubKey = Utils.pubkeyCreate(privateKey).toHexKey() - var tags = listOf( - listOf("p", userHex), - listOf("relays") + relays - ) - if (zapType == LnZapEvent.ZapType.ANONYMOUS) { - privkey = Utils.privkeyCreate() - pubKey = Utils.pubkeyCreate(privkey).toHexKey() - tags = tags + listOf(listOf("anon", "")) - } else if (zapType == LnZapEvent.ZapType.PRIVATE) { - var enc_prkey = createPrivateKey(privateKey.toHexKey(), userHex, createdAt) - var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() - var privreq = encryptPrivateZapMessage(noteJson, enc_prkey, userHex.toByteArray()) - tags = tags + listOf(listOf("anon", privreq)) - content = "" - privkey = enc_prkey - pubKey = Utils.pubkeyCreate(enc_prkey).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()) - } } } /* From 9ae13a9f97e64a5d845880774c593cf4cd78c41f Mon Sep 17 00:00:00 2001 From: Believethehype Date: Sat, 22 Apr 2023 05:54:43 +0200 Subject: [PATCH 7/7] Update LnZapRequestEvent.kt --- .../amethyst/service/model/LnZapRequestEvent.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt index 221d470dd..32583b549 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt @@ -88,13 +88,13 @@ class LnZapRequestEvent( pubKey = Utils.pubkeyCreate(privkey).toHexKey() tags = tags + listOf(listOf("anon", "")) } else if (zapType == LnZapEvent.ZapType.PRIVATE) { - var enc_prkey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt) + var encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt) var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() - var privreq = encryptPrivateZapMessage(noteJson, enc_prkey, userHex.toByteArray()) - tags = tags + listOf(listOf("anon", privreq)) + var encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, userHex.toByteArray()) + tags = tags + listOf(listOf("anon", encryptedContent)) content = "" - privkey = enc_prkey - pubKey = Utils.pubkeyCreate(enc_prkey).toHexKey() + privkey = encryptionPrivateKey + pubKey = Utils.pubkeyCreate(encryptionPrivateKey).toHexKey() } val id = generateId(pubKey, createdAt, kind, tags, content) val sig = Utils.sign(id, privkey)