pakstr 0.19.3 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/android-template/app/build.gradle.kts +4 -0
- package/android-template/app/src/androidTest/java/com/pakstr/app/signer/SignerManagerTest.kt +273 -0
- package/android-template/app/src/main/assets/www/index.html +0 -6
- package/android-template/app/src/main/java/com/pakstr/app/LocalServer.kt +20 -4
- package/android-template/app/src/main/java/com/pakstr/app/MainActivity.kt +136 -12
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Bip340Hash.kt +56 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Bip340Signer.kt +104 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Bip340Verifier.kt +20 -170
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Nip44Crypto.kt +513 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Nip46ClientKey.kt +140 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/NostrEventHasher.kt +3 -28
- package/android-template/app/src/main/java/com/pakstr/app/crypto/NostrEventSerializer.kt +74 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Secp256k1.kt +100 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/BunkerSigner.kt +32 -7
- package/android-template/app/src/main/java/com/pakstr/app/signer/NostrBridge.kt +36 -9
- package/android-template/app/src/main/java/com/pakstr/app/signer/SignerManager.kt +240 -163
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/BunkerConfig.kt +175 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/BunkerConnectionStore.kt +144 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46Client.kt +79 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46Models.kt +319 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46PairingClient.kt +159 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46PairingQrCode.kt +35 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46PairingUri.kt +104 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46Protocol.kt +695 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/Nip46RelayTransport.kt +489 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/bunker/RelayNip46Client.kt +403 -0
- package/android-template/app/src/main/java/com/pakstr/app/utils/BunkerPairingDialog.kt +82 -0
- package/android-template/app/src/main/java/com/pakstr/app/utils/SignerPickerBottomSheet.kt +78 -17
- package/android-template/app/src/main/res/layout/activity_layout.xml +11 -0
- package/android-template/app/src/main/res/layout/dialog_bunker_pairing.xml +75 -0
- package/android-template/app/src/main/res/layout/dialog_picker.xml +60 -31
- package/android-template/app/src/main/res/values/strings.xml +27 -0
- package/android-template/app/src/test/java/com/pakstr/app/LocalServerQueryKeepAliveTest.kt +118 -0
- package/android-template/app/src/test/java/com/pakstr/app/crypto/Bip340SignerTest.kt +133 -0
- package/android-template/app/src/test/java/com/pakstr/app/crypto/Nip44CryptoTest.kt +199 -0
- package/android-template/app/src/test/java/com/pakstr/app/crypto/Nip46ClientKeyProtocolTest.kt +82 -0
- package/android-template/app/src/test/java/com/pakstr/app/crypto/Nip46ClientKeyTest.kt +52 -0
- package/android-template/app/src/test/java/com/pakstr/app/crypto/NostrEventSerializerTest.kt +42 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/NostrBridgeErrorTest.kt +32 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/BunkerConfigTest.kt +120 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/Nip46ModelsTest.kt +111 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/Nip46PairingClientTest.kt +271 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/Nip46PairingQrCodeTest.kt +54 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/Nip46PairingUriTest.kt +80 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/Nip46ProtocolTest.kt +578 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/Nip46RelayTransportTest.kt +508 -0
- package/android-template/app/src/test/java/com/pakstr/app/signer/bunker/RelayNip46ClientTest.kt +678 -0
- package/android-template/gradle/libs.versions.toml +8 -0
- package/dist/cli.js +8 -0
- package/dist/commands/init.js +16 -0
- package/dist/commands/nsite.js +38 -0
- package/dist/commands/publish.js +50 -2
- package/dist/commands/run.js +5 -5
- package/dist/core/blossom.js +2 -2
- package/dist/core/nsite.js +176 -0
- package/dist/core/pakstrConfig.js +85 -6
- package/package.json +1 -1
package/android-template/app/src/test/java/com/pakstr/app/crypto/Nip46ClientKeyProtocolTest.kt
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
package com.pakstr.app.crypto
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertArrayEquals
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertFalse
|
|
6
|
+
import org.junit.Assert.assertThrows
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
class Nip46ClientKeyProtocolTest {
|
|
10
|
+
|
|
11
|
+
@Test
|
|
12
|
+
fun client_keys_encrypt_and_decrypt_without_mutating_peer_input() {
|
|
13
|
+
val first =
|
|
14
|
+
Nip46ClientKey.fromPrivateKey(
|
|
15
|
+
privateKey(1)
|
|
16
|
+
)
|
|
17
|
+
val second =
|
|
18
|
+
Nip46ClientKey.fromPrivateKey(
|
|
19
|
+
privateKey(2)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
first.use {
|
|
23
|
+
second.use {
|
|
24
|
+
val peerPublicKey = second.publicKey
|
|
25
|
+
val original = peerPublicKey.copyOf()
|
|
26
|
+
val payload =
|
|
27
|
+
first.encryptNip44(
|
|
28
|
+
"sensitive message",
|
|
29
|
+
second.publicKeyHex
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
assertEquals(
|
|
33
|
+
"sensitive message",
|
|
34
|
+
second.decryptNip44(
|
|
35
|
+
payload,
|
|
36
|
+
first.publicKeyHex
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
assertArrayEquals(original, peerPublicKey)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun closed_and_invalid_keys_fail_with_safe_errors() {
|
|
46
|
+
val key = Nip46ClientKey.generate()
|
|
47
|
+
key.close()
|
|
48
|
+
|
|
49
|
+
val closedError =
|
|
50
|
+
assertThrows(IllegalStateException::class.java) {
|
|
51
|
+
key.encryptNip44(
|
|
52
|
+
"private plaintext",
|
|
53
|
+
"f".repeat(64)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
assertFalse(
|
|
57
|
+
closedError.message.orEmpty()
|
|
58
|
+
.contains("private plaintext")
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
val active = Nip46ClientKey.generate()
|
|
62
|
+
active.use {
|
|
63
|
+
val invalidError =
|
|
64
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
65
|
+
active.encryptNip44(
|
|
66
|
+
"private plaintext",
|
|
67
|
+
"not-a-pubkey"
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
assertFalse(
|
|
71
|
+
invalidError.message.orEmpty()
|
|
72
|
+
.contains("private plaintext")
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private fun privateKey(value: Int): ByteArray {
|
|
78
|
+
return ByteArray(32).also {
|
|
79
|
+
it[31] = value.toByte()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package com.pakstr.app.crypto
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertFalse
|
|
5
|
+
import org.junit.Assert.assertThrows
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
class Nip46ClientKeyTest {
|
|
10
|
+
|
|
11
|
+
@Test
|
|
12
|
+
fun generated_key_signs_with_its_public_identity() {
|
|
13
|
+
Nip46ClientKey.generate().use { key ->
|
|
14
|
+
val message = Bip340Hash.sha256("message".toByteArray())
|
|
15
|
+
val signature = key.sign(message)
|
|
16
|
+
|
|
17
|
+
assertEquals(32, key.publicKey.size)
|
|
18
|
+
assertTrue(key.publicKeyHex.matches(Regex("[0-9a-f]{64}")))
|
|
19
|
+
assertTrue(
|
|
20
|
+
Bip340Verifier.verify(
|
|
21
|
+
key.publicKeyHex,
|
|
22
|
+
Bip340Hash.bytesToHex(message),
|
|
23
|
+
Bip340Hash.bytesToHex(signature)
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun closed_key_rejects_signing() {
|
|
31
|
+
val key = Nip46ClientKey.generate()
|
|
32
|
+
key.close()
|
|
33
|
+
|
|
34
|
+
assertThrows(IllegalStateException::class.java) {
|
|
35
|
+
key.sign(ByteArray(32))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Test
|
|
40
|
+
fun diagnostics_do_not_expose_private_key() {
|
|
41
|
+
val privateKey =
|
|
42
|
+
Bip340Hash.hexToBytes(
|
|
43
|
+
"0000000000000000000000000000000000000000000000000000000000000003"
|
|
44
|
+
)
|
|
45
|
+
val privateKeyHex = Bip340Hash.bytesToHex(privateKey)
|
|
46
|
+
val key = Nip46ClientKey.fromPrivateKey(privateKey)
|
|
47
|
+
|
|
48
|
+
key.use {
|
|
49
|
+
assertFalse(key.toString().contains(privateKeyHex))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package com.pakstr.app.crypto
|
|
2
|
+
|
|
3
|
+
import org.json.JSONArray
|
|
4
|
+
import org.json.JSONObject
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
|
|
8
|
+
class NostrEventSerializerTest {
|
|
9
|
+
|
|
10
|
+
@Test
|
|
11
|
+
fun serializes_nip01_event_with_exact_required_escaping() {
|
|
12
|
+
val content =
|
|
13
|
+
"quote\" slash\\ backspace\b formfeed" +
|
|
14
|
+
" newline\n return\r tab\t </ " +
|
|
15
|
+
"
café"
|
|
16
|
+
val tagValue = "tag\"\\</
"
|
|
17
|
+
val event =
|
|
18
|
+
JSONObject()
|
|
19
|
+
.put("pubkey", "a".repeat(64))
|
|
20
|
+
.put("created_at", 1L)
|
|
21
|
+
.put("kind", 24133)
|
|
22
|
+
.put(
|
|
23
|
+
"tags",
|
|
24
|
+
JSONArray()
|
|
25
|
+
.put(
|
|
26
|
+
JSONArray()
|
|
27
|
+
.put("p")
|
|
28
|
+
.put(tagValue)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
.put("content", content)
|
|
32
|
+
|
|
33
|
+
assertEquals(
|
|
34
|
+
"[0,\"${"a".repeat(64)}\",1,24133," +
|
|
35
|
+
"[[\"p\",\"tag\\\"\\\\</
\"]]," +
|
|
36
|
+
"\"quote\\\" slash\\\\ backspace\\b " +
|
|
37
|
+
"formfeed\\f newline\\n return\\r tab\\t " +
|
|
38
|
+
"</
café\"]",
|
|
39
|
+
NostrEventSerializer.serialize(event)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
package com.pakstr.app.signer
|
|
2
|
+
|
|
3
|
+
import com.pakstr.app.signer.exceptions.SignerUnavailableException
|
|
4
|
+
import org.json.JSONObject
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
|
|
8
|
+
class NostrBridgeErrorTest {
|
|
9
|
+
@Test
|
|
10
|
+
fun signer_unavailable_uses_availability_error_code() {
|
|
11
|
+
val response =
|
|
12
|
+
NostrBridge.signerErrorResponse(
|
|
13
|
+
SignerUnavailableException("not connected")
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
val json = JSONObject(response)
|
|
17
|
+
assertEquals("SIGNER_UNAVAILABLE", json.getString("error"))
|
|
18
|
+
assertEquals("not connected", json.getString("message"))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Test
|
|
22
|
+
fun other_failures_use_generic_signer_error_code() {
|
|
23
|
+
val response =
|
|
24
|
+
NostrBridge.signerErrorResponse(
|
|
25
|
+
IllegalStateException("failed")
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
val json = JSONObject(response)
|
|
29
|
+
assertEquals("SIGNER_ERROR", json.getString("error"))
|
|
30
|
+
assertEquals("failed", json.getString("message"))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
package com.pakstr.app.signer.bunker
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertFalse
|
|
5
|
+
import org.junit.Assert.assertNull
|
|
6
|
+
import org.junit.Assert.assertThrows
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
class BunkerConfigTest {
|
|
10
|
+
|
|
11
|
+
@Test
|
|
12
|
+
fun parses_valid_bunker_connection() {
|
|
13
|
+
val config =
|
|
14
|
+
BunkerConfig.parse(
|
|
15
|
+
"bunker://$VALID_PUBKEY" +
|
|
16
|
+
"?relay=wss://relay.one" +
|
|
17
|
+
"&relay=wss%3A%2F%2Frelay.two%2Fnostr" +
|
|
18
|
+
"&secret=pairing%20secret"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
assertEquals(VALID_PUBKEY, config.remoteSignerPubkey)
|
|
22
|
+
assertEquals(
|
|
23
|
+
listOf(
|
|
24
|
+
"wss://relay.one",
|
|
25
|
+
"wss://relay.two/nostr"
|
|
26
|
+
),
|
|
27
|
+
config.relayUrls
|
|
28
|
+
)
|
|
29
|
+
assertEquals("pairing secret", config.pairingSecret)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Test
|
|
33
|
+
fun accepts_connection_without_pairing_secret() {
|
|
34
|
+
val config =
|
|
35
|
+
BunkerConfig.parse(
|
|
36
|
+
"bunker://$VALID_PUBKEY?relay=wss://relay.example"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
assertNull(config.pairingSecret)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun rejects_non_secure_relay() {
|
|
44
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
45
|
+
BunkerConfig.parse(
|
|
46
|
+
"bunker://$VALID_PUBKEY?relay=ws://relay.example"
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun rejects_duplicate_secrets_and_unsafe_uri_components() {
|
|
53
|
+
listOf(
|
|
54
|
+
"bunker://$VALID_PUBKEY?relay=wss://relay.example&secret=one&secret=two",
|
|
55
|
+
"bunker://user@$VALID_PUBKEY?relay=wss://relay.example",
|
|
56
|
+
"bunker://$VALID_PUBKEY/path?relay=wss://relay.example",
|
|
57
|
+
"bunker://$VALID_PUBKEY?relay=wss://relay.example#fragment"
|
|
58
|
+
).forEach { value ->
|
|
59
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
60
|
+
BunkerConfig.parse(value)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Test
|
|
66
|
+
fun rejects_missing_relay() {
|
|
67
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
68
|
+
BunkerConfig.parse(
|
|
69
|
+
"bunker://$VALID_PUBKEY"
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Test
|
|
75
|
+
fun rejects_malformed_public_key() {
|
|
76
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
77
|
+
BunkerConfig.parse(
|
|
78
|
+
"bunker://not-a-pubkey?relay=wss://relay.example"
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Test
|
|
84
|
+
fun rejects_public_key_outside_curve_field() {
|
|
85
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
86
|
+
BunkerConfig.parse(
|
|
87
|
+
"bunker://${"f".repeat(64)}?relay=wss://relay.example"
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Test
|
|
93
|
+
fun pairing_secret_is_not_exposed_by_diagnostics_or_errors() {
|
|
94
|
+
val secret = "unique-pairing-secret"
|
|
95
|
+
val config =
|
|
96
|
+
BunkerConfig.parse(
|
|
97
|
+
"bunker://$VALID_PUBKEY" +
|
|
98
|
+
"?relay=wss://relay.example" +
|
|
99
|
+
"&secret=$secret"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
assertFalse(config.toString().contains(secret))
|
|
103
|
+
|
|
104
|
+
val error =
|
|
105
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
106
|
+
BunkerConfig.parse(
|
|
107
|
+
"bunker://$VALID_PUBKEY" +
|
|
108
|
+
"?relay=ws://relay.example" +
|
|
109
|
+
"&secret=$secret"
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
assertFalse(error.message.orEmpty().contains(secret))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
companion object {
|
|
117
|
+
private const val VALID_PUBKEY =
|
|
118
|
+
"cb227f60cb2a6d982ecbfc51b9199d340afc00b1ef81b67d8e3778e097e70d2c"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
package com.pakstr.app.signer.bunker
|
|
2
|
+
|
|
3
|
+
import org.json.JSONArray
|
|
4
|
+
import org.json.JSONObject
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Assert.assertFalse
|
|
7
|
+
import org.junit.Assert.assertThrows
|
|
8
|
+
import org.junit.Assert.assertTrue
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
|
|
11
|
+
class Nip46ModelsTest {
|
|
12
|
+
|
|
13
|
+
@Test
|
|
14
|
+
fun request_round_trip_preserves_method_and_string_params() {
|
|
15
|
+
val request =
|
|
16
|
+
Nip46Request(
|
|
17
|
+
REQUEST_ID,
|
|
18
|
+
Nip46Method.NIP44_ENCRYPT,
|
|
19
|
+
listOf(PUBKEY, "private plaintext")
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
val parsed = Nip46Request.parse(request.toJson())
|
|
23
|
+
|
|
24
|
+
assertEquals(REQUEST_ID, parsed.id)
|
|
25
|
+
assertEquals(Nip46Method.NIP44_ENCRYPT, parsed.method)
|
|
26
|
+
assertEquals(2, parsed.params.size)
|
|
27
|
+
assertFalse(request.toString().contains("private plaintext"))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Test
|
|
31
|
+
fun empty_params_are_serialized_as_array() {
|
|
32
|
+
val encoded =
|
|
33
|
+
JSONObject(
|
|
34
|
+
Nip46Request(
|
|
35
|
+
REQUEST_ID,
|
|
36
|
+
Nip46Method.GET_PUBLIC_KEY,
|
|
37
|
+
emptyList()
|
|
38
|
+
).toJson()
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
assertEquals(0, encoded.getJSONArray("params").length())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun response_parsing_is_strict_and_safe() {
|
|
46
|
+
val secretResult = "sensitive result"
|
|
47
|
+
val response =
|
|
48
|
+
Nip46Response.parse(
|
|
49
|
+
JSONObject()
|
|
50
|
+
.put("id", REQUEST_ID)
|
|
51
|
+
.put("result", secretResult)
|
|
52
|
+
.toString()
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
assertEquals(secretResult, response.result)
|
|
56
|
+
assertFalse(response.toString().contains(secretResult))
|
|
57
|
+
|
|
58
|
+
assertThrows(Nip46ProtocolException::class.java) {
|
|
59
|
+
Nip46Response.parse(
|
|
60
|
+
JSONObject()
|
|
61
|
+
.put("id", REQUEST_ID)
|
|
62
|
+
.put("result", 42)
|
|
63
|
+
.toString()
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Test
|
|
69
|
+
fun malformed_json_unknown_methods_and_invalid_ids_are_rejected() {
|
|
70
|
+
assertThrows(Nip46ProtocolException::class.java) {
|
|
71
|
+
Nip46Request.parse("not-json")
|
|
72
|
+
}
|
|
73
|
+
assertThrows(Nip46ProtocolException::class.java) {
|
|
74
|
+
Nip46Request.parse(
|
|
75
|
+
JSONObject()
|
|
76
|
+
.put("id", REQUEST_ID)
|
|
77
|
+
.put("method", "unknown")
|
|
78
|
+
.put("params", JSONArray())
|
|
79
|
+
.toString()
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
assertThrows(Nip46ProtocolException::class.java) {
|
|
83
|
+
Nip46Request(
|
|
84
|
+
"predictable",
|
|
85
|
+
Nip46Method.CONNECT,
|
|
86
|
+
emptyList()
|
|
87
|
+
).toJson()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Test
|
|
92
|
+
fun secure_request_ids_are_canonical_and_unique() {
|
|
93
|
+
val ids =
|
|
94
|
+
(0 until 256)
|
|
95
|
+
.map {
|
|
96
|
+
SecureNip46RequestIdGenerator.nextId()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
assertTrue(
|
|
100
|
+
ids.all { it.matches(Regex("[0-9a-f]{64}")) }
|
|
101
|
+
)
|
|
102
|
+
assertEquals(ids.size, ids.toSet().size)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
companion object {
|
|
106
|
+
private const val REQUEST_ID =
|
|
107
|
+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
108
|
+
private const val PUBKEY =
|
|
109
|
+
"cb227f60cb2a6d982ecbfc51b9199d340afc00b1ef81b67d8e3778e097e70d2c"
|
|
110
|
+
}
|
|
111
|
+
}
|