npm package: @smartledger/keys
Description: Key management SDK for Lumen cryptographic operations - Now with proper ML-DSA-44/65/87 implementations
Author: Gregory Ward
Latest version: 2.0.0

# @smartledger/keys SDK Documentation

**Single source of truth for cryptographic key operations.**

Version: 1.5.1  
Status: ✅ Production Ready  
Tests: 52/52 passing

**New in 1.5.1:** All three ML-DSA security levels now available (44/65/87)

---

## Why This SDK?

Keys are the **root of everything** in your crypto architecture:
- Identity
- Signatures  
- PQ migration
- Regulatory/audit story

Without a clean SDK layer, key handling becomes **ad hoc** and **brittle**:
- ❌ Random `generateKeypair()` implementations scattered across repos
- ❌ Slightly different formats (hex here, base64 there)
- ❌ Surprise refactors when adding PQ suites
- ❌ Private keys exposed to calling code

**With `@smartledger/keys`**, all your agents/modules/bridge code simply ask:
```typescript
"Give me a signing key of suite X"
"Sign this payload with keyId Y"
```

And **don't care** how keys are stored, rotated, or implemented.

---

## Design Principles

1. **Private keys hidden** - Never exposed to most callers
2. **Crypto agility** - Suite is config, not code  
3. **Single entry point** - Easy to audit
4. **Idempotent operations** - Safe for retries
5. **Minimal API surface** - Small, focused, easy to learn

---

## Installation

```bash
npm install @smartledger/keys
```

### Supported ML-DSA Variants

Choose the right security level for your use case:

| Variant | NIST Level | Equivalent | Use Case |
|---------|------------|------------|----------|
| **ml-dsa-44** | Level 2 | AES-128 | IoT devices, high-throughput systems |
| **ml-dsa-65** | Level 3 | AES-192 | **General purpose (recommended)** |
| **ml-dsa-87** | Level 5 | AES-256 | High-security, long-term confidential data |

---

## Quick Start

```typescript
import { createKeySDK } from '@smartledger/keys';

// One line to get a ready-to-use SDK with ML-DSA + ECDSA suites registered
const sdk = createKeySDK();

// Create both PQ + ECDSA keys for an agent
// Using ml-dsa-65 (Level 3) - recommended for most use cases
const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
  'agent-resonance',
  {
    primarySignatureSuite: 'ml-dsa-65',  // or 'ml-dsa-44', 'ml-dsa-87'
    secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
  }
);

// Sign with both suites (private keys stay hidden)
const message = new TextEncoder().encode('agent output');
const { signatures } = await sdk.signWithSuites('agent-resonance', message);

// Verify both signatures
const verify = await sdk.verifyWithSuites('agent-resonance', message, signatures);
console.log(verify.allValid); // true
```

---

## API Reference

### Factory Helper

`createKeySDK(config?)` returns a ready-to-use SDK wired with in-memory storage and the default ML-DSA + ECDSA suites. Pass your own `keyRegistry` or `suiteRegistry` to override defaults.

### KeySDK Interface

#### `createKey(agentId, profile, options?)`

Create a new key for an agent/module.

**Parameters:**
- `agentId` (string) - Agent identifier (e.g. `'agent-resonance'`)
- `profile` (CryptoProfile) - Which signature suites to use
- `options` (CreateKeyOptions) - Optional expiration, usage, etc.

**Returns:** `Promise<KeyRecord>` - KeyRecord with metadata and public key

**Example:**
```typescript
const key = await sdk.createKey(
  'agent-schema',
  { primarySignatureSuite: 'ml-dsa-65' },  // Recommended: Level 3
  {
    expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(), // 1 year
    usage: ['signing'],
  }
);
```

---

#### `getKey(keyId)`

Get an existing key by ID.

**Parameters:**
- `keyId` (string) - Key identifier

**Returns:** `Promise<KeyRecord | null>` - KeyRecord or null if not found

**Example:**
```typescript
const key = await sdk.getKey('agent-resonance:pk-ml-1');
if (key) {
  console.log(key.meta.suiteId); // 'ml-dsa-65' or 'ml-dsa-44', 'ml-dsa-87'
  console.log(key.publicKey); // Uint8Array
}
```

---

#### `signWithKey(keyId, message)`

Sign a message with a key. **Private key never leaves the SDK.**

**Parameters:**
- `keyId` (string) - Which key to sign with
- `message` (Uint8Array) - Message to sign (will be hashed internally)

**Returns:** `Promise<Uint8Array>` - Signature bytes

**Throws:** Error if key not found or not active

**Example:**
```typescript
const message = new TextEncoder().encode('agent output');
const signature = await sdk.signWithKey('agent-resonance:pk-ml-1', message);
```

---

#### `verifySignature(keyId, message, signature)`

Verify a signature against a public key.

**Parameters:**
- `keyId` (string) - Which key to verify with
- `message` (Uint8Array) - Original message
- `signature` (Uint8Array) - Signature to verify

**Returns:** `Promise<boolean>` - true if valid

