test(windows): compare signing concurrency with supervised probes

This commit is contained in:
winewill committed 2026-10-22 14:27:28 +08:00
1 parent 0f21742b25
commit 4db4345865
7 files changed
+692 -2

No files matched your search

@@ -0,0 +1,70 @@
/** Bounded scheduling for the opt-in signing experiment; production signing does not use this helper. */
/** Operations owned by one experiment, with independently bounded completion and hardware work. */
export interface SigningExperimentOptions<T> {
readonly jobs: 1 | 2
readonly hardwareConcurrency: 1 | 2
readonly signal?: AbortSignal
/** Complete one item, awaiting the supplied signing operation and all subsequent work before returning. */
readonly complete: (item: T, sign: (path: string) => Promise<void>) => Promise<void>
/** Perform one hardware request without retrying it. */
readonly sign: (item: T, path: string) => Promise<void>
}
/**
* Schedule independent experiment items and stop dispatch on the first failure or cancellation.
* @param items Ordered inputs owned by this batch.
* @param options Completion workers, hardware limit, cancellation and experiment operations.
* @returns Resolves after every item completes; otherwise drains issued work before rejecting with the first reason.
* Cancellation prevents new items and hardware requests but does not terminate an issued native operation.
*/
export async function runSigningExperimentBatch<T>(items: readonly T[], options: SigningExperimentOptions<T>): Promise<void> {
let failure: { reason: unknown } | undefined
let next = 0
let signing = 0
const requests = new Set<Promise<void>>()
const waiting = new Set<() => void>()
const wake = (): void => {
for (const resume of waiting) resume()
waiting.clear()
}
const stop = (reason: unknown): void => {
failure ??= { reason }
wake()
}
const aborted = (): void => { stop(options.signal?.reason) }
const signItem = async (item: T, path: string): Promise<void> => {
while (failure === undefined && signing >= options.hardwareConcurrency) {
await new Promise<void>((resume) => { waiting.add(resume) })
}
if (failure !== undefined) throw failure.reason
signing++
try { await options.sign(item, path) }
catch (error) { stop(error); throw error }
finally { signing--; wake() }
}
const worker = async (): Promise<void> => {
while (failure === undefined && next < items.length) {
const item = items[next++]!
try {
await options.complete(item, (path) => {
const request = signItem(item, path)
requests.add(request)
void request.then(() => requests.delete(request), () => requests.delete(request))
return request
})
}
catch (error) { stop(error) }
}
}
options.signal?.addEventListener('abort', aborted, { once: true })
try {
if (options.signal?.aborted) aborted()
await Promise.all(Array.from({ length: Math.min(options.jobs, items.length) }, worker))
// A completion failure can leave a previously issued request awaiting its native result.
await Promise.allSettled(requests)
if (failure !== undefined) throw failure.reason
} finally {
options.signal?.removeEventListener('abort', aborted)
}
}
@@ -0,0 +1,180 @@
/** Operator-only comparison of serial signing, post-sign overlap and two concurrent hardware requests. */
import { execFile } from 'node:child_process'
import { createHash, X509Certificate } from 'node:crypto'
import { existsSync } from 'node:fs'
import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'
import { homedir, cpus, totalmem } from 'node:os'
import { isAbsolute, join, resolve } from 'node:path'
import { parseArgs, promisify } from 'node:util'
import { fileURLToPath } from 'node:url'
import { loadDesktopPackageEnvironment } from '../scripts/desktop-package-environment.mjs'
import { createPackagingRun, recordPackagingEvent } from '../scripts/packaging-run.mjs'
import { beginWindowsSigningAttempt } from '../scripts/windows-signing-state.mjs'
import { withWindowsSigningStage } from '../scripts/windows-signing-stage.mjs'
import { inspectWindowsTokenIdentity, windowsTokenIdentityFailure } from '../scripts/windows-token-identity.ts'
import { buildWindowsSigningEnvironment, createWindowsTokenSigner, scrubWindowsSigningEnvironment } from '../scripts/windows-sign.mjs'
import { inspectWindowsRuntimeSignature } from '../scripts/windows-runtime-signature.mjs'
import { completeWindowsSignature, normalizeWindowsSignature } from '../scripts/windows-timestamp.mjs'
import { runSigningExperimentBatch } from './helpers/windows-signing-experiment.ts'
const APP_ROOT = fileURLToPath(new URL('..', import.meta.url))
const MODES = [
{ name: 'serial', jobs: 1, hardwareConcurrency: 1 },
{ name: 'pipeline', jobs: 2, hardwareConcurrency: 1 },
{ name: 'hardware2', jobs: 2, hardwareConcurrency: 2 },
] as const
/**
* Run an explicitly opted-in hardware experiment, retaining all generated probes and audit records.
* @param args CLI arguments; omission uses the current process arguments.
* @returns Resolves after all requested batches verify; any failure retains the batch interlock and stops later modes.
*/
export async function runWindowsSigningConcurrencyExperiment(args = process.argv.slice(2)): Promise<void> {
const { values } = parseArgs({ args, options: {
execute: { type: 'boolean' }, 'config-root': { type: 'string' }, rounds: { type: 'string', default: '2' },
} })
if (!values.execute) {
console.log('Operator hardware experiment: node apps/desktop/tests/windows-signing-concurrency.perf.ts --execute --config-root <absolute-machine-packaging-directory> [--rounds 1|2]')
return
}
if (process.platform !== 'win32' || !values['config-root'] || !isAbsolute(values['config-root'])
|| !['1', '2'].includes(values.rounds)) throw new Error('Requires Windows, an absolute config-root and rounds 1 or 2')
const environment = loadDesktopPackageEnvironment('win32', {
...process.env, DSH_PACKAGING_PRESET: 'test', DSH_PACKAGING_CONFIG_DIR: values['config-root'],
})
const certificateFile = environment.DSH_DESKTOP_WINDOWS_CER_FILE!
const signTool = environment.DSH_DESKTOP_WINDOWS_SIGNTOOL!
const tokenPin = environment.DSH_DESKTOP_WINDOWS_TOKEN_PIN!
const keyContainer = environment.DSH_DESKTOP_WINDOWS_KEY_CONTAINER!.trim()
// Construction validates the same signing inputs; the production serial hook is never invoked here.
createWindowsTokenSigner({ certificateFile, signTool, tokenPin, keyContainer })
const certificate = await readFile(certificateFile)
const leaf = new X509Certificate(certificate)
if (Date.now() < Date.parse(leaf.validFrom) || Date.now() > Date.parse(leaf.validTo)) {
throw new Error('Signing experiment requires a currently valid public certificate')
}
const thumbprint = leaf.fingerprint.replaceAll(':', '')
const safeEnvironment = scrubWindowsSigningEnvironment(environment)
delete safeEnvironment.NODE_OPTIONS
delete safeEnvironment.NODE_PATH
const run = createPackagingRun(join(APP_ROOT, '.desktop-build', 'qualification', 'signing-concurrency'), {
kind: 'operator-signing-concurrency', certificateSha256: createHash('sha256').update(certificate).digest('hex'),
node: process.version, cpu: cpus()[0]?.model, logicalCpus: cpus().length, memoryBytes: totalmem(),
rounds: Number(values.rounds), resourceBytes: [0, 1024 * 1024, 16 * 1024 * 1024], filesPerBatch: 6,
cache: 'disabled', probeExecuted: false,
}, { parallel: true, secrets: [tokenPin, keyContainer] })
console.log(`SIGNING_EXPERIMENT_RECORD ${run.directory}`)
const controller = new AbortController()
const abort = (): void => { controller.abort(new Error('Operator interrupted the signing experiment')) }
process.once('SIGINT', abort)
process.once('SIGTERM', abort)
const record = (event: object): void => { recordPackagingEvent(run.directory, event) }
let success = false
try {
await withWindowsSigningStage({ stage: 'operator-signing-concurrency', signal: controller.signal, record }, async () => {
if (existsSync(join(homedir(), '.dsh-desktop-signing', 'attempt.json'))) {
throw new Error('Retained signing interlock requires operator inspection; experiment refused')
}
const identity = await inspectWindowsTokenIdentity({ certificate, container: keyContainer, environment: safeEnvironment })
record({ type: 'experiment-public-identity', result: identity, pinSupplied: false })
if (identity !== 'matched') throw new Error(windowsTokenIdentityFailure(identity))
const templates: string[] = []
const source = join(run.directory, 'probe.cs')
await writeFile(source, 'internal static class SigningProbe { private static int Main() { return 0; } }\n', { flag: 'wx' })
for (const size of [0, 1024 * 1024, 16 * 1024 * 1024]) {
controller.signal.throwIfAborted()
const resource = join(run.directory, `resource-${size}.bin`)
if (size) await writeFile(resource, Buffer.alloc(size, 0x5a), { flag: 'wx' })
const output = join(run.directory, `unsigned-${size}.exe`)
await run.run(`compile-probe-${size}`, join(environment.SystemRoot ?? 'C:\\Windows', 'Microsoft.NET/Framework64/v4.0.30319/csc.exe'),
['/nologo', '/target:exe', '/platform:x64', `/out:${output}`, ...(size ? [`/resource:${resource}`] : []), source],
{ cwd: run.directory, env: safeEnvironment, timeoutMs: 30_000 })
if ((await inspectWindowsRuntimeSignature(output)).status !== 'NotSigned') throw new Error('Probe must be unsigned')
templates.push(output)
}
const results: { round: number; mode: string; elapsedMs: number; files: number }[] = []
for (let round = 1; round <= Number(values.rounds); round++) {
const order = round % 2 ? MODES : [...MODES].reverse()
for (const mode of order) {
controller.signal.throwIfAborted()
const directory = join(run.directory, `round-${round}-${mode.name}`)
await mkdir(directory)
const files: string[] = []
for (let index = 0; index < templates.length * 2; index++) {
const file = join(directory, `probe-${index}.exe`)
await copyFile(templates[Math.floor(index / 2)]!, file)
files.push(file)
}
record({ type: 'experiment-batch-plan', round, ...mode, files })
const attempt = beginWindowsSigningAttempt({ runDirectory: run.directory, target: directory })
let commandIndex = 0
const started = performance.now()
try {
await runSigningExperimentBatch(files, {
...mode, signal: controller.signal,
sign: async (_file, path) => {
controller.signal.throwIfAborted()
const stage = `experiment-sign-${round}-${mode.name}-${commandIndex++}`
record({ type: 'experiment-hardware-start', stage, path })
const interpreter = environment.ComSpec ?? join(environment.SystemRoot ?? 'C:\\Windows', 'System32', 'cmd.exe')
await run.run(stage, interpreter,
['/d', '/v:off', '/c', 'windows-sign.cmd'], {
cwd: join(APP_ROOT, 'scripts'), timeoutMs: 60_000,
env: buildWindowsSigningEnvironment(safeEnvironment, {
certificateFile, signTool, tokenPin, keyContainer, path, isNest: false,
}),
})
record({ type: 'experiment-hardware-exited', stage, path })
const signature = await inspectWindowsRuntimeSignature(path)
if (signature.status !== 'Valid' || signature.timestamped || signature.thumbprint?.toUpperCase() !== thumbprint.toUpperCase()) {
throw new Error('Primary signature verification failed')
}
record({ type: 'experiment-hardware-verified', stage, path })
},
complete: async (path, sign) => {
await completeWindowsSignature(path, {
thumbprint, sign, inspect: inspectWindowsRuntimeSignature, evidenceDirectory: run.directory, record,
normalize: file => normalizeWindowsSignature(file, signTool, safeEnvironment),
timestamp: async (file) => {
await promisify(execFile)(signTool, ['timestamp', '/v', '/tr', 'http://timestamp.digicert.com', '/td', 'sha256', file], {
env: safeEnvironment, windowsHide: true, timeout: 60_000,
})
},
})
const signature = await inspectWindowsRuntimeSignature(path)
if (signature.status !== 'Valid' || !signature.timestamped || signature.thumbprint?.toUpperCase() !== thumbprint.toUpperCase()) {
throw new Error('Published probe signature verification failed')
}
record({ type: 'experiment-file-verified', path, ...signature,
sha256: createHash('sha256').update(await readFile(path)).digest('hex') })
},
})
controller.signal.throwIfAborted()
const result = { round, mode: mode.name, elapsedMs: performance.now() - started, files: files.length }
results.push(result)
record({ type: 'experiment-batch-complete', ...result })
await writeFile(join(run.directory, 'measurements.json'), `${JSON.stringify(results, null, 2)}\n`, { flush: true })
attempt.success()
console.log(`SIGNING_EXPERIMENT_SAMPLE ${JSON.stringify(result)}`)
} catch (error) {
attempt.failure(null, 'Signing concurrency experiment failed; inspect retained records before recovery')
throw error
}
}
}
})
success = true
} finally {
process.removeListener('SIGINT', abort)
process.removeListener('SIGTERM', abort)
run.finish(success)
}
}
if (process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
runWindowsSigningConcurrencyExperiment().catch(() => {
// Child-process errors can embed arguments; the retained supervisor output is already redacted.
console.error('Signing experiment stopped; inspect its retained record. No automatic hardware retry or interlock recovery.')
process.exitCode = 1
})
}
@@ -0,0 +1,178 @@
/** Driver ownership tests replace every native signing and device boundary. */
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { runWindowsSigningConcurrencyExperiment } from './windows-signing-concurrency.perf.ts'
import { loadDesktopPackageEnvironment } from '../scripts/desktop-package-environment.mjs'
import { createPackagingRun } from '../scripts/packaging-run.mjs'
import { beginWindowsSigningAttempt } from '../scripts/windows-signing-state.mjs'
import { inspectWindowsTokenIdentity } from '../scripts/windows-token-identity.ts'
const state = vi.hoisted(() => ({
root: '', environment: {} as NodeJS.ProcessEnv,
identity: 'matched', failure: '' as '' | 'primary' | 'final' | 'audit',
signed: new Set<string>(), completed: new Set<string>(), stages: [] as string[],
events: [] as object[], successes: 0, failures: 0,
}))
vi.mock('node:child_process', () => ({
execFile: vi.fn(() => { throw new Error('Native subprocesses are forbidden in driver tests') }),
}))
vi.mock('node:os', async original => ({ ...await original<typeof import('node:os')>(), homedir: () => state.root }))
vi.mock('node:crypto', async original => ({
...await original<typeof import('node:crypto')>(),
X509Certificate: class {
fingerprint = 'A'.repeat(40)
validFrom = 'Jan 1 2000 GMT'
validTo = 'Jan 1 2100 GMT'
},
}))
vi.mock('../scripts/desktop-package-environment.mjs', () => ({
loadDesktopPackageEnvironment: vi.fn(() => state.environment),
}))
vi.mock('../scripts/windows-sign.mjs', async original => ({
...await original<typeof import('../scripts/windows-sign.mjs')>(),
createWindowsTokenSigner: vi.fn(() => { throw new Error('unconfigured constructor') }),
}))
vi.mock('../scripts/windows-token-identity.ts', () => ({
inspectWindowsTokenIdentity: vi.fn(async () => state.identity),
windowsTokenIdentityFailure: (result: string) => `Identity refused: ${result}`,
}))
vi.mock('../scripts/windows-signing-stage.mjs', () => ({
withWindowsSigningStage: vi.fn(async (_options: object, operation: () => Promise<void>) => operation()),
}))
vi.mock('../scripts/packaging-run.mjs', () => ({
createPackagingRun: vi.fn(() => ({
directory: join(state.root, 'run'),
finish: vi.fn(),
run: vi.fn(async (stage: string, _executable: string, args: readonly string[], options: { env: NodeJS.ProcessEnv }) => {
state.stages.push(stage)
if (stage.startsWith('compile-')) {
await writeFile(args.find(arg => arg.startsWith('/out:'))!.slice(5), 'inert probe')
} else {
state.signed.add(options.env.DSH_DESKTOP_WINDOWS_SIGN_TARGET!)
}
}),
})),
recordPackagingEvent: (_directory: string, event: object) => {
if (state.failure === 'audit' && 'type' in event && event.type === 'experiment-file-verified') throw new Error('Audit failed')
state.events.push(event)
},
}))
vi.mock('../scripts/windows-signing-state.mjs', () => ({
beginWindowsSigningAttempt: vi.fn((options: { runDirectory: string; target: string; stateDirectory?: string }) => {
expect(options.stateDirectory).toBeUndefined()
const lock = join(state.root, '.dsh-desktop-signing', 'attempt.json')
mkdirSync(dirname(lock), { recursive: true })
writeFileSync(lock, 'batch attempt', { flag: 'wx' })
return {
success: () => {
const verified = state.events.filter(event => 'type' in event && event.type === 'experiment-file-verified'
&& 'path' in event && typeof event.path === 'string' && dirname(event.path) === options.target)
expect(verified).toHaveLength(6)
expect(readFileSync(join(options.runDirectory, 'measurements.json'), 'utf8')).toContain('"files": 6')
unlinkSync(lock)
state.successes++
},
failure: () => { state.failures++ },
}
}),
}))
vi.mock('../scripts/windows-runtime-signature.mjs', () => ({
inspectWindowsRuntimeSignature: vi.fn(async (path: string) => {
if (!state.signed.has(path)) return { status: 'NotSigned', timestamped: false, thumbprint: null }
const timestamped = state.completed.has(path)
const failed = state.failure === (timestamped ? 'final' : 'primary')
return { status: failed ? 'HashMismatch' : 'Valid', timestamped, thumbprint: 'A'.repeat(40) }
}),
}))
vi.mock('../scripts/windows-timestamp.mjs', () => ({
completeWindowsSignature: vi.fn(async (path: string, options: { sign: (path: string) => Promise<void> }) => {
await options.sign(path)
state.completed.add(path)
}),
normalizeWindowsSignature: vi.fn(() => { throw new Error('Native normalization is forbidden in driver tests') }),
}))
const platform = Object.getOwnPropertyDescriptor(process, 'platform')!
beforeEach(async () => {
vi.clearAllMocks()
state.root = await mkdtemp(join(tmpdir(), 'signing-driver-'))
mkdirSync(join(state.root, 'run'))
state.identity = 'matched'; state.failure = ''; state.successes = 0; state.failures = 0
state.signed.clear(); state.completed.clear(); state.stages = []; state.events = []
state.environment = {
DSH_DESKTOP_WINDOWS_CER_FILE: join(state.root, 'certificate.cer'),
DSH_DESKTOP_WINDOWS_SIGNTOOL: 'forbidden-signer', DSH_DESKTOP_WINDOWS_TOKEN_PIN: 'fixture-pin',
DSH_DESKTOP_WINDOWS_KEY_CONTAINER: ' fixture-container ', API_KEY: 'fixture-secret', NODE_OPTIONS: 'forbidden-option',
}
await writeFile(state.environment.DSH_DESKTOP_WINDOWS_CER_FILE!, 'inert certificate')
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true })
const { createWindowsTokenSigner } = await import('../scripts/windows-sign.mjs')
vi.mocked(createWindowsTokenSigner).mockReturnValue(vi.fn(async () => { throw new Error('Production signer is forbidden') }))
vi.spyOn(console, 'log').mockImplementation(() => {})
})
afterEach(async () => {
Object.defineProperty(process, 'platform', platform)
vi.useRealTimers()
vi.restoreAllMocks()
await rm(state.root, { recursive: true, force: true, maxRetries: 3 })
})
const execute = () => runWindowsSigningConcurrencyExperiment(['--execute', '--config-root', state.root, '--rounds', '1'])
const lock = () => join(state.root, '.dsh-desktop-signing', 'attempt.json')
it('requires opt-in before reading configuration or querying a device', async () => {
await runWindowsSigningConcurrencyExperiment([])
expect(loadDesktopPackageEnvironment).not.toHaveBeenCalled()
expect(inspectWindowsTokenIdentity).not.toHaveBeenCalled()
expect(createPackagingRun).not.toHaveBeenCalled()
})
it.each(['1999-01-01', '2101-01-01'])('rejects certificate validity at %s before querying or signing', async (date) => {
vi.useFakeTimers({ toFake: ['Date'] }); vi.setSystemTime(new Date(date))
await expect(execute()).rejects.toThrow('currently valid')
expect(inspectWindowsTokenIdentity).not.toHaveBeenCalled()
expect(beginWindowsSigningAttempt).not.toHaveBeenCalled()
expect(state.stages).toEqual([])
})
it.each(['certificate-mismatch', 'provider-unavailable', 'container-unavailable', 'unknown'])('refuses public identity %s before any signing', async (identity) => {
state.identity = identity
await expect(execute()).rejects.toThrow('Identity refused')
expect(state.stages).toEqual([])
expect(beginWindowsSigningAttempt).not.toHaveBeenCalled()
})
it('retains an existing default-account interlock without accessing the device', async () => {
mkdirSync(dirname(lock()), { recursive: true }); writeFileSync(lock(), 'prior failure')
await expect(execute()).rejects.toThrow('Retained signing interlock')
expect(inspectWindowsTokenIdentity).not.toHaveBeenCalled()
expect(readFileSync(lock(), 'utf8')).toBe('prior failure')
})
it('completes all three modes and releases each default-account batch only after recording every verification', async () => {
await execute()
expect(state.successes).toBe(3)
expect(state.failures).toBe(0)
expect(state.signed.size).toBe(18)
expect(existsSync(lock())).toBe(false)
expect(beginWindowsSigningAttempt).toHaveBeenCalledTimes(3)
expect(inspectWindowsTokenIdentity).toHaveBeenCalledWith(expect.objectContaining({
container: 'fixture-container', environment: {},
}))
expect(JSON.stringify(state.events)).not.toMatch(/fixture-pin|fixture-secret|fixture-container/u)
})
it.each(['primary', 'final', 'audit'] as const)('retains the batch interlock and stops later modes after %s failure', async (failure) => {
state.failure = failure
await expect(execute()).rejects.toThrow(failure === 'audit' ? 'Audit failed' : 'signature verification failed')
expect(state.successes).toBe(0)
expect(state.failures).toBe(1)
expect(beginWindowsSigningAttempt).toHaveBeenCalledOnce()
expect(state.signed.size).toBe(1)
expect(existsSync(lock())).toBe(true)
expect(state.stages.some(stage => stage.includes('pipeline') || stage.includes('hardware2'))).toBe(false)
})
@@ -0,0 +1,258 @@
import { expect, it, vi } from 'vitest'
import { runSigningExperimentBatch } from './helpers/windows-signing-experiment.ts'
function barrier() {
const pending = Promise.withResolvers<undefined>()
return { promise: pending.promise, resolve: () => { pending.resolve(undefined) } }
}
const observe = (operation: Promise<void>) => operation.then(
() => ({ ok: true as const }), (error: unknown) => ({ ok: false as const, error }),
)
it('finishes each item before beginning the next in serial mode', async () => {
const events: string[] = []
await runSigningExperimentBatch([1, 2], {
jobs: 1, hardwareConcurrency: 1,
sign: async (item, path) => { expect(path).toBe(`staged-${item}`); events.push(`sign ${item}`) },
complete: async (item, sign) => {
events.push(`start ${item}`)
await sign(`staged-${item}`)
events.push(`complete ${item}`)
},
})
expect(events).toEqual(['start 1', 'sign 1', 'complete 1', 'start 2', 'sign 2', 'complete 2'])
})
it('overlaps completion with the next signature while keeping hardware concurrency at one', async () => {
const firstSigning = barrier()
const releaseFirstSignature = barrier()
const secondStarted = barrier()
const firstPost = barrier()
const secondSigned = barrier()
const releaseCompletion = barrier()
const started: number[] = []
let active = 0
let peak = 0
const outcome = observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 1,
sign: async (item) => {
active++
peak = Math.max(peak, active)
try {
if (item === 1) { firstSigning.resolve(); await releaseFirstSignature.promise }
if (item === 2) secondSigned.resolve()
} finally { active-- }
},
complete: async (item, sign) => {
started.push(item)
if (item === 2) secondStarted.resolve()
await sign(`staged-${item}`)
if (item === 1) firstPost.resolve()
await releaseCompletion.promise
},
}))
try {
await Promise.all([firstSigning.promise, secondStarted.promise])
expect(active).toBe(1)
releaseFirstSignature.resolve()
await Promise.all([firstPost.promise, secondSigned.promise])
expect(started).toEqual([1, 2])
expect(peak).toBe(1)
} finally {
releaseFirstSignature.resolve()
releaseCompletion.resolve()
expect(await outcome).toEqual({ ok: true })
}
expect(started).toEqual([1, 2, 3])
})
it('allows two hardware requests but does not start a third item while both jobs are occupied', async () => {
const bothSigning = barrier()
const release = barrier()
const started: number[] = []
let active = 0
let peak = 0
const outcome = observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 2,
sign: async () => {
active++
peak = Math.max(peak, active)
if (active === 2) bothSigning.resolve()
try { await release.promise } finally { active-- }
},
complete: async (item, sign) => { started.push(item); await sign(`staged-${item}`) },
}))
try {
await bothSigning.promise
expect(started).toEqual([1, 2])
expect(active).toBe(2)
} finally { release.resolve(); expect(await outcome).toEqual({ ok: true }) }
expect(peak).toBe(2)
expect(active).toBe(0)
})
it.each([new Error('hardware failed'), undefined])('stops queued signatures and new items after a hardware rejection (%s)', async (failure) => {
const secondStarted = barrier()
const sign = vi.fn(async () => { await secondStarted.promise; throw failure })
const started: number[] = []
const result = await observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 1, sign,
complete: async (item, request) => {
started.push(item)
if (item === 2) secondStarted.resolve()
await request(`staged-${item}`)
},
}))
expect(result).toEqual({ ok: false, error: failure })
expect(sign).toHaveBeenCalledTimes(1)
expect(started).toEqual([1, 2])
})
it('drains concurrent hardware after the first failure and retains that failure', async () => {
const secondSigning = barrier()
const firstFailed = barrier()
const releaseSecond = barrier()
const failure = new Error('first hardware failure')
const calls: number[] = []
let active = 0
let settled = false
const outcome = observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 2,
sign: async (item) => {
calls.push(item)
active++
try {
if (item === 1) { await secondSigning.promise; throw failure }
secondSigning.resolve()
await releaseSecond.promise
throw new Error('later hardware failure')
} finally { active-- }
},
complete: async (item, sign) => {
try { await sign(`staged-${item}`) }
catch (error) { if (item === 1) firstFailed.resolve(); throw error }
},
})).then((result) => { settled = true; return result })
try {
await firstFailed.promise
expect(settled).toBe(false)
expect(active).toBe(1)
expect(calls).toEqual([1, 2])
} finally { releaseSecond.resolve(); expect(await outcome).toEqual({ ok: false, error: failure }) }
expect(active).toBe(0)
expect(calls).toEqual([1, 2])
})
it('preserves an undefined completion failure and starts no further work', async () => {
const sign = vi.fn(async () => {})
const complete = vi.fn(async () => { throw undefined })
expect(await observe(runSigningExperimentBatch([1, 2], {
jobs: 1, hardwareConcurrency: 1, complete, sign,
}))).toEqual({ ok: false, error: undefined })
expect(complete).toHaveBeenCalledTimes(1)
expect(sign).not.toHaveBeenCalled()
})
it('prevents a waiting signature when completion fails and drains the issued hardware request', async () => {
const secondSigning = barrier()
const releaseSecond = barrier()
const failureObserved = barrier()
const failure = new Error('timestamp failed')
const calls: number[] = []
let pending: ReturnType<typeof observe> | undefined
let settled = false
const outcome = observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 1,
sign: async (item) => {
calls.push(item)
if (item === 2) { secondSigning.resolve(); await releaseSecond.promise }
},
complete: async (item, sign) => {
await sign(`staged-${item}`)
if (item === 1) {
await secondSigning.promise
// Failure can interrupt a completion that already owns a queued request.
const waiting = observe(sign(`staged-retry-${item}`)).then((result) => { failureObserved.resolve(); return result })
pending = waiting
throw failure
}
},
})).then((result) => { settled = true; return result })
try {
await failureObserved.promise
expect(settled).toBe(false)
expect(calls).toEqual([1, 2])
} finally { releaseSecond.resolve(); expect(await outcome).toEqual({ ok: false, error: failure }) }
expect(calls).toEqual([1, 2])
expect(await pending).toEqual({ ok: false, error: failure })
})
it('drains both completion workers after one fails without starting more items', async () => {
const secondPost = barrier()
const releaseSecondPost = barrier()
const failed = barrier()
const failure = new Error('verification failed')
const started: number[] = []
let settled = false
const outcome = observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 2, sign: async () => {},
complete: async (item, sign) => {
started.push(item)
await sign(`staged-${item}`)
if (item === 1) { await secondPost.promise; failed.resolve(); throw failure }
secondPost.resolve()
await releaseSecondPost.promise
},
})).then((result) => { settled = true; return result })
try {
await failed.promise
expect(settled).toBe(false)
expect(started).toEqual([1, 2])
} finally { releaseSecondPost.resolve(); expect(await outcome).toEqual({ ok: false, error: failure }) }
expect(started).toEqual([1, 2])
})
it('cancels waiting signatures and new items while waiting for issued hardware to finish', async () => {
const controller = new AbortController()
const secondStarted = barrier()
const release = barrier()
const waitingRejected = barrier()
const reason = new Error('operator cancelled')
const calls: number[] = []
let active = false
let settled = false
const outcome = observe(runSigningExperimentBatch([1, 2, 3], {
jobs: 2, hardwareConcurrency: 1, signal: controller.signal,
sign: async (item) => {
calls.push(item)
active = true
try { await release.promise } finally { active = false }
},
complete: async (item, sign) => {
if (item === 2) secondStarted.resolve()
try { await sign(`staged-${item}`) }
catch (error) { if (item === 2) waitingRejected.resolve(); throw error }
},
})).then((result) => { settled = true; return result })
try {
await secondStarted.promise
controller.abort(reason)
await waitingRejected.promise
expect(active).toBe(true)
expect(settled).toBe(false)
expect(calls).toEqual([1])
} finally { release.resolve(); expect(await outcome).toEqual({ ok: false, error: reason }) }
expect(active).toBe(false)
expect(calls).toEqual([1])
})
it('does not begin work when cancellation was already requested', async () => {
const reason = new Error('already cancelled')
const complete = vi.fn(async () => {})
const sign = vi.fn(async () => {})
expect(await observe(runSigningExperimentBatch([1], {
jobs: 2, hardwareConcurrency: 2, signal: AbortSignal.abort(reason), complete, sign,
}))).toEqual({ ok: false, error: reason })
expect(complete).not.toHaveBeenCalled()
expect(sign).not.toHaveBeenCalled()
})
@@ -36,8 +36,8 @@
en: 6e8208d19ad510e5
zh: eee42e8938c0f603
/windows-signing-and-runner-operations/verification-limits:
en: 94a471410c84a1c7
zh: 115228e4ca36581d
en: e04b54cec106c2b7
zh: 1941e4b0f0249c20
/windows-signing-and-runner-operations/dev-note:
en: b75c8458928b72b9
zh: 003ba4e17b4b5269
+2
View File
@@ -106,6 +106,8 @@ Use `pnpm --dir apps/desktop run cache:windows-signatures --usage` to inspect st
Real hardware and installation evidence is owned by the [Desktop qualification record](../tests/README.md) and [installed-update procedure](../tests/installed-update/README.md). This document does not authorize PIN retries, automatic interlock deletion, device initialization or changes to the accepted isolation policy.
For operator-approved concurrency experiments, run `node apps/desktop/tests/windows-signing-concurrency.perf.ts --execute --config-root <absolute-packaging-directory> --rounds 2`. Without `--execute`, it only prints usage. The diagnostic compares serial completion, two completion workers with one hardware request, and two concurrent hardware requests, using fresh copies of six synthetic PE files per batch; the second round reverses mode order. It uses the machine test preset, reads public identity first, holds the account signing-stage lock and retains one hardware-attempt interlock per batch. It stops dispatch on the first failure and drains issued work; up to two hardware calls may already be in flight. Only complete signature, timestamp and file verification permits normal interlock release. Records and probes remain under `.desktop-build/qualification/signing-concurrency`; probes are never executed and no release cache is used. Timings exclude probe preparation, lock waiting and package assembly. This experiment does not enable production concurrency or establish full-release performance.
## Dev Note
None.
@@ -106,6 +106,8 @@ Windows 打包从 `.env.windows` 或所选预设读取 `DSH_DESKTOP_WINDOWS_PACK
真实硬件与安装证据由[Desktop 验证记录](../tests/README.zh.md)和[已安装更新流程](../tests/installed-update/README.zh.md)维护。本文档不授权 PIN 重试、自动删除 interlock、设备初始化或改变已接受的隔离策略。
经操作人员授权后,可运行 `node apps/desktop/tests/windows-signing-concurrency.perf.ts --execute --config-root <打包配置目录的绝对路径> --rounds 2` 做并发实验。不加 `--execute` 只打印用法。诊断比较完全串行、两个完成任务共用单路硬件签名,以及两路硬件签名;每批使用六个合成 PE 文件的全新副本,第二轮反转模式顺序。实验读取机器 test preset,先核对公开身份,持有账户签名阶段锁,每批保留一条硬件尝试保护记录。首错停止派发并排空已发出的工作;最多可能已有两条硬件请求在途。只有全部签名、时间戳和文件验证成功才正常释放保护记录。记录与探针保留在 `.desktop-build/qualification/signing-concurrency`,探针从不执行,也不使用发布签名缓存。计时不含探针准备、等锁及包组装,实验不启用生产并发,也不代表完整发布性能。
## 开发备注
无。