CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-peculiar--webcrypto

A WebCrypto polyfill for Node.js that provides comprehensive cryptographic operations using standard Web Crypto API

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

key-derivation.mddocs/

Key Derivation

Key derivation functions for generating cryptographic keys from passwords, shared secrets, or other key material. Supports PBKDF2, HKDF, and HMAC operations.

Capabilities

PBKDF2 (Password-Based Key Derivation Function 2)

Derives cryptographic keys from passwords using iterative hashing for brute-force resistance.

/**
 * PBKDF2 key derivation parameters
 */
interface Pbkdf2Params extends Algorithm {
  name: "PBKDF2";
  salt: BufferSource; // Random salt (minimum 64 bits recommended)
  iterations: number; // Iteration count (minimum 10,000 recommended)
  hash: "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512"; // Hash algorithm
}

Usage Example:

// Import password as key material
const password = new TextEncoder().encode("user-password");
const passwordKey = await crypto.subtle.importKey(
  "raw",
  password,
  { name: "PBKDF2" },
  false,
  ["deriveKey", "deriveBits"]
);

// Generate random salt
const salt = crypto.getRandomValues(new Uint8Array(16));

// Derive AES key from password
const derivedKey = await crypto.subtle.deriveKey(
  {
    name: "PBKDF2",
    salt: salt,
    iterations: 100000,
    hash: "SHA-256"
  },
  passwordKey,
  { name: "AES-GCM", length: 256 },
  false,
  ["encrypt", "decrypt"]
);

// Alternative: derive raw bits
const derivedBits = await crypto.subtle.deriveBits(
  {
    name: "PBKDF2",
    salt: salt,
    iterations: 100000,
    hash: "SHA-256"
  },
  passwordKey,
  256 // 256 bits output
);

HKDF (HMAC-based Key Derivation Function)

Extracts and expands key material using HMAC for key agreement and key stretching.

/**
 * HKDF key derivation parameters
 */
interface HkdfParams extends Algorithm {
  name: "HKDF";
  hash: "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512"; // Hash algorithm
  salt: BufferSource; // Salt value (can be empty)
  info: BufferSource; // Application-specific context information
}

Usage Example:

// Import shared secret as key material
const sharedSecret = crypto.getRandomValues(new Uint8Array(32));
const baseKey = await crypto.subtle.importKey(
  "raw",
  sharedSecret,
  { name: "HKDF" },
  false,
  ["deriveKey", "deriveBits"]
);

// Derive multiple keys from shared secret
const salt = new TextEncoder().encode("unique-salt");
const info = new TextEncoder().encode("application-context");

// Derive encryption key
const encryptionKey = await crypto.subtle.deriveKey(
  {
    name: "HKDF",
    hash: "SHA-256",
    salt: salt,
    info: new TextEncoder().encode("encryption-key")
  },
  baseKey,
  { name: "AES-GCM", length: 256 },
  false,
  ["encrypt", "decrypt"]
);

// Derive MAC key
const macKey = await crypto.subtle.deriveKey(
  {
    name: "HKDF", 
    hash: "SHA-256",
    salt: salt,
    info: new TextEncoder().encode("mac-key")
  },
  baseKey,
  { name: "HMAC", hash: "SHA-256" },
  false,
  ["sign", "verify"]
);

HMAC (Hash-based Message Authentication Code)

Provides message authentication and integrity verification using cryptographic hash functions.

/**
 * HMAC key generation parameters
 */
interface HmacKeyGenParams extends Algorithm {
  name: "HMAC";
  hash: "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512";
  length?: number; // Key length in bits (optional)
}

/**
 * HMAC operation parameters
 */
interface HmacParams extends Algorithm {
  name: "HMAC";
}

Usage Example:

// Generate HMAC key
const hmacKey = await crypto.subtle.generateKey(
  { name: "HMAC", hash: "SHA-256", length: 256 },
  true,
  ["sign", "verify"]
);

// Create HMAC signature
const data = new TextEncoder().encode("Message to authenticate");
const signature = await crypto.subtle.sign(
  { name: "HMAC" },
  hmacKey,
  data
);

// Verify HMAC signature
const isValid = await crypto.subtle.verify(
  { name: "HMAC" },
  hmacKey,
  signature,
  data
);

// Import HMAC key from raw bytes
const keyMaterial = crypto.getRandomValues(new Uint8Array(32));
const importedKey = await crypto.subtle.importKey(
  "raw",
  keyMaterial,
  { name: "HMAC", hash: "SHA-256" },
  false,
  ["sign", "verify"]
);

Key Classes

PBKDF2 Key Class

/**
 * PBKDF2 key for deriving other keys from passwords
 */
