From 567985434db47297675bf1d9ce61e44832dd2d5c Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Thu, 20 Aug 2026 11:28:57 -0400 Subject: [PATCH] v0.0.13 - Renamed nsigner to signer throughout project; release build with signer + signer-client binaries --- Cargo.lock | 62 ++++++++++++------------ Cargo.toml | 8 ++-- increment_and_push.sh | 30 +++++++++--- plans/menu_gap_analysis.md | 4 +- plans/policy_enforcement_plan.md | 6 +-- plans/port_nsigner_to_rust.md | 8 ++-- plans/ratatui_migration_plan.md | 6 +-- plans/signer_client_plan.md | 42 ++++++++-------- plans/tui_flow_redesign.md | 10 ++-- src/alg_cache.rs | 24 +++++----- src/auth_envelope.rs | 8 ++-- src/client/README.md | 16 +++---- src/client/auth.rs | 14 +++--- src/client/cli.rs | 8 ++-- src/client/main.rs | 12 ++--- src/client/rpc.rs | 14 +++--- src/client/signer.rs | 16 +++---- src/client/transport.rs | 28 +++++------ src/dispatcher.rs | 6 +-- src/error.rs | 18 +++---- src/http.rs | 2 +- src/key_store.rs | 82 ++++++++++++++++---------------- src/lib.rs | 6 +-- src/main.rs | 80 +++++++++++++++---------------- src/miner.rs | 12 ++--- src/mnemonic.rs | 14 +++--- src/otp_pad.rs | 46 +++++++++--------- src/pq_crypto.rs | 42 ++++++++-------- src/role_table.rs | 30 ++++++------ src/secure_mem.rs | 14 +++--- src/server.rs | 30 ++++++------ src/socket_name.rs | 44 ++++++++--------- src/tui.rs | 12 ++--- tests/integration.rs | 30 ++++++------ 34 files changed, 401 insertions(+), 383 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0702728..e07530b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1488,37 +1488,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "nsigner" -version = "0.0.12" -dependencies = [ - "base64", - "chacha20poly1305", - "clap", - "crossterm 0.27.0", - "ed25519-dalek", - "hex", - "hmac 0.12.1", - "libc", - "ml-dsa", - "ml-kem", - "nostr-core", - "nostr-nips", - "rand", - "rand_core 0.6.4", - "ratatui", - "secp256k1", - "serde", - "serde_json", - "sha2 0.10.9", - "sha3 0.10.9", - "slh-dsa", - "tempfile", - "thiserror", - "x25519-dalek", - "zeroize", -] - [[package]] name = "num-conv" version = "0.2.2" @@ -2236,6 +2205,37 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "signer" +version = "0.0.13" +dependencies = [ + "base64", + "chacha20poly1305", + "clap", + "crossterm 0.27.0", + "ed25519-dalek", + "hex", + "hmac 0.12.1", + "libc", + "ml-dsa", + "ml-kem", + "nostr-core", + "nostr-nips", + "rand", + "rand_core 0.6.4", + "ratatui", + "secp256k1", + "serde", + "serde_json", + "sha2 0.10.9", + "sha3 0.10.9", + "slh-dsa", + "tempfile", + "thiserror", + "x25519-dalek", + "zeroize", +] + [[package]] name = "slab" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index ac023bd..fdc0fa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "nsigner" -version = "0.0.12" +name = "signer" +version = "0.0.13" edition = "2021" license = "MIT" description = "Attended Nostr signing daemon — Rust port of n_signer" [[bin]] -name = "nsigner" +name = "signer" path = "src/main.rs" [[bin]] @@ -14,7 +14,7 @@ name = "signer-client" path = "src/client/main.rs" [lib] -name = "nsigner" +name = "signer" path = "src/lib.rs" [dependencies] diff --git a/increment_and_push.sh b/increment_and_push.sh index 234b262..b1a0ad6 100755 --- a/increment_and_push.sh +++ b/increment_and_push.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -# nsigner (Rust) — Increment and Push Script +# signer (Rust) — Increment and Push Script # # Increments the version (patch/minor/major), updates Cargo.toml and # src/lib.rs, commits, tags, and pushes. Optionally creates a release @@ -34,7 +34,7 @@ RELEASE_MODE=false VERSION_INCREMENT_TYPE="patch" show_usage() { - echo "nsigner (Rust) Increment and Push Script" + echo "signer (Rust) Increment and Push Script" echo "" echo "USAGE:" echo " $0 [OPTIONS] \"commit message\"" @@ -169,18 +169,23 @@ verify_binary_version() { } build_release_binary() { - print_status "Building release binary (cargo build --release)..." + print_status "Building release binaries (cargo build --release)..." cargo build --release 2>&1 | tail -5 || return 1 - local bin_path="target/release/nsigner" + local bin_path="target/release/signer" verify_binary_version "$bin_path" "$NEW_VERSION" || return 1 + local client_path="target/release/signer-client" + if [[ -f "$client_path" ]]; then + print_success "Release binary built: $client_path" + fi + print_success "Release binary built: $bin_path" return 0 } create_source_tarball() { - local tarball_name="nsigner-${NEW_VERSION#v}.tar.gz" + local tarball_name="signer-${NEW_VERSION#v}.tar.gz" if tar -czf "$tarball_name" \ --exclude='target/*' \ @@ -279,7 +284,8 @@ main() { git_commit_and_push - local binary_path="target/release/nsigner" + local binary_path="target/release/signer" + local client_path="target/release/signer-client" local tarball_path="" tarball_path=$(create_source_tarball || true) @@ -288,6 +294,18 @@ main() { if [[ -n "$release_id" ]]; then upload_release_assets "$release_id" "$binary_path" "$tarball_path" + # Also upload the signer-client binary if it exists + if [[ -f "$client_path" ]]; then + local token + token=$(cat "$HOME/.gitea_token" | tr -d '\n\r') + local api_url="https://git.laantungir.net/api/v1/repos/laantungir/signer" + local assets_url="$api_url/releases/$release_id/assets" + print_status "Uploading signer-client..." + curl -s -X POST "$assets_url" \ + -H "Authorization: token $token" \ + -F "attachment=@$client_path;filename=signer-client" \ + -F "name=signer-client" > /dev/null + fi fi print_success "Release flow completed: $NEW_VERSION" diff --git a/plans/menu_gap_analysis.md b/plans/menu_gap_analysis.md index 4aca174..fdcf978 100644 --- a/plans/menu_gap_analysis.md +++ b/plans/menu_gap_analysis.md @@ -1,7 +1,7 @@ # Menu Gap Analysis: C `main.c` vs Rust `signer` **Source of truth:** the C code in [`src/main.c`](../n_signer/src/main.c:1), NOT -[`documents/nsigner_menus.md`](../n_signer/documents/nsigner_menus.md:1) (which is +[`documents/signer_menus.md`](../n_signer/documents/signer_menus.md:1) (which is stale — e.g. it claims the wizard prompts `Require interactive approval? [Y/n]`, but the actual C code hardcodes `requires_approval = 0` and never prompts). @@ -31,7 +31,7 @@ Legend: ✅ matches, ⚠️ partial, ❌ missing/divergent. - Success: `Seed phrase is valid and accepted.` **Rust** ([`load_mnemonic_tui`](../signer/src/main.rs:549)): -- Frame: `nsigner v > Unlock`, title `"Enter mnemonic phrase"` +- Frame: `signer v > Unlock`, title `"Enter mnemonic phrase"` - Prompt: `Enter your BIP-39 mnemonic phrase, or 'g' to generate a new one.` - `g`/`G` → generate, numbered, warning ✅ - Otherwise → load as mnemonic (paste works implicitly) ⚠️ diff --git a/plans/policy_enforcement_plan.md b/plans/policy_enforcement_plan.md index 81429d1..7dec8e7 100644 --- a/plans/policy_enforcement_plan.md +++ b/plans/policy_enforcement_plan.md @@ -2,7 +2,7 @@ ## Problem -The Rust `nsigner` library modules are complete and tested (92 tests pass), but the server loop in [`server.rs`](src/server.rs:117) accepts connections, reads requests, dispatches them, and sends responses **without any policy enforcement**. The `policy: &mut PolicyTable` parameter is accepted but never used. This means the daemon would sign anything for anyone without prompting — it is not an "attended signer." +The Rust `signer` library modules are complete and tested (92 tests pass), but the server loop in [`server.rs`](src/server.rs:117) accepts connections, reads requests, dispatches them, and sends responses **without any policy enforcement**. The `policy: &mut PolicyTable` parameter is accepted but never used. This means the daemon would sign anything for anyone without prompting — it is not an "attended signer." ## What the C version does (server.c) @@ -148,10 +148,10 @@ Add methods to insert session grants: ```rust impl PolicyTable { /// Insert a session grant for caller+role+verb. - pub fn insert_session_grant(&mut self, caller: &str, verb: &str, role: &str) -> Result<(), NsignerError>; + pub fn insert_session_grant(&mut self, caller: &str, verb: &str, role: &str) -> Result<(), SignerError>; /// Insert a session grant for caller+role (all verbs). - pub fn insert_session_grant_all(&mut self, caller: &str, role: &str) -> Result<(), NsignerError>; + pub fn insert_session_grant_all(&mut self, caller: &str, role: &str) -> Result<(), SignerError>; } ``` diff --git a/plans/port_nsigner_to_rust.md b/plans/port_nsigner_to_rust.md index 8fccb66..badb5ce 100644 --- a/plans/port_nsigner_to_rust.md +++ b/plans/port_nsigner_to_rust.md @@ -101,7 +101,7 @@ graph TB ### 4. Error Handling - **C**: Integer error codes + string messages -- **Rust**: `thiserror`-based `NsignerError` enum. The JSON-RPC error codes are preserved exactly for wire compatibility. +- **Rust**: `thiserror`-based `SignerError` enum. The JSON-RPC error codes are preserved exactly for wire compatibility. ### 5. Transport - **C**: Raw syscalls (`socket`, `bind`, `accept`, `SO_PEERCRED`) @@ -323,7 +323,7 @@ signer/ │ ├── otp_pad.rs # One-time pad encryption │ ├── socket_name.rs # Abstract socket naming │ ├── tui.rs # Terminal UI -│ └── error.rs # NsignerError enum +│ └── error.rs # SignerError enum ├── tests/ │ ├── integration_test.rs │ ├── algorithm_test.rs @@ -337,7 +337,7 @@ signer/ ```toml [package] -name = "nsigner" +name = "signer" version = "0.1.0" edition = "2021" @@ -372,7 +372,7 @@ tempfile = "3" 3. **Derivation paths**: BIP-32/SLIP-0010 paths must produce identical keys from the same mnemonic 4. **Socket protocol**: Length-prefixed framing (4-byte BE) must be compatible 5. **HTTP**: Same minimal HTTP/1.1 POST-only parser behavior -6. **Abstract socket names**: `@nsigner__` format preserved +6. **Abstract socket names**: `@signer__` format preserved ## Open Questions diff --git a/plans/ratatui_migration_plan.md b/plans/ratatui_migration_plan.md index 5c4603d..96cb586 100644 --- a/plans/ratatui_migration_plan.md +++ b/plans/ratatui_migration_plan.md @@ -33,8 +33,8 @@ full width at the bottom. ├──────────────────────────────────────┬───────────────────────────────┤ │ Information │ Activity (latest first) ▲ │ │ session=unlocked (12 words) │ 2026-08-18 08:00:15 req… │ -│ signer=nsigner01 derived=2 │ 2026-08-18 08:00:01 start │ -│ socket=@nsigner01 transport=unix │ │ +│ signer=signer01 derived=2 │ 2026-08-18 08:00:01 start │ +│ socket=@signer01 transport=unix │ │ │ OTP pad: chksum=abc… offset=128 │ │ ├──────────────────────────────────────┤ │ │ Roles │ │ @@ -273,7 +273,7 @@ Each setup screen has its own `draw` function and event handler. The `tui_continuous.rs` entirely. 9. **Test** — `cargo test` (unit tests don't touch the TUI). Manual test: start signer, verify setup screens work with InputField, verify 4-section - main screen renders, press `d`/`l`/`r`/`q`, connect with `nsigner_client`. + main screen renders, press `d`/`l`/`r`/`q`, connect with `signer_client`. ## What stays the same diff --git a/plans/signer_client_plan.md b/plans/signer_client_plan.md index 093eea0..597400e 100644 --- a/plans/signer_client_plan.md +++ b/plans/signer_client_plan.md @@ -1,14 +1,14 @@ -# Plan: `signer-client` — Rust CLI for the nsigner daemon +# Plan: `signer-client` — Rust CLI for the signer daemon ## Goal A standalone Rust command-line client `signer-client` that connects to a running -`nsigner` process over its framed transports (Unix abstract socket, TCP, serial, +`signer` process over its framed transports (Unix abstract socket, TCP, serial, qrexec) and exposes the full JSON-RPC verb surface over stdin/stdout so that signed events can be piped directly into `nak publish`. This is a **Rust port** of the C [`n_signer_client.c`](../n_signer/client/n_signer_client.c:1) -(~855 lines). It reuses the existing `nsigner` library crate for transport +(~855 lines). It reuses the existing `signer` library crate for transport framing, socket discovery, and verb/error constants — the new code is the typed-verb client layer + CLI parsing + non-Unix transports. @@ -24,8 +24,8 @@ typed-verb client layer + CLI parsing + non-Unix transports. - [`src/client/main.rs`](src/client/main.rs:1) — entry point, CLI parse, dispatch - [`src/client/cli.rs`](src/client/cli.rs:1) — clap `Cli` / `Verb` structs + usage text - [`src/client/transport.rs`](src/client/transport.rs:1) — `ClientTransport` enum (Unix/Tcp/Serial/Qrexec) + open/connect helpers - - [`src/client/rpc.rs`](src/client/rpc.rs:1) — low-level `NsignerClient` (send framed JSON-RPC, recv, parse result/error) - - [`src/client/signer.rs`](src/client/signer.rs:1) — high-level `NsignerSigner` typed-verb wrappers (mirrors C `nostr_signer_t`) + - [`src/client/rpc.rs`](src/client/rpc.rs:1) — low-level `SignerClient` (send framed JSON-RPC, recv, parse result/error) + - [`src/client/signer.rs`](src/client/signer.rs:1) — high-level `SignerSigner` typed-verb wrappers (mirrors C `nostr_signer_t`) - [`src/client/auth.rs`](src/client/auth.rs:1) — client-side auth envelope builder for TCP/qrexec - New doc: [`src/client/README.md`](src/client/README.md:1) — usage, verbs, pipe-to-nak recipes (port of `n_signer_client_README.md`). - The existing `client` subcommand in [`src/main.rs`](src/main.rs:276) stays as a thin raw-passthrough convenience; it is **not** removed. @@ -48,23 +48,23 @@ typed-verb client layer + CLI parsing + non-Unix transports. options, selector options, algorithm options, mine-event options, and a `Verb` enum. 2. **`ClientTransport`** — enum wrapping the four connection types behind a - unified `send`/`recv` interface (the C `nsigner_transport_t` vtable). + unified `send`/`recv` interface (the C `signer_transport_t` vtable). - Unix: `connect_abstract_unix` (already in crate). - TCP: `std::net::TcpStream` + framed I/O (server already speaks framed JSON over TCP per [`server.rs`](src/server.rs:108)). - Serial: `std::fs::OpenOptions` on `/dev/ttyACM*` + framed I/O over the - file handle (matches C `nsigner_transport_open_serial`). + file handle (matches C `signer_transport_open_serial`). - Qrexec: spawn `qrexec-client-vm ` via `std::process`, pipe framed JSON over its stdin/stdout (matches C - `nsigner_transport_open_qrexec`). -3. **`NsignerClient`** — low-level RPC caller: builds `{"id","method","params"}` + `signer_transport_open_qrexec`). +3. **`SignerClient`** — low-level RPC caller: builds `{"id","method","params"}` JSON, sends framed, receives framed, splits `result` vs `error`, holds - `last_error`. Mirrors C `nsigner_client_t` / `nsigner_client_call`. -4. **`NsignerSigner`** — high-level typed-verb layer. Holds a `NsignerClient` + `last_error`. Mirrors C `signer_client_t` / `signer_client_call`. +4. **`SignerSigner`** — high-level typed-verb layer. Holds a `SignerClient` plus the resolved selector (`role` + `role_path`) and auth state. One method per verb, each building the correct `params` array + options object and parsing the typed result. Mirrors C `nostr_signer_t` / - `nostr_signer_nsigner_from_client`. + `nostr_signer_signer_from_client`. 5. **Client-side auth envelope builder** — for TCP/qrexec: construct a NIP-42 kind-22242 auth event from the `--auth-privkey`, sign it, and prepend it to the request frame. The server-side verifier in @@ -125,7 +125,7 @@ and the verb table in [`enforcement.rs`](src/enforcement.rs:22). | Verb | stdout | |---|---| -| `list` | Running nsigner abstract sockets (one per line) | +| `list` | Running signer abstract sockets (one per line) | ### Metadata @@ -229,9 +229,9 @@ flowchart TD Tr -->|tcp| Tcp[TcpStream + auth.rs] Tr -->|serial| Serial[OpenOptions /dev/ttyACM] Tr -->|qrexec| Qrexec[spawn qrexec-client-vm] - Tr --> Rpc[rpc.rs
NsignerClient] - Rpc --> Signer[signer.rs
NsignerSigner typed verbs] - Signer -->|build params| Disp[nsigner daemon
dispatcher.rs] + Tr --> Rpc[rpc.rs
SignerClient] + Rpc --> Signer[signer.rs
SignerSigner typed verbs] + Signer -->|build params| Disp[signer daemon
dispatcher.rs] Disp -->|result/error| Signer Signer -->|one line| Stdout[stdout] ``` @@ -246,10 +246,10 @@ flowchart TD `open_tcp`, `open_serial`, `open_qrexec`; unified `send`/`recv` via the existing `send_framed`/`recv_framed`. Include `parse_host_port` and `parse_qube_service` helpers. -4. Implement `rpc.rs`: `NsignerClient` struct holding the transport, +4. Implement `rpc.rs`: `SignerClient` struct holding the transport, `call(method, params) -> Result`, `last_error`, and framed send/recv using `serde_json`. -5. Implement `signer.rs`: `NsignerSigner` with selector state + one method +5. Implement `signer.rs`: `SignerSigner` with selector state + one method per verb (get_info, get_public_key, sign_event, mine_event, nip04/44 encrypt/decrypt, sign, verify, derive, encapsulate, decapsulate, derive_shared_secret, otp encrypt/decrypt). Each builds the `params` @@ -269,8 +269,8 @@ flowchart TD - **Unit tests** in `rpc.rs` / `signer.rs`: build-params correctness using `serde_json::json!` assertions (no socket needed). -- **Integration test** `tests/client_smoke.rs`: spawn `nsigner - --mnemonic-stdin --listen unix --socket-name nsigner_test` with a fixed +- **Integration test** `tests/client_smoke.rs`: spawn `signer + --mnemonic-stdin --listen unix --socket-name signer_test` with a fixed test mnemonic in a thread, then run the client verbs against it and assert stdout shape + exit codes. Tear down the server. - Manual pipe-to-`nak` check for `sign-event`. @@ -278,7 +278,7 @@ flowchart TD ## Out of scope - No TUI, no approval UI — the human attendant lives in the running - `nsigner` process; the client is a thin wire caller. + `signer` process; the client is a thin wire caller. - No key storage, no mnemonic handling. - No HTTP-listener client (HTTP is a server-side listener mode; the client uses the framed transports). diff --git a/plans/tui_flow_redesign.md b/plans/tui_flow_redesign.md index 4bcae32..35589b1 100644 --- a/plans/tui_flow_redesign.md +++ b/plans/tui_flow_redesign.md @@ -34,7 +34,7 @@ underlined (e.g. "Quit" with Q underlined, not "Q quit"). The user asked whether "client name" should be renamed since the signer is more a server than a client. **Decision: rename to "signer -name".** The field shows the socket name (e.g. `nsigner01`), which is +name".** The field shows the socket name (e.g. `signer01`), which is the name clients use to connect. Calling it "signer name" is clearer than "client name" and consistent with the program name. @@ -124,9 +124,9 @@ the C-format log entries (newest first). ├──────────────────────────────────────────┬──────────────────────────────────────────┤ │ Information │ Activity │ │ │ │ -│ signer name: nsigner01 │ 2026-08-18 15:05:42 unix:1000 │ +│ signer name: signer01 │ 2026-08-18 15:05:42 unix:1000 │ │ Unix address: │ secp256k1 m/44'/1237'/0'/0/0 │ -│ nsigner01 │ 2026-08-18 15:05:30 unix:1000 │ +│ signer01 │ 2026-08-18 15:05:30 unix:1000 │ │ Qube address: │ - - │ │ (inactive) │ 2026-08-18 15:04:55 unix:1000 │ │ FIPS address: │ secp256k1 m/44'/1237'/0'/0/0 │ @@ -142,7 +142,7 @@ the C-format log entries (newest first). │ [ ] Qube b̲ridge │ secp256k1 m/44'/1237'/0'/0/0 │ │ [ ] F̲IPS │ 2026-08-18 15:00:22 unix:1000 │ │ [ ] H̲TTP │ secp256k1 m/44'/1237'/0'/0/0 │ -│ │ 2026-08-18 15:00:10 nsigner started │ +│ │ 2026-08-18 15:00:10 signer started │ ├──────────────────────────────────────────┤ │ │ Roles │ │ │ │ │ @@ -575,7 +575,7 @@ and deleted directly in the Roles section. verify AddRole popup preset menu flow, verify Transport toggle (4 lines, tab navigation), verify Help screen is scrollable and shows app description + key commands, verify activity log shows - detailed request info, connect with `nsigner_client`. + detailed request info, connect with `signer_client`. ## What stays the same diff --git a/src/alg_cache.rs b/src/alg_cache.rs index 154c1f9..9c92a2e 100644 --- a/src/alg_cache.rs +++ b/src/alg_cache.rs @@ -6,7 +6,7 @@ use crate::mnemonic::MnemonicState; use crate::pq_crypto::CryptoAlg; use crate::secure_mem::SecureBuf; -use crate::NsignerError; +use crate::SignerError; pub const ALG_KEY_CACHE_MAX: usize = 32; @@ -47,9 +47,9 @@ impl AlgorithmKeyCache { mnemonic: &MnemonicState, alg: CryptoAlg, index: i32, - ) -> Result<(), NsignerError> { + ) -> Result<(), SignerError> { if !mnemonic.is_loaded() { - return Err(NsignerError::MnemonicNotLoaded); + return Err(SignerError::MnemonicNotLoaded); } // Already cached? @@ -62,7 +62,7 @@ impl AlgorithmKeyCache { self.entries.remove(0); } - let phrase = mnemonic.phrase().ok_or(NsignerError::MnemonicNotLoaded)?; + let phrase = mnemonic.phrase().ok_or(SignerError::MnemonicNotLoaded)?; // Build the standard derivation path for this algorithm let path = match alg { @@ -72,7 +72,7 @@ impl AlgorithmKeyCache { CryptoAlg::MlDsa65 => format!("m/44'/102003'/{}'/0'/0'", index), CryptoAlg::SlhDsa128s => format!("m/44'/102004'/{}'/0'/0'", index), CryptoAlg::MlKem768 => format!("m/44'/102005'/{}'/0'/0'", index), - CryptoAlg::Unknown => return Err(NsignerError::InvalidInput), + CryptoAlg::Unknown => return Err(SignerError::InvalidInput), }; let entry = derive_alg_key(phrase, &path, alg, index)?; @@ -98,10 +98,10 @@ fn derive_alg_key( path: &str, alg: CryptoAlg, index: i32, -) -> Result { +) -> Result { let sizes = alg .sizes() - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; let seed = crate::pq_crypto::derive_seed_from_mnemonic(mnemonic_phrase, path)?; @@ -111,19 +111,19 @@ fn derive_alg_key( let bip39_seed = nips::nip006::mnemonic_to_seed(mnemonic_phrase, ""); let (master_key, master_chain_code) = nips::nip006::bip32_master_key(&bip39_seed); let path_indices = nips::nip006::parse_bip44_path(path) - .map_err(|_| NsignerError::KeyDerivationFailed)?; + .map_err(|_| SignerError::KeyDerivationFailed)?; let (derived_key, _) = nips::nip006::bip32_derive_path( &master_key, &master_chain_code, &path_indices, ) - .map_err(|_| NsignerError::KeyDerivationFailed)?; + .map_err(|_| SignerError::KeyDerivationFailed)?; let mut priv_arr = [0u8; 32]; priv_arr.copy_from_slice(&derived_key); let sk = nostr_core::types::SecretKey::from_bytes(priv_arr); let pk = nostr_core::crypto::keys::public_key_from_secret_key(&sk) - .map_err(|_| NsignerError::CryptoFailed)?; + .map_err(|_| SignerError::CryptoFailed)?; let pubkey_hex = hex::encode(pk.as_bytes()); let key_id = if pubkey_hex.len() >= 16 { @@ -200,8 +200,8 @@ fn derive_alg_key( } CryptoAlg::MlDsa65 | CryptoAlg::SlhDsa128s | CryptoAlg::MlKem768 => { // PQ algorithms — TODO: Phase 13 - Err(NsignerError::NotYetImplemented) + Err(SignerError::NotYetImplemented) } - CryptoAlg::Unknown => Err(NsignerError::InvalidInput), + CryptoAlg::Unknown => Err(SignerError::InvalidInput), } } diff --git a/src/auth_envelope.rs b/src/auth_envelope.rs index 6610607..cc0937a 100644 --- a/src/auth_envelope.rs +++ b/src/auth_envelope.rs @@ -132,17 +132,17 @@ pub fn verify_request( return Err((AUTH_ERR_KIND_INVALID, "auth_kind_invalid")); } - // Extract tags: nsigner_rpc, nsigner_method, nsigner_body_hash + // Extract tags: signer_rpc, signer_method, signer_body_hash let mut tag_rpc: Option = None; let mut tag_method: Option = None; let mut tag_body_hash: Option = None; for tag in &auth_event.tags { - if tag.kind() == "nsigner_rpc" { + if tag.kind() == "signer_rpc" { tag_rpc = tag.get(1).map(|s| s.to_string()); - } else if tag.kind() == "nsigner_method" { + } else if tag.kind() == "signer_method" { tag_method = tag.get(1).map(|s| s.to_string()); - } else if tag.kind() == "nsigner_body_hash" { + } else if tag.kind() == "signer_body_hash" { tag_body_hash = tag.get(1).map(|s| s.to_string()); } } diff --git a/src/client/README.md b/src/client/README.md index d38ef74..0db88b0 100644 --- a/src/client/README.md +++ b/src/client/README.md @@ -1,6 +1,6 @@ -# `signer-client` — Rust CLI for nsigner +# `signer-client` — Rust CLI for signer -A standalone command-line client that connects to a running [`nsigner`](../..) +A standalone command-line client that connects to a running [`signer`](../..) process and calls its JSON-RPC verbs over stdin/stdout. Designed for pipe-to-`nak` workflows. Rust port of the C [`n_signer_client.c`](../../n_signer/client/n_signer_client.c). @@ -62,7 +62,7 @@ signer-client [global options] [verb args...] | Verb | stdout | |------|--------| -| `list` | Lists running nsigner abstract sockets (one per line) | +| `list` | Lists running signer abstract sockets (one per line) | ### Metadata @@ -140,10 +140,10 @@ signer-client get-info | Transport | Flag | Notes | |-----------|------|-------| -| UNIX abstract socket | `--socket-name ` or auto-discover | Default. Auto-discovers if exactly one `nsigner*` socket exists. | +| UNIX abstract socket | `--socket-name ` or auto-discover | Default. Auto-discovers if exactly one `signer*` socket exists. | | TCP | `--tcp ` | Requires `--auth-privkey` for auth envelope. | | Serial (USB CDC-ACM) | `--serial ` | e.g. `--serial /dev/ttyACM0` | -| Qubes qrexec | `--qrexec ` | e.g. `--qrexec sys-signer:qubes.NsignerRpc` | +| Qubes qrexec | `--qrexec ` | e.g. `--qrexec sys-signer:qubes.SignerRpc` | ## Exit codes @@ -170,11 +170,11 @@ signer-client get-info | [`main.rs`](main.rs:1) | Entry point, CLI parse, verb dispatch, stdin/stdout | | [`cli.rs`](cli.rs:1) | clap `Cli` / `Verb` structs + usage text | | [`transport.rs`](transport.rs:1) | `ClientTransport` enum (Unix/Tcp/Serial/Qrexec) | -| [`rpc.rs`](rpc.rs:1) | Low-level `NsignerClient` (framed JSON-RPC send/recv) | -| [`signer.rs`](signer.rs:1) | High-level `NsignerSigner` typed-verb wrappers | +| [`rpc.rs`](rpc.rs:1) | Low-level `SignerClient` (framed JSON-RPC send/recv) | +| [`signer.rs`](signer.rs:1) | High-level `SignerSigner` typed-verb wrappers | | [`auth.rs`](auth.rs:1) | Client-side auth envelope builder (NIP-42 kind 22242) | ## See also - [`plans/signer_client_plan.md`](../../plans/signer_client_plan.md:1) — implementation plan -- [`README.md`](../../README.md:1) — nsigner main documentation +- [`README.md`](../../README.md:1) — signer main documentation diff --git a/src/client/auth.rs b/src/client/auth.rs index 3345a4a..7a73e64 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -1,13 +1,13 @@ //! Client-side auth envelope builder for TCP/qrexec transports. //! //! Builds a NIP-42 kind-22242 auth event matching the wire shape verified by -//! [`nsigner::auth_envelope::verify_request`]. The event is attached as a +//! [`signer::auth_envelope::verify_request`]. The event is attached as a //! top-level `"auth"` field on the JSON-RPC request. //! //! Tag contract (must match the server verifier): -//! - `["nsigner_rpc", ]` -//! - `["nsigner_method", ]` -//! - `["nsigner_body_hash", ]` +//! - `["signer_rpc", ]` +//! - `["signer_method", ]` +//! - `["signer_body_hash", ]` //! - `content` = auth label use nostr_core::types::{Event, Kind, SecretKey, Tag}; @@ -46,9 +46,9 @@ pub fn build_auth_event( // Tags (two-element: kind + value) let tags = vec![ - Tag::with_value("nsigner_rpc", request_id), - Tag::with_value("nsigner_method", method), - Tag::with_value("nsigner_body_hash", &body_hash_hex), + Tag::with_value("signer_rpc", request_id), + Tag::with_value("signer_method", method), + Tag::with_value("signer_body_hash", &body_hash_hex), ]; let created_at = std::time::SystemTime::now() diff --git a/src/client/cli.rs b/src/client/cli.rs index 0f354f1..110154b 100644 --- a/src/client/cli.rs +++ b/src/client/cli.rs @@ -6,8 +6,8 @@ use clap::{Parser, Subcommand}; #[derive(Parser, Debug)] #[command( name = "signer-client", - version = nsigner::VERSION, - about = "Standalone CLI for the nsigner JSON-RPC API" + version = ::signer::VERSION, + about = "Standalone CLI for the signer JSON-RPC API" )] pub struct Cli { /// Abstract socket name (without @ prefix). Default: auto-discover. @@ -89,7 +89,7 @@ pub struct Cli { /// Verb subcommands. #[derive(Subcommand, Debug)] pub enum Verb { - /// List running nsigner abstract sockets. + /// List running signer abstract sockets. List, /// Get signer metadata. @@ -227,7 +227,7 @@ pub fn print_usage(prog: &str) { \x20 --timeout-sec Mining timeout in seconds\n\ \n\ Verbs:\n\ - \x20 list List running nsigner sockets\n\ + \x20 list List running signer sockets\n\ \x20 get-info\n\ \x20 get-public-key\n\ \x20 sign-event\n\ diff --git a/src/client/main.rs b/src/client/main.rs index 1e3918a..69aa18a 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -1,7 +1,7 @@ -//! signer-client — standalone CLI for the nsigner JSON-RPC API. +//! signer-client — standalone CLI for the signer JSON-RPC API. //! //! Port of the C [`n_signer_client.c`](../../n_signer/client/n_signer_client.c). -//! Connects to a running `nsigner` process over its framed transports +//! Connects to a running `signer` process over its framed transports //! (Unix / TCP / serial / qrexec) and exposes the full verb surface over //! stdin/stdout so that signed events can be piped directly into `nak publish`. @@ -13,7 +13,7 @@ mod transport; use clap::Parser; use cli::{Cli, Verb}; -use signer::{result_to_line, AlgOptions, NsignerSigner}; +use crate::signer::{result_to_line, AlgOptions, SignerSigner}; fn main() { let cli = match Cli::try_parse() { @@ -31,9 +31,9 @@ fn main() { fn run(cli: Cli) -> i32 { // ── list verb (no connection needed) ────────────────────────────── if matches!(cli.verb, Verb::List) { - let sockets = nsigner::socket_name::list_sockets(); + let sockets = ::signer::socket_name::list_sockets(); if sockets.is_empty() { - println!("no nsigner sockets found"); + println!("no signer sockets found"); } else { for name in &sockets { println!("{}", name); @@ -92,7 +92,7 @@ fn run(cli: Cli) -> i32 { return 2; } - let mut signer = NsignerSigner::new(rpc::NsignerClient::new(transport)); + let mut signer = SignerSigner::new(rpc::SignerClient::new(transport)); // ── set selector for nostr verbs ────────────────────────────────── if is_nostr_verb && !is_algorithm_verb { diff --git a/src/client/rpc.rs b/src/client/rpc.rs index 0dc87db..5fed21a 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -1,6 +1,6 @@ //! Low-level JSON-RPC 2.0 client — framed send/recv over a transport. //! -//! Port of the C `nsigner_client_t` / `nsigner_client_call`. Builds a +//! Port of the C `signer_client_t` / `signer_client_call`. Builds a //! `{"id","method","params"}` request, sends it framed, receives the framed //! response, and splits `result` vs `error`. @@ -8,17 +8,17 @@ use serde_json::{json, Value}; use super::transport::ClientTransport; -/// Low-level nsigner RPC client. Owns the transport. -pub struct NsignerClient { +/// Low-level signer RPC client. Owns the transport. +pub struct SignerClient { transport: ClientTransport, last_error: String, next_id: u64, } -impl NsignerClient { +impl SignerClient { /// Wrap an open transport. pub fn new(transport: ClientTransport) -> Self { - NsignerClient { + SignerClient { transport, last_error: String::new(), next_id: 1, @@ -115,7 +115,7 @@ mod tests { #[test] fn test_extract_result_success() { - let mut client = NsignerClient { + let mut client = SignerClient { transport: ClientTransport::Unix( std::os::unix::net::UnixStream::pair().unwrap().0, ), @@ -129,7 +129,7 @@ mod tests { #[test] fn test_extract_result_error() { - let mut client = NsignerClient { + let mut client = SignerClient { transport: ClientTransport::Unix( std::os::unix::net::UnixStream::pair().unwrap().0, ), diff --git a/src/client/signer.rs b/src/client/signer.rs index 96d7bdd..0bf0056 100644 --- a/src/client/signer.rs +++ b/src/client/signer.rs @@ -1,12 +1,12 @@ //! High-level typed-verb layer — one method per JSON-RPC verb. //! //! Port of the C `nostr_signer_t` wrappers. Each method builds the correct -//! `params` array + options object, calls the low-level [`NsignerClient`], +//! `params` array + options object, calls the low-level [`SignerClient`], //! and parses the typed result. use serde_json::{json, Value}; -use super::rpc::NsignerClient; +use super::rpc::SignerClient; /// Selector state for nostr verbs. #[derive(Debug, Clone, Default)] @@ -58,18 +58,18 @@ impl AlgOptions { } } -/// High-level nsigner signer. Wraps a low-level client and holds selector + +/// High-level signer signer. Wraps a low-level client and holds selector + /// auth state used across typed verbs. -pub struct NsignerSigner { - pub client: NsignerClient, +pub struct SignerSigner { + pub client: SignerClient, pub selector: Selector, pub auth_privkey: Option, pub auth_label: String, } -impl NsignerSigner { - pub fn new(client: NsignerClient) -> Self { - NsignerSigner { +impl SignerSigner { + pub fn new(client: SignerClient) -> Self { + SignerSigner { client, selector: Selector::default(), auth_privkey: None, diff --git a/src/client/transport.rs b/src/client/transport.rs index 705c2a6..63220f2 100644 --- a/src/client/transport.rs +++ b/src/client/transport.rs @@ -1,8 +1,8 @@ //! Client transport — unified framed I/O over Unix / TCP / Serial / Qrexec. //! -//! Port of the C `nsigner_transport_t` vtable. All four transports share the +//! Port of the C `signer_transport_t` vtable. All four transports share the //! same `send_framed` / `recv_framed` path after construction (defined in -//! [`nsigner::transport`]). +//! [`::signer::transport`]). use std::io; use std::process::{Child, ChildStdin, ChildStdout}; @@ -27,7 +27,7 @@ pub enum ClientTransport { impl ClientTransport { /// Open a Unix abstract-socket transport by name (without `@`). pub fn open_unix(name: &str, _timeout_ms: u64) -> io::Result { - let stream = nsigner::transport::connect_abstract_unix(name)?; + let stream = ::signer::transport::connect_abstract_unix(name)?; Ok(ClientTransport::Unix(stream)) } @@ -76,11 +76,11 @@ impl ClientTransport { /// Send a framed JSON message. pub fn send(&mut self, payload: &str) -> io::Result<()> { match self { - ClientTransport::Unix(s) => nsigner::transport::send_framed(s, payload), - ClientTransport::Tcp(s) => nsigner::transport::send_framed(s, payload), - ClientTransport::Serial(f) => nsigner::transport::send_framed(f, payload), + ClientTransport::Unix(s) => ::signer::transport::send_framed(s, payload), + ClientTransport::Tcp(s) => ::signer::transport::send_framed(s, payload), + ClientTransport::Serial(f) => ::signer::transport::send_framed(f, payload), ClientTransport::Qrexec { stdin, .. } => { - nsigner::transport::send_framed(stdin, payload) + ::signer::transport::send_framed(stdin, payload) } } } @@ -88,11 +88,11 @@ impl ClientTransport { /// Receive a framed JSON message. pub fn recv(&mut self) -> io::Result { match self { - ClientTransport::Unix(s) => nsigner::transport::recv_framed(s), - ClientTransport::Tcp(s) => nsigner::transport::recv_framed(s), - ClientTransport::Serial(f) => nsigner::transport::recv_framed(f), + ClientTransport::Unix(s) => ::signer::transport::recv_framed(s), + ClientTransport::Tcp(s) => ::signer::transport::recv_framed(s), + ClientTransport::Serial(f) => ::signer::transport::recv_framed(f), ClientTransport::Qrexec { stdout, .. } => { - nsigner::transport::recv_framed(stdout) + ::signer::transport::recv_framed(stdout) } } } @@ -179,13 +179,13 @@ pub fn open_from_cli( } // Auto-discover: enumerate abstract UNIX sockets. - let sockets = nsigner::socket_name::list_sockets(); + let sockets = ::signer::socket_name::list_sockets(); if sockets.is_empty() { - return Err("no nsigner sockets found. Is nsigner running?".into()); + return Err("no signer sockets found. Is signer running?".into()); } if sockets.len() > 1 { let mut msg = String::from( - "multiple nsigner sockets found. Use --socket-name to select one:\n", + "multiple signer sockets found. Use --socket-name to select one:\n", ); for n in &sockets { msg.push_str(&format!(" {}\n", n)); diff --git a/src/dispatcher.rs b/src/dispatcher.rs index 4540a1a..6a2a766 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -11,7 +11,7 @@ use crate::mnemonic::MnemonicState; use crate::pq_crypto::CryptoAlg; use crate::role_table::RoleTable; use crate::selector::{selector_resolve, SelectorRequest}; -use crate::NsignerError; +use crate::SignerError; use serde_json::{json, Value}; @@ -551,7 +551,7 @@ fn is_nostr_verb(verb: &str) -> bool { ) } -fn sign_with_alg(alg: CryptoAlg, priv_key: &[u8; 32], msg: &[u8]) -> Result, NsignerError> { +fn sign_with_alg(alg: CryptoAlg, priv_key: &[u8; 32], msg: &[u8]) -> Result, SignerError> { match alg { CryptoAlg::Secp256k1 => { // Check for scheme option (schnorr default, ecdsa alternative) @@ -567,7 +567,7 @@ fn sign_with_alg(alg: CryptoAlg, priv_key: &[u8; 32], msg: &[u8]) -> Result crate::pq_crypto::ml_dsa_65_sign(priv_key, msg), CryptoAlg::SlhDsa128s => crate::pq_crypto::slh_dsa_128s_sign(priv_key, msg), - _ => Err(NsignerError::CryptoFailed), + _ => Err(SignerError::CryptoFailed), } } diff --git a/src/error.rs b/src/error.rs index 0341dae..90f615c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,13 @@ -//! Error types for nsigner. +//! Error types for signer. //! //! JSON-RPC error codes are preserved exactly for wire compatibility //! with the C n_signer. use thiserror::Error; -/// nsigner-specific errors (internal operations). +/// signer-specific errors (internal operations). #[derive(Error, Debug, Clone)] -pub enum NsignerError { +pub enum SignerError { #[error("invalid input")] InvalidInput, #[error("memory allocation failed (mlock)")] @@ -32,13 +32,13 @@ pub enum NsignerError { Internal(String), } -impl From for NsignerError { +impl From for SignerError { fn from(e: nostr_core::error::NostrError) -> Self { - // Map NostrError to NsignerError + // Map NostrError to SignerError match e { - nostr_core::error::NostrError::InvalidInput => NsignerError::InvalidInput, - nostr_core::error::NostrError::CryptoFailed => NsignerError::CryptoFailed, - _ => NsignerError::CryptoFailed, + nostr_core::error::NostrError::InvalidInput => SignerError::InvalidInput, + nostr_core::error::NostrError::CryptoFailed => SignerError::CryptoFailed, + _ => SignerError::CryptoFailed, } } } @@ -60,7 +60,7 @@ impl RpcError { pub const INVALID_PARAMS: Self = RpcError { code: -32602, message: "invalid_params" }; pub const INTERNAL_ERROR: Self = RpcError { code: -32603, message: "internal_error" }; - // ── nsigner-specific errors ────────────────────────────────────── + // ── signer-specific errors ────────────────────────────────────── pub const AMBIGUOUS_ROLE_SELECTOR: Self = RpcError { code: 1001, message: "ambiguous_role_selector" }; pub const UNKNOWN_ROLE: Self = RpcError { code: 1002, message: "unknown_role" }; pub const NO_DEFAULT_ROLE: Self = RpcError { code: 1003, message: "no_default_role" }; diff --git a/src/http.rs b/src/http.rs index ee0f01b..ec9ab8a 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,4 +1,4 @@ -//! Minimal HTTP/1.1 parser for nsigner's HTTP listener mode. +//! Minimal HTTP/1.1 parser for signer's HTTP listener mode. //! //! Port of `http_listener.c`. Only supports POST with a JSON body. //! No chunked encoding, no keep-alive, one request per connection. diff --git a/src/key_store.rs b/src/key_store.rs index 3d6a6c8..6f57546 100644 --- a/src/key_store.rs +++ b/src/key_store.rs @@ -7,7 +7,7 @@ use crate::mnemonic::MnemonicState; use crate::pq_crypto::{self, CryptoAlg}; use crate::role_table::{self, RoleCurve, RoleEntry, RolePurpose, RoleTable}; use crate::secure_mem::SecureBuf; -use crate::NsignerError; +use crate::SignerError; /// Per-role derived key material (stored in secure memory). pub struct DerivedKey { @@ -35,12 +35,12 @@ impl KeyStore { &mut self, table: &mut RoleTable, mnemonic: &MnemonicState, - ) -> Result { + ) -> Result { if !mnemonic.is_loaded() { - return Err(NsignerError::MnemonicNotLoaded); + return Err(SignerError::MnemonicNotLoaded); } - let phrase = mnemonic.phrase().ok_or(NsignerError::MnemonicNotLoaded)?; + let phrase = mnemonic.phrase().ok_or(SignerError::MnemonicNotLoaded)?; self.keys.clear(); self.keys.resize_with(table.entries.len(), || None); @@ -86,16 +86,16 @@ impl KeyStore { table: &mut RoleTable, mnemonic: &MnemonicState, role_index: usize, - ) -> Result<(), NsignerError> { + ) -> Result<(), SignerError> { if !mnemonic.is_loaded() { - return Err(NsignerError::MnemonicNotLoaded); + return Err(SignerError::MnemonicNotLoaded); } - let phrase = mnemonic.phrase().ok_or(NsignerError::MnemonicNotLoaded)?; + let phrase = mnemonic.phrase().ok_or(SignerError::MnemonicNotLoaded)?; let role = table .entries .get_mut(role_index) - .ok_or(NsignerError::InvalidInput)?; + .ok_or(SignerError::InvalidInput)?; role.derived = false; role.pubkey_hex.clear(); @@ -120,16 +120,16 @@ impl KeyStore { mnemonic: &MnemonicState, role_index: usize, concrete_path: &str, - ) -> Result<(), NsignerError> { + ) -> Result<(), SignerError> { if !mnemonic.is_loaded() { - return Err(NsignerError::MnemonicNotLoaded); + return Err(SignerError::MnemonicNotLoaded); } - let phrase = mnemonic.phrase().ok_or(NsignerError::MnemonicNotLoaded)?; + let phrase = mnemonic.phrase().ok_or(SignerError::MnemonicNotLoaded)?; let role = table .entries .get_mut(role_index) - .ok_or(NsignerError::InvalidInput)?; + .ok_or(SignerError::InvalidInput)?; role.derived = false; role.pubkey_hex.clear(); @@ -161,14 +161,14 @@ impl KeyStore { &self, role_index: usize, event_json: &str, - ) -> Result { + ) -> Result { let priv_bytes = self .get_private_key(role_index) - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; // Parse the unsigned event from JSON let event: nostr_core::types::Event = - serde_json::from_str(event_json).map_err(|_| NsignerError::InvalidInput)?; + serde_json::from_str(event_json).map_err(|_| SignerError::InvalidInput)?; // Use NIP-01 to create and sign the event let mut priv_arr = [0u8; 32]; @@ -182,9 +182,9 @@ impl KeyStore { &sk, event.created_at, ) - .map_err(|_| NsignerError::CryptoFailed)?; + .map_err(|_| SignerError::CryptoFailed)?; - serde_json::to_string(&signed).map_err(|_| NsignerError::InvalidInput) + serde_json::to_string(&signed).map_err(|_| SignerError::InvalidInput) } /// NIP-44 encrypt. @@ -193,20 +193,20 @@ impl KeyStore { role_index: usize, recipient_pubkey_hex: &str, plaintext: &str, - ) -> Result, NsignerError> { + ) -> Result, SignerError> { let priv_bytes = self .get_private_key(role_index) - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; let recipient_pk: nostr_core::types::PublicKey = recipient_pubkey_hex .parse() - .map_err(|_| NsignerError::InvalidInput)?; + .map_err(|_| SignerError::InvalidInput)?; let mut priv_arr = [0u8; 32]; priv_arr.copy_from_slice(&priv_bytes[..32]); let sk = nostr_core::types::SecretKey::from_bytes(priv_arr); nostr_core::crypto::nip44::nip44_encrypt(&sk, &recipient_pk, plaintext.as_bytes()) - .map_err(|_| NsignerError::CryptoFailed) + .map_err(|_| SignerError::CryptoFailed) } /// NIP-44 decrypt. @@ -215,20 +215,20 @@ impl KeyStore { role_index: usize, sender_pubkey_hex: &str, ciphertext: &[u8], - ) -> Result, NsignerError> { + ) -> Result, SignerError> { let priv_bytes = self .get_private_key(role_index) - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; let sender_pk: nostr_core::types::PublicKey = sender_pubkey_hex .parse() - .map_err(|_| NsignerError::InvalidInput)?; + .map_err(|_| SignerError::InvalidInput)?; let mut priv_arr = [0u8; 32]; priv_arr.copy_from_slice(&priv_bytes[..32]); let sk = nostr_core::types::SecretKey::from_bytes(priv_arr); nostr_core::crypto::nip44::nip44_decrypt(&sk, &sender_pk, ciphertext) - .map_err(|_| NsignerError::CryptoFailed) + .map_err(|_| SignerError::CryptoFailed) } /// NIP-04 encrypt. @@ -237,20 +237,20 @@ impl KeyStore { role_index: usize, recipient_pubkey_hex: &str, plaintext: &str, - ) -> Result { + ) -> Result { let priv_bytes = self .get_private_key(role_index) - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; let recipient_pk: nostr_core::types::PublicKey = recipient_pubkey_hex .parse() - .map_err(|_| NsignerError::InvalidInput)?; + .map_err(|_| SignerError::InvalidInput)?; let mut priv_arr = [0u8; 32]; priv_arr.copy_from_slice(&priv_bytes[..32]); let sk = nostr_core::types::SecretKey::from_bytes(priv_arr); nips::nip004::nip04_encrypt(&sk, &recipient_pk, plaintext) - .map_err(|_| NsignerError::CryptoFailed) + .map_err(|_| SignerError::CryptoFailed) } /// NIP-04 decrypt. @@ -259,20 +259,20 @@ impl KeyStore { role_index: usize, sender_pubkey_hex: &str, ciphertext: &str, - ) -> Result { + ) -> Result { let priv_bytes = self .get_private_key(role_index) - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; let sender_pk: nostr_core::types::PublicKey = sender_pubkey_hex .parse() - .map_err(|_| NsignerError::InvalidInput)?; + .map_err(|_| SignerError::InvalidInput)?; let mut priv_arr = [0u8; 32]; priv_arr.copy_from_slice(&priv_bytes[..32]); let sk = nostr_core::types::SecretKey::from_bytes(priv_arr); nips::nip004::nip04_decrypt(&sk, &sender_pk, ciphertext) - .map_err(|_| NsignerError::CryptoFailed) + .map_err(|_| SignerError::CryptoFailed) } /// Zeroize all derived keys. @@ -288,18 +288,18 @@ fn derive_for_role( path: &str, role: &RoleEntry, mnemonic_phrase: &str, -) -> Result { +) -> Result { let alg = role_table::crypto_alg_from_role(role.curve, role.purpose); // crypto_alg_from_role returns Unknown for OTP, but we skip OTP earlier let alg = if alg == CryptoAlg::Unknown { - return Err(NsignerError::KeyDerivationFailed); + return Err(SignerError::KeyDerivationFailed); } else { alg }; let sizes = alg .sizes() - .ok_or(NsignerError::KeyDerivationFailed)?; + .ok_or(SignerError::KeyDerivationFailed)?; // Derive the 32-byte seed from the mnemonic using the path let seed = pq_crypto::derive_seed_from_mnemonic(mnemonic_phrase, path)?; @@ -310,19 +310,19 @@ fn derive_for_role( let bip39_seed = nips::nip006::mnemonic_to_seed(mnemonic_phrase, ""); let (master_key, master_chain_code) = nips::nip006::bip32_master_key(&bip39_seed); let path_indices = nips::nip006::parse_bip44_path(path) - .map_err(|_| NsignerError::KeyDerivationFailed)?; + .map_err(|_| SignerError::KeyDerivationFailed)?; let (derived_key, _) = nips::nip006::bip32_derive_path( &master_key, &master_chain_code, &path_indices, ) - .map_err(|_| NsignerError::KeyDerivationFailed)?; + .map_err(|_| SignerError::KeyDerivationFailed)?; let mut priv_arr = [0u8; 32]; priv_arr.copy_from_slice(&derived_key); let sk = nostr_core::types::SecretKey::from_bytes(priv_arr); let pk = nostr_core::crypto::keys::public_key_from_secret_key(&sk) - .map_err(|_| NsignerError::CryptoFailed)?; + .map_err(|_| SignerError::CryptoFailed)?; let mut priv_buf = SecureBuf::alloc(sizes.priv_key_len)?; priv_buf.copy_from(&priv_arr); @@ -384,9 +384,9 @@ fn derive_for_role( } CryptoAlg::MlDsa65 | CryptoAlg::SlhDsa128s | CryptoAlg::MlKem768 => { // PQ algorithms — TODO: Phase 13 - Err(NsignerError::NotYetImplemented) + Err(SignerError::NotYetImplemented) } - CryptoAlg::Unknown => Err(NsignerError::KeyDerivationFailed), + CryptoAlg::Unknown => Err(SignerError::KeyDerivationFailed), } } diff --git a/src/lib.rs b/src/lib.rs index 167c64f..f8b05cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! # nsigner — Attended Nostr signing daemon +//! # signer — Attended Nostr signing daemon //! //! Rust port of the C-based `n_signer`. Holds signing key material in //! locked memory and signs on request via a JSON-RPC 2.0 API over @@ -28,7 +28,7 @@ pub mod socket_name; pub mod tui; pub mod error; -pub use error::NsignerError; +pub use error::SignerError; /// Version string (matches C NSIGNER_VERSION). -pub const VERSION: &str = "v0.0.12"; +pub const VERSION: &str = "v0.0.13"; diff --git a/src/main.rs b/src/main.rs index ce79b2f..2ded126 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,22 @@ -//! nsigner — attended Nostr signing daemon. +//! signer — attended Nostr signing daemon. //! //! Port of `main.c`. Single binary that holds signing key material in //! locked memory and signs on request via JSON-RPC 2.0. use clap::{Parser, Subcommand}; -use nsigner::{ +use signer::{ alg_cache::AlgorithmKeyCache, dispatcher::DispatcherContext, key_store::KeyStore, mnemonic::MnemonicState, role_table::{RoleCurve, RolePurpose, RoleTable}, server::{AuthMode, ListenMode, ServerContext}, - NsignerError, + SignerError, }; /// Command-line arguments. #[derive(Parser, Debug)] -#[command(name = "nsigner", version = nsigner::VERSION, about = "Attended Nostr signing daemon")] +#[command(name = "signer", version = signer::VERSION, about = "Attended Nostr signing daemon")] struct Cli { /// Socket name (abstract namespace, without @ prefix) #[arg(long, short = 'n', alias = "name")] @@ -85,7 +85,7 @@ enum Commands { to: Option, }, - /// List running nsigner abstract sockets + /// List running signer abstract sockets List, } @@ -111,7 +111,7 @@ fn main() { match server_main(&cli) { Ok(()) => {} Err(e) => { - eprintln!("nsigner: {}", e); + eprintln!("signer: {}", e); std::process::exit(1); } } @@ -126,11 +126,11 @@ fn main() { /// `--register-role` / `--listen`): mnemonic and roles are set up here, /// then the App runs with `listen_override` so it goes straight to the /// main screen. Headless modes (stdio/qrexec/tcp/http) never show a TUI. -fn server_main(cli: &Cli) -> Result<(), NsignerError> { - println!("nsigner {}", nsigner::VERSION); +fn server_main(cli: &Cli) -> Result<(), SignerError> { + println!("signer {}", signer::VERSION); if cli.allow_unlocked_memory { - nsigner::secure_mem::allow_unlocked(); + signer::secure_mem::allow_unlocked(); } let interactive = !cli.mnemonic_stdin && cli.mnemonic_fd.is_none(); @@ -153,7 +153,7 @@ fn server_main(cli: &Cli) -> Result<(), NsignerError> { .socket_name .clone() .unwrap_or_else(|| { - nsigner::socket_name::socket_name_random().unwrap_or_default() + signer::socket_name::socket_name_random().unwrap_or_default() }); let auth_mode = parse_auth_mode(&cli.auth); @@ -163,7 +163,7 @@ fn server_main(cli: &Cli) -> Result<(), NsignerError> { // (Unix only — non-Unix modes are headless above), the transport // is pre-selected on the main screen. Pass the raw --listen string // so the App can adopt an explicit tcp:/http: bind address. - let mut app = nsigner::tui::App::new( + let mut app = signer::tui::App::new( RoleTable::new(), MnemonicState::new(), KeyStore::new(), @@ -176,7 +176,7 @@ fn server_main(cli: &Cli) -> Result<(), NsignerError> { let mut terminal = ratatui::init(); let result = app.run(&mut terminal); ratatui::restore(); - result.map_err(|e| NsignerError::IoFailed(e.to_string())) + result.map_err(|e| SignerError::IoFailed(e.to_string())) } else { // Unreachable: non-interactive modes return headless above. Ok(()) @@ -184,14 +184,14 @@ fn server_main(cli: &Cli) -> Result<(), NsignerError> { } /// Run a headless server (stdio, qrexec, tcp, http) — no TUI. -fn run_headless(cli: &Cli, listen_mode: ListenMode) -> Result<(), NsignerError> { +fn run_headless(cli: &Cli, listen_mode: ListenMode) -> Result<(), SignerError> { let mut mnemonic = MnemonicState::new(); if cli.mnemonic_stdin { let mut input = String::new(); std::io::stdin() .read_line(&mut input) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let phrase = input.trim().to_string(); mnemonic.load(&phrase)?; } else if let Some(fd) = cli.mnemonic_fd { @@ -200,7 +200,7 @@ fn run_headless(cli: &Cli, listen_mode: ListenMode) -> Result<(), NsignerError> let mut file = unsafe { std::fs::File::from_raw_fd(fd) }; let mut input = String::new(); file.read_to_string(&mut input) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let phrase = input.trim().to_string(); mnemonic.load(&phrase)?; } @@ -219,7 +219,7 @@ fn run_headless(cli: &Cli, listen_mode: ListenMode) -> Result<(), NsignerError> RoleCurve::Secp256k1, -1, -1, -1, &[], ) - .map_err(|e| NsignerError::Internal(e.to_string()))?; + .map_err(|e| SignerError::Internal(e.to_string()))?; } let mut key_store = KeyStore::new(); @@ -276,16 +276,16 @@ fn run_headless(cli: &Cli, listen_mode: ListenMode) -> Result<(), NsignerError> fn client_main(request: &str, cli: &Cli) -> i32 { use std::io::Read; - let socket_name = cli.socket_name.as_deref().unwrap_or("nsigner01"); + let socket_name = cli.socket_name.as_deref().unwrap_or("signer01"); // Discover single socket if not explicit let socket_name = if cli.socket_name.is_some() { socket_name.to_string() } else { - nsigner::socket_name::discover_single_socket().unwrap_or_else(|_| socket_name.to_string()) + signer::socket_name::discover_single_socket().unwrap_or_else(|_| socket_name.to_string()) }; - let mut stream = match nsigner::transport::connect_abstract_unix(&socket_name) { + let mut stream = match signer::transport::connect_abstract_unix(&socket_name) { Ok(s) => s, Err(e) => { eprintln!("Failed to connect to {}: {}", socket_name, e); @@ -306,13 +306,13 @@ fn client_main(request: &str, cli: &Cli) -> i32 { }; // Send framed request - if nsigner::transport::send_framed(&mut stream, &request).is_err() { + if signer::transport::send_framed(&mut stream, &request).is_err() { eprintln!("Failed to send request"); return 1; } // Receive framed response - match nsigner::transport::recv_framed(&mut stream) { + match signer::transport::recv_framed(&mut stream) { Ok(response) => { println!("{}", response); 0 @@ -328,18 +328,18 @@ fn client_main(request: &str, cli: &Cli) -> i32 { fn bridge_main(to: Option<&str>, cli: &Cli) -> i32 { - let target = to.unwrap_or("nsigner01"); + let target = to.unwrap_or("signer01"); let target = if cli.socket_name.is_some() { target.to_string() } else { - nsigner::socket_name::discover_single_socket().unwrap_or_else(|_| target.to_string()) + signer::socket_name::discover_single_socket().unwrap_or_else(|_| target.to_string()) }; // Read source qube from qrexec environment let source_qube = std::env::var("QREXEC_REMOTE_DOMAIN").unwrap_or_default(); // Connect to persistent signer via abstract socket - let mut stream = match nsigner::transport::connect_abstract_unix(&target) { + let mut stream = match signer::transport::connect_abstract_unix(&target) { Ok(s) => s, Err(e) => { eprintln!("bridge: cannot connect to {}: {}", target, e); @@ -349,14 +349,14 @@ fn bridge_main(to: Option<&str>, cli: &Cli) -> i32 { // Send source-qube preamble let preamble = format!(r#"{{"qrexec_source":"{}"}}"#, source_qube); - if nsigner::transport::send_framed(&mut stream, &preamble).is_err() { + if signer::transport::send_framed(&mut stream, &preamble).is_err() { eprintln!("bridge: failed to send preamble"); return 1; } // Read one framed request from stdin and forward let mut stdin = std::io::stdin(); - let request = match nsigner::transport::recv_framed(&mut stdin) { + let request = match signer::transport::recv_framed(&mut stdin) { Ok(r) => r, Err(e) => { eprintln!("bridge: failed to read request from stdin: {}", e); @@ -364,17 +364,17 @@ fn bridge_main(to: Option<&str>, cli: &Cli) -> i32 { } }; - if nsigner::transport::send_framed(&mut stream, &request).is_err() { + if signer::transport::send_framed(&mut stream, &request).is_err() { eprintln!("bridge: failed to forward request"); return 1; } // Relay response to stdout - match nsigner::transport::recv_framed(&mut stream) { + match signer::transport::recv_framed(&mut stream) { Ok(response) => { let mut stdout = std::io::stdout(); - if nsigner::transport::send_framed(&mut stdout, &response).is_err() { + if signer::transport::send_framed(&mut stdout, &response).is_err() { eprintln!("bridge: failed to relay response"); return 1; } @@ -387,9 +387,9 @@ fn bridge_main(to: Option<&str>, cli: &Cli) -> i32 { } } -/// List subcommand: list running nsigner sockets. +/// List subcommand: list running signer sockets. fn list_main() -> i32 { - let sockets = nsigner::socket_name::list_sockets(); + let sockets = signer::socket_name::list_sockets(); if sockets.is_empty() { println!("(none)"); } else { @@ -404,10 +404,10 @@ fn list_main() -> i32 { fn register_role_from_spec( role_table: &mut RoleTable, spec: &str, -) -> Result<(), NsignerError> { +) -> Result<(), SignerError> { let parts: Vec<&str> = spec.splitn(3, ':').collect(); if parts.len() != 3 { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } let name = parts[0]; @@ -415,13 +415,13 @@ fn register_role_from_spec( let path_token = parts[2]; if name.is_empty() || path_token.is_empty() { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } // Resolve curve let curve = if curve_str.is_empty() { // Auto-detect from path - match nsigner::role_table::purpose_from_path(path_token) { + match signer::role_table::purpose_from_path(path_token) { RolePurpose::Ssh => RoleCurve::Ed25519, RolePurpose::Age => RoleCurve::X25519, RolePurpose::PqSig => { @@ -439,15 +439,15 @@ fn register_role_from_spec( }; if curve == RoleCurve::Unknown { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } - let purpose = nsigner::role_table::purpose_from_path(path_token); + let purpose = signer::role_table::purpose_from_path(path_token); // Parse path template let (template, range_lo, range_hi, allowed_indices) = - nsigner::role_table::parse_path_template(path_token) - .map_err(|_| NsignerError::InvalidInput)?; + signer::role_table::parse_path_template(path_token) + .map_err(|_| SignerError::InvalidInput)?; role_table .register_role_path( @@ -460,7 +460,7 @@ fn register_role_from_spec( -1, // no default index &allowed_indices, ) - .map_err(|e| NsignerError::Internal(e.to_string()))?; + .map_err(|e| SignerError::Internal(e.to_string()))?; Ok(()) } diff --git a/src/miner.rs b/src/miner.rs index 8fcf3cd..5721425 100644 --- a/src/miner.rs +++ b/src/miner.rs @@ -44,14 +44,14 @@ pub fn miner_run( target_difficulty: i32, thread_count: i32, timeout_sec: u64, -) -> Result { +) -> Result { let threads = thread_count.clamp(1, 32) as usize; let timeout = if timeout_sec == 0 { 600 } else { timeout_sec }; let deadline = Instant::now() + Duration::from_secs(timeout); // Parse the unsigned event let mut event: Event = - serde_json::from_str(event_json).map_err(|_| crate::NsignerError::InvalidInput)?; + serde_json::from_str(event_json).map_err(|_| crate::SignerError::InvalidInput)?; // Ensure there's a nonce tag (will be updated by workers) let has_nonce = event.tags.iter().any(|t| t.kind() == "nonce"); @@ -96,9 +96,9 @@ pub fn miner_run( } let shared = Arc::try_unwrap(shared) - .map_err(|_| crate::NsignerError::Internal("mining thread still holds shared state".into()))? + .map_err(|_| crate::SignerError::Internal("mining thread still holds shared state".into()))? .into_inner() - .map_err(|_| crate::NsignerError::Internal("mining shared state poisoned".into()))?; + .map_err(|_| crate::SignerError::Internal("mining shared state poisoned".into()))?; let elapsed = start.elapsed().as_secs(); @@ -117,8 +117,8 @@ pub fn miner_run( &sk, event.created_at, ) - .map_err(|_| crate::NsignerError::CryptoFailed)?; - serde_json::to_string(&signed).map_err(|_| crate::NsignerError::InvalidInput)? + .map_err(|_| crate::SignerError::CryptoFailed)?; + serde_json::to_string(&signed).map_err(|_| crate::SignerError::InvalidInput)? } else { // No event mined — return the original unsigned event event_json.to_string() diff --git a/src/mnemonic.rs b/src/mnemonic.rs index 5b42322..2581ec3 100644 --- a/src/mnemonic.rs +++ b/src/mnemonic.rs @@ -4,7 +4,7 @@ //! generation, and seed conversion. use crate::secure_mem::SecureBuf; -use crate::NsignerError; +use crate::SignerError; /// Maximum mnemonic length: 24 words * ~10 chars + spaces + null. pub const MNEMONIC_MAX_LEN: usize = 256; @@ -32,18 +32,18 @@ impl MnemonicState { /// /// Validates word count (12/15/18/21/24) and BIP-39 checksum. /// Returns `InvalidInput` on invalid mnemonic, `MemoryFailed` on alloc error. - pub fn load(&mut self, phrase: &str) -> Result<(), NsignerError> { + pub fn load(&mut self, phrase: &str) -> Result<(), SignerError> { let words: Vec<&str> = phrase.split_whitespace().collect(); let count = words.len(); // Validate word count if ![12, 15, 18, 21, 24].contains(&count) { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } // Validate via nostr_core_lib_rust if !nips::nip006::mnemonic_validate(phrase) { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } // Store in secure memory @@ -62,14 +62,14 @@ impl MnemonicState { /// Generate a new BIP-39 mnemonic phrase. /// /// `word_count` must be 12, 15, 18, 21, or 24. - pub fn generate(&mut self, word_count: u8) -> Result { + pub fn generate(&mut self, word_count: u8) -> Result { let entropy_bytes = match word_count { 12 => 16, 15 => 20, 18 => 24, 21 => 28, 24 => 32, - _ => return Err(NsignerError::InvalidInput), + _ => return Err(SignerError::InvalidInput), }; let mut entropy = vec![0u8; entropy_bytes]; @@ -77,7 +77,7 @@ impl MnemonicState { rand::thread_rng().fill_bytes(&mut entropy); let phrase = nips::nip006::mnemonic_from_bytes(&entropy) - .map_err(|_| NsignerError::CryptoFailed)?; + .map_err(|_| SignerError::CryptoFailed)?; // Zeroize entropy use zeroize::Zeroize; diff --git a/src/otp_pad.rs b/src/otp_pad.rs index 022a34f..3e5cdd1 100644 --- a/src/otp_pad.rs +++ b/src/otp_pad.rs @@ -4,7 +4,7 @@ //! startup. Pad offset advances monotonically across requests. use crate::secure_mem::SecureBuf; -use crate::NsignerError; +use crate::SignerError; use std::fs::File; use std::io::{Read, Seek, SeekFrom}; @@ -63,14 +63,14 @@ impl OtpPadState { /// `dir` — directory containing .pad and .state files /// `spec` — pad checksum (64 hex) or unique prefix /// `allow_blkback` — allow pads on qvm-block devices (not for production) - pub fn bind(&mut self, dir: &str, spec: &str, _allow_blkback: bool) -> Result<(), NsignerError> { + pub fn bind(&mut self, dir: &str, spec: &str, _allow_blkback: bool) -> Result<(), SignerError> { // Find the pad file matching the spec let pad_filename = if spec.len() == 64 { format!("{}/{}.pad", dir, spec) } else { // Prefix match — find a .pad file starting with spec let entries = std::fs::read_dir(dir) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let mut found = None; for entry in entries { @@ -85,15 +85,15 @@ impl OtpPadState { } found .map(|p| p.to_string_lossy().to_string()) - .ok_or(NsignerError::InvalidInput)? + .ok_or(SignerError::InvalidInput)? }; let file = File::open(&pad_filename) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let metadata = file .metadata() - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let size = metadata.len(); // Extract checksum from filename @@ -113,7 +113,7 @@ impl OtpPadState { // Allocate scratch buffer for XOR let scratch = SecureBuf::alloc(4 * 1024 * 1024) // 4 MB max chunk - .map_err(|_| NsignerError::MemoryFailed)?; + .map_err(|_| SignerError::MemoryFailed)?; self.bound = true; self.pads_dir = dir.to_string(); @@ -144,9 +144,9 @@ impl OtpPadState { &mut self, plaintext: &[u8], _encoding: Option<&str>, - ) -> Result<(Vec, u64, u64), NsignerError> { + ) -> Result<(Vec, u64, u64), SignerError> { if !self.bound { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } let off_before = self.offset; @@ -164,32 +164,32 @@ impl OtpPadState { &mut self, ciphertext: &[u8], _encoding: Option<&str>, - ) -> Result, NsignerError> { + ) -> Result, SignerError> { if !self.bound { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } self.xor_with_pad(ciphertext) } /// XOR data with pad bytes at the current offset, advancing the offset. - fn xor_with_pad(&mut self, data: &[u8]) -> Result, NsignerError> { - let file = self.pad_file.as_mut().ok_or(NsignerError::InvalidInput)?; - let scratch = self.scratch.as_mut().ok_or(NsignerError::InvalidInput)?; + fn xor_with_pad(&mut self, data: &[u8]) -> Result, SignerError> { + let file = self.pad_file.as_mut().ok_or(SignerError::InvalidInput)?; + let scratch = self.scratch.as_mut().ok_or(SignerError::InvalidInput)?; let data_len = data.len(); if data_len > scratch.size() { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } // Seek to current offset file.seek(SeekFrom::Start(self.offset)) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; // Read pad bytes let pad_slice = &mut scratch.as_mut_slice()[..data_len]; file.read_exact(pad_slice) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; // XOR let result: Vec = data @@ -231,11 +231,11 @@ static GLOBAL_OTP_PAD: Mutex> = Mutex::new(None); /// Bind the global OTP pad. Called from the role wizard (OTP preset) or from /// `--otp-pad-dir` CLI handling. Replaces any previously bound pad. -pub fn bind_global(dir: &str, spec: &str, allow_blkback: bool) -> Result<(), NsignerError> { +pub fn bind_global(dir: &str, spec: &str, allow_blkback: bool) -> Result<(), SignerError> { let mut pad = OtpPadState::new(); pad.bind(dir, spec, allow_blkback)?; let mut guard = GLOBAL_OTP_PAD.lock().map_err(|e| { - NsignerError::Internal(format!("global otp pad lock poisoned: {}", e)) + SignerError::Internal(format!("global otp pad lock poisoned: {}", e)) })?; *guard = Some(pad); Ok(()) @@ -287,7 +287,7 @@ mod tests { #[test] fn test_bind_and_encrypt() { - let dir = std::env::temp_dir().join("nsigner_otp_test_1"); + let dir = std::env::temp_dir().join("signer_otp_test_1"); let _ = std::fs::remove_dir_all(&dir); let chksum = make_test_pad(&dir, 1024); @@ -313,7 +313,7 @@ mod tests { #[test] fn test_encrypt_decrypt_roundtrip() { - let dir = std::env::temp_dir().join("nsigner_otp_test_2"); + let dir = std::env::temp_dir().join("signer_otp_test_2"); let _ = std::fs::remove_dir_all(&dir); let chksum = make_test_pad(&dir, 1024); @@ -346,7 +346,7 @@ mod tests { #[test] fn test_unbind() { - let dir = std::env::temp_dir().join("nsigner_otp_test_3"); + let dir = std::env::temp_dir().join("signer_otp_test_3"); let _ = std::fs::remove_dir_all(&dir); let chksum = make_test_pad(&dir, 1024); @@ -363,7 +363,7 @@ mod tests { #[test] fn test_offset_advances() { - let dir = std::env::temp_dir().join("nsigner_otp_test_4"); + let dir = std::env::temp_dir().join("signer_otp_test_4"); let _ = std::fs::remove_dir_all(&dir); let chksum = make_test_pad(&dir, 1024); diff --git a/src/pq_crypto.rs b/src/pq_crypto.rs index 70bfc64..d0d8ccd 100644 --- a/src/pq_crypto.rs +++ b/src/pq_crypto.rs @@ -90,12 +90,12 @@ impl CryptoAlg { pub fn derive_seed_from_mnemonic( mnemonic: &str, path: &str, -) -> Result<[u8; 32], crate::NsignerError> { +) -> Result<[u8; 32], crate::SignerError> { let seed = nips::nip006::mnemonic_to_seed(mnemonic, ""); // Parse the path let path_indices = nips::nip006::parse_bip44_path(path) - .map_err(|_| crate::NsignerError::KeyDerivationFailed)?; + .map_err(|_| crate::SignerError::KeyDerivationFailed)?; // Determine if this is a secp256k1 path (BIP-32) or ed25519/x25519 path (SLIP-0010) // by checking the purpose prefix. @@ -103,13 +103,13 @@ pub fn derive_seed_from_mnemonic( // BIP-32 derivation for secp256k1 let (master_key, master_chain_code) = nips::nip006::bip32_master_key(&seed); let (derived_key, _) = nips::nip006::bip32_derive_path(&master_key, &master_chain_code, &path_indices) - .map_err(|_| crate::NsignerError::KeyDerivationFailed)?; + .map_err(|_| crate::SignerError::KeyDerivationFailed)?; Ok(derived_key) } else { // SLIP-0010 derivation for ed25519/x25519/PQ let (master_key, master_chain_code) = nips::nip006::slip10_master_key(&seed); let (derived_key, _) = nips::nip006::slip10_derive_path(&master_key, &master_chain_code, &path_indices) - .map_err(|_| crate::NsignerError::KeyDerivationFailed)?; + .map_err(|_| crate::SignerError::KeyDerivationFailed)?; Ok(derived_key) } } @@ -162,12 +162,12 @@ pub fn x25519_ecdh(our_priv: &[u8; 32], peer_pub: &[u8; 32]) -> [u8; 32] { /// secp256k1 ECDSA sign arbitrary bytes. /// Hashes the message with SHA-256 before signing. /// Returns 64-byte compact signature (r || s). -pub fn secp256k1_ecdsa_sign(priv_key: &[u8; 32], msg: &[u8]) -> Result<[u8; 64], crate::NsignerError> { +pub fn secp256k1_ecdsa_sign(priv_key: &[u8; 32], msg: &[u8]) -> Result<[u8; 64], crate::SignerError> { use secp256k1::{Message, Secp256k1, SecretKey}; let secp = Secp256k1::new(); - let sk = SecretKey::from_slice(priv_key).map_err(|_| crate::NsignerError::CryptoFailed)?; + let sk = SecretKey::from_slice(priv_key).map_err(|_| crate::SignerError::CryptoFailed)?; let hash = sha256(msg); - let msg = Message::from_digest_slice(&hash).map_err(|_| crate::NsignerError::CryptoFailed)?; + let msg = Message::from_digest_slice(&hash).map_err(|_| crate::SignerError::CryptoFailed)?; let sig = secp.sign_ecdsa(&msg, &sk); Ok(sig.serialize_compact()) } @@ -211,14 +211,14 @@ pub fn secp256k1_ecdsa_verify(pub_key: &[u8; 32], msg: &[u8], sig: &[u8; 64]) -> /// ML-DSA-65: generate keypair from a 32-byte seed (deterministic). /// TODO: Wire up ml-dsa crate API. -pub fn ml_dsa_65_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec, Vec), crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn ml_dsa_65_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec, Vec), crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } /// ML-DSA-65: sign a message. /// TODO: Wire up ml-dsa crate API. -pub fn ml_dsa_65_sign(_priv: &[u8], _msg: &[u8]) -> Result, crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn ml_dsa_65_sign(_priv: &[u8], _msg: &[u8]) -> Result, crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } /// ML-DSA-65: verify a signature. @@ -229,14 +229,14 @@ pub fn ml_dsa_65_verify(_pub: &[u8], _msg: &[u8], _sig: &[u8]) -> bool { /// SLH-DSA-128s: generate keypair from a 32-byte seed (deterministic). /// TODO: Wire up slh-dsa crate API. -pub fn slh_dsa_128s_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec, Vec), crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn slh_dsa_128s_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec, Vec), crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } /// SLH-DSA-128s: sign a message. /// TODO: Wire up slh-dsa crate API. -pub fn slh_dsa_128s_sign(_priv: &[u8], _msg: &[u8]) -> Result, crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn slh_dsa_128s_sign(_priv: &[u8], _msg: &[u8]) -> Result, crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } /// SLH-DSA-128s: verify a signature. @@ -247,22 +247,22 @@ pub fn slh_dsa_128s_verify(_pub: &[u8], _msg: &[u8], _sig: &[u8]) -> bool { /// ML-KEM-768: generate keypair from a 32-byte seed (deterministic). /// TODO: Wire up ml-kem crate API. -pub fn ml_kem_768_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec, Vec), crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn ml_kem_768_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec, Vec), crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } /// ML-KEM-768: encapsulate. pub is 1184-byte public key. /// Returns (ciphertext[1088], shared_secret[32]). /// TODO: Wire up ml-kem crate API. -pub fn ml_kem_768_encaps(_pub: &[u8]) -> Result<(Vec, [u8; 32]), crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn ml_kem_768_encaps(_pub: &[u8]) -> Result<(Vec, [u8; 32]), crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } /// ML-KEM-768: decapsulate. priv is 2400-byte secret key, ct is 1088-byte ciphertext. /// Returns shared_secret[32]. /// TODO: Wire up ml-kem crate API. -pub fn ml_kem_768_decaps(_priv: &[u8], _ct: &[u8]) -> Result<[u8; 32], crate::NsignerError> { - Err(crate::NsignerError::NotYetImplemented) +pub fn ml_kem_768_decaps(_priv: &[u8], _ct: &[u8]) -> Result<[u8; 32], crate::SignerError> { + Err(crate::SignerError::NotYetImplemented) } // ── Helpers ───────────────────────────────────────────────────────────────── diff --git a/src/role_table.rs b/src/role_table.rs index 92bf756..9d988d3 100644 --- a/src/role_table.rs +++ b/src/role_table.rs @@ -4,7 +4,7 @@ //! (acting as an access token) to a BIP-44 derivation path template. //! The template may contain a `%d` placeholder for a variable index. -use crate::NsignerError; +use crate::SignerError; use std::collections::HashSet; // ── Limits ─────────────────────────────────────────────────────────────────── @@ -264,12 +264,12 @@ impl RoleTable { } /// Add a role entry. Returns error if table full or name duplicate. - pub fn add(&mut self, entry: RoleEntry) -> Result<(), NsignerError> { + pub fn add(&mut self, entry: RoleEntry) -> Result<(), SignerError> { if self.entries.len() >= ROLE_TABLE_MAX_ENTRIES { - return Err(NsignerError::Internal("role table full".into())); + return Err(SignerError::Internal("role table full".into())); } if self.find_by_name(&entry.name).is_some() { - return Err(NsignerError::Internal("duplicate role name".into())); + return Err(SignerError::Internal("duplicate role name".into())); } self.entries.push(entry); Ok(()) @@ -303,7 +303,7 @@ impl RoleTable { } /// Register a nostr-index role if missing. - pub fn register_nostr_index(&mut self, nostr_index: i32) -> Result<(), NsignerError> { + pub fn register_nostr_index(&mut self, nostr_index: i32) -> Result<(), SignerError> { if self.find_by_nostr_index(nostr_index).is_some() { return Ok(()); } @@ -336,7 +336,7 @@ impl RoleTable { range_hi: i32, default_index: i32, allowed_indices: &[i32], - ) -> Result<(), NsignerError> { + ) -> Result<(), SignerError> { let mut entry = RoleEntry::default(); entry.name = name.to_string(); entry.purpose = purpose; @@ -431,10 +431,10 @@ pub fn role_path_extract_index(concrete: &str, template: &str) -> i32 { #[allow(clippy::too_many_arguments)] pub fn parse_path_template( token: &str, -) -> Result<(String, i32, i32, Vec), NsignerError> { +) -> Result<(String, i32, i32, Vec), SignerError> { let segs: Vec<&str> = token.split('/').collect(); if segs.is_empty() { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } let mut template_out = String::new(); @@ -490,18 +490,18 @@ pub fn parse_path_template( let mut set = HashSet::new(); for tok in seg_clean.split('+') { if let Some(dash) = tok.find('-') { - let lo: i32 = tok[..dash].parse().map_err(|_| NsignerError::InvalidInput)?; - let hi: i32 = tok[dash + 1..].parse().map_err(|_| NsignerError::InvalidInput)?; + let lo: i32 = tok[..dash].parse().map_err(|_| SignerError::InvalidInput)?; + let hi: i32 = tok[dash + 1..].parse().map_err(|_| SignerError::InvalidInput)?; if lo < 0 || hi < 0 || lo > hi { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } for v in lo..=hi { set.insert(v); } } else { - let val: i32 = tok.parse().map_err(|_| NsignerError::InvalidInput)?; + let val: i32 = tok.parse().map_err(|_| SignerError::InvalidInput)?; if val < 0 { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } set.insert(val); } @@ -527,10 +527,10 @@ pub fn parse_path_template( let dash_pos = seg_clean.find('-').unwrap(); let lo: i32 = seg_clean[..dash_pos] .parse() - .map_err(|_| NsignerError::InvalidInput)?; + .map_err(|_| SignerError::InvalidInput)?; let hi: i32 = seg_clean[dash_pos + 1..] .parse() - .map_err(|_| NsignerError::InvalidInput)?; + .map_err(|_| SignerError::InvalidInput)?; if lo < 0 || hi < 0 || lo > hi { // Not a valid numeric range — treat as literal template_out.push_str(seg); diff --git a/src/secure_mem.rs b/src/secure_mem.rs index 6ff67b6..4d4918b 100644 --- a/src/secure_mem.rs +++ b/src/secure_mem.rs @@ -43,17 +43,17 @@ impl SecureBuf { /// /// Returns `MemoryFailed` if allocation or mlock fails (unless /// `allow_unlocked()` was called). - pub fn alloc(size: usize) -> Result { + pub fn alloc(size: usize) -> Result { if size == 0 { - return Err(crate::NsignerError::InvalidInput); + return Err(crate::SignerError::InvalidInput); } let layout = Layout::from_size_align(size, 1) - .map_err(|_| crate::NsignerError::MemoryFailed)?; + .map_err(|_| crate::SignerError::MemoryFailed)?; let ptr = unsafe { alloc(layout) }; if ptr.is_null() { - return Err(crate::NsignerError::MemoryFailed); + return Err(crate::SignerError::MemoryFailed); } // Zero-initialize @@ -64,7 +64,7 @@ impl SecureBuf { if !locked && !is_unlocked_allowed() { // mlock failed and unlocked mode not permitted — fail hard unsafe { dealloc(ptr, layout) }; - return Err(crate::NsignerError::MemoryFailed); + return Err(crate::SignerError::MemoryFailed); } Ok(SecureBuf { ptr, size, locked }) @@ -111,9 +111,9 @@ impl SecureBuf { /// If `new_size` is 0, returns `InvalidInput`. If allocation of the /// new buffer fails, the original buffer is left intact and an error /// is returned. - pub fn resize(&mut self, new_size: usize) -> Result<(), crate::NsignerError> { + pub fn resize(&mut self, new_size: usize) -> Result<(), crate::SignerError> { if new_size == 0 { - return Err(crate::NsignerError::InvalidInput); + return Err(crate::SignerError::InvalidInput); } if new_size == self.size { return Ok(()); diff --git a/src/server.rs b/src/server.rs index 548f103..02479b3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,7 +10,7 @@ use crate::auth_envelope::AuthNonceCache; use crate::dispatcher::DispatcherContext; use crate::selector::{selector_resolve, SelectorRequest}; -use crate::NsignerError; +use crate::SignerError; use std::net::TcpListener; use std::os::unix::net::UnixListener; @@ -93,14 +93,14 @@ impl ServerContext { } /// Start listening. Returns error on bind failure. - pub fn start(&mut self) -> Result<(), NsignerError> { + pub fn start(&mut self) -> Result<(), SignerError> { match self.listen_mode { ListenMode::Unix => { // Abstract namespace: bind via libc (sun_path[0] = '\0') let listener = bind_abstract_unix(&self.socket_name)?; listener .set_nonblocking(true) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; self.listener = Some(listener); self.running = true; Ok(()) @@ -113,10 +113,10 @@ impl ServerContext { .or_else(|| self.socket_name.strip_prefix("http:")) .unwrap_or(&self.socket_name); let listener = TcpListener::bind(addr) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; listener .set_nonblocking(true) - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; self.tcp_listener = Some(listener); self.running = true; Ok(()) @@ -142,7 +142,7 @@ impl ServerContext { pub fn handle_one( &mut self, dispatcher: &mut DispatcherContext, - ) -> Result, NsignerError> { + ) -> Result, SignerError> { if let Some(ref listener) = self.listener { match listener.accept() { Ok((stream, _)) => { @@ -153,7 +153,7 @@ impl ServerContext { let mut reader = stream .try_clone() - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let mut writer = stream; // Read framed request. A connection with no data yet @@ -182,7 +182,7 @@ impl ServerContext { Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { return Ok(None); // Nothing pending } - Err(e) => return Err(NsignerError::IoFailed(e.to_string())), + Err(e) => return Err(SignerError::IoFailed(e.to_string())), } } @@ -198,7 +198,7 @@ impl ServerContext { let mut reader = stream .try_clone() - .map_err(|e| NsignerError::IoFailed(e.to_string()))?; + .map_err(|e| SignerError::IoFailed(e.to_string()))?; let mut writer = stream; let request = if self.listen_mode == ListenMode::Http { @@ -232,7 +232,7 @@ impl ServerContext { Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { return Ok(None); } - Err(e) => return Err(NsignerError::IoFailed(e.to_string())), + Err(e) => return Err(SignerError::IoFailed(e.to_string())), } } @@ -358,16 +358,16 @@ impl ServerContext { /// /// Rust's safe `UnixListener::bind` rejects paths containing null bytes, /// so abstract sockets (sun_path[0] = '\0') must be bound via libc. -fn bind_abstract_unix(name: &str) -> Result { +fn bind_abstract_unix(name: &str) -> Result { use std::os::unix::io::FromRawFd; if name.len() >= 107 { - return Err(NsignerError::InvalidInput); + return Err(SignerError::InvalidInput); } let fd = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_STREAM, 0) }; if fd < 0 { - return Err(NsignerError::IoFailed("socket() failed".into())); + return Err(SignerError::IoFailed("socket() failed".into())); } // Build sockaddr_un with abstract namespace (sun_path[0] = '\0') @@ -391,14 +391,14 @@ fn bind_abstract_unix(name: &str) -> Result { if rc != 0 { let err = std::io::Error::last_os_error(); unsafe { libc::close(fd) }; - return Err(NsignerError::IoFailed(format!("bind: {}", err))); + return Err(SignerError::IoFailed(format!("bind: {}", err))); } let rc = unsafe { libc::listen(fd, 16) }; if rc != 0 { let err = std::io::Error::last_os_error(); unsafe { libc::close(fd) }; - return Err(NsignerError::IoFailed(format!("listen: {}", err))); + return Err(SignerError::IoFailed(format!("listen: {}", err))); } // Wrap the raw fd in a UnixListener diff --git a/src/socket_name.rs b/src/socket_name.rs index 1a87b08..a73b758 100644 --- a/src/socket_name.rs +++ b/src/socket_name.rs @@ -1,23 +1,23 @@ //! Socket naming — sequential abstract socket name generation. //! -//! Generates names in the format `nsigner01`, `nsigner02`, … incrementing +//! Generates names in the format `signer01`, `signer02`, … incrementing //! until an unused name is found (by checking /proc/net/unix). //! -//! The `nsigner` prefix is required for compatibility with the C -//! `nsigner_client` / `nsigner_transport_list_unix`, which scans -//! /proc/net/unix for the literal prefix `@nsigner`. +//! The `signer` prefix is required for compatibility with the C +//! `signer_client` / `signer_transport_list_unix`, which scans +//! /proc/net/unix for the literal prefix `@signer`. /// Prefix for generated socket names. -pub const SOCKET_NAME_PREFIX: &str = "nsigner"; +pub const SOCKET_NAME_PREFIX: &str = "signer"; -/// Generate a socket name: `nsigner01`, `nsigner02`, … +/// Generate a socket name: `signer01`, `signer02`, … /// -/// Scans /proc/net/unix for already-running nsigner sockets and picks the +/// Scans /proc/net/unix for already-running signer sockets and picks the /// lowest unused number (starting at 1, zero-padded to 2 digits). -pub fn socket_name_random() -> Result { +pub fn socket_name_random() -> Result { let in_use = list_sockets(); - // Try nsigner01, nsigner02, … up to nsigner99 + // Try signer01, signer02, … up to signer99 for n in 1..=99u32 { let candidate = format!("{}{:02}", SOCKET_NAME_PREFIX, n); if !in_use.contains(&candidate) { @@ -25,7 +25,7 @@ pub fn socket_name_random() -> Result { } } - // Fallback: nsigner100, nsigner101, … (no zero-padding beyond 99) + // Fallback: signer100, signer101, … (no zero-padding beyond 99) for n in 100..=9999u32 { let candidate = format!("{}{}", SOCKET_NAME_PREFIX, n); if !in_use.contains(&candidate) { @@ -33,22 +33,22 @@ pub fn socket_name_random() -> Result { } } - Err(crate::NsignerError::Internal( - "no available socket name (nsigner01..nsigner9999 all in use)".into(), + Err(crate::SignerError::Internal( + "no available socket name (signer01..signer9999 all in use)".into(), )) } -/// List running nsigner abstract sockets by reading /proc/net/unix. +/// List running signer abstract sockets by reading /proc/net/unix. /// -/// Matches the C `nsigner_transport_list_unix` scan: looks for the literal -/// prefix `@nsigner` in the path column. +/// Matches the C `signer_transport_list_unix` scan: looks for the literal +/// prefix `@signer` in the path column. pub fn list_sockets() -> Vec { let mut found = Vec::new(); if let Ok(content) = std::fs::read_to_string("/proc/net/unix") { for line in content.lines() { - // Look for @nsigner prefix in the path column (matches C client scan) - if let Some(pos) = line.find("@nsigner") { + // Look for @signer prefix in the path column (matches C client scan) + if let Some(pos) = line.find("@signer") { let rest = &line[pos + 1..]; // skip @ // Extract the name (up to whitespace or end of line) let name: String = rest @@ -65,14 +65,14 @@ pub fn list_sockets() -> Vec { found } -/// Discover a single running nsigner socket. +/// Discover a single running signer socket. /// Returns Ok(name) if exactly one is found, Err if zero or multiple. -pub fn discover_single_socket() -> Result { +pub fn discover_single_socket() -> Result { let sockets = list_sockets(); if sockets.len() == 1 { Ok(sockets[0].clone()) } else { - Err(crate::NsignerError::NotFound) + Err(crate::SignerError::NotFound) } } @@ -83,8 +83,8 @@ mod tests { #[test] fn test_socket_name_random() { let name = socket_name_random().unwrap(); - assert!(name.starts_with("nsigner")); - // Should be nsigner01..nsigner99 (8 chars) or nsigner100+ (9+ chars) + assert!(name.starts_with("signer")); + // Should be signer01..signer99 (8 chars) or signer100+ (9+ chars) assert!(name.len() >= 8); } } diff --git a/src/tui.rs b/src/tui.rs index dc0c3bd..2b5e389 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -10,7 +10,7 @@ use crate::key_store::KeyStore; use crate::mnemonic::MnemonicState; use crate::role_table::{RoleCurve, RolePurpose, RoleTable}; use crate::server::{AuthMode, ListenMode, ServerContext}; -use crate::NsignerError; +use crate::SignerError; use ratatui::layout::{Constraint, Layout, Rect, Spacing}; use ratatui::style::{Modifier, Style}; @@ -36,7 +36,7 @@ pub const TRANSPORT_HTTP: u8 = 0x08; /// Qrexec service name used for the bridge transport. /// Matches NSIGNER_QREXEC_SERVICE_NAME in n_signer/src/main.c. -pub const QREXEC_SERVICE_NAME: &str = "qubes.NsignerRpc"; +pub const QREXEC_SERVICE_NAME: &str = "qubes.SignerRpc"; /// Check if a word is in the BIP-39 English wordlist. fn is_valid_bip39_word(word: &str) -> bool { @@ -535,7 +535,7 @@ impl App { } /// Start the server for the currently selected transport. - fn start_server(&mut self) -> Result<(), NsignerError> { + fn start_server(&mut self) -> Result<(), SignerError> { // Pick the first active transport by priority (Unix > Qrexec > TCP > HTTP). let (listen_mode, server_name) = if self.transport_toggles[0] { (ListenMode::Unix, self.socket_name.clone()) @@ -652,7 +652,7 @@ impl App { self.transport_mask_apply(); match self.start_server() { Ok(()) => { - self.activity_log.add("nsigner started"); + self.activity_log.add("signer started"); } Err(e) => { self.activity_log.add(&format!("server start failed: {}", e)); @@ -781,7 +781,7 @@ impl App { } match self.start_server() { Ok(()) => { - self.activity_log.add("nsigner started"); + self.activity_log.add("signer started"); } Err(e) => { self.activity_log.add(&format!("server start failed: {}", e)); @@ -1484,7 +1484,7 @@ impl App { // client command that callers use to reach the signer, // matching the connection info in n_signer's main.c. lines.push(Line::from(format!(" qrexec service: {}", QREXEC_SERVICE_NAME))); - lines.push(Line::from(format!(" nsigner_client --qrexec :{}", QREXEC_SERVICE_NAME))); + lines.push(Line::from(format!(" signer_client --qrexec :{}", QREXEC_SERVICE_NAME))); } else { lines.push(Line::from(" (inactive)")); } diff --git a/tests/integration.rs b/tests/integration.rs index 47375dc..c660a10 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -4,7 +4,7 @@ //! caller sends a request with a role name → selector resolution → dispatch. //! No policy table, no approval prompt. Knowing a valid role name is sufficient. -use nsigner::{ +use signer::{ alg_cache::AlgorithmKeyCache, dispatcher::DispatcherContext, key_store::KeyStore, @@ -85,15 +85,15 @@ fn spawn_server_loop( /// Send a framed request to a Unix socket and return the response. fn send_request(socket_name: &str, request: &str) -> String { - let mut stream = nsigner::transport::connect_abstract_unix(socket_name).unwrap(); - nsigner::transport::send_framed(&mut stream, request).unwrap(); - nsigner::transport::recv_framed(&mut stream).unwrap() + let mut stream = signer::transport::connect_abstract_unix(socket_name).unwrap(); + signer::transport::send_framed(&mut stream, request).unwrap(); + signer::transport::recv_framed(&mut stream).unwrap() } /// Wait for the server socket to be ready. fn wait_for_server(socket_name: &str, attempts: u32) { for _ in 0..attempts { - if nsigner::transport::connect_abstract_unix(socket_name).is_ok() { + if signer::transport::connect_abstract_unix(socket_name).is_ok() { return; } std::thread::sleep(Duration::from_millis(20)); @@ -103,7 +103,7 @@ fn wait_for_server(socket_name: &str, attempts: u32) { #[test] fn test_get_info_works() { - let socket_name = format!("nsigner_test_info_{}", std::process::id()); + let socket_name = format!("signer_test_info_{}", std::process::id()); let (server, role_table, mnemonic, key_store, alg_cache) = setup_server(&socket_name); let (handle, stop) = spawn_server_loop(server, role_table, mnemonic, key_store, alg_cache); @@ -113,14 +113,14 @@ fn test_get_info_works() { let resp = send_request(&socket_name, r#"{"id":"1","method":"get_info","params":[]}"#); assert!(resp.contains("\"result\""), "get_info failed: {}", resp); - let _ = nsigner::transport::connect_abstract_unix(&socket_name); + let _ = signer::transport::connect_abstract_unix(&socket_name); stop.store(true, std::sync::atomic::Ordering::SeqCst); handle.join().ok(); } #[test] fn test_role_as_password_allows_with_valid_role() { - let socket_name = format!("nsigner_test_allow_{}", std::process::id()); + let socket_name = format!("signer_test_allow_{}", std::process::id()); let (server, role_table, mnemonic, key_store, alg_cache) = setup_server(&socket_name); let (handle, stop) = spawn_server_loop(server, role_table, mnemonic, key_store, alg_cache); @@ -136,14 +136,14 @@ fn test_role_as_password_allows_with_valid_role() { assert!(resp.contains("e8bcf3823669444d0b49ad45d65088635d9fd8500a75b5f20b59abefa56a144f"), "expected pubkey in result, got: {}", resp); - let _ = nsigner::transport::connect_abstract_unix(&socket_name); + let _ = signer::transport::connect_abstract_unix(&socket_name); stop.store(true, std::sync::atomic::Ordering::SeqCst); handle.join().ok(); } #[test] fn test_unknown_role_returns_selector_error() { - let socket_name = format!("nsigner_test_unknown_{}", std::process::id()); + let socket_name = format!("signer_test_unknown_{}", std::process::id()); let (server, role_table, mnemonic, key_store, alg_cache) = setup_server(&socket_name); let (handle, stop) = spawn_server_loop(server, role_table, mnemonic, key_store, alg_cache); @@ -156,14 +156,14 @@ fn test_unknown_role_returns_selector_error() { ); assert!(resp.contains("unknown_role"), "expected unknown_role, got: {}", resp); - let _ = nsigner::transport::connect_abstract_unix(&socket_name); + let _ = signer::transport::connect_abstract_unix(&socket_name); stop.store(true, std::sync::atomic::Ordering::SeqCst); handle.join().ok(); } #[test] fn test_ed25519_sign_allowed_no_authorization() { - let socket_name = format!("nsigner_test_alg_{}", std::process::id()); + let socket_name = format!("signer_test_alg_{}", std::process::id()); let (server, role_table, mnemonic, key_store, alg_cache) = setup_server(&socket_name); let (handle, stop) = spawn_server_loop(server, role_table, mnemonic, key_store, alg_cache); @@ -179,14 +179,14 @@ fn test_ed25519_sign_allowed_no_authorization() { assert!(resp.contains("\"result\""), "expected success, got: {}", resp); assert!(resp.contains("signature"), "expected signature in result: {}", resp); - let _ = nsigner::transport::connect_abstract_unix(&socket_name); + let _ = signer::transport::connect_abstract_unix(&socket_name); stop.store(true, std::sync::atomic::Ordering::SeqCst); handle.join().ok(); } #[test] fn test_repeated_requests_all_allowed() { - let socket_name = format!("nsigner_test_repeat_{}", std::process::id()); + let socket_name = format!("signer_test_repeat_{}", std::process::id()); let (server, role_table, mnemonic, key_store, alg_cache) = setup_server(&socket_name); let (handle, stop) = spawn_server_loop(server, role_table, mnemonic, key_store, alg_cache); @@ -202,7 +202,7 @@ fn test_repeated_requests_all_allowed() { assert!(resp.contains("\"result\""), "request {} failed: {}", i, resp); } - let _ = nsigner::transport::connect_abstract_unix(&socket_name); + let _ = signer::transport::connect_abstract_unix(&socket_name); stop.store(true, std::sync::atomic::Ordering::SeqCst); handle.join().ok(); }