mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-07 05:12:34 +00:00
TS port slice 4: portable backup, CI, and the agent-blind CLI MVP
- Portable backup (format_version 1): TS imports Python exports, re-exports
byte-identically under pinned nonce/timestamp, verifies the canonical
checksum, rejects tampering and unknown versions.
- .github/workflows/ts-parity.yml: typecheck + Node/jsdom parity suite on
js/ changes, plus a fixture-drift job that regenerates fixtures from
Python and fails if the committed ones don't match (manifest commit field
excluded by construction).
- packages/cli — seedpass-js MVP implementing plan section 9.3:
* reference-first output: entry list/get/search emit sp://entry/<id> refs
and metadata; secret-bearing fields become has_* flags
* 'entry reveal' is the only plaintext egress command
* 'use --clipboard|--exec|--stdin-to' delivers secrets to sinks; --exec
injects SEEDPASS_SECRET into the child env, never argv or stdout
* vault export/import over portable backups; capabilities --format json
* mnemonic comes from SEEDPASS_MNEMONIC env, never the command line
- CLI tests prove the blindness properties: derived password matches the v2
fixture, TOTP at pinned time, secrets absent from all default output and
from sink-delivery confirmations.
- 125 core parity tests (Node + jsdom) + 11 CLI tests green.
Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
2345f61431
commit
ab569fe900
22 files changed
+1362
-14
No files matched your search
@@ -812,6 +812,53 @@ def gen_delta_replay() -> dict:
|
||||
}
|
||||
|
||||
|
||||
def gen_portable_backup(entries: dict) -> dict:
|
||||
from utils.checksum import canonical_json_dumps, json_checksum
|
||||
|
||||
mnemonic = MNEMONICS[PRIMARY]
|
||||
index_data = entries
|
||||
canonical = canonical_json_dumps(index_data)
|
||||
checksum = json_checksum(index_data)
|
||||
|
||||
key = base64.urlsafe_b64decode(derive_index_key(mnemonic))
|
||||
nonce = hashlib.sha256(b"seedpass-ts-fixture-export-nonce").digest()[:12]
|
||||
payload = b"V3|" + nonce + AESGCM(key).encrypt(nonce, canonical.encode(), None)
|
||||
|
||||
encrypted_wrapper = {
|
||||
"format_version": 1,
|
||||
"created_at": FIXED_UNIX,
|
||||
"fingerprint": generate_fingerprint(mnemonic),
|
||||
"encryption_mode": "seed-only",
|
||||
"cipher": "aes-gcm",
|
||||
"checksum": checksum,
|
||||
"payload": base64.b64encode(payload).decode(),
|
||||
}
|
||||
plaintext_wrapper = {
|
||||
"format_version": 1,
|
||||
"created_at": FIXED_UNIX,
|
||||
"fingerprint": generate_fingerprint(mnemonic),
|
||||
"encryption_mode": "none",
|
||||
"cipher": "none",
|
||||
"checksum": checksum,
|
||||
"payload": base64.b64encode(canonical.encode()).decode(),
|
||||
}
|
||||
|
||||
return {
|
||||
"description": (
|
||||
"Portable backup wrapper (portable_backup.py, format_version 1). "
|
||||
"payload = V3 AES-GCM over canonical JSON of the index, key = "
|
||||
"index key of the parent seed; checksum = "
|
||||
"sha256(canonical_json(index)). Nonce/created_at pinned "
|
||||
"(fixture-only)."
|
||||
),
|
||||
"mnemonic_id": PRIMARY,
|
||||
"index": index_data,
|
||||
"canonical_json_sha256": checksum,
|
||||
"encrypted_wrapper": encrypted_wrapper,
|
||||
"plaintext_wrapper": plaintext_wrapper,
|
||||
}
|
||||
|
||||
|
||||
def gen_kdf_metadata() -> dict:
|
||||
return {
|
||||
"description": (
|
||||
@@ -861,6 +908,7 @@ def main() -> None:
|
||||
files["entries_index.json"] = entries_fixture
|
||||
files["vault_v3_payload.json"] = vault_fixture
|
||||
files["nostr_snapshot.json"] = gen_nostr_snapshot(vault_fixture["payload_b64"])
|
||||
files["portable_backup.json"] = gen_portable_backup(entries_fixture["entries"])
|
||||
files["sync_merge.json"] = gen_sync_merge()
|
||||
files["delta_replay.json"] = gen_delta_replay()
|
||||
|
||||
|
||||
Reference in new issue
Block a user