import { ed25519 } from '@noble/curves/ed25519.js' import { utf8ToBytes } from '@noble/hashes/utils.js' import { decodeBase64 } from '../security/base64.js' import { canonicalJson } from '../security/canonical-json.js' const TRUSTED_SIGNING_KEYS = require('../security/trusted-signing-keys.json') as Readonly< Record > export interface SignedPluginManifest { appKey: string packageName: string platform: 'ANDROID' | 'IOS' moduleId: string type: 'common' | 'app' | 'buz' version: string appVersionRange: string builtAgainstNativeBaselineId: string buildId: string bundleFormat: 'hermes-bytecode' | 'javascript' commonVersionRange?: string minNativeApiLevel: number bundleSha256: string archiveSha256: string } export class PluginManifestSignatureError extends Error { readonly name = 'PluginManifestSignatureError' constructor( message: string, public readonly code: 'UNKNOWN_SIGNING_KEY' | 'INVALID_SIGNATURE', ) { super(message) } } /** @internal 插件候选必须在生成计划和下载前通过同一可信密钥集合验签。 */ export function verifySignedPluginManifest( manifest: SignedPluginManifest, keyId: string, signature: string, trustedKeys: Readonly> = TRUSTED_SIGNING_KEYS, ): void { const publicKey = trustedKeys[keyId] if (!publicKey) { throw new PluginManifestSignatureError( `[XuqmSecurity] Untrusted plugin signing key: ${keyId}`, 'UNKNOWN_SIGNING_KEY', ) } let valid = false try { valid = ed25519.verify( decodeBase64(signature), utf8ToBytes(canonicalJson(manifest)), decodeBase64(publicKey), ) } catch { valid = false } if (!valid) { throw new PluginManifestSignatureError( `[XuqmSecurity] Invalid plugin manifest signature: ${manifest.moduleId}@${manifest.version}`, 'INVALID_SIGNATURE', ) } }