feat(web): add account workspaces and signed content flows
This commit is contained in:
1 parent
7b8d61d1ba
commit
b5cf3d5958
117 files changed
+2789
-1674
No files matched your search
@@ -0,0 +1,22 @@
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user