crypto-random
Implement cryptographically secure random generation using the Web Crypto API.
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
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/crypto-random/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How crypto-random Compares
| Feature / Agent | crypto-random | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/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
## Overview
Skill: Status Page
## Overview
Skill: unit-conversion
## Overview
Skill: recipe-scaler
## Overview
reading-list
Operate the reading-list API to save, manage, tag, search, and export articles.
email-digest
Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.
websocket-realtime
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
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
## Overview
Skill: csv-import
## Overview
Skill: Syntax Highlighting
## Purpose
Skill: Pastebin Core
## Purpose