키 관리 로직을 Keyring으로 통합하여 하드코딩된 MEK 및 HSK 버전을 제거하고 Svelte Store를 Rune으로 대체

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
staticandClaude Opus 5.5 committed 2026-10-12 00:10:37 +09:00
1 parent aee87fd9b6
commit 2ce73aa331
50 files changed
+633 -641

No files matched your search

+10 -13
View File
@@ -5,7 +5,6 @@
import { CategoryCreateModal } from "#lib/components/organisms/index.js";
import { updateCategoryInfo } from "#lib/client/storage/indexedDB/index.js";
import { getCategoryInfo, type MaybeCategoryInfo } from "#lib/modules/filesystem/index.js";
import { masterKeyStore } from "#lib/stores/index.js";
import { HybridPromise, sortEntries } from "#lib/utils/index.js";
import CategoryDeleteModal from "./CategoryDeleteModal.svelte";
import CategoryMenuBottomSheet from "./CategoryMenuBottomSheet.svelte";
@@ -56,13 +55,11 @@
});
$effect(() => {
HybridPromise.resolve(getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!)).then(
(result) => {
if (data.id === result.id) {
info = result;
}
},
);
HybridPromise.resolve(getCategoryInfo(data.id)).then((result) => {
if (data.id === result.id) {
info = result;
}
});
});
</script>
@@ -114,7 +111,7 @@
onRemoveClick={!details.isRecursive
? async ({ id }) => {
await requestFileRemovalFromCategory(id, data.id as number);
void getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
void getCategoryInfo(data.id); // TODO: FIXME
}
: undefined}
/>
@@ -132,8 +129,8 @@
<CategoryCreateModal
bind:isOpen={isCategoryCreateModalOpen}
onCreateClick={async (name: string) => {
if (await requestCategoryCreation(name, data.id, $masterKeyStore?.get(1)!)) {
void getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
if (await requestCategoryCreation(name, data.id)) {
void getCategoryInfo(data.id); // TODO: FIXME
return true;
}
return false;
@@ -155,7 +152,7 @@
bind:isOpen={isCategoryRenameModalOpen}
onRenameClick={async (newName: string) => {
if (await requestCategoryRename(context.selectedCategory!, newName)) {
void getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
void getCategoryInfo(data.id); // TODO: FIXME
return true;
}
return false;
@@ -165,7 +162,7 @@
bind:isOpen={isCategoryDeleteModalOpen}
onDeleteClick={async () => {
if (await requestCategoryDeletion(context.selectedCategory!)) {
void getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
void getCategoryInfo(data.id); // TODO: FIXME
return true;
}
return false;
@@ -27,7 +27,7 @@
>
<DirectoryEntryLabel
type="file"
thumbnail={$thumbnail}
thumbnail={thumbnail?.url}
name={info.name}
subtext={formatDateTime(info.createdAt ?? info.lastModifiedAt)}
isFavorite={info.isFavorite}
+12 -23
View File
@@ -1,11 +1,9 @@
<script lang="ts">
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { page } from "$app/state";
import { FloatingButton } from "#lib/components/atoms/index.js";
import { TopBar } from "#lib/components/molecules/index.js";
import { getDirectoryInfo, type MaybeDirectoryInfo } from "#lib/modules/filesystem/index.js";
import { masterKeyStore, hmacSecretStore } from "#lib/stores/index.js";
import { HybridPromise } from "#lib/utils/index.js";
import DirectoryCreateModal from "./DirectoryCreateModal.svelte";
import DirectoryEntries from "./DirectoryEntries";
@@ -18,7 +16,6 @@
import UploadStatusCard from "./UploadStatusCard.svelte";
import {
createContext,
requestHmacSecretDownload,
requestDirectoryCreation,
requestFileUpload,
requestEntryRename,
@@ -64,7 +61,7 @@
if (!files || files.length === 0) return;
for (const file of files) {
requestFileUpload(file, data.id, $masterKeyStore?.get(1)!, $hmacSecretStore?.get(1)!, () => {
requestFileUpload(file, data.id, () => {
return new Promise((resolve) => {
duplicatedFile = file;
resolveForDuplicateFileModal = resolve;
@@ -74,7 +71,7 @@
.then((res) => {
if (!res) return;
// TODO: FIXME
void getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!);
void getDirectoryInfo(data.id);
})
.catch((e: Error) => {
// TODO: FIXME
@@ -85,20 +82,12 @@
fileInput!.value = "";
};
onMount(async () => {
if (!$hmacSecretStore && !(await requestHmacSecretDownload($masterKeyStore?.get(1)?.key!))) {
throw new Error("Failed to download hmac secrets");
}
});
$effect(() => {
HybridPromise.resolve(getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!)).then(
(result) => {
if (data.id === result.id) {
info = result;
}
},
);
HybridPromise.resolve(getDirectoryInfo(data.id)).then((result) => {
if (data.id === result.id) {
info = result;
}
});
});
</script>
@@ -162,8 +151,8 @@
<DirectoryCreateModal
bind:isOpen={isDirectoryCreateModalOpen}
onCreateClick={async (name) => {
if (await requestDirectoryCreation(name, data.id, $masterKeyStore?.get(1)!)) {
void getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
if (await requestDirectoryCreation(name, data.id)) {
void getDirectoryInfo(data.id); // TODO: FIXME
return true;
}
return false;
@@ -195,7 +184,7 @@
onFavoriteClick={async () => {
if (await requestFavoriteToggle(context.selectedEntry!)) {
isEntryMenuBottomSheetOpen = false;
void getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
void getDirectoryInfo(data.id); // TODO: FIXME
}
}}
/>
@@ -203,7 +192,7 @@
bind:isOpen={isEntryRenameModalOpen}
onRenameClick={async (newName: string) => {
if (await requestEntryRename(context.selectedEntry!, newName)) {
void getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
void getDirectoryInfo(data.id); // TODO: FIXME
return true;
}
return false;
@@ -213,7 +202,7 @@
bind:isOpen={isEntryDeleteModalOpen}
onDeleteClick={async () => {
if (await requestEntryDeletion(context.selectedEntry!)) {
void getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
void getDirectoryInfo(data.id); // TODO: FIXME
return true;
}
return false;
@@ -37,7 +37,7 @@
>
<DirectoryEntryLabel
type="file"
thumbnail={$thumbnail}
thumbnail={thumbnail?.url}
name={info.name}
subtext={formatDateTime(info.createdAt ?? info.lastModifiedAt)}
isFavorite={info.isFavorite}
@@ -5,16 +5,10 @@ import {
storeFileThumbnailCache,
deleteFileThumbnailCache,
} from "#lib/client/cache/index.js";
import { storeHmacSecrets } from "#lib/client/storage/indexedDB/index.js";
import { keyring } from "#lib/client/keys/index.js";
import { uploadFile } from "#lib/client/transfer/index.js";
import type { DataKey } from "#lib/modules/filesystem/index.js";
import {
generateDataKey,
wrapDataKey,
unwrapHmacSecret,
encryptString,
} from "#lib/shared/crypto/index.js";
import { hmacSecretStore, type MasterKey, type HmacSecret } from "#lib/stores/index.js";
import { generateDataKey, wrapDataKey, encryptString } from "#lib/shared/crypto/index.js";
import { trpc } from "#trpc/client.js";
export interface SelectedEntry {
@@ -36,35 +30,8 @@ export const useContext = () => {
return getContext<ReturnType<typeof createContext>>("context");
};
export const requestHmacSecretDownload = async (masterKey: CryptoKey) => {
// TODO: MEK rotation
let hmacSecretsWrapped;
try {
hmacSecretsWrapped = await trpc.hsk.list.query();
} catch {
// TODO: Error Handling
return false;
}
const hmacSecrets = await Promise.all(
hmacSecretsWrapped.map(async ({ version, state, hsk: hmacSecretWrapped }) => {
const { hmacSecret } = await unwrapHmacSecret(hmacSecretWrapped, masterKey);
return { version, state, secret: hmacSecret };
}),
);
await storeHmacSecrets(hmacSecrets);
hmacSecretStore.set(new Map(hmacSecrets.map((hmacSecret) => [hmacSecret.version, hmacSecret])));
return true;
};
export const requestDirectoryCreation = async (
name: string,
parentId: "root" | number,
masterKey: MasterKey,
) => {
export const requestDirectoryCreation = async (name: string, parentId: "root" | number) => {
const masterKey = keyring.activeMasterKey;
const { dataKey, dataKeyVersion } = await generateDataKey();
const nameEncrypted = await encryptString(name, dataKey);
@@ -87,11 +54,9 @@ export const requestDirectoryCreation = async (
export const requestFileUpload = async (
file: File,
parentId: "root" | number,
masterKey: MasterKey,
hmacSecret: HmacSecret,
onDuplicate: () => Promise<boolean>,
) => {
const res = await uploadFile(file, parentId, masterKey, hmacSecret, onDuplicate);
const res = await uploadFile(file, parentId, onDuplicate);
if (!res) return false;
if (res.fileBuffer) {
+1 -5
View File
@@ -3,7 +3,6 @@
import { goto } from "$app/navigation";
import { RowVirtualizer } from "#lib/components/atoms/index.js";
import { TopBar } from "#lib/components/molecules/index.js";
import { masterKeyStore } from "#lib/stores/index.js";
import Directory from "./Directory.svelte";
import File from "./File.svelte";
import { requestFavoriteEntries, requestRemoveFavorite, type FavoriteEntry } from "./service";
@@ -16,10 +15,7 @@
let isLoading = $state(true);
onMount(async () => {
const masterKey = $masterKeyStore?.get(1)?.key;
if (masterKey) {
entries = await requestFavoriteEntries(data.favorites, masterKey);
}
entries = await requestFavoriteEntries(data.favorites);
isLoading = false;
});
+1 -1
View File
@@ -26,7 +26,7 @@
>
<DirectoryEntryLabel
type="file"
thumbnail={$thumbnail}
thumbnail={thumbnail?.url}
name={info.name}
subtext={formatDateTime(info.createdAt ?? info.lastModifiedAt)}
isFavorite
+3 -6
View File
@@ -13,21 +13,18 @@ export type FavoriteEntry =
| { type: "directory"; name: string; details: SubDirectoryInfo }
| { type: "file"; name: string; details: SummarizedFileInfo };
export const requestFavoriteEntries = async (
favorites: RouterOutputs["favorites"]["get"],
masterKey: CryptoKey,
) => {
export const requestFavoriteEntries = async (favorites: RouterOutputs["favorites"]["get"]) => {
const [directories, files] = await HybridPromise.all([
HybridPromise.all(
favorites.directories.map((directory) =>
getDirectoryInfo(directory.id, masterKey, {
getDirectoryInfo(directory.id, {
serverResponse: { ...directory, isFavorite: true },
}),
),
),
HybridPromise.all(
favorites.files.map((file) =>
getFileInfo(file.id, masterKey, { serverResponse: { ...file, isFavorite: true } }),
getFileInfo(file.id, { serverResponse: { ...file, isFavorite: true } }),
),
),
]);