Skip to content

ammmcreativetech-dot/unbiased-random-string

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

unbiased-random-string

Cryptographically secure random strings with rejection sampling — no modulo bias, no dependencies, works the same in the browser, Node.js and edge runtimes.

types dependencies license isomorphic

Two subtle bugs hide in most hand-rolled token generators:

  1. Math.random() is predictable. V8's PRNG (xorshift128+) can be reconstructed from a few outputs — so Math.random()-based passwords, invite codes and share IDs are guessable.
  2. byte % charsetLen is biased. When the charset size doesn't divide 256 evenly (e.g. 62 or 36 chars), the lower characters appear slightly more often. Over millions of codes that's a real, exploitable skew.

unbiased-random-string fixes both: it draws from the Web Crypto CSPRNG (crypto.getRandomValues) and uses rejection sampling so every character is equally likely.

npm install unbiased-random-string

Usage

import { randomString, randomId, randomCode, UNAMBIGUOUS, ALPHANUMERIC } from 'unbiased-random-string';

randomId();                    // "Xk7Qp2mZ9rLa"  — 12-char alphanumeric (~71 bits)
randomId(21);                  // longer id
randomCode();                  // "K7P2QM"        — 6-char human-typable code (no 0/O, 1/l/I)
randomString(32);              // 32 chars from the unambiguous set (default)
randomString(16, ALPHANUMERIC) // pick any charset

Built-in charsets

Export Set Use
ALPHANUMERIC A–Z a–z 0–9 (62) ids, tokens
UNAMBIGUOUS alphanumeric minus 0 O 1 l I default — human-safe
UPPER_CODE upper + digits, unambiguous invite/redeem codes
LOWER_CODE lower + digits, unambiguous URL slugs / share IDs

API

  • randomString(length, chars?) => string — the core; chars must have 2–256 characters.
  • randomId(length = 12) => string — alphanumeric.
  • randomCode(length = 6) => string — uppercase + digits, unambiguous.

How the bias is removed

Map a byte 0–255 to a character with byte % charLen and the first 256 mod charLen characters get one extra chance each. So we compute maxValid = floor(256 / charLen) * charLen and reject any byte ≥ maxValid before mapping. Every accepted byte then lands on exactly one character with equal probability. The test suite verifies the distribution stays uniform for a deliberately awkward charset size.

Why this exists

Extracted from the production token/ID layer of quanta-study.de — the single source of truth for every share ID, invite code and temporary password.

License

MIT © Amos Matzke · quanta-study.de

About

Cryptographically secure random strings with rejection sampling (no modulo bias). Isomorphic browser/Node/edge, zero dependencies.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors