23 lines
1.5 KiB
TypeScript
23 lines
1.5 KiB
TypeScript
import { bytes, b64, unb64, sealJson, openJson } from './accountCrypto';
|
|
|
|
export interface ObjectContext { spaceId: string; channelId: string; domain: 'chat' | 'files'; epoch: number; itemId: string }
|
|
const label = (c: ObjectContext) => JSON.stringify(['webdrop/object-key/v1', c.spaceId, c.channelId, c.domain, c.epoch, c.itemId]);
|
|
export const randomDomainKey = () => b64(crypto.getRandomValues(new Uint8Array(32)));
|
|
async function aes(value: string): Promise<CryptoKey> {
|
|
const material = unb64(value);
|
|
try { if (material.length !== 32) throw new Error('数据域密钥格式无效'); return await crypto.subtle.importKey('raw', bytes(material), 'AES-GCM', false, ['encrypt', 'decrypt']); }
|
|
finally { material.fill(0); }
|
|
}
|
|
export async function newObjectKey(domainKey: string, context: ObjectContext): Promise<{ key: CryptoKey; envelope: string }> {
|
|
const raw = crypto.getRandomValues(new Uint8Array(32));
|
|
try {
|
|
const key = await crypto.subtle.importKey('raw', bytes(raw), 'AES-GCM', false, ['encrypt', 'decrypt']);
|
|
return { key, envelope: await sealJson({ key: b64(raw) }, await aes(domainKey), label(context)) };
|
|
} finally { raw.fill(0); }
|
|
}
|
|
export async function unwrapObjectKey(domainKey: string, envelope: string, context: ObjectContext): Promise<CryptoKey> {
|
|
const value = await openJson(envelope, await aes(domainKey), label(context)) as { key?: unknown };
|
|
if (typeof value.key !== 'string') throw new Error('对象密钥封装格式无效');
|
|
return aes(value.key);
|
|
}
|