132 lines
8.9 KiB
Markdown
132 lines
8.9 KiB
Markdown
# Bitpacker crypto rewrite — auxiliary primitives only (slim)
|
|
|
|
**Package**: `@suaveplan/bitpacker-crypto`
|
|
**Status**: PROPOSED — supersedes `bitpacker-crypto-port`. Slimmed 2026-05-02 after discovering parallel work in `restore-crypto-keychain-packer`.
|
|
**Type**: clean-sheet additions of auxiliary bit-packer-aware primitives
|
|
|
|
## Why (slim version)
|
|
|
|
The 2026-05-02 audit drove an initial design with seven primitives. A subsequent uncommitted-OpenSpec scan found that **`restore-crypto-keychain-packer`** (parallel work, partially implemented) already proposes a new opinionated `@suaveplan/crypto` layer that contains essentially every primitive whose design depends on `Authority` + `DomainTag` + `Keychain`:
|
|
|
|
| First-draft primitive | Owned by `restore-crypto-keychain-packer` |
|
|
|---|---|
|
|
| `SignedBitSchema` (Authority + DomainTag) | `Packer` (encrypt-and-sign envelope) + `Authority.sign()` |
|
|
| `EnvelopeSchema(magic, version, body)` | `packPublicBundle` shape + Packer envelope |
|
|
| `HashChainSchema` (Merkle-chained signed log) | `AuditLog` (Merkle log + `proveInclusion` / `verifyInclusion`) |
|
|
| `MultiSignedBitSchema` (N-of-N co-sign) | `RevocationList` (root-signed); co-signing belongs in the `Authority`-owning crypto layer if needed |
|
|
| `EncryptedBitSchema` (niche AEAD) | `Packer` (covers this exactly) |
|
|
| New domain tags (`DOMAIN_BITPACKER_*_V1`) | Closed-list extensions belong in `@suaveplan/crypto/domain-tags` |
|
|
|
|
After dropping every overlapping primitive, the **two remaining additions** in this change are pure bit-packer utilities that don't touch `Authority` or the closed-list `DomainTag` registry:
|
|
|
|
- **`SchemaFingerprint`** — content-hash a `BitPackerSchema` (canonical form). Drives mesh schema-agreement handshakes per `docs/network/security-and-tokens.md`.
|
|
- **`HashAlgorithmRegistry`** — pluggable hash-algorithm registry (xxhash32 / crc32 / sha256 default; consumers register BLAKE3, future PQC).
|
|
|
|
Both compose with `restore-crypto-keychain-packer` without conflict.
|
|
|
|
## Decision history
|
|
|
|
1. **`bitpacker-crypto-port`** (2026-05-02) — proposed deprecate-and-route through `@suaveplan/crypto` family. Superseded by this rewrite.
|
|
2. **`bitpacker-crypto-rewrite` first draft** (2026-05-02) — proposed seven bit-packer-aware primitives consolidating eight inline consumers. Superseded by this slim version after `restore-crypto-keychain-packer` discovery.
|
|
3. **`bitpacker-crypto-rewrite` slim** (this proposal) — keeps only the two primitives that don't overlap `restore-crypto-keychain-packer`'s `@suaveplan/crypto` layer.
|
|
|
|
The audit findings from the first draft (eight inline consumers manually re-implementing crypto+bitpacker patterns) **still hold** — but they're consolidated by `restore-crypto-keychain-packer`'s `Packer` / `Authority` / `AuditLog` / `RevocationList`, not by primitives in `bitpacker-crypto`. Per-consumer migration to those primitives is `restore-crypto-keychain-packer`'s scope, not this change's.
|
|
|
|
## What this package SHALL ship
|
|
|
|
### `schemaFingerprint(schema, algorithm?, registry?) → Promise<Uint8Array>`
|
|
|
|
Content-hash a `BitPackerSchema` after canonical serialization (sorted keys, no whitespace, deterministic encoding). Equivalent schemas in different declaration orders produce byte-identical fingerprints. Used for "do we agree on the schema?" handshakes between mesh peers.
|
|
|
|
```ts
|
|
function schemaFingerprint(
|
|
schema: BitPackerSchema,
|
|
algorithm?: string, // default: "sha256"
|
|
registry?: HashAlgorithmRegistry // default: process-wide singleton
|
|
): Promise<Uint8Array>;
|
|
|
|
function assertSchemaFingerprint(
|
|
schema: BitPackerSchema,
|
|
expected: Uint8Array,
|
|
algorithm?: string,
|
|
registry?: HashAlgorithmRegistry
|
|
): Promise<void>;
|
|
```
|
|
|
|
Companion `assertSchemaFingerprint` throws `BitpackerError` code `CRYPTO_SCHEMA_FINGERPRINT_MISMATCH` on disagreement.
|
|
|
|
### `HashAlgorithmRegistry` (pluggable extension)
|
|
|
|
Open registry of hash algorithms. Defaults shipped: `xxhash32`, `crc32`, `sha256` (the existing checksum surface in `bitpacker-crypto`). Consumers register their own (`blake3` for drive whole-file hash, future PQC algorithms, etc.).
|
|
|
|
```ts
|
|
interface HashAlgorithm {
|
|
readonly name: string;
|
|
readonly outputBytes: number;
|
|
hash(data: Uint8Array): Uint8Array | Promise<Uint8Array>;
|
|
}
|
|
|
|
interface HashAlgorithmRegistry {
|
|
register(alg: HashAlgorithm): void;
|
|
resolve(name: string): HashAlgorithm | undefined;
|
|
has(name: string): boolean;
|
|
list(): readonly string[];
|
|
}
|
|
|
|
function defaultHashRegistry(): HashAlgorithmRegistry; // process-wide singleton
|
|
function createHashRegistry(): HashAlgorithmRegistry; // fresh
|
|
```
|
|
|
|
Re-registering an algorithm name with a new implementation is last-write-wins; the replacement emits an OTel span `bitpacker-crypto.hash-registry.replaced`.
|
|
|
|
### KEPT (existing checksum surface unchanged)
|
|
|
|
- `crc32`, `adler32`, `appendCrc32`, `verifyAndStripCrc32`
|
|
- `calculateXxhash32`, `packXxhash32`, `unpackXxhash32`, `appendXxhash32`, `verifyXxhash32`, `verifyAndStripXxhash32`
|
|
|
|
These remain as in the current package; the `HashAlgorithmRegistry`'s default implementations consume them.
|
|
|
|
## What this package SHALL NOT ship (dropped from first draft)
|
|
|
|
| Dropped | Reason |
|
|
|---|---|
|
|
| `SignedBitSchema` | `restore-crypto-keychain-packer` ships `Packer` (encrypt-and-sign envelope under DomainTag via Authority); for sign-only use cases, callers use `Authority.sign(domain, slot, payload)` directly. No bitpacker-side primitive needed. |
|
|
| `EnvelopeSchema(magic, version, bodySchema)` | Pattern lives in `@suaveplan/crypto`'s `packPublicBundle` and `Packer` envelope; bit-packer doesn't need a generic envelope primitive when the only consumers are crypto-layer primitives. |
|
|
| `HashChainSchema` | `AuditLog` in `@suaveplan/crypto` is a Merkle-chained, root-signed append-only log with `proveInclusion` / `verifyInclusion`. Strict superset. |
|
|
| `MultiSignedBitSchema` | Co-signing is an `Authority`-using primitive; lives next to `Packer` / `RevocationList` in `@suaveplan/crypto`. If/when `MeshTrustToken` (NET-110) needs it, the spec delta lands in the new `@suaveplan/crypto`, not here. |
|
|
| `EncryptedBitSchema` | `Packer` covers small-payload encrypt-and-sign exactly. No niche left to fill. |
|
|
| New `DOMAIN_BITPACKER_*_V1` constants | `DomainTag` is a closed list owned by `@suaveplan/crypto`; new domains are wire-format extensions in that package, not bit-packer. |
|
|
| `ChunkVerifier` | Already dropped in first audit pass. Genesis `@suaveplan/packer::ChunkVerifier` covers locked v1 chunks. |
|
|
|
|
## Source mapping
|
|
|
|
| Requirement | Source | Notes |
|
|
|---|---|---|
|
|
| `schemaFingerprint` canonicalization + algorithm registry | `network-registry::Xxhash32HashProvider`/`Crc32HashProvider` (upstream) | Pattern: hash-provider over consistent bytes. Schema canonicalization is the bit-packer-side novelty. |
|
|
| `assertSchemaFingerprint` mismatch error | Mesh schema-agreement handshake — `docs/network/security-and-tokens.md`, `docs/network/architecture.md` | Future consumer; the primitive prepares for it. |
|
|
| `HashAlgorithmRegistry` default algorithms (xxhash32, crc32, sha256) | Existing `bitpacker-crypto/src/index.ts` | Re-uses checksum impls already shipping. |
|
|
| `HashAlgorithmRegistry` BLAKE3 plug-in path | `docs/drive/encryption.md` L79, L105 (whole-file BLAKE3) | Future consumer registers BLAKE3 once. |
|
|
|
|
### Requirements with no source line
|
|
|
|
| Requirement | Justification |
|
|
|---|---|
|
|
| Pluggable hash-registry pattern | User directive on extensibility / plugins / middleware. |
|
|
| Last-write-wins re-register with OTel emission | Standard genesis registry pattern (matches `@suaveplan/random` algorithm registries). |
|
|
| Zod-validated public types | enterprise/bitpacker.md L38-L39. |
|
|
| OTel span on every primitive | enterprise/bitpacker.md L41. |
|
|
|
|
## Out of scope
|
|
|
|
- Everything in `restore-crypto-keychain-packer` (Packer, Authority, AuditLog, RevocationList, Keychain, Vault, etc.)
|
|
- Domain-tag closed-list additions (those belong in `@suaveplan/crypto/domain-tags`)
|
|
- The `bytes` field type and `byteAlign()` core-bitpacker additions (already specced in `restore-crypto-keychain-packer/specs/bitpacker/spec.md`)
|
|
- Per-consumer migration of inline crypto patterns — those go to follow-up changes after `restore-crypto-keychain-packer` lands its `Packer` / `AuditLog` primitives.
|
|
- All seven dropped primitives (see "What this package SHALL NOT ship" above)
|
|
|
|
## Risks
|
|
|
|
1. **`restore-crypto-keychain-packer` may revise the closed-list `DomainTag` design.** No risk to this slim change since we don't add domain tags. If their design changes, only their spec needs revision.
|
|
2. **Hash registry singleton state leakage between tests.** Mitigated by `createHashRegistry()` for tests + a `spec-conformance` test that singletons are read-only beyond `register`.
|
|
3. **BLAKE3 not in genesis yet.** The `HashAlgorithmRegistry` ships extension points but doesn't ship a BLAKE3 implementation. The drive layer's BLAKE3 use (per `docs/drive/encryption.md`) becomes its own follow-up change to register a BLAKE3 implementation.
|