26 lines
2.4 KiB
JavaScript
26 lines
2.4 KiB
JavaScript
import channel from '../../packages/core/policy-channel.cjs';
|
|||
|
|
import {managementIdentity} from './channel-identity.mjs';
|
||
|
|
import {traceOperation} from './telemetry.mjs';
|
||
|
|
function expireChallenge(pending,id){const timer=setTimeout(()=>pending.delete(id),60001);timer.unref();}
|
||
|
|
export function installEvidenceChannel(app,store,device,enabled,recordEvidence){
|
||
|
|
const profile=channel.evidenceChannel,keys=managementIdentity(store),pending=new Map();
|
||
|
|
app.post('/api/device/channel/evidence/challenge',device,enabled,async(req,res)=>{
|
||
|
|
if(!req.body||Array.isArray(req.body)||typeof req.body!=='object'||Object.keys(req.body).length)return res.status(400).json({error:'Evidence challenge requires an empty request'});
|
||
|
|
const policy=store.effectivePolicy(req.device.id);if(!policy.evidence.enabled||Date.parse(policy.expiresAt)<=Date.now())return res.status(403).json({error:'Evidence collection is disabled'});
|
||
|
|
for(const [id,item]of pending)if(item.bundle.expiresAt<=Date.now()||item.bundle.deviceId===req.device.id)pending.delete(id);
|
||
|
|
if(pending.size>=10000)return res.status(503).json({error:'Protected evidence channel unavailable'});
|
||
|
|
const item=await traceOperation('evidence.challenge',{},()=>profile.challenge(req.device.id,keys.edPrivate,keys.pqPrivate));pending.set(item.bundle.id,item);expireChallenge(pending,item.bundle.id);res.json(item.bundle);
|
||
|
|
});
|
||
|
|
app.post('/api/device/channel/evidence',device,enabled,async(req,res)=>{
|
||
|
|
const item=pending.get(req.body?.id);if(!item||item.bundle.deviceId!==req.device.id)return res.status(403).json({error:'Protected evidence channel refused'});
|
||
|
|
pending.delete(item.bundle.id);let context;
|
||
|
|
try{
|
||
|
|
context=await traceOperation('evidence.decrypt',{},()=>profile.receive(req.body,item.bundle,item.privateKey));
|
||
|
|
let status=200,body;const inner={status(value){status=value;return this;},json(value){body=value;return this;}};
|
||
|
|
try{await traceOperation('evidence.store',{},()=>recordEvidence({device:req.device,body:context.payload},inner));}
|
||
|
|
catch(error){status=error?.name==='ZodError'?400:500;body={error:status===400?'Protected evidence request invalid':'Protected evidence storage failed'};}
|
||
|
|
const response=await traceOperation('evidence.encrypt',{},()=>profile.response({status,body},item.bundle,context));res.json(response);
|
||
|
|
}catch{res.status(403).json({error:'Protected evidence channel refused'});}
|
||
|
|
finally{context?.responseKey.fill(0);if(context)context.payload=null;}
|
||
|
|
});
|
||
|
|
}
|