UNPKG

pw-punch

Version:

🔐 Ultra-lightweight password hashing & token signing with WebCrypto. Zero dependencies. Edge-native. Built for Cloudflare, Deno, Bun, and Vercel.

2 lines (1 loc) 3.19 kB
var O=1e5,F=500000,H=3600,D=1e4,W=new TextEncoder,K=[256,512],N=150000,C=256;async function I(z){return crypto.subtle.importKey("raw",W.encode(z),{name:"PBKDF2"},!1,["deriveBits"])}async function R(z,Q,Z,$){return crypto.subtle.deriveBits({name:"PBKDF2",salt:Q,iterations:Z,hash:$},z,256)}async function U(z,Q,Z){let $=Z==="sign"?["sign"]:["verify"];return crypto.subtle.importKey("raw",W.encode(z),{name:"HMAC",hash:Q},!1,$)}function k(z,Q){let Z=Math.floor(Date.now()/1000);if(z.exp!==void 0&&Z>=z.exp)return!1;if(z.nbf!==void 0&&Z<z.nbf)return!1;if(z.iat!==void 0&&Z<z.iat-60)return!1;if(Q?.iss&&z.iss!==Q.iss)return!1;if(Q?.sub&&z.sub!==Q.sub)return!1;if(Q?.aud&&z.aud!==Q.aud)return!1;return!0}function f(z,Q){if(!(z instanceof Uint8Array)||!(Q instanceof Uint8Array))throw new TypeError("Inputs must be Uint8Arrays");if(Math.max(z.length,Q.length)>D)throw new Error("Input too large");let Z=0,$=Math.max(z.length,Q.length);for(let j=0;j<$;j++){let G=j<z.length?z[j]:0,q=j<Q.length?Q[j]:0;Z|=G^q}return Z===0}function v(z){z=z.replace(/-/g,"+").replace(/_/g,"/");while(z.length%4!==0)z+="=";return Uint8Array.from(atob(z),(Q)=>Q.charCodeAt(0))}function Y(z){let Q=typeof z==="string"?z:String.fromCharCode(...z);return btoa(Q).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function P(z){z=z.replace(/-/g,"+").replace(/_/g,"/");while(z.length%4!==0)z+="=";return atob(z)}function m(z,Q={}){let Z=Math.floor(Date.now()/1000),$=z.iat??Q.iat??Z,j=z.nbf??Q.nbf??$,G=Q.expSeconds??H,q=z.exp??Q.exp??$+G;return{...z,iat:$,nbf:j,exp:q}}function w(z){try{return JSON.parse(P(z))}catch{return null}}async function S(z,Q=C,Z=N){if(Z<O||Z>F)throw new Error(`Iterations must be between ${O} and ${F}`);if(!K.includes(Q))throw new Error("Only SHA-256 and SHA-512 are supported");let $=crypto.getRandomValues(new Uint8Array(32)),j=await I(z),G=await R(j,$.buffer,Z,`SHA-${Q}`),q=btoa(String.fromCharCode(...$)),x=btoa(String.fromCharCode(...new Uint8Array(G)));return`${q}:${x}`}async function _(z,Q,Z=C,$=N){let[j,G]=Q.split(":");if(!j||!G)return!1;let q=v(j),x=v(G),J=await I(z),X=await R(J,q.buffer,$,`SHA-${Z}`);return f(new Uint8Array(X),x)}async function A(z,Q,Z=C,$){if(!K.includes(Z)||typeof z!=="object"||z===null||Array.isArray(z))throw new TypeError("Invalid token payload or unsupported hash type");let j=m(z),G={alg:`HS${Z}`,typ:"JWT",...$?{kid:$}:{},...z.iss?{iss:z.iss}:{}},q,x;try{q=Y(JSON.stringify(G)),x=Y(JSON.stringify(j))}catch{throw new Error("Failed to encode token payload or header")}let J=`${q}.${x}`,X=await U(Q,`SHA-${Z}`,"sign"),L=await crypto.subtle.sign("HMAC",X,W.encode(J)),g=Y(new Uint8Array(L));return`${q}.${x}.${g}`}async function B(z,Q,Z=C,$){let[j,G,q]=z.split(".");if(!j||!G||!q||q.length>1024)return null;let x=null,J=w(j);if(!J||J.alg!==`HS${Z}`)return null;if(typeof Q==="string")x=Q;else if(typeof Q==="object"){let M=J.kid;if(!M||!Q[M])return null;x=Q[M]}if(!x)return null;let X=`${j}.${G}`,L=await U(x,`SHA-${Z}`,"verify");if(!await crypto.subtle.verify("HMAC",L,v(q),W.encode(X)))return null;let V=w(G);if(!V||!k(V))return null;if($&&!$(V))return null;return V}export{B as verifyToken,_ as verifyPassword,A as signToken,S as hashPassword};