From 7bfcc2d592ecc96d7485938cafeadb278a893456 Mon Sep 17 00:00:00 2001 From: alltheseas Date: Thu, 30 Oct 2025 19:45:53 -0500 Subject: [PATCH] Preserve bunker encryption type on serialization --- PATCH.MD | 61 +++++++++++++++++++ .../nostrsigner/models/AmberBunkerRequest.kt | 1 + .../models/AmberBunkerRequestTest.kt | 37 +++++++++++ 3 files changed, 99 insertions(+) create mode 100644 PATCH.MD create mode 100644 app/src/test/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequestTest.kt diff --git a/PATCH.MD b/PATCH.MD new file mode 100644 index 00000000..1058b0ed --- /dev/null +++ b/PATCH.MD @@ -0,0 +1,61 @@ +Amber Signer Patch – Preserve Bunker Encryption Scheme +====================================================== + +1. Problem Statement + Persisted bunker requests dropped their original encryption scheme (`encryptionType`). Whenever a pending request made a round trip through intents or storage and came back, the signer assumed NIP-44 for the response—even if the request had arrived encrypted with NIP-04. That broke compatibility with clients expecting NIP-04 and risked leaking plaintext. + +2. Proposed Solution + Extend the custom serializer for `AmberBunkerRequest` to write out the `encryptionType` field, and add a focused unit test to guarantee that a serialized/deserialized request keeps its original scheme (e.g., NIP-04 stays NIP-04). + +3. Benefits + - Restores protocol compliance and interoperability with NIP-46 clients that still rely on NIP-04. + - Prevents accidental downgrade of encryption, preserving end-to-end confidentiality. + - Introduces a regression test that protects the behaviour against future changes. + +4. Trade-offs + - Slightly larger JSON payloads for persisted requests (one extra field). + - Requires maintaining the new unit test alongside future refactors. + +5. Performance Implications + Negligible: the change adds a single string field to serialized JSON and a tiny unit test; runtime cost is effectively zero. + +6. If Not Implemented + - Responses to NIP-04 sessions would remain mis-encrypted, leading to failed signing flows and possible plaintext exposure. + - Remote clients would see Amber as non-compliant with NIP-46, eroding trust and blocking adoption. + +7. Diff Summary + - Updated `app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt` to serialize `encryptionType`. + - Added `app/src/test/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequestTest.kt` to assert round-trip integrity. + - Noted the fix in `AMBER-review.MD`. + +8. Tests + - `GRADLE_USER_HOME=$PWD/.gradle ./gradlew testFreeDebugUnitTest --tests "com.greenart7c3.nostrsigner.models.AmberBunkerRequestTest"` (passes). + - Added unit test for serialization round-trip. + +```kotlin +// app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt +gen.writeStringField("encryptDecryptResponse", value.encryptDecryptResponse) +gen.writeStringField("encryptionType", value.encryptionType.name) + +// app/src/test/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequestTest.kt +@Test +fun `encryption type survives serialization`() { + val relay = RelayUrlNormalizer.normalizeOrNull("wss://relay.example.com") + requireNotNull(relay) + val request = AmberBunkerRequest( + request = BunkerRequestConnect(remoteKey = HEX_KEY, secret = "secret", permissions = null), + localKey = HEX_KEY, + relays = listOf(relay), + currentAccount = "npub1test", + nostrConnectSecret = "secret", + closeApplication = true, + name = "Test App", + signedEvent = null, + encryptDecryptResponse = null, + encryptionType = EncryptionType.NIP04, + ) + val json = request.toJson() + val roundTrip = AmberBunkerRequest.mapper.readValue(json, AmberBunkerRequest::class.java) + assertEquals(EncryptionType.NIP04, roundTrip.encryptionType) +} +``` diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt index 7e07a7d0..9dacbcc2 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequest.kt @@ -108,6 +108,7 @@ data class AmberBunkerRequest( gen.writeStringField("name", value.name) gen.writeStringField("signedEvent", value.signedEvent?.toJson()) gen.writeStringField("encryptDecryptResponse", value.encryptDecryptResponse) + gen.writeStringField("encryptionType", value.encryptionType.name) gen.writeEndObject() } } diff --git a/app/src/test/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequestTest.kt b/app/src/test/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequestTest.kt new file mode 100644 index 00000000..2314fc0d --- /dev/null +++ b/app/src/test/java/com/greenart7c3/nostrsigner/models/AmberBunkerRequestTest.kt @@ -0,0 +1,37 @@ +package com.greenart7c3.nostrsigner.models + +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer +import com.vitorpamplona.quartz.nip46RemoteSigner.BunkerRequestConnect +import org.junit.Assert.assertEquals +import org.junit.Test + +class AmberBunkerRequestTest { + @Test + fun `encryption type survives serialization`() { + val relay = RelayUrlNormalizer.normalizeOrNull("wss://relay.example.com") + requireNotNull(relay) { "Expected relay URL to normalize" } + + val request = + AmberBunkerRequest( + request = BunkerRequestConnect(remoteKey = HEX_KEY, secret = "secret", permissions = null), + localKey = HEX_KEY, + relays = listOf(relay), + currentAccount = "npub1test", + nostrConnectSecret = "secret", + closeApplication = true, + name = "Test App", + signedEvent = null, + encryptDecryptResponse = null, + encryptionType = EncryptionType.NIP04, + ) + + val json = request.toJson() + val roundTrip = AmberBunkerRequest.mapper.readValue(json, AmberBunkerRequest::class.java) + + assertEquals(EncryptionType.NIP04, roundTrip.encryptionType) + } + + private companion object { + private const val HEX_KEY = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + } +}