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
@@ -8,17 +8,19 @@ export function b64(value: Uint8Array): string {
|
||||
return btoa(text);
|
||||
}
|
||||
export function unb64(value: string): Uint8Array { return Uint8Array.from(atob(value), (c) => c.charCodeAt(0)); }
|
||||
export async function deriveAccountKeys(password: string, salt: Uint8Array): Promise<{ verifier: Uint8Array; key: CryptoKey }> {
|
||||
export async function deriveAccountKeys(password: string, salt: Uint8Array, exportMaterial = false): Promise<{ verifier: Uint8Array; key: CryptoKey; material?: Uint8Array }> {
|
||||
if (salt.length !== 16 || password.length > 1024) throw new Error('账号凭据格式无效');
|
||||
const base = await crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveBits']);
|
||||
const root = new Uint8Array(await crypto.subtle.deriveBits({ name: 'PBKDF2', hash: 'SHA-256', salt: bytes(salt), iterations: 600_000 }, base, 256));
|
||||
try {
|
||||
const hmac = await crypto.subtle.importKey('raw', root, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
||||
const verifier = new Uint8Array(await crypto.subtle.sign('HMAC', hmac, encoder.encode('webdrop-account-login-v1')));
|
||||
const hkdf = await crypto.subtle.importKey('raw', root, 'HKDF', false, ['deriveKey']);
|
||||
const key = await crypto.subtle.deriveKey({ name: 'HKDF', hash: 'SHA-256', salt: bytes(salt), info: encoder.encode(vaultLabel) }, hkdf,
|
||||
{ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
|
||||
return { verifier, key };
|
||||
const hkdf = await crypto.subtle.importKey('raw', root, 'HKDF', false, ['deriveBits']);
|
||||
const material = new Uint8Array(await crypto.subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt: bytes(salt), info: encoder.encode(vaultLabel) }, hkdf, 256));
|
||||
try {
|
||||
const key = await crypto.subtle.importKey('raw', bytes(material), 'AES-GCM', false, ['encrypt', 'decrypt']);
|
||||
return { verifier, key, ...(exportMaterial ? { material: Uint8Array.from(material) } : {}) };
|
||||
} finally { material.fill(0); }
|
||||
} finally { root.fill(0); }
|
||||
}
|
||||
export async function sealJson(value: unknown, key: CryptoKey, context = vaultLabel): Promise<string> {
|
||||
@@ -36,11 +38,18 @@ export async function openJson(value: string, key: CryptoKey, context = vaultLab
|
||||
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(plain));
|
||||
} catch { throw new Error('加密数据校验失败,已拒绝导入'); }
|
||||
}
|
||||
export interface InvitationKeyPair { publicKey: JsonWebKey; privateKey: JsonWebKey }
|
||||
export type { InvitationKeyPair } from '../protocol/account';
|
||||
import type { InvitationKeyPair } from '../protocol/account';
|
||||
import type { SigningKeyPair } from '../protocol/account';
|
||||
import { generateSigningKeyPair } from '@webdrop/core';
|
||||
export async function generateInvitationKeys(): Promise<InvitationKeyPair> {
|
||||
const pair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits']);
|
||||
return { publicKey: await crypto.subtle.exportKey('jwk', pair.publicKey), privateKey: await crypto.subtle.exportKey('jwk', pair.privateKey) };
|
||||
}
|
||||
export async function generateAccountSigningKeys(): Promise<SigningKeyPair> {
|
||||
const pair = await generateSigningKeyPair(true);
|
||||
return { publicKey: await crypto.subtle.exportKey('jwk', pair.publicKey), privateKey: await crypto.subtle.exportKey('jwk', pair.privateKey) };
|
||||
}
|
||||
async function invitationKey(privateKey: JsonWebKey, publicKey: JsonWebKey, context: string): Promise<CryptoKey> {
|
||||
const own = await crypto.subtle.importKey('jwk', privateKey, { name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits']);
|
||||
const peer = await crypto.subtle.importKey('jwk', publicKey, { name: 'ECDH', namedCurve: 'P-256' }, false, []);
|
||||
|
||||
Reference in new issue
Block a user