SPB Git

Repository Public MIT

crypto.ts 1.7 KB 42 lines typescript

1import { createHmac, randomBytes, randomInt, timingSafeEqual } from "node:crypto";2import { TOKEN_PREFIX } from "@skills/registry-core";3import { env } from "./env";45/** Keyed verifier. Tokens carry 256 bits of entropy, so an HMAC-SHA-256 verifier is sufficient and constant-cost. */6export function verifier(kind: "api-token" | "session" | "csrf" | "download", value: string): string {7  return createHmac("sha256", env.tokenPepper).update(`${kind}\0${value}`).digest("hex");8}910export function safeEqualHex(a: string, b: string): boolean {11  const x = Buffer.from(a, "hex");12  const y = Buffer.from(b, "hex");13  return x.length === y.length && x.length > 0 && timingSafeEqual(x, y);14}1516const ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";17function randomId(len: number): string {18  let s = "";19  for (let i = 0; i < len; i++) s += ALPHABET[randomInt(ALPHABET.length)];20  return s;21}2223/** skr_<10-char public prefix>_<32 random bytes, base64url>. Only the prefix and the verifier are stored. */24export function generateApiToken(): { token: string; prefix: string; hash: string } {25  const prefix = randomId(10);26  const token = `${TOKEN_PREFIX}_${prefix}_${randomBytes(32).toString("base64url")}`;27  return { token, prefix, hash: verifier("api-token", token) };28}2930export function generateSessionToken(): { token: string; hash: string; csrfSecret: string } {31  const token = randomBytes(32).toString("base64url");32  return { token, hash: verifier("session", token), csrfSecret: randomBytes(24).toString("base64url") };33}3435export function csrfTokenFor(csrfSecret: string): string {36  return verifier("csrf", csrfSecret);37}3839export function generatePassword(): string {40  return randomBytes(18).toString("base64url");41}42
Files main
Menu

Theme