auth-yes/tasks/complete/2026-0824.01.jul.feat.spire-ffi.native-argon2-0352.md
google-labs-jules[bot] 9da28a6416 feat(spire-ffi): implement native Argon2id derivation
- Added `argon2` v0.5 dependency to `spire_ffi/Cargo.toml`
- Implemented `argon2id_derive` FFI function in `spire_ffi/src/lib.rs` with C-ABI.
- Added Deno FFI binding `deriveArgon2idKey` in `server/spire_ffi.ts` with `nonblocking: true` to prevent stalling the event loop.
- Pre-allocates output buffer on the Deno side as the standard FFI pattern.
- Included fallback mock behavior when `libspire_ffi.so` is not loaded, returning a 32-byte 0xaa filled array.
- Updated unit tests in `server/spire_ffi.test.ts` to test mock usage and successful generation.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-24 04:19:48 +00:00

93 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TASK METADATA
- **Target Files:** `spire_ffi/Cargo.toml`, `spire_ffi/src/lib.rs`,
`server/spire_ffi.ts`, `server/spire_ffi.test.ts`
- **Core Objective:** Implement high-throughput native Argon2id derivation (12
iterations, 64 MiB memory, 128-bit salt, 256-bit output key) via the
`spire_ffi` Rust crate to accelerate zero-knowledge hashing.
- **Dependencies:** The Deno application must be able to compile or load the
Rust dynamic library (`libspire_ffi.so` / `spire_ffi.dll` /
`libspire_ffi.dylib`).
- **Additional Important Notes:** Fallback behavior for local dev/testing must
mimic the existing pattern in `spire_ffi.ts`, returning deterministic mock
data when the library is unavailable, but throwing a fatal error in production
contexts.
---
## Architectural Considerations & Risks
### Risks
- **FFI Memory Safety:** Exposing a new C-compatible function via FFI
(`argon2id_derive`) involves raw pointer manipulation. Incorrectly sizing the
output buffer, failing to free memory properly, or reading past the bounds of
input buffers (password/salt) could lead to segfaults or memory leaks.
- **Platform Compatibility:** Relying on native code means the target
environment must support the compiled architecture. The CI/CD pipeline must
build the Rust crate for all supported deployment targets (x86_64, aarch64).
- **Blocking the Event Loop:** Hashing with Argon2id is intentionally slow and
CPU-intensive. If the Deno FFI call is blocking, it will stall the Deno event
loop. The FFI function must be marked as `nonblocking: true` in `Deno.dlopen`,
and the Rust implementation must be executed asynchronously or off the main
thread.
### Alternatives
- **WASM (e.g., `hash-wasm`):** An alternative is to use WebAssembly to perform
the hashing directly in Deno without FFI.
- _Justification against:_ Native SIMD provides 35x greater throughput
compared to V8 WebAssembly for 64 MiB memory-hard hashing. Furthermore, we
already have and maintain the `spire_ffi` Rust workspace. Avoiding a new npm
dependency keeps the server and `@auth-yes/sdk` zero-dependency, adhering to
our core architectural goals.
## Proposed Implementation
### 1. Update Rust Dependencies
- Add the `argon2` crate to `spire_ffi/Cargo.toml`:
```toml
[dependencies]
argon2 = { version = "0.5", features = ["std"] }
```
- _Note on SIMD:_ Version 0.5.x automatically uses runtime CPU feature detection
(e.g., `std::is_x86_feature_detected!("avx2")`). For portable builds, use
standard `cargo build --release`. For optional optimized container builds,
document the use of `RUSTFLAGS="-C target-cpu=native" cargo build --release`.
### 2. Implement Rust FFI Export
- In `spire_ffi/src/lib.rs`, create a new `#[no_mangle]` extern "C" function
named `argon2id_derive`.
- The function signature should accept pointers and lengths for the password and
salt, iteration count, memory size (in KB), and a pointer/length for the
output buffer.
- Use the `argon2` crate to compute the hash and write the result into the
provided output buffer. Ensure all FFI safety boundaries are respected (e.g.,
checking for null pointers).
### 3. Expose via Deno FFI (`server/spire_ffi.ts`)
- Add the `argon2id_derive` symbol to the `Deno.dlopen` definition. Critically,
set `nonblocking: true` to prevent stalling the Deno event loop during hash
computation.
- Create an exported async function
`deriveArgon2idKey(password: Uint8Array, salt: Uint8Array): Promise<Uint8Array>`.
- Hardcode the security parameters: 12 iterations, 64 MiB (65536 KB) memory, and
a 32-byte (256-bit) output key length.
- Implement the fallback pattern: If `dylib` is null, log
`[SPIRE FFI] Dynamic library (...) is not loaded. Mocking Argon2id derivation for local development.`
and return a deterministic 32-byte mock buffer (e.g., filled with a repeating
pattern).
### 4. Write Unit Tests
- In `server/spire_ffi.test.ts`, write tests to ensure:
- The fallback mechanism works when the library isn't loaded (returns the mock
buffer).
- When the library _is_ available, the function successfully returns a 32-byte
Uint8Array.
- (Optional) Verify the output matches a known standard Argon2id test vector
to confirm correctness across the FFI boundary.