69 行
1.9 KiB
TypeScript
69 行
1.9 KiB
TypeScript
|
|
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<string, string>
|
||
|
|
>
|
||
|
|
|
||
|
|
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<Record<string, string>> = 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',
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|