# Capability Spec Delta: crypto-hash (crypto-completion) **Package**: `@suaveplan/crypto-hash` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. ## ADDED Requirements ### Requirement: scrypt KDF primitive The package SHALL expose `scrypt(password, salt, options)` from `primitives/scrypt/scrypt.ts`, where `options` carries `N` (CPU/memory cost, must be a power of two ≥ 2), `r` (block size), `p` (parallelisation), and `dkLen` (output length in bytes). The implementation SHALL match RFC 7914 byte-for-byte and SHALL use the `@noble/hashes/scrypt::scryptAsync` engine in the noble selector. Output is a `Uint8Array` of length `dkLen`. #### Scenario: matches RFC 7914 known-answer test - **WHEN** `scrypt("password", "NaCl", { N: 1024, r: 8, p: 16, dkLen: 64 })` is called - **THEN** the output equals the RFC 7914 §11 KAT for those parameters byte-for-byte #### Scenario: invalid N rejected - **WHEN** `scrypt(pw, salt, { N: 1000, r: 8, p: 1, dkLen: 32 })` is called (1000 is not a power of two) - **THEN** it throws `SuaveplanError` with code `CRYPTO_HASH.SCRYPT_INVALID_N` and `context.N === 1000` **Verified by:** `packages/crypto/crypto-hash/src/primitives/scrypt/scrypt.test.ts::"matches RFC 7914 known-answer test for N=1024 r=8 p=16"` AND `packages/crypto/crypto-hash/src/primitives/scrypt/scrypt.spec-conformance.test.ts::"throws SCRYPT_INVALID_N when N is not a power of two"` --- ### Requirement: blake2b hash primitive The package SHALL expose `blake2b(message, options)` from `primitives/blake2b/blake2b.ts`, where `options` carries `dkLen` (1..64), optional `key` (≤ 64 bytes), optional `salt` (16 bytes), and optional `personalization` (16 bytes). The implementation SHALL match RFC 7693 byte-for-byte. #### Scenario: matches RFC 7693 known-answer test - **WHEN** `blake2b("abc", { dkLen: 64 })` is called - **THEN** the output equals the RFC 7693 §B Appendix KAT for the input "abc" byte-for-byte #### Scenario: dkLen > 64 rejected - **WHEN** `blake2b(msg, { dkLen: 65 })` is called - **THEN** it throws `SuaveplanError` with code `CRYPTO_HASH.BLAKE2B_OUTPUT_TOO_LONG` and `context.requestedLength === 65` #### Scenario: key longer than 64 bytes rejected - **WHEN** `blake2b(msg, { dkLen: 32, key: 65-byte-buffer })` is called - **THEN** it throws `SuaveplanError` with code `CRYPTO_HASH.BLAKE2B_KEY_TOO_LONG` and `context.keyLength === 65` **Verified by:** `packages/crypto/crypto-hash/src/primitives/blake2b/blake2b.test.ts::"matches RFC 7693 known-answer test for input \"abc\""` AND `packages/crypto/crypto-hash/src/primitives/blake2b/blake2b.spec-conformance.test.ts::"throws BLAKE2B_OUTPUT_TOO_LONG when dkLen exceeds 64"` AND `packages/crypto/crypto-hash/src/primitives/blake2b/blake2b.spec-conformance.test.ts::"throws BLAKE2B_KEY_TOO_LONG when key exceeds 64 bytes"` --- ### Requirement: blake2b password hashing and verification The package SHALL expose `hashPasswordBlake2b(password, options)` returning a stored hash in the format `$blake2b$v=1$cf=$$`, where `costFactor` controls iteration count (default 14, doubling each level) and a 16-byte random salt is generated when not supplied. It SHALL also expose `verifyPasswordBlake2b(storedHash, password)` returning `true` when the password verifies and `false` otherwise. Comparison SHALL be constant-time over the hash bytes. #### Scenario: hash and verify round-trip - **WHEN** `hashPasswordBlake2b("correct horse battery staple")` produces `stored` and `verifyPasswordBlake2b(stored, "correct horse battery staple")` is called - **THEN** the result is `true` #### Scenario: wrong password verifies as false - **WHEN** `verifyPasswordBlake2b(stored, "wrong password")` is called - **THEN** the result is `false` (no exception thrown) #### Scenario: salt is fresh per call - **WHEN** `hashPasswordBlake2b("same password")` is called twice - **THEN** the two stored hashes are not byte-equal (different salts → different outputs) #### Scenario: malformed stored hash rejected - **WHEN** `verifyPasswordBlake2b("not-a-valid-format", pw)` is called - **THEN** it throws `SuaveplanError` with code `CRYPTO_HASH.BLAKE2B_PASSWORD_FORMAT_INVALID` **Verified by:** `packages/crypto/crypto-hash/src/mac/blake2b-password/blake2b-password.test.ts::"hash and verify round-trip with same password"` AND `packages/crypto/crypto-hash/src/mac/blake2b-password/blake2b-password.test.ts::"verify returns false for wrong password"` AND `packages/crypto/crypto-hash/src/mac/blake2b-password/blake2b-password.test.ts::"salt is fresh per call so identical inputs hash differently"` AND `packages/crypto/crypto-hash/src/mac/blake2b-password/blake2b-password.spec-conformance.test.ts::"throws BLAKE2B_PASSWORD_FORMAT_INVALID for malformed stored hash"`