|
@@ -0,0 +1,420 @@
|
|
|
+Error.stackTraceLimit = Infinity;
|
|
|
+
|
|
|
+let urlPublicKeysMap = new Map();
|
|
|
+
|
|
|
+const hex2Arr = str => {
|
|
|
+ if (!str) {
|
|
|
+ return new Uint8Array()
|
|
|
+ }
|
|
|
+ const arr = []
|
|
|
+ for (let i = 0, len = str.length; i < len; i+=2) {
|
|
|
+ arr.push(parseInt(str.substr(i, 2), 16))
|
|
|
+ }
|
|
|
+ return new Uint8Array(arr)
|
|
|
+}
|
|
|
+
|
|
|
+const buf2Hex = buf => {
|
|
|
+ return Array.from(new Uint8Array(buf))
|
|
|
+ .map(x => ('00' + x.toString(16)).slice(-2))
|
|
|
+ .join('')
|
|
|
+}
|
|
|
+
|
|
|
+function doVerify(msg1, sigval, pubkey) {
|
|
|
+ var curve = "secp256r1";
|
|
|
+ let sigalg = "SHA256withECDSA";
|
|
|
+ var sig = new KJUR.crypto.Signature({"alg": sigalg, "prov": "cryptojs/jsrsa"});
|
|
|
+ sig.init({xy: pubkey, curve: curve});
|
|
|
+ sig.updateHex(msg1);
|
|
|
+ var result = sig.verify(sigval);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+async function generateOwnKeypairAndDeriveKey(dh_ga_x, dh_ga_y)
|
|
|
+{
|
|
|
+ let alicePublicKey = hex2Arr('04' + dh_ga_x + dh_ga_y);
|
|
|
+ try {
|
|
|
+ const bobKey = await window.crypto.subtle.generateKey(
|
|
|
+ { name: 'ECDH', namedCurve: 'P-256' },
|
|
|
+ false,
|
|
|
+ ['deriveKey', 'deriveBits']);
|
|
|
+
|
|
|
+ const bobPublicKeyExported = await window.crypto.subtle.exportKey('raw', bobKey.publicKey);
|
|
|
+ const bobPublicKeyHex = buf2Hex(bobPublicKeyExported);
|
|
|
+
|
|
|
+ console.log(`Client's publicKey: ${bobPublicKeyHex}`);
|
|
|
+
|
|
|
+ const aliceKeyImported = await window.crypto.subtle.importKey(
|
|
|
+ 'raw',
|
|
|
+ alicePublicKey,
|
|
|
+ { name: 'ECDH', namedCurve: 'P-256'},
|
|
|
+ true,
|
|
|
+ []);
|
|
|
+
|
|
|
+ const sharedSecret = await window.crypto.subtle.deriveBits(
|
|
|
+ { name: 'ECDH', namedCurve: 'P-256', public: aliceKeyImported },
|
|
|
+ bobKey.privateKey,
|
|
|
+ 256);
|
|
|
+
|
|
|
+ const sharedSecretHex = buf2Hex(sharedSecret);
|
|
|
+ console.log(`sharedSecret: ${sharedSecretHex}`);
|
|
|
+
|
|
|
+ const hashOfSharedSecret = await window.crypto.subtle.digest("SHA-256", sharedSecret);
|
|
|
+ console.log("This is the hash" + buf2Hex(hashOfSharedSecret));
|
|
|
+
|
|
|
+ const ownPublicKeyArray=Array.from(hex2Arr(bobPublicKeyHex).slice(1));
|
|
|
+ const ownPublicKeyUnreadableString = ownPublicKeyArray.map(x => String.fromCharCode(x)).join('');
|
|
|
+ const ownPublicKeyBase64=btoa(ownPublicKeyUnreadableString);
|
|
|
+
|
|
|
+ return {mitigatorClientPublicKey: ownPublicKeyBase64, mitigatorDerivedKey: hashOfSharedSecret.slice(0, 16)};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (err) {
|
|
|
+ console.log(err);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function useDecryptorPublicKeyReturnOwnPublicKey(message) // sender
|
|
|
+{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(!("decryptorPublicKey" in message))
|
|
|
+ return {error: "could not find decryptorPublicKey"};
|
|
|
+
|
|
|
+ decryptorPublicKeyX=message["decryptorPublicKey"]["x"];
|
|
|
+ decryptorPublicKeyY=message["decryptorPublicKey"]["y"];
|
|
|
+ let returnValue = await generateOwnKeypairAndDeriveKey(decryptorPublicKeyX, decryptorPublicKeyY);
|
|
|
+ if(!("mitigatorClientPublicKey" in returnValue) || !("mitigatorDerivedKey" in returnValue))
|
|
|
+ return {error: "Oh no :( Could not generate the public key or the derived key"};
|
|
|
+ console.log("And this is the derived key.");
|
|
|
+ console.log(buf2Hex(returnValue.mitigatorDerivedKey));
|
|
|
+ return {"ownPublicKeyBase64": returnValue.mitigatorClientPublicKey, "derivedKey": returnValue.mitigatorDerivedKey};
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function recursiveIncrementIV(iv, byteCounter, minByteCounter)
|
|
|
+{
|
|
|
+ if(iv[byteCounter] !== 0xff)
|
|
|
+ {
|
|
|
+ iv[byteCounter]++;
|
|
|
+ return {iv: iv};
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(byteCounter === minByteCounter)
|
|
|
+ return {error: "Reached maximum number of messages. Plz regenerate a new key."};
|
|
|
+ else
|
|
|
+ return recursiveIncrementIV(iv, byteCounter-1, minByteCounter);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function incrementIV(iv)
|
|
|
+{
|
|
|
+ return recursiveIncrementIV(iv, 11, 8);
|
|
|
+}
|
|
|
+*/
|
|
|
+async function regenerateOwnPublicKey(input)
|
|
|
+{
|
|
|
+ if(!input.decryptorPublicKeyObj && !input.url)
|
|
|
+ {
|
|
|
+ console.log("No decryptor public key or URL: dont know which website to regenerate own public key, derived key for");
|
|
|
+ return {error: "No decryptor public key or URL: dont know which website to regenerate own public key, derived key for"};
|
|
|
+ }
|
|
|
+ let decryptorPublicKeyObj;
|
|
|
+
|
|
|
+ if(input.url)
|
|
|
+ {
|
|
|
+ returnValue=getDecryptorPublicKeyForUrl(input.url);
|
|
|
+ if(returnValue.error)
|
|
|
+ return returnValue;
|
|
|
+ decryptorPublicKeyObj=returnValue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ decryptorPublicKeyObj=input.decryptorPublicKeyObj;
|
|
|
+
|
|
|
+ returnValue=await useDecryptorPublicKeyReturnOwnPublicKey(decryptorPublicKeyObj);
|
|
|
+ console.log(returnValue);
|
|
|
+ if(!returnValue.ownPublicKeyBase64 || !returnValue.derivedKey)
|
|
|
+ {
|
|
|
+ let string3="Could not derive own public key or the derivedkey";
|
|
|
+ console.log(string3 + returnValue);
|
|
|
+ return {error: string3 + returnValue};
|
|
|
+ }
|
|
|
+
|
|
|
+ decryptorPublicKeyObj.ownPublicKey=returnValue.ownPublicKeyBase64;
|
|
|
+ decryptorPublicKeyObj.derivedKey = returnValue.derivedKey;
|
|
|
+ let iv=new Uint8Array(12);
|
|
|
+ window.crypto.getRandomValues(iv);
|
|
|
+ decryptorPublicKeyObj.iv = iv;
|
|
|
+ urlPublicKeysMap.set(input.url, decryptorPublicKeyObj);
|
|
|
+ return {success: "Successfully set own public key"};
|
|
|
+}
|
|
|
+
|
|
|
+async function processMitigatorPublicKeyHeader(e)
|
|
|
+{
|
|
|
+ const headers=e.responseHeaders;
|
|
|
+ const mitigatorHeader = headers.find(function(element) {
|
|
|
+ return element.name === "Mitigator-Public-Key";
|
|
|
+ });
|
|
|
+
|
|
|
+ if(mitigatorHeader === undefined)
|
|
|
+ {
|
|
|
+ const string="Either Mitigator is not supported or this function is called on headers for other objects fetched along with the webpage.";
|
|
|
+ console.log(string);
|
|
|
+ return {log: string};
|
|
|
+ }
|
|
|
+
|
|
|
+ let url;
|
|
|
+ if(e.documentUrl === undefined)
|
|
|
+ url=e.url;
|
|
|
+ else
|
|
|
+ url=e.documentUrl;
|
|
|
+ url=url.split('/').slice(0, 3).join('/').concat('/');
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ console.log("Here's the header value:");
|
|
|
+ console.log(mitigatorHeader["value"]);
|
|
|
+
|
|
|
+ let returnValue = verifyHeaderRetrieveDecryptorPublicKey(mitigatorHeader["value"]);
|
|
|
+ if (!returnValue.decryptorPublicKey)
|
|
|
+ {
|
|
|
+ console.log("Could not find decryptor public key:");
|
|
|
+ return {error: "Could not find decryptor public key:"};
|
|
|
+ }
|
|
|
+ const decryptorPublicKeyObj=returnValue;
|
|
|
+ urlPublicKeysMap.set(url, decryptorPublicKeyObj);
|
|
|
+
|
|
|
+ returnValue = await regenerateOwnPublicKey({decryptorPublicKeyObj: decryptorPublicKeyObj});
|
|
|
+ console.log(returnValue);
|
|
|
+}
|
|
|
+
|
|
|
+async function encryptFieldsForUrl(url, fields_array)
|
|
|
+{
|
|
|
+ let returnValue = getDerivedKeyIvForUrl(url);
|
|
|
+ if(returnValue.error)
|
|
|
+ {
|
|
|
+ console.log(returnValue);
|
|
|
+ return returnValue;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ const derivedKey=returnValue.derivedKey;
|
|
|
+ let iv=returnValue.iv;
|
|
|
+ let ciphertextArray = [];
|
|
|
+
|
|
|
+ const algo = { name: "AES-GCM", iv: iv };
|
|
|
+
|
|
|
+ const aes_key_handle = await window.crypto.subtle.importKey("raw", derivedKey,
|
|
|
+ algo,
|
|
|
+ false,
|
|
|
+ ["encrypt"]
|
|
|
+ );
|
|
|
+
|
|
|
+
|
|
|
+ for (field of fields_array) {
|
|
|
+ let fieldAscii = new Uint8Array(field.length);
|
|
|
+ for (let i=0;i<field.length; i++)
|
|
|
+ {
|
|
|
+ fieldAscii[i] = field.charCodeAt(i);
|
|
|
+ }
|
|
|
+ console.log("About to encrypt this:");
|
|
|
+ console.log(field);
|
|
|
+ console.log("In ASCII:");
|
|
|
+ console.log(fieldAscii.toString());
|
|
|
+ try {
|
|
|
+ const ciphertextAndTag = await crypto.subtle.encrypt(algo, aes_key_handle, fieldAscii);
|
|
|
+ const ciphertextAndTagUint8 = new Uint8Array(ciphertextAndTag);
|
|
|
+ let ciphertext = ciphertextAndTagUint8.slice(0,-16);
|
|
|
+ let tag = ciphertextAndTagUint8.slice(-16);
|
|
|
+ let ciphertextIVTag = Array.of(...ciphertext, ...iv, ...tag);
|
|
|
+ console.log(ciphertextIVTag);
|
|
|
+ let ciphertextIVTagBase64 = btoa(ciphertextIVTag.map(x => String.fromCharCode(x)).join(''));
|
|
|
+ console.log(ciphertextIVTagBase64);
|
|
|
+ ciphertextArray.push(ciphertextIVTagBase64);
|
|
|
+ }
|
|
|
+ catch(err)
|
|
|
+ {
|
|
|
+ console.log(err);
|
|
|
+ console.log("Something went wrong when encrypting the fields.");
|
|
|
+ return {error: "Something went wrong when encrypting the fields."};
|
|
|
+ }
|
|
|
+
|
|
|
+ window.crypto.getRandomValues(iv);
|
|
|
+ returnValue.iv = iv;
|
|
|
+ }
|
|
|
+ return {ciphertextFields: ciphertextArray};
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function verifyHeaderRetrieveDecryptorPublicKey(mitigatorHeaderValue) {
|
|
|
+
|
|
|
+ console.log("Verifying header");
|
|
|
+ let decryptorLongTermPublicKey = "04e2e1449dece8b8cf759b2f52541a9ff28eee70449cfb67807079b04f6d2a10e36dd1f3735ebfc95e1a7a4162c6eede07a8969bd1ff5850008614325002a882c3";
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ const decodedData = atob(mitigatorHeaderValue);
|
|
|
+
|
|
|
+
|
|
|
+ const decodedDataArray = [];
|
|
|
+ for (let i = 0; i < decodedData.length; i++) {
|
|
|
+ decodedDataArray[i] = decodedData.charCodeAt(i).toString(16).padStart(2, '0');
|
|
|
+ }
|
|
|
+
|
|
|
+ const signature_r = decodedDataArray.slice(96,128).join('');
|
|
|
+ const signature_s = decodedDataArray.slice(128).join('');
|
|
|
+ let signature = "30440220";
|
|
|
+ signature=signature + signature_r + "0220" + signature_s;
|
|
|
+ const signature_data = decodedDataArray.slice(0,96).join('');
|
|
|
+
|
|
|
+ const result = doVerify(signature_data, signature, decryptorLongTermPublicKey);
|
|
|
+ if(result)
|
|
|
+ console.log("Yay dude it works wtf");
|
|
|
+ else
|
|
|
+ {
|
|
|
+ console.log("Damn, dude, it doesn't.");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const verifier_mrenclave = decodedDataArray.slice(64).join('');
|
|
|
+
|
|
|
+
|
|
|
+ const public_key_x=decodedDataArray.slice(0,32).join('');
|
|
|
+ const public_key_y=decodedDataArray.slice(32,64).join('');
|
|
|
+
|
|
|
+ const urlDecryptorPublicKeyObject = {decryptorPublicKey: {x: public_key_x, y: public_key_y}};
|
|
|
+ return urlDecryptorPublicKeyObject;
|
|
|
+}
|
|
|
+
|
|
|
+function setMitigatorClientPublicKeyHeader(e) {
|
|
|
+ let headers = e.requestHeaders;
|
|
|
+ let returnValue = getOwnPublicKeyForUrl(e.url);
|
|
|
+ if(returnValue.ownPublicKey)
|
|
|
+ {
|
|
|
+ let clientHeaderObj={name: "mitigator-client-public-key", value:returnValue["ownPublicKey"] };
|
|
|
+ headers.push(clientHeaderObj);
|
|
|
+ console.log("Setting client's public key");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ console.log(returnValue);
|
|
|
+ return {requestHeaders: headers};
|
|
|
+}
|
|
|
+
|
|
|
+function getDecryptorPublicKeyForUrl(url)
|
|
|
+{
|
|
|
+ const searchUrl = url.split('/').slice(0, 3).join('/').concat('/');
|
|
|
+ const hasSearchUrl = urlPublicKeysMap.has(searchUrl);
|
|
|
+ let urlPublicKeyObj;
|
|
|
+ if(hasSearchUrl)
|
|
|
+ {
|
|
|
+ urlPublicKeyObj= urlPublicKeysMap.get(searchUrl);
|
|
|
+ return {decryptorPublicKey: urlPublicKeyObj["decryptorPublicKey"]};
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return {error: "Could not find this URL"};
|
|
|
+}
|
|
|
+
|
|
|
+function setOwnPublicKeyForUrl(url, ownPublicKeyBase64)
|
|
|
+{
|
|
|
+ const searchUrl = url.split('/').slice(0, 3).join('/').concat('/');
|
|
|
+ const hasSearchUrl = urlPublicKeysMap.has(searchUrl);
|
|
|
+ if(hasSearchUrl)
|
|
|
+ {
|
|
|
+ let urlPublicKeyObj= urlPublicKeysMap.get(searchUrl);
|
|
|
+ urlPublicKeyObj["ownPublicKey"]=ownPublicKeyBase64;
|
|
|
+ urlPublicKeysMap.set(searchUrl, urlPublicKeyObj);
|
|
|
+ return {success: "Successfully set own public key"};
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return { error: "Could not find this URL"};
|
|
|
+}
|
|
|
+*/
|
|
|
+function getOwnPublicKeyForUrl(url)
|
|
|
+{
|
|
|
+ const searchUrl = url.split('/').slice(0, 3).join('/').concat('/');
|
|
|
+ const hasSearchUrl = urlPublicKeysMap.has(searchUrl);
|
|
|
+ let returnValue;
|
|
|
+ if(hasSearchUrl)
|
|
|
+ {
|
|
|
+ returnValue=urlPublicKeysMap.get(searchUrl);
|
|
|
+ if(returnValue.ownPublicKey)
|
|
|
+ return {ownPublicKey:returnValue["ownPublicKey"]};
|
|
|
+ else {
|
|
|
+ return {error: "Own public key has not been set by content script yet."}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return {error: "Could not find this URL"};
|
|
|
+}
|
|
|
+
|
|
|
+function getDerivedKeyIvForUrl(url)
|
|
|
+{
|
|
|
+ const searchUrl = url.split('/').slice(0, 3).join('/').concat('/');
|
|
|
+ const hasSearchUrl = urlPublicKeysMap.has(searchUrl);
|
|
|
+ let returnValue;
|
|
|
+ if(hasSearchUrl)
|
|
|
+ {
|
|
|
+ returnValue=urlPublicKeysMap.get(searchUrl);
|
|
|
+ if(returnValue.derivedKey && returnValue.iv)
|
|
|
+ return {derivedKey:returnValue.derivedKey, iv:returnValue.iv};
|
|
|
+ else {
|
|
|
+ return {error: "Derived key, IV have not been set by content script yet."}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return {error: "Could not find this URL"};
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+async function backgroundListener(message)
|
|
|
+{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let returnValue; let ownPublicKey;
|
|
|
+ if(!message.url)
|
|
|
+ {
|
|
|
+ returnValue={error: "No URL sent: cant check if Mitigator is supported on the content script site."};
|
|
|
+ console.log(returnValue.error);
|
|
|
+ return returnValue;
|
|
|
+ }
|
|
|
+ if(message.regenerateOwnPublicKey)
|
|
|
+ {
|
|
|
+ returnValue = await regenerateOwnPublicKey({url: message.url});
|
|
|
+ console.log(returnValue);
|
|
|
+ if(returnValue.error)
|
|
|
+ return returnValue;
|
|
|
+ }
|
|
|
+ if(message.fields)
|
|
|
+ returnValue=await encryptFieldsForUrl(message.url, message.fields);
|
|
|
+ if(message.getOwnPublicKey)
|
|
|
+ {
|
|
|
+ ownPublicKey=getOwnPublicKeyForUrl(message.url);
|
|
|
+ returnValue.ownPublicKey=ownPublicKey;
|
|
|
+ }
|
|
|
+ return returnValue;
|
|
|
+}
|
|
|
+
|
|
|
+browser.webRequest.onHeadersReceived.addListener(
|
|
|
+ processMitigatorPublicKeyHeader,
|
|
|
+ {urls: ["http://*/*"]},
|
|
|
+ ["responseHeaders"]
|
|
|
+);
|
|
|
+
|
|
|
+browser.webRequest.onBeforeSendHeaders.addListener(
|
|
|
+ setMitigatorClientPublicKeyHeader,
|
|
|
+ {urls: ["http://*/*"]},
|
|
|
+ ["blocking", "requestHeaders"]
|
|
|
+);
|
|
|
+
|
|
|
+browser.runtime.onMessage.addListener(backgroundListener);
|