2026-10-13 16:58:15 +02:00
|
|
|
import {managementIdentity} from './channel-identity.mjs';
|
2026-10-13 10:33:31 +02:00
|
|
|
import channel from '../../packages/core/policy-channel.cjs';
|
|
|
|
|
|
|
|
|
|
export function installPolicyChannel(app,store,device){
|
2026-10-13 16:58:15 +02:00
|
|
|
const shared=managementIdentity(store),keys={privateKey:shared.pqPrivate,publicKey:shared.publicKey};
|
|
|
|
|
const ed=shared.edPrivate,identity=shared.identity,pending=new Map();
|
2026-10-13 10:33:31 +02:00
|
|
|
app.get('/api/device/channel/identity',device,(_req,res)=>res.json(identity));
|
|
|
|
|
app.post('/api/device/channel/challenge',device,(req,res)=>{
|
|
|
|
|
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 management channel unavailable'});
|
|
|
|
|
const item=channel.challenge(req.device.id,ed,keys.privateKey);pending.set(item.bundle.id,item);res.json(item.bundle);
|
|
|
|
|
});
|
|
|
|
|
app.post('/api/device/channel/policy',device,(req,res)=>{
|
|
|
|
|
const item=pending.get(req.body?.id);
|
|
|
|
|
if(!item||item.bundle.deviceId!==req.device.id)return res.status(403).json({error:'Protected management channel refused'});
|
|
|
|
|
// Consume before processing: every challenge is one use, including failed ciphertext.
|
|
|
|
|
pending.delete(item.bundle.id);let context;
|
|
|
|
|
try{context=channel.receive(req.body,item.bundle,item.privateKey);res.json(channel.response(store.envelope(req.device.id),item.bundle,context));}
|
|
|
|
|
catch{return res.status(403).json({error:'Protected management channel refused'});}
|
|
|
|
|
finally{context?.responseKey.fill(0);}
|
|
|
|
|
});
|
|
|
|
|
return identity;
|
|
|
|
|
}
|