Commit Graph

1367 Commits

Author SHA1 Message Date
M 2e02edc333 refactor: rename trustAllCerts → usePinnedTrustStore
The field no longer means 'trust all certificates' — since the Samsung
One UI 7 fix, it means 'use the pinned trust store (hardcoded +
TOFU-pinned certs + system CAs) instead of system-only trust.'

The old name was actively misleading and could cause a future
contributor to interpret the flag literally, potentially reintroducing
the trust-all pattern that Samsung Knox and GrapheneOS reject.

Renamed across all 4 files:
- ElectrumxServer.kt: field definition + updated KDoc
- ElectrumXClient.kt: createSocket() usage + comments
- NamecoinSettings.kt: parseServerString() usage + comments
- NamecoinSettingsTest.kt: test assertions
2026-03-26 06:33:43 +11:00
M 7f451ee7c7 fix: harden TLS for GrapheneOS and security-conscious Android ROMs
Address compatibility and security issues identified by reviewing
GrapheneOS source (hardened Conscrypt, strict TLS enforcement):

1. .onion: prefer pinned factory, fall back to trust-all

   GrapheneOS patches Conscrypt with stricter TLS enforcement that may
   reject no-op X509TrustManagers even for proxied sockets. The .onion
   server's cert is already in PINNED_ELECTRUMX_CERTS (same operator as
   electrumx.testls.space), so we now use cachedPinnedSslFactory() as
   the primary path. onionSslFactory() (trust-all) is kept as a fallback
   on SSLHandshakeException only — this handles cert rotation or unknown
   .onion servers gracefully.

2. TOFU: require explicit user confirmation before pinning

   Previously, Test Connection auto-pinned every cert on success. An
   attacker performing MITM during first test would get their cert
   permanently trusted. Now each new cert triggers an AlertDialog showing
   the full SHA-256 fingerprint. Users must explicitly accept ('Trust')
   or reject each cert — aligning with GrapheneOS's philosophy of
   explicit trust decisions.

3. Clarify trustAllCerts semantics

   The field name is misleading post-refactor: it now means 'use pinned
   trust store' not 'trust all certificates'. Added TODO to rename to
   usePinnedTrustStore and updated inline comments to prevent future
   misinterpretation.

Note: hostname verification on raw SSLSocket is a pre-existing gap
(not introduced by the Samsung fix PR) — SSLSocket.createSocket() uses
the host parameter for SNI only, not hostname verification. A follow-up
should add endpointIdentificationAlgorithm='HTTPS' or fingerprint-based
verification for pinned certs.
2026-03-26 06:26:18 +11:00
M a4953f628d doc: verify .onion ElectrumX cert via Tor, document pinning
Connected to the .onion hidden service via Tor SOCKS proxy and
confirmed it serves the SAME certificate as electrumx.testls.space:
  SHA-256: 53:65:D5:BB:26:19:F5:40:1C:D8:8E:FC:AF:FB:A5:B2:...

The .onion cert is already covered by the first pinned cert entry.
Added comments documenting this relationship and instructions for
fetching .onion certs via Tor for future updates.

The .onion still uses onionSslFactory() (trust-all) as the primary
path — this is correct since Tor provides its own authentication.
The pinned cert serves as defense-in-depth.
2026-03-25 12:13:49 +11:00
M 3afe0c9978 fix: bypass cert pinning for .onion ElectrumX servers
Tor hidden services are authenticated by their onion address (the
public key hash), making TLS certificate verification redundant.

The .onion server in TOR_ELECTRUMX_SERVERS was going through
pinnedSslFactory() but its cert isn't in the pinned list (and can't
be fetched without Tor). This would cause .onion connections to fail
with SSLHandshakeException when Tor mode is active.

Fix: .onion addresses now use a dedicated onionSslFactory() with
trust-all, which is safe because:
1. Tor provides end-to-end encryption
2. The onion address IS the server identity proof
3. Samsung Knox trust-all rejection doesn't apply to proxied sockets
2026-03-25 12:10:45 +11:00
M b8b51db0b5 feat: TOFU cert pinning for custom ElectrumX servers
When a user adds a custom ElectrumX server and runs Test Connection,
the server's TLS certificate is automatically captured and pinned
(Trust On First Use). This allows custom servers with self-signed
certificates to work on Samsung/Xiaomi/OnePlus devices.