**Example:**
```typescript
const valid = await sdk.verifySignature(
  'agent-resonance:pk-ml-1',
  message,
  signature
);
```

---

#### `listKeysForAgent(agentId, activeOnly?)`

List all keys for an agent.

**Parameters:**
- `agentId` (string) - Agent identifier
- `activeOnly` (boolean) - If true, only return active keys (default: true)

**Returns:** `Promise<KeyRecord[]>` - Array of KeyRecords

**Example:**
```typescript
const keys = await sdk.listKeysForAgent('agent-schema');
for (const key of keys) {
  console.log(`${key.meta.keyId} (${key.meta.suiteId})`);
}
```

---

#### `getActiveCryptoProfile(agentId)`

Get the active crypto profile for an agent.

Looks up the most recent active keys and infers the profile.

**Parameters:**
- `agentId` (string) - Agent identifier

**Returns:** `Promise<CryptoProfile | null>` - CryptoProfile or null if no keys found

**Example:**
```typescript
const profile = await sdk.getActiveCryptoProfile('agent-schema');
console.log(profile.primarySignatureSuite); // 'ml-dsa-65'
console.log(profile.secondarySignatureSuite); // 'bsv-ecdsa-secp256k1'
```

---

#### `rotateAgentKeys(agentId)`

Rotate an agent's keys.

Generates new keypairs for all active keys, marks old ones as rotated.

**Parameters:**
- `agentId` (string) - Agent identifier

**Returns:** `Promise<KeyRecord[]>` - Array of new KeyRecords

**Example:**
```typescript
const newKeys = await sdk.rotateAgentKeys('agent-schema');
for (const key of newKeys) {
  console.log(`New: ${key.meta.keyId}`);
  console.log(`Rotated from: ${key.meta.rotatedFrom}`);
}
```

---

#### `getOrCreateKey(agentId, profile, options?)`

Create key if it doesn't exist, otherwise return existing.

**Idempotent** key creation for agent setup.

**Parameters:**
- `agentId` (string) - Agent identifier
- `profile` (CryptoProfile) - Crypto configuration
- `options` (CreateKeyOptions) - Creation options

**Returns:** `Promise<KeyRecord>` - KeyRecord (new or existing)

**Example:**
```typescript
// Safe to call multiple times
const key1 = await sdk.getOrCreateKey('agent-validator', {
  primarySignatureSuite: 'ml-dsa-65',
});

const key2 = await sdk.getOrCreateKey('agent-validator', {
  primarySignatureSuite: 'ml-dsa-65',
});

console.log(key1.meta.keyId === key2.meta.keyId); // true
```

---

#### `createDualSignatureKeys(agentId, profile, options?)`

Create both primary and secondary keys in one call. Requires `profile.secondarySignatureSuite`.

**Returns:** `{ primaryKey, secondaryKey }`

**Example:**
```typescript
const { primaryKey, secondaryKey } = await sdk.createDualSignatureKeys(
  'agent-bridge',
  {
    primarySignatureSuite: 'ml-dsa-65',  // Recommended
    secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
  }
);
```

---

#### `getOrCreateDualSignatureKeys(agentId, profile, options?)`

Idempotent version of the above; reuses active keys when present.

**Returns:** `{ primaryKey, secondaryKey }`

---

#### `signWithSuites(agentId, message, suites?)`

Sign once per suite. When `suites` is omitted, active primary + secondary are used.

**Returns:** `{ signatures: Array<{ suiteId, keyId, signature }> }`

**Example:**
```typescript
const message = new TextEncoder().encode('hybrid payload');
const { signatures } = await sdk.signWithSuites('agent-bridge', message);
```

---

#### `verifyWithSuites(agentId, message, signatures)`

Verify multiple signatures and get per-suite results.

**Returns:** `{ results: Array<{ suiteId, keyId, valid }>, allValid: boolean }`

**Example:**
```typescript
const verify = await sdk.verifyWithSuites('agent-bridge', message, signatures);
console.log(verify.allValid); // true when every suite verifies
```
---

---

## Types

### KeyRecord

Public key record (safe to expose, no private key).

```typescript
interface KeyRecord {
  meta: KeyMeta;          // Key metadata
  publicKey: Uint8Array;  // Public key bytes
}
```

### CreateKeyOptions

Options for key creation.

```typescript
interface CreateKeyOptions {
  expiresAt?: string;                              // ISO 8601
  usage?: Array<'signing' | 'encryption' | 'both'>; // Default: ['signing']
  cryptoProfileVersion?: string;                    // Default: '1.0.0'
}
```

### CryptoProfile

Per-agent/module crypto configuration.

```typescript
interface CryptoProfile {
  primarySignatureSuite: string;     // e.g. 'ml-dsa-65' (recommended), 'ml-dsa-44', 'ml-dsa-87'
  secondarySignatureSuite?: string;  // e.g. 'bsv-ecdsa-secp256k1' (hybrid)
  keyEncapsulationSuite?: string;    // e.g. 'ml-kem-768' (future)
}
```

---

## Usage Patterns

### Single Signature (PQ-only)

