Skip to content

Instantly share code, notes, and snippets.

@flipeador
Last active September 13, 2025 03:27
Show Gist options
  • Save flipeador/af42918938573efe8eef7d09f6cac1ca to your computer and use it in GitHub Desktop.
Save flipeador/af42918938573efe8eef7d09f6cac1ca to your computer and use it in GitHub Desktop.

Password hashing

Scrypt & Argon2

import crypto from 'node:crypto';

const PASSWORD = '12345678';
const SALT = crypto.randomBytes(16); // unique for each password

const NORMALIZED_PASSWORD = PASSWORD.slice(-128).normalize('NFKC');

console.log('Salt:', SALT.toString('hex'), '\n');

//////////////////////////////////////////////////
// SCRYPT (Node.js v10.9)
//////////////////////////////////////////////////

const hashScryptBuffer = crypto.scryptSync(
    NORMALIZED_PASSWORD,
    SALT,
    64, // keylen
    {
        N: 16384, // CPU/memory cost
        r: 8,     // Block size
        p: 1      // Parallelization
    }
);

const hashScryptHex = hashScryptBuffer.toString('hex');

console.log('Scrypt:', hashScryptHex);

const hashScryptIsEqual = crypto.timingSafeEqual(
    Buffer.from(hashScryptHex, 'hex'),
    hashScryptBuffer
);

console.log('Verify:', hashScryptIsEqual, '\n');

//////////////////////////////////////////////////
// ARGON2 (Node.js v24.7)
//////////////////////////////////////////////////

const hashArgon2Buffer = crypto.argon2Sync(
    'argon2id',
    {
        message: NORMALIZED_PASSWORD,
        nonce: SALT,
        parallelism: 4,
        tagLength: 64,
        memory: 65536,
        passes: 3
    }
);

const hashArgon2Hex = hashArgon2Buffer.toString('hex');

console.log('Argon2:', hashArgon2Hex);

const hashArgon2IsEqual = crypto.timingSafeEqual(
    Buffer.from(hashArgon2Hex, 'hex'),
    hashArgon2Buffer
);

console.log('Verify:', hashArgon2IsEqual);

JSON Web Token (JWT)

Internet standard for creating data with optional signature whose payload holds JSON that asserts some number of claims.

https://jwt.io

RSA PKCS1-v1.5 (1993)

import crypto from 'node:crypto';

// Base 64 Encoding with URL and Filename Safe Alphabet.
function toBase64Url(data) {
    if (Buffer.isBuffer(data))
        return data.toString('base64url');
    if (typeof(data) !== 'string')
        data = JSON.stringify(data);
    return toBase64Url(Buffer.from(data));
}

function fromBase64Url(data, parseJson) {
    data = Buffer.from(data, 'base64url');
    if (parseJson != null)
        data = data.toString('utf8');
    return parseJson ? JSON.parse(data) : data;
}

//////////////////////////////////////////////////
// JWT: HEADER & PAYLOAD
//////////////////////////////////////////////////

// RSA PKCS1-v1.5 (1993).
//
// RSA: Rivest–Shamir–Adleman
// PKCS1: Public Key Cryptography Standard #1

const ALG       = 'RS256';  // 'RS256'  | 'RS384'  | 'RS512'
const ALGORITHM = 'SHA256'; // 'SHA256' | 'SHA384' | 'SHA512'

const header = { alg: ALG, typ: 'JWT' };
const payload = { data: 'Hello World!' };

//////////////////////////////////////////////////
// KEYS
//////////////////////////////////////////////////

const { publicKey, privateKey } = crypto.generateKeyPairSync(
    'rsa',
    {
        modulusLength: 3072, // ≥2048
        publicKeyEncoding: {
            type: 'spki',
            format: 'pem'
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem'
        },
    }
);

const PADDING = crypto.constants.RSA_PKCS1_PADDING;

const PUBLIC_KEY = { key: publicKey, padding: PADDING };
const PRIVATE_KEY = { key: privateKey, padding: PADDING };

