feat(api): add account workspaces and signed content events

This commit is contained in:
justme committed 2026-10-14 04:05:30 +08:00
1 parent 937b774a93
commit 7b8d61d1ba
63 files changed
+5407 -370

No files matched your search

+42 -1
View File
@@ -9,7 +9,7 @@
//
// 所有输入都是固定值,输出完全确定,可以放心提交到仓库。
import { createCipheriv, createHash, hkdfSync } from 'node:crypto';
import { createCipheriv, createHash, createPublicKey, hkdfSync, verify as verifySignature } from 'node:crypto';
import { readFileSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -149,6 +149,47 @@ function encodeItem({ itemId, meta, content, maxFrameData = MAX_FRAME_DATA, snap
const vectors = { version: 1 };
// 固定 ECDSA P-256 验签向量。签名只用于测试,对应私钥不进入向量。
const signatureFixture = {
x: 'OJVV0pkDZHH7F92rxIQt1EuXk4ZesLwoWTaMSANt79A',
y: 'HRP3ZgqoD_8Iv42Mw1oflcJUKeq3H1gEbbOLNJoC_zs',
kind: 'chat.create',
payload: '01020304deadbeef',
signature: '9dad329614d98c97acc5a60622329e57e111fd7579edc7e4bcc535476aae281cc7e3ee00fa2d5d901af525875af653d74bd441ca5dd54d00081b51b7c3b5adcb',
};
const signatureKind = Buffer.from(signatureFixture.kind, 'ascii');
const signaturePayload = unhex(signatureFixture.payload);
const signatureMessage = Buffer.concat([
Buffer.from([0x57, 0x44, 0x53, 0x01]), u16(signatureKind.length), signatureKind,
u32(signaturePayload.length), signaturePayload,
]);
const signaturePublicKey = createPublicKey({ key: { kty: 'EC', crv: 'P-256', x: signatureFixture.x, y: signatureFixture.y }, format: 'jwk' });
if (!verifySignature('sha256', signatureMessage, { key: signaturePublicKey, dsaEncoding: 'ieee-p1363' }, unhex(signatureFixture.signature)))
throw new Error('固定签名向量无效');
vectors.signatures = { ...signatureFixture, message: hex(signatureMessage) };
const eventDigests = [seq(0x31, 32), seq(0x71, 32)];
const eventManifestInput = Buffer.concat([Buffer.from('WD1M', 'ascii'), u32(eventDigests.length),
...eventDigests.flatMap((digest, index) => [u32(index), digest])]);
const eventFields = {
space_id: hex(channelId), channel_id: '01234567-89ab-cdef-0123-456789abcdef', username: 'alice',
device_id: '11111111-2222-3333-4444-555555555555', event_id: hex(itemC), kind: 'chat.edit',
domain: 'chat', epoch: 2, item_id: hex(itemA), frame_count: 2, target_item_id: hex(itemB),
};
const eventKind = Buffer.from(eventFields.kind, 'ascii');
const eventChannel = Buffer.from(eventFields.channel_id, 'ascii');
const eventUser = Buffer.from(eventFields.username, 'ascii');
const eventDevice = Buffer.from(eventFields.device_id, 'ascii');
const eventTarget = unhex(eventFields.target_item_id);
const eventManifest = sha256(eventManifestInput);
const eventPayload = Buffer.concat([
channelId, Buffer.from([eventChannel.length]), eventChannel, Buffer.from([eventUser.length]), eventUser,
Buffer.from([eventDevice.length]), eventDevice, itemC, Buffer.from([eventKind.length]), eventKind,
Buffer.from([1]), u32(eventFields.epoch), itemA, u32(eventFields.frame_count), eventManifest,
Buffer.from([1]), eventTarget,
]);
vectors.content_event = { ...eventFields, frame_digests: eventDigests.map(hex), manifest_hash: hex(eventManifest), payload: hex(eventPayload) };
vectors.inputs = {
channel_id: hex(channelId),
channel_id_b64: b64url(channelId),