Changes:
- ElectrumXClient: addPinnedCert(), setDynamicCerts() for runtime
  cert management; testServer() now captures server cert PEM and
  SHA-256 fingerprint from SSL session
- NamecoinSharedPreferences: persist pinned certs to DataStore
- AppModules: load pinned certs on startup, sync to ElectrumXClient
- NamecoinSettings: custom servers always set trustAllCerts=true
  (ElectrumX servers almost universally use self-signed certs)
- UI: shows cert fingerprint in test results, auto-pins on success
- ServerTestResult: new serverCertPem + certFingerprint fields

Flow: Add server → Test Connection → cert auto-captured and pinned →
future connections trust that cert even on Samsung Knox devices.
2026-03-25 12:05:05 +11:00
M 49698de99c feat: add Test Connection diagnostics to Namecoin settings
Add per-server connection testing with detailed error reporting:
- Test Connection button tests each ElectrumX server individually
- Shows streaming results:  success with response time,  failure
  with human-readable error (TLS handshake failed, connection refused,
  timeout, DNS failed, invalid response, etc.)
- Diagnostic card shows: last test timestamp + success count, device
  info (manufacturer/model/Android/API), TLS version negotiated
- ElectrumXClient.testServer() method for single-server diagnostics
  with TLS version capture from SSL session

This gives users (and bug reporters) immediate visibility into why
Namecoin resolution may be failing silently on their device.
2026-03-25 11:52:05 +11:00
M 15a2d77a0f fix: pin ElectrumX server certs instead of trust-all TrustManager
Samsung One UI 7 (Android 16) silently rejects TLS connections that use
a no-op X509TrustManager which accepts all certificates. This breaks
Namecoin resolution on all Samsung devices running One UI 7, including
Galaxy A15 (SM-A156E) and Galaxy S24 Ultra (SM-S938B).

Replace trustAllSslFactory() with pinnedSslFactory() that:
- Pins the actual self-signed certificates of known ElectrumX servers
- Also includes system CA certificates for servers with real certs
- Uses a proper TrustManagerFactory chain that Samsung Knox accepts

Additional OEM compatibility hardening:
- Cache the SSLSocketFactory (avoid expensive rebuild per connection)
- Request TLSv1.2 explicitly (Xiaomi MIUI/HyperOS and OnePlus ColorOS
  Conscrypt forks may default to TLS 1.0 for raw socket upgrades)
- Enforce TLSv1.2+ enabled protocols on the SSLSocket
- KeyStore fallback to PKCS12 if default type fails (Xiaomi)
- Defensive try/catch on system CA cert re-insertion (some OEMs return
  certs that cannot be added to a new KeyStore)

Tested on Android 16 (API 36) emulator — all ElectrumX servers connect
(hostname, IP-address, factory reuse) and .bit resolution works E2E.

