mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-05 19:08:23 +00:00
Add portable NIP-46 crypto + protocol parsing to :shared/commonMain
BunkerSigner wraps Quartz's NostrSignerInternal for sign/nip44/nip04 encrypt-decrypt; BunkerMethod/BunkerRequestPayload mirror Amber's existing bunker-request method dispatch and per-method params layout (app/src/main/java/.../service/BunkerRequestUtils.kt) as pure, platform-agnostic functions with unit tests running on the JVM target.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package com.greenart7c3.nostrsigner.shared
|
||||
|
||||
/** The NIP-46 bunker RPC methods a signer can be asked to perform. */
|
||||
enum class BunkerMethod {
|
||||
CONNECT,
|
||||
SIGN_EVENT,
|
||||
NIP04_ENCRYPT,
|
||||
NIP04_DECRYPT,
|
||||
NIP44_ENCRYPT,
|
||||
NIP44_DECRYPT,
|
||||
NIP44_V3_ENCRYPT,
|
||||
NIP44_V3_DECRYPT,
|
||||
GET_PUBLIC_KEY,
|
||||
DECRYPT_ZAP_EVENT,
|
||||
PING,
|
||||
SWITCH_RELAYS,
|
||||
SIGN_PSBT,
|
||||
LOGOUT,
|
||||
INVALID,
|
||||
}
|
||||
|
||||
fun bunkerMethodOf(method: String): BunkerMethod = when (method) {
|
||||
"connect" -> BunkerMethod.CONNECT
|
||||
"sign_event" -> BunkerMethod.SIGN_EVENT
|
||||
"get_public_key" -> BunkerMethod.GET_PUBLIC_KEY
|
||||
"nip04_encrypt" -> BunkerMethod.NIP04_ENCRYPT
|
||||
"nip04_decrypt" -> BunkerMethod.NIP04_DECRYPT
|
||||
"nip44_encrypt" -> BunkerMethod.NIP44_ENCRYPT
|
||||
"nip44_decrypt" -> BunkerMethod.NIP44_DECRYPT
|
||||
"nip44v3_encrypt" -> BunkerMethod.NIP44_V3_ENCRYPT
|
||||
"nip44v3_decrypt" -> BunkerMethod.NIP44_V3_DECRYPT
|
||||
"decrypt_zap_event" -> BunkerMethod.DECRYPT_ZAP_EVENT
|
||||
"ping" -> BunkerMethod.PING
|
||||
"switch_relays" -> BunkerMethod.SWITCH_RELAYS
|
||||
"sign_psbt" -> BunkerMethod.SIGN_PSBT
|
||||
"logout" -> BunkerMethod.LOGOUT
|
||||
else -> BunkerMethod.INVALID
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.greenart7c3.nostrsigner.shared
|
||||
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
|
||||
|
||||
/**
|
||||
* Pure helpers for reading the fields a [BunkerRequest]'s NIP-46 `params` array carries,
|
||||
* mirroring the NIP-46 spec's per-method param layout. No signing/encryption happens here —
|
||||
* see [BunkerSigner] for that.
|
||||
*/
|
||||
object BunkerRequestPayload {
|
||||
/** The event JSON (for `sign_event`), ciphertext/plaintext payload, or PSBT hex a request carries, if any. */
|
||||
fun payload(bunkerRequest: BunkerRequest): String = when (bunkerMethodOf(bunkerRequest.method)) {
|
||||
BunkerMethod.SIGN_EVENT -> bunkerRequest.params.firstOrNull() ?: ""
|
||||
BunkerMethod.NIP04_ENCRYPT, BunkerMethod.NIP04_DECRYPT,
|
||||
BunkerMethod.NIP44_ENCRYPT, BunkerMethod.NIP44_DECRYPT,
|
||||
BunkerMethod.DECRYPT_ZAP_EVENT,
|
||||
-> bunkerRequest.params.getOrElse(1) { "" }
|
||||
// NIP-44 v3 NIP-46 layout: [pubkey, kind, scope, payload]. For
|
||||
// `nip44v3_encrypt` the payload is base64-encoded plaintext; for
|
||||
// `nip44v3_decrypt` it is the v3 ciphertext.
|
||||
BunkerMethod.NIP44_V3_ENCRYPT, BunkerMethod.NIP44_V3_DECRYPT -> bunkerRequest.params.getOrElse(3) { "" }
|
||||
BunkerMethod.SIGN_PSBT -> bunkerRequest.params.firstOrNull() ?: ""
|
||||
else -> ""
|
||||
}
|
||||
|
||||
/** The counterparty pubkey a NIP-04/NIP-44 encrypt/decrypt request targets, if any. */
|
||||
fun counterpartyPubKey(bunkerRequest: BunkerRequest): String? = when (bunkerMethodOf(bunkerRequest.method)) {
|
||||
BunkerMethod.NIP04_ENCRYPT, BunkerMethod.NIP04_DECRYPT,
|
||||
BunkerMethod.NIP44_ENCRYPT, BunkerMethod.NIP44_DECRYPT,
|
||||
BunkerMethod.NIP44_V3_ENCRYPT, BunkerMethod.NIP44_V3_DECRYPT,
|
||||
-> bunkerRequest.params.firstOrNull()
|
||||
else -> null
|
||||
}
|
||||
|
||||
/** Extract `kind` from a NIP-44 v3 bunker request; null if missing/invalid. */
|
||||
fun nip44v3Kind(bunkerRequest: BunkerRequest): Int? = bunkerRequest.params.getOrNull(1)?.toIntOrNull()
|
||||
|
||||
/** Extract `scope` from a NIP-44 v3 bunker request; defaults to empty per spec. */
|
||||
fun nip44v3Scope(bunkerRequest: BunkerRequest): String = bunkerRequest.params.getOrElse(2) { "" }
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.greenart7c3.nostrsigner.shared
|
||||
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class BunkerRequestPayloadTest {
|
||||
@Test
|
||||
fun signEventPayload() {
|
||||
val request = BunkerRequest("id-1", "sign_event", arrayOf("{\"kind\":1}"))
|
||||
assertEquals(BunkerMethod.SIGN_EVENT, bunkerMethodOf(request.method))
|
||||
assertEquals("{\"kind\":1}", BunkerRequestPayload.payload(request))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nip44EncryptPayload() {
|
||||
val request = BunkerRequest("id-2", "nip44_encrypt", arrayOf("deadbeef", "plaintext"))
|
||||
assertEquals(BunkerMethod.NIP44_ENCRYPT, bunkerMethodOf(request.method))
|
||||
assertEquals("plaintext", BunkerRequestPayload.payload(request))
|
||||
assertEquals("deadbeef", BunkerRequestPayload.counterpartyPubKey(request))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nip44v3Payload() {
|
||||
val request = BunkerRequest("id-3", "nip44v3_encrypt", arrayOf("deadbeef", "7", "scope", "cGF5bG9hZA=="))
|
||||
assertEquals(BunkerMethod.NIP44_V3_ENCRYPT, bunkerMethodOf(request.method))
|
||||
assertEquals("cGF5bG9hZA==", BunkerRequestPayload.payload(request))
|
||||
assertEquals(7, BunkerRequestPayload.nip44v3Kind(request))
|
||||
assertEquals("scope", BunkerRequestPayload.nip44v3Scope(request))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unknownMethodIsInvalid() {
|
||||
val request = BunkerRequest("id-4", "not_a_real_method", arrayOf())
|
||||
assertEquals(BunkerMethod.INVALID, bunkerMethodOf(request.method))
|
||||
assertNull(BunkerRequestPayload.counterpartyPubKey(request))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.greenart7c3.nostrsigner.shared
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
class BunkerSignerTest {
|
||||
@Test
|
||||
fun nip44RoundTrip() = runTest {
|
||||
val alice = BunkerSigner(KeyPair())
|
||||
val bob = BunkerSigner(KeyPair())
|
||||
|
||||
val cipherText = alice.nip44Encrypt("hello bunker", bob.pubKey)
|
||||
val plainText = bob.nip44Decrypt(cipherText, alice.pubKey)
|
||||
|
||||
assertEquals("hello bunker", plainText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nip04RoundTrip() = runTest {
|
||||
val alice = BunkerSigner(KeyPair())
|
||||
val bob = BunkerSigner(KeyPair())
|
||||
|
||||
val cipherText = alice.nip04Encrypt("hello bunker nip04", bob.pubKey)
|
||||
val plainText = bob.nip04Decrypt(cipherText, alice.pubKey)
|
||||
|
||||
assertEquals("hello bunker nip04", plainText)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user