# crypto-utils ## Purpose `@suaveplan/crypto-utils` is the cross-package crypto-helper layer for the Suaveplan monorepo. It owns the pure-composition utilities every primitive package builds on: hex / base64 / base64url / base58 encoding (delegated to `@suaveplan/bytes` where overlap exists), byte helpers (`concatBytes`, `i2osp`, `os2ip`, `xor`), constant-time equality, cryptographically-secure `randomBytes`, eager `zeroize`, the `PrimitiveId` registry + `PrimitiveNotImplemented` error, and the KAT / differential-fuzz test harness consumed by every `@suaveplan/crypto-*` engine selector. The package is deliberately field-arithmetic-free and side-channel-clean (no secret-data branches beyond constant-time compare) so it can ship as the universal-tier dependency for `crypto-cipher`, `crypto-curve`, `crypto-hash`, and the higher-level `crypto` package. ## Requirements ### Requirement: Pure-composition utility surface `@suaveplan/crypto-utils` SHALL expose encoding, byte, constant-time, and test-harness utilities that are safe to author from scratch (no field arithmetic, no secret-data side channels beyond constant-time equality). #### Scenario: Encoding utilities available - **WHEN** a consumer imports from `@suaveplan/crypto-utils` - **THEN** `hex`, `base64`, `base64url`, `base58` encoders/decoders are exported, each with `encode(bytes): string` and `decode(text): Uint8Array` - **AND** `toHex` / `fromHex` and `toBase64` / `fromBase64` delegate to `@suaveplan/bytes`; decoders throw `BytesError` (a `SuaveplanError` subclass) on malformed input - **AND** `toBase64Url` / `fromBase64Url` are implemented in-package by post-processing the standard base64 result ### Requirement: Encoding delegation to @suaveplan/bytes `@suaveplan/crypto-utils` SHALL delegate `toHex` / `fromHex` and `toBase64` / `fromBase64` to `@suaveplan/bytes`, and `concatBytes` to `concatUint8Arrays` from `@suaveplan/bytes`. The exported function names are preserved so downstream callers need no import changes. #### Scenario: Hex delegates to bytes - **WHEN** `toHex` or `fromHex` is called - **THEN** the call is forwarded to `bytesToHex` / `hexToBytes` from `@suaveplan/bytes` - **AND** errors thrown are `BytesError` instances with code `BYTES.ENCODE.INVALID_HEX` #### Scenario: Standard base64 delegates to bytes - **WHEN** `toBase64` or `fromBase64` is called - **THEN** the call is forwarded to `bytesToBase64` / `base64ToBytes` from `@suaveplan/bytes` - **AND** errors thrown are `BytesError` instances with code `BYTES.ENCODE.INVALID_BASE64` #### Scenario: concatBytes delegates to bytes - **WHEN** `concatBytes` is called - **THEN** the call is forwarded to `concatUint8Arrays` from `@suaveplan/bytes` #### Scenario: Byte utilities available - **WHEN** a consumer imports from `@suaveplan/crypto-utils` - **THEN** `concatBytes(...arrays: Uint8Array[]): Uint8Array`, `xor(a: Uint8Array, b: Uint8Array): Uint8Array`, `i2osp(n: bigint, len: number): Uint8Array`, `os2ip(bytes: Uint8Array): bigint` are exported #### Scenario: Constant-time, random, and zeroize utilities available - **WHEN** a consumer imports from `@suaveplan/crypto-utils` - **THEN** `constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean` is exported and implemented without early return - **AND** `randomBytes(n: number): Uint8Array` is exported, sourcing from `globalThis.crypto.getRandomValues` - **AND** `zeroize(bytes: Uint8Array): void` is exported ### Requirement: Test-vector and fuzz harness `@suaveplan/crypto-utils` SHALL expose harness functions that `crypto-hash`, `crypto-curve`, and `crypto-cipher` use to run KATs and differential fuzz against their primitive impls. #### Scenario: KAT harness - **WHEN** a primitive test imports `runVectors` from `@suaveplan/crypto-utils/testing` - **THEN** it can pass a vector list and a set of impls, and receive a per-impl pass/fail/pending report - **AND** pending impls (throwing `PrimitiveNotImplemented`) are distinguished from failing impls #### Scenario: Differential fuzz harness - **WHEN** a primitive test imports `diffFuzz` from `@suaveplan/crypto-utils/testing` - **THEN** it can pass two impls, an input generator, and an iteration count, and receive a mismatch report - **AND** any byte-level divergence in outputs is reported with the reproducing input ### Requirement: Primitive id registry `@suaveplan/crypto-utils` SHALL export the `PrimitiveId` union type enumerating every hash, curve, and AEAD primitive across the crypto family, and a `PRIMITIVE_IDS` const array for runtime iteration. #### Scenario: Registry available - **WHEN** a consumer imports `PrimitiveId` and `PRIMITIVE_IDS` from `@suaveplan/crypto-utils/primitive-id` - **THEN** `PRIMITIVE_IDS` contains every id supported by `crypto-hash`, `crypto-curve`, and `crypto-cipher` - **AND** `PrimitiveId` narrows to the same union at compile time ### Requirement: No vendored code `@suaveplan/crypto-utils` SHALL contain only original code authored in this monorepo. No `src/vendored/` directory. #### Scenario: No vendored directory - **WHEN** the package tree is inspected - **THEN** no path matches `packages/universal/crypto-utils/src/vendored/**` ### Requirement: Merkle helpers `@suaveplan/crypto-utils` SHALL expose Merkle tree construction and inclusion-proof helpers generic over any hash primitive from `@suaveplan/crypto-hash`. #### Scenario: Merkle helpers available - **WHEN** a consumer imports `merkleRoot` and `merkleProof` from `@suaveplan/crypto-utils/merkle` - **THEN** they accept a hash function parameter and a list of leaves, returning a root and an inclusion proof respectively