mirror of
https://github.com/emn178/js-sha3.git
synced 2026-08-13 03:51:54 +08:00
Compare commits
1
Commits
f0ab0aec2c
...
v0.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01b1baf1e7 |
@@ -1,5 +1,12 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## v0.11.0 / 2026-07-18
|
||||||
|
### Added
|
||||||
|
- TupleHash128, TupleHash256, TupleHashXOF128, and TupleHashXOF256 (NIST SP 800-185)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- KMAC repeated output reads after finalize
|
||||||
|
|
||||||
## v0.10.0 / 2026-07-17
|
## v0.10.0 / 2026-07-17
|
||||||
### Added
|
### Added
|
||||||
- support ESM #42, #38
|
- support ESM #42, #38
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
[](https://coveralls.io/r/emn178/js-sha3?branch=master)
|
[](https://coveralls.io/r/emn178/js-sha3?branch=master)
|
||||||
[](https://nodei.co/npm/js-sha3/)
|
[](https://nodei.co/npm/js-sha3/)
|
||||||
|
|
||||||
A simple SHA-3 / Keccak / Shake hash function for JavaScript supports UTF-8 encoding.
|
A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash 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.
|
||||||
@@ -53,6 +53,10 @@ cshake128('Message to hash', 256, 'function name', 'customization');
|
|||||||
cshake256('Message to hash', 512, 'function name', 'customization');
|
cshake256('Message to hash', 512, 'function name', 'customization');
|
||||||
kmac128('key', 'Message to hash', 256, 'customization');
|
kmac128('key', 'Message to hash', 256, 'customization');
|
||||||
kmac256('key', 'Message to hash', 512, 'customization');
|
kmac256('key', 'Message to hash', 512, 'customization');
|
||||||
|
tuplehash128(['abc', 'd'], 256, 'customization');
|
||||||
|
tuplehash256(['abc', 'd'], 512, 'customization');
|
||||||
|
tuplehashxof128(['abc', 'd'], 256, 'customization');
|
||||||
|
tuplehashxof256(['abc', 'd'], 512, 'customization');
|
||||||
|
|
||||||
// Support ArrayBuffer output
|
// Support ArrayBuffer output
|
||||||
var arrayBuffer = keccak224.arrayBuffer('Message to hash');
|
var arrayBuffer = keccak224.arrayBuffer('Message to hash');
|
||||||
@@ -83,6 +87,29 @@ var hash = cshake128.create(256, 'function name', 'customization');
|
|||||||
|
|
||||||
// specify kmac key, output bits and customization when creating
|
// specify kmac key, output bits and customization when creating
|
||||||
var hash = kmac128.create('key', 256, 'customization');
|
var hash = kmac128.create('key', 256, 'customization');
|
||||||
|
|
||||||
|
// TupleHash: a tuple contains zero or more input strings.
|
||||||
|
// One-shot and method-level update
|
||||||
|
tuplehash128(['abc', 'd'], 256, '');
|
||||||
|
tuplehash128.update(['abc', 'd'], 256, '').hex();
|
||||||
|
|
||||||
|
// Incremental complete inputs (each update is one tuple input string)
|
||||||
|
tuplehash128.create(256, '')
|
||||||
|
.update('abc')
|
||||||
|
.update('d')
|
||||||
|
.hex();
|
||||||
|
|
||||||
|
// Streaming a large input when its byte length is known in advance.
|
||||||
|
// beginInput + updateChunk; no endInput() is needed.
|
||||||
|
// Inputs are absorbed sequentially. TupleHash does not parallelize tuple inputs;
|
||||||
|
// use ParallelHash for parallel hashing of one large input.
|
||||||
|
var tupleHash = tuplehash128.create(256, '');
|
||||||
|
tupleHash.beginInput(3);
|
||||||
|
tupleHash.updateChunk([0x61, 0x62]);
|
||||||
|
tupleHash.updateChunk([0x63]);
|
||||||
|
tupleHash.beginInput(1);
|
||||||
|
tupleHash.updateChunk([0x64]);
|
||||||
|
tupleHash.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:
|
||||||
@@ -101,7 +128,11 @@ const {
|
|||||||
cshake128,
|
cshake128,
|
||||||
cshake256,
|
cshake256,
|
||||||
kmac128,
|
kmac128,
|
||||||
kmac256
|
kmac256,
|
||||||
|
tuplehash128,
|
||||||
|
tuplehash256,
|
||||||
|
tuplehashxof128,
|
||||||
|
tuplehashxof256
|
||||||
} = require('js-sha3');
|
} = require('js-sha3');
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -122,7 +153,11 @@ import {
|
|||||||
cshake128,
|
cshake128,
|
||||||
cshake256,
|
cshake256,
|
||||||
kmac128,
|
kmac128,
|
||||||
kmac256
|
kmac256,
|
||||||
|
tuplehash128,
|
||||||
|
tuplehash256,
|
||||||
|
tuplehashxof128,
|
||||||
|
tuplehashxof256
|
||||||
} from 'js-sha3';
|
} from 'js-sha3';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+154
-8
@@ -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.10.0
|
* @version 0.11.0
|
||||||
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||||
* @copyright Chen, Yi-Cyuan 2015-2023
|
* @copyright Chen, Yi-Cyuan 2015-2023
|
||||||
* @license MIT
|
* @license MIT
|
||||||
@@ -21,6 +21,10 @@ var sha3$1 = {exports: {}};
|
|||||||
|
|
||||||
var INPUT_ERROR = 'input is invalid type';
|
var INPUT_ERROR = 'input is invalid type';
|
||||||
var FINALIZE_ERROR = 'finalize already called';
|
var FINALIZE_ERROR = 'finalize already called';
|
||||||
|
var TUPLE_ACTIVE_ERROR = 'no active tuple input';
|
||||||
|
var TUPLE_INCOMPLETE_ERROR = 'tuple input is incomplete';
|
||||||
|
var TUPLE_LENGTH_ERROR = 'tuple input exceeds declared length';
|
||||||
|
var TUPLE_BYTE_LENGTH_ERROR = 'tuple input byte length 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) {
|
||||||
@@ -179,12 +183,45 @@ var sha3$1 = {exports: {}};
|
|||||||
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var createTupleHashOutputMethod = function (bits, padding, xof, outputType) {
|
||||||
|
return function (inputs, outputBits, s) {
|
||||||
|
return methods[(xof ? 'tuplehashxof' : 'tuplehash') + bits].update(inputs, outputBits, s)[outputType]();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createTupleHashMethod = function (bits, padding, xof) {
|
||||||
|
var w = CSHAKE_BYTEPAD[bits];
|
||||||
|
var method = createTupleHashOutputMethod(bits, padding, xof, 'hex');
|
||||||
|
method.create = function (outputBits, s) {
|
||||||
|
return new TupleHash(bits, padding, outputBits, xof).bytepad(['TupleHash', s], w);
|
||||||
|
};
|
||||||
|
method.update = function (inputs, outputBits, s) {
|
||||||
|
if (!isArray(inputs)) {
|
||||||
|
throw new Error(INPUT_ERROR);
|
||||||
|
}
|
||||||
|
var hash = method.create(outputBits, s);
|
||||||
|
for (var i = 0; i < inputs.length; ++i) {
|
||||||
|
hash.update(inputs[i]);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
return createOutputMethods(method, function (b, p, outputType) {
|
||||||
|
return createTupleHashOutputMethod(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 },
|
||||||
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
||||||
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
||||||
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod }
|
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod },
|
||||||
|
{ name: 'tuplehash', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
|
||||||
|
return createTupleHashMethod(bits, padding, false);
|
||||||
|
}},
|
||||||
|
{ name: 'tuplehashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
|
||||||
|
return createTupleHashMethod(bits, padding, true);
|
||||||
|
}}
|
||||||
];
|
];
|
||||||
|
|
||||||
var methods = {}, methodNames = [];
|
var methods = {}, methodNames = [];
|
||||||
@@ -298,7 +335,7 @@ var sha3$1 = {exports: {}};
|
|||||||
} else {
|
} else {
|
||||||
bytes.unshift(n);
|
bytes.unshift(n);
|
||||||
}
|
}
|
||||||
this.update(bytes);
|
Keccak.prototype.update.call(this, bytes);
|
||||||
return bytes.length;
|
return bytes.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -325,7 +362,7 @@ var sha3$1 = {exports: {}};
|
|||||||
bytes = length;
|
bytes = length;
|
||||||
}
|
}
|
||||||
bytes += this.encode(bytes * 8);
|
bytes += this.encode(bytes * 8);
|
||||||
this.update(str);
|
Keccak.prototype.update.call(this, str);
|
||||||
return bytes;
|
return bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -337,7 +374,7 @@ var sha3$1 = {exports: {}};
|
|||||||
var paddingBytes = (w - bytes % w) % w;
|
var paddingBytes = (w - bytes % w) % w;
|
||||||
var zeros = [];
|
var zeros = [];
|
||||||
zeros.length = paddingBytes;
|
zeros.length = paddingBytes;
|
||||||
this.update(zeros);
|
Keccak.prototype.update.call(this, zeros);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -466,7 +503,107 @@ var sha3$1 = {exports: {}};
|
|||||||
Kmac.prototype = new Keccak();
|
Kmac.prototype = new Keccak();
|
||||||
|
|
||||||
Kmac.prototype.finalize = function () {
|
Kmac.prototype.finalize = function () {
|
||||||
this.encode(this.outputBits, true);
|
if (!this.finalized) {
|
||||||
|
this.encode(this.outputBits, true);
|
||||||
|
}
|
||||||
|
return Keccak.prototype.finalize.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
function TupleHash(bits, padding, outputBits, xof) {
|
||||||
|
Keccak.call(this, bits, padding, outputBits);
|
||||||
|
this.xof = !!xof;
|
||||||
|
this.inputActive = false;
|
||||||
|
this.inputBytesRemaining = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TupleHash.prototype = new Keccak();
|
||||||
|
|
||||||
|
TupleHash.prototype.getMessageByteLength = function (message) {
|
||||||
|
var result = formatMessage(message);
|
||||||
|
message = result[0];
|
||||||
|
if (!result[1]) {
|
||||||
|
return message.length;
|
||||||
|
}
|
||||||
|
var bytes = 0;
|
||||||
|
for (var i = 0; i < message.length; ++i) {
|
||||||
|
var code = message.charCodeAt(i);
|
||||||
|
if (code < 0x80) {
|
||||||
|
bytes += 1;
|
||||||
|
} else if (code < 0x800) {
|
||||||
|
bytes += 2;
|
||||||
|
} else if (code < 0xd800 || code >= 0xe000) {
|
||||||
|
bytes += 3;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
bytes += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.beginInput = function (byteLength) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
if (this.inputActive) {
|
||||||
|
throw new Error(TUPLE_INCOMPLETE_ERROR);
|
||||||
|
}
|
||||||
|
if (typeof byteLength !== 'number' || !isFinite(byteLength) || byteLength < 0 ||
|
||||||
|
Math.floor(byteLength) !== byteLength || byteLength > 0x0fffffff) {
|
||||||
|
throw new Error(TUPLE_BYTE_LENGTH_ERROR);
|
||||||
|
}
|
||||||
|
this.encode(byteLength * 8, false);
|
||||||
|
if (byteLength === 0) {
|
||||||
|
this.inputActive = false;
|
||||||
|
this.inputBytesRemaining = 0;
|
||||||
|
} else {
|
||||||
|
this.inputActive = true;
|
||||||
|
this.inputBytesRemaining = byteLength;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.updateChunk = function (message) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
if (!this.inputActive) {
|
||||||
|
throw new Error(TUPLE_ACTIVE_ERROR);
|
||||||
|
}
|
||||||
|
var byteLength = this.getMessageByteLength(message);
|
||||||
|
if (byteLength > this.inputBytesRemaining) {
|
||||||
|
throw new Error(TUPLE_LENGTH_ERROR);
|
||||||
|
}
|
||||||
|
Keccak.prototype.update.call(this, message);
|
||||||
|
this.inputBytesRemaining -= byteLength;
|
||||||
|
if (this.inputBytesRemaining === 0) {
|
||||||
|
this.inputActive = false;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.update = function (message) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
if (this.inputActive) {
|
||||||
|
throw new Error(TUPLE_INCOMPLETE_ERROR);
|
||||||
|
}
|
||||||
|
var byteLength = this.getMessageByteLength(message);
|
||||||
|
this.beginInput(byteLength);
|
||||||
|
if (byteLength === 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.updateChunk(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.finalize = function () {
|
||||||
|
if (this.inputActive) {
|
||||||
|
throw new Error(TUPLE_INCOMPLETE_ERROR);
|
||||||
|
}
|
||||||
|
if (!this.finalized) {
|
||||||
|
this.encode(this.xof ? 0 : this.outputBits, true);
|
||||||
|
}
|
||||||
return Keccak.prototype.finalize.call(this);
|
return Keccak.prototype.finalize.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -696,7 +833,16 @@ const {
|
|||||||
kmac_128,
|
kmac_128,
|
||||||
kmac_256,
|
kmac_256,
|
||||||
kmac128,
|
kmac128,
|
||||||
kmac256
|
kmac256,
|
||||||
|
|
||||||
|
tuplehash_128,
|
||||||
|
tuplehash_256,
|
||||||
|
tuplehash128,
|
||||||
|
tuplehash256,
|
||||||
|
tuplehashxof_128,
|
||||||
|
tuplehashxof_256,
|
||||||
|
tuplehashxof128,
|
||||||
|
tuplehashxof256
|
||||||
} = 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, sha3_224, sha3_256, sha3_384, sha3_512, shake128, shake256, shake_128, shake_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, 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
+98
@@ -291,6 +291,96 @@ interface KmacHash {
|
|||||||
update(key: Message, message: Message, outputBits: number, customization: Message): Hasher;
|
update(key: Message, message: Message, outputBits: number, customization: Message): Hasher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TupleInput = Message[];
|
||||||
|
|
||||||
|
interface TupleHash extends Hasher {
|
||||||
|
/**
|
||||||
|
* Start a streaming tuple input with a known UTF-8/binary byte length.
|
||||||
|
*
|
||||||
|
* @param byteLength The byte length of the input that will follow through updateChunk().
|
||||||
|
*/
|
||||||
|
beginInput(byteLength: number): TupleHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absorb part of the active streaming tuple input.
|
||||||
|
*
|
||||||
|
* @param message The next message chunk.
|
||||||
|
*/
|
||||||
|
updateChunk(message: Message): TupleHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append one complete tuple input string.
|
||||||
|
* Equivalent to beginInput(byteLength) followed by updateChunk(message).
|
||||||
|
*
|
||||||
|
* @param message The input string to encode and absorb.
|
||||||
|
*/
|
||||||
|
update(message: Message): TupleHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TupleHashMethod {
|
||||||
|
/**
|
||||||
|
* Hash a tuple and return hex string.
|
||||||
|
*
|
||||||
|
* @param inputs The tuple of input strings.
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
(inputs: TupleInput, outputBits: number, customization: Message): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a tuple and return hex string.
|
||||||
|
*
|
||||||
|
* @param inputs The tuple of input strings.
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
hex(inputs: TupleInput, outputBits: number, customization: Message): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a tuple and return ArrayBuffer.
|
||||||
|
*
|
||||||
|
* @param inputs The tuple of input strings.
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
arrayBuffer(inputs: TupleInput, outputBits: number, customization: Message): ArrayBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a tuple and return integer array.
|
||||||
|
*
|
||||||
|
* @param inputs The tuple of input strings.
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
digest(inputs: TupleInput, outputBits: number, customization: Message): number[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a tuple and return integer array.
|
||||||
|
*
|
||||||
|
* @param inputs The tuple of input strings.
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
array(inputs: TupleInput, outputBits: number, customization: Message): number[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TupleHash object.
|
||||||
|
*
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
create(outputBits: number, customization: Message): TupleHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TupleHash object and absorb the given tuple inputs.
|
||||||
|
*
|
||||||
|
* @param inputs The tuple of input strings.
|
||||||
|
* @param outputBits The length of output.
|
||||||
|
* @param customization The customization string.
|
||||||
|
*/
|
||||||
|
update(inputs: TupleInput, outputBits: number, customization: Message): TupleHash;
|
||||||
|
}
|
||||||
|
|
||||||
export var sha3_512: Hash;
|
export var sha3_512: Hash;
|
||||||
export var sha3_384: Hash;
|
export var sha3_384: Hash;
|
||||||
export var sha3_256: Hash;
|
export var sha3_256: Hash;
|
||||||
@@ -315,3 +405,11 @@ export var kmac_128: KmacHash;
|
|||||||
export var kmac_256: KmacHash;
|
export var kmac_256: KmacHash;
|
||||||
export var kmac128: KmacHash;
|
export var kmac128: KmacHash;
|
||||||
export var kmac256: KmacHash;
|
export var kmac256: KmacHash;
|
||||||
|
export var tuplehash_128: TupleHashMethod;
|
||||||
|
export var tuplehash_256: TupleHashMethod;
|
||||||
|
export var tuplehash128: TupleHashMethod;
|
||||||
|
export var tuplehash256: TupleHashMethod;
|
||||||
|
export var tuplehashxof_128: TupleHashMethod;
|
||||||
|
export var tuplehashxof_256: TupleHashMethod;
|
||||||
|
export var tuplehashxof128: TupleHashMethod;
|
||||||
|
export var tuplehashxof256: TupleHashMethod;
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "js-sha3",
|
"name": "js-sha3",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"description": "A simple SHA-3 / Keccak / Shake hash function for JavaScript supports UTF-8 encoding.",
|
"description": "A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash 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": {
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"shake",
|
"shake",
|
||||||
"cshake",
|
"cshake",
|
||||||
"kmac",
|
"kmac",
|
||||||
|
"tuplehash",
|
||||||
"hash",
|
"hash",
|
||||||
"encryption",
|
"encryption",
|
||||||
"cryptography",
|
"cryptography",
|
||||||
|
|||||||
+143
-6
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* [js-sha3]{@link https://github.com/emn178/js-sha3}
|
* [js-sha3]{@link https://github.com/emn178/js-sha3}
|
||||||
*
|
*
|
||||||
* @version 0.10.0
|
* @version 0.11.0
|
||||||
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||||
* @copyright Chen, Yi-Cyuan 2015-2023
|
* @copyright Chen, Yi-Cyuan 2015-2023
|
||||||
* @license MIT
|
* @license MIT
|
||||||
@@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
var INPUT_ERROR = 'input is invalid type';
|
var INPUT_ERROR = 'input is invalid type';
|
||||||
var FINALIZE_ERROR = 'finalize already called';
|
var FINALIZE_ERROR = 'finalize already called';
|
||||||
|
var TUPLE_ACTIVE_ERROR = 'no active tuple input';
|
||||||
|
var TUPLE_INCOMPLETE_ERROR = 'tuple input is incomplete';
|
||||||
|
var TUPLE_LENGTH_ERROR = 'tuple input exceeds declared length';
|
||||||
|
var TUPLE_BYTE_LENGTH_ERROR = 'tuple input byte length 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) {
|
||||||
@@ -171,12 +175,45 @@
|
|||||||
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var createTupleHashOutputMethod = function (bits, padding, xof, outputType) {
|
||||||
|
return function (inputs, outputBits, s) {
|
||||||
|
return methods[(xof ? 'tuplehashxof' : 'tuplehash') + bits].update(inputs, outputBits, s)[outputType]();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createTupleHashMethod = function (bits, padding, xof) {
|
||||||
|
var w = CSHAKE_BYTEPAD[bits];
|
||||||
|
var method = createTupleHashOutputMethod(bits, padding, xof, 'hex');
|
||||||
|
method.create = function (outputBits, s) {
|
||||||
|
return new TupleHash(bits, padding, outputBits, xof).bytepad(['TupleHash', s], w);
|
||||||
|
};
|
||||||
|
method.update = function (inputs, outputBits, s) {
|
||||||
|
if (!isArray(inputs)) {
|
||||||
|
throw new Error(INPUT_ERROR);
|
||||||
|
}
|
||||||
|
var hash = method.create(outputBits, s);
|
||||||
|
for (var i = 0; i < inputs.length; ++i) {
|
||||||
|
hash.update(inputs[i]);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
return createOutputMethods(method, function (b, p, outputType) {
|
||||||
|
return createTupleHashOutputMethod(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 },
|
||||||
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
||||||
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
||||||
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod }
|
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod },
|
||||||
|
{ name: 'tuplehash', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
|
||||||
|
return createTupleHashMethod(bits, padding, false);
|
||||||
|
}},
|
||||||
|
{ name: 'tuplehashxof', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: function (bits, padding) {
|
||||||
|
return createTupleHashMethod(bits, padding, true);
|
||||||
|
}}
|
||||||
];
|
];
|
||||||
|
|
||||||
var methods = {}, methodNames = [];
|
var methods = {}, methodNames = [];
|
||||||
@@ -290,7 +327,7 @@
|
|||||||
} else {
|
} else {
|
||||||
bytes.unshift(n);
|
bytes.unshift(n);
|
||||||
}
|
}
|
||||||
this.update(bytes);
|
Keccak.prototype.update.call(this, bytes);
|
||||||
return bytes.length;
|
return bytes.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -317,7 +354,7 @@
|
|||||||
bytes = length;
|
bytes = length;
|
||||||
}
|
}
|
||||||
bytes += this.encode(bytes * 8);
|
bytes += this.encode(bytes * 8);
|
||||||
this.update(str);
|
Keccak.prototype.update.call(this, str);
|
||||||
return bytes;
|
return bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -329,7 +366,7 @@
|
|||||||
var paddingBytes = (w - bytes % w) % w;
|
var paddingBytes = (w - bytes % w) % w;
|
||||||
var zeros = [];
|
var zeros = [];
|
||||||
zeros.length = paddingBytes;
|
zeros.length = paddingBytes;
|
||||||
this.update(zeros);
|
Keccak.prototype.update.call(this, zeros);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -458,7 +495,107 @@
|
|||||||
Kmac.prototype = new Keccak();
|
Kmac.prototype = new Keccak();
|
||||||
|
|
||||||
Kmac.prototype.finalize = function () {
|
Kmac.prototype.finalize = function () {
|
||||||
this.encode(this.outputBits, true);
|
if (!this.finalized) {
|
||||||
|
this.encode(this.outputBits, true);
|
||||||
|
}
|
||||||
|
return Keccak.prototype.finalize.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
function TupleHash(bits, padding, outputBits, xof) {
|
||||||
|
Keccak.call(this, bits, padding, outputBits);
|
||||||
|
this.xof = !!xof;
|
||||||
|
this.inputActive = false;
|
||||||
|
this.inputBytesRemaining = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TupleHash.prototype = new Keccak();
|
||||||
|
|
||||||
|
TupleHash.prototype.getMessageByteLength = function (message) {
|
||||||
|
var result = formatMessage(message);
|
||||||
|
message = result[0];
|
||||||
|
if (!result[1]) {
|
||||||
|
return message.length;
|
||||||
|
}
|
||||||
|
var bytes = 0;
|
||||||
|
for (var i = 0; i < message.length; ++i) {
|
||||||
|
var code = message.charCodeAt(i);
|
||||||
|
if (code < 0x80) {
|
||||||
|
bytes += 1;
|
||||||
|
} else if (code < 0x800) {
|
||||||
|
bytes += 2;
|
||||||
|
} else if (code < 0xd800 || code >= 0xe000) {
|
||||||
|
bytes += 3;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
bytes += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.beginInput = function (byteLength) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
if (this.inputActive) {
|
||||||
|
throw new Error(TUPLE_INCOMPLETE_ERROR);
|
||||||
|
}
|
||||||
|
if (typeof byteLength !== 'number' || !isFinite(byteLength) || byteLength < 0 ||
|
||||||
|
Math.floor(byteLength) !== byteLength || byteLength > 0x0fffffff) {
|
||||||
|
throw new Error(TUPLE_BYTE_LENGTH_ERROR);
|
||||||
|
}
|
||||||
|
this.encode(byteLength * 8, false);
|
||||||
|
if (byteLength === 0) {
|
||||||
|
this.inputActive = false;
|
||||||
|
this.inputBytesRemaining = 0;
|
||||||
|
} else {
|
||||||
|
this.inputActive = true;
|
||||||
|
this.inputBytesRemaining = byteLength;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.updateChunk = function (message) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
if (!this.inputActive) {
|
||||||
|
throw new Error(TUPLE_ACTIVE_ERROR);
|
||||||
|
}
|
||||||
|
var byteLength = this.getMessageByteLength(message);
|
||||||
|
if (byteLength > this.inputBytesRemaining) {
|
||||||
|
throw new Error(TUPLE_LENGTH_ERROR);
|
||||||
|
}
|
||||||
|
Keccak.prototype.update.call(this, message);
|
||||||
|
this.inputBytesRemaining -= byteLength;
|
||||||
|
if (this.inputBytesRemaining === 0) {
|
||||||
|
this.inputActive = false;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.update = function (message) {
|
||||||
|
if (this.finalized) {
|
||||||
|
throw new Error(FINALIZE_ERROR);
|
||||||
|
}
|
||||||
|
if (this.inputActive) {
|
||||||
|
throw new Error(TUPLE_INCOMPLETE_ERROR);
|
||||||
|
}
|
||||||
|
var byteLength = this.getMessageByteLength(message);
|
||||||
|
this.beginInput(byteLength);
|
||||||
|
if (byteLength === 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.updateChunk(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
TupleHash.prototype.finalize = function () {
|
||||||
|
if (this.inputActive) {
|
||||||
|
throw new Error(TUPLE_INCOMPLETE_ERROR);
|
||||||
|
}
|
||||||
|
if (!this.finalized) {
|
||||||
|
this.encode(this.xof ? 0 : this.outputBits, true);
|
||||||
|
}
|
||||||
return Keccak.prototype.finalize.call(this);
|
return Keccak.prototype.finalize.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -28,7 +28,16 @@ export const {
|
|||||||
kmac_128,
|
kmac_128,
|
||||||
kmac_256,
|
kmac_256,
|
||||||
kmac128,
|
kmac128,
|
||||||
kmac256
|
kmac256,
|
||||||
|
|
||||||
|
tuplehash_128,
|
||||||
|
tuplehash_256,
|
||||||
|
tuplehash128,
|
||||||
|
tuplehash256,
|
||||||
|
tuplehashxof_128,
|
||||||
|
tuplehashxof_256,
|
||||||
|
tuplehashxof128,
|
||||||
|
tuplehashxof256
|
||||||
} = sha3;
|
} = sha3;
|
||||||
|
|
||||||
export default sha3;
|
export default sha3;
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ function unset() {
|
|||||||
shake256 = null;
|
shake256 = null;
|
||||||
kmac128 = null;
|
kmac128 = null;
|
||||||
kmac256 = null;
|
kmac256 = null;
|
||||||
|
tuplehash128 = null;
|
||||||
|
tuplehash256 = null;
|
||||||
|
tuplehashxof128 = null;
|
||||||
|
tuplehashxof256 = 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;
|
||||||
@@ -41,6 +45,10 @@ function requireToGlobal() {
|
|||||||
cshake256 = sha3.cshake256;
|
cshake256 = sha3.cshake256;
|
||||||
kmac128 = sha3.kmac128;
|
kmac128 = sha3.kmac128;
|
||||||
kmac256 = sha3.kmac256;
|
kmac256 = sha3.kmac256;
|
||||||
|
tuplehash128 = sha3.tuplehash128;
|
||||||
|
tuplehash256 = sha3.tuplehash256;
|
||||||
|
tuplehashxof128 = sha3.tuplehashxof128;
|
||||||
|
tuplehashxof256 = sha3.tuplehashxof256;
|
||||||
}
|
}
|
||||||
|
|
||||||
function runCommonJsTest() {
|
function runCommonJsTest() {
|
||||||
@@ -57,6 +65,7 @@ function runWindowTest(extra) {
|
|||||||
require('./test-shake.js');
|
require('./test-shake.js');
|
||||||
require('./test-cshake.js');
|
require('./test-cshake.js');
|
||||||
require('./test-kmac.js');
|
require('./test-kmac.js');
|
||||||
|
require('./test-tuplehash.js');
|
||||||
}
|
}
|
||||||
unset();
|
unset();
|
||||||
}
|
}
|
||||||
@@ -108,6 +117,10 @@ define = function (func) {
|
|||||||
cshake256 = sha3.cshake256;
|
cshake256 = sha3.cshake256;
|
||||||
kmac128 = sha3.kmac128;
|
kmac128 = sha3.kmac128;
|
||||||
kmac256 = sha3.kmac256;
|
kmac256 = sha3.kmac256;
|
||||||
|
tuplehash128 = sha3.tuplehash128;
|
||||||
|
tuplehash256 = sha3.tuplehash256;
|
||||||
|
tuplehashxof128 = sha3.tuplehashxof128;
|
||||||
|
tuplehashxof256 = sha3.tuplehashxof256;
|
||||||
require('./test.js');
|
require('./test.js');
|
||||||
};
|
};
|
||||||
define.amd = true;
|
define.amd = true;
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
(function (tuplehash256, tuplehash128, tuplehashxof256, tuplehashxof128, kmac128) {
|
||||||
|
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TupleHash_samples.pdf
|
||||||
|
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TupleHashXOF_samples.pdf
|
||||||
|
var t1 = [0x00, 0x01, 0x02];
|
||||||
|
var t2 = [0x10, 0x11, 0x12, 0x13, 0x14, 0x15];
|
||||||
|
var t3 = [0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28];
|
||||||
|
|
||||||
|
var testCases = [
|
||||||
|
{
|
||||||
|
name: 'tuplehash128',
|
||||||
|
method: tuplehash128,
|
||||||
|
cases: [
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 256,
|
||||||
|
s: '',
|
||||||
|
output: 'c5d8786c1afb9b82111ab34b65b2c0048fa64e6d48e263264ce1707d3ffc8ed1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 256,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '75cdb20ff4db1154e841d758e24160c54bae86eb8c13e7f5f40eb35588e96dfb'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2, t3],
|
||||||
|
bits: 256,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: 'e60f202c89a2631eda8d4c588ca5fd07f39e5151998deccf973adb3804bb6e84'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tuplehash256',
|
||||||
|
method: tuplehash256,
|
||||||
|
cases: [
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 512,
|
||||||
|
s: '',
|
||||||
|
output: 'cfb7058caca5e668f81a12a20a2195ce97a925f1dba3e7449a56f82201ec607311ac2696b1ab5ea2352df1423bde7bd4bb78c9aed1a853c78672f9eb23bbe194'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 512,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '147c2191d5ed7efd98dbd96d7ab5a11692576f5fe2a5065f3e33de6bba9f3aa1c4e9a068a289c61c95aab30aee1e410b0b607de3620e24a4e3bf9852a1d4367e'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2, t3],
|
||||||
|
bits: 512,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '45000be63f9b6bfd89f54717670f69a9bc763591a4f05c50d68891a744bcc6e7d6d5b5e82c018da999ed35b0bb49c9678e526abd8e85c13ed254021db9e790ce'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tuplehashxof128',
|
||||||
|
method: tuplehashxof128,
|
||||||
|
cases: [
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 256,
|
||||||
|
s: '',
|
||||||
|
output: '2f103cd7c32320353495c68de1a8129245c6325f6f2a3d608d92179c96e68488'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 256,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '3fc8ad69453128292859a18b6c67d7ad85f01b32815e22ce839c49ec374e9b9a'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2, t3],
|
||||||
|
bits: 256,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '900fe16cad098d28e74d632ed852f99daab7f7df4d99e775657885b4bf76d6f8'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tuplehashxof256',
|
||||||
|
method: tuplehashxof256,
|
||||||
|
cases: [
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 512,
|
||||||
|
s: '',
|
||||||
|
output: '03ded4610ed6450a1e3f8bc44951d14fbc384ab0efe57b000df6b6df5aae7cd568e77377daf13f37ec75cf5fc598b6841d51dd207c991cd45d210ba60ac52eb9'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2],
|
||||||
|
bits: 512,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '6483cb3c9952eb20e830af4785851fc597ee3bf93bb7602c0ef6a65d741aeca7e63c3b128981aa05c6d27438c79d2754bb1b7191f125d6620fca12ce658b2442'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [t1, t2, t3],
|
||||||
|
bits: 512,
|
||||||
|
s: 'My Tuple App',
|
||||||
|
output: '0c59b11464f2336c34663ed51b2b950bec743610856f36c28d1d088d8a2446284dd09830a6a178dc752376199fae935d86cfdee5913d4922dfd369b66a53c897'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach(function (testCase) {
|
||||||
|
describe('#' + testCase.name, function () {
|
||||||
|
testCase.cases.forEach(function (c) {
|
||||||
|
it('should match NIST sample vector', function () {
|
||||||
|
expect(testCase.method(c.inputs, c.bits, c.s)).to.be(c.output);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#tuplehash API', function () {
|
||||||
|
it('should treat empty tuple and empty inputs as valid', function () {
|
||||||
|
var emptyTuple = tuplehash128([], 256, '');
|
||||||
|
var oneEmpty = tuplehash128([''], 256, '');
|
||||||
|
var twoEmpty = tuplehash128(['', ''], 256, '');
|
||||||
|
expect(emptyTuple).to.not.be(oneEmpty);
|
||||||
|
expect(oneEmpty).to.not.be(twoEmpty);
|
||||||
|
expect(twoEmpty).to.not.be(tuplehash128(['', '', ''], 256, ''));
|
||||||
|
expect(tuplehash128.create(256, '').update('').update(t1).hex()).to.be(tuplehash128(['', t1], 256, ''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should distinguish tuple boundaries', function () {
|
||||||
|
expect(tuplehash128(['abc', 'd'], 256, '')).to.not.be(tuplehash128(['ab', 'cd'], 256, ''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match instance update and method update with one-shot', function () {
|
||||||
|
var expected = tuplehash128(['abc', 'd'], 256, 'cache');
|
||||||
|
var incremental = tuplehash128.create(256, 'cache').update('abc').update('d').hex();
|
||||||
|
var methodUpdate = tuplehash128.update(['abc'], 256, 'cache').update('d').hex();
|
||||||
|
expect(incremental).to.be(expected);
|
||||||
|
expect(methodUpdate).to.be(expected);
|
||||||
|
expect(tuplehash128.update(['abc', 'd'], 256, 'cache').hex()).to.be(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should stream binary messages in irregular chunks', function () {
|
||||||
|
var bytes = [];
|
||||||
|
for (var i = 0; i < 200; ++i) {
|
||||||
|
bytes.push(i & 0xff);
|
||||||
|
}
|
||||||
|
var expected = tuplehash128([bytes, t1], 256, '');
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
hash.beginInput(bytes.length);
|
||||||
|
hash.updateChunk(bytes.slice(0, 1));
|
||||||
|
hash.updateChunk(bytes.slice(1, 17));
|
||||||
|
hash.updateChunk(bytes.slice(17, 168));
|
||||||
|
hash.updateChunk(bytes.slice(168));
|
||||||
|
hash.beginInput(t1.length);
|
||||||
|
hash.updateChunk(t1);
|
||||||
|
expect(hash.hex()).to.be(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should stream ArrayBuffer and Uint8Array views', function () {
|
||||||
|
var expected = tuplehash128([t1, t2], 256, '');
|
||||||
|
var buffer = new Uint8Array(t1).buffer;
|
||||||
|
var view = new Uint8Array(new Uint8Array(t2).buffer, 0, t2.length);
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
hash.beginInput(t1.length).updateChunk(buffer);
|
||||||
|
hash.beginInput(t2.length).updateChunk(view);
|
||||||
|
expect(hash.hex()).to.be(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support zero-length streaming input', function () {
|
||||||
|
var expected = tuplehash128(['', t1], 256, '');
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
hash.beginInput(0);
|
||||||
|
hash.beginInput(t1.length).updateChunk(t1);
|
||||||
|
expect(hash.hex()).to.be(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid streaming usage', function () {
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
expect(function () { hash.updateChunk(t1); }).to.throwError(/no active tuple input/);
|
||||||
|
hash.beginInput(2);
|
||||||
|
expect(function () { hash.updateChunk([1, 2, 3]); }).to.throwError(/exceeds declared length/);
|
||||||
|
expect(function () { hash.beginInput(1); }).to.throwError(/incomplete/);
|
||||||
|
expect(function () { hash.update(t1); }).to.throwError(/incomplete/);
|
||||||
|
expect(function () { hash.hex(); }).to.throwError(/incomplete/);
|
||||||
|
hash.updateChunk([1]);
|
||||||
|
expect(function () { hash.hex(); }).to.throwError(/incomplete/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid beginInput lengths', function () {
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
expect(function () { hash.beginInput(-1); }).to.throwError(/byte length is invalid/);
|
||||||
|
expect(function () { hash.beginInput(1.5); }).to.throwError(/byte length is invalid/);
|
||||||
|
expect(function () { hash.beginInput(NaN); }).to.throwError(/byte length is invalid/);
|
||||||
|
expect(function () { hash.beginInput(0x20000000); }).to.throwError(/byte length is invalid/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow repeated output representations', function () {
|
||||||
|
var hash = tuplehash128.create(256, '').update(t1).update(t2);
|
||||||
|
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(t1); }).to.throwError(/finalize already called/);
|
||||||
|
expect(function () { hash.updateChunk(t1); }).to.throwError(/finalize already called/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require customization like KMAC', function () {
|
||||||
|
expect(function () { tuplehash128([t1], 256); }).to.throwError(/input is invalid type/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require inputs to be an array', function () {
|
||||||
|
expect(function () { tuplehash128('abc', 256, ''); }).to.throwError(/input is invalid type/);
|
||||||
|
expect(function () { tuplehash128.update(t1, 256, ''); }).to.throwError(/input is invalid type/);
|
||||||
|
expect(function () { tuplehash128.hex(null, 256, ''); }).to.throwError(/input is invalid type/);
|
||||||
|
expect(function () { tuplehashxof128(undefined, 256, ''); }).to.throwError(/input is invalid type/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export identical aliases', function () {
|
||||||
|
var sha3 = require('../src/sha3.js');
|
||||||
|
expect(sha3.tuplehash128).to.be(sha3.tuplehash_128);
|
||||||
|
expect(sha3.tuplehash256).to.be(sha3.tuplehash_256);
|
||||||
|
expect(sha3.tuplehashxof128).to.be(sha3.tuplehashxof_128);
|
||||||
|
expect(sha3.tuplehashxof256).to.be(sha3.tuplehashxof_256);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count UTF-8 string bytes for streaming', function () {
|
||||||
|
var message = 'åbc'; // 2 + 1 + 1 = 4 UTF-8 bytes
|
||||||
|
var expected = tuplehash128([message], 256, '');
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
hash.beginInput(4).updateChunk('å').updateChunk('bc');
|
||||||
|
expect(hash.hex()).to.be(expected);
|
||||||
|
expect(message.length).to.be(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count 3-byte and 4-byte UTF-8 string bytes for streaming', function () {
|
||||||
|
var message = '中\uE000\uD83D\uDE00'; // 3 + 3 + 4 = 10 UTF-8 bytes
|
||||||
|
var expected = tuplehash128([message], 256, '');
|
||||||
|
var hash = tuplehash128.create(256, '');
|
||||||
|
hash.beginInput(10).updateChunk('中').updateChunk('\uE000').updateChunk('\uD83D\uDE00');
|
||||||
|
expect(hash.hex()).to.be(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject update and beginInput after finalize', function () {
|
||||||
|
var hash = tuplehash128.create(256, '').update([0x00]);
|
||||||
|
hash.hex();
|
||||||
|
expect(function () { hash.update([0x01]); }).to.throwError(/finalize already called/);
|
||||||
|
expect(function () { hash.beginInput(1); }).to.throwError(/finalize already called/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#kmac repeated output', function () {
|
||||||
|
it('should allow repeated output reads after finalize fix', function () {
|
||||||
|
var hash = kmac128.create([0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F], 256, '');
|
||||||
|
hash.update([0x00, 0x01, 0x02, 0x03]);
|
||||||
|
var hex = hash.hex();
|
||||||
|
expect(hash.hex()).to.be(hex);
|
||||||
|
expect(hash.array().length).to.be(32);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})(tuplehash256, tuplehash128, tuplehashxof256, tuplehashxof128, kmac128);
|
||||||
Reference in New Issue
Block a user