mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-11-04 08:10:28 +09:00
서버, 클라이언트, 서비스 워커 공용 코드를 lib/shared로 분리하고 브라우저 전용 모듈을 lib/client로 재구성
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6a6aa1765b
commit
aee87fd9b6
82 files changed
+326
-221
No files matched your search
@@ -0,0 +1,134 @@
|
||||
import { AES_GCM_IV_SIZE } from "#lib/shared/format/index.js";
|
||||
import {
|
||||
encodeString,
|
||||
decodeString,
|
||||
encodeToBase64,
|
||||
decodeFromBase64,
|
||||
concatenateBuffers,
|
||||
} from "./encoding";
|
||||
|
||||
export const generateMasterKey = async () => {
|
||||
return {
|
||||
masterKey: await crypto.subtle.generateKey(
|
||||
{
|
||||
name: "AES-KW",
|
||||
length: 256,
|
||||
} satisfies AesKeyGenParams,
|
||||
true,
|
||||
["wrapKey", "unwrapKey"],
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const generateDataKey = async () => {
|
||||
return {
|
||||
dataKey: await crypto.subtle.generateKey(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
length: 256,
|
||||
} satisfies AesKeyGenParams,
|
||||
true,
|
||||
["encrypt", "decrypt"],
|
||||
),
|
||||
dataKeyVersion: new Date(),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeAESKeyNonextractable = async (key: CryptoKey) => {
|
||||
return await crypto.subtle.importKey(
|
||||
"raw",
|
||||
await crypto.subtle.exportKey("raw", key),
|
||||
key.algorithm,
|
||||
false,
|
||||
key.usages,
|
||||
);
|
||||
};
|
||||
|
||||
export const wrapDataKey = async (dataKey: CryptoKey, masterKey: CryptoKey) => {
|
||||
return encodeToBase64(await crypto.subtle.wrapKey("raw", dataKey, masterKey, "AES-KW"));
|
||||
};
|
||||
|
||||
export const unwrapDataKey = async (dataKeyWrapped: string, masterKey: CryptoKey) => {
|
||||
return {
|
||||
dataKey: await crypto.subtle.unwrapKey(
|
||||
"raw",
|
||||
decodeFromBase64(dataKeyWrapped),
|
||||
masterKey,
|
||||
"AES-KW",
|
||||
"AES-GCM",
|
||||
false, // Nonextractable
|
||||
["encrypt", "decrypt"],
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const wrapHmacSecret = async (hmacSecret: CryptoKey, masterKey: CryptoKey) => {
|
||||
return encodeToBase64(await crypto.subtle.wrapKey("raw", hmacSecret, masterKey, "AES-KW"));
|
||||
};
|
||||
|
||||
export const unwrapHmacSecret = async (hmacSecretWrapped: string, masterKey: CryptoKey) => {
|
||||
return {
|
||||
hmacSecret: await crypto.subtle.unwrapKey(
|
||||
"raw",
|
||||
decodeFromBase64(hmacSecretWrapped),
|
||||
masterKey,
|
||||
"AES-KW",
|
||||
{
|
||||
name: "HMAC",
|
||||
hash: "SHA-256",
|
||||
} satisfies HmacImportParams,
|
||||
true, // Extractable
|
||||
["sign", "verify"],
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const encryptData = async (data: BufferSource, dataKey: CryptoKey) => {
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const ciphertext = await crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv,
|
||||
} satisfies AesGcmParams,
|
||||
dataKey,
|
||||
data,
|
||||
);
|
||||
return { ciphertext, iv: iv.buffer };
|
||||
};
|
||||
|
||||
export const decryptData = async (
|
||||
ciphertext: BufferSource,
|
||||
iv: string | BufferSource,
|
||||
dataKey: CryptoKey,
|
||||
) => {
|
||||
return await crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: typeof iv === "string" ? decodeFromBase64(iv) : iv,
|
||||
} satisfies AesGcmParams,
|
||||
dataKey,
|
||||
ciphertext,
|
||||
);
|
||||
};
|
||||
|
||||
export const encryptString = async (plaintext: string, dataKey: CryptoKey) => {
|
||||
const { ciphertext, iv } = await encryptData(encodeString(plaintext), dataKey);
|
||||
return { ciphertext: encodeToBase64(ciphertext), iv: encodeToBase64(iv) };
|
||||
};
|
||||
|
||||
export const decryptString = async (ciphertext: string, iv: string, dataKey: CryptoKey) => {
|
||||
return decodeString(await decryptData(decodeFromBase64(ciphertext), iv, dataKey));
|
||||
};
|
||||
|
||||
export const encryptChunk = async (chunk: ArrayBuffer, dataKey: CryptoKey) => {
|
||||
const { ciphertext, iv } = await encryptData(chunk, dataKey);
|
||||
return concatenateBuffers(iv, ciphertext).buffer;
|
||||
};
|
||||
|
||||
export const decryptChunk = async (encryptedChunk: ArrayBuffer, dataKey: CryptoKey) => {
|
||||
return await decryptData(
|
||||
encryptedChunk.slice(AES_GCM_IV_SIZE),
|
||||
encryptedChunk.slice(0, AES_GCM_IV_SIZE),
|
||||
dataKey,
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user