mirror of
https://github.com/excalidraw/excalidraw.git
synced 2024-11-10 11:35:52 +01:00
* fix: use random IV for link-sharing encryption (#2829) * fix: add backward compatibility for link-sharing encryption (#2829)
This commit is contained in:
parent
127c1be6ad
commit
c8743a8c02
@ -80,8 +80,10 @@ export type SocketUpdateData = SocketUpdateDataSource[keyof SocketUpdateDataSour
|
||||
_brand: "socketUpdateData";
|
||||
};
|
||||
|
||||
const IV_LENGTH_BYTES = 12; // 96 bits
|
||||
|
||||
export const createIV = () => {
|
||||
const arr = new Uint8Array(12);
|
||||
const arr = new Uint8Array(IV_LENGTH_BYTES);
|
||||
return window.crypto.getRandomValues(arr);
|
||||
};
|
||||
|
||||
@ -175,6 +177,22 @@ export const getImportedKey = (key: string, usage: KeyUsage) =>
|
||||
[usage],
|
||||
);
|
||||
|
||||
const decryptImported = async (
|
||||
iv: ArrayBuffer,
|
||||
encrypted: ArrayBuffer,
|
||||
privateKey: string,
|
||||
): Promise<ArrayBuffer> => {
|
||||
const key = await getImportedKey(privateKey, "decrypt");
|
||||
return window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv,
|
||||
},
|
||||
key,
|
||||
encrypted,
|
||||
);
|
||||
};
|
||||
|
||||
const importFromBackend = async (
|
||||
id: string | null,
|
||||
privateKey?: string | null,
|
||||
@ -183,6 +201,7 @@ const importFromBackend = async (
|
||||
const response = await fetch(
|
||||
privateKey ? `${BACKEND_V2_GET}${id}` : `${BACKEND_GET}${id}.json`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
window.alert(t("alerts.importBackendFailed"));
|
||||
return {};
|
||||
@ -190,16 +209,19 @@ const importFromBackend = async (
|
||||
let data: ImportedDataState;
|
||||
if (privateKey) {
|
||||
const buffer = await response.arrayBuffer();
|
||||
const key = await getImportedKey(privateKey, "decrypt");
|
||||
const iv = new Uint8Array(12);
|
||||
const decrypted = await window.crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv,
|
||||
},
|
||||
key,
|
||||
buffer,
|
||||
);
|
||||
|
||||
let decrypted: ArrayBuffer;
|
||||
try {
|
||||
// Buffer should contain both the IV (fixed length) and encrypted data
|
||||
const iv = buffer.slice(0, IV_LENGTH_BYTES);
|
||||
const encrypted = buffer.slice(IV_LENGTH_BYTES, buffer.byteLength);
|
||||
decrypted = await decryptImported(iv, encrypted, privateKey);
|
||||
} catch (error) {
|
||||
// Fixed IV (old format, backward compatibility)
|
||||
const fixedIv = new Uint8Array(IV_LENGTH_BYTES);
|
||||
decrypted = await decryptImported(fixedIv, buffer, privateKey);
|
||||
}
|
||||
|
||||
// We need to convert the decrypted array buffer to a string
|
||||
const string = new window.TextDecoder("utf-8").decode(
|
||||
new Uint8Array(decrypted) as any,
|
||||
@ -263,9 +285,8 @@ export const exportToBackend = async (
|
||||
true, // extractable
|
||||
["encrypt", "decrypt"],
|
||||
);
|
||||
// The iv is set to 0. We are never going to reuse the same key so we don't
|
||||
// need to have an iv. (I hope that's correct...)
|
||||
const iv = new Uint8Array(12);
|
||||
|
||||
const iv = createIV();
|
||||
// We use symmetric encryption. AES-GCM is the recommended algorithm and
|
||||
// includes checks that the ciphertext has not been modified by an attacker.
|
||||
const encrypted = await window.crypto.subtle.encrypt(
|
||||
@ -276,6 +297,11 @@ export const exportToBackend = async (
|
||||
key,
|
||||
encoded,
|
||||
);
|
||||
|
||||
// Concatenate IV with encrypted data (IV does not have to be secret).
|
||||
const payloadBlob = new Blob([iv.buffer, encrypted]);
|
||||
const payload = await new Response(payloadBlob).arrayBuffer();
|
||||
|
||||
// We use jwk encoding to be able to extract just the base64 encoded key.
|
||||
// We will hardcode the rest of the attributes when importing back the key.
|
||||
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
|
||||
@ -283,7 +309,7 @@ export const exportToBackend = async (
|
||||
try {
|
||||
const response = await fetch(BACKEND_V2_POST, {
|
||||
method: "POST",
|
||||
body: encrypted,
|
||||
body: payload,
|
||||
});
|
||||
const json = await response.json();
|
||||
if (json.id) {
|
||||
|
Loading…
Reference in New Issue
Block a user