CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sjcl

Stanford JavaScript Crypto Library providing comprehensive cryptographic operations including AES encryption, hash functions, key derivation, and elliptic curve cryptography.

Pending
Overview
Eval results
Files

message-authentication.mddocs/

Message Authentication

SJCL provides HMAC (Hash-based Message Authentication Code) for message authentication and integrity verification, ensuring that data has not been tampered with and comes from an authenticated source.

Capabilities

HMAC (Hash-based Message Authentication Code)

Provides message authentication using cryptographic hash functions, combining a secret key with hash algorithms for secure authentication.

/**
 * HMAC constructor
 * @param {BitArray} key - Secret key for HMAC (any length)
 * @param {Function} [Hash] - Hash function to use (default: SHA-256)
 * @throws {sjcl.exception.invalid} If key or hash function is invalid
 */
new sjcl.misc.hmac(key, Hash);

Instance Methods:

/**
 * Compute HMAC of data (alias for mac method)
 * @param {BitArray|string} data - Data to authenticate
 * @returns {BitArray} HMAC authentication tag
 * @throws {sjcl.exception.invalid} If called after update without reset
 */
sjcl.misc.hmac.prototype.encrypt(data);

/**
 * Compute HMAC of data
 * @param {BitArray|string} data - Data to authenticate
 * @returns {BitArray} HMAC authentication tag
 * @throws {sjcl.exception.invalid} If called after update without reset
 */
sjcl.misc.hmac.prototype.mac(data);

/**
 * Reset HMAC state for reuse
 * @returns {sjcl.misc.hmac} This HMAC instance for chaining
 */
sjcl.misc.hmac.prototype.reset();

/**
 * Add data to HMAC computation (streaming interface)
 * @param {BitArray|string} data - Data to add to HMAC
 * @returns {sjcl.misc.hmac} This HMAC instance for chaining
 */
sjcl.misc.hmac.prototype.update(data);

/**
 * Complete HMAC computation and return result
 * @returns {BitArray} Final HMAC authentication tag
 */
sjcl.misc.hmac.prototype.digest();

Usage Examples:

const sjcl = require('sjcl');

// Basic HMAC with SHA-256 (default)
const key = sjcl.random.randomWords(8); // 256-bit key
const hmac = new sjcl.misc.hmac(key);
const data = "Hello, World!";
const authTag = hmac.encrypt(data);

console.log("HMAC:", sjcl.codec.hex.fromBits(authTag));

// Verify HMAC
const hmacVerify = new sjcl.misc.hmac(key);
const computedTag = hmacVerify.encrypt(data);
const isValid = sjcl.bitArray.equal(authTag, computedTag);
console.log("HMAC valid:", isValid);

// Using different hash functions
const hmacSHA1 = new sjcl.misc.hmac(key, sjcl.hash.sha1);
const hmacSHA512 = new sjcl.misc.hmac(key, sjcl.hash.sha512);

const tagSHA1 = hmacSHA1.encrypt(data);
const tagSHA512 = hmacSHA512.encrypt(data);

Streaming HMAC

For large data or when data arrives in chunks, use the streaming interface:

const sjcl = require('sjcl');

// Streaming HMAC computation
const key = sjcl.random.randomWords(8);
const hmac = new sjcl.misc.hmac(key);

// Add data in chunks
hmac.update("First chunk ");
hmac.update("Second chunk ");
hmac.update("Final chunk");

// Get final result
const streamingTag = hmac.digest();

// Compare with one-shot computation
const hmacOneShot = new sjcl.misc.hmac(key);
const oneShotTag = hmacOneShot.encrypt("First chunk Second chunk Final chunk");

console.log("Tags equal:", sjcl.bitArray.equal(streamingTag, oneShotTag));

// Reset for reuse
hmac.reset();
const newTag = hmac.encrypt("New data");

HMAC Key Management

Proper key management is crucial for HMAC security:

const sjcl = require('sjcl');

// Generate HMAC keys
function generateHMACKey(keySize = 256) {
  // Key should be at least as long as hash output
  const minSize = 256; // SHA-256 output size
  const actualSize = Math.max(keySize, minSize);
  return sjcl.random.randomWords(actualSize / 32);
}

