<script>
const id = {
"P": "",
"p": "" // pkcs8
};
const ID = { publicKey: id.P, privateKey: id.p };
const message = { data: "You've got mail!" };
const subscription = {
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=BQYAAABvp59EVrscIXz82bXgaceSDSDjODHmHkbaesMsajXKnsHMRblD9KCsBcCBqyD9t0%2bMpzykYZDifrSx%2fjlcrvSiGOej2SuB%2bvJMt7X6QRyReMRu5fE22qhaVNEbAPeXDHJei07tV%2fUDW97ej0VNNZGyvUrw1tqgYQw6r7Xc372NnvLehPwoqkSpq8tLN7Uy9WCCXusRm9yoC4XJ3jWy812UexJZVSD94FhtEcdOfZ61u5HPahq9GgY89Nlio21ZRgWwr21ZDzGrlmXu9a8vujWcI8IqqIjdZVvWHnK3gHyxRiuHMrSKDETszRJTEvzKADA%3d",
"expirationTime": null,
"keys": {
"p256dh": "BPy_jJEqmdSYVnSyw_sr-CVFR7sTtdQePNJyQITlsBm1pz2cmdG6HQ1terT3ngoNpbC0ia-uKoU0sK8s5CUehy0",
"auth": "_GeuNaTkwdxxNlXn-FE6bg"
}
};
const adminContact = 'mailto:[email protected]';
</script>
<script type=module>
// https://cdn.jsdelivr.net/npm/webpush-webcrypto/+esm
function e(e){let t=e;"string"==typeof t&&(t=(new TextEncoder).encode(t));let n="";return new Uint8Array(t).forEach((e=>{n+=String.fromCharCode(e)})),btoa(n).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}function t(e){let t=e.replace(/\-/g,"+").replace(/_/g,"/");const n=4-t.length%4;if(4!==n)for(let e=0;e<n;e++)t+="=";if(t.length%4!=0)throw new Error("Decoded to incorrect length");const r=atob(t),a=new Uint8Array(r.length);for(let e=0;e<r.length;e++)a[e]=r.charCodeAt(e);return a.buffer}function n(e){const t=e.reduce(((e,t)=>e+t.byteLength),0);let n=0;const r=new Uint8Array(t);for(const t of e)r.set(t,n),n+=t.byteLength;return r}let r;"undefined"!=typeof self&&(r=self.crypto);const a={get subtle(){if(!r)throw new Error("Could not find global Crypto module. Please set it with the setCrypto method.");return r.subtle},getRandomValues(e){if(!r)throw new Error("Could not find global Crypto module. Please set it with the setCrypto method.");return r.getRandomValues(e)}};function o(e){r=e}const i=new TextEncoder,c=i.encode("Content-Encoding: auth\0"),s=i.encode("P-256\0"),u=i.encode("Content-Encoding: nonce\0"),l=i.encode("Content-Encoding: aesgcm\0"),y={typ:"JWT",alg:"ES256"};async function p({options:t,payloadLength:n,salt:r,localPublicKey:o}){const i=await a.subtle.exportKey("raw",o),c=await a.subtle.exportKey("raw",t.applicationServerKeys.publicKey),s=e(i),u=e(c),l=new URL(t.target.endpoint),p=await async function(t,n){if(!t.privateKey||!t.publicKey)throw new Error("Missing public or private key");const r=Math.floor(Date.now()/1e3)+43200,o={aud:n.aud,exp:r,sub:n.sub},i=e(JSON.stringify(y)),c=e(JSON.stringify(o)),s=i+"."+c;return i+"."+c+"."+e(await a.subtle.sign({name:"ECDSA",hash:{name:"SHA-256"}},t.privateKey,(new TextEncoder).encode(s)))}(t.applicationServerKeys,{aud:l.origin,sub:t.adminContact}),w={Encryption:`salt=${e(r)}`,"Crypto-Key":`dh=${s}; p256ecdsa=${u}`,"Content-Length":n.toString(),"Content-Type":"application/octet-stream","Content-Encoding":"aesgcm",Authorization:`WebPush ${p}`,TTL:t.ttl.toString()};return t.topic&&(w.Topic=t.topic),t.urgency&&(w.Urgency=t.urgency),w}async function w(e){const r=await a.subtle.generateKey({name:"ECDH",namedCurve:"P-256"},!0,["deriveBits"]);if(!r.privateKey||!r.publicKey)throw new Error("Local key generation failed");const o=await async function(e){const n=t(e.auth);if(16!==n.byteLength)throw new Error(`incorrect auth length, expected 16 bytes got ${n.byteLength}`);return{auth:n,p256:await a.subtle.importKey("raw",t(e.p256dh),{name:"ECDH",namedCurve:"P-256"},!0,[])}}(e.target.keys),i=await async function(e,t){const n=await a.subtle.deriveBits({name:"ECDH",public:e},t,256);return a.subtle.importKey("raw",n,{name:"HKDF"},!1,["deriveBits","deriveKey"])}(o.p256,r.privateKey),y=await async function(e,t){const n=await a.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:e,info:c},t,256);return a.subtle.importKey("raw",n,"HKDF",!1,["deriveBits"])}(o.auth,i),w=await async function(e,t){const r=await a.subtle.exportKey("raw",e),o=await a.subtle.exportKey("raw",t);return n([s,new Uint8Array([0,r.byteLength]),new Uint8Array(r),new Uint8Array([0,o.byteLength]),new Uint8Array(o)])}(o.p256,r.publicKey),d=new Uint8Array(16);a.getRandomValues(d);const g=await async function(e,t,r){const o=n([u,r]);return a.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:t,info:o},e,96)}(y,d,w),h=await async function(e,t,r){const o=n([l,r]),i=await a.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:t,info:o},e,128);return a.subtle.importKey("raw",i,"AES-GCM",!1,["encrypt"])}(y,d,w),b=function(e){let t=Math.round(100*Math.random());const r=e.byteLength+2+t;r>4078&&(t-=r-4078);const a=new Uint8Array(2+t);return new DataView(a.buffer).setUint16(0,t),n([a,e])}((new TextEncoder).encode(e.payload)),f=await a.subtle.encrypt({name:"AES-GCM",iv:g},h,b);return{headers:await p({options:e,payloadLength:f.byteLength,salt:d,localPublicKey:r.publicKey}),body:f,endpoint:e.target.endpoint}}class d{constructor(e,t){this.publicKey=e,this.privateKey=t}async toJSON(){const t=await a.subtle.exportKey("raw",this.publicKey),n=await a.subtle.exportKey("pkcs8",this.privateKey);return{publicKey:e(t),privateKey:e(n)}}static async fromJSON(e){const n=await a.subtle.importKey("raw",t(e.publicKey),{name:"ECDSA",namedCurve:"P-256"},!0,[]),r=await a.subtle.importKey("pkcs8",t(e.privateKey),{name:"ECDSA",namedCurve:"P-256"},!0,["sign"]);return new d(n,r)}static async generate(){const e=await a.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!0,["sign"]);if(console.log("got private?"),!e.publicKey||!e.privateKey)throw new Error("Did not generate both public and private key somehow");return new d(e.publicKey,e.privateKey)}}
const [ ApplicationServerKeys, generatePushHTTPRequest ] = [ d, w ];
function filter(str, include, ...keys) {
const pairs = str.split(';').map(pair => pair.trim());
const filteredPairs = pairs.filter(pair => { const key = pair.split('=')[0]; return include ? keys.includes(key) : !keys.includes(key); });
return filteredPairs.join(';');
}
const applicationServerKeys = await ApplicationServerKeys.fromJSON(ID);
const request = await generatePushHTTPRequest({ applicationServerKeys, target: subscription, payload: { data: 'test' }, ttl: 60 });
if (1) {
request.headers['Encryption'] = `keyid=p256dh;${request.headers['Encryption']}`;
request.headers['Crypto-Key'] = `keyid=p256dh;${filter(request.headers['Crypto-Key'])}`;
}
delete request.endpoint;
request.method = 'POST';
const f = `await fetch('${subscription.endpoint}', ${JSON.stringify(request)})`; console.warn(f); // @maxholman – don't worry about the body; server complains about the headers!
</script>
<script type=module>
import { buildPushPayload } from '//js.efn.kr/npm/@block65/webcrypto-web-push/+esm';
const vapid = { subject: adminContact, publicKey: id.P, privateKey: 'cCLvfK0eWp1uJFRc0Srmx2Ne1NYPSX6_YGnJnRtATGu' };
const request = await buildPushPayload(message, subscription, vapid);
const f = `await fetch('${subscription.endpoint}', ${JSON.stringify(request)})`; console.warn(f); // @maxholman – don't worry about the body; server complains about the headers!
</script>