38 lines
3.0 KiB
JavaScript
38 lines
3.0 KiB
JavaScript
'use strict';
|
|
const {BootstrapSchema}=require('./production-factory.cjs');
|
|
const {createHash}=require('node:crypto');
|
|
// Matches the installed native service's fixed receiver/WFP contract. Caller
|
|
// bootstrap JSON cannot choose a port outside that protected boundary.
|
|
const SERVICE_BROKER_PORT=43120;
|
|
function freeze(value){if(value&&typeof value==='object'){for(const child of Object.values(value))freeze(child);Object.freeze(value);}return value;}
|
|
// loadTrust is fixed privileged code: verify protected release/deployment files,
|
|
// rollback state and native-authenticated SID before returning its bootstrap.
|
|
// No IPC field may select this callback, trust paths, keys or SID.
|
|
function createServiceFactory({loadTrust,factory}={}){
|
|
if(typeof loadTrust!=='function'||typeof factory!=='function')throw Error('broker-bootstrap-invalid');
|
|
return async(config,callbacks)=>{
|
|
let parsed,binding,expiresAt;
|
|
try{
|
|
parsed=freeze(BootstrapSchema.parse(config));
|
|
if(parsed.brokerPort!==SERVICE_BROKER_PORT)throw Error();
|
|
const verified=await loadTrust(parsed);binding=verified?.bootstrap;expiresAt=verified?.deployment?.manifest?.expiresAt;
|
|
if(!Number.isSafeInteger(expiresAt)||expiresAt<=Date.now())throw Error();
|
|
if(!Number.isSafeInteger(verified?.policyVersion)||verified.policyVersion<1||verified.policyVersion!==callbacks?.expectedPolicyVersion||typeof verified.policyDigest!=='string'||!/^[a-f0-9]{64}$/.test(verified.policyDigest)||verified.policyDigest!==createHash('sha256').update(parsed.envelope.payload).digest('hex'))throw Error();
|
|
if(!binding||typeof binding.publicKey!=='string'||typeof binding.deviceId!=='string'||!Number.isSafeInteger(binding.minimumVersion)||binding.minimumVersion<1||
|
|
parsed.publicKey!==binding.publicKey||parsed.deviceId!==binding.deviceId||parsed.minimumVersion<binding.minimumVersion)throw Error();
|
|
// Reparse the trusted projection to prevent a malformed privileged loader
|
|
// from weakening the production bootstrap contract.
|
|
parsed=BootstrapSchema.parse({...parsed,publicKey:binding.publicKey,deviceId:binding.deviceId,minimumVersion:Math.max(parsed.minimumVersion,binding.minimumVersion)});
|
|
}catch{throw Error('broker-bootstrap-invalid');}
|
|
const created=await factory(parsed,callbacks);
|
|
if(!created||typeof created.listen!=='function'||typeof created.close!=='function')throw Error('broker-bootstrap-invalid');
|
|
let closed=false,timer,closeWork;
|
|
const close=()=>{if(closeWork)return closeWork;closed=true;clearTimeout(timer);closeWork=Promise.resolve().then(()=>created.close());return closeWork;};
|
|
const live=()=>!closed&&Date.now()<expiresAt;
|
|
const expire=()=>{if(!live()){void close().catch(()=>{});return;}timer=setTimeout(expire,Math.min(expiresAt-Date.now(),2147483647));timer.unref?.();};
|
|
expire();
|
|
return Object.freeze({...created,async listen(){if(!live()){await close();throw Error('broker-bootstrap-invalid');}const result=await created.listen();if(!live()){await close();throw Error('broker-bootstrap-invalid');}return result;},close});
|
|
};
|
|
}
|
|
module.exports={createServiceFactory,SERVICE_BROKER_PORT};
|