Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .kiro/specs/crypto-utilities-package/.config.kiro
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"specId": "f09d6234-a0e2-4cd6-897c-ffb58e9d1e76", "workflowType": "requirements-first", "specType": "feature"}
261 changes: 261 additions & 0 deletions .kiro/specs/crypto-utilities-package/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
# Design Document: @ancore/crypto Package Integration

## Overview

The `@ancore/crypto` package is the single cryptographic entry point for the Ancore wallet monorepo. Currently it is a stub that exports only `CRYPTO_VERSION` and `verifySignature`. This design covers wiring the package together: updating `packages/crypto/src/index.ts` to re-export all public symbols from every submodule, and adding a smoke test that verifies the export surface is complete and correct.

The scope is **integration and export correctness only**. The internal logic of each submodule (`signing.ts`, `hashing.ts`, `keys.ts`, etc.) is implemented in separate issues (#065–#072). This design assumes those submodules will exist on disk when the index is updated.

Key design goals:

- One import path (`@ancore/crypto`) for all consumers — no internal path leakage.
- Clean TypeScript build producing CJS, ESM, and `.d.ts` outputs via `tsup`.
- A smoke test that acts as a living manifest of the public API surface.
- Zero secret material in logs or error messages.

---

## Architecture

The package follows a **barrel export** pattern. Each submodule owns its domain and explicitly marks its public surface with named exports. The index file is a pure re-export aggregator — it contains no logic of its own.

```mermaid
graph TD
Consumer["Consumer\n(@ancore/core-sdk, apps, etc.)"]
Index["index.ts\n(barrel — re-exports only)"]
Signing["signing.ts\n(verifySignature, signMessage)"]
Hashing["hashing.ts\n(sha256, sha512, hmac)"]
Keys["keys.ts\n(deriveKeyPair, publicKeyFromSecret)"]
Mnemonic["mnemonic.ts\n(generateMnemonic, mnemonicToSeed)"]
Encoding["encoding.ts\n(toHex, fromHex, toBase64, fromBase64)"]

Consumer -->|"import { ... } from '@ancore/crypto'"| Index
Index --> Signing
Index --> Hashing
Index --> Keys
Index --> Mnemonic
Index --> Encoding
```

Build pipeline:

```mermaid
graph LR
TS["TypeScript sources\n(src/*.ts)"]
tsup["tsup"]
CJS["dist/index.js\n(CommonJS)"]
ESM["dist/index.mjs\n(ESM)"]
DTS["dist/index.d.ts\n(declarations)"]

TS --> tsup --> CJS
tsup --> ESM
tsup --> DTS
```

---

## Components and Interfaces

### index.ts — Barrel Export

The index is the only file consumers interact with. Its structure is:

```typescript
export const CRYPTO_VERSION = '0.1.0';

export * from './signing';
export * from './hashing';
export * from './keys';
export * from './mnemonic';
export * from './encoding';
```

Rules:

- Only `export *` or named `export { ... }` from submodules — no logic.
- If a submodule has name collisions, use explicit named re-exports with aliases.
- Internal helpers (e.g., `toMessageBytes`, `isHex`) must **not** be exported from their submodule's public surface.

### Submodule Contract

Each submodule must follow this contract so the barrel works correctly:

| Rule | Detail |
| ---------------------------- | --------------------------------------------------------------- |
| Named exports only | No default exports — enables `export *` without ambiguity |
| No console calls | `console.log/warn/error` are forbidden in production code paths |
| No secret material in errors | Error messages must not include key bytes, seeds, or mnemonics |
| Pure functions preferred | Side-effect-free functions are easier to test and tree-shake |

### Expected Submodules and Their Public Symbols

The table below defines the intended public API surface. It will be updated as issues #065–#072 land.

| Submodule | Public Exports |
| ------------- | -------------------------------------------- |
| `signing.ts` | `verifySignature`, `signMessage` |
| `hashing.ts` | `sha256`, `sha512`, `hmac` |
| `keys.ts` | `deriveKeyPair`, `publicKeyFromSecret` |
| `mnemonic.ts` | `generateMnemonic`, `mnemonicToSeed` |
| `encoding.ts` | `toHex`, `fromHex`, `toBase64`, `fromBase64` |
| `index.ts` | `CRYPTO_VERSION` + all of the above |
Comment on lines +94 to +101
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check what signing.ts actually exports
echo "=== Exports from signing.ts ==="
rg -n "^export (async )?function" packages/crypto/src/signing.ts

Repository: ancore-org/ancore

Length of output: 176


🏁 Script executed:

#!/bin/bash
# Check what index.ts exports related to signing
echo "=== Exports from index.ts related to signing ==="
rg -n "export.*sign" packages/crypto/src/index.ts

Repository: ancore-org/ancore

Length of output: 173


Design specifies signMessage but implementation exports signTransaction.

Line 96 in the Expected Submodules table lists signMessage as a public export from signing.ts, but the actual implementation exports signTransaction instead. This discrepancy should be reconciled — either:

  1. Update signing.ts to export signMessage (or both)
  2. Update the design doc to reflect signTransaction
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.kiro/specs/crypto-utilities-package/design.md around lines 94 - 101, The
design lists signing.ts exporting signMessage but the implementation currently
exports signTransaction; reconcile by adding or renaming so the API matches the
design: update signing.ts to export a signMessage function (and optionally
export signTransaction as an alias) and update index.ts to re-export signMessage
(or change the design doc if you prefer signTransaction), ensuring both the
module export names (signing.ts: signMessage, signTransaction) and the public
re-exports in index.ts/CRYPTO_VERSION reflect the chosen name consistently.


### Smoke Test — `__tests__/smoke.test.ts`

The smoke test is a living manifest of the public API. It:

1. Imports the entire namespace from `@ancore/crypto`.
2. Asserts every expected symbol is defined.
3. Invokes at least one async function with valid inputs.
4. Spies on `console` methods to assert no output occurs.

```typescript
import * as CryptoAPI from '@ancore/crypto';

const EXPECTED_EXPORTS = [
'CRYPTO_VERSION',
'verifySignature',
'signMessage',
'sha256',
'sha512',
'hmac',
'deriveKeyPair',
'publicKeyFromSecret',
'generateMnemonic',
'mnemonicToSeed',
'toHex',
'fromHex',
'toBase64',
'fromBase64',
] as const;
```

---

## Data Models

This package is a utility library — it has no persistent state or database models. The relevant data types are:

```typescript
// Shared input type for signable values
type SignableValue = string | Uint8Array;

// Key pair returned by key derivation
interface KeyPair {
publicKey: string; // Stellar-encoded G... address
secretKey: string; // Stellar-encoded S... secret
}

// Result of mnemonic-to-seed derivation
interface SeedResult {
seed: Uint8Array; // 64-byte BIP39 seed
mnemonic: string; // space-separated word list
}
```

These types are defined in their respective submodules and re-exported through the index. They may also be shared with `@ancore/types` if needed by other packages.

---

## Correctness Properties

_A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees._

### Property 1: All expected exports are defined

_For any_ symbol in the declared public API list, importing that symbol from `@ancore/crypto` should yield a value that is not `undefined`.

**Validates: Requirements 1.1, 1.3, 3.1, 5.2**

### Property 2: Export set matches the public API exactly

_For any_ key present in the module namespace imported from `@ancore/crypto`, that key should appear in the declared public API list — and conversely, every key in the declared list should appear in the namespace. The sets are equal.

**Validates: Requirements 1.4, 4.3**

### Property 3: No console output during normal operation

_For any_ call to an exported function with valid inputs, the `console.log`, `console.warn`, and `console.error` methods should not be invoked.

**Validates: Requirements 3.3, 4.1**

### Property 4: Error messages do not contain secret material

_For any_ exported function called with an invalid input alongside a known secret value (e.g., a random 32-byte seed), any error thrown or rejection returned should not include the secret value in its message string.

**Validates: Requirements 4.2**

### Property 5: Module resolution is idempotent

_For any_ named export from `@ancore/crypto`, importing the same symbol twice (in the same process) should yield the same reference (`===` equality), confirming stable module identity.

**Validates: Requirements 5.3**

---

## Error Handling

| Scenario | Behavior |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Invalid public key passed to `verifySignature` | Returns `false` (already implemented) — never throws |
| Malformed hex/base64 in encoding functions | Throws a `TypeError` with a message describing the format issue, not the input value |
| Invalid mnemonic passed to `mnemonicToSeed` | Throws a typed `CryptoError` with a safe message |
| Missing submodule at build time | `tsup` / `tsc` fails with a module-not-found error identifying the missing file |
| Secret material in error path | Forbidden — error messages must use placeholders like `"invalid key"`, never the key bytes |

A `CryptoError` class (or discriminated union) may be introduced in a future issue to give consumers typed error handling. For now, functions either return a safe value (like `false`) or throw a plain `Error` with a safe message.

---

## Testing Strategy

### Dual Approach

Both unit tests and property-based tests are used. They are complementary:

- **Unit tests** cover specific examples, integration points, and known edge cases.
- **Property tests** verify universal invariants across randomly generated inputs.

### Unit Tests

Located in `packages/crypto/src/__tests__/`. Existing tests (`verify-signature.test.ts`) remain unchanged. New file: `smoke.test.ts`.

Unit test focus areas:

- Specific valid/invalid input examples for each exported function.
- Edge cases: empty strings, zero-length buffers, all-zero keys.
- Error path assertions: confirm safe error messages.

### Property-Based Tests

Library: **`fast-check`** (TypeScript-native, works with Jest via `fc.assert`).

Install: `pnpm add -D fast-check --filter @ancore/crypto`

Configuration: each property test runs a minimum of **100 iterations**.

Each test is tagged with a comment referencing the design property:

```typescript
// Feature: crypto-utilities-package, Property 1: All expected exports are defined
fc.assert(
fc.property(fc.constantFrom(...EXPECTED_EXPORTS), (symbol) => {
expect(CryptoAPI[symbol]).toBeDefined();
}),
{ numRuns: 100 }
);
```

### Property Test Mapping

| Design Property | Test Description | Pattern |
| --------------- | ------------------------------------------------------------------- | ------------------------ |
| Property 1 | For each symbol in EXPECTED_EXPORTS, it is defined in the namespace | Invariant |
| Property 2 | Namespace keys === EXPECTED_EXPORTS (no extras, no missing) | Invariant |
| Property 3 | No console calls during valid function invocations | Invariant |
| Property 4 | Error messages from invalid inputs don't contain the secret value | Error condition |
| Property 5 | Same symbol imported twice yields `===` reference | Idempotence / Round-trip |

### CI Integration

The existing `jest` setup in `jest.config.cjs` picks up all `*.test.ts` files under `src/__tests__/`. No additional infrastructure is needed — `fast-check` integrates directly with Jest's `it`/`test` blocks.
81 changes: 81 additions & 0 deletions .kiro/specs/crypto-utilities-package/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Requirements Document

## Introduction

The `@ancore/crypto` package provides cryptographic utilities for the Ancore wallet. Currently the package is a stub — only `CRYPTO_VERSION` and `verifySignature` are exported. This feature wires together all cryptographic submodules (signing, hashing, key derivation, etc., implemented in separate issues #065–#072) and exposes a clean, stable public API surface from `packages/crypto/src/index.ts`. The scope here is integration and export correctness, not the internal logic of each submodule.

## Glossary

- **Package**: The `@ancore/crypto` npm package located at `packages/crypto`.
- **Index**: The file `packages/crypto/src/index.ts` — the single public entry point of the Package.
- **Submodule**: A TypeScript source file inside `packages/crypto/src/` that implements a cohesive group of cryptographic functions (e.g., `signing.ts`, `hashing.ts`, `keys.ts`).
- **Public_API**: The set of functions, types, and constants re-exported from the Index.
- **Consumer**: Any package or application that imports from `@ancore/crypto`.
- **Secret_Material**: Private keys, seed phrases, raw entropy, or any value that must not be logged or exposed outside its intended scope.
- **Smoke_Test**: A lightweight test that imports from the Index and asserts that exported symbols are callable and return expected types, without exercising full cryptographic correctness.
- **Build**: The TypeScript compilation and bundling step executed via `tsup`.

---

## Requirements

### Requirement 1: Public API Surface

**User Story:** As a Consumer, I want all cryptographic utilities to be importable from `@ancore/crypto`, so that I do not need to reference internal submodule paths.

#### Acceptance Criteria

1. THE Index SHALL re-export every public function and type from each Submodule present in `packages/crypto/src/`.
2. THE Index SHALL export `CRYPTO_VERSION` as a string constant.
3. WHEN a Consumer imports a symbol from `@ancore/crypto`, THE Package SHALL resolve that symbol without requiring the Consumer to reference any internal Submodule path.
4. THE Index SHALL NOT export any symbol that is not part of the intended Public_API (i.e., internal helpers remain unexported).

---

### Requirement 2: Build Integrity

**User Story:** As a developer, I want the package to compile cleanly, so that downstream packages can depend on `@ancore/crypto` without build failures.

#### Acceptance Criteria

1. WHEN the Build is executed, THE Package SHALL produce no TypeScript compiler errors.
2. WHEN the Build is executed, THE Package SHALL produce no missing-import or missing-export errors.
3. THE Package SHALL generate CommonJS (`dist/index.js`), ESM (`dist/index.mjs`), and TypeScript declaration (`dist/index.d.ts`) outputs.
4. IF a Submodule referenced in the Index does not exist on disk, THEN THE Build SHALL fail with a descriptive error identifying the missing Submodule.

---

### Requirement 3: Smoke Test

**User Story:** As a developer, I want a smoke test that verifies the wiring of exports, so that integration regressions are caught immediately.

#### Acceptance Criteria

1. THE Smoke_Test SHALL import each symbol exported from the Index and assert that the symbol is defined (not `undefined`).
2. THE Smoke_Test SHALL invoke at least one exported async function with valid inputs and assert that it resolves without throwing.
3. WHEN the Smoke_Test is executed, THE Package SHALL not log any output to `console.log`, `console.warn`, or `console.error`.
4. THE Smoke_Test SHALL pass within the existing Jest test suite without requiring additional test infrastructure.

---

### Requirement 4: No Secret Material Exposure

**User Story:** As a security reviewer, I want the package to never log or expose Secret Material, so that private keys and seeds cannot be leaked through observability tooling.

#### Acceptance Criteria

1. THE Package SHALL NOT call `console.log`, `console.warn`, `console.error`, or any equivalent logging function with Secret_Material as an argument.
2. IF an error occurs during a cryptographic operation, THEN THE Package SHALL return an error result or throw a typed error WITHOUT including Secret_Material in the error message.
3. THE Index SHALL NOT re-export any internal utility whose sole purpose is to handle or transform raw Secret_Material in an unprotected form.

---

### Requirement 5: Export Completeness Verification

**User Story:** As a developer, I want a way to verify that all intended exports are present after submodules are added, so that accidental omissions are caught during CI.

#### Acceptance Criteria

1. WHEN a new Submodule is added to `packages/crypto/src/`, THE Index SHALL be updated to include a re-export of that Submodule's public symbols.
2. THE Smoke_Test SHALL enumerate and assert the presence of each named export defined in the Public_API, so that a missing re-export causes a test failure.
3. FOR ALL symbols asserted in the Smoke_Test, importing then re-importing the same symbol from `@ancore/crypto` SHALL resolve to the same reference (idempotent module resolution).
Loading