class PbkdfCryptoKey extends SymmetricKey {
  public algorithm: PbkdfKeyAlgorithm;
  public type: "secret";
  public usages: ("deriveKey" | "deriveBits")[];
  public extractable: boolean;
}

interface PbkdfKeyAlgorithm extends KeyAlgorithm {
  name: "PBKDF2";
}

HKDF Key Class

/**
 * HKDF key for expanding key material
 */
class HkdfCryptoKey extends SymmetricKey {
  public algorithm: HkdfKeyAlgorithm;
  public type: "secret";
  public usages: ("deriveKey" | "deriveBits")[];
  public extractable: boolean;
}

interface HkdfKeyAlgorithm extends KeyAlgorithm {
  name: "HKDF";
}

HMAC Key Class

/**
 * HMAC key for message authentication
 */
class HmacCryptoKey extends SymmetricKey {
  public algorithm: HmacKeyAlgorithm;
  public type: "secret";
  public usages: ("sign" | "verify")[];
  public extractable: boolean;
}

interface HmacKeyAlgorithm extends KeyAlgorithm {
  name: "HMAC";
  hash: KeyAlgorithm;
  length: number;
}

Advanced Usage Patterns

Key Stretching with PBKDF2

async function stretchPassword(
  password: string,
  salt: Uint8Array,
  iterations: number = 100000
) {
  const passwordKey = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(password),
    { name: "PBKDF2" },
    false,
    ["deriveBits"]
  );
  
  return await crypto.subtle.deriveBits(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: iterations,
      hash: "SHA-256"
    },
    passwordKey,
    256
  );
}

Key Expansion with HKDF

async function expandKey(
  masterKey: ArrayBuffer,
  salt: string,
  info: string,
  keyLength: number = 256
) {
  const baseKey = await crypto.subtle.importKey(
    "raw",
    masterKey,
    { name: "HKDF" },
    false,
    ["deriveBits"]
  );
  
  return await crypto.subtle.deriveBits(
    {
      name: "HKDF",
      hash: "SHA-256",
      salt: new TextEncoder().encode(salt),
      info: new TextEncoder().encode(info)
    },
    baseKey,
    keyLength
  );
}

HMAC Chain for Sequential Authentication

async function createHmacChain(
  key: CryptoKey,
  messages: string[]
): Promise<ArrayBuffer[]> {
  const signatures: ArrayBuffer[] = [];
  
  for (const message of messages) {
    const data = new TextEncoder().encode(message);
    const signature = await crypto.subtle.sign(
      { name: "HMAC" },
      key,
      data
    );
    signatures.push(signature);
  }
  
  return signatures;
}

Security Best Practices

PBKDF2 Recommendations

  • Minimum iterations: 100,000 for SHA-256 (2023 recommendations)
  • Salt length: At least 128 bits (16 bytes) of random data
  • Unique salts: Use different salt for each password
  • Hash algorithm: SHA-256 or stronger

HKDF Recommendations

  • Salt usage: Use random salt when possible, empty salt is acceptable
  • Info parameter: Include application context to prevent key reuse
  • Hash algorithm: SHA-256 or stronger for most applications

HMAC Recommendations

  • Key length: At least as long as hash output (256 bits for SHA-256)
  • Key generation: Use cryptographically secure random generation
  • Hash algorithm: SHA-256 or stronger, avoid SHA-1

Error Handling

async function safeDeriveKey(
  algorithm: any,
  baseKey: CryptoKey,
  derivedKeyType: any,
  extractable: boolean,
  keyUsages: KeyUsage[]
) {
  try {
    return await crypto.subtle.deriveKey(
      algorithm,
      baseKey,
      derivedKeyType,
      extractable,
      keyUsages
    );
  } catch (error) {
    if (error.name === "InvalidAccessError") {
      throw new Error("Base key doesn't support key derivation");
    }
    if (error.name === "NotSupportedError") {
      throw new Error("Key derivation algorithm not supported");
    }
    if (error.name === "OperationError") {
      throw new Error("Key derivation failed - check parameters");
    }
    throw error;
  }
}

Performance Considerations

  • PBKDF2: Higher iteration counts increase security but reduce performance
  • HKDF: Fast operation, suitable for real-time key derivation
  • HMAC: Very fast, suitable for high-throughput authentication
  • Caching: Consider caching derived keys when appropriate for performance

Install with Tessl CLI

npx tessl i tessl/npm-peculiar--webcrypto

docs

asymmetric-cryptography.md

crypto-interface.md

hash-functions.md

index.md

key-derivation.md

modern-cryptography.md

symmetric-encryption.md

tile.json