From c580556740f0e8ed3ea38599e8b3346a61b05b20 Mon Sep 17 00:00:00 2001 From: static Date: Sun, 5 Jan 2025 20:45:31 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=9E=84=EC=8B=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/modules/crypto/aes.ts | 11 ++++- src/lib/modules/crypto/util.ts | 11 +++++ src/lib/services/file.ts | 10 +++++ .../(fullscreen)/file/[id]/+page.svelte | 42 +++++++++++++++++++ src/routes/(fullscreen)/file/[id]/+page.ts | 24 +++++++++++ src/routes/(fullscreen)/file/[id]/service.ts | 33 +++++++++++++++ src/routes/(main)/directory/[[id]]/service.ts | 24 ++++------- 7 files changed, 137 insertions(+), 18 deletions(-) create mode 100644 src/lib/services/file.ts create mode 100644 src/routes/(fullscreen)/file/[id]/+page.svelte create mode 100644 src/routes/(fullscreen)/file/[id]/+page.ts create mode 100644 src/routes/(fullscreen)/file/[id]/service.ts diff --git a/src/lib/modules/crypto/aes.ts b/src/lib/modules/crypto/aes.ts index e1e917a..7301373 100644 --- a/src/lib/modules/crypto/aes.ts +++ b/src/lib/modules/crypto/aes.ts @@ -1,4 +1,4 @@ -import { encodeToBase64, decodeFromBase64 } from "./util"; +import { encodeString, decodeString, encodeToBase64, decodeFromBase64 } from "./util"; export const generateMasterKey = async () => { return { @@ -81,3 +81,12 @@ export const decryptData = async (ciphertext: BufferSource, iv: string, dataKey: ciphertext, ); }; + +export const encryptString = async (plaintext: string, dataKey: CryptoKey) => { + const { ciphertext, iv } = await encryptData(encodeString(plaintext), dataKey); + return { ciphertext: encodeToBase64(ciphertext), iv }; +}; + +export const decryptString = async (ciphertext: string, iv: string, dataKey: CryptoKey) => { + return decodeString(await decryptData(decodeFromBase64(ciphertext), iv, dataKey)); +}; diff --git a/src/lib/modules/crypto/util.ts b/src/lib/modules/crypto/util.ts index 9aeeb9d..a3e3bc0 100644 --- a/src/lib/modules/crypto/util.ts +++ b/src/lib/modules/crypto/util.ts @@ -1,3 +1,14 @@ +const textEncoder = new TextEncoder(); +const textDecoder = new TextDecoder(); + +export const encodeString = (data: string) => { + return textEncoder.encode(data); +}; + +export const decodeString = (data: ArrayBuffer) => { + return textDecoder.decode(data); +}; + export const encodeToBase64 = (data: ArrayBuffer) => { return btoa(String.fromCharCode(...new Uint8Array(data))); }; diff --git a/src/lib/services/file.ts b/src/lib/services/file.ts new file mode 100644 index 0000000..adaa524 --- /dev/null +++ b/src/lib/services/file.ts @@ -0,0 +1,10 @@ +import { unwrapDataKey, decryptString } from "$lib/modules/crypto"; +import type { FileInfoResponse } from "$lib/server/schemas"; + +export const decryptFileMetadata = async (metadata: FileInfoResponse, masterKey: CryptoKey) => { + const { dataKey } = await unwrapDataKey(metadata.dek, masterKey); + return { + dataKey, + name: await decryptString(metadata.name, metadata.nameIv, dataKey), + }; +}; diff --git a/src/routes/(fullscreen)/file/[id]/+page.svelte b/src/routes/(fullscreen)/file/[id]/+page.svelte new file mode 100644 index 0000000..380dfb2 --- /dev/null +++ b/src/routes/(fullscreen)/file/[id]/+page.svelte @@ -0,0 +1,42 @@ + + + + 파일 + + +{#if metadata} + +{:else} + +{/if} diff --git a/src/routes/(fullscreen)/file/[id]/+page.ts b/src/routes/(fullscreen)/file/[id]/+page.ts new file mode 100644 index 0000000..6ecfe77 --- /dev/null +++ b/src/routes/(fullscreen)/file/[id]/+page.ts @@ -0,0 +1,24 @@ +import { error } from "@sveltejs/kit"; +import { z } from "zod"; +import { callGetApi } from "$lib/hooks"; +import type { FileInfoResponse } from "$lib/server/schemas"; +import type { PageLoad } from "./$types"; + +export const load: PageLoad = async ({ params, fetch }) => { + const zodRes = z + .object({ + id: z.coerce.number().int().positive(), + }) + .safeParse(params); + if (!zodRes.success) error(404, "Not found"); + const { id } = zodRes.data; + + const res = await callGetApi(`/api/file/${id}`, fetch); + if (!res.ok) error(404, "Not found"); + + const fileInfo: FileInfoResponse = await res.json(); + return { + id, + metadata: fileInfo, + }; +}; diff --git a/src/routes/(fullscreen)/file/[id]/service.ts b/src/routes/(fullscreen)/file/[id]/service.ts new file mode 100644 index 0000000..16aba8b --- /dev/null +++ b/src/routes/(fullscreen)/file/[id]/service.ts @@ -0,0 +1,33 @@ +import { decryptData } from "$lib/modules/crypto"; + +export { decryptFileMetadata } from "$lib/services/file"; + +export const requestFileDownload = ( + fileId: number, + fileEncryptedIv: string, + dataKey: CryptoKey, +) => { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.responseType = "arraybuffer"; + + xhr.addEventListener("load", async () => { + if (xhr.status !== 200) { + reject(new Error("Failed to download file")); + return; + } + + const fileDecrypted = await decryptData( + xhr.response as ArrayBuffer, + fileEncryptedIv, + dataKey, + ); + resolve(fileDecrypted); + }); + + // TODO: Progress, ... + + xhr.open("GET", `/api/file/${fileId}/download`); + xhr.send(); + }); +}; diff --git a/src/routes/(main)/directory/[[id]]/service.ts b/src/routes/(main)/directory/[[id]]/service.ts index b093e61..d7cae06 100644 --- a/src/routes/(main)/directory/[[id]]/service.ts +++ b/src/routes/(main)/directory/[[id]]/service.ts @@ -1,12 +1,12 @@ import { callSignedPostApi } from "$lib/hooks"; import { encodeToBase64, - decodeFromBase64, generateDataKey, wrapDataKey, unwrapDataKey, encryptData, - decryptData, + encryptString, + decryptString, digestMessage, signRequestBody, } from "$lib/modules/crypto"; @@ -14,28 +14,18 @@ import type { DirectroyInfoResponse, DirectoryCreateRequest, FileUploadRequest, - FileInfoResponse, } from "$lib/server/schemas"; import type { MasterKey } from "$lib/stores"; +export { decryptFileMetadata } from "$lib/services/file"; + export const decryptDirectroyMetadata = async ( metadata: NonNullable, masterKey: CryptoKey, ) => { const { dataKey } = await unwrapDataKey(metadata.dek, masterKey); return { - name: new TextDecoder().decode( - await decryptData(decodeFromBase64(metadata.name), metadata.nameIv, dataKey), - ), - }; -}; - -export const decryptFileMetadata = async (metadata: FileInfoResponse, masterKey: CryptoKey) => { - const { dataKey } = await unwrapDataKey(metadata.dek, masterKey); - return { - name: new TextDecoder().decode( - await decryptData(decodeFromBase64(metadata.name), metadata.nameIv, dataKey), - ), + name: await decryptString(metadata.name, metadata.nameIv, dataKey), }; }; @@ -69,7 +59,7 @@ export const requestFileUpload = async ( const { dataKey } = await generateDataKey(); const fileEncrypted = await encryptData(await file.arrayBuffer(), dataKey); const fileEncryptedHash = await digestMessage(fileEncrypted.ciphertext); - const nameEncrypted = await encryptData(new TextEncoder().encode(file.name), dataKey); + const nameEncrypted = await encryptString(file.name, dataKey); const form = new FormData(); form.set( @@ -81,7 +71,7 @@ export const requestFileUpload = async ( dek: await wrapDataKey(dataKey, masterKey.key), contentHash: encodeToBase64(fileEncryptedHash), contentIv: fileEncrypted.iv, - name: encodeToBase64(nameEncrypted.ciphertext), + name: nameEncrypted.ciphertext, nameIv: nameEncrypted.iv, }, signKey,