Pure TypeScript/JavaScript streaming implementation of the complete Secure Hash Standard (SHA) family with HMAC support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Advanced SHA-3 functionality including variable-length SHAKE algorithms, customizable cSHAKE variants, and KMAC (Keccak-based Message Authentication Code). These modern cryptographic functions provide enhanced security features and flexibility beyond traditional fixed-length hash functions.
SHAKE algorithms provide variable-length output based on the Keccak sponge construction, offering configurable output sizes for specific security requirements.
/**
* SHAKE constructor for TEXT input format
* @param variant - SHAKE variant: SHAKE128 or SHAKE256
* @param inputFormat - Must be "TEXT"
* @param options - Optional configuration including encoding and numRounds
*/
constructor(variant: "SHAKE128" | "SHAKE256", inputFormat: "TEXT", options?: SHAKEOptionsEncodingType);
/**
* SHAKE constructor for binary input formats
* @param variant - SHAKE variant: SHAKE128 or SHAKE256
* @param inputFormat - Binary format: HEX, B64, BYTES, ARRAYBUFFER, UINT8ARRAY
* @param options - Optional configuration including numRounds
*/
constructor(variant: "SHAKE128" | "SHAKE256", inputFormat: FormatNoTextType, options?: SHAKEOptionsNoEncodingType);
interface SHAKEOptionsEncodingType {
numRounds?: number;
encoding?: EncodingType;
}
interface SHAKEOptionsNoEncodingType {
numRounds?: number;
}
type EncodingType = "UTF8" | "UTF16BE" | "UTF16LE";
type FormatNoTextType = "HEX" | "B64" | "BYTES" | "ARRAYBUFFER" | "UINT8ARRAY";Usage Examples:
import jsSHA from "jssha";
// SHAKE128 with 256-bit output
const shake128 = new jsSHA("SHAKE128", "TEXT", { encoding: "UTF8" });
shake128.update("Hello, SHAKE128!");
const hash256 = shake128.getHash("HEX", { outputLen: 256 });
// SHAKE256 with 512-bit output
const shake256 = new jsSHA("SHAKE256", "TEXT");
shake256.update("Hello, SHAKE256!");
const hash512 = shake256.getHash("HEX", { outputLen: 512 });
// Variable output lengths
const shake = new jsSHA("SHAKE128", "TEXT");
shake.update("Same input");
const short = shake.getHash("HEX", { outputLen: 128 }); // 128-bit output
const medium = shake.getHash("HEX", { outputLen: 384 }); // 384-bit output
const long = shake.getHash("HEX", { outputLen: 1024 }); // 1024-bit output
// SHAKE with hex input
const shakeHex = new jsSHA("SHAKE256", "HEX");
shakeHex.update("48656c6c6f"); // "Hello" in hex
const result = shakeHex.getHash("B64", { outputLen: 256 });cSHAKE extends SHAKE with domain separation through customization strings and function names, providing enhanced security in multi-purpose applications.
/**
* cSHAKE constructor for TEXT input format
* @param variant - cSHAKE variant: CSHAKE128 or CSHAKE256
* @param inputFormat - Must be "TEXT"
* @param options - Customization options including funcName and customization
*/
constructor(variant: "CSHAKE128" | "CSHAKE256", inputFormat: "TEXT", options?: CSHAKEOptionsEncodingType);
/**
* cSHAKE constructor for binary input formats
* @param variant - cSHAKE variant: CSHAKE128 or CSHAKE256
* @param inputFormat - Binary format: HEX, B64, BYTES, ARRAYBUFFER, UINT8ARRAY
* @param options - Customization options including funcName and customization
*/
constructor(variant: "CSHAKE128" | "CSHAKE256", inputFormat: FormatNoTextType, options?: CSHAKEOptionsNoEncodingType);
interface CSHAKEOptionsEncodingType {
customization?: GenericInputType;
funcName?: GenericInputType;
encoding?: EncodingType;
}
interface CSHAKEOptionsNoEncodingType {
customization?: GenericInputType;
funcName?: GenericInputType;
}
interface GenericInputType {
value: string;
format: "TEXT";
encoding?: EncodingType;
} | {
value: string;
format: "B64" | "HEX" | "BYTES";
} | {
value: ArrayBuffer;
format: "ARRAYBUFFER";
} | {
value: Uint8Array;
format: "UINT8ARRAY";
}Usage Examples:
import jsSHA from "jssha";
// cSHAKE with customization string
const cshake1 = new jsSHA("CSHAKE128", "TEXT", {
customization: { value: "MyApp", format: "TEXT" },
encoding: "UTF8"
});
cshake1.update("Hello, cSHAKE!");
const result1 = cshake1.getHash("HEX", { outputLen: 256 });
// cSHAKE with function name and customization
const cshake2 = new jsSHA("CSHAKE256", "TEXT", {
funcName: { value: "KeyDerivation", format: "TEXT" },
customization: { value: "UserAuth-v1.0", format: "TEXT" }
});
cshake2.update("sensitive-data");
const result2 = cshake2.getHash("B64", { outputLen: 384 });
// cSHAKE with hex customization
const cshake3 = new jsSHA("CSHAKE128", "HEX", {
customization: { value: "deadbeef", format: "HEX" }
});
cshake3.update("48656c6c6f"); // "Hello" in hex
const result3 = cshake3.getHash("UINT8ARRAY", { outputLen: 512 });
// Domain separation example
const emailHash = new jsSHA("CSHAKE256", "TEXT", {
funcName: { value: "EmailHash", format: "TEXT" },
customization: { value: "MyCompany-2024", format: "TEXT" }
});
emailHash.update("user@example.com");
const emailDigest = emailHash.getHash("HEX", { outputLen: 256 });
const passwordHash = new jsSHA("CSHAKE256", "TEXT", {
funcName: { value: "PasswordHash", format: "TEXT" },
customization: { value: "MyCompany-2024", format: "TEXT" }
});
passwordHash.update("user-password");
const passwordDigest = passwordHash.getHash("HEX", { outputLen: 256 });KMAC provides authenticated hashing based on cSHAKE, offering strong security guarantees for message authentication and integrity verification.
/**
* KMAC constructor for TEXT input format
* @param variant - KMAC variant: KMAC128 or KMAC256
* @param inputFormat - Must be "TEXT"
* @param options - Required kmacKey and optional customization
*/
constructor(variant: "KMAC128" | "KMAC256", inputFormat: "TEXT", options: KMACOptionsEncodingType);
/**
* KMAC constructor for binary input formats
* @param variant - KMAC variant: KMAC128 or KMAC256
* @param inputFormat - Binary format: HEX, B64, BYTES, ARRAYBUFFER, UINT8ARRAY
* @param options - Required kmacKey and optional customization
*/
constructor(variant: "KMAC128" | "KMAC256", inputFormat: FormatNoTextType, options: KMACOptionsNoEncodingType);
interface KMACOptionsEncodingType {
kmacKey: GenericInputType;
customization?: GenericInputType;
encoding?: EncodingType;
}
interface KMACOptionsNoEncodingType {
kmacKey: GenericInputType;
customization?: GenericInputType;
}Usage Examples:
import jsSHA from "jssha";
// Basic KMAC with text key
const kmac1 = new jsSHA("KMAC128", "TEXT", {
kmacKey: { value: "secret-key", format: "TEXT" },
encoding: "UTF8"
});
kmac1.update("Message to authenticate");
const mac1 = kmac1.getHash("HEX", { outputLen: 256 });
// KMAC with customization string
const kmac2 = new jsSHA("KMAC256", "TEXT", {
kmacKey: { value: "another-secret", format: "TEXT" },
customization: { value: "MyProtocol-v2", format: "TEXT" }
});
kmac2.update("Authenticated message");
const mac2 = kmac2.getHash("B64", { outputLen: 512 });
// KMAC with hex key
const kmac3 = new jsSHA("KMAC128", "HEX", {
kmacKey: { value: "deadbeefcafebabe", format: "HEX" }
});
kmac3.update("48656c6c6f"); // "Hello" in hex
const mac3 = kmac3.getHash("UINT8ARRAY", { outputLen: 256 });
// KMAC with ArrayBuffer key
const keyBuffer = new TextEncoder().encode("binary-key").buffer;
const kmac4 = new jsSHA("KMAC256", "ARRAYBUFFER", {
kmacKey: { value: keyBuffer, format: "ARRAYBUFFER" },
customization: { value: "FileIntegrity", format: "TEXT" }
});
const messageBuffer = new TextEncoder().encode("File content").buffer;
kmac4.update(messageBuffer);
const mac4 = kmac4.getHash("ARRAYBUFFER", { outputLen: 384 });
// API authentication with KMAC
function authenticateAPICall(endpoint: string, payload: string, apiKey: string): string {
const message = `${endpoint}:${payload}`;
const kmac = new jsSHA("KMAC256", "TEXT", {
kmacKey: { value: apiKey, format: "TEXT" },
customization: { value: "APIAuth-v1", format: "TEXT" }
});
kmac.update(message);
return kmac.getHash("B64", { outputLen: 256 });
}All variable-length variants (SHAKE, cSHAKE, KMAC) require the outputLen parameter in the getHash() method. Note that shakeLen is a deprecated alias for outputLen and should not be used in new code:
// outputLen is required and specifies output length in BITS
const shake = new jsSHA("SHAKE128", "TEXT");
shake.update("Hello");
// Valid output lengths (multiples of 8 bits)
const hash128 = shake.getHash("HEX", { outputLen: 128 }); // 16 bytes
const hash256 = shake.getHash("HEX", { outputLen: 256 }); // 32 bytes
const hash512 = shake.getHash("HEX", { outputLen: 512 }); // 64 bytes
const hash1024 = shake.getHash("HEX", { outputLen: 1024 }); // 128 bytes
// Invalid - outputLen must be specified
try {
const invalid = shake.getHash("HEX"); // Error: outputLen required
} catch (error) {
console.error("outputLen parameter is required for variable-length variants");
}
// Invalid - outputLen must be multiple of 8
try {
const invalid = shake.getHash("HEX", { outputLen: 129 }); // Error: not multiple of 8
} catch (error) {
console.error("outputLen must be multiple of 8 bits");
}// Good: Descriptive customization strings
const cshake = new jsSHA("CSHAKE256", "TEXT", {
funcName: { value: "KeyDerivation", format: "TEXT" },
customization: { value: "MyApp-UserAuth-v1.2", format: "TEXT" }
});
// Good: Domain-specific KMAC customization
const kmac = new jsSHA("KMAC256", "TEXT", {
kmacKey: { value: "secret", format: "TEXT" },
customization: { value: "FileIntegrity-SHA3", format: "TEXT" }
});
// Avoid: Empty or generic customization strings reduce security benefitsimport jsSHA from "jssha";
function deriveKey(masterKey: string, salt: string, info: string, keyLength: number): Uint8Array {
const cshake = new jsSHA("CSHAKE256", "TEXT", {
funcName: { value: "KeyDerivation", format: "TEXT" },
customization: { value: `${salt}:${info}`, format: "TEXT" }
});
cshake.update(masterKey);
return cshake.getHash("UINT8ARRAY", { outputLen: keyLength * 8 });
}
const derivedKey = deriveKey("master-secret", "random-salt", "encryption-key", 32);import jsSHA from "jssha";
function generateKeystream(seed: string, nonce: string, length: number): Uint8Array {
const shake = new jsSHA("SHAKE256", "TEXT");
shake.update(`${seed}:${nonce}`);
return shake.getHash("UINT8ARRAY", { outputLen: length * 8 });
}
const keystream = generateKeystream("cipher-seed", "unique-nonce", 1024);import jsSHA from "jssha";
function hashNode(left: string, right: string): string {
const cshake = new jsSHA("CSHAKE256", "TEXT", {
funcName: { value: "MerkleNode", format: "TEXT" },
customization: { value: "MerkleTree-v1", format: "TEXT" }
});
cshake.update(`${left}${right}`);
return cshake.getHash("HEX", { outputLen: 256 });
}
const rootHash = hashNode(hashNode("leaf1", "leaf2"), hashNode("leaf3", "leaf4"));Advanced SHA-3 variants have specific requirements that can cause errors:
// KMAC requires kmacKey
try {
const invalid = new jsSHA("KMAC128", "TEXT", {}); // Error: kmacKey required
} catch (error) {
console.error("KMAC variants require kmacKey parameter");
}
// Variable-length variants require outputLen
try {
const shake = new jsSHA("SHAKE128", "TEXT");
shake.update("Hello");
const invalid = shake.getHash("HEX"); // Error: outputLen required
} catch (error) {
console.error("Variable-length variants require outputLen parameter");
}
// numRounds not supported with some variants
try {
const invalid = new jsSHA("CSHAKE256", "TEXT", {
numRounds: 2, // Error: numRounds not valid with cSHAKE
customization: { value: "test", format: "TEXT" }
});
} catch (error) {
console.error("numRounds parameter not supported with MAC or cSHAKE variants");
}