hsynchronous
Version:
Hybrid synchronous encryption protocol using NIST approved post-quantum algorithms.
1 lines • 5.87 kB
JavaScript
;var pqclean=require('pqclean'),crypto=require('crypto');var W=Object.defineProperty;var G=Object.getOwnPropertySymbols;var ee=Object.prototype.hasOwnProperty,te=Object.prototype.propertyIsEnumerable;var C=(t,e,r)=>e in t?W(t,e,{enumerable:true,configurable:true,writable:true,value:r}):t[e]=r,T=(t,e)=>{for(var r in e||(e={}))ee.call(e,r)&&C(t,r,e[r]);if(G)for(var r of G(e))te.call(e,r)&&C(t,r,e[r]);return t};var b=(t,e,r)=>new Promise((s,c)=>{var f=a=>{try{n(r.next(a));}catch(o){c(o);}},i=a=>{try{n(r.throw(a));}catch(o){c(o);}},n=a=>a.done?s(a.value):Promise.resolve(a.value).then(f,i);n((r=r.apply(t,e)).next());});function re(t,e){let r=crypto.createHmac("sha256",t),s=Buffer.concat([e,Buffer.alloc(Math.max(0,256-e.length))]);return r.update(s),r.digest()}function I(t,e,r,s){let c=re(e,t),f=Math.ceil(s/32),i=[],n=Buffer.alloc(0);for(let a=0;a<f;a++){let o=crypto.createHmac("sha256",c);o.update(Buffer.concat([n,r,Buffer.from([a+1])])),n=o.digest(),i.push(n);}return Buffer.concat(i).subarray(0,s)}function v(t){for(let e=0;e<t.length;e++)t[e]=0;}function Y(t,e=1024){let r=e-t.length%e;return t+"\0".repeat(r)}var se=pqclean.kem.supportedAlgorithms,ie=pqclean.sign.supportedAlgorithms,j=se.map(t=>t.name),q=ie.map(t=>t.name),L="1.0.1",_="hsynchronous",R="aes-256-gcm",X=32,J=50,U=25,l=128,ce={fixedRunTime:true},fe={fixedRunTime:true,memoryNonceProtection:true},Z=new Set,de=(t="ml-kem-1024",e="falcon-1024")=>b(null,null,function*(){let r=yield pqclean.kem.generateKeyPair(t),s=yield pqclean.sign.generateKeyPair(e);return {kemKeyPair:r,sigKeyPair:s}}),Ke=t=>b(null,null,function*(){let e=t.kemKeyPair.publicKey.algorithm.name,r=t.sigKeyPair.publicKey.algorithm.name,s=t.kemKeyPair.publicKey.export(),c=t.kemKeyPair.privateKey.export(),f=t.sigKeyPair.publicKey.export(),i=t.sigKeyPair.privateKey.export(),n=[e.length,r.length,s.byteLength,c.byteLength,f.byteLength],a=Buffer.from(n.join(","),"utf8"),o=Buffer.concat([Buffer.from(e,"utf8"),Buffer.from(r,"utf8"),Buffer.from(s),Buffer.from(c),Buffer.from(f),Buffer.from(i)]);return `${_}:${L}:${a.toString("hex")}:${o.toString("hex")}`}),he=t=>b(null,null,function*(){let[e,r,s,c]=t.split(":");if(e!==_||r!==L)throw new Error(`Unsupported protocol or version: ${e} ${r}`);let f=Buffer.from(s,"hex").toString("utf8").split(",").map(Number),[i,n,a,o,m]=f,u=Buffer.from(c,"hex"),p=u.subarray(0,i).toString("utf8"),y=u.subarray(i,i+n).toString("utf8");if(!j.includes(p))throw new Error(`Unsupported KEM algorithm: ${p}`);if(!q.includes(y))throw new Error(`Unsupported signature algorithm: ${y}`);let P=u.subarray(i+n,i+n+a),w=u.subarray(i+n+a,i+n+a+o),d=u.subarray(i+n+a+o,i+n+a+o+m),K=u.subarray(i+n+a+o+m),g=new pqclean.kem.PublicKey(p,P),B=new pqclean.kem.PrivateKey(p,w),S=new pqclean.sign.PublicKey(y,d),h=new pqclean.sign.PrivateKey(y,K);return {kemKeyPair:{publicKey:g,privateKey:B},sigKeyPair:{publicKey:S,privateKey:h}}}),Pe=(t,e,r,s)=>b(null,null,function*(){let c=performance.now();if(!e||!e.kemKeyPair||!e.sigKeyPair)throw new Error("Call generateKeys() first");if(typeof t!="string")throw new Error("Message must be a string");if(s||(s=crypto.randomBytes(l/2).toString("hex")),typeof s!="string")throw new Error("Nonce must be a string");if(s.length>l)throw new Error(`Nonce must be less than ${l} characters`);if(r=T(T({},ce),r),!j.includes(e.kemKeyPair.publicKey.algorithm.name))throw new Error("Unsupported KEM algorithm");if(!q.includes(e.sigKeyPair.publicKey.algorithm.name))throw new Error("Unsupported signature algorithm");let{key:f,encryptedKey:i}=yield e.kemKeyPair.publicKey.generateKey(),n=Buffer.from(f),a=Buffer.from(i),o=crypto.randomBytes(64),m=Buffer.from(`${e.kemKeyPair.publicKey.algorithm.name}-${e.sigKeyPair.publicKey.algorithm.name}-${R}-${_}-${L}`,"utf8"),u=I(n,o,m,X);v(n);let y=Date.now().toString().padStart(U,"0"),P=s.padStart(l," "),w=`${t}${y}${P}`,d=crypto.randomBytes(12),K=crypto.createCipheriv(R,u,d),g=Buffer.concat([K.update(w,"utf8"),K.final()]),B=K.getAuthTag();v(u);let S=Buffer.concat([o,a,g,d,B]),h=yield e.sigKeyPair.privateKey.sign(S),$=g.byteLength,x=Buffer.from(h).byteLength,E=Buffer.from(`${$},${x}`,"utf8"),O=Buffer.concat([o,a,d,B,g,Buffer.from(h)]),k=`${E.toString("hex")}.${O.toString("hex")}`,A=Y(k,1024);return r.fixedRunTime?yield new Promise(D=>setTimeout(D,J-(performance.now()-c))).then(()=>A):A}),we=(t,e,r)=>b(null,null,function*(){r=T(T({},fe),r);let s=performance.now(),[c,f]=t.split("."),i=Buffer.from(c,"hex"),[n,a]=i.toString("utf8").split(",").map(Number),o=e.kemKeyPair.publicKey.algorithm.encryptedKeySize,m=Buffer.from(f,"hex"),u=m.subarray(0,64),p=m.subarray(64,64+o),y=m.subarray(64+o,64+o+12),P=m.subarray(64+o+12,64+o+12+16),w=m.subarray(64+o+12+16,64+o+12+16+n),d=m.subarray(64+o+12+16+n),K=Buffer.concat([u,p,w,y,P]),g=yield e.sigKeyPair.publicKey.verify(K,d),B=crypto.timingSafeEqual(Buffer.from(g?"":"\0"),Buffer.from(""));if(!g)throw new Error("Invalid signature");if(!B)throw new Error("Signature verification failed due to timing attack protection");let S=Buffer.from(`${e.kemKeyPair.publicKey.algorithm.name}-${e.sigKeyPair.publicKey.algorithm.name}-${R}-${_}-${L}`,"utf8"),h=Buffer.from(yield e.kemKeyPair.privateKey.decryptKey(p)),$=I(h,u,S,X);v(h);let x=crypto.createDecipheriv(R,$,y);v($),x.setAuthTag(P);let E=Buffer.concat([x.update(w),x.final()]).toString("utf8"),O=E.slice(-l),k=E.slice(-153,-l),A=E.slice(0,-153),D=new Date(parseInt(k,10)),H=O.trimEnd();if(r.memoryNonceProtection){if(Z.has(H))throw new Error("Nonce reuse detected");Z.add(H);}let z={message:A,createdAt:D,decryptedAt:new Date,nonce:H};return r.fixedRunTime?yield new Promise(Q=>setTimeout(Q,J-(performance.now()-s))).then(()=>z):z});exports.VERSION=L;exports.decrypt=we;exports.encrypt=Pe;exports.exportKeys=Ke;exports.generateKeys=de;exports.importKeys=he;exports.supportedKemAlgorithms=se;exports.supportedSigAlgorithms=ie;