From ff490a2b14ac21b5570c9d4020afbe4e105a8f57 Mon Sep 17 00:00:00 2001 From: Believethehype Date: Thu, 13 Apr 2023 00:15:36 +0200 Subject: [PATCH] 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") )