From da82403f7f9e8f5b705abaa93cc63dcbdbd53318 Mon Sep 17 00:00:00 2001 From: imbytecat Date: Tue, 10 Mar 2026 15:08:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor(server):=20signAndPackReport=20?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20Kotlin=20=E5=8F=82=E8=80=83=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E7=9A=84=E6=91=98=E8=A6=81=E4=B8=8E=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/routers/crypto.router.ts | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/apps/server/src/server/api/routers/crypto.router.ts b/apps/server/src/server/api/routers/crypto.router.ts index 259bc97..fdd7982 100644 --- a/apps/server/src/server/api/routers/crypto.router.ts +++ b/apps/server/src/server/api/routers/crypto.router.ts @@ -19,8 +19,11 @@ import { os } from '../server' const summaryPayloadSchema = z .object({ taskId: z.string().min(1, 'summary.json must contain a non-empty taskId'), - checkId: z.string().optional(), - inspectionId: z.string().optional(), + checkId: z.union([z.string(), z.number()]).optional(), + inspectionId: z.union([z.string(), z.number()]).optional(), + orgId: z.union([z.string(), z.number()]).optional(), + enterpriseId: z.union([z.string(), z.number()]).optional(), + summary: z.string().optional(), }) .loose() @@ -106,43 +109,52 @@ export const signAndPackReport = os.crypto.signAndPackReport.use(db).handler(asy } const summaryPayload = parsed.data - const checkId = summaryPayload.checkId ?? summaryPayload.inspectionId ?? '' - const signingContext = `${summaryPayload.taskId}${checkId}` + const checkId = String(summaryPayload.checkId ?? summaryPayload.inspectionId ?? '') + const orgId = summaryPayload.orgId ?? summaryPayload.enterpriseId ?? '' + + // Helper: find file in ZIP and compute its SHA256 hash + const requireFileHash = (name: string): string => { + const file = zipFiles.find((f) => f.name === name) + if (!file) { + throw new ORPCError('BAD_REQUEST', { message: `rawZip must contain ${name}` }) + } + return sha256Hex(Buffer.from(file.bytes)) + } + + // Compute SHA256 of each content file (fixed order, matching Kotlin reference) + const assetsSha256 = requireFileHash('assets.json') + const vulnerabilitiesSha256 = requireFileHash('vulnerabilities.json') + const weakPasswordsSha256 = requireFileHash('weakPasswords.json') + const reportHtmlSha256 = requireFileHash('漏洞评估报告.html') // Compute device signature + // signPayload = taskId + inspectionId + assetsSha256 + vulnerabilitiesSha256 + weakPasswordsSha256 + reportHtmlSha256 + // (plain concatenation, no separators, fixed order — matching Kotlin reference) const ikm = config.licence + config.fingerprint const signingKey = hkdfSha256(ikm, 'AUTH_V3_SALT', 'device_report_signature') - const fileHashEntries = zipFiles - .map((item) => ({ - name: item.name, - hash: sha256Hex(Buffer.from(item.bytes)), - })) - .sort((a, b) => a.name.localeCompare(b.name, 'en')) - - const hashPayload = fileHashEntries.map((item) => `${item.name}:${item.hash}`).join('|') - const signPayload = `${signingContext}|${hashPayload}` + const signPayload = `${summaryPayload.taskId}${checkId}${assetsSha256}${vulnerabilitiesSha256}${weakPasswordsSha256}${reportHtmlSha256}` const deviceSignature = hmacSha256Base64(signingKey, signPayload) - // Build final summary.json with device signature and identity + // Build final summary.json with flat structure (matching Kotlin reference) const finalSummary = { - deviceSignature, - signingContext, + orgId, + checkId, + taskId: summaryPayload.taskId, licence: config.licence, fingerprint: config.fingerprint, - payload: summaryPayload, - timestamp: Date.now(), + deviceSignature, + summary: summaryPayload.summary ?? '', } const summaryBytes = Buffer.from(JSON.stringify(finalSummary), 'utf-8') - // Build manifest.json + // Build manifest.json (fixed file list, matching Kotlin reference) const manifestFiles: Record = { 'summary.json': sha256Hex(summaryBytes), - } - for (const item of fileHashEntries) { - if (item.name !== 'summary.json') { - manifestFiles[item.name] = item.hash - } + 'assets.json': assetsSha256, + 'vulnerabilities.json': vulnerabilitiesSha256, + 'weakPasswords.json': weakPasswordsSha256, + '漏洞评估报告.html': reportHtmlSha256, } const manifestBytes = Buffer.from(JSON.stringify({ files: manifestFiles }, null, 2), 'utf-8')