v0.0.13 - Renamed nsigner to signer throughout project; release build with signer + signer-client binaries
This commit is contained in:
Generated
+31
-31
@@ -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"
|
||||
|
||||
+4
-4
@@ -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]
|
||||
|
||||
+24
-6
@@ -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"
|
||||
|
||||
@@ -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<ver> > Unlock`, title `"Enter mnemonic phrase"`
|
||||
- Frame: `signer v<ver> > 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) ⚠️
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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_<word1>_<word2>` format preserved
|
||||
6. **Abstract socket names**: `@signer_<word1>_<word2>` format preserved
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+21
-21
@@ -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 <qube> <service>` 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<br/>NsignerClient]
|
||||
Rpc --> Signer[signer.rs<br/>NsignerSigner typed verbs]
|
||||
Signer -->|build params| Disp[nsigner daemon<br/>dispatcher.rs]
|
||||
Tr --> Rpc[rpc.rs<br/>SignerClient]
|
||||
Rpc --> Signer[signer.rs<br/>SignerSigner typed verbs]
|
||||
Signer -->|build params| Disp[signer daemon<br/>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<Value, String>`, `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).
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+12
-12
@@ -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<AlgKeyEntry, NsignerError> {
|
||||
) -> Result<AlgKeyEntry, SignerError> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> = None;
|
||||
let mut tag_method: Option<String> = None;
|
||||
let mut tag_body_hash: Option<String> = 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> [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 <name>` or auto-discover | Default. Auto-discovers if exactly one `nsigner*` socket exists. |
|
||||
| UNIX abstract socket | `--socket-name <name>` or auto-discover | Default. Auto-discovers if exactly one `signer*` socket exists. |
|
||||
| TCP | `--tcp <host:port>` | Requires `--auth-privkey` for auth envelope. |
|
||||
| Serial (USB CDC-ACM) | `--serial <device>` | e.g. `--serial /dev/ttyACM0` |
|
||||
| Qubes qrexec | `--qrexec <qube:service>` | e.g. `--qrexec sys-signer:qubes.NsignerRpc` |
|
||||
| Qubes qrexec | `--qrexec <qube:service>` | 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
|
||||
|
||||
+7
-7
@@ -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", <request id>]`
|
||||
//! - `["nsigner_method", <method>]`
|
||||
//! - `["nsigner_body_hash", <hex sha256 of compact params JSON>]`
|
||||
//! - `["signer_rpc", <request id>]`
|
||||
//! - `["signer_method", <method>]`
|
||||
//! - `["signer_body_hash", <hex sha256 of compact params JSON>]`
|
||||
//! - `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()
|
||||
|
||||
+4
-4
@@ -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 <N> 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\
|
||||
|
||||
+6
-6
@@ -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 {
|
||||
|
||||
+7
-7
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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<String>,
|
||||
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,
|
||||
|
||||
+14
-14
@@ -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<Self> {
|
||||
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<String> {
|
||||
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));
|
||||
|
||||
+3
-3
@@ -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<Vec<u8>, NsignerError> {
|
||||
fn sign_with_alg(alg: CryptoAlg, priv_key: &[u8; 32], msg: &[u8]) -> Result<Vec<u8>, 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<Vec<
|
||||
}
|
||||
CryptoAlg::MlDsa65 => 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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -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<nostr_core::error::NostrError> for NsignerError {
|
||||
impl From<nostr_core::error::NostrError> 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" };
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
+41
-41
@@ -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<usize, NsignerError> {
|
||||
) -> Result<usize, 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)?;
|
||||
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<String, NsignerError> {
|
||||
) -> Result<String, SignerError> {
|
||||
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<Vec<u8>, NsignerError> {
|
||||
) -> Result<Vec<u8>, 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<Vec<u8>, NsignerError> {
|
||||
) -> Result<Vec<u8>, 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<String, NsignerError> {
|
||||
) -> Result<String, 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);
|
||||
|
||||
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<String, NsignerError> {
|
||||
) -> Result<String, 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);
|
||||
|
||||
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<DerivedKey, NsignerError> {
|
||||
) -> Result<DerivedKey, SignerError> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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";
|
||||
|
||||
+40
-40
@@ -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<String>,
|
||||
},
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
||||
+6
-6
@@ -44,14 +44,14 @@ pub fn miner_run(
|
||||
target_difficulty: i32,
|
||||
thread_count: i32,
|
||||
timeout_sec: u64,
|
||||
) -> Result<MineResult, crate::NsignerError> {
|
||||
) -> Result<MineResult, crate::SignerError> {
|
||||
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()
|
||||
|
||||
+7
-7
@@ -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<String, NsignerError> {
|
||||
pub fn generate(&mut self, word_count: u8) -> Result<String, SignerError> {
|
||||
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;
|
||||
|
||||
+23
-23
@@ -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<u8>, u64, u64), NsignerError> {
|
||||
) -> Result<(Vec<u8>, 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<Vec<u8>, NsignerError> {
|
||||
) -> Result<Vec<u8>, 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<Vec<u8>, 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<Vec<u8>, 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<u8> = data
|
||||
@@ -231,11 +231,11 @@ static GLOBAL_OTP_PAD: Mutex<Option<OtpPadState>> = 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);
|
||||
|
||||
|
||||
+21
-21
@@ -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<u8>, Vec<u8>), crate::NsignerError> {
|
||||
Err(crate::NsignerError::NotYetImplemented)
|
||||
pub fn ml_dsa_65_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec<u8>, Vec<u8>), 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<Vec<u8>, crate::NsignerError> {
|
||||
Err(crate::NsignerError::NotYetImplemented)
|
||||
pub fn ml_dsa_65_sign(_priv: &[u8], _msg: &[u8]) -> Result<Vec<u8>, 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<u8>, Vec<u8>), crate::NsignerError> {
|
||||
Err(crate::NsignerError::NotYetImplemented)
|
||||
pub fn slh_dsa_128s_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec<u8>, Vec<u8>), 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<Vec<u8>, crate::NsignerError> {
|
||||
Err(crate::NsignerError::NotYetImplemented)
|
||||
pub fn slh_dsa_128s_sign(_priv: &[u8], _msg: &[u8]) -> Result<Vec<u8>, 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<u8>, Vec<u8>), crate::NsignerError> {
|
||||
Err(crate::NsignerError::NotYetImplemented)
|
||||
pub fn ml_kem_768_keygen_from_seed(_seed: &[u8; 32]) -> Result<(Vec<u8>, Vec<u8>), 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>, [u8; 32]), crate::NsignerError> {
|
||||
Err(crate::NsignerError::NotYetImplemented)
|
||||
pub fn ml_kem_768_encaps(_pub: &[u8]) -> Result<(Vec<u8>, [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 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
+15
-15
@@ -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<i32>), NsignerError> {
|
||||
) -> Result<(String, i32, i32, Vec<i32>), 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);
|
||||
|
||||
+7
-7
@@ -43,17 +43,17 @@ impl SecureBuf {
|
||||
///
|
||||
/// Returns `MemoryFailed` if allocation or mlock fails (unless
|
||||
/// `allow_unlocked()` was called).
|
||||
pub fn alloc(size: usize) -> Result<Self, crate::NsignerError> {
|
||||
pub fn alloc(size: usize) -> Result<Self, crate::SignerError> {
|
||||
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(());
|
||||
|
||||
+15
-15
@@ -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<Option<String>, NsignerError> {
|
||||
) -> Result<Option<String>, 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<UnixListener, NsignerError> {
|
||||
fn bind_abstract_unix(name: &str) -> Result<UnixListener, SignerError> {
|
||||
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<UnixListener, NsignerError> {
|
||||
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
|
||||
|
||||
+22
-22
@@ -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<String, crate::NsignerError> {
|
||||
pub fn socket_name_random() -> Result<String, crate::SignerError> {
|
||||
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<String, crate::NsignerError> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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<String, crate::NsignerError> {
|
||||
}
|
||||
}
|
||||
|
||||
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<String> {
|
||||
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<String> {
|
||||
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<String, crate::NsignerError> {
|
||||
pub fn discover_single_socket() -> Result<String, crate::SignerError> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -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 <qube>:{}", QREXEC_SERVICE_NAME)));
|
||||
lines.push(Line::from(format!(" signer_client --qrexec <qube>:{}", QREXEC_SERVICE_NAME)));
|
||||
} else {
|
||||
lines.push(Line::from(" (inactive)"));
|
||||
}
|
||||
|
||||
+15
-15
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user