Pinned certs:
- electrumx.testls.space:50002 (expires 2027-05-04)
- nmc2.bitcoins.sk:57002 (expires 2030-10-22)
2026-03-25 11:41:12 +11:00
Vitor Pamplona 8437a819f8 Merge pull request #1909 from KotlinGeekDev/kmp-completeness
Kmp completeness, part 3 - adios, Swift bridge.
2026-03-24 13:38:44 -04:00
Vitor Pamplona cef07a2460 Fixes single label domains with non ascii chars around them 2026-03-24 13:33:14 -04:00
KotlinGeekDev 349781d9be Use uri-reference-kmp in commonMain. Remove platform-specific implementations. 2026-03-24 16:34:13 +01:00
KotlinGeekDev 2933454fb6 Merge branch 'main' of https://github.com/vitorpamplona/amethyst into upstream-main
# Conflicts:
#	quartz/src/iosMain/kotlin/com/vitorpamplona/quartz/utils/URLs.ios.kt
2026-03-24 16:11:04 +01:00
Vitor Pamplona d6d4bdc5e3 avoids duplicating quote tags by key + value 2026-03-24 09:40:02 -04:00
Vitor Pamplona c39eca5172 Add new utility methods 2026-03-24 09:39:09 -04:00
Vitor Pamplona 8c4b2fe62b Makes sure quotes only happen once in events. 2026-03-24 09:30:07 -04:00
Vitor Pamplona 514e90ff17 Fixes non http uris in the references tag 2026-03-24 09:13:48 -04:00
KotlinGeekDev 4c14554263 Merge branch 'vitorpamplona:main' into kmp-completeness 2026-03-24 13:27:40 +01:00
Vitor Pamplona 7874893938 v1.06.1 2026-03-23 18:19:42 -04:00
Vitor Pamplona 51fa28f22a no message 2026-03-23 18:16:24 -04:00
Vitor Pamplona e5aeb49a4b Solves some of the crashes of concurrent modification exception 2026-03-23 17:59:14 -04:00
Claude 7ea5557828 test: fix UrlTest to use 今北産業 as the crash input (PR #1907)
https://claude.ai/code/session_013rJ9iYndYVJgpYK2VazxL2
2026-03-23 21:02:41 +00:00
Claude 3a93c4b075 test: add UrlTest for getPart() boundary conditions fixed in PR #1907
Tests the StringIndexOutOfBoundsException fix in Url.getPart() by directly
constructing Url objects with UrlMarker indices that exceed the trimmed
originalUrl length — the exact scenario that occurs when readEnd() strips
a trailing character (e.g. ':') while the PORT/QUERY marker still points
to the original buffer position.

https://claude.ai/code/session_013rJ9iYndYVJgpYK2VazxL2
2026-03-23 20:57:49 +00:00
Claude 9ebc4a1e67 style: remove trailing blank line in UrlMarkerTest
https://claude.ai/code/session_013rJ9iYndYVJgpYK2VazxL2
2026-03-23 20:56:58 +00:00
Claude 9202b60dcf test: add regression tests for 今北産業 URL crash (PR #1907)
Verifies that the Japanese phrase "今北産業" does not cause a
StringIndexOutOfBoundsException in Url.getPart() and is not
detected as a URL.

https://claude.ai/code/session_013rJ9iYndYVJgpYK2VazxL2
2026-03-23 20:46:59 +00:00
Claude e31e3829a6 test: add regression tests for PR #1907 URL crash fix
Tests cover the StringIndexOutOfBoundsException in Url.getPart() that
occurred when readEnd() trimmed trailing characters (e.g. ':') from a
detected URL but urlMarker indices remained pointing past the trimmed
string's end.

- UrlMarkerTest: three cases testing PORT/QUERY index at or beyond
  string length (the boundary conditions fixed by minOf clamping and
  the startIndex >= length guard)
- UrlsDetectorTest: regression for "今北産業" (no crash, 0 URLs) and
  for a bare "example.com:" host
- UrlParserTest: end-to-end regression for "今北産業" and "example.com:"

https://claude.ai/code/session_013rJ9iYndYVJgpYK2VazxL2
2026-03-23 20:31:28 +00:00
Vitor Pamplona 2b920fc47a Merge pull request #1907 from greenart7c3/claude/fix-android-runtime-exception-auFbS
Fix URL parsing bounds checking in Url.kt
2026-03-23 15:51:07 -04:00
KotlinGeekDev ac38a73b5e spotless apply. 2026-03-23 20:27:04 +01:00
KotlinGeekDev bddff36e6b androidLibrary {} -> android {} 2026-03-23 19:55:35 +01:00
KotlinGeekDev b495f3f409 NIP19 tests for iOS. 2026-03-23 19:50:26 +01:00
KotlinGeekDev cfecfe2130 Move uri-reference-kmp to version catalog. Remove swift bridge(was a nice run). 2026-03-23 19:49:24 +01:00
KotlinGeekDev f2bdb24ee5 Removal of references to APIs using the Swift bridge. 2026-03-23 19:38:10 +01:00
KotlinGeekDev a9c41e1d55 Introduce uri-reference-kmp and replace uriBridge usages with it. 2026-03-23 19:31:58 +01:00
Claude 951610d9ca fix: prevent StringIndexOutOfBoundsException in Url.getPart()
When UrlDetector.readEnd() trims the last character from URLs ending
with characters like '.', ',', etc., the urlMarker indices remain
based on the original buffer length. This caused getPart() to call
substring() with an end index beyond the trimmed URL's length.

Fix by clamping the end index to originalUrl.length and guarding
against an out-of-bounds start index.

https://claude.ai/code/session_014Q2SvTE5vKgUm1w8dPVjYo
2026-03-23 17:10:56 +00:00
Vitor Pamplona 81380a32ae Fixes POCO phone's removal of the + sign in NWC 2026-03-23 13:03:18 -04:00
Vitor Pamplona 7af1be7363 v1.06.0 2026-03-23 11:23:44 -04:00
Vitor Pamplona ed3390f64a Fixes weird import 2026-03-23 10:16:59 -04:00
nrobi144 9d8d0e249d fix(chess): encode required fields in Jester content JSON
JesterContent fields version, fen, and history were omitted from
serialized JSON because kotlinx.serialization skips default values.
Jester's isStartGameEvent checks arrayEquals(json.history, []) which
fails when the field is absent (undefined !== []).

Added @EncodeDefault to version, fen, and history so they are always
included in the JSON output regardless of value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:08:28 +02:00
nrobi144 3eaf3d38a5 fix(chess): normalize SAN for cross-client interop
ChessEngine.makeMove() now normalizes SAN notation before passing to
chesslib, fixing games against jester.nyo.dev and other clients that
use different notation:

- Castling: 0-0 → O-O, 0-0-0 → O-O-O (zeros to letters)
- Annotations: strips !, ?, !!, ??, !?, ?! suffixes

Previously, chesslib rejected 0-0/0-0-0 causing ChessStateReconstructor
to mark games as desynced and skip remaining moves. Games appeared
permanently stuck, even after app restart.

Also fixes discoverUserGames() silently dropping games classified as
spectator — now adds them to spectatingGames instead.

23 new interop tests covering SAN variants, reconstruction, spectator
detection, and missing start event handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 07:21:17 +02:00
Vitor Pamplona 37f6a7b133 Makes sure subscriptions are closed on the NostrClient utility methods 2026-03-22 15:18:48 -04:00
Vitor Pamplona 439b392aa0 Moves EmptyNostrClient to a class to avoid auto-import issues 2026-03-22 15:14:49 -04:00
Vitor Pamplona b6b4856fe4 Fixes a closing bug when the coroutine crashes
Fixes an index bug on matching subscriptions
Requires the relay to send events that match the sub in order to move forward with the paging
2026-03-22 11:18:12 -04:00
Vitor Pamplona adb3a0e0fc Better transitions to a Nostrclient that is autocloseable, destroying all links to everything else when closing 2026-03-22 11:16:54 -04:00
Vitor Pamplona 15ff3040b5 Merge branch 'main' into claude/nip42-relay-auth-P878m 2026-03-21 13:37:21 -04:00
Vitor Pamplona cfead47d71 Improves design of the policy 2026-03-21 13:21:21 -04:00
Vitor Pamplona df6dc17047 Final touches on the Attestation cards 2026-03-20 21:16:59 -04:00
Claude b0a8cae194 refactor: extract AuthPolicy interface from requireAuth boolean
Replace the requireAuth: Boolean parameter in NostrServer with a
pluggable AuthPolicy interface. Each policy has trigger points for
EVENT (acceptEvent), REQ (acceptReq with filter rewriting), COUNT
(acceptCount), and live event delivery (canSendToSession).

Built-in policies: OpenPolicy (allow all) and RequireAuthPolicy
(require auth for all commands, matching the previous behavior).

https://claude.ai/code/session_017vdjbdxdYK1oJMH66koVZE
2026-03-20 22:40:18 +00:00
Claude 8fd08aa56d feat: implement NIP-42 server-side relay authentication
Add server-side AUTH challenge/response handling per NIP-42 spec:
- RelaySession generates a unique challenge per connection and tracks
  authenticated pubkeys (multiple users can auth on one session)
- NostrServer gains relayUrl and requireAuth parameters; when requireAuth
  is true, EVENT/REQ/COUNT are rejected with auth-required: prefix
- AUTH handler validates kind 22242, created_at within 10 min, challenge
  and relay tag matching
- AuthCmd now accepts Event (not just RelayAuthEvent) so the server can
  gracefully reject wrong-kind auth attempts
- 11 new tests covering auth success, wrong challenge/relay/kind/timestamp,
  multi-user auth, and requireAuth gating

https://claude.ai/code/session_017vdjbdxdYK1oJMH66koVZE
2026-03-20 22:05:53 +00:00
Vitor Pamplona 9bfb170ffa simple documentation 2026-03-20 17:15:05 -04:00
Vitor Pamplona fc1e3e6b83 Adds a simple relay to quartz 2026-03-20 16:54:04 -04:00
Vitor Pamplona d431b12f94 Migrates EventStore from Android's SQLLite to KMP
Fixes testing of libsodium between java and android
2026-03-20 16:00:17 -04:00
Vitor Pamplona c5066d89c3 Fixes serialization issues when the code falls back to Kotlin Serialization 2026-03-20 15:58:17 -04:00