// Derive HMAC key from password
function deriveHMACKey(password, salt, iterations = 100000) {
  return sjcl.misc.pbkdf2(password, salt, iterations, 256);
}

// Multiple HMAC keys from master key
function deriveHMACKeys(masterKey, purposes) {
  const keys = {};
  purposes.forEach(purpose => {
    const info = sjcl.codec.utf8String.toBits(purpose);
    keys[purpose] = sjcl.misc.hkdf(masterKey, 256, null, info);
  });
  return keys;
}

// Usage
const masterKey = sjcl.random.randomWords(8);
const keys = deriveHMACKeys(masterKey, ['authentication', 'integrity', 'session']);

const authHMAC = new sjcl.misc.hmac(keys.authentication);
const integrityHMAC = new sjcl.misc.hmac(keys.integrity);

Authentication Patterns

Message Authentication

Simple message authentication with HMAC:

const sjcl = require('sjcl');

class MessageAuthenticator {
  constructor(key) {
    this.key = key;
  }
  
  authenticate(message) {
    const hmac = new sjcl.misc.hmac(this.key);
    const tag = hmac.encrypt(message);
    
    return {
      message: message,
      tag: sjcl.codec.hex.fromBits(tag)
    };
  }
  
  verify(message, hexTag) {
    const hmac = new sjcl.misc.hmac(this.key);
    const computedTag = hmac.encrypt(message);
    const providedTag = sjcl.codec.hex.toBits(hexTag);
    
    return sjcl.bitArray.equal(computedTag, providedTag);
  }
}

// Usage
const authKey = sjcl.random.randomWords(8);
const authenticator = new MessageAuthenticator(authKey);

const authenticated = authenticator.authenticate("Important message");
console.log(authenticated);

const isValid = authenticator.verify("Important message", authenticated.tag);
console.log("Message valid:", isValid);

Encrypt-then-MAC

Secure pattern combining encryption with authentication:

const sjcl = require('sjcl');