console.log(`publicKey:\n${publicKey}`);

//////////////////////////////////////////////////
// SIGN
//////////////////////////////////////////////////

const headerBase64Url = toBase64Url(header);
const payloadBase64Url = toBase64Url(payload);

const data = `${headerBase64Url}.${payloadBase64Url}`;
const signature = crypto.sign(ALGORITHM, data, PRIVATE_KEY);
const signatureBase64Url = toBase64Url(signature);

const jsonWebToken = `${data}.${signatureBase64Url}`;
console.log('Token:', jsonWebToken, '\n');

//////////////////////////////////////////////////
// VERIFY
//////////////////////////////////////////////////

const jwtParts = jsonWebToken.split('.');

const jwtHeaderBase64Url = jwtParts[0];
const jwtPayloadBase64Url = jwtParts[1];
const jwtSignatureBase64Url = jwtParts[2];

const jwtHeaderObject = fromBase64Url(jwtHeaderBase64Url, true);
const jwtPayloadObject = fromBase64Url(jwtPayloadBase64Url, true);
const jwtSignatureBuffer = fromBase64Url(jwtSignatureBase64Url);

if (jwtHeaderObject.alg !== header.alg)
    throw new Error('Invalid algorithm');

const jwtData = `${jwtHeaderBase64Url}.${jwtPayloadBase64Url}`;
const isValid = crypto.verify(ALGORITHM, jwtData, PUBLIC_KEY, jwtSignatureBuffer);

console.log('Verify:', isValid);           // true
console.log('Header:', jwtHeaderObject);   // { alg: 'RS256', typ: 'JWT' }
console.log('Payload:', jwtPayloadObject); // { data: 'Hello World!' }

RSA-PSS PKCS1-v2.1 (2002)

import crypto from 'node:crypto';

// Base 64 Encoding with URL and Filename Safe Alphabet.
function toBase64Url(data) {
    if (Buffer.isBuffer(data))
        return data.toString('base64url');
    if (typeof(data) !== 'string')
        data = JSON.stringify(data);
    return toBase64Url(Buffer.from(data));
}

function fromBase64Url(data, parseJson) {
    data = Buffer.from(data, 'base64url');
    if (parseJson != null)
        data = data.toString('utf8');
    return parseJson ? JSON.parse(data) : data;
}

//////////////////////////////////////////////////
// JWT: HEADER & PAYLOAD
//////////////////////////////////////////////////

// RSA-PSS PKCS1-v2.1 (2002)
//
// RSA: Rivest–Shamir–Adleman
// PSS: Probabilistic Signature Scheme
// PKCS1: Public Key Cryptography Standard #1

const ALG       = 'PS256';  // 'PS256'  | 'PS384'  | 'PS512'
const ALGORITHM = 'SHA256'; // 'SHA256' | 'SHA384' | 'SHA512'

const header = { alg: ALG, typ: 'JWT' };
const payload = { data: 'Hello World!' };

//////////////////////////////////////////////////
// KEYS
//////////////////////////////////////////////////

const { publicKey, privateKey } = crypto.generateKeyPairSync(
    'rsa',
    {
        modulusLength: 3072, // ≥2048
        publicKeyEncoding: {
            type: 'spki',
            format: 'pem'
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem'
        },
    }
);

const PADDING = crypto.constants.RSA_PKCS1_PSS_PADDING; // MGF1
const SALTLENGTH = crypto.constants.RSA_PSS_SALTLEN_DIGEST;

const PUBLIC_KEY = { key: publicKey, padding: PADDING, saltLength: SALTLENGTH };
const PRIVATE_KEY = { key: privateKey, padding: PADDING, saltLength: SALTLENGTH };

console.log(`publicKey:\n${publicKey}`);

//////////////////////////////////////////////////
// SIGN
//////////////////////////////////////////////////

const headerBase64Url = toBase64Url(header);
const payloadBase64Url = toBase64Url(payload);

