Caching Private Zap decryption
This commit is contained in:
@@ -78,7 +78,7 @@ class PrivateZapTests {
|
|||||||
if (recepientPK != null && recepientPost != null) {
|
if (recepientPK != null && recepientPost != null) {
|
||||||
val privateKey = createEncryptionPrivateKey(loggedIn.toHexKey(), recepientPost, privateZapRequest.createdAt)
|
val privateKey = createEncryptionPrivateKey(loggedIn.toHexKey(), recepientPost, privateZapRequest.createdAt)
|
||||||
val decodedPrivateZap =
|
val decodedPrivateZap =
|
||||||
LnZapRequestEvent.checkForPrivateZap(privateZapRequest, privateKey, recepientPK)
|
privateZapRequest.getPrivateZapEvent(privateKey, recepientPK)
|
||||||
|
|
||||||
println(decodedPrivateZap?.toJson())
|
println(decodedPrivateZap?.toJson())
|
||||||
assertNotNull(decodedPrivateZap)
|
assertNotNull(decodedPrivateZap)
|
||||||
@@ -127,8 +127,7 @@ class PrivateZapTests {
|
|||||||
|
|
||||||
if (recepientPK != null && recepientPost != null) {
|
if (recepientPK != null && recepientPost != null) {
|
||||||
val privateKey = createEncryptionPrivateKey(loggedIn.toHexKey(), recepientPost, privateZapRequest.createdAt)
|
val privateKey = createEncryptionPrivateKey(loggedIn.toHexKey(), recepientPost, privateZapRequest.createdAt)
|
||||||
val decodedPrivateZap =
|
val decodedPrivateZap = privateZapRequest.getPrivateZapEvent(privateKey, recepientPK)
|
||||||
LnZapRequestEvent.checkForPrivateZap(privateZapRequest, privateKey, recepientPK)
|
|
||||||
|
|
||||||
println(decodedPrivateZap?.toJson())
|
println(decodedPrivateZap?.toJson())
|
||||||
assertNotNull(decodedPrivateZap)
|
assertNotNull(decodedPrivateZap)
|
||||||
|
|||||||
@@ -847,7 +847,7 @@ class Account(
|
|||||||
val privateKeyToUse = loggedInPrivateKey
|
val privateKeyToUse = loggedInPrivateKey
|
||||||
val pubkeyToUse = event.pubKey
|
val pubkeyToUse = event.pubKey
|
||||||
|
|
||||||
LnZapRequestEvent.checkForPrivateZap(event, privateKeyToUse, pubkeyToUse)
|
event.getPrivateZapEvent(privateKeyToUse, pubkeyToUse)
|
||||||
} else {
|
} else {
|
||||||
// if the sender is logged in, these are the params
|
// if the sender is logged in, these are the params
|
||||||
val altPubkeyToUse = recipientPK
|
val altPubkeyToUse = recipientPK
|
||||||
@@ -868,7 +868,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (altPrivateKeyToUse != null && altPubkeyToUse != null) {
|
if (altPrivateKeyToUse != null && altPubkeyToUse != null) {
|
||||||
val result = LnZapRequestEvent.checkForPrivateZap(event, altPrivateKeyToUse, altPubkeyToUse)
|
val result = event.getPrivateZapEvent(altPrivateKeyToUse, altPubkeyToUse)
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
Log.w("Private ZAP Decrypt", "Fail to decrypt Zap from ${note.author?.toBestDisplayName()} ${note.idNote()}")
|
Log.w("Private ZAP Decrypt", "Fail to decrypt Zap from ${note.author?.toBestDisplayName()} ${note.idNote()}")
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -21,9 +21,17 @@ class LnZapPaymentRequestEvent(
|
|||||||
sig: HexKey
|
sig: HexKey
|
||||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
|
|
||||||
|
// Once one of an app user decrypts the payment, all users else can see it.
|
||||||
|
@Transient
|
||||||
|
private var lnInvoice: String? = null
|
||||||
|
|
||||||
fun walletServicePubKey() = tags.firstOrNull() { it.size > 1 && it[0] == "p" }?.get(1)
|
fun walletServicePubKey() = tags.firstOrNull() { it.size > 1 && it[0] == "p" }?.get(1)
|
||||||
|
|
||||||
fun lnInvoice(privKey: ByteArray, pubkey: ByteArray): String? {
|
fun lnInvoice(privKey: ByteArray, pubkey: ByteArray): String? {
|
||||||
|
if (lnInvoice != null) {
|
||||||
|
return lnInvoice
|
||||||
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = Utils.getSharedSecret(privKey, pubkey)
|
val sharedSecret = Utils.getSharedSecret(privKey, pubkey)
|
||||||
|
|
||||||
@@ -31,7 +39,9 @@ class LnZapPaymentRequestEvent(
|
|||||||
|
|
||||||
val payInvoiceMethod = gson.fromJson(jsonText, Request::class.java)
|
val payInvoiceMethod = gson.fromJson(jsonText, Request::class.java)
|
||||||
|
|
||||||
return (payInvoiceMethod as? PayInvoiceMethod)?.params?.invoice
|
lnInvoice = (payInvoiceMethod as? PayInvoiceMethod)?.params?.invoice
|
||||||
|
|
||||||
|
return lnInvoice
|
||||||
} 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
|
||||||
|
|||||||
+12
-3
@@ -19,10 +19,14 @@ class LnZapPaymentResponseEvent(
|
|||||||
sig: HexKey
|
sig: HexKey
|
||||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
|
|
||||||
|
// Once one of an app user decrypts the payment, all users else can see it.
|
||||||
|
@Transient
|
||||||
|
private var response: Response? = null
|
||||||
|
|
||||||
fun requestAuthor() = tags.firstOrNull() { it.size > 1 && it[0] == "p" }?.get(1)
|
fun requestAuthor() = tags.firstOrNull() { it.size > 1 && it[0] == "p" }?.get(1)
|
||||||
fun requestId() = tags.firstOrNull() { it.size > 1 && it[0] == "e" }?.get(1)
|
fun requestId() = tags.firstOrNull() { it.size > 1 && it[0] == "e" }?.get(1)
|
||||||
|
|
||||||
fun decrypt(privKey: ByteArray, pubKey: ByteArray): String? {
|
private fun decrypt(privKey: ByteArray, pubKey: ByteArray): String? {
|
||||||
return try {
|
return try {
|
||||||
val sharedSecret = Utils.getSharedSecret(privKey, pubKey)
|
val sharedSecret = Utils.getSharedSecret(privKey, pubKey)
|
||||||
|
|
||||||
@@ -39,10 +43,14 @@ class LnZapPaymentResponseEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun response(privKey: ByteArray, pubKey: ByteArray): Response? = try {
|
fun response(privKey: ByteArray, pubKey: ByteArray): Response? {
|
||||||
|
if (response != null) response
|
||||||
|
|
||||||
|
return try {
|
||||||
if (content.isNotEmpty()) {
|
if (content.isNotEmpty()) {
|
||||||
val decrypted = decrypt(privKey, pubKey)
|
val decrypted = decrypt(privKey, pubKey)
|
||||||
gson.fromJson(decrypted, Response::class.java)
|
response = gson.fromJson(decrypted, Response::class.java)
|
||||||
|
response
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -50,6 +58,7 @@ class LnZapPaymentResponseEvent(
|
|||||||
Log.w("LnZapPaymentResponseEvent", "Can't parse content as a payment response: $content", e)
|
Log.w("LnZapPaymentResponseEvent", "Can't parse content as a payment response: $content", e)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val kind = 23195
|
const val kind = 23195
|
||||||
|
|||||||
@@ -20,12 +20,37 @@ class LnZapRequestEvent(
|
|||||||
sig: HexKey
|
sig: HexKey
|
||||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private var privateZapEvent: Event? = null
|
||||||
|
|
||||||
fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
|
fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
|
||||||
|
|
||||||
fun zappedAuthor() = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
|
fun zappedAuthor() = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
|
||||||
|
|
||||||
fun isPrivateZap() = tags.any { t -> t.size >= 2 && t[0] == "anon" && t[1].isNotBlank() }
|
fun isPrivateZap() = tags.any { t -> t.size >= 2 && t[0] == "anon" && t[1].isNotBlank() }
|
||||||
|
|
||||||
|
fun getPrivateZapEvent(loggedInUserPrivKey: ByteArray, pubKey: HexKey): Event? {
|
||||||
|
if (privateZapEvent != null) return privateZapEvent
|
||||||
|
|
||||||
|
val anonTag = tags.firstOrNull { t -> t.size >= 2 && t[0] == "anon" }
|
||||||
|
if (anonTag != null) {
|
||||||
|
val encnote = anonTag[1]
|
||||||
|
if (encnote.isNotBlank()) {
|
||||||
|
try {
|
||||||
|
val note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, pubKey.hexToByteArray())
|
||||||
|
val decryptedEvent = fromJson(note)
|
||||||
|
if (decryptedEvent.kind == 9733) {
|
||||||
|
privateZapEvent = decryptedEvent
|
||||||
|
return privateZapEvent
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val kind = 9734
|
const val kind = 9734
|
||||||
|
|
||||||
@@ -109,7 +134,7 @@ class LnZapRequestEvent(
|
|||||||
return sha256.digest(strbyte)
|
return sha256.digest(strbyte)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
||||||
var sharedSecret = Utils.getSharedSecret(privkey, pubkey)
|
var sharedSecret = Utils.getSharedSecret(privkey, pubkey)
|
||||||
val iv = ByteArray(16)
|
val iv = ByteArray(16)
|
||||||
SecureRandom().nextBytes(iv)
|
SecureRandom().nextBytes(iv)
|
||||||
@@ -128,7 +153,7 @@ class LnZapRequestEvent(
|
|||||||
return encryptedMsgBech32 + "_" + ivBech32
|
return encryptedMsgBech32 + "_" + ivBech32
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
private fun decryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
||||||
var sharedSecret = Utils.getSharedSecret(privkey, pubkey)
|
var sharedSecret = Utils.getSharedSecret(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")
|
||||||
@@ -150,61 +175,5 @@ class LnZapRequestEvent(
|
|||||||
throw IllegalArgumentException("Bad padding: ${ex.message}")
|
throw IllegalArgumentException("Bad padding: ${ex.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkForPrivateZap(zapRequest: LnZapRequestEvent, loggedInUserPrivKey: ByteArray, pubKey: HexKey): Event? {
|
|
||||||
val anonTag = zapRequest.tags.firstOrNull { t -> t.size >= 2 && t[0] == "anon" }
|
|
||||||
if (anonTag != null) {
|
|
||||||
val encnote = anonTag[1]
|
|
||||||
if (encnote.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, pubKey.hexToByteArray())
|
|
||||||
val decryptedEvent = fromJson(note)
|
|
||||||
if (decryptedEvent.kind == 9733) {
|
|
||||||
return decryptedEvent
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"pubkey": "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245",
|
|
||||||
"content": "",
|
|
||||||
"id": "d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d",
|
|
||||||
"created_at": 1674164539,
|
|
||||||
"sig": "77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d",
|
|
||||||
"kind": 9734,
|
|
||||||
"tags": [
|
|
||||||
[
|
|
||||||
"e",
|
|
||||||
"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"p",
|
|
||||||
"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"relays",
|
|
||||||
"wss://relay.damus.io",
|
|
||||||
"wss://nostr-relay.wlvs.space",
|
|
||||||
"wss://nostr.fmt.wiz.biz",
|
|
||||||
"wss://relay.nostr.bg",
|
|
||||||
"wss://nostr.oxtr.dev",
|
|
||||||
"wss://nostr.v0l.io",
|
|
||||||
"wss://brb.io",
|
|
||||||
"wss://nostr.bitcoiner.social",
|
|
||||||
"ws://monad.jb55.com:8080",
|
|
||||||
"wss://relay.snort.social"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"poll_option", "n"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"ots": <base64-encoded OTS file data> // TODO
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
Reference in New Issue
Block a user