crypto-random

Implement cryptographically secure random generation using the Web Crypto API.

7 stars

Best use case

crypto-random is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Implement cryptographically secure random generation using the Web Crypto API.

Teams using crypto-random should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/crypto-random/SKILL.md --create-dirs "https://raw.githubusercontent.com/heldernoid/agentic-build-templates/main/projects/security-privacy/password-generator/skills/crypto-random/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/crypto-random/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How crypto-random Compares

Feature / Agentcrypto-randomStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Implement cryptographically secure random generation using the Web Crypto API.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# crypto-random skill

## Core primitives

All randomness in password-generator flows through two functions in `src/crypto/random.ts`:

### randomBytes

```typescript
export function randomBytes(n: number): Uint8Array {
  const buf = new Uint8Array(n);
  crypto.getRandomValues(buf);
  return buf;
}
```

### randomIndex (rejection sampling)

Naive modulo (`index % max`) introduces bias when `max` is not a power of 2. Rejection sampling eliminates this:

```typescript
export function randomIndex(max: number): number {
  if (max <= 0) throw new RangeError('max must be > 0');
  if (max === 1) return 0;

  // largest multiple of max that fits in Uint32
  const limit = Math.floor(0x100000000 / max) * max;
  const buf = new Uint32Array(1);

  for (;;) {
    crypto.getRandomValues(buf);
    if (buf[0] < limit) return buf[0] % max;
    // reject and retry (probability < 1/max that this branch is taken)
  }
}
```

This guarantees each index in `[0, max)` is selected with equal probability.

## Password generation

```typescript
export function generatePassword(opts: PasswordOptions): string {
  const charset = buildCharset(opts);
  if (charset.length === 0) {
    throw new Error('At least one character set must be selected');
  }
  return Array.from(
    { length: opts.length },
    () => charset[randomIndex(charset.length)]
  ).join('');
}
```

## Passphrase generation

Uses the EFF large wordlist (7776 words, indices 0-7775):

```typescript
import { WORDS } from '../lib/wordlist.js';

export function generatePassphrase(opts: PassphraseOptions): string {
  const words = Array.from(
    { length: opts.words },
    () => WORDS[randomIndex(WORDS.length)]
  );

  const result = words
    .map((w, i) => {
      let word = opts.capitalize ? w[0].toUpperCase() + w.slice(1) : w;
      if (opts.includeNumber && i === words.length - 1) {
        word += String(randomIndex(10));
      }
      return word;
    })
    .join(opts.separator);

  return result;
}
```

## PIN generation

```typescript
export function generatePin(length: number): string {
  return Array.from({ length }, () => String(randomIndex(10))).join('');
}
```

## Entropy calculation

```typescript
export function calculateEntropy(charsetSize: number, length: number): number {
  if (charsetSize <= 0 || length <= 0) return 0;
  return length * Math.log2(charsetSize);
}

export function passphraseEntropy(wordCount: number, wordlistSize = 7776): number {
  return wordCount * Math.log2(wordlistSize);
}

export function pinEntropy(length: number): number {
  return length * Math.log2(10);
}
```

## Strength label

```typescript
export type StrengthLabel = 'weak' | 'fair' | 'strong' | 'very-strong' | 'excellent';

export function strengthLabel(entropy: number): StrengthLabel {
  if (entropy < 40)  return 'weak';
  if (entropy < 60)  return 'fair';
  if (entropy < 80)  return 'strong';
  if (entropy < 100) return 'very-strong';
  return 'excellent';
}
```

## Crack time estimation

Assumes 10 billion (1e10) guesses per second:

```typescript
export function crackTime(entropy: number): string {
  const seconds = Math.pow(2, entropy) / 1e10;
  if (seconds < 1)          return 'instant';
  if (seconds < 60)         return `${Math.round(seconds)} seconds`;
  if (seconds < 3600)       return `${Math.round(seconds / 60)} minutes`;
  if (seconds < 86400)      return `${Math.round(seconds / 3600)} hours`;
  if (seconds < 2592000)    return `${Math.round(seconds / 86400)} days`;
  if (seconds < 31536000)   return `${Math.round(seconds / 2592000)} months`;
  if (seconds < 3.15e9)     return `${Math.round(seconds / 31536000)} years`;
  if (seconds < 3.15e12)    return 'centuries';
  return 'heat death of the universe';
}
```

## Testing randomIndex uniformity

Chi-squared test to verify `randomIndex` produces a uniform distribution:

```typescript
// src/crypto/__tests__/random.test.ts
import { test, expect } from 'vitest';
import { randomIndex } from '../random.js';

test('randomIndex is uniform over non-power-of-2 max', () => {
  const max = 90;   // typical charset size
  const samples = 90_000;
  const counts = new Array(max).fill(0);

  for (let i = 0; i < samples; i++) {
    counts[randomIndex(max)]++;
  }

  const expected = samples / max;
  const chi2 = counts.reduce((sum, c) => sum + (c - expected) ** 2 / expected, 0);

  // chi2 critical value for df=89 at p=0.001 is ~140
  expect(chi2).toBeLessThan(140);
});

test('randomIndex never returns value >= max', () => {
  for (let i = 0; i < 1000; i++) {
    const max = 37;
    expect(randomIndex(max)).toBeLessThan(max);
    expect(randomIndex(max)).toBeGreaterThanOrEqual(0);
  }
});
```

## Why `crypto.getRandomValues` and not `Math.random`

`Math.random()` is a pseudo-random number generator seeded from the system clock or a small entropy pool. Its output is predictable given enough samples. The Web Crypto API uses the operating system's CSPRNG (cryptographically secure pseudo-random number generator), which is suitable for generating secrets.

Never use `Math.random()` for password generation, key derivation, or any security-sensitive context.

## Browser compatibility

`crypto.getRandomValues` is available in all modern browsers and in Node.js >= 19 via the global `crypto` object. In Node.js < 19, import it from `node:crypto`:

```typescript
import { webcrypto } from 'node:crypto';
const crypto = webcrypto;
```

For vitest tests running in jsdom, the global `crypto` is polyfilled automatically.

Related Skills

Skill: Uptime Monitoring

7
from heldernoid/agentic-build-templates

## Overview

Skill: Status Page

7
from heldernoid/agentic-build-templates

## Overview

Skill: unit-conversion

7
from heldernoid/agentic-build-templates

## Overview

Skill: recipe-scaler

7
from heldernoid/agentic-build-templates

## Overview

reading-list

7
from heldernoid/agentic-build-templates

Operate the reading-list API to save, manage, tag, search, and export articles.

email-digest

7
from heldernoid/agentic-build-templates

Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.

websocket-realtime

7
from heldernoid/agentic-build-templates

Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".

poll-builder

7
from heldernoid/agentic-build-templates

Self-hosted poll creation tool with real-time results. Use when you need to create a poll, check vote counts, close a poll, export results, or get the shareable link for a poll. Triggers include "create poll", "vote", "poll results", "survey", "collect votes", "share poll", or any task involving polling or voting.

Skill: personal-finance

7
from heldernoid/agentic-build-templates

## Overview

Skill: csv-import

7
from heldernoid/agentic-build-templates

## Overview

Skill: Syntax Highlighting

7
from heldernoid/agentic-build-templates

## Purpose

Skill: Pastebin Core

7
from heldernoid/agentic-build-templates

## Purpose