const data = `${headerBase64Url}.${payloadBase64Url}`;
const signature = crypto.sign(ALGORITHM, data, PRIVATE_KEY);
const signatureBase64Url = toBase64Url(signature);

const jsonWebToken = `${data}.${signatureBase64Url}`;
console.log('Token:', jsonWebToken, '\n');

//////////////////////////////////////////////////
// VERIFY
//////////////////////////////////////////////////

const jwtParts = jsonWebToken.split('.');

const jwtHeaderBase64Url = jwtParts[0];
const jwtPayloadBase64Url = jwtParts[1];
const jwtSignatureBase64Url = jwtParts[2];

const jwtHeaderObject = fromBase64Url(jwtHeaderBase64Url, true);
const jwtPayloadObject = fromBase64Url(jwtPayloadBase64Url, true);
const jwtSignatureBuffer = fromBase64Url(jwtSignatureBase64Url);

if (jwtHeaderObject.alg !== header.alg)
    throw new Error('Invalid algorithm');

const jwtData = `${jwtHeaderBase64Url}.${jwtPayloadBase64Url}`;
const isValid = crypto.verify(ALGORITHM, jwtData, PUBLIC_KEY, jwtSignatureBuffer);

console.log('Verify:', isValid);           // true
console.log('Header:', jwtHeaderObject);   // { alg: 'PS256', typ: 'JWT' }
console.log('Payload:', jwtPayloadObject); // { data: 'Hello World!' }

Elliptic Curve Digital Signature Algorithm (ECDSA)

import crypto from 'node:crypto';

// Base 64 Encoding with URL and Filename Safe Alphabet.
function toBase64Url(data) {
    if (Buffer.isBuffer(data))
        return data.toString('base64url');
    if (typeof(data) !== 'string')
        data = JSON.stringify(data);
    return toBase64Url(Buffer.from(data));
}

function fromBase64Url(data, parseJson) {
    data = Buffer.from(data, 'base64url');
    if (parseJson != null)
        data = data.toString('utf8');
    return parseJson ? JSON.parse(data) : data;
}

//////////////////////////////////////////////////
// JWT: HEADER & PAYLOAD
//////////////////////////////////////////////////

// Elliptic Curve Digital Signature Algorithm (ECDSA)

const ALG         = 'ES256';  // 'ES256'  | 'ES384'  | 'ES512'
const NAMED_CURVE = 'P-256';  // 'P-256'  | 'P-384'  | 'P-512'
const ALGORITHM   = 'SHA256'; // 'SHA256' | 'SHA384' | 'SHA512'

const header = { alg: ALG, typ: 'JWT' };
const payload = { data: 'Hello World!' };

//////////////////////////////////////////////////
// KEYS
//////////////////////////////////////////////////

const { publicKey, privateKey } = crypto.generateKeyPairSync(
    'ec',
    {
        namedCurve: NAMED_CURVE,
        publicKeyEncoding: {
            type: 'spki',
            format: 'pem'
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem'
        },
    }
);

console.log(`publicKey:\n${publicKey}`);

//////////////////////////////////////////////////
// SIGN
//////////////////////////////////////////////////

const headerBase64Url = toBase64Url(header);
const payloadBase64Url = toBase64Url(payload);

const data = `${headerBase64Url}.${payloadBase64Url}`;
const signature = crypto.sign(ALGORITHM, data, privateKey);
const signatureBase64Url = toBase64Url(signature);

const jsonWebToken = `${data}.${signatureBase64Url}`;
console.log('Token:', jsonWebToken, '\n');

//////////////////////////////////////////////////
// VERIFY
//////////////////////////////////////////////////

const jwtParts = jsonWebToken.split('.');

const jwtHeaderBase64Url = jwtParts[0];
const jwtPayloadBase64Url = jwtParts[1];
const jwtSignatureBase64Url = jwtParts[2];

