diff --git a/src/App.tsx b/src/App.tsx index ddae05d..f2578fd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -86,6 +86,7 @@ const router = createBrowserRouter([ }, { path: "/reports", + loader: getServerSettings, element: , }, { diff --git a/src/apis/helpers/decrypt-report.ts b/src/apis/helpers/decrypt-report.ts index e77eee9..dfcc0bb 100644 --- a/src/apis/helpers/decrypt-report.ts +++ b/src/apis/helpers/decrypt-report.ts @@ -3,13 +3,17 @@ import update from "immutability-helper" import {DecryptedReportContent} from "~/server-types" import {AuthContextType} from "~/components" +import {extractCleartextFromSignedMessage} from "~/utils" export default async function decryptReport( - encryptedContent: string, + signedMessage: string, decryptContent: AuthContextType["_decryptUsingPrivateKey"], + publicKeyInPEM: string, ): Promise { + const encryptedMessage = await extractCleartextFromSignedMessage(signedMessage, publicKeyInPEM) + return update( - camelcaseKeys(JSON.parse(await decryptContent(encryptedContent)), { + camelcaseKeys(JSON.parse(await decryptContent(encryptedMessage)), { deep: true, }), { diff --git a/src/components/widgets/DecryptReport.tsx b/src/components/widgets/DecryptReport.tsx index 8ccb400..4f58625 100644 --- a/src/components/widgets/DecryptReport.tsx +++ b/src/components/widgets/DecryptReport.tsx @@ -1,10 +1,11 @@ import {ReactElement, useContext} from "react" import {useAsync} from "react-use" -import {DecryptedReportContent, Report} from "~/server-types" +import {DecryptedReportContent, Report, ServerSettings} from "~/server-types" import decryptReport from "~/apis/helpers/decrypt-report" import {AuthContext} from "../AuthContext" +import {useLoaderData} from "react-router-dom" interface DecryptReportPropsBase { encryptedContent?: string @@ -30,11 +31,12 @@ export default function DecryptReport({ reports, children: render, }: DecryptReportProps): ReactElement { + const serverSettings = useLoaderData() as ServerSettings const {_decryptUsingPrivateKey} = useContext(AuthContext) const {value} = useAsync(async () => { const decrypt = async (content: string): Promise => - decryptReport(content, _decryptUsingPrivateKey) + decryptReport(content, _decryptUsingPrivateKey, serverSettings.publicKey) if (encryptedContent) { return decrypt(encryptedContent) diff --git a/src/server-types.ts b/src/server-types.ts index ef3ee5e..2d522e1 100644 --- a/src/server-types.ts +++ b/src/server-types.ts @@ -81,6 +81,7 @@ export interface ServerSettings { emailLoginExpirationInSeconds: number customAliasSuffixLength: number instanceSalt: string + publicKey: string } export interface Alias { diff --git a/src/utils/crypto/extract-cleartext-from-signed-message.ts b/src/utils/crypto/extract-cleartext-from-signed-message.ts new file mode 100644 index 0000000..6ecf725 --- /dev/null +++ b/src/utils/crypto/extract-cleartext-from-signed-message.ts @@ -0,0 +1,26 @@ +import {readCleartextMessage, readKey, verify} from "openpgp" + +// Extracts the message from a signed message +// Automatically verifies the signature, if it fails, an error is thrown +export default async function extractCleartextFromSignedMessage( + signedMessage: string, + publicKeyInPEM: string, +): Promise { + const publicKey = await readKey({ + armoredKey: publicKeyInPEM, + }) + const message = await readCleartextMessage({ + cleartextMessage: signedMessage, + }) + const result = await verify({ + // @ts-ignore: The example shows this exact usage + message: message, + verificationKeys: publicKey, + }) + + const {verified} = result.signatures[0] + + await verified + + return message.getText() +} diff --git a/src/utils/crypto/index.ts b/src/utils/crypto/index.ts index b945c22..066a3dd 100644 --- a/src/utils/crypto/index.ts +++ b/src/utils/crypto/index.ts @@ -10,3 +10,5 @@ export * from "./get-master-password" export {default as getMasterPassword} from "./get-master-password" export * from "./generate-keys" export {default as generateKeys} from "./generate-keys" +export * from "./extract-cleartext-from-signed-message" +export {default as extractCleartextFromSignedMessage} from "./extract-cleartext-from-signed-message"