From f027d23fcf6b505a6abf5d61e35f726dc70d62bc Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 22 Aug 2023 16:10:32 -0400 Subject: [PATCH] Small refactoring to create the PrivateZap Event --- .../quartz/events/EventFactory.kt | 4 +- .../quartz/events/LnZapPrivateEvent.kt | 43 +++++++++++++++++++ .../quartz/events/LnZapRequestEvent.kt | 29 +++++++------ 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPrivateEvent.kt diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/EventFactory.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/EventFactory.kt index 745d03ecd..70f66321a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/EventFactory.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/EventFactory.kt @@ -41,9 +41,6 @@ class EventFactory { ContactListEvent.kind -> ContactListEvent(id, pubKey, createdAt, tags, content, sig) DeletionEvent.kind -> DeletionEvent(id, pubKey, createdAt, tags, content, sig) - // Will never happen. - // DirectMessageEvent.kind -> DirectMessageEvent(createdAt, tags, content) - EmojiPackEvent.kind -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig) EmojiPackSelectionEvent.kind -> EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig) SealedGossipEvent.kind -> SealedGossipEvent(id, pubKey, createdAt, tags, content, sig) @@ -59,6 +56,7 @@ class EventFactory { LnZapEvent.kind -> LnZapEvent(id, pubKey, createdAt, tags, content, sig) LnZapPaymentRequestEvent.kind -> LnZapPaymentRequestEvent(id, pubKey, createdAt, tags, content, sig) LnZapPaymentResponseEvent.kind -> LnZapPaymentResponseEvent(id, pubKey, createdAt, tags, content, sig) + LnZapPrivateEvent.kind -> LnZapPrivateEvent(id, pubKey, createdAt, tags, content, sig) LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig) LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig) MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPrivateEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPrivateEvent.kt new file mode 100644 index 000000000..b3207d3ac --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapPrivateEvent.kt @@ -0,0 +1,43 @@ +package com.vitorpamplona.quartz.events + +import android.util.Log +import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.crypto.CryptoUtils +import com.vitorpamplona.quartz.encoders.Bech32 +import com.vitorpamplona.quartz.encoders.HexKey +import com.vitorpamplona.quartz.encoders.LnInvoiceUtil +import com.vitorpamplona.quartz.encoders.toHexKey +import com.vitorpamplona.quartz.utils.TimeUtils +import java.nio.charset.Charset +import java.security.SecureRandom +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +@Immutable +class LnZapPrivateEvent( + id: HexKey, + pubKey: HexKey, + createdAt: Long, + tags: List>, + content: String, + sig: HexKey +) : Event(id, pubKey, createdAt, kind, tags, content, sig) { + + companion object { + const val kind = 9733 + + fun create( + privateKey: ByteArray, + tags: List> = emptyList(), + content: String = "", + createdAt: Long = TimeUtils.now() + ): Event { + val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() + val id = generateId(pubKey, createdAt, kind, tags, content) + val sig = CryptoUtils.sign(id, privateKey).toHexKey() + return Event(id.toHexKey(), pubKey, createdAt, kind, tags, content, sig) + } + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt index c318ca669..50cbe743d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/LnZapRequestEvent.kt @@ -86,9 +86,9 @@ class LnZapRequestEvent( privkey = CryptoUtils.privkeyCreate() pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey() } else if (zapType == LnZapEvent.ZapType.PRIVATE) { - var encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt) - var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() - var encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, originalNote.pubKey().hexToByteArray()) + val encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt) + val noteJson = (LnZapPrivateEvent.create(privkey, listOf(tags[0], tags[1]), message)).toJson() + val encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, originalNote.pubKey().hexToByteArray()) 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 @@ -119,9 +119,9 @@ class LnZapRequestEvent( pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey() tags = tags + listOf(listOf("anon", "")) } else if (zapType == LnZapEvent.ZapType.PRIVATE) { - var encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt) - var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson() - var encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, userHex.hexToByteArray()) + val encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt) + val noteJson = LnZapPrivateEvent.create(privkey, listOf(tags[0], tags[1]), message).toJson() + val encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, userHex.hexToByteArray()) tags = tags + listOf(listOf("anon", encryptedContent)) content = "" privkey = encryptionPrivateKey @@ -132,21 +132,22 @@ class LnZapRequestEvent( 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")) + val str = privkey + id + createdAt.toString() + val strbyte = str.toByteArray(Charset.forName("utf-8")) return CryptoUtils.sha256(strbyte) } private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String { - var sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey) + val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey) val iv = ByteArray(16) SecureRandom().nextBytes(iv) val keySpec = SecretKeySpec(sharedSecret, "AES") val ivSpec = IvParameterSpec(iv) - var utf8message = msg.toByteArray(Charset.forName("utf-8")) + val 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(utf8message) @@ -158,7 +159,7 @@ class LnZapRequestEvent( } private fun decryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String { - var sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey) + val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey) if (sharedSecret.size != 16 && sharedSecret.size != 32) { throw IllegalArgumentException("Invalid shared secret size") } @@ -170,8 +171,10 @@ class LnZapRequestEvent( val encryptedMsg = parts.first().run { Bech32.decode(this).second } val encryptedBytes = Bech32.five2eight(encryptedMsg, 0) val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec( - Bech32.five2eight(iv, 0))) + cipher.init( + Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec( + Bech32.five2eight(iv, 0)) + ) try { val decryptedMsgBytes = cipher.doFinal(encryptedBytes)