const jwtHeaderObject = fromBase64Url(jwtHeaderBase64Url, true);
const jwtPayloadObject = fromBase64Url(jwtPayloadBase64Url, true);
const jwtSignatureBuffer = fromBase64Url(jwtSignatureBase64Url);

if (jwtHeaderObject.alg !== header.alg)
    throw new Error('Invalid algorithm');

const jwtData = `${jwtHeaderBase64Url}.${jwtPayloadBase64Url}`;
const isValid = crypto.verify(ALGORITHM, jwtData, publicKey, jwtSignatureBuffer);

console.log('Verify:', isValid);           // true
console.log('Header:', jwtHeaderObject);   // { alg: 'ES256', typ: 'JWT' }
console.log('Payload:', jwtPayloadObject); // { data: 'Hello World!' }

Module-Lattice-Based Digital Signature Algorithm (MLDSA)

Node.js v24.7.0.

import crypto from 'node:crypto';

// Base 64 Encoding with URL and Filename Safe Alphabet.
function toBase64Url(data) {
    if (Buffer.isBuffer(data))
        return data.toString('base64url');
    if (typeof(data) !== 'string')
        data = JSON.stringify(data);
    return toBase64Url(Buffer.from(data));
}

function fromBase64Url(data, parseJson) {
    data = Buffer.from(data, 'base64url');
    if (parseJson != null)
        data = data.toString('utf8');
    return parseJson ? JSON.parse(data) : data;
}

//////////////////////////////////////////////////
// JWT: HEADER & PAYLOAD
//////////////////////////////////////////////////

// Post-Quantum Cryptography (NIST FIPS 204, ML-DSA)
// Module-Lattice-Based Digital Signature Algorithm (MLDSA)

const ALG      = 'MLDSA44';   // 'MLDSA44'   | 'MLDSA65'   | 'MLDSA87'
const KEY_TYPE = 'ml-dsa-44'; // 'ml-dsa-44' | 'ml-dsa-65' | 'ml-dsa-87'

const header = { alg: ALG, typ: 'JWT' };
const payload = { data: 'Hello World!' };

//////////////////////////////////////////////////
// KEYS
//////////////////////////////////////////////////

const { publicKey, privateKey } = crypto.generateKeyPairSync(
    KEY_TYPE,
    {
        publicKeyEncoding: {
            type: 'spki',
            format: 'pem'
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem'
        },
    }
);

console.log(`publicKey:\n${publicKey}`);

//////////////////////////////////////////////////
// SIGN
//////////////////////////////////////////////////

const headerBase64Url = toBase64Url(header);
const payloadBase64Url = toBase64Url(payload);

const data = `${headerBase64Url}.${payloadBase64Url}`;
const signature = crypto.sign(null, data, privateKey);
const signatureBase64Url = toBase64Url(signature);

const jsonWebToken = `${data}.${signatureBase64Url}`;
console.log(`Token:\n${jsonWebToken}\n`);

//////////////////////////////////////////////////
// VERIFY
//////////////////////////////////////////////////

const jwtParts = jsonWebToken.split('.');

const jwtHeaderBase64Url = jwtParts[0];
const jwtPayloadBase64Url = jwtParts[1];
const jwtSignatureBase64Url = jwtParts[2];

const jwtHeaderObject = fromBase64Url(jwtHeaderBase64Url, true);
const jwtPayloadObject = fromBase64Url(jwtPayloadBase64Url, true);
const jwtSignatureBuffer = fromBase64Url(jwtSignatureBase64Url);

if (jwtHeaderObject.alg !== header.alg)
    throw new Error('Invalid algorithm');

const jwtData = `${jwtHeaderBase64Url}.${jwtPayloadBase64Url}`;
const isValid = crypto.verify(null, jwtData, publicKey, jwtSignatureBuffer);

console.log('Verify:', isValid);           // true
console.log('Header:', jwtHeaderObject);   // { alg: 'MLDSA44', typ: 'JWT' }
console.log('Payload:', jwtPayloadObject); // { data: 'Hello World!' }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment