From 01b1baf1e7d86f832f1889309599bdb590bf9a8d Mon Sep 17 00:00:00 2001 From: Yi-Cyuan Chen Date: Sat, 18 Jul 2026 08:52:44 +0800 Subject: [PATCH] ### Added - TupleHash128, TupleHash256, TupleHashXOF128, and TupleHashXOF256 (NIST SP 800-185) ### Fixed - KMAC repeated output reads after finalize --- CHANGELOG.md | 7 ++ README.md | 41 ++++++- build/sha3.min.js | 4 +- build/sha3.min.mjs | 4 +- build/sha3.mjs | 162 +++++++++++++++++++++++-- index.d.ts | 98 +++++++++++++++ package.json | 5 +- src/sha3.js | 149 ++++++++++++++++++++++- src/sha3.mjs | 11 +- tests/node-test.js | 13 ++ tests/test-tuplehash.js | 262 ++++++++++++++++++++++++++++++++++++++++ 11 files changed, 732 insertions(+), 24 deletions(-) create mode 100644 tests/test-tuplehash.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fedaea..a36874f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 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 ### Added - support ESM #42, #38 diff --git a/README.md b/README.md index 890c6fc..5ad4ee4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![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/) -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 * 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'); kmac128('key', 'Message to hash', 256, '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 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 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 If you use node.js, you should require the module first: @@ -101,7 +128,11 @@ const { cshake128, cshake256, kmac128, - kmac256 + kmac256, + tuplehash128, + tuplehash256, + tuplehashxof128, + tuplehashxof256 } = require('js-sha3'); ``` @@ -122,7 +153,11 @@ import { cshake128, cshake256, kmac128, - kmac256 + kmac256, + tuplehash128, + tuplehash256, + tuplehashxof128, + tuplehashxof256 } from 'js-sha3'; ``` diff --git a/build/sha3.min.js b/build/sha3.min.js index 4f359fb..84474f2 100644 --- a/build/sha3.min.js +++ b/build/sha3.min.js @@ -1,9 +1,9 @@ /** * [js-sha3]{@link https://github.com/emn178/js-sha3} * - * @version 0.10.0 + * @version 0.11.0 * @author Chen, Yi-Cyuan [emn178@gmail.com] * @copyright Chen, Yi-Cyuan 2015-2023 * @license MIT */ -!function(){"use strict";function t(t,e,r){this.blocks=[],this.s=[],this.padding=e,this.outputBits=r,this.reset=!0,this.finalized=!1,this.block=0,this.start=0,this.blockCount=1600-(t<<1)>>5,this.byteCount=this.blockCount<<2,this.outputBlocks=r>>5,this.extraBytes=(31&r)>>3;for(var n=0;n<50;++n)this.s[n]=0}function e(e,r,n){t.call(this,e,r,n)}var r="input is invalid type",n="object"==typeof window,i=n?window:{};i.JS_SHA3_NO_WINDOW&&(n=!1);var o=!n&&"object"==typeof self;!i.JS_SHA3_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node?i=global:o&&(i=self);for(var a=!i.JS_SHA3_NO_COMMON_JS&&"object"==typeof module&&module.exports,s="function"==typeof define&&define.amd,u=!i.JS_SHA3_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,f="0123456789abcdef".split(""),c=[4,1024,262144,67108864],h=[0,8,16,24],p=[1,0,32898,0,32906,2147483648,2147516416,2147483648,32907,0,2147483649,0,2147516545,2147483648,32777,2147483648,138,0,136,0,2147516425,0,2147483658,0,2147516555,0,139,2147483648,32905,2147483648,32771,2147483648,32770,2147483648,128,2147483648,32778,0,2147483658,2147483648,2147516545,2147483648,32896,2147483648,2147483649,0,2147516424,2147483648],d=[224,256,384,512],l=[128,256],y=["hex","buffer","arrayBuffer","array","digest"],b={128:168,256:136},v=i.JS_SHA3_NO_NODE_JS||!Array.isArray?function(t){return"[object Array]"===Object.prototype.toString.call(t)}:Array.isArray,A=!u||!i.JS_SHA3_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView?ArrayBuffer.isView:function(t){return"object"==typeof t&&t.buffer&&t.buffer.constructor===ArrayBuffer},g=function(t){var e=typeof t;if("string"===e)return[t,!0];if("object"!==e||null===t)throw new Error(r);if(u&&t.constructor===ArrayBuffer)return[new Uint8Array(t),!1];if(!v(t)&&!A(t))throw new Error(r);return[t,!1]},B=function(t){return 0===g(t)[0].length},_=function(t){for(var e=[],r=0;r>2]|=n<>2]|=(192|n>>6)<>2]|=(128|63&n)<=57344?(o[r>>2]|=(224|n>>12)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=(240|n>>18)<>2]|=(128|n>>12&63)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=t[f]<=a){for(this.start=r-a,this.block=o[u],r=0;r>=8);r>0;)i.unshift(r),r=255&(t>>=8),++n;return e?i.push(n):i.unshift(n),this.update(i),i.length},t.prototype.encodeString=function(t){var e=g(t);t=e[0];var r=e[1],n=0,i=t.length;if(r)for(var o=0;o=57344?n+=3:(a=65536+((1023&a)<<10|1023&t.charCodeAt(++o)),n+=4)}else n=i;return n+=this.encode(8*n),this.update(t),n},t.prototype.bytepad=function(t,e){for(var r=this.encode(e),n=0;n>2]|=this.padding[3&e],this.lastByteIndex===this.byteCount)for(t[0]=t[r],e=1;e>4&15]+f[15&t]+f[t>>12&15]+f[t>>8&15]+f[t>>20&15]+f[t>>16&15]+f[t>>28&15]+f[t>>24&15];a%e==0&&(r=_(r),R(r),o=0)}return i&&(t=r[o],s+=f[t>>4&15]+f[15&t],i>1&&(s+=f[t>>12&15]+f[t>>8&15]),i>2&&(s+=f[t>>20&15]+f[t>>16&15])),s},t.prototype.arrayBuffer=function(){this.finalize();var t,e=this.blockCount,r=this.s,n=this.outputBlocks,i=this.extraBytes,o=0,a=0,s=this.outputBits>>3;t=i?new ArrayBuffer(n+1<<2):new ArrayBuffer(s);for(var u=new Uint32Array(t);a>8&255,u[t+2]=e>>16&255,u[t+3]=e>>24&255;s%r==0&&(n=_(n),R(n))}return o&&(t=s<<2,e=n[a],u[t]=255&e,o>1&&(u[t+1]=e>>8&255),o>2&&(u[t+2]=e>>16&255)),u},(e.prototype=new t).finalize=function(){return this.encode(this.outputBits,!0),t.prototype.finalize.call(this)};var R=function(t){var e,r,n,i,o,a,s,u,f,c,h,d,l,y,b,v,A,g,B,_,k,w,S,C,x,m,O,z,N,J,M,j,E,H,I,R,F,U,D,V,W,Y,K,q,G,L,P,Q,T,X,Z,$,tt,et,rt,nt,it,ot,at,st,ut,ft,ct;for(n=0;n<48;n+=2)i=t[0]^t[10]^t[20]^t[30]^t[40],o=t[1]^t[11]^t[21]^t[31]^t[41],a=t[2]^t[12]^t[22]^t[32]^t[42],s=t[3]^t[13]^t[23]^t[33]^t[43],u=t[4]^t[14]^t[24]^t[34]^t[44],f=t[5]^t[15]^t[25]^t[35]^t[45],c=t[6]^t[16]^t[26]^t[36]^t[46],h=t[7]^t[17]^t[27]^t[37]^t[47],e=(d=t[8]^t[18]^t[28]^t[38]^t[48])^(a<<1|s>>>31),r=(l=t[9]^t[19]^t[29]^t[39]^t[49])^(s<<1|a>>>31),t[0]^=e,t[1]^=r,t[10]^=e,t[11]^=r,t[20]^=e,t[21]^=r,t[30]^=e,t[31]^=r,t[40]^=e,t[41]^=r,e=i^(u<<1|f>>>31),r=o^(f<<1|u>>>31),t[2]^=e,t[3]^=r,t[12]^=e,t[13]^=r,t[22]^=e,t[23]^=r,t[32]^=e,t[33]^=r,t[42]^=e,t[43]^=r,e=a^(c<<1|h>>>31),r=s^(h<<1|c>>>31),t[4]^=e,t[5]^=r,t[14]^=e,t[15]^=r,t[24]^=e,t[25]^=r,t[34]^=e,t[35]^=r,t[44]^=e,t[45]^=r,e=u^(d<<1|l>>>31),r=f^(l<<1|d>>>31),t[6]^=e,t[7]^=r,t[16]^=e,t[17]^=r,t[26]^=e,t[27]^=r,t[36]^=e,t[37]^=r,t[46]^=e,t[47]^=r,e=c^(i<<1|o>>>31),r=h^(o<<1|i>>>31),t[8]^=e,t[9]^=r,t[18]^=e,t[19]^=r,t[28]^=e,t[29]^=r,t[38]^=e,t[39]^=r,t[48]^=e,t[49]^=r,y=t[0],b=t[1],L=t[11]<<4|t[10]>>>28,P=t[10]<<4|t[11]>>>28,z=t[20]<<3|t[21]>>>29,N=t[21]<<3|t[20]>>>29,st=t[31]<<9|t[30]>>>23,ut=t[30]<<9|t[31]>>>23,Y=t[40]<<18|t[41]>>>14,K=t[41]<<18|t[40]>>>14,H=t[2]<<1|t[3]>>>31,I=t[3]<<1|t[2]>>>31,v=t[13]<<12|t[12]>>>20,A=t[12]<<12|t[13]>>>20,Q=t[22]<<10|t[23]>>>22,T=t[23]<<10|t[22]>>>22,J=t[33]<<13|t[32]>>>19,M=t[32]<<13|t[33]>>>19,ft=t[42]<<2|t[43]>>>30,ct=t[43]<<2|t[42]>>>30,et=t[5]<<30|t[4]>>>2,rt=t[4]<<30|t[5]>>>2,R=t[14]<<6|t[15]>>>26,F=t[15]<<6|t[14]>>>26,g=t[25]<<11|t[24]>>>21,B=t[24]<<11|t[25]>>>21,X=t[34]<<15|t[35]>>>17,Z=t[35]<<15|t[34]>>>17,j=t[45]<<29|t[44]>>>3,E=t[44]<<29|t[45]>>>3,C=t[6]<<28|t[7]>>>4,x=t[7]<<28|t[6]>>>4,nt=t[17]<<23|t[16]>>>9,it=t[16]<<23|t[17]>>>9,U=t[26]<<25|t[27]>>>7,D=t[27]<<25|t[26]>>>7,_=t[36]<<21|t[37]>>>11,k=t[37]<<21|t[36]>>>11,$=t[47]<<24|t[46]>>>8,tt=t[46]<<24|t[47]>>>8,q=t[8]<<27|t[9]>>>5,G=t[9]<<27|t[8]>>>5,m=t[18]<<20|t[19]>>>12,O=t[19]<<20|t[18]>>>12,ot=t[29]<<7|t[28]>>>25,at=t[28]<<7|t[29]>>>25,V=t[38]<<8|t[39]>>>24,W=t[39]<<8|t[38]>>>24,w=t[48]<<14|t[49]>>>18,S=t[49]<<14|t[48]>>>18,t[0]=y^~v&g,t[1]=b^~A&B,t[10]=C^~m&z,t[11]=x^~O&N,t[20]=H^~R&U,t[21]=I^~F&D,t[30]=q^~L&Q,t[31]=G^~P&T,t[40]=et^~nt&ot,t[41]=rt^~it&at,t[2]=v^~g&_,t[3]=A^~B&k,t[12]=m^~z&J,t[13]=O^~N&M,t[22]=R^~U&V,t[23]=F^~D&W,t[32]=L^~Q&X,t[33]=P^~T&Z,t[42]=nt^~ot&st,t[43]=it^~at&ut,t[4]=g^~_&w,t[5]=B^~k&S,t[14]=z^~J&j,t[15]=N^~M&E,t[24]=U^~V&Y,t[25]=D^~W&K,t[34]=Q^~X&$,t[35]=T^~Z&tt,t[44]=ot^~st&ft,t[45]=at^~ut&ct,t[6]=_^~w&y,t[7]=k^~S&b,t[16]=J^~j&C,t[17]=M^~E&x,t[26]=V^~Y&H,t[27]=W^~K&I,t[36]=X^~$&q,t[37]=Z^~tt&G,t[46]=st^~ft&et,t[47]=ut^~ct&rt,t[8]=w^~y&v,t[9]=S^~b&A,t[18]=j^~C&m,t[19]=E^~x&O,t[28]=Y^~H&R,t[29]=K^~I&F,t[38]=$^~q&L,t[39]=tt^~G&P,t[48]=ft^~et&nt,t[49]=ct^~rt&it,t[0]^=p[n],t[1]^=p[n+1]};if(a)module.exports=z;else{for(J=0;J>5,this.byteCount=this.blockCount<<2,this.outputBlocks=r>>5,this.extraBytes=(31&r)>>3;for(var n=0;n<50;++n)this.s[n]=0}function e(e,r,n){t.call(this,e,r,n)}function r(e,r,n,i){t.call(this,e,r,n),this.xof=!!i,this.inputActive=!1,this.inputBytesRemaining=0}var n="input is invalid type",i="finalize already called",o="tuple input is incomplete",u="object"==typeof window,a=u?window:{};a.JS_SHA3_NO_WINDOW&&(u=!1);var s=!u&&"object"==typeof self;!a.JS_SHA3_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node?a=global:s&&(a=self);for(var h=!a.JS_SHA3_NO_COMMON_JS&&"object"==typeof module&&module.exports,f="function"==typeof define&&define.amd,c=!a.JS_SHA3_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,p="0123456789abcdef".split(""),l=[4,1024,262144,67108864],d=[0,8,16,24],y=[1,0,32898,0,32906,2147483648,2147516416,2147483648,32907,0,2147483649,0,2147516545,2147483648,32777,2147483648,138,0,136,0,2147516425,0,2147483658,0,2147516555,0,139,2147483648,32905,2147483648,32771,2147483648,32770,2147483648,128,2147483648,32778,0,2147483658,2147483648,2147516545,2147483648,32896,2147483648,2147483649,0,2147516424,2147483648],g=[224,256,384,512],v=[128,256],b=["hex","buffer","arrayBuffer","array","digest"],w={128:168,256:136},A=a.JS_SHA3_NO_NODE_JS||!Array.isArray?function(t){return"[object Array]"===Object.prototype.toString.call(t)}:Array.isArray,B=!c||!a.JS_SHA3_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView?ArrayBuffer.isView:function(t){return"object"==typeof t&&t.buffer&&t.buffer.constructor===ArrayBuffer},k=function(t){var e=typeof t;if("string"===e)return[t,!0];if("object"!==e||null===t)throw new Error(n);if(c&&t.constructor===ArrayBuffer)return[new Uint8Array(t),!1];if(!A(t)&&!B(t))throw new Error(n);return[t,!1]},_=function(t){return 0===k(t)[0].length},m=function(t){for(var e=[],r=0;r>2]|=n<>2]|=(192|n>>6)<>2]|=(128|63&n)<=57344?(u[r>>2]|=(224|n>>12)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=(240|n>>18)<>2]|=(128|n>>12&63)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=t[f]<=a){for(this.start=r-a,this.block=u[h],r=0;r>=8);n>0;)o.unshift(n),n=255&(e>>=8),++i;return r?o.push(i):o.unshift(i),t.prototype.update.call(this,o),o.length},t.prototype.encodeString=function(e){var r=k(e);e=r[0];var n=r[1],i=0,o=e.length;if(n)for(var u=0;u=57344?i+=3:(a=65536+((1023&a)<<10|1023&e.charCodeAt(++u)),i+=4)}else i=o;return i+=this.encode(8*i),t.prototype.update.call(this,e),i},t.prototype.bytepad=function(e,r){for(var n=this.encode(r),i=0;i>2]|=this.padding[3&e],this.lastByteIndex===this.byteCount)for(t[0]=t[r],e=1;e>4&15]+p[15&t]+p[t>>12&15]+p[t>>8&15]+p[t>>20&15]+p[t>>16&15]+p[t>>28&15]+p[t>>24&15];u%e==0&&(r=m(r),V(r),o=0)}return i&&(t=r[o],a+=p[t>>4&15]+p[15&t],i>1&&(a+=p[t>>12&15]+p[t>>8&15]),i>2&&(a+=p[t>>20&15]+p[t>>16&15])),a},t.prototype.arrayBuffer=function(){this.finalize();var t,e=this.blockCount,r=this.s,n=this.outputBlocks,i=this.extraBytes,o=0,u=0,a=this.outputBits>>3;t=i?new ArrayBuffer(n+1<<2):new ArrayBuffer(a);for(var s=new Uint32Array(t);u>8&255,s[t+2]=e>>16&255,s[t+3]=e>>24&255;a%r==0&&(n=m(n),V(n))}return o&&(t=a<<2,e=n[u],s[t]=255&e,o>1&&(s[t+1]=e>>8&255),o>2&&(s[t+2]=e>>16&255)),s},(e.prototype=new t).finalize=function(){return this.finalized||this.encode(this.outputBits,!0),t.prototype.finalize.call(this)},(r.prototype=new t).getMessageByteLength=function(t){var e=k(t);if(t=e[0],!e[1])return t.length;for(var r=0,n=0;n=57344?r+=3:(++n,r+=4)}return r},r.prototype.beginInput=function(t){if(this.finalized)throw new Error(i);if(this.inputActive)throw new Error(o);if("number"!=typeof t||!isFinite(t)||t<0||Math.floor(t)!==t||t>268435455)throw new Error("tuple input byte length is invalid");return this.encode(8*t,!1),0===t?(this.inputActive=!1,this.inputBytesRemaining=0):(this.inputActive=!0,this.inputBytesRemaining=t),this},r.prototype.updateChunk=function(e){if(this.finalized)throw new Error(i);if(!this.inputActive)throw new Error("no active tuple input");var r=this.getMessageByteLength(e);if(r>this.inputBytesRemaining)throw new Error("tuple input exceeds declared length");return t.prototype.update.call(this,e),this.inputBytesRemaining-=r,0===this.inputBytesRemaining&&(this.inputActive=!1),this},r.prototype.update=function(t){if(this.finalized)throw new Error(i);if(this.inputActive)throw new Error(o);var e=this.getMessageByteLength(t);return this.beginInput(e),0===e?this:this.updateChunk(t)},r.prototype.finalize=function(){if(this.inputActive)throw new Error(o);return this.finalized||this.encode(this.xof?0:this.outputBits,!0),t.prototype.finalize.call(this)};var V=function(t){var e,r,n,i,o,u,a,s,h,f,c,p,l,d,g,v,b,w,A,B,k,_,m,x,S,C,z,E,M,O,R,N,J,j,I,H,F,U,D,L,V,W,Y,K,T,q,G,P,Q,X,Z,$,tt,et,rt,nt,it,ot,ut,at,st,ht,ft;for(n=0;n<48;n+=2)i=t[0]^t[10]^t[20]^t[30]^t[40],o=t[1]^t[11]^t[21]^t[31]^t[41],u=t[2]^t[12]^t[22]^t[32]^t[42],a=t[3]^t[13]^t[23]^t[33]^t[43],s=t[4]^t[14]^t[24]^t[34]^t[44],h=t[5]^t[15]^t[25]^t[35]^t[45],f=t[6]^t[16]^t[26]^t[36]^t[46],c=t[7]^t[17]^t[27]^t[37]^t[47],e=(p=t[8]^t[18]^t[28]^t[38]^t[48])^(u<<1|a>>>31),r=(l=t[9]^t[19]^t[29]^t[39]^t[49])^(a<<1|u>>>31),t[0]^=e,t[1]^=r,t[10]^=e,t[11]^=r,t[20]^=e,t[21]^=r,t[30]^=e,t[31]^=r,t[40]^=e,t[41]^=r,e=i^(s<<1|h>>>31),r=o^(h<<1|s>>>31),t[2]^=e,t[3]^=r,t[12]^=e,t[13]^=r,t[22]^=e,t[23]^=r,t[32]^=e,t[33]^=r,t[42]^=e,t[43]^=r,e=u^(f<<1|c>>>31),r=a^(c<<1|f>>>31),t[4]^=e,t[5]^=r,t[14]^=e,t[15]^=r,t[24]^=e,t[25]^=r,t[34]^=e,t[35]^=r,t[44]^=e,t[45]^=r,e=s^(p<<1|l>>>31),r=h^(l<<1|p>>>31),t[6]^=e,t[7]^=r,t[16]^=e,t[17]^=r,t[26]^=e,t[27]^=r,t[36]^=e,t[37]^=r,t[46]^=e,t[47]^=r,e=f^(i<<1|o>>>31),r=c^(o<<1|i>>>31),t[8]^=e,t[9]^=r,t[18]^=e,t[19]^=r,t[28]^=e,t[29]^=r,t[38]^=e,t[39]^=r,t[48]^=e,t[49]^=r,d=t[0],g=t[1],q=t[11]<<4|t[10]>>>28,G=t[10]<<4|t[11]>>>28,E=t[20]<<3|t[21]>>>29,M=t[21]<<3|t[20]>>>29,at=t[31]<<9|t[30]>>>23,st=t[30]<<9|t[31]>>>23,W=t[40]<<18|t[41]>>>14,Y=t[41]<<18|t[40]>>>14,j=t[2]<<1|t[3]>>>31,I=t[3]<<1|t[2]>>>31,v=t[13]<<12|t[12]>>>20,b=t[12]<<12|t[13]>>>20,P=t[22]<<10|t[23]>>>22,Q=t[23]<<10|t[22]>>>22,O=t[33]<<13|t[32]>>>19,R=t[32]<<13|t[33]>>>19,ht=t[42]<<2|t[43]>>>30,ft=t[43]<<2|t[42]>>>30,et=t[5]<<30|t[4]>>>2,rt=t[4]<<30|t[5]>>>2,H=t[14]<<6|t[15]>>>26,F=t[15]<<6|t[14]>>>26,w=t[25]<<11|t[24]>>>21,A=t[24]<<11|t[25]>>>21,X=t[34]<<15|t[35]>>>17,Z=t[35]<<15|t[34]>>>17,N=t[45]<<29|t[44]>>>3,J=t[44]<<29|t[45]>>>3,x=t[6]<<28|t[7]>>>4,S=t[7]<<28|t[6]>>>4,nt=t[17]<<23|t[16]>>>9,it=t[16]<<23|t[17]>>>9,U=t[26]<<25|t[27]>>>7,D=t[27]<<25|t[26]>>>7,B=t[36]<<21|t[37]>>>11,k=t[37]<<21|t[36]>>>11,$=t[47]<<24|t[46]>>>8,tt=t[46]<<24|t[47]>>>8,K=t[8]<<27|t[9]>>>5,T=t[9]<<27|t[8]>>>5,C=t[18]<<20|t[19]>>>12,z=t[19]<<20|t[18]>>>12,ot=t[29]<<7|t[28]>>>25,ut=t[28]<<7|t[29]>>>25,L=t[38]<<8|t[39]>>>24,V=t[39]<<8|t[38]>>>24,_=t[48]<<14|t[49]>>>18,m=t[49]<<14|t[48]>>>18,t[0]=d^~v&w,t[1]=g^~b&A,t[10]=x^~C&E,t[11]=S^~z&M,t[20]=j^~H&U,t[21]=I^~F&D,t[30]=K^~q&P,t[31]=T^~G&Q,t[40]=et^~nt&ot,t[41]=rt^~it&ut,t[2]=v^~w&B,t[3]=b^~A&k,t[12]=C^~E&O,t[13]=z^~M&R,t[22]=H^~U&L,t[23]=F^~D&V,t[32]=q^~P&X,t[33]=G^~Q&Z,t[42]=nt^~ot&at,t[43]=it^~ut&st,t[4]=w^~B&_,t[5]=A^~k&m,t[14]=E^~O&N,t[15]=M^~R&J,t[24]=U^~L&W,t[25]=D^~V&Y,t[34]=P^~X&$,t[35]=Q^~Z&tt,t[44]=ot^~at&ht,t[45]=ut^~st&ft,t[6]=B^~_&d,t[7]=k^~m&g,t[16]=O^~N&x,t[17]=R^~J&S,t[26]=L^~W&j,t[27]=V^~Y&I,t[36]=X^~$&K,t[37]=Z^~tt&T,t[46]=at^~ht&et,t[47]=st^~ft&rt,t[8]=_^~d&v,t[9]=m^~g&b,t[18]=N^~x&C,t[19]=J^~S&z,t[28]=W^~j&H,t[29]=Y^~I&F,t[38]=$^~K&q,t[39]=tt^~T&G,t[48]=ht^~et&nt,t[49]=ft^~rt&it,t[0]^=y[n],t[1]^=y[n+1]};if(h)module.exports=J;else{for(I=0;I>5,this.byteCount=this.blockCount<<2,this.outputBlocks=r>>5,this.extraBytes=(31&r)>>3;for(var n=0;n<50;++n)this.s[n]=0}function R(t,e,r){I.call(this,t,e,r)}I.prototype.update=function(t){if(this.finalized)throw new Error("finalize already called");var e=_(t);t=e[0];for(var r,n,o=e[1],i=this.blocks,a=this.byteCount,s=t.length,u=this.blockCount,f=0,h=this.s;f>2]|=n<>2]|=(192|n>>6)<>2]|=(128|63&n)<=57344?(i[r>>2]|=(224|n>>12)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=(240|n>>18)<>2]|=(128|n>>12&63)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=t[f]<=a){for(this.start=r-a,this.block=i[u],r=0;r>=8);r>0;)o.unshift(r),r=255&(t>>=8),++n;return e?o.push(n):o.unshift(n),this.update(o),o.length},I.prototype.encodeString=function(t){var e=_(t);t=e[0];var r=e[1],n=0,o=t.length;if(r)for(var i=0;i=57344?n+=3:(a=65536+((1023&a)<<10|1023&t.charCodeAt(++i)),n+=4)}else n=o;return n+=this.encode(8*n),this.update(t),n},I.prototype.bytepad=function(t,e){for(var r=this.encode(e),n=0;n>2]|=this.padding[3&e],this.lastByteIndex===this.byteCount)for(t[0]=t[r],e=1;e>4&15]+u[15&t]+u[t>>12&15]+u[t>>8&15]+u[t>>20&15]+u[t>>16&15]+u[t>>28&15]+u[t>>24&15];a%e===0&&(r=g(r),F(r),i=0)}return o&&(t=r[i],s+=u[t>>4&15]+u[15&t],o>1&&(s+=u[t>>12&15]+u[t>>8&15]),o>2&&(s+=u[t>>20&15]+u[t>>16&15])),s},I.prototype.arrayBuffer=function(){this.finalize();var t,e=this.blockCount,r=this.s,n=this.outputBlocks,o=this.extraBytes,i=0,a=0,s=this.outputBits>>3;t=o?new ArrayBuffer(n+1<<2):new ArrayBuffer(s);for(var u=new Uint32Array(t);a>8&255,u[t+2]=e>>16&255,u[t+3]=e>>24&255;s%r===0&&(n=g(n),F(n))}return i&&(t=s<<2,e=n[a],u[t]=255&e,i>1&&(u[t+1]=e>>8&255),i>2&&(u[t+2]=e>>16&255)),u},R.prototype=new I,R.prototype.finalize=function(){return this.encode(this.outputBits,!0),I.prototype.finalize.call(this)};var F=function(t){var e,r,n,o,i,a,s,u,f,c,p,d,l,y,k,b,_,v,g,A,w,B,S,x,C,m,O,z,M,N,J,j,E,H,I,R,F,U,D,V,W,T,Y,K,P,q,G,L,Q,X,Z,$,tt,et,rt,nt,ot,it,at,st,ut,ft,ct;for(n=0;n<48;n+=2)o=t[0]^t[10]^t[20]^t[30]^t[40],i=t[1]^t[11]^t[21]^t[31]^t[41],a=t[2]^t[12]^t[22]^t[32]^t[42],s=t[3]^t[13]^t[23]^t[33]^t[43],u=t[4]^t[14]^t[24]^t[34]^t[44],f=t[5]^t[15]^t[25]^t[35]^t[45],c=t[6]^t[16]^t[26]^t[36]^t[46],p=t[7]^t[17]^t[27]^t[37]^t[47],e=(d=t[8]^t[18]^t[28]^t[38]^t[48])^(a<<1|s>>>31),r=(l=t[9]^t[19]^t[29]^t[39]^t[49])^(s<<1|a>>>31),t[0]^=e,t[1]^=r,t[10]^=e,t[11]^=r,t[20]^=e,t[21]^=r,t[30]^=e,t[31]^=r,t[40]^=e,t[41]^=r,e=o^(u<<1|f>>>31),r=i^(f<<1|u>>>31),t[2]^=e,t[3]^=r,t[12]^=e,t[13]^=r,t[22]^=e,t[23]^=r,t[32]^=e,t[33]^=r,t[42]^=e,t[43]^=r,e=a^(c<<1|p>>>31),r=s^(p<<1|c>>>31),t[4]^=e,t[5]^=r,t[14]^=e,t[15]^=r,t[24]^=e,t[25]^=r,t[34]^=e,t[35]^=r,t[44]^=e,t[45]^=r,e=u^(d<<1|l>>>31),r=f^(l<<1|d>>>31),t[6]^=e,t[7]^=r,t[16]^=e,t[17]^=r,t[26]^=e,t[27]^=r,t[36]^=e,t[37]^=r,t[46]^=e,t[47]^=r,e=c^(o<<1|i>>>31),r=p^(i<<1|o>>>31),t[8]^=e,t[9]^=r,t[18]^=e,t[19]^=r,t[28]^=e,t[29]^=r,t[38]^=e,t[39]^=r,t[48]^=e,t[49]^=r,y=t[0],k=t[1],q=t[11]<<4|t[10]>>>28,G=t[10]<<4|t[11]>>>28,z=t[20]<<3|t[21]>>>29,M=t[21]<<3|t[20]>>>29,st=t[31]<<9|t[30]>>>23,ut=t[30]<<9|t[31]>>>23,T=t[40]<<18|t[41]>>>14,Y=t[41]<<18|t[40]>>>14,H=t[2]<<1|t[3]>>>31,I=t[3]<<1|t[2]>>>31,b=t[13]<<12|t[12]>>>20,_=t[12]<<12|t[13]>>>20,L=t[22]<<10|t[23]>>>22,Q=t[23]<<10|t[22]>>>22,N=t[33]<<13|t[32]>>>19,J=t[32]<<13|t[33]>>>19,ft=t[42]<<2|t[43]>>>30,ct=t[43]<<2|t[42]>>>30,et=t[5]<<30|t[4]>>>2,rt=t[4]<<30|t[5]>>>2,R=t[14]<<6|t[15]>>>26,F=t[15]<<6|t[14]>>>26,v=t[25]<<11|t[24]>>>21,g=t[24]<<11|t[25]>>>21,X=t[34]<<15|t[35]>>>17,Z=t[35]<<15|t[34]>>>17,j=t[45]<<29|t[44]>>>3,E=t[44]<<29|t[45]>>>3,x=t[6]<<28|t[7]>>>4,C=t[7]<<28|t[6]>>>4,nt=t[17]<<23|t[16]>>>9,ot=t[16]<<23|t[17]>>>9,U=t[26]<<25|t[27]>>>7,D=t[27]<<25|t[26]>>>7,A=t[36]<<21|t[37]>>>11,w=t[37]<<21|t[36]>>>11,$=t[47]<<24|t[46]>>>8,tt=t[46]<<24|t[47]>>>8,K=t[8]<<27|t[9]>>>5,P=t[9]<<27|t[8]>>>5,m=t[18]<<20|t[19]>>>12,O=t[19]<<20|t[18]>>>12,it=t[29]<<7|t[28]>>>25,at=t[28]<<7|t[29]>>>25,V=t[38]<<8|t[39]>>>24,W=t[39]<<8|t[38]>>>24,B=t[48]<<14|t[49]>>>18,S=t[49]<<14|t[48]>>>18,t[0]=y^~b&v,t[1]=k^~_&g,t[10]=x^~m&z,t[11]=C^~O&M,t[20]=H^~R&U,t[21]=I^~F&D,t[30]=K^~q&L,t[31]=P^~G&Q,t[40]=et^~nt&it,t[41]=rt^~ot&at,t[2]=b^~v&A,t[3]=_^~g&w,t[12]=m^~z&N,t[13]=O^~M&J,t[22]=R^~U&V,t[23]=F^~D&W,t[32]=q^~L&X,t[33]=G^~Q&Z,t[42]=nt^~it&st,t[43]=ot^~at&ut,t[4]=v^~A&B,t[5]=g^~w&S,t[14]=z^~N&j,t[15]=M^~J&E,t[24]=U^~V&T,t[25]=D^~W&Y,t[34]=L^~X&$,t[35]=Q^~Z&tt,t[44]=it^~st&ft,t[45]=at^~ut&ct,t[6]=A^~B&y,t[7]=w^~S&k,t[16]=N^~j&x,t[17]=J^~E&C,t[26]=V^~T&H,t[27]=W^~Y&I,t[36]=X^~$&K,t[37]=Z^~tt&P,t[46]=st^~ft&et,t[47]=ut^~ct&rt,t[8]=B^~y&b,t[9]=S^~k&_,t[18]=j^~x&m,t[19]=E^~C&O,t[28]=T^~H&R,t[29]=Y^~I&F,t[38]=$^~K&q,t[39]=tt^~P&G,t[48]=ft^~et&nt,t[49]=ct^~rt&ot,t[0]^=h[n],t[1]^=h[n+1]};if(a)r.exports=O;else for(M=0;M>5,this.byteCount=this.blockCount<<2,this.outputBlocks=r>>5,this.extraBytes=(31&r)>>3;for(var n=0;n<50;++n)this.s[n]=0}function L(t,e,r){D.call(this,t,e,r)}function T(t,e,r,n){D.call(this,t,e,r),this.xof=!!n,this.inputActive=!1,this.inputBytesRemaining=0}D.prototype.update=function(t){if(this.finalized)throw new Error(n);var e=w(t);t=e[0];for(var r,i,o=e[1],a=this.blocks,s=this.byteCount,u=t.length,h=this.blockCount,f=0,c=this.s;f>2]|=i<>2]|=(192|i>>6)<>2]|=(128|63&i)<=57344?(a[r>>2]|=(224|i>>12)<>2]|=(128|i>>6&63)<>2]|=(128|63&i)<>2]|=(240|i>>18)<>2]|=(128|i>>12&63)<>2]|=(128|i>>6&63)<>2]|=(128|63&i)<>2]|=t[f]<=s){for(this.start=r-s,this.block=a[h],r=0;r>=8);r>0;)i.unshift(r),r=255&(t>>=8),++n;return e?i.push(n):i.unshift(n),D.prototype.update.call(this,i),i.length},D.prototype.encodeString=function(t){var e=w(t);t=e[0];var r=e[1],n=0,i=t.length;if(r)for(var o=0;o=57344?n+=3:(a=65536+((1023&a)<<10|1023&t.charCodeAt(++o)),n+=4)}else n=i;return n+=this.encode(8*n),D.prototype.update.call(this,t),n},D.prototype.bytepad=function(t,e){for(var r=this.encode(e),n=0;n>2]|=this.padding[3&e],this.lastByteIndex===this.byteCount)for(t[0]=t[r],e=1;e>4&15]+f[15&t]+f[t>>12&15]+f[t>>8&15]+f[t>>20&15]+f[t>>16&15]+f[t>>28&15]+f[t>>24&15];a%e===0&&(r=A(r),V(r),o=0)}return i&&(t=r[o],s+=f[t>>4&15]+f[15&t],i>1&&(s+=f[t>>12&15]+f[t>>8&15]),i>2&&(s+=f[t>>20&15]+f[t>>16&15])),s},D.prototype.arrayBuffer=function(){this.finalize();var t,e=this.blockCount,r=this.s,n=this.outputBlocks,i=this.extraBytes,o=0,a=0,s=this.outputBits>>3;t=i?new ArrayBuffer(n+1<<2):new ArrayBuffer(s);for(var u=new Uint32Array(t);a>8&255,u[t+2]=e>>16&255,u[t+3]=e>>24&255;s%r===0&&(n=A(n),V(n))}return o&&(t=s<<2,e=n[a],u[t]=255&e,o>1&&(u[t+1]=e>>8&255),o>2&&(u[t+2]=e>>16&255)),u},L.prototype=new D,L.prototype.finalize=function(){return this.finalized||this.encode(this.outputBits,!0),D.prototype.finalize.call(this)},T.prototype=new D,T.prototype.getMessageByteLength=function(t){var e=w(t);if(t=e[0],!e[1])return t.length;for(var r=0,n=0;n=57344?r+=3:(++n,r+=4)}return r},T.prototype.beginInput=function(t){if(this.finalized)throw new Error(n);if(this.inputActive)throw new Error(i);if("number"!=typeof t||!isFinite(t)||t<0||Math.floor(t)!==t||t>268435455)throw new Error("tuple input byte length is invalid");return this.encode(8*t,!1),0===t?(this.inputActive=!1,this.inputBytesRemaining=0):(this.inputActive=!0,this.inputBytesRemaining=t),this},T.prototype.updateChunk=function(t){if(this.finalized)throw new Error(n);if(!this.inputActive)throw new Error("no active tuple input");var e=this.getMessageByteLength(t);if(e>this.inputBytesRemaining)throw new Error("tuple input exceeds declared length");return D.prototype.update.call(this,t),this.inputBytesRemaining-=e,0===this.inputBytesRemaining&&(this.inputActive=!1),this},T.prototype.update=function(t){if(this.finalized)throw new Error(n);if(this.inputActive)throw new Error(i);var e=this.getMessageByteLength(t);return this.beginInput(e),0===e?this:this.updateChunk(t)},T.prototype.finalize=function(){if(this.inputActive)throw new Error(i);return this.finalized||this.encode(this.xof?0:this.outputBits,!0),D.prototype.finalize.call(this)};var V=function(t){var e,r,n,i,o,a,s,u,h,f,c,p,d,y,g,k,v,b,w,_,A,B,x,m,S,C,z,E,M,O,R,N,J,j,I,H,F,U,D,L,T,V,W,Y,K,P,q,G,Q,X,Z,$,tt,et,rt,nt,it,ot,at,st,ut,ht,ft;for(n=0;n<48;n+=2)i=t[0]^t[10]^t[20]^t[30]^t[40],o=t[1]^t[11]^t[21]^t[31]^t[41],a=t[2]^t[12]^t[22]^t[32]^t[42],s=t[3]^t[13]^t[23]^t[33]^t[43],u=t[4]^t[14]^t[24]^t[34]^t[44],h=t[5]^t[15]^t[25]^t[35]^t[45],f=t[6]^t[16]^t[26]^t[36]^t[46],c=t[7]^t[17]^t[27]^t[37]^t[47],e=(p=t[8]^t[18]^t[28]^t[38]^t[48])^(a<<1|s>>>31),r=(d=t[9]^t[19]^t[29]^t[39]^t[49])^(s<<1|a>>>31),t[0]^=e,t[1]^=r,t[10]^=e,t[11]^=r,t[20]^=e,t[21]^=r,t[30]^=e,t[31]^=r,t[40]^=e,t[41]^=r,e=i^(u<<1|h>>>31),r=o^(h<<1|u>>>31),t[2]^=e,t[3]^=r,t[12]^=e,t[13]^=r,t[22]^=e,t[23]^=r,t[32]^=e,t[33]^=r,t[42]^=e,t[43]^=r,e=a^(f<<1|c>>>31),r=s^(c<<1|f>>>31),t[4]^=e,t[5]^=r,t[14]^=e,t[15]^=r,t[24]^=e,t[25]^=r,t[34]^=e,t[35]^=r,t[44]^=e,t[45]^=r,e=u^(p<<1|d>>>31),r=h^(d<<1|p>>>31),t[6]^=e,t[7]^=r,t[16]^=e,t[17]^=r,t[26]^=e,t[27]^=r,t[36]^=e,t[37]^=r,t[46]^=e,t[47]^=r,e=f^(i<<1|o>>>31),r=c^(o<<1|i>>>31),t[8]^=e,t[9]^=r,t[18]^=e,t[19]^=r,t[28]^=e,t[29]^=r,t[38]^=e,t[39]^=r,t[48]^=e,t[49]^=r,y=t[0],g=t[1],P=t[11]<<4|t[10]>>>28,q=t[10]<<4|t[11]>>>28,E=t[20]<<3|t[21]>>>29,M=t[21]<<3|t[20]>>>29,st=t[31]<<9|t[30]>>>23,ut=t[30]<<9|t[31]>>>23,V=t[40]<<18|t[41]>>>14,W=t[41]<<18|t[40]>>>14,j=t[2]<<1|t[3]>>>31,I=t[3]<<1|t[2]>>>31,k=t[13]<<12|t[12]>>>20,v=t[12]<<12|t[13]>>>20,G=t[22]<<10|t[23]>>>22,Q=t[23]<<10|t[22]>>>22,O=t[33]<<13|t[32]>>>19,R=t[32]<<13|t[33]>>>19,ht=t[42]<<2|t[43]>>>30,ft=t[43]<<2|t[42]>>>30,et=t[5]<<30|t[4]>>>2,rt=t[4]<<30|t[5]>>>2,H=t[14]<<6|t[15]>>>26,F=t[15]<<6|t[14]>>>26,b=t[25]<<11|t[24]>>>21,w=t[24]<<11|t[25]>>>21,X=t[34]<<15|t[35]>>>17,Z=t[35]<<15|t[34]>>>17,N=t[45]<<29|t[44]>>>3,J=t[44]<<29|t[45]>>>3,m=t[6]<<28|t[7]>>>4,S=t[7]<<28|t[6]>>>4,nt=t[17]<<23|t[16]>>>9,it=t[16]<<23|t[17]>>>9,U=t[26]<<25|t[27]>>>7,D=t[27]<<25|t[26]>>>7,_=t[36]<<21|t[37]>>>11,A=t[37]<<21|t[36]>>>11,$=t[47]<<24|t[46]>>>8,tt=t[46]<<24|t[47]>>>8,Y=t[8]<<27|t[9]>>>5,K=t[9]<<27|t[8]>>>5,C=t[18]<<20|t[19]>>>12,z=t[19]<<20|t[18]>>>12,ot=t[29]<<7|t[28]>>>25,at=t[28]<<7|t[29]>>>25,L=t[38]<<8|t[39]>>>24,T=t[39]<<8|t[38]>>>24,B=t[48]<<14|t[49]>>>18,x=t[49]<<14|t[48]>>>18,t[0]=y^~k&b,t[1]=g^~v&w,t[10]=m^~C&E,t[11]=S^~z&M,t[20]=j^~H&U,t[21]=I^~F&D,t[30]=Y^~P&G,t[31]=K^~q&Q,t[40]=et^~nt&ot,t[41]=rt^~it&at,t[2]=k^~b&_,t[3]=v^~w&A,t[12]=C^~E&O,t[13]=z^~M&R,t[22]=H^~U&L,t[23]=F^~D&T,t[32]=P^~G&X,t[33]=q^~Q&Z,t[42]=nt^~ot&st,t[43]=it^~at&ut,t[4]=b^~_&B,t[5]=w^~A&x,t[14]=E^~O&N,t[15]=M^~R&J,t[24]=U^~L&V,t[25]=D^~T&W,t[34]=G^~X&$,t[35]=Q^~Z&tt,t[44]=ot^~st&ht,t[45]=at^~ut&ft,t[6]=_^~B&y,t[7]=A^~x&g,t[16]=O^~N&m,t[17]=R^~J&S,t[26]=L^~V&j,t[27]=T^~W&I,t[36]=X^~$&Y,t[37]=Z^~tt&K,t[46]=st^~ht&et,t[47]=ut^~ft&rt,t[8]=B^~y&k,t[9]=x^~g&v,t[18]=N^~m&C,t[19]=J^~S&z,t[28]=V^~j&H,t[29]=W^~I&F,t[38]=$^~Y&P,t[39]=tt^~K&q,t[48]=ht^~et&nt,t[49]=ft^~rt&it,t[0]^=l[n],t[1]^=l[n+1]};if(u)r.exports=R;else for(J=0;J= 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); }; @@ -696,7 +833,16 @@ const { kmac_128, kmac_256, kmac128, - kmac256 + kmac256, + + tuplehash_128, + tuplehash_256, + tuplehash128, + tuplehash256, + tuplehashxof_128, + tuplehashxof_256, + tuplehashxof128, + tuplehashxof256 } = 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 }; diff --git a/index.d.ts b/index.d.ts index 4fa31c4..ad3aabc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -291,6 +291,96 @@ interface KmacHash { 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_384: Hash; export var sha3_256: Hash; @@ -315,3 +405,11 @@ export var kmac_128: KmacHash; export var kmac_256: KmacHash; export var kmac128: 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; diff --git a/package.json b/package.json index 4b3e54d..f581825 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "js-sha3", - "version": "0.10.0", - "description": "A simple SHA-3 / Keccak / Shake hash function for JavaScript supports UTF-8 encoding.", + "version": "0.11.0", + "description": "A simple SHA-3 / Keccak / SHAKE / cSHAKE / KMAC / TupleHash hash function for JavaScript supports UTF-8 encoding.", "main": "src/sha3.js", "module": "build/sha3.mjs", "exports": { @@ -44,6 +44,7 @@ "shake", "cshake", "kmac", + "tuplehash", "hash", "encryption", "cryptography", diff --git a/src/sha3.js b/src/sha3.js index f7cf302..0f7bde0 100644 --- a/src/sha3.js +++ b/src/sha3.js @@ -1,7 +1,7 @@ /** * [js-sha3]{@link https://github.com/emn178/js-sha3} * - * @version 0.10.0 + * @version 0.11.0 * @author Chen, Yi-Cyuan [emn178@gmail.com] * @copyright Chen, Yi-Cyuan 2015-2023 * @license MIT @@ -12,6 +12,10 @@ var INPUT_ERROR = 'input is invalid type'; 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 root = WINDOW ? window : {}; if (root.JS_SHA3_NO_WINDOW) { @@ -171,12 +175,45 @@ 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 = [ { name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod }, { name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod }, { name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod }, { 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 = []; @@ -290,7 +327,7 @@ } else { bytes.unshift(n); } - this.update(bytes); + Keccak.prototype.update.call(this, bytes); return bytes.length; }; @@ -317,7 +354,7 @@ bytes = length; } bytes += this.encode(bytes * 8); - this.update(str); + Keccak.prototype.update.call(this, str); return bytes; }; @@ -329,7 +366,7 @@ var paddingBytes = (w - bytes % w) % w; var zeros = []; zeros.length = paddingBytes; - this.update(zeros); + Keccak.prototype.update.call(this, zeros); return this; }; @@ -458,7 +495,107 @@ Kmac.prototype = new Keccak(); 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); }; diff --git a/src/sha3.mjs b/src/sha3.mjs index b3453db..e810805 100644 --- a/src/sha3.mjs +++ b/src/sha3.mjs @@ -28,7 +28,16 @@ export const { kmac_128, kmac_256, kmac128, - kmac256 + kmac256, + + tuplehash_128, + tuplehash_256, + tuplehash128, + tuplehash256, + tuplehashxof_128, + tuplehashxof_256, + tuplehashxof128, + tuplehashxof256 } = sha3; export default sha3; diff --git a/tests/node-test.js b/tests/node-test.js index 75f4886..b411cc5 100644 --- a/tests/node-test.js +++ b/tests/node-test.js @@ -16,6 +16,10 @@ function unset() { shake256 = null; kmac128 = null; kmac256 = null; + tuplehash128 = null; + tuplehash256 = null; + tuplehashxof128 = null; + tuplehashxof256 = null; BUFFER = undefined; JS_SHA3_NO_WINDOW = undefined; JS_SHA3_NO_NODE_JS = undefined; @@ -41,6 +45,10 @@ function requireToGlobal() { cshake256 = sha3.cshake256; kmac128 = sha3.kmac128; kmac256 = sha3.kmac256; + tuplehash128 = sha3.tuplehash128; + tuplehash256 = sha3.tuplehash256; + tuplehashxof128 = sha3.tuplehashxof128; + tuplehashxof256 = sha3.tuplehashxof256; } function runCommonJsTest() { @@ -57,6 +65,7 @@ function runWindowTest(extra) { require('./test-shake.js'); require('./test-cshake.js'); require('./test-kmac.js'); + require('./test-tuplehash.js'); } unset(); } @@ -108,6 +117,10 @@ define = function (func) { cshake256 = sha3.cshake256; kmac128 = sha3.kmac128; kmac256 = sha3.kmac256; + tuplehash128 = sha3.tuplehash128; + tuplehash256 = sha3.tuplehash256; + tuplehashxof128 = sha3.tuplehashxof128; + tuplehashxof256 = sha3.tuplehashxof256; require('./test.js'); }; define.amd = true; diff --git a/tests/test-tuplehash.js b/tests/test-tuplehash.js new file mode 100644 index 0000000..c6190e5 --- /dev/null +++ b/tests/test-tuplehash.js @@ -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);