- 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>
93 lines
4.2 KiB
Markdown
93 lines
4.2 KiB
Markdown
# 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 3–5x 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.
|