function encryptThenMAC(plaintext, encKey, macKey) {
  // Encrypt first
  const iv = sjcl.random.randomWords(4);
  const aes = new sjcl.cipher.aes(encKey);
  const ciphertext = sjcl.mode.gcm.encrypt(aes, plaintext, iv);
  
  // Then authenticate the ciphertext + IV
  const hmac = new sjcl.misc.hmac(macKey);
  const dataToAuth = sjcl.bitArray.concat(iv, ciphertext);
  const authTag = hmac.encrypt(dataToAuth);
  
  return {\n    iv: iv,\n    ciphertext: ciphertext,\n    authTag: authTag\n  };\n}\n\nfunction verifyThenDecrypt(encrypted, encKey, macKey) {\n  // Verify authentication first\n  const hmac = new sjcl.misc.hmac(macKey);\n  const dataToAuth = sjcl.bitArray.concat(encrypted.iv, encrypted.ciphertext);\n  const computedTag = hmac.encrypt(dataToAuth);\n  \n  if (!sjcl.bitArray.equal(computedTag, encrypted.authTag)) {\n    throw new sjcl.exception.corrupt(\"Authentication failed\");\n  }\n  \n  // Then decrypt\n  const aes = new sjcl.cipher.aes(encKey);\n  return sjcl.mode.gcm.decrypt(aes, encrypted.ciphertext, encrypted.iv);\n}\n\n// Usage\nconst encKey = sjcl.random.randomWords(8);\nconst macKey = sjcl.random.randomWords(8);\nconst plaintext = sjcl.codec.utf8String.toBits(\"Secret message\");\n\nconst encrypted = encryptThenMAC(plaintext, encKey, macKey);\nconst decrypted = verifyThenDecrypt(encrypted, encKey, macKey);\n\nconsole.log(sjcl.codec.utf8String.fromBits(decrypted));\n```\n\n### Time-safe HMAC Verification\n\nPrevent timing attacks when verifying HMAC tags:\n\n```javascript\nconst sjcl = require('sjcl');\n\nfunction timeSafeHMACVerify(key, data, providedTag) {\n  const hmac = new sjcl.misc.hmac(key);\n  const computedTag = hmac.encrypt(data);\n  \n  // Use bitArray.equal for constant-time comparison\n  return sjcl.bitArray.equal(computedTag, providedTag);\n}\n\n// Usage\nconst key = sjcl.random.randomWords(8);\nconst data = \"sensitive data\";\nconst hmac = new sjcl.misc.hmac(key);\nconst tag = hmac.encrypt(data);\n\n// This is time-safe\nconst isValid1 = timeSafeHMACVerify(key, data, tag);\n\n// This is NOT time-safe (don't do this)\nconst hmac2 = new sjcl.misc.hmac(key);\nconst tag2 = hmac2.encrypt(data);\nconst hexTag1 = sjcl.codec.hex.fromBits(tag);\nconst hexTag2 = sjcl.codec.hex.fromBits(tag2);\nconst isValid2 = hexTag1 === hexTag2; // Vulnerable to timing attacks\n```\n\n## API Authentication\n\n### Request Signing\n\nSign API requests with HMAC for authentication:\n\n```javascript\nconst sjcl = require('sjcl');\n\nclass APIRequestSigner {\n  constructor(apiKey, secretKey) {\n    this.apiKey = apiKey;\n    this.secretKey = sjcl.codec.utf8String.toBits(secretKey);\n  }\n  \n  signRequest(method, url, body, timestamp) {\n    // Create string to sign\n    const stringToSign = [method, url, body || '', timestamp].join('\\n');\n    \n    // Generate HMAC signature\n    const hmac = new sjcl.misc.hmac(this.secretKey);\n    const signature = hmac.encrypt(stringToSign);\n    \n    return {\n      apiKey: this.apiKey,\n      timestamp: timestamp,\n      signature: sjcl.codec.base64.fromBits(signature)\n    };\n  }\n  \n  verifyRequest(method, url, body, timestamp, signature) {\n    const expected = this.signRequest(method, url, body, timestamp);\n    const providedSig = sjcl.codec.base64.toBits(signature);\n    const expectedSig = sjcl.codec.base64.toBits(expected.signature);\n    \n    return sjcl.bitArray.equal(providedSig, expectedSig);\n  }\n}\n\n// Usage\nconst signer = new APIRequestSigner('api123', 'secret456');\nconst timestamp = Date.now().toString();\n\nconst signature = signer.signRequest('POST', '/api/data', '{\"test\":true}', timestamp);\nconsole.log('Authorization:', `HMAC ${signature.apiKey}:${signature.signature}`);\n\nconst isValid = signer.verifyRequest(\n  'POST', \n  '/api/data', \n  '{\"test\":true}', \n  timestamp, \n  signature.signature\n);\nconsole.log('Request valid:', isValid);\n```\n\n## Security Recommendations\n\n1. **Key Length**: Use keys at least as long as the hash output (256 bits for SHA-256)\n2. **Key Generation**: Generate keys using cryptographically secure random number generators\n3. **Key Derivation**: Use PBKDF2/scrypt/HKDF for deriving keys from passwords\n4. **Constant-time Verification**: Always use `sjcl.bitArray.equal()` for tag comparison\n5. **Hash Function**: Use SHA-256 or SHA-512, avoid SHA-1\n6. **Tag Length**: Use full-length tags, don't truncate unless absolutely necessary\n7. **Separate Keys**: Use different keys for different purposes (encryption vs authentication)\n\n## Common Pitfalls\n\n1. **Timing Attacks**: Never use string comparison for HMAC verification\n2. **Key Reuse**: Don't use encryption keys for HMAC\n3. **Weak Keys**: Don't use predictable or short keys\n4. **MAC-then-Encrypt**: Always use Encrypt-then-MAC pattern\n5. **Tag Truncation**: Avoid truncating HMAC tags without security analysis

Install with Tessl CLI

npx tessl i tessl/npm-sjcl

docs

big-number-arithmetic.md

bit-array-utilities.md

cipher-modes.md

data-encoding.md

elliptic-curve-cryptography.md

hash-functions.md

high-level-encryption.md

index.md

key-derivation.md

key-exchange.md

message-authentication.md

random-number-generation.md

symmetric-encryption.md

tile.json