Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add encryptFileWithKeys #580

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@hookform/resolvers": "^2.9.10",
"@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.4",
"@openpgp/web-stream-tools": "^0.0.14",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/dompurify": "^3.0.1",
"@types/downloadjs": "^1.4.2",
Expand Down
43 changes: 43 additions & 0 deletions packages/web/src/pages/Submissions/SubmissionFormPage/encrypt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Key, MaybeArray, createMessage, encrypt, generateSessionKey, readKey } from "openpgp";
import { getHatsPublicKey } from "./submissionsService.api";
import { readToEnd } from "@openpgp/web-stream-tools";

const IpfsHash = require("ipfs-only-hash");

Expand Down Expand Up @@ -39,6 +40,48 @@ export async function encryptWithKeys(publicKeyOrKeys: string | string[], dataTo
return { encryptedData, sessionKey };
}

export async function encryptFileWithKeys(publicKeyOrKeys: string | string[], fileToEncrypt: File) {
let encryptionKeys: MaybeArray<Key>;

if (Array.isArray(publicKeyOrKeys)) {
encryptionKeys = [];

const encryptionKeysList = await Promise.all(publicKeyOrKeys.map((key) => readKey({ armoredKey: key })));
for (let key of encryptionKeysList) {
try {
await key.verifyPrimaryKey();
encryptionKeys.push(key);
} catch {
continue;
}
}

if (encryptionKeys.length === 0) return undefined;
} else {
encryptionKeys = await readKey({ armoredKey: publicKeyOrKeys });

try {
await encryptionKeys.verifyPrimaryKey();
} catch (error) {
return undefined;
}
}

const sessionKey = await generateSessionKey({ encryptionKeys });

const message = await encrypt({
message: await createMessage({ binary: new Uint8Array(await fileToEncrypt.arrayBuffer() as ArrayBuffer) }),
encryptionKeys,
sessionKey,
});

const encryptedData = await readToEnd(message);

const encryptedFile = new File([encryptedData], fileToEncrypt.name, {type: fileToEncrypt.type})

return { encryptedFile, sessionKey };
Comment on lines +43 to +82

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image description CodeRabbit

The function encryptFileWithKeys is quite large and does a lot of things. It would be beneficial to break it down into smaller, more manageable functions. This will improve readability, maintainability, and testability.

For example, the key reading and verification process could be extracted into its own function:

+ async function readAndVerifyKeys(publicKeyOrKeys: string | string[]): Promise<MaybeArray<Key> | undefined> {
+   let encryptionKeys: MaybeArray<Key>;
+
+   if (Array.isArray(publicKeyOrKeys)) {
+     encryptionKeys = [];
+
+     const encryptionKeysList = await Promise.all(publicKeyOrKeys.map((key) => readKey({ armoredKey: key })));
+     for (let key of encryptionKeysList) {
+       try {
+         await key.verifyPrimaryKey();
+         encryptionKeys.push(key);
+       } catch {
+         continue;
+       }
+     }
+
+     if (encryptionKeys.length === 0) return undefined;
+   } else {
+     encryptionKeys = await readKey({ armoredKey: publicKeyOrKeys });
+
+     try {
+       await encryptionKeys.verifyPrimaryKey();
+     } catch (error) {
+       return undefined;
+     }
+   }
+
+   return encryptionKeys;
+ }

- export async function encryptFileWithKeys(publicKeyOrKeys: string | string[], fileToEncrypt: File) {
-   let encryptionKeys: MaybeArray<Key>;
-
-   if (Array.isArray(publicKeyOrKeys)) {
-     encryptionKeys = [];
-
-     const encryptionKeysList = await Promise.all(publicKeyOrKeys.map((key) => readKey({ armoredKey: key })));
-     for (let key of encryptionKeysList) {
-       try {
-         await key.verifyPrimaryKey();
-         encryptionKeys.push(key);
-       } catch {
-         continue;
-       }
-     }
-
-     if (encryptionKeys.length === 0) return undefined;
-   } else {
-     encryptionKeys = await readKey({ armoredKey: publicKeyOrKeys });
-
-     try {
-       await encryptionKeys.verifyPrimaryKey();
-     } catch (error) {
-       return undefined;
-     }
-   }
+   const encryptionKeys = await readAndVerifyKeys(publicKeyOrKeys);
+   if (!encryptionKeys) return undefined;

    const sessionKey = await generateSessionKey({ encryptionKeys });

    const message = await encrypt({
      message: await createMessage({ binary: new Uint8Array(await fileToEncrypt.arrayBuffer() as ArrayBuffer) }),
      encryptionKeys,
      sessionKey,
    });

    const encryptedData = await readToEnd(message);

    const encryptedFile = new File([encryptedData], fileToEncrypt.name, {type: fileToEncrypt.type})

    return { encryptedFile, sessionKey };
}

Also, it's important to note that the function currently swallows any errors that occur during the key verification process. This could make debugging difficult in the future. It might be better to let these errors propagate up to the caller, or at least log them for future reference.

}

export async function encryptWithHatsKey(dataToEncrypt: string): Promise<string> {
try {
const hatsPublicKeyString = await getHatsPublicKey();
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2522,6 +2522,11 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@openpgp/web-stream-tools@^0.0.14":
version "0.0.14"
resolved "https://registry.yarnpkg.com/@openpgp/web-stream-tools/-/web-stream-tools-0.0.14.tgz#513214cd2a20903558448980085155752be445e4"
integrity sha512-6btCNVf6YSsmlyIS7yw+IbzXeXCEcJxeSpxvSxkDuZj9B/ekt4fXkZj4oOaIxG4SKTftIK1svnlVroJ1cCMT4g==

"@pedrouid/environment@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@pedrouid/environment/-/environment-1.0.1.tgz"
Expand Down