Compare commits

...
4 Commits
Author SHA1 Message Date
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c3fc9645d4 Bump picomatch from 4.0.2 to 4.0.5 (#46)
Bumps [picomatch](https://github.com/micromatch/picomatch) from 4.0.2 to 4.0.5.
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/4.0.2...4.0.5)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 4.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-07 08:35:23 +08:00
Yi-Cyuan Chen 59d5441220 ### Added
- GitHub Actions CI across Node.js 18, 20, 22, and 24 against the packed npm artifact
- GitHub Actions publish workflow on release with npm trusted publishing
- `prepack` script to build distributions before `npm pack` / `npm publish`

### Changed
- Replace Travis CI with GitHub Actions
- Update README badges to GitHub Actions, npm, and jsDelivr
- Upgrade mocha and pin vulnerable transitive dependencies via npm overrides
2026-08-07 08:31:31 +08:00
Yi-Cyuan Chen a67f74cf2c ### Added
- ParallelHash128, ParallelHash256, ParallelHashXOF128, and ParallelHashXOF256 (NIST SP 800-185)
2026-08-07 08:18:38 +08:00
Yi-Cyuan Chen d506b22f67 update RERADME 2026-08-06 11:38:20 +08:00
17 changed files with 1740 additions and 1154 deletions
+77
View File
@@ -0,0 +1,77 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node 24
uses: actions/setup-node@v4
with:
node-version: 24.x
- name: Install dependencies
run: npm ci
- name: Pack npm package
run: |
mkdir -p package-artifact
npm pack --pack-destination package-artifact
- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: package-node-24
path: package-artifact/*.tgz
if-no-files-found: error
runtime-test:
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x, 24.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Download package artifact
uses: actions/download-artifact@v4
with:
name: package-node-24
path: package-artifact
- name: Extract package artifact
run: tar -xzf package-artifact/*.tgz --strip-components=1 -C .
- name: Run runtime tests
if: matrix.node-version != '24.x'
run: npm run test:runtime
- name: Run runtime tests with coverage
if: matrix.node-version == '24.x'
run: npm run test:coverage
- name: Upload to coveralls
if: matrix.node-version == '24.x'
uses: coverallsapp/github-action@v2
with:
file: ./coverage/lcov.info
+32
View File
@@ -0,0 +1,32 @@
name: Publish
on:
release:
types: [published]
permissions:
contents: read
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24.x
registry-url: https://registry.npmjs.org
package-manager-cache: false
- run: npm ci
- name: Verify release version
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
test "$GITHUB_REF_NAME" = "v$PACKAGE_VERSION"
- run: npm run build
- run: npm test
- run: npm publish
-1
View File
@@ -2,6 +2,5 @@
/coverage/ /coverage/
/.nyc_output/ /.nyc_output/
/tests/ /tests/
.travis.yml
.npmignore .npmignore
bower.json bower.json
-10
View File
@@ -1,10 +0,0 @@
language: node_js
node_js:
- "6.11.4"
- "8.6.0"
before_install:
- npm install coveralls
after_success: npm run coveralls
branches:
only:
- master
+12
View File
@@ -1,5 +1,17 @@
# Change Log # Change Log
## v0.13.0 / 2026-08-07
### Added
- ParallelHash128, ParallelHash256, ParallelHashXOF128, and ParallelHashXOF256 (NIST SP 800-185)
- GitHub Actions CI across Node.js 18, 20, 22, and 24 against the packed npm artifact
- GitHub Actions publish workflow on release with npm trusted publishing
- `prepack` script to build distributions before `npm pack` / `npm publish`
### Changed
- Replace Travis CI with GitHub Actions
- Update README badges to GitHub Actions, npm, and jsDelivr
- Upgrade mocha and pin vulnerable transitive dependencies via npm overrides
## v0.12.0 / 2026-07-18 ## v0.12.0 / 2026-07-18
### Added ### Added
- KMACXOF128 and KMACXOF256 (NIST SP 800-185) - KMACXOF128 and KMACXOF256 (NIST SP 800-185)
+41 -8
View File
@@ -1,10 +1,10 @@
# js-sha3 # js-sha3
[![CI](https://github.com/emn178/js-sha3/actions/workflows/ci.yml/badge.svg)](https://github.com/emn178/js-sha3/actions/workflows/ci.yml)
[![Build Status](https://travis-ci.org/emn178/js-sha3.svg?branch=master)](https://travis-ci.org/emn178/js-sha3)
[![Coverage Status](https://coveralls.io/repos/emn178/js-sha3/badge.svg?branch=master)](https://coveralls.io/r/emn178/js-sha3?branch=master) [![Coverage Status](https://coveralls.io/repos/emn178/js-sha3/badge.svg?branch=master)](https://coveralls.io/r/emn178/js-sha3?branch=master)
[![NPM](https://nodei.co/npm/js-sha3.png?stars&downloads)](https://nodei.co/npm/js-sha3/) [![NPM](https://img.shields.io/npm/v/js-sha3)](https://www.npmjs.com/package/js-sha3)
[![CDNJS](https://img.shields.io/jsdelivr/npm/hm/js-sha3)](https://www.jsdelivr.com/package/npm/js-sha3)
A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash hash function for JavaScript supports UTF-8 encoding. A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash / ParallelHash hash function for JavaScript supports UTF-8 encoding.
## Notice ## Notice
* v0.8.0+ will throw an error if try to update hash after finalize. * v0.8.0+ will throw an error if try to update hash after finalize.
@@ -20,8 +20,22 @@ A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash hash function for Ja
[Keccak-384 Online](http://emn178.github.io/online-tools/keccak_384.html) [Keccak-384 Online](http://emn178.github.io/online-tools/keccak_384.html)
[Keccak-256 Online](http://emn178.github.io/online-tools/keccak_256.html) [Keccak-256 Online](http://emn178.github.io/online-tools/keccak_256.html)
[Keccak-224 Online](http://emn178.github.io/online-tools/keccak_224.html) [Keccak-224 Online](http://emn178.github.io/online-tools/keccak_224.html)
[Shake128 Online](http://emn178.github.io/online-tools/shake_128.html) [Shake128 Online](http://emn178.github.io/online-tools/shake128/)
[Shake256 Online](http://emn178.github.io/online-tools/shake_256.html) [Shake256 Online](http://emn178.github.io/online-tools/shake256/)
[cShake128 Online](http://emn178.github.io/online-tools/cshake128/)
[cShake256 Online](http://emn178.github.io/online-tools/cshake256/)
[KMAC128 Online](http://emn178.github.io/online-tools/kmac128/)
[KMAC256 Online](http://emn178.github.io/online-tools/kmac256/)
[KMACXOF128 Online](http://emn178.github.io/online-tools/kmacxof128/)
[KMACXOF256 Online](http://emn178.github.io/online-tools/kmacxof256/)
[TupleHash128 Online](http://emn178.github.io/online-tools/tuplehash128/)
[TupleHash256 Online](http://emn178.github.io/online-tools/tuplehash256/)
[TupleHashXOF128 Online](http://emn178.github.io/online-tools/tuplehashxof128/)
[TupleHashXOF256 Online](http://emn178.github.io/online-tools/tuplehashxof256/)
[ParallelHash128 Online](http://emn178.github.io/online-tools/parallelhash128/)
[ParallelHash256 Online](http://emn178.github.io/online-tools/parallelhash256/)
[ParallelHashXOF128 Online](http://emn178.github.io/online-tools/parallelhashxof128/)
[ParallelHashXOF256 Online](http://emn178.github.io/online-tools/parallelhashxof256/)
## Download ## Download
[Compress](https://raw.github.com/emn178/js-sha3/master/build/sha3.min.js) [Compress](https://raw.github.com/emn178/js-sha3/master/build/sha3.min.js)
@@ -59,6 +73,10 @@ tuplehash128(['abc', 'd'], 256, 'customization');
tuplehash256(['abc', 'd'], 512, 'customization'); tuplehash256(['abc', 'd'], 512, 'customization');
tuplehashxof128(['abc', 'd'], 256, 'customization'); tuplehashxof128(['abc', 'd'], 256, 'customization');
tuplehashxof256(['abc', 'd'], 512, 'customization'); tuplehashxof256(['abc', 'd'], 512, 'customization');
parallelhash128('Message to hash', 8, 256, 'customization');
parallelhash256('Message to hash', 8, 512, 'customization');
parallelhashxof128('Message to hash', 8, 256, 'customization');
parallelhashxof256('Message to hash', 8, 512, 'customization');
// Support ArrayBuffer output // Support ArrayBuffer output
var arrayBuffer = keccak224.arrayBuffer('Message to hash'); var arrayBuffer = keccak224.arrayBuffer('Message to hash');
@@ -112,6 +130,13 @@ tupleHash.updateChunk([0x63]);
tupleHash.beginInput(1); tupleHash.beginInput(1);
tupleHash.updateChunk([0x64]); tupleHash.updateChunk([0x64]);
tupleHash.hex(); tupleHash.hex();
// ParallelHash: hash one large input in fixed-size blocks.
parallelhash128('Message to hash', 8, 256, '');
parallelhash128.create(8, 256, '')
.update('Message ')
.update('to hash')
.hex();
``` ```
### Node.js ### Node.js
If you use node.js, you should require the module first: If you use node.js, you should require the module first:
@@ -136,7 +161,11 @@ const {
tuplehash128, tuplehash128,
tuplehash256, tuplehash256,
tuplehashxof128, tuplehashxof128,
tuplehashxof256 tuplehashxof256,
parallelhash128,
parallelhash256,
parallelhashxof128,
parallelhashxof256
} = require('js-sha3'); } = require('js-sha3');
``` ```
@@ -163,7 +192,11 @@ import {
tuplehash128, tuplehash128,
tuplehash256, tuplehash256,
tuplehashxof128, tuplehashxof128,
tuplehashxof256 tuplehashxof256,
parallelhash128,
parallelhash256,
parallelhashxof128,
parallelhashxof256
} from 'js-sha3'; } from 'js-sha3';
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "js-sha3", "name": "js-sha3",
"version": "0.12.0", "version": "0.13.0",
"main": ["src/sha3.js"], "main": ["src/sha3.js"],
"ignore": [ "ignore": [
"samples", "samples",
+2 -2
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
File diff suppressed because one or more lines are too long
+132 -3
View File
@@ -9,7 +9,7 @@ var sha3$1 = {exports: {}};
/** /**
* [js-sha3]{@link https://github.com/emn178/js-sha3} * [js-sha3]{@link https://github.com/emn178/js-sha3}
* *
* @version 0.12.0 * @version 0.13.0
* @author Chen, Yi-Cyuan [emn178@gmail.com] * @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2015-2026 * @copyright Chen, Yi-Cyuan 2015-2026
* @license MIT * @license MIT
@@ -25,6 +25,7 @@ var sha3$1 = {exports: {}};
var TUPLE_INCOMPLETE_ERROR = 'tuple input is incomplete'; var TUPLE_INCOMPLETE_ERROR = 'tuple input is incomplete';
var TUPLE_LENGTH_ERROR = 'tuple input exceeds declared length'; var TUPLE_LENGTH_ERROR = 'tuple input exceeds declared length';
var TUPLE_BYTE_LENGTH_ERROR = 'tuple input byte length is invalid'; var TUPLE_BYTE_LENGTH_ERROR = 'tuple input byte length is invalid';
var BLOCK_SIZE_ERROR = 'block size is invalid';
var WINDOW = typeof window === 'object'; var WINDOW = typeof window === 'object';
var root = WINDOW ? window : {}; var root = WINDOW ? window : {};
if (root.JS_SHA3_NO_WINDOW) { if (root.JS_SHA3_NO_WINDOW) {
@@ -131,6 +132,12 @@ var sha3$1 = {exports: {}};
}; };
}; };
var createParallelHashOutputMethod = function (bits, padding, xof, outputType) {
return function (message, blockSize, outputBits, s) {
return methods[(xof ? 'parallelhashxof' : 'parallelhash') + bits].update(message, blockSize, outputBits, s)[outputType]();
};
};
var createOutputMethods = function (method, createMethod, bits, padding) { var createOutputMethods = function (method, createMethod, bits, padding) {
for (var i = 0; i < OUTPUT_TYPES.length; ++i) { for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i]; var type = OUTPUT_TYPES[i];
@@ -212,6 +219,26 @@ var sha3$1 = {exports: {}};
}, bits, padding); }, bits, padding);
}; };
var createParallelHashMethod = function (bits, padding, xof) {
var w = CSHAKE_BYTEPAD[bits];
var method = createParallelHashOutputMethod(bits, padding, xof, 'hex');
method.create = function (blockSize, outputBits, s) {
if (typeof blockSize !== 'number' || !isFinite(blockSize) || blockSize < 1 ||
Math.floor(blockSize) !== blockSize || blockSize > 0x0fffffff) {
throw new Error(BLOCK_SIZE_ERROR);
}
var hash = new ParallelHash(bits, padding, outputBits, xof, blockSize).bytepad(['ParallelHash', s], w);
hash.encode(blockSize, false);
return hash;
};
method.update = function (message, blockSize, outputBits, s) {
return method.create(blockSize, outputBits, s).update(message);
};
return createOutputMethods(method, function (b, p, outputType) {
return createParallelHashOutputMethod(b, p, xof, outputType);
}, bits, padding);
};
var algorithms = [ var algorithms = [
{ name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod }, { name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod },
{ name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod }, { name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod },
@@ -228,6 +255,12 @@ var sha3$1 = {exports: {}};
}}, }},
{ name: 'tuplehashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) { { name: 'tuplehashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
return createTupleHashMethod(bits, padding, true); return createTupleHashMethod(bits, padding, true);
}},
{ name: 'parallelhash', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
return createParallelHashMethod(bits, padding, false);
}},
{ name: 'parallelhashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
return createParallelHashMethod(bits, padding, true);
}} }}
]; ];
@@ -597,6 +630,93 @@ var sha3$1 = {exports: {}};
return Kmac.prototype.finalize.call(this); return Kmac.prototype.finalize.call(this);
}; };
function ParallelHash(bits, padding, outputBits, xof, blockSize) {
Kmac.call(this, bits, padding, outputBits, xof);
this.bits = bits;
this.blockSize = blockSize;
this.inner = null;
this.innerBytes = 0;
this.blockNumber = 0;
}
ParallelHash.prototype = new Kmac();
ParallelHash.prototype._finishInner = function () {
Kmac.prototype.update.call(this, this.inner.array());
this.inner = null;
this.innerBytes = 0;
++this.blockNumber;
};
ParallelHash.prototype._updateBytes = function (message) {
var length = message.length;
if (!length) {
return;
}
var slice = message.subarray || message.slice;
var blockSize = this.blockSize;
var index = 0;
while (index < length) {
if (!this.inner) {
this.inner = new Keccak(this.bits, SHAKE_PADDING, this.bits << 1);
}
var take = blockSize - this.innerBytes;
if (length - index < take) {
take = length - index;
}
this.inner.update(slice.call(message, index, index + take));
this.innerBytes += take;
index += take;
if (this.innerBytes === blockSize) {
this._finishInner();
}
}
};
ParallelHash.prototype.update = function (message) {
if (this.finalized) {
throw new Error(FINALIZE_ERROR);
}
var result = formatMessage(message);
message = result[0];
if (result[1]) {
var bytes = [], length = message.length, index = 0, code, i;
for (i = 0; i < length; ++i) {
code = message.charCodeAt(i);
if (code < 0x80) {
bytes[index++] = code;
} else if (code < 0x800) {
bytes[index++] = (0xc0 | (code >>> 6));
bytes[index++] = (0x80 | (code & 0x3f));
} else if (code < 0xd800 || code >= 0xe000) {
bytes[index++] = (0xe0 | (code >>> 12));
bytes[index++] = (0x80 | ((code >>> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f));
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++i) & 0x3ff));
bytes[index++] = (0xf0 | (code >>> 18));
bytes[index++] = (0x80 | ((code >>> 12) & 0x3f));
bytes[index++] = (0x80 | ((code >>> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f));
}
}
message = bytes;
}
this._updateBytes(message);
return this;
};
ParallelHash.prototype.finalize = function () {
if (!this.finalized) {
if (this.inner) {
this._finishInner();
}
this.encode(this.blockNumber, true);
}
return Kmac.prototype.finalize.call(this);
};
var f = function (s) { var f = function (s) {
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
@@ -836,7 +956,16 @@ const {
tuplehashxof_128, tuplehashxof_128,
tuplehashxof_256, tuplehashxof_256,
tuplehashxof128, tuplehashxof128,
tuplehashxof256 tuplehashxof256,
parallelhash_128,
parallelhash_256,
parallelhash128,
parallelhash256,
parallelhashxof_128,
parallelhashxof_256,
parallelhashxof128,
parallelhashxof256
} = sha3; } = sha3;
export { cshake128, cshake256, cshake_128, cshake_256, sha3 as default, keccak224, keccak256, keccak384, keccak512, keccak_224, keccak_256, keccak_384, keccak_512, kmac128, kmac256, kmac_128, kmac_256, kmacxof128, kmacxof256, kmacxof_128, kmacxof_256, sha3_224, sha3_256, sha3_384, sha3_512, shake128, shake256, shake_128, shake_256, tuplehash128, tuplehash256, tuplehash_128, tuplehash_256, tuplehashxof128, tuplehashxof256, tuplehashxof_128, tuplehashxof_256 }; export { cshake128, cshake256, cshake_128, cshake_256, sha3 as default, keccak224, keccak256, keccak384, keccak512, keccak_224, keccak_256, keccak_384, keccak_512, kmac128, kmac256, kmac_128, kmac_256, kmacxof128, kmacxof256, kmacxof_128, kmacxof_256, parallelhash128, parallelhash256, parallelhash_128, parallelhash_256, parallelhashxof128, parallelhashxof256, parallelhashxof_128, parallelhashxof_256, sha3_224, sha3_256, sha3_384, sha3_512, shake128, shake256, shake_128, shake_256, tuplehash128, tuplehash256, tuplehash_128, tuplehash_256, tuplehashxof128, tuplehashxof256, tuplehashxof_128, tuplehashxof_256 };
Vendored
+89
View File
@@ -417,3 +417,92 @@ export var tuplehashxof_128: TupleHashMethod;
export var tuplehashxof_256: TupleHashMethod; export var tuplehashxof_256: TupleHashMethod;
export var tuplehashxof128: TupleHashMethod; export var tuplehashxof128: TupleHashMethod;
export var tuplehashxof256: TupleHashMethod; export var tuplehashxof256: TupleHashMethod;
interface ParallelHash extends Hasher {
/**
* Absorb message bytes into ParallelHash blocks.
*
* @param message The next message chunk.
*/
update(message: Message): ParallelHash;
}
interface ParallelHashMethod {
/**
* Hash and return hex string.
*
* @param message The message you want to hash.
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
(message: Message, blockSize: number, outputBits: number, customization: Message): string;
/**
* Hash and return hex string.
*
* @param message The message you want to hash.
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
hex(message: Message, blockSize: number, outputBits: number, customization: Message): string;
/**
* Hash and return ArrayBuffer.
*
* @param message The message you want to hash.
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
arrayBuffer(message: Message, blockSize: number, outputBits: number, customization: Message): ArrayBuffer;
/**
* Hash and return integer array.
*
* @param message The message you want to hash.
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
digest(message: Message, blockSize: number, outputBits: number, customization: Message): number[];
/**
* Hash and return integer array.
*
* @param message The message you want to hash.
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
array(message: Message, blockSize: number, outputBits: number, customization: Message): number[];
/**
* Create a ParallelHash object.
*
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
create(blockSize: number, outputBits: number, customization: Message): ParallelHash;
/**
* Create a ParallelHash object and hash message.
*
* @param message The message you want to hash.
* @param blockSize The ParallelHash block size in bytes.
* @param outputBits The length of output.
* @param customization The customization string.
*/
update(message: Message, blockSize: number, outputBits: number, customization: Message): ParallelHash;
}
export var parallelhash_128: ParallelHashMethod;
export var parallelhash_256: ParallelHashMethod;
export var parallelhash128: ParallelHashMethod;
export var parallelhash256: ParallelHashMethod;
export var parallelhashxof_128: ParallelHashMethod;
export var parallelhashxof_256: ParallelHashMethod;
export var parallelhashxof128: ParallelHashMethod;
export var parallelhashxof256: ParallelHashMethod;
+942 -1118
View File
File diff suppressed because it is too large Load Diff
+14 -6
View File
@@ -1,7 +1,7 @@
{ {
"name": "js-sha3", "name": "js-sha3",
"version": "0.12.0", "version": "0.13.0",
"description": "A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash hash function for JavaScript supports UTF-8 encoding.", "description": "A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash / ParallelHash hash function for JavaScript supports UTF-8 encoding.",
"main": "src/sha3.js", "main": "src/sha3.js",
"module": "build/sha3.mjs", "module": "build/sha3.mjs",
"exports": { "exports": {
@@ -22,17 +22,20 @@
"@rollup/plugin-commonjs": "^28.0.5", "@rollup/plugin-commonjs": "^28.0.5",
"@rollup/plugin-terser": "^1.0.0", "@rollup/plugin-terser": "^1.0.0",
"expect.js": "~0.3.1", "expect.js": "~0.3.1",
"mocha": "^11.7.6", "mocha": "^11.8.0",
"nyc": "^15.1.0", "nyc": "^18.0.0",
"rollup": "^4.43.0", "rollup": "^4.43.0",
"tiny-worker": "^2.3.0", "tiny-worker": "^2.3.0",
"uglify-js": "^3.1.9" "uglify-js": "^3.1.9"
}, },
"scripts": { "scripts": {
"test": "nyc mocha tests/node-test.js", "test": "npm run build && npm run test:coverage",
"test:runtime": "mocha tests/node-test.js",
"test:coverage": "nyc --reporter=text --reporter=html --reporter=lcov mocha tests/node-test.js",
"report": "nyc --reporter=html --reporter=text mocha tests/node-test.js", "report": "nyc --reporter=html --reporter=text mocha tests/node-test.js",
"coveralls": "nyc report --reporter=text-lcov | coveralls", "coveralls": "nyc report --reporter=text-lcov | coveralls",
"build": "rollup -c && uglifyjs src/sha3.js -c -m --comments --output build/sha3.min.js" "build": "rollup -c && uglifyjs src/sha3.js -c -m --comments --output build/sha3.min.js",
"prepack": "npm run build"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -45,6 +48,7 @@
"cshake", "cshake",
"kmac", "kmac",
"tuplehash", "tuplehash",
"parallelhash",
"hash", "hash",
"encryption", "encryption",
"cryptography", "cryptography",
@@ -60,5 +64,9 @@
"exclude": [ "exclude": [
"tests" "tests"
] ]
},
"overrides": {
"diff": "^8.0.3",
"serialize-javascript": "^7.0.7"
} }
} }
+121 -1
View File
@@ -1,7 +1,7 @@
/** /**
* [js-sha3]{@link https://github.com/emn178/js-sha3} * [js-sha3]{@link https://github.com/emn178/js-sha3}
* *
* @version 0.12.0 * @version 0.13.0
* @author Chen, Yi-Cyuan [emn178@gmail.com] * @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2015-2026 * @copyright Chen, Yi-Cyuan 2015-2026
* @license MIT * @license MIT
@@ -16,6 +16,7 @@
var TUPLE_INCOMPLETE_ERROR = 'tuple input is incomplete'; var TUPLE_INCOMPLETE_ERROR = 'tuple input is incomplete';
var TUPLE_LENGTH_ERROR = 'tuple input exceeds declared length'; var TUPLE_LENGTH_ERROR = 'tuple input exceeds declared length';
var TUPLE_BYTE_LENGTH_ERROR = 'tuple input byte length is invalid'; var TUPLE_BYTE_LENGTH_ERROR = 'tuple input byte length is invalid';
var BLOCK_SIZE_ERROR = 'block size is invalid';
var WINDOW = typeof window === 'object'; var WINDOW = typeof window === 'object';
var root = WINDOW ? window : {}; var root = WINDOW ? window : {};
if (root.JS_SHA3_NO_WINDOW) { if (root.JS_SHA3_NO_WINDOW) {
@@ -123,6 +124,12 @@
}; };
}; };
var createParallelHashOutputMethod = function (bits, padding, xof, outputType) {
return function (message, blockSize, outputBits, s) {
return methods[(xof ? 'parallelhashxof' : 'parallelhash') + bits].update(message, blockSize, outputBits, s)[outputType]();
};
};
var createOutputMethods = function (method, createMethod, bits, padding) { var createOutputMethods = function (method, createMethod, bits, padding) {
for (var i = 0; i < OUTPUT_TYPES.length; ++i) { for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i]; var type = OUTPUT_TYPES[i];
@@ -204,6 +211,26 @@
}, bits, padding); }, bits, padding);
}; };
var createParallelHashMethod = function (bits, padding, xof) {
var w = CSHAKE_BYTEPAD[bits];
var method = createParallelHashOutputMethod(bits, padding, xof, 'hex');
method.create = function (blockSize, outputBits, s) {
if (typeof blockSize !== 'number' || !isFinite(blockSize) || blockSize < 1 ||
Math.floor(blockSize) !== blockSize || blockSize > 0x0fffffff) {
throw new Error(BLOCK_SIZE_ERROR);
}
var hash = new ParallelHash(bits, padding, outputBits, xof, blockSize).bytepad(['ParallelHash', s], w);
hash.encode(blockSize, false);
return hash;
};
method.update = function (message, blockSize, outputBits, s) {
return method.create(blockSize, outputBits, s).update(message);
};
return createOutputMethods(method, function (b, p, outputType) {
return createParallelHashOutputMethod(b, p, xof, outputType);
}, bits, padding);
};
var algorithms = [ var algorithms = [
{ name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod }, { name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod },
{ name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod }, { name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod },
@@ -220,6 +247,12 @@
}}, }},
{ name: 'tuplehashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) { { name: 'tuplehashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
return createTupleHashMethod(bits, padding, true); return createTupleHashMethod(bits, padding, true);
}},
{ name: 'parallelhash', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
return createParallelHashMethod(bits, padding, false);
}},
{ name: 'parallelhashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
return createParallelHashMethod(bits, padding, true);
}} }}
]; ];
@@ -589,6 +622,93 @@
return Kmac.prototype.finalize.call(this); return Kmac.prototype.finalize.call(this);
}; };
function ParallelHash(bits, padding, outputBits, xof, blockSize) {
Kmac.call(this, bits, padding, outputBits, xof);
this.bits = bits;
this.blockSize = blockSize;
this.inner = null;
this.innerBytes = 0;
this.blockNumber = 0;
}
ParallelHash.prototype = new Kmac();
ParallelHash.prototype._finishInner = function () {
Kmac.prototype.update.call(this, this.inner.array());
this.inner = null;
this.innerBytes = 0;
++this.blockNumber;
};
ParallelHash.prototype._updateBytes = function (message) {
var length = message.length;
if (!length) {
return;
}
var slice = message.subarray || message.slice;
var blockSize = this.blockSize;
var index = 0;
while (index < length) {
if (!this.inner) {
this.inner = new Keccak(this.bits, SHAKE_PADDING, this.bits << 1);
}
var take = blockSize - this.innerBytes;
if (length - index < take) {
take = length - index;
}
this.inner.update(slice.call(message, index, index + take));
this.innerBytes += take;
index += take;
if (this.innerBytes === blockSize) {
this._finishInner();
}
}
};
ParallelHash.prototype.update = function (message) {
if (this.finalized) {
throw new Error(FINALIZE_ERROR);
}
var result = formatMessage(message);
message = result[0];
if (result[1]) {
var bytes = [], length = message.length, index = 0, code, i;
for (i = 0; i < length; ++i) {
code = message.charCodeAt(i);
if (code < 0x80) {
bytes[index++] = code;
} else if (code < 0x800) {
bytes[index++] = (0xc0 | (code >>> 6));
bytes[index++] = (0x80 | (code & 0x3f));
} else if (code < 0xd800 || code >= 0xe000) {
bytes[index++] = (0xe0 | (code >>> 12));
bytes[index++] = (0x80 | ((code >>> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f));
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++i) & 0x3ff));
bytes[index++] = (0xf0 | (code >>> 18));
bytes[index++] = (0x80 | ((code >>> 12) & 0x3f));
bytes[index++] = (0x80 | ((code >>> 6) & 0x3f));
bytes[index++] = (0x80 | (code & 0x3f));
}
}
message = bytes;
}
this._updateBytes(message);
return this;
};
ParallelHash.prototype.finalize = function () {
if (!this.finalized) {
if (this.inner) {
this._finishInner();
}
this.encode(this.blockNumber, true);
}
return Kmac.prototype.finalize.call(this);
};
var f = function (s) { var f = function (s) {
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
+10 -1
View File
@@ -41,7 +41,16 @@ export const {
tuplehashxof_128, tuplehashxof_128,
tuplehashxof_256, tuplehashxof_256,
tuplehashxof128, tuplehashxof128,
tuplehashxof256 tuplehashxof256,
parallelhash_128,
parallelhash_256,
parallelhash128,
parallelhash256,
parallelhashxof_128,
parallelhashxof_256,
parallelhashxof128,
parallelhashxof256
} = sha3; } = sha3;
export default sha3; export default sha3;
+13
View File
@@ -22,6 +22,10 @@ function unset() {
tuplehash256 = null; tuplehash256 = null;
tuplehashxof128 = null; tuplehashxof128 = null;
tuplehashxof256 = null; tuplehashxof256 = null;
parallelhash128 = null;
parallelhash256 = null;
parallelhashxof128 = null;
parallelhashxof256 = null;
BUFFER = undefined; BUFFER = undefined;
JS_SHA3_NO_WINDOW = undefined; JS_SHA3_NO_WINDOW = undefined;
JS_SHA3_NO_NODE_JS = undefined; JS_SHA3_NO_NODE_JS = undefined;
@@ -53,6 +57,10 @@ function requireToGlobal() {
tuplehash256 = sha3.tuplehash256; tuplehash256 = sha3.tuplehash256;
tuplehashxof128 = sha3.tuplehashxof128; tuplehashxof128 = sha3.tuplehashxof128;
tuplehashxof256 = sha3.tuplehashxof256; tuplehashxof256 = sha3.tuplehashxof256;
parallelhash128 = sha3.parallelhash128;
parallelhash256 = sha3.parallelhash256;
parallelhashxof128 = sha3.parallelhashxof128;
parallelhashxof256 = sha3.parallelhashxof256;
} }
function runCommonJsTest() { function runCommonJsTest() {
@@ -70,6 +78,7 @@ function runWindowTest(extra) {
require('./test-cshake.js'); require('./test-cshake.js');
require('./test-kmac.js'); require('./test-kmac.js');
require('./test-tuplehash.js'); require('./test-tuplehash.js');
require('./test-parallelhash.js');
} }
unset(); unset();
} }
@@ -127,6 +136,10 @@ define = function (func) {
tuplehash256 = sha3.tuplehash256; tuplehash256 = sha3.tuplehash256;
tuplehashxof128 = sha3.tuplehashxof128; tuplehashxof128 = sha3.tuplehashxof128;
tuplehashxof256 = sha3.tuplehashxof256; tuplehashxof256 = sha3.tuplehashxof256;
parallelhash128 = sha3.parallelhash128;
parallelhash256 = sha3.parallelhash256;
parallelhashxof128 = sha3.parallelhashxof128;
parallelhashxof256 = sha3.parallelhashxof256;
require('./test.js'); require('./test.js');
}; };
define.amd = true; define.amd = true;
+251
View File
@@ -0,0 +1,251 @@
(function (parallelhash256, parallelhash128, parallelhashxof256, parallelhashxof128) {
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/ParallelHash_samples.pdf
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/ParallelHashXOF_samples.pdf
var d1 = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
];
var d2 = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b
];
var testCases = [
{
name: 'parallelhash128',
method: parallelhash128,
cases: [
{
message: d1,
blockSize: 8,
bits: 256,
s: '',
output: 'ba8dc1d1d979331d3f813603c67f72609ab5e44b94a0b8f9af46514454a2b4f5'
},
{
message: d1,
blockSize: 8,
bits: 256,
s: 'Parallel Data',
output: 'fc484dcb3f84dceedc353438151bee58157d6efed0445a81f165e495795b7206'
},
{
message: d2,
blockSize: 12,
bits: 256,
s: 'Parallel Data',
output: 'f7fd5312896c6685c828af7e2adb97e393e7f8d54e3c2ea4b95e5aca3796e8fc'
}
]
},
{
name: 'parallelhash256',
method: parallelhash256,
cases: [
{
message: d1,
blockSize: 8,
bits: 512,
s: '',
output: 'bc1ef124da34495e948ead207dd9842235da432d2bbc54b4c110e64c451105531b7f2a3e0ce055c02805e7c2de1fb746af97a1dd01f43b824e31b87612410429'
},
{
message: d1,
blockSize: 8,
bits: 512,
s: 'Parallel Data',
output: 'cdf15289b54f6212b4bc270528b49526006dd9b54e2b6add1ef6900dda3963bb33a72491f236969ca8afaea29c682d47a393c065b38e29fae651a2091c833110'
},
{
message: d2,
blockSize: 12,
bits: 512,
s: 'Parallel Data',
output: '69d0fcb764ea055dd09334bc6021cb7e4b61348dff375da262671cdec3effa8d1b4568a6cce16b1cad946ddde27f6ce2b8dee4cd1b24851ebf00eb90d43813e9'
}
]
},
{
name: 'parallelhashxof128',
method: parallelhashxof128,
cases: [
{
message: d1,
blockSize: 8,
bits: 256,
s: '',
output: 'fe47d661e49ffe5b7d999922c062356750caf552985b8e8ce6667f2727c3c8d3'
},
{
message: d1,
blockSize: 8,
bits: 256,
s: 'Parallel Data',
output: 'ea2a793140820f7a128b8eb70a9439f93257c6e6e79b4a540d291d6dae7098d7'
},
{
message: d2,
blockSize: 12,
bits: 256,
s: 'Parallel Data',
output: '0127ad9772ab904691987fcc4a24888f341fa0db2145e872d4efd255376602f0'
}
]
},
{
name: 'parallelhashxof256',
method: parallelhashxof256,
cases: [
{
message: d1,
blockSize: 8,
bits: 512,
s: '',
output: 'c10a052722614684144d28474850b410757e3cba87651ba167a5cbddff7f466675fbf84bcae7378ac444be681d729499afca667fb879348bfdda427863c82f1c'
},
{
message: d1,
blockSize: 8,
bits: 512,
s: 'Parallel Data',
output: '538e105f1a22f44ed2f5cc1674fbd40be803d9c99bf5f8d90a2c8193f3fe6ea768e5c1a20987e2c9c65febed03887a51d35624ed12377594b5585541dc377efc'
},
{
message: d2,
blockSize: 12,
bits: 512,
s: 'Parallel Data',
output: '6b3e790b330c889a204c2fbc728d809f19367328d852f4002dc829f73afd6bcefb7fe5b607b13a801c0be5c1170bdb794e339458fdb0e62a6af3d42558970249'
}
]
}
];
testCases.forEach(function (testCase) {
describe('#' + testCase.name, function () {
testCase.cases.forEach(function (c) {
it('should match NIST sample vector', function () {
expect(testCase.method(c.message, c.blockSize, c.bits, c.s)).to.be(c.output);
});
});
});
});
describe('#parallelhash API', function () {
it('should match instance update and method update with one-shot', function () {
var expected = parallelhash128(d1, 8, 256, 'cache');
var incremental = parallelhash128.create(8, 256, 'cache').update(d1.slice(0, 10)).update(d1.slice(10)).hex();
var methodUpdate = parallelhash128.update(d1.slice(0, 5), 8, 256, 'cache').update(d1.slice(5)).hex();
expect(incremental).to.be(expected);
expect(methodUpdate).to.be(expected);
expect(parallelhash128.update(d1, 8, 256, 'cache').hex()).to.be(expected);
});
it('should stream binary messages in irregular chunks', function () {
var expected = parallelhash128(d2, 12, 256, '');
var hash = parallelhash128.create(12, 256, '');
hash.update(d2.slice(0, 1));
hash.update(d2.slice(1, 17));
hash.update(d2.slice(17, 50));
hash.update(d2.slice(50));
expect(hash.hex()).to.be(expected);
});
it('should stream ArrayBuffer and Uint8Array views', function () {
var expected = parallelhash128(d1, 8, 256, '');
var buffer = new Uint8Array(d1).buffer;
var view = new Uint8Array(new Uint8Array(d1).buffer, 0, d1.length);
expect(parallelhash128.create(8, 256, '').update(buffer).hex()).to.be(expected);
expect(parallelhash128.create(8, 256, '').update(view).hex()).to.be(expected);
});
it('should hash empty message with n = 0', function () {
var empty = parallelhash128([], 8, 256, '');
var emptyString = parallelhash128('', 8, 256, '');
expect(empty).to.be(emptyString);
expect(empty).to.not.be(parallelhash128([0x00], 8, 256, ''));
});
it('should handle UTF-8 strings across block boundaries', function () {
var message = 'åbc'; // 2 + 1 + 1 = 4 UTF-8 bytes
var expected = parallelhash128(message, 3, 256, '');
var hash = parallelhash128.create(3, 256, '');
hash.update('å').update('bc');
expect(hash.hex()).to.be(expected);
});
it('should encode 3-byte and 4-byte UTF-8 characters', function () {
var message = '中\uE000\uD83D\uDE00'; // 3 + 3 + 4 = 10 UTF-8 bytes
var expected = parallelhash128(message, 4, 256, '');
var hash = parallelhash128.create(4, 256, '');
hash.update('中').update('\uE000').update('\uD83D\uDE00');
expect(hash.hex()).to.be(expected);
expect(hash.array().length).to.be(32);
});
it('should keep absorbing when update is shorter than remaining block space', function () {
var expected = parallelhash128(d1, 8, 256, '');
var hash = parallelhash128.create(8, 256, '');
hash.update(d1.slice(0, 3)); // partial inner block, need 5 more
hash.update(d1.slice(3, 5)); // still short, continue same inner SHAKE
hash.update(d1.slice(5)); // finish remaining blocks
expect(hash.hex()).to.be(expected);
});
it('should not be affected by mutating input after update', function () {
var expected = parallelhash128(d1, 8, 256, '');
var chunk = new Uint8Array(d1.slice(0, 3));
var rest = new Uint8Array(d1.slice(3));
var hash = parallelhash128.create(8, 256, '');
hash.update(chunk);
chunk[0] = 0xff;
chunk[1] = 0xff;
chunk[2] = 0xff;
hash.update(rest);
expect(hash.hex()).to.be(expected);
});
it('should allow repeated output representations', function () {
var hash = parallelhash128.create(8, 256, '').update(d1);
var hex = hash.hex();
var array = hash.array();
var digest = hash.digest();
var buffer = hash.arrayBuffer();
expect(hash.hex()).to.be(hex);
expect(array).to.eql(digest);
expect(Array.prototype.slice.call(new Uint8Array(buffer))).to.eql(array);
expect(function () { hash.update(d1); }).to.throwError(/finalize already called/);
});
it('should require customization like KMAC', function () {
expect(function () { parallelhash128(d1, 8, 256); }).to.throwError(/input is invalid type/);
});
it('should reject invalid block sizes', function () {
expect(function () { parallelhash128.create(0, 256, ''); }).to.throwError(/block size is invalid/);
expect(function () { parallelhash128.create(-1, 256, ''); }).to.throwError(/block size is invalid/);
expect(function () { parallelhash128.create(1.5, 256, ''); }).to.throwError(/block size is invalid/);
expect(function () { parallelhash128.create(NaN, 256, ''); }).to.throwError(/block size is invalid/);
expect(function () { parallelhash128.create(0x20000000, 256, ''); }).to.throwError(/block size is invalid/);
});
it('should export identical aliases', function () {
var sha3 = require('../src/sha3.js');
expect(sha3.parallelhash128).to.be(sha3.parallelhash_128);
expect(sha3.parallelhash256).to.be(sha3.parallelhash_256);
expect(sha3.parallelhashxof128).to.be(sha3.parallelhashxof_128);
expect(sha3.parallelhashxof256).to.be(sha3.parallelhashxof_256);
});
it('should reject update after finalize', function () {
var hash = parallelhash128.create(8, 256, '').update([0x00]);
hash.hex();
expect(function () { hash.update([0x01]); }).to.throwError(/finalize already called/);
});
});
})(parallelhash256, parallelhash128, parallelhashxof256, parallelhashxof128);