```typescript
// Agent uses only ML-DSA (recommended: ml-dsa-65)
const key = await sdk.createKey('agent-research', {
  primarySignatureSuite: 'ml-dsa-65',  // or 'ml-dsa-44' for IoT, 'ml-dsa-87' for max security
});

const message = new TextEncoder().encode('research results');
const signature = await sdk.signWithKey(key.meta.keyId, message);
```

### Hybrid Signature (ECDSA + PQ)

```typescript
// One call to create both keys
await sdk.getOrCreateDualSignatureKeys('agent-bridge', {
  primarySignatureSuite: 'ml-dsa-65',  // Balanced security (recommended)
  secondarySignatureSuite: 'bsv-ecdsa-secp256k1',
});

// Sign with both suites
const message = new TextEncoder().encode('bridge output');
const { signatures } = await sdk.signWithSuites('agent-bridge', message);

// Verify both signatures
const verified = await sdk.verifyWithSuites('agent-bridge', message, signatures);
console.log(verified.allValid); // true
```

### Agent Setup (Idempotent)

```typescript
// Safe to call on every startup
async function setupAgent(agentId: string) {
  const key = await sdk.getOrCreateKey(agentId, {
    primarySignatureSuite: 'ml-dsa-65',  // Recommended default
  }, {
    expiresAt: new Date(Date.now() + 365 * 86400000).toISOString(),
  });
  
  return key;
}
```

### Monthly Key Rotation

```typescript
// Automated rotation job
async function monthlyRotation() {
  const agents = ['agent-resonance', 'agent-schema', 'agent-validator'];
  
  for (const agentId of agents) {
    const newKeys = await sdk.rotateAgentKeys(agentId);
    console.log(`✓ Rotated ${agentId}: ${newKeys.length} keys`);
  }
}
```

---

## Benefits

### 🔒 Security

- **Private keys hidden** - Never exposed to calling code
- **Single audit point** - All key operations go through SDK
- **No key leakage** - Keys stay in KeyRegistry storage
- **Consistent hashing** - Suite-appropriate algorithms

### 🔄 Crypto Agility

- **Suite is config** - Change ML-DSA → Falcon without code changes
- **Profile-driven** - CryptoProfile defines agent's crypto setup
- **Easy migration** - Hybrid mode for ECDSA → PQ transition

### 🛠️ Developer Experience

- **Clean API** - 8 methods, easy to learn
- **Idempotent** - `getOrCreateKey` safe for retries
- **TypeScript** - Full type safety
- **Well-tested** - 23 tests covering all scenarios

### 📊 Operations

- **Centralized** - One SDK for all agents
- **Auditable** - Easy to track key operations
- **Scalable** - Storage backends (in-memory, file, KMS)
- **Rotation-friendly** - Built-in rotation support

---

## Best Practices

### ✅ Do

- Use SDK for **all** key operations
- Call `getOrCreateKey` for idempotent setup
- Set expiration dates on keys
- Rotate keys regularly (monthly/quarterly)
- Use `signWithKey` (not direct registry access)

### ❌ Don't

- Expose private keys to calling code
- Create keys manually outside SDK
- Hard-code suite IDs in business logic
- Skip key rotation

---

## Integration with Existing Code

### Before (Direct Registry)

```typescript
// Scattered key operations
const suite = globalRegistry.getSuite('ml-dsa-87');
const keypair = await suite.generateKeypair();
await keyRegistry.registerKey('agent-1:pk-1', keypair, 'ml-dsa-87');

const key = await keyRegistry.getKey('agent-1:pk-1');
const sig = await suite.sign(key.keypair.privateKey, message);
```

### After (SDK)

```typescript
// Clean, centralized
const key = await sdk.createKey('agent-1', {
  primarySignatureSuite: 'ml-dsa-65',  // Choose: 'ml-dsa-44', '65', or '87'
});

const sig = await sdk.signWithKey(key.meta.keyId, message);
```

---

## What's Next?

- **File Storage** - Persistent key storage (not just in-memory)
- **KMS Integration** - AWS KMS, Azure Key Vault, HSM
- **Key Backup** - Encrypted backup/recovery procedures
- **Multi-tenant** - Isolate keys per tenant
- **Hardware Keys** - Support for non-exportable hardware keys

---

## Summary

**`@smartledger/keys` is the single source of truth for cryptographic keys:**

✅ 23 tests passing  
✅ Private keys hidden from callers  
✅ Crypto-agnostic API (suite is config)  
✅ Idempotent operations  
✅ Easy to audit  
✅ Production-ready  

**Use it for all key operations. Make crypto agility a reality.**

---

**See Also:**
- [Key Management Guide](../docs/KEY_MANAGEMENT_GUIDE.md) - Lower-level KeyRegistry details
- [Algorithm Selection Guide](../docs/ALGORITHM_SELECTION_GUIDE.md) - Which suite to use?
- [ECDSA Implementation Strategy](../docs/ECDSA_IMPLEMENTATION_STRATEGY.md) - Why Noble

---

**Version**: 1.0.0  
**Last Updated**: November 28, 2025