112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
import {
|
|
deriveSharedSecret,
|
|
encrypt,
|
|
retrieveOrGenerateKeyPair,
|
|
} from "../utils/crypto";
|
|
|
|
const ENCRYPTED_URL_INPUT_ID = "encrypted-url";
|
|
const MESSAGE_INPUT_ID = "message";
|
|
const ENCRYPT_BUTTON_ID = "encrypt";
|
|
const REQUEST_PUBLIC_KEY = "requestPublicKey";
|
|
const TEMPLATE = `
|
|
<details open>
|
|
<summary>How To Use:</summary>
|
|
<ol>
|
|
<li>Enter the information you want to send back to the original requester into the text input below.</li>
|
|
<li>Click on 'Generate Response'. This will create a new URL that contains your encrypted message.</li>
|
|
<li>Send the newly generated URL back to the original requester. Only their browser will be able to decrypt the message.</li>
|
|
</ol>
|
|
</details>
|
|
|
|
<form>
|
|
<input
|
|
type="text"
|
|
name="message"
|
|
id="${MESSAGE_INPUT_ID}"
|
|
placeholder="Enter your message here..."
|
|
aria-label="Message to Encrypt"
|
|
required
|
|
/>
|
|
<input
|
|
id="${ENCRYPT_BUTTON_ID}"
|
|
type="submit"
|
|
value="Generate Response"
|
|
aria-label="Generate Response"
|
|
/>
|
|
<input
|
|
type="text"
|
|
name="encrypted-url"
|
|
id="${ENCRYPTED_URL_INPUT_ID}"
|
|
placeholder="URL with your encrypted response will appear here..."
|
|
aria-label="Encrypted URL"
|
|
/>
|
|
</form>
|
|
`;
|
|
|
|
export async function setupSendPage(element: HTMLElement, key: string) {
|
|
element.innerHTML = TEMPLATE;
|
|
localStorage.setItem(REQUEST_PUBLIC_KEY, key);
|
|
|
|
// Add an event listener to the "Encrypt" button
|
|
const encryptButton = document.getElementById(
|
|
ENCRYPT_BUTTON_ID
|
|
) as HTMLButtonElement;
|
|
encryptButton.addEventListener("click", encryptData);
|
|
}
|
|
|
|
async function encryptData(event: Event) {
|
|
event.preventDefault();
|
|
const key = localStorage.getItem(REQUEST_PUBLIC_KEY)!;
|
|
|
|
// Parse the 'p' parameter to get publicA
|
|
const publicAJwk = JSON.parse(atob(key));
|
|
|
|
// Import publicA as a CryptoKey
|
|
const publicA = await window.crypto.subtle.importKey(
|
|
"jwk",
|
|
publicAJwk,
|
|
{
|
|
name: "ECDH",
|
|
namedCurve: "P-256",
|
|
},
|
|
false,
|
|
[]
|
|
);
|
|
|
|
const keyPairB = await retrieveOrGenerateKeyPair();
|
|
|
|
// Retrieve the message input
|
|
const messageInput = document.getElementById(
|
|
MESSAGE_INPUT_ID
|
|
) as HTMLInputElement;
|
|
|
|
// Derive the AES key from your private key and the recipient's public key
|
|
const aesKey = await deriveSharedSecret(keyPairB.privateKey, publicA);
|
|
|
|
// Encrypt the message input value using the AES key
|
|
const { encryptedData, iv } = await encrypt(aesKey, messageInput.value);
|
|
|
|
// Update the encrypted URL input with the encrypted message
|
|
const encryptedUrlInput = document.getElementById(
|
|
ENCRYPTED_URL_INPUT_ID
|
|
) as HTMLInputElement;
|
|
|
|
const ecdhPublicJwk = await window.crypto.subtle.exportKey(
|
|
"jwk",
|
|
keyPairB.publicKey
|
|
);
|
|
const url = new URL(window.location.toString());
|
|
url.search = "";
|
|
url.hash = `p=${encodeURIComponent(
|
|
btoa(JSON.stringify(ecdhPublicJwk))
|
|
)}&iv=${encodeURIComponent(
|
|
btoa(String.fromCharCode.apply(null, Array.from(iv)))
|
|
)}&m=${encodeURIComponent(
|
|
btoa(
|
|
String.fromCharCode.apply(null, Array.from(new Uint8Array(encryptedData)))
|
|
)
|
|
)}`;
|
|
|
|
encryptedUrlInput.value = url.toString();
|
|
}
|