UNPKG

@iden3/js-jwz

Version:

JS implementation of JWZ

3 lines (2 loc) 10.7 kB
import{fromBigEndian as t,toBigEndian as e,Id as r}from"@iden3/js-iden3-core";import{sha256 as n,poseidon as i}from"@iden3/js-crypto";import{groth16 as s}from"snarkjs";import{Hash as o}from"@iden3/js-merkletree";import{getCurveFromName as a}from"ffjavascript";const c="21888242871839275222246405745257275088548364400416034343698204186575808495617";function h(e){const r=n(e),s=t(r.reverse());let o=BigInt(0);return o=s<BigInt(c)?s:s%BigInt(c),i.hash([o])}class l{constructor(t,e){this.alg=t,this.circuitId=e}toString(){return`${this.alg}:${this.circuitId}`}}const d=new Map;function u(t,e){return new Promise((r=>{d.set(t.toString(),e),r()}))}function g(t){return new Promise(((e,r)=>{const n=d.get(t.toString());if(n){e(n())}else r("unknown alg")}))}var p,w={chars:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",bits:6},f=function(t,e){return function(t,e,r){var n;if(void 0===r&&(r={}),!e.codes){e.codes={};for(var i=0;i<e.chars.length;++i)e.codes[e.chars[i]]=i}if(!r.loose&&t.length*e.bits&7)throw new SyntaxError("Invalid padding");for(var s=t.length;"="===t[s-1];)if(--s,!(r.loose||(t.length-s)*e.bits&7))throw new SyntaxError("Invalid padding");for(var o=new(null!=(n=r.out)?n:Uint8Array)(s*e.bits/8|0),a=0,c=0,h=0,l=0;l<s;++l){var d=e.codes[t[l]];if(void 0===d)throw new SyntaxError("Invalid character "+t[l]);c=c<<e.bits|d,(a+=e.bits)>=8&&(a-=8,o[h++]=255&c>>a)}if(a>=e.bits||255&c<<8-a)throw new SyntaxError("Unexpected end of data");return o}(t,w,e)},m=function(t,e){return function(t,e,r){void 0===r&&(r={});for(var n=r.pad,i=void 0===n||n,s=(1<<e.bits)-1,o="",a=0,c=0,h=0;h<t.length;++h)for(c=c<<8|255&t[h],a+=8;a>e.bits;)a-=e.bits,o+=e.chars[s&c>>a];if(a&&(o+=e.chars[s&c<<e.bits-a]),i)for(;o.length*e.bits&7;)o+="=";return o}(t,w,e)};!function(t){t.Type="typ",t.Alg="alg",t.CircuitId="circuitId",t.Critical="crit"}(p||(p={}));class y{constructor(t,e,r,n){this.payload=t,this.protectedHeaders=e,this.header=r,this.zkp=n}async sanitized(){if(!this.payload)throw new Error("iden3/js-jwz: missing payload in JWZ message");const t=JSON.parse((new TextDecoder).decode(this.protectedHeaders));t[p.Critical].forEach((e=>{if(!t[e])throw new Error(`iden3/js-jwz: header is listed in critical ${e}, but not presented`)}));const e=t[p.Alg],r=t[p.CircuitId],n=await g(new l(e,r)),i=JSON.parse((new TextDecoder).decode(this.zkp)),s=new I(n,(new TextDecoder).decode(this.payload));s.alg=e,s.circuitId=r,s.zkProof=i;for(const[e,r]of Object.entries(t))s.setHeader(e,r);return s}}class I{constructor(t,e,r){this.method=t,this.inputsPreparer=r,this.zkProof={},this.alg=this.method.alg,this.circuitId=this.method.circuitId,this.raw={},this.raw.header=this.getDefaultHeaders(),this.raw.payload=(new TextEncoder).encode(e)}setHeader(t,e){this.raw.header[t]=e}getPayload(){return(new TextDecoder).decode(this.raw.payload)}getDefaultHeaders(){return{[p.Alg]:this.alg,[p.Critical]:[p.CircuitId],[p.CircuitId]:this.circuitId,[p.Type]:"JWZ"}}static parse(t){const e=t?.trim();return e.startsWith("{")?I.parseFull(t):I.parseCompact(t)}static async parseCompact(t){const e=t.split(".");if(3!=e.length)throw new Error("iden3/js-jwz: compact JWZ format must have three segments");const r=f(e[0],{loose:!0}),n=f(e[1],{loose:!0}),i=f(e[2],{loose:!0}),s=new y(n,r,{},i);return await s.sanitized()}static async parseFull(t){const e=JSON.parse(t);return await e.sanitized()}async prove(t,e){const r=this.serializeHeaders();this.raw.protectedHeaders=(new TextEncoder).encode(r);const n=await this.getMessageHash();if(!this.inputsPreparer)throw new Error("iden3/jwz: prepare func must be defined");const i=await function(t,e,r){return t(e,r)}(this.inputsPreparer,n,this.circuitId),s=await this.method.prove(i,t,e),o=JSON.stringify(s);return this.zkProof=s,this.raw.zkp=(new TextEncoder).encode(o),this.compactSerialize()}compactSerialize(){if(!this.raw.header||!this.raw.protectedHeaders||!this.zkProof)throw new Error("iden3/jwz:can't serialize without one of components");const t=m(this.raw.protectedHeaders,{pad:!1}),e=m(this.raw.zkp,{pad:!1});return`${t}.${m(this.raw.payload,{pad:!1})}.${e}`}fullSerialize(){return JSON.stringify(this.raw)}async getMessageHash(){const t=this.serializeHeaders(),r=(new TextEncoder).encode(t),n=m(r,{pad:!1}),i=m(this.raw.payload,{pad:!1}),s=(new TextEncoder).encode(`${n}.${i}`),o=await h(s);return e(o,32)}async verify(t){const e=await this.getMessageHash();return this.method.verify(e,this.zkProof,t)}serializeHeaders(){return JSON.stringify(this.raw.header,Object.keys(this.raw.header).sort())}}async function S(t,e){let r;e=e||{};try{r=await WebAssembly.compile(t)}catch(t){throw console.log(t),console.log("\nTry to run circom --c in order to generate c++ code instead\n"),new Error(t)}let n="",i="";const s=await WebAssembly.instantiate(r,{runtime:{exceptionHandler:function(t){let e;throw e=1==t?"Signal not found.\n":2==t?"Too many signals set.\n":3==t?"Signal already set.\n":4==t?"Assert Failed.\n":5==t?"Not enough memory.\n":6==t?"Input signal array access exceeds the size.\n":"Unknown error.\n",new Error(e+n)},printErrorMessage:function(){n+=o()+"\n"},writeBufferMessage:function(){const t=o();"\n"===t?(console.log(i),i=""):(""!==i&&(i+=" "),i+=t)},showSharedRWMemory:function(){!function(){const t=s.exports.getFieldNumLen32(),e=new Uint32Array(t);for(let r=0;r<t;r++)e[t-1-r]=s.exports.readSharedRWMemory(r);""!==i&&(i+=" ");i+=z(e).toString()}()}}});return new x(s,e);function o(){let t="",e=s.exports.getMessageChar();for(;0!=e;)t+=String.fromCharCode(e),e=s.exports.getMessageChar();return t}}class x{constructor(t,e){this.instance=t,this.instance=t,this.version=this.instance.exports.getVersion(),this.n32=this.instance.exports.getFieldNumLen32(),this.instance.exports.getRawPrime();const r=new Uint32Array(this.n32);for(let t=0;t<this.n32;t++)r[this.n32-1-t]=this.instance.exports.readSharedRWMemory(t);this.prime=z(r),this.witnessSize=this.instance.exports.getWitnessSize(),this.sanityCheck=e}circom_version(){return this.instance.exports.getVersion()}async _doCalculateWitness(t,e){this.instance.exports.init(this.sanityCheck||e?1:0);const r=Object.keys(t);let n=0;if(r.forEach((e=>{const r=function(t){const e=BigInt(2)**BigInt(64);let r=BigInt("0xCBF29CE484222325");for(let n=0;n<t.length;n++)r^=BigInt(t[n].charCodeAt()),r*=BigInt(1099511628211),r%=e;let n=r.toString(16);const i=16-n.length;return n="0".repeat(i).concat(n),n}(e),i=parseInt(r.slice(0,8),16),s=parseInt(r.slice(8,16),16),o=function(t){const e=[];return r(e,t),e;function r(t,e){if(Array.isArray(e))for(let n=0;n<e.length;n++)r(t,e[n]);else t.push(e)}}(t[e]),a=this.instance.exports.getInputSignalSize(i,s);if(a<0)throw new Error(`Signal ${e} not found\n`);if(o.length<a)throw new Error(`Not enough values for input signal ${e}\n`);if(o.length>a)throw new Error(`Too many values for input signal ${e}\n`);for(let t=0;t<o.length;t++){const e=v(BigInt(o[t])%this.prime,this.n32);for(let t=0;t<this.n32;t++)this.instance.exports.writeSharedRWMemory(t,e[this.n32-1-t]);try{this.instance.exports.setInputSignal(i,s,t),n++}catch(t){throw new Error(t)}}})),n<this.instance.exports.getInputSize())throw new Error(`Not all inputs have been set. Only ${n} out of ${this.instance.exports.getInputSize()}`)}async calculateWitness(t,e){const r=[];await this._doCalculateWitness(t,e);for(let t=0;t<this.witnessSize;t++){this.instance.exports.getWitness(t);const e=new Uint32Array(this.n32);for(let t=0;t<this.n32;t++)e[this.n32-1-t]=this.instance.exports.readSharedRWMemory(t);r.push(z(e))}return r}async calculateBinWitness(t,e){const r=new Uint32Array(this.witnessSize*this.n32),n=new Uint8Array(r.buffer);await this._doCalculateWitness(t,e);for(let t=0;t<this.witnessSize;t++){this.instance.exports.getWitness(t);const e=t*this.n32;for(let t=0;t<this.n32;t++)r[e+t]=this.instance.exports.readSharedRWMemory(t)}return n}async calculateWTNSBin(t,e){const r=new Uint32Array(this.witnessSize*this.n32+this.n32+11),n=new Uint8Array(r.buffer);await this._doCalculateWitness(t,e),n[0]="w".charCodeAt(0),n[1]="t".charCodeAt(0),n[2]="n".charCodeAt(0),n[3]="s".charCodeAt(0),r[1]=2,r[2]=2,r[3]=1;const i=4*this.n32,s=(8+i).toString(16);r[4]=parseInt(s.slice(0,8),16),r[5]=parseInt(s.slice(8,16),16),r[6]=i,this.instance.exports.getRawPrime();let o=7;for(let t=0;t<this.n32;t++)r[o+t]=this.instance.exports.readSharedRWMemory(t);o+=this.n32,r[o]=this.witnessSize,o++,r[o]=2,o++;const a=(i*this.witnessSize).toString(16);r[o]=parseInt(a.slice(0,8),16),r[o+1]=parseInt(a.slice(8,16),16),o+=2;for(let t=0;t<this.witnessSize;t++){this.instance.exports.getWitness(t);for(let t=0;t<this.n32;t++)r[o+t]=this.instance.exports.readSharedRWMemory(t);o+=this.n32}return n}}function v(t,e){const r=[],n=BigInt(4294967296);for(;t;)r.unshift(Number(t%n)),t/=n;if(e){let t=e-r.length;for(;t>0;)r.unshift(0),t--}return r}function z(t){let e=BigInt(0);const r=BigInt(4294967296);for(let n=0;n<t.length;n++)e=e*r+BigInt(t[n]);return e}const b="groth16",A=new TextDecoder;async function C(t,e,r){const n=await S(r),i=(new TextDecoder).decode(t),o=JSON.parse(i),a=await n.calculateWTNSBin(o,0),{proof:c,publicSignals:h}=await s.prove(e,a);return{proof:c,pub_signals:h}}async function E(e,r,n,i){if(i(r.pub_signals).challenge!==t(e))throw new Error("challenge is not equal to message hash");return await s.verify(JSON.parse(A.decode(n)),r.pub_signals,r.proof)}const W=new class{constructor(t){this.methodAlg=t}get alg(){return this.methodAlg.alg}get circuitId(){return this.methodAlg.circuitId}unmarshall(t){const e={};if(3!=t.length)throw new Error(`invalid number of Output values expected 3 got ${t.length}`);return e.challenge=BigInt(t[0]),e.userState=BigInt(t[1]),e.userId=r.fromBigInt(BigInt(t[2])),e}async verify(t,e,r){return E(t,e,r,this.unmarshall)}prove(t,e,r){return C(t,e,r)}}(new l(b,"auth"));class B{constructor(t){this.methodAlg=t}get alg(){return this.methodAlg.alg}get circuitId(){return this.methodAlg.circuitId}async verify(t,e,r){const n=await E(t,e,r,this.unmarshall);return await this.terminateCurve(),n}async prove(t,e,r){const n=await C(t,e,r);return await this.terminateCurve(),n}async terminateCurve(){(await a(B.curveName)).terminate()}unmarshall(t){if(3!==t.length)throw new Error(`invalid number of Output values expected 3 got ${t.length}`);return{userID:r.fromBigInt(BigInt(t[0])),challenge:BigInt(t[1]),GISTRoot:o.fromString(t[2])}}}B.curveName="bn128";const M=new B(new l(b,"authV2"));u(W.methodAlg,(()=>W)),u(M.methodAlg,(()=>M));const T={registerProvingMethod:u,getProvingMethod:g,provingMethodGroth16AuthInstance:W,provingMethodGroth16AuthV2Instance:M};export{p as Header,l as ProvingMethodAlg,I as Token,h as hash,T as proving}; //# sourceMappingURL=index.js.map