From a253574386e02b5947818cf7997dfa4d0457b07c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 16:20:10 +0000 Subject: [PATCH 1/5] ogar-encryption: the single generic encryption surface for all Ada consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The forward crypto suite (Argon2id KDF, XChaCha20-Poly1305 AEAD, Ed25519, SHA-384, the wasm-capable seal/open envelope) was re-exported through ogar-auth from the ndarray `encryption` crate. Split it into its own crate so consumers that need RAW crypto (seal client-side, verify a licence sig, hash for merkle) pull ONE classid-agnostic crate — not the ndarray fork directly and never a hand-rolled argon2/chacha/ed25519 copy. - crates/ogar-encryption: thin, `#![forbid(unsafe_code)]` re-export of the encryption crate's kdf/aead/hash/sign/envelope (+ seal/open/EnvelopeError/ KdfParams/RngError root aliases); `wasm` feature forwards to encryption/wasm-bindings for browser (client-side envelope::seal) consumers. Implements nothing — a re-export crate cannot dilute the audited suite. - ogar-auth now deps ogar-encryption (path) instead of the ndarray git dep for the forward suite; its password/totp/legacy modules + public API (ogar_auth::kdf/aead/hash/sign/envelope) are unchanged, now resolved through ogar-encryption. This is the "globally reusable" structural half; the ndarray::simd::chacha20_block hardware-accel primitive (ndarray 7923c177) is the acceleration the encryption crate's AEAD hot path adopts next. --- Cargo.toml | 1 + crates/ogar-auth/Cargo.toml | 15 ++++--- crates/ogar-auth/src/lib.rs | 18 ++++---- crates/ogar-encryption/Cargo.toml | 22 +++++++++ crates/ogar-encryption/src/lib.rs | 74 +++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 crates/ogar-encryption/Cargo.toml create mode 100644 crates/ogar-encryption/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 23669d4..cb3f497 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "crates/ogar-adapter-python", "crates/ogar-adapter-csharp", "crates/ogar-auth", + "crates/ogar-encryption", ] [workspace.package] diff --git a/crates/ogar-auth/Cargo.toml b/crates/ogar-auth/Cargo.toml index a804364..1cb28cf 100644 --- a/crates/ogar-auth/Cargo.toml +++ b/crates/ogar-auth/Cargo.toml @@ -7,13 +7,14 @@ repository.workspace = true description = "OGAR auth arm: the reusable authentication SDK for OGAR consumers. Agnostic RFC 6238 TOTP, Argon2id PHC password hash/verify, a legacy 3DES-EDE2/PBKDF1-MD5 transition primitive (caller-supplied key table — carries no secrets), and a re-export of the ndarray `encryption` forward suite (XChaCha20-Poly1305 / SHA-384 / Ed25519 / zero-knowledge envelope). One surface every consumer pulls, so the auth primitives never diverge. Endgame seam: OGIT[auth] IAM federation (OIDC / Zitadel)." [dependencies] -# Forward suite — REUSED from the ndarray fork, never re-implemented here. -# Argon2id KDF, XChaCha20-Poly1305 AEAD, Ed25519 signatures, SHA-384, and the -# wasm-capable zero-knowledge envelope all live in `encryption`; ogar-auth -# re-exports them (see lib.rs) so a consumer imports ONE crate. Git dep, -# matching the ogar-class-view → lance-graph-contract precedent (ndarray's -# default branch is `master`). -encryption = { git = "https://github.com/AdaWorldAPI/ndarray", branch = "master" } +# Forward suite — REUSED via OGAR's own generic encryption surface, never +# re-implemented here. Argon2id KDF, XChaCha20-Poly1305 AEAD, Ed25519 +# signatures, SHA-384, and the wasm-capable zero-knowledge envelope all live +# in `encryption` (the ndarray fork); `ogar-encryption` is the single +# classid-agnostic re-export every Ada consumer pulls, and ogar-auth builds +# its auth-specific flows (password/totp/legacy) on top of it. Path dep, +# in-workspace sibling. +ogar-encryption = { path = "../ogar-encryption" } # Argon2id PHC hash+verify (the login-credential path — distinct from the # envelope KDF). Default features carry `password-hash` + `rand` (OsRng salt). diff --git a/crates/ogar-auth/src/lib.rs b/crates/ogar-auth/src/lib.rs index 048662d..d632f2f 100644 --- a/crates/ogar-auth/src/lib.rs +++ b/crates/ogar-auth/src/lib.rs @@ -19,12 +19,14 @@ //! | [`envelope`] (re-export) | seal / open | wasm-capable zero-knowledge envelope | //! //! The forward suite (`kdf` / `aead` / `hash` / `sign` / `envelope`) is -//! **re-exported from the ndarray `encryption` crate, never re-implemented**. -//! That crate is the audited, wasm-capable home of XChaCha20-Poly1305 + -//! Argon2id + Ed25519 + SHA-384; duplicating its byte-exact envelope layout -//! here would be exactly the dilution ogar-auth prevents. The three modules -//! this crate *adds* (`password`, `totp`, `legacy`) are the auth-specific -//! primitives the encryption crate deliberately does not carry. +//! **re-exported via [`ogar-encryption`](https://github.com/AdaWorldAPI/OGAR), +//! never re-implemented**. `ogar-encryption` is itself a thin, classid-agnostic +//! re-export of the ndarray `encryption` crate — the audited, wasm-capable +//! home of XChaCha20-Poly1305 + Argon2id + Ed25519 + SHA-384; duplicating its +//! byte-exact envelope layout here would be exactly the dilution ogar-auth +//! prevents. The three modules this crate *adds* (`password`, `totp`, +//! `legacy`) are the auth-specific primitives neither `ogar-encryption` nor +//! the encryption crate it wraps deliberately carries. //! //! ## Agnostic by construction — no consumer secrets //! @@ -50,10 +52,10 @@ pub mod legacy; pub mod password; pub mod totp; -// ── Forward suite: re-exported from the ndarray `encryption` crate ────────── +// ── Forward suite: re-exported via `ogar-encryption` ──────────────────────── // Reused wholesale, never re-implemented (see crate docs). A consumer that // pulls `ogar-auth` gets the entire forward crypto surface under one import. -pub use encryption::{aead, envelope, hash, kdf, sign}; +pub use ogar_encryption::{aead, envelope, hash, kdf, sign}; /// Unified error for the auth-specific primitives this crate adds /// (`password`, `totp`, `legacy`). Field-free on the secret-bearing paths so diff --git a/crates/ogar-encryption/Cargo.toml b/crates/ogar-encryption/Cargo.toml new file mode 100644 index 0000000..a4128e5 --- /dev/null +++ b/crates/ogar-encryption/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ogar-encryption" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +description = "OGAR's single, generic, classid-agnostic encryption surface: a thin re-export of the ndarray `encryption` crate (Argon2id KDF, XChaCha20-Poly1305 AEAD, Ed25519 signatures, SHA-384 hash, the wasm-capable zero-knowledge seal/open envelope). Every Ada consumer that needs raw forward crypto pulls THIS crate rather than depping the ndarray fork directly or hand-rolling argon2/chacha/ed25519. Carries no secrets and no consumer specifics; ogar-auth builds its auth-specific flows (password/totp/legacy) on top of this." + +[dependencies] +# The forward crypto suite — REUSED from the ndarray fork, never re-implemented +# here. Argon2id KDF, XChaCha20-Poly1305 AEAD, Ed25519 signatures, SHA-384, and +# the wasm-capable zero-knowledge envelope all live in `encryption`; this crate +# re-exports them verbatim (see lib.rs) so every consumer imports ONE crate. +# Git dep, matching the ogar-auth precedent (ndarray's default branch is +# `master`). +encryption = { git = "https://github.com/AdaWorldAPI/ndarray", branch = "master" } + +[features] +# Forward to the ndarray crate's wasm bindings, so browser consumers (e.g. a +# hub-client-style SPA that wants client-side `envelope::seal` before secrets +# ever reach a server) get the wasm-bindgen surface through this crate too. +wasm = ["encryption/wasm-bindings"] diff --git a/crates/ogar-encryption/src/lib.rs b/crates/ogar-encryption/src/lib.rs new file mode 100644 index 0000000..41a4c9f --- /dev/null +++ b/crates/ogar-encryption/src/lib.rs @@ -0,0 +1,74 @@ +//! # ogar-encryption — OGAR's generic encryption surface +//! +//! One small, classid-agnostic crate re-exporting the Ada stack's forward +//! crypto suite, so every OGAR consumer that needs raw encryption imports +//! **exactly one crate** instead of depping the ndarray fork directly or +//! (worse) hand-rolling its own Argon2/XChaCha20/Ed25519 wrapper. +//! +//! ## What lives here +//! +//! Nothing is implemented in this crate. It is a thin, documented re-export +//! of [`encryption`] (the ndarray fork's audited, wasm-capable crypto +//! module): +//! +//! | Module / item | Primitive | Role | +//! |---|---|---| +//! | [`kdf`] | Argon2id | password/secret → raw key derivation | +//! | [`aead`] | XChaCha20-Poly1305 | authenticated encryption | +//! | [`hash`] | SHA-384 | merkle / fingerprint hashing | +//! | [`sign`] | Ed25519 | licence / audit signatures | +//! | [`envelope`] | seal / open | wasm-capable zero-knowledge envelope | +//! | [`seal`], [`open`] | — | root-level aliases for `envelope::seal` / `envelope::open` | +//! | [`EnvelopeError`], [`KdfParams`] | — | root-level aliases for the envelope's error + parameter types | +//! | [`RngError`] | — | the platform-CSPRNG-unavailable error | +//! | [`wasm`] (feature `wasm`) | — | wasm-bindgen bindings for browser consumers | +//! +//! ## Generic, classid-agnostic, no secrets — by construction +//! +//! This crate carries **no consumer specifics**: no classid, no tenant, no +//! key material, no wire DTO. It is pure re-export surface — every symbol +//! here is exactly what [`encryption`] exports, unmodified. That is the +//! point: the forward suite must never diverge into per-consumer copies, and +//! a crate that re-exports and adds nothing cannot dilute it. +//! +//! ## Who builds on this +//! +//! - [`ogar-auth`](https://github.com/AdaWorldAPI/OGAR) depends on +//! `ogar-encryption` for the forward suite and adds the auth-specific +//! primitives the encryption crate deliberately does not carry (Argon2id +//! PHC password hash/verify, RFC 6238 TOTP, the legacy 3DES-EDE2/PBKDF1-MD5 +//! transition cipher). +//! - Every other Ada consumer (medcare-rs, woa-rs, smb-office-rs, and +//! siblings) that needs raw forward crypto — sealing a secret client-side, +//! verifying a licence signature, hashing for a merkle chain — pulls +//! `ogar-encryption` directly rather than the ndarray fork or a hand-rolled +//! equivalent. +//! +//! ## wasm build +//! +//! ```text +//! cargo check -p ogar-encryption --target wasm32-unknown-unknown +//! cargo build -p ogar-encryption --target wasm32-unknown-unknown \ +//! --features wasm --release +//! ``` + +#![forbid(unsafe_code)] + +// ── The forward suite: re-exported wholesale from the ndarray `encryption` +// crate. Reused, never re-implemented (see crate docs). A consumer that pulls +// `ogar-encryption` gets the entire forward crypto surface under one import. +pub use encryption::{aead, envelope, hash, kdf, sign}; + +// ── Root-level convenience aliases, mirrored from `encryption`'s own root +// re-exports (`envelope::{seal, open}` plus the envelope's error/parameter +// types), so callers that used the upstream crate's short paths keep them. +pub use encryption::{open, seal, EnvelopeError, KdfParams}; + +// ── The platform-CSPRNG-unavailable error, mirrored from `encryption`'s +// crate root. +pub use encryption::RngError; + +/// wasm-bindgen bindings for browser consumers (forwarded from +/// [`encryption`]'s `wasm-bindings` feature via this crate's `wasm` feature). +#[cfg(feature = "wasm")] +pub use encryption::wasm; From c9eb501195d05dd01204b3c925aed74809d94893 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 16:21:23 +0000 Subject: [PATCH 2/5] docs: ogar crypto consumer migration plan (the reusability map) Captures the cross-repo reusability audit: the one crypto+SIMD spine (ndarray::simd::chacha20_block <- encryption <- ogar-encryption <- ogar-auth; lance-graph-rbac as ogar-rbac), the three migration tiers (Tier 1 byte-compatible password/totp drop-ins; Tier 2 data-compat AES-GCM/3DES/vault rekeys; Tier 3 keep-local content hashing + the JWT-session surface gap), the RBAC status per consumer, and the blockers to confirm (woa-rs BBB allowlist ruling for the OGAR namespace; Sharepoint-rs missing Cargo.tomls). Durable record so the migration is trackable across sessions. --- docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md | 72 ++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md diff --git a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md new file mode 100644 index 0000000..34dedf6 --- /dev/null +++ b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md @@ -0,0 +1,72 @@ +# OGAR crypto — consumer migration plan (ogar-encryption / ogar-auth / ogar-rbac) + +> **Goal (operator):** make `ogar-auth` / `ogar-encryption` / `ogar-rbac` +> globally reusable for all consumers, backed by `AdaWorldAPI/crypto` (= the +> ndarray `encryption` crate) wired through the `ndarray::simd::*` hardware- +> acceleration layer (server AVX-512 / browser wasm128). Argon2id + XChaCha20- +> Poly1305 etc. run on that one accelerated spine, native and wasm. +> +> **Status:** the generic surface exists — `ogar-encryption` (this repo, thin +> re-export of the `encryption` crate), `ogar-auth` (password/totp/legacy on top), +> `lance-graph-rbac` (the `0x0B` auth membrane / `ClassRbac`). The +> `ndarray::simd::chacha20_block` ARX primitive (ndarray, RFC 8439 KAT-green) is +> the acceleration foundation the `encryption` AEAD adopts next. + +## The one crypto+SIMD spine + +``` +ndarray::simd::chacha20_block (ARX keystream, AVX-512 / wasm128 / NEON / scalar) + ▲ hot path +ndarray crates/encryption (Argon2id · XChaCha20-Poly1305 · Ed25519 · SHA-384 · envelope · wasm) + ▲ re-export (never re-implement) +ogar-encryption (generic, classid-agnostic raw-crypto surface) ← ALL consumers pull this + ▲ +ogar-auth (+ password PHC · RFC-6238 TOTP · legacy 3DES bridge) +lance-graph-rbac (authorize() · ClassRbac · 0x0B membrane) ← the "ogar-rbac" role +``` + +Consumers pull `ogar-encryption` for raw crypto, `ogar-auth` for login/2FA, +`lance-graph-rbac` (directly, or via `lance-graph-callcenter`'s re-export where a +BBB allowlist forbids the direct dep) for authorization. **Never hand-roll +argon2/chacha/ed25519; never copy the codebook.** + +## Migration tiers (from the 2026-07-11 cross-repo reusability audit) + +### Tier 1 — byte-compatible, drop-in (same `Argon2::default()` → identical PHC) +| Consumer | Site | → pull | +|---|---|---| +| MedCare-rs | `medcare-core::crypto::{hash,verify}_password` | `ogar-auth::password` | +| MedCare-rs | `medcare-core::totp` (near-verbatim duplicate) | `ogar-auth::totp` | +| woa-rs | `src/auth/mod.rs::{hash_password,verify_argon2id}` | `ogar-auth::password` | +| openproject-nexgen-rs | `op-auth::api_key` Argon2 arm | `ogar-auth::password` | + +### Tier 2 — data-compat (different algorithm/format → coordinated rekey, not drop-in) +| Consumer | Site | Note | +|---|---|---| +| MedCare-rs | `medcare-core::crypto` AES-256-GCM (DMS blobs) | XChaCha20-Poly1305 ≠ AES-GCM; decrypt-old/encrypt-new pass over `pma_dokument` + key rotation | +| MedCare-rs | `legacy_crypt.rs` (3DES-EDE2/PBKDF1-MD5) | `ogar-auth::legacy` exists but BOTH sides UNVERIFIED vs prod ciphertext — pair with the byte-parity vector work, don't swap blind | +| woa-rs | RFC-005 Fernet→chacha vault (unimplemented) | build straight onto `ogar-encryption::{aead,kdf}`, amend RFC-005 first | +| Sharepoint-rs | `smb-policy-encryption` (`aes_gcm_kv` stub, no Cargo.toml) | greenfield — scaffold on `ogar-encryption` (XChaCha), drop the AES-GCM name | + +### Tier 3 — keep-local (justified) +- Content-integrity **SHA-256** (MedCare hardware/licence/dms; woa GoBD audit-chain; op-attachments) — hashing records, not secrets; the envelope's SHA-**384** is a different width for its own use. +- woa-rs legacy salted-SHA256 portal format — pinned to Python `hashlib.sha256(salt+pw)` for writer-parity. +- **JWT session mint** (MedCare-rs, openproject) — a genuine *surface gap*: no generic JWT/session type in ogar-auth yet (candidate `ogar-auth::session`). +- spider PKCE SHA-256 — RFC 7636, non-secret. + +## RBAC ("ogar-rbac") status +- **MedCare-rs** `medcare-rbac` and **woa-rs** `unified_bridge.rs` already route `Policy` through `lance_graph_rbac` — the reference pattern (woa via callcenter's re-export to honor its BBB allowlist). +- **openproject-nexgen-rs** `op-auth::permissions.rs` hand-rolls a bespoke permission engine → should map OpenProject permission *names* over `lance_graph_rbac::policy::Policy`, not a parallel engine. +- **Sharepoint-rs** `smb-policy-tenant-rbac` — declared workspace member, no source; scaffold on `lance-graph-rbac` from the first commit. + +## Blockers to confirm before wiring +- **woa-rs BBB-Barriere** (`.claude/CLAUDE.md` §3 Iron Rule 1): allowlist = `lance-graph-{contract,ontology,callcenter}`; `lance-graph-rbac` is VERBOTEN direct. `ogar-auth`/`ogar-encryption` are a *different namespace*, named in neither list → **needs an explicit allowlist ruling** before woa-rs deps them. (MedCare-rs & openproject have no such barrier — same-pattern add.) +- **Sharepoint-rs** won't `cargo build` today (3 members missing `Cargo.toml`, 1 missing directory) — nothing to migrate yet; fix by scaffolding onto the generic surface. + +## Sequencing +1. Adopt `ndarray::simd::chacha20_block` inside the `encryption` AEAD hot path (behind a feature; parity vs RustCrypto on RFC 8439 vectors) — the acceleration the goal names. **(next)** +2. Land the AVX-512 / wasm128 backends of `chacha20_block` (parity vs the scalar KAT). +3. Tier-1 drop-in migrations (password/totp) — start with MedCare-rs (no barrier). +4. Confirm the woa-rs allowlist ruling; then woa-rs Tier-1. +5. Tier-2 data-compat migrations behind their rekey plans. +6. Fill the JWT-session gap (`ogar-auth::session`) if the consumers want to converge it. From d9a1c80a4983436455e2bed8b4f57d697f605a03 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 16:45:15 +0000 Subject: [PATCH 3/5] docs(crypto-migration): record accel-primitive DONE + AEAD security ruling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ndarray ChaCha20 keystream (scalar + AVX-512 + wasm128) is complete and byte-parity-proven vs RustCrypto — sequencing step updated to DONE. Record the security ruling: the encryption XChaCha20-Poly1305 AEAD is NOT re-wired to the primitive (that would mean hand-composing HChaCha20 + Poly1305 = the roll-your-own-AEAD footgun the stack forbids); the primitive is for raw- stream use sites with a caller-owned MAC. Flags the open operator decision (add optional ndarray dep to the lean encryption crate?) and advances the sequence to the Tier-1 password/totp consumer migrations. Co-Authored-By: Claude Opus 4.8 --- docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md index 34dedf6..554b5a3 100644 --- a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md +++ b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md @@ -9,8 +9,11 @@ > **Status:** the generic surface exists — `ogar-encryption` (this repo, thin > re-export of the `encryption` crate), `ogar-auth` (password/totp/legacy on top), > `lance-graph-rbac` (the `0x0B` auth membrane / `ClassRbac`). The -> `ndarray::simd::chacha20_block` ARX primitive (ndarray, RFC 8439 KAT-green) is -> the acceleration foundation the `encryption` AEAD adopts next. +> `ndarray::simd` ChaCha20 ARX keystream is **complete and trusted**: scalar +> reference + **AVX-512 (server)** + **wasm128 (browser)** backends, all +> byte-parity-proven against both the RFC 8439 KAT and the vetted RustCrypto +> `chacha20` implementation across the parameter space (`chacha20_keystream`, +> ndarray branch `claude/medcare-ruff-codebook-handover-5ulx0i`). ## The one crypto+SIMD spine @@ -64,9 +67,9 @@ argon2/chacha/ed25519; never copy the codebook.** - **Sharepoint-rs** won't `cargo build` today (3 members missing `Cargo.toml`, 1 missing directory) — nothing to migrate yet; fix by scaffolding onto the generic surface. ## Sequencing -1. Adopt `ndarray::simd::chacha20_block` inside the `encryption` AEAD hot path (behind a feature; parity vs RustCrypto on RFC 8439 vectors) — the acceleration the goal names. **(next)** -2. Land the AVX-512 / wasm128 backends of `chacha20_block` (parity vs the scalar KAT). -3. Tier-1 drop-in migrations (password/totp) — start with MedCare-rs (no barrier). +1. ~~Land the AVX-512 / wasm128 backends of `chacha20_block` (parity vs the scalar KAT).~~ **DONE.** Both backends shipped, byte-parity-proven vs the scalar KAT *and* vs the vetted RustCrypto `chacha20` across the parameter space. The keystream primitive is trusted. +2. **The `encryption` AEAD is NOT re-wired to the primitive — by security ruling, not omission.** XChaCha20-Poly1305 = HChaCha20 subkey + Poly1305 one-time-key + MAC/AAD/length framing, not just a keystream. Swapping only the keystream means hand-composing that authenticated construction — the "roll your own AEAD" footgun the stack forbids ("never roll your own crypto — wrap vetted RustCrypto"). The RustCrypto `XChaCha20Poly1305` stays. The accelerated primitive is for **raw-stream** use sites whose caller already owns a vetted MAC/framing (e.g. a future woa-rs RFC-005 vault built on `ogar-encryption::{aead,kdf}`), never for reimplementing an AEAD. See the doc-comment in `encryption/src/aead.rs`. **OPEN DECISION for the operator:** whether to add `ndarray` as an *optional* dep of the lean `encryption` crate to expose a raw-stream `apply_keystream` at all — it pulls a heavy dep and the crate's wasm build currently has an unrelated `getrandom` `wasm_js` config gap. Deferred pending that call. +3. Tier-1 drop-in migrations (password/totp → `ogar-auth`) — start with MedCare-rs (no barrier). **(next)** 4. Confirm the woa-rs allowlist ruling; then woa-rs Tier-1. 5. Tier-2 data-compat migrations behind their rekey plans. 6. Fill the JWT-session gap (`ogar-auth::session`) if the consumers want to converge it. From c17212dd651062c537cfb806ce8494a6bc823668 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 16:48:48 +0000 Subject: [PATCH 4/5] docs(crypto-migration): capture MedCare Tier-1 recipe + dep-wiring decision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Byte-compatibility of medcare-core::{crypto,totp} vs ogar-auth confirmed by source read (Argon2::default() PHC parity; RFC 6238 TOTP verbatim parity). Record the exact body-only delegation recipe (zero caller changes) so it is cheap to execute once green-lit, and flag the operator decision that gates it: ogar-auth pulls `encryption` via git into a workspace that vendors ndarray locally — accept the git pull, or repoint ogar-encryption at the local path first (fork-policy-consistent). Staged, not shipped, pending that call — not forcing a heavy cross-repo dep + build under the disk budget. Co-Authored-By: Claude Opus 4.8 --- docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md index 554b5a3..2ec66a7 100644 --- a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md +++ b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md @@ -43,6 +43,39 @@ argon2/chacha/ed25519; never copy the codebook.** | woa-rs | `src/auth/mod.rs::{hash_password,verify_argon2id}` | `ogar-auth::password` | | openproject-nexgen-rs | `op-auth::api_key` Argon2 arm | `ogar-auth::password` | +#### MedCare-rs Tier-1 recipe (verified drop-in; staged — needs the dep-wiring green-light below) + +Byte-compatibility is **confirmed by source read** (2026-07-11): `ogar-auth::password` +is `Argon2::default()` (identical PHC to `medcare-core::crypto`), and `ogar-auth::totp` +is a near-verbatim copy of `medcare-core::totp` — same `STEP_SECONDS=30`/`DIGITS=6`/ +`SKEW_STEPS=1`, same RFC 4648 base32, same RFC 4226 HOTP, same RFC 6238 Appendix-B +vectors, same `otpauth://` URI. So the migration is a **body-only delegation** that +preserves every `medcare-core` public signature (zero caller changes in medcare-db / +medcare-server): + +1. `medcare-core/Cargo.toml`: add `ogar-auth = { path = "../../vendor/OGAR/crates/ogar-auth" }`. +2. `medcare-core/src/crypto.rs`: replace the `hash_password`/`verify_password` **bodies** + with `ogar_auth::password::*`, mapping `AuthError → DomainError::Crypto`. Keep AES-GCM + + SHA-256 local (Tier 2/3). +3. `medcare-core/src/totp.rs`: replace `base32_{encode,decode}` / `hotp` / `code_at` / + `verify_code` / `provisioning_uri` / `generate_secret_base32` **bodies** with + `ogar_auth::totp::*`, mapping errors; keep the existing medcare tests (they now + exercise the delegated path and must stay green). One signature mismatch to bridge: + ogar-auth `generate_secret_base32()` is `AuthResult` (getrandom) vs medcare's + infallible `-> String` — either propagate the Result (touches 1-2 callers) or map a + CSPRNG failure explicitly; do NOT paper over it. + +**⚠ Dep-wiring decision the operator must make first** (why this is staged, not shipped): +`ogar-auth` → `ogar-encryption` → `encryption` is a **git** dep (`ndarray` `branch="master"`), +so depping `ogar-auth` into `medcare-core` git-fetches an `encryption`/`ndarray` tree into a +workspace that otherwise **vendors ndarray locally** (`vendor/ndarray -> ../../ndarray`). That +is the established `ogar-encryption` wiring, not a new inconsistency — but it means a second, +git-sourced ndarray-encryption alongside the locally-vendored one, plus a heavier first build. +Resolve before wiring: either (a) accept the git `encryption` pull as-is, or (b) repoint +`ogar-encryption`'s `encryption` dep at the local path so all consumers share one ndarray +source (the fork-policy-consistent option). Only `password`+`totp` are used here, but depping +`ogar-auth` pulls the whole forward suite (incl. the git `encryption`) regardless. + ### Tier 2 — data-compat (different algorithm/format → coordinated rekey, not drop-in) | Consumer | Site | Note | |---|---|---| From f3012bea908f678da95e0ef550b55f936163e659 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 16:59:38 +0000 Subject: [PATCH 5/5] =?UTF-8?q?docs(crypto-migration):=20MedCare-rs=20Tier?= =?UTF-8?q?-1=20SHIPPED=20(password+totp=20=E2=86=92=20ogar-auth)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit medcare-core now delegates crypto password + the totp module to ogar-auth (medcare-rs 6d2c372): 56/56 tests green through the delegated path incl. RFC 6238 Appendix-B vectors, all medcare crates compile. Dep-wiring resolved as option (b) — a root [patch] folds ogar-encryption's git `encryption` onto the locally-vendored fork copy (one source, no git fetch). ogar-auth is now a live, working consumer dependency, not just a documented surface. Co-Authored-By: Claude Opus 4.8 --- docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md index 2ec66a7..3fc4ff1 100644 --- a/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md +++ b/docs/OGAR-CRYPTO-CONSUMER-MIGRATION.md @@ -43,9 +43,21 @@ argon2/chacha/ed25519; never copy the codebook.** | woa-rs | `src/auth/mod.rs::{hash_password,verify_argon2id}` | `ogar-auth::password` | | openproject-nexgen-rs | `op-auth::api_key` Argon2 arm | `ogar-auth::password` | -#### MedCare-rs Tier-1 recipe (verified drop-in; staged — needs the dep-wiring green-light below) +#### MedCare-rs Tier-1 — ✅ SHIPPED (2026-07-11, medcare-rs `6d2c372`) -Byte-compatibility is **confirmed by source read** (2026-07-11): `ogar-auth::password` +`medcare-core::crypto::{hash,verify}_password` and the whole `medcare-core::totp` +module now **delegate to `ogar-auth`** (body-only, public signatures preserved). +Empirically byte-compatible: medcare-core's own crypto+totp tests — including the +RFC 6238 Appendix-B vectors and the Argon2 hash/verify round-trip — pass **through** +the delegated ogar-auth path (56/56). medcare-core / medcare-db / medcare-server all +compile. The dep-wiring decision below was resolved as **option (b)**: a root +`[patch."…/ndarray"] encryption = { path = "vendor/ndarray/crates/encryption" }` +folds `ogar-encryption`'s git `encryption` onto the locally-vendored fork copy — one +source, no git fetch, no OGAR change. (The `encryption` crate has no ndarray dep, so +the patch touches only that leaf.) The recipe + rationale below are retained as the +template for the woa-rs / openproject Tier-1 migrations. + +Byte-compatibility was **confirmed by source read** (2026-07-11) before wiring: `ogar-auth::password` is `Argon2::default()` (identical PHC to `medcare-core::crypto`), and `ogar-auth::totp` is a near-verbatim copy of `medcare-core::totp` — same `STEP_SECONDS=30`/`DIGITS=6`/ `SKEW_STEPS=1`, same RFC 4648 base32, same RFC 4226 HOTP, same RFC 6238 Appendix-B @@ -102,7 +114,7 @@ source (the fork-policy-consistent option). Only `password`+`totp` are used here ## Sequencing 1. ~~Land the AVX-512 / wasm128 backends of `chacha20_block` (parity vs the scalar KAT).~~ **DONE.** Both backends shipped, byte-parity-proven vs the scalar KAT *and* vs the vetted RustCrypto `chacha20` across the parameter space. The keystream primitive is trusted. 2. **The `encryption` AEAD is NOT re-wired to the primitive — by security ruling, not omission.** XChaCha20-Poly1305 = HChaCha20 subkey + Poly1305 one-time-key + MAC/AAD/length framing, not just a keystream. Swapping only the keystream means hand-composing that authenticated construction — the "roll your own AEAD" footgun the stack forbids ("never roll your own crypto — wrap vetted RustCrypto"). The RustCrypto `XChaCha20Poly1305` stays. The accelerated primitive is for **raw-stream** use sites whose caller already owns a vetted MAC/framing (e.g. a future woa-rs RFC-005 vault built on `ogar-encryption::{aead,kdf}`), never for reimplementing an AEAD. See the doc-comment in `encryption/src/aead.rs`. **OPEN DECISION for the operator:** whether to add `ndarray` as an *optional* dep of the lean `encryption` crate to expose a raw-stream `apply_keystream` at all — it pulls a heavy dep and the crate's wasm build currently has an unrelated `getrandom` `wasm_js` config gap. Deferred pending that call. -3. Tier-1 drop-in migrations (password/totp → `ogar-auth`) — start with MedCare-rs (no barrier). **(next)** -4. Confirm the woa-rs allowlist ruling; then woa-rs Tier-1. +3. Tier-1 drop-in migrations (password/totp → `ogar-auth`). **MedCare-rs DONE** (`6d2c372`, delegated + 56/56 green + all crates compile; dep-wiring resolved via the local-`encryption` patch). openproject-nexgen-rs next (no barrier, same pattern). +4. Confirm the woa-rs allowlist ruling (the `ogar-*` namespace is in neither BBB list); then woa-rs Tier-1. 5. Tier-2 data-compat migrations behind their rekey plans. 6. Fill the JWT-session gap (`ogar-auth::session`) if the consumers want to converge it.