99 lines
6.1 KiB
Markdown
99 lines
6.1 KiB
Markdown
# Multi-User OTP Partitioning Parity & Enhancement Plan
|
|
|
|
## Overview
|
|
|
|
The multi-user / multi-device OTP dynamic partitioning scheme (`OTP-GRANT-V1`, `end_offset`, `next_extent`, and passive `peer_extent` tracking in a single `<chksum>.state` file) is partially implemented in `signer`, but has several protocol parity gaps compared to `~/lt/otp` (`src/pads.c` and `src/crypto.c`), as well as missing RPC and TUI integration.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Donor as Donor Device - otp or signer
|
|
participant StateA as Donor .state
|
|
participant Recip as Recipient - signer
|
|
participant StateB as Recipient .state
|
|
|
|
Donor->>Donor: mid = align_256((offset + end) / 2)
|
|
Donor->>StateA: Write end_offset=mid & peer_extent=mid+256:old_end + fsync
|
|
Donor->>Donor: Encrypt OTP-GRANT-V1 at offset mid (256B)
|
|
Donor->>Recip: Send -----BEGIN OTP MESSAGE-----
|
|
Recip->>Recip: Decrypt chunk at mid & verify Pad-ChkSum
|
|
Recip->>Recip: Check no overlap with [offset, end_offset)
|
|
Recip->>StateB: Adopt immediately (if new/exhausted) or push next_extent + fsync
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Core Protocol & Safety Fixes in `src/otp_pad.rs`
|
|
|
|
1. **Validate `Pad-ChkSum` in `parse_grant_message`**:
|
|
- Update `parse_grant_message(plaintext: &[u8]) -> Option<(String, u64, u64)>` to require and return the `Pad-ChkSum: <64hex>` header (matching `parse_grant_message` in `~/lt/otp/src/pads.c`).
|
|
- In `OtpPadState::decrypt_encoded`, verify `grant_chksum == self.chksum` before applying the grant; return `Err(SignerError::InvalidInput)` on mismatch.
|
|
|
|
2. **Harden Grant Import (`apply_grant` logic in `decrypt_encoded`)**:
|
|
- **Overlap check**: If the state file already exists, reject any grant where `start < self.partition_end() && self.offset < end` (`Err(SignerError::InvalidInput)`), matching `apply_grant_to_state` in `~/lt/otp/src/pads.c`.
|
|
- **Immediate adoption on exhausted partition**: If `self.offset >= self.partition_end() && self.next_extents.is_empty()`, immediately set `self.offset = start` and `self.end_offset = Some(end)` instead of pushing to `next_extents`.
|
|
- **Bounds check**: When queueing into `self.next_extents`, enforce `self.next_extents.len() < MAX_NEXT_EXTENTS` (return `Err(SignerError::InvalidInput)` if full).
|
|
|
|
3. **Crash-Safe Persistence (`fsync`)**:
|
|
- In `OtpPadState::persist_state`, open/create the `.tmp` file, write the state content, call `file.sync_all()`, close it, and then `std::fs::rename` over `<chksum>.state`. This guarantees the shrunken `end_offset` is on disk before any grant message is emitted.
|
|
|
|
4. **Extended `.state` Support in `scan_pads` and `PadInfo`**:
|
|
- Expand `PadInfo` to include `end_offset: Option<u64>`, `next_count: usize`, `peer_count: usize`, and a helper `partition_status(&self) -> String` that formats `"whole"`, `"1/2"`, `"2/3+q"`, etc. (matching `format_partition_status` in `~/lt/otp/src/pads.c`).
|
|
- Fix `scan_pads` to call `parse_state_file` instead of `s.trim().parse::<u64>()` so `.state` files with `offset=<N>` and multi-line extents report accurate offsets and partition status.
|
|
|
|
5. **Global Status & Global Split Entry Point**:
|
|
- Update `OtpPadState` and `global_status()` to display the active partition upper bound (`offset/end_offset (part 1/2)`) rather than only `offset/pad_size`.
|
|
- Add `pub struct OtpSplitOutput { pub pad_chksum: String, pub grant_start: u64, pub grant_end: u64, pub new_end_offset: u64, pub grant_armor: String }` and `pub fn global_split() -> Result<OtpSplitOutput, SignerError>` which calls `split_partition()`, `format_grant_message()`, and `encrypt_encoded_at(grant.start - 256, ...)`.
|
|
|
|
---
|
|
|
|
## 2. JSON-RPC & Server Support (`src/enforcement.rs`, `src/dispatcher.rs`, `src/server.rs`)
|
|
|
|
1. **`VERB_SPLIT` Constant & Routing**:
|
|
- Add `pub const VERB_SPLIT: &str = "split";` in `src/enforcement.rs`.
|
|
- In `src/dispatcher.rs`:
|
|
- Route `VERB_SPLIT` (when `options.algorithm == "otp"` or no other algorithm is specified) to `handle_otp_verb`.
|
|
- Include `"split"` in `get_info` verbs list.
|
|
- Implement `VERB_SPLIT` in `handle_otp_verb` calling `crate::otp_pad::global_split()` and returning:
|
|
```json
|
|
{
|
|
"grant": "<ascii-armored OTP-GRANT-V1>",
|
|
"pad_chksum": "<64hex>",
|
|
"grant_start": 2304,
|
|
"grant_end": 4096,
|
|
"end_offset": 2048
|
|
}
|
|
```
|
|
- In `src/server.rs`, allow `VERB_SPLIT` alongside `VERB_ENCRYPT` and `VERB_DECRYPT` in `process_request`.
|
|
|
|
---
|
|
|
|
## 3. TUI Updates (`src/tui.rs`)
|
|
|
|
1. **OTP Pad Browser (`RoleAddStage::OtpBrowse`)**:
|
|
- Display both partition usage and partition status (`whole`, `1/2`, `1/2+q`) from `PadInfo` when browsing pads.
|
|
2. **Main Screen Connections Panel**:
|
|
- Automatically reflects the richer `global_status()` showing partition bounds and peer/queued counts.
|
|
|
|
---
|
|
|
|
## 4. `signer-client` Updates (`src/client/`)
|
|
|
|
1. **Fix `end_offset` Output Bug in `src/client/main.rs`**:
|
|
- In `run_split`, fix line 627 from `println!("This device's new end_offset: {}", grant.start);` to `grant.start - 256` (matching `otp.partition_end()` and `~/lt/otp/src/pads.c`).
|
|
2. **Support Daemon RPC Split**:
|
|
- Add `otp_split(&mut self, alg: &AlgOptions)` to `SignerSigner` in `src/client/signer.rs`.
|
|
- In `src/client/cli.rs` and `src/client/main.rs`, allow `signer-client -a otp split` (or `signer-client split --remote`) to invoke the `split` RPC verb against the running `signer` daemon's bound pad, while keeping `signer-client split <pad-prefix>` for direct local pad splitting.
|
|
|
|
---
|
|
|
|
## 5. Tests (`src/otp_pad.rs`, `src/dispatcher.rs`)
|
|
|
|
1. Update existing unit tests in `src/otp_pad.rs` for the 3-tuple return of `parse_grant_message`.
|
|
2. Add unit tests for:
|
|
- Rejecting a grant with a mismatched `Pad-ChkSum`.
|
|
- Rejecting a grant that overlaps the active `[offset, end_offset)` partition.
|
|
- Immediate adoption of a grant when the current partition is exhausted (`offset == end_offset`).
|
|
- Rejecting grants beyond `MAX_NEXT_EXTENTS`.
|
|
- `scan_pads` parsing `offset=512`, `end_offset=...`, `next_extent=...`, and `peer_extent=...`.
|
|
3. Add dispatcher unit test `test_otp_split_and_grant_import_roundtrip` in `src/dispatcher.rs`.
|