Add packaged DLP broker and protected Windows service foundation

This commit is contained in:
Karl committed 2026-10-14 14:19:22 +02:00
1 parent 95d23f4e0b
commit 7b5057901a
140 files changed
+11215 -60

No files matched your search

+12 -6
View File
@@ -1,21 +1,27 @@
'use strict';
const {z}=require('zod');
const {FileFactsSchema}=require('./transfer-facts.cjs');
const {RequestFactsSchema}=require('./request-transfer-facts.cjs');
const BROKER_REASONS=Object.freeze(['broker-approved','broker-policy-block','broker-inspection-fault','broker-authority-expired','broker-audit-failed','broker-connect-denied','broker-auth-denied','broker-framing-block','broker-size-block','broker-request-aborted','broker-request-timeout','broker-upstream-failed','broker-response-invalid']);
const BrokerFactsSchema=z.object({requestId:z.string().uuid(),connectionId:z.string().uuid().optional(),phase:z.enum(['connect.block','request.authorized','request.block','request.fault','forward.fault']),reason:z.enum(BROKER_REASONS),authorizedEventId:z.string().uuid().optional()}).strict();
const BROKER_REASONS=Object.freeze(['broker-approved','broker-policy-block','broker-inspection-fault','broker-authority-expired','broker-audit-failed','broker-connect-denied','broker-auth-denied','broker-framing-block','broker-size-block','broker-request-aborted','broker-request-timeout','broker-upstream-failed','broker-response-invalid','broker-scan-timeout','broker-download-approved','broker-download-policy-block','broker-download-inspection-block']);
const BrokerFactsSchema=z.object({requestId:z.string().uuid(),connectionId:z.string().uuid().optional(),phase:z.enum(['connect.block','request.authorized','request.block','request.fault','forward.fault','response.scanned','response.block']),reason:z.enum(BROKER_REASONS),authorizedEventId:z.string().uuid().optional()}).strict();
const phaseReasons=Object.freeze({
'connect.block':['broker-connect-denied','broker-auth-denied','broker-framing-block','broker-authority-expired','broker-audit-failed','broker-request-timeout','broker-size-block','broker-request-aborted'],
'request.authorized':['broker-approved'],
'request.block':['broker-policy-block','broker-authority-expired','broker-framing-block','broker-size-block','broker-request-aborted','broker-request-timeout'],
'request.block':['broker-scan-timeout','broker-policy-block','broker-authority-expired','broker-framing-block','broker-size-block','broker-request-aborted','broker-request-timeout'],
'request.fault':['broker-inspection-fault','broker-audit-failed'],
'forward.fault':['broker-upstream-failed','broker-response-invalid','broker-request-aborted','broker-request-timeout','broker-authority-expired','broker-audit-failed']
'response.scanned':['broker-download-approved'],
'response.block':['broker-download-policy-block','broker-download-inspection-block'],
'forward.fault':['broker-scan-timeout','broker-upstream-failed','broker-response-invalid','broker-request-aborted','broker-request-timeout','broker-authority-expired','broker-audit-failed']
});
function validateBrokerSemantics(event,context){
if(!event.broker)return;
const value=event.broker;
if(!event.request||event.file||event.kind!==(value.phase==='request.authorized'?'upload.authorized':'upload.block')||!phaseReasons[value.phase].includes(value.reason)||value.phase==='forward.fault'&&!value.authorizedEventId||value.phase!=='forward.fault'&&value.authorizedEventId||value.authorizedEventId===event.eventId){
const download=event.kind==='download.scan'||event.kind==='download.block',linked=value.phase==='forward.fault'||value.phase.startsWith('response.');
const expectedKind=value.phase==='request.authorized'?'upload.authorized':value.phase==='response.scanned'?'download.scan':value.phase==='response.block'?'download.block':'upload.block';
if(download?(!event.file||event.request||!value.phase.startsWith('response.')):(!event.request||event.file||value.phase.startsWith('response.')))context.addIssue({code:'custom',path:['broker'],message:'Broker transfer facts rejected'});
if(event.kind!==expectedKind||!phaseReasons[value.phase].includes(value.reason)||linked&&!value.authorizedEventId||!linked&&value.authorizedEventId||value.authorizedEventId===event.eventId){
context.addIssue({code:'custom',path:['broker'],message:'Broker lifecycle semantics rejected'});
}
}
const BrokerAuditEventSchema=z.object({eventId:z.string().uuid(),kind:z.enum(['upload.authorized','upload.block']),ruleId:z.string().min(1).max(100),policyVersion:z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),host:z.string().max(253).optional(),request:RequestFactsSchema,broker:BrokerFactsSchema.optional(),traceId:z.string().regex(/^[a-f0-9]{32}$/).optional(),spanId:z.string().regex(/^[a-f0-9]{16}$/).optional()}).strict().superRefine(validateBrokerSemantics);
const BrokerAuditEventSchema=z.object({eventId:z.string().uuid(),kind:z.enum(['upload.authorized','upload.block','download.scan','download.block']),ruleId:z.string().min(1).max(100),policyVersion:z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),host:z.string().max(253).optional(),request:RequestFactsSchema.optional(),file:FileFactsSchema.optional(),broker:BrokerFactsSchema.optional(),traceId:z.string().regex(/^[a-f0-9]{32}$/).optional(),spanId:z.string().regex(/^[a-f0-9]{16}$/).optional()}).strict().superRefine((event,context)=>{validateBrokerSemantics(event,context);const download=event.kind.startsWith('download.');if(download?(!event.file||event.request):(!event.request||event.file))context.addIssue({code:'custom',message:'Exclusive transfer facts required'});});
module.exports={BrokerAuditEventSchema,BrokerFactsSchema,BROKER_REASONS,validateBrokerSemantics};
+75
View File
@@ -0,0 +1,75 @@
'use strict';
const {createHash}=require('node:crypto');
const {detectType}=require('./file-transfer.cjs');
// Typed binary carriers supplement text normalization. No whole decoded file
// may be released on the strength of an allow verdict for its outer JSON text.
function extractEncodedDocuments(bytes,{maxDecodedBytes=16777216,maxParts=16,maxNodes=20000,maxDepth=32,jsonRequired=false}={}){
if(!Buffer.isBuffer(bytes)||bytes.length>16777216||![maxDecodedBytes,maxParts,maxNodes,maxDepth].every(Number.isSafeInteger)||maxDecodedBytes<1||maxDecodedBytes>16777216||maxParts<1||maxParts>64||maxNodes<1||maxNodes>20000||maxDepth<1||maxDepth>32)throw Error('encoded-document-configuration-invalid');
if(typeof jsonRequired!=='boolean')throw Error('encoded-document-configuration-invalid');
const parts=[],seen=new Set(),encodings=new Set();let total=0,nodes=0,complete=true;
const result=()=>Object.freeze({parts:Object.freeze(parts),complete,decodedBytes:total,visitedNodes:nodes,dispose});
const dispose=()=>{for(const part of parts)part.bytes.fill(0);};
function candidate(value){
if(!complete||typeof value!=='string')return;
let encoded=value.trim(),explicit=false,mime;
if(/^data:/i.test(encoded)){
const match=/^data:([a-z0-9.+/-]+)?(?:;[a-z0-9-]+=[a-z0-9._-]+)*;base64,([\s\S]*)$/i.exec(encoded);
if(!match){complete=false;return;}mime=(match[1]||'application/octet-stream').toLowerCase();encoded=match[2];explicit=true;
if(encoded.includes('%')){try{encoded=decodeURIComponent(encoded);}catch{complete=false;return;}}
}
if(!explicit&&encoded.length<16)return;
if(!/^[A-Za-z0-9+/_-]*={0,2}$/.test(encoded)||encoded.length%4===1){if(explicit)complete=false;return;}
const normalized=encoded.replace(/-/g,'+').replace(/_/g,'/').replace(/=+$/,'');
const encodingKey=createHash('sha256').update(normalized).update('\0'+(mime||'')).digest('hex');
if(encodings.has(encodingKey))return;
const projected=Math.floor(normalized.length*3/4);
if(total+projected>maxDecodedBytes){complete=false;return;}
const decoded=Buffer.from(normalized,'base64');
total+=decoded.length;encodings.add(encodingKey);
const type=detectType(decoded);
if(decoded.toString('base64').replace(/=+$/,'')!==normalized){decoded.fill(0);if(explicit||type!=='unknown')complete=false;return;}
if(!explicit&&type==='unknown'){decoded.fill(0);return;}
const digest=createHash('sha256').update(decoded).digest('hex');
const identity=digest+'\0'+(mime||'');
if(seen.has(identity)){decoded.fill(0);return;}
if(parts.length>=maxParts){decoded.fill(0);complete=false;return;}
seen.add(identity);parts.push(Object.freeze({bytes:decoded,...(mime?{mime}:{})}));
}
function scanText(value,percentDepth=0){
candidate(value);
const carriers=[];
for(const match of value.matchAll(/data:[^\s,"'<>]+,[A-Za-z0-9+/_=%-]+/gi)){if(++nodes>maxNodes){complete=false;break;}carriers.push([match.index,match.index+match[0].length]);candidate(match[0]);}
let carrier=0;
for(const match of value.matchAll(/[A-Za-z0-9+/_-]{16,}={0,2}/g)){
while(carrier<carriers.length&&carriers[carrier][1]<=match.index)carrier++;
if(carrier<carriers.length&&match.index>=carriers[carrier][0]&&match.index<carriers[carrier][1])continue;
if(++nodes>maxNodes){complete=false;break;}candidate(match[0]);
}
if(complete&&value.includes('%')){
if(percentDepth>=4){complete=false;return;}
try{const decoded=decodeURIComponent(value);if(decoded!==value)scanText(decoded,percentDepth+1);}catch{if(/data%3a/i.test(value))complete=false;}
}
}
try{
const text=new TextDecoder('utf-8',{fatal:true}).decode(bytes),trimmed=text.trim();
if(jsonRequired||trimmed.startsWith('{')||trimmed.startsWith('[')){
let root;try{root=JSON.parse(trimmed);}catch{if(jsonRequired)complete=false;else scanText(trimmed);return result();}
const stack=[{value:root,depth:0}];
while(stack.length&&complete){const {value,depth}=stack.pop();if(++nodes>maxNodes||depth>maxDepth){complete=false;break;}
if(typeof value==='string')scanText(value);
else if(Array.isArray(value)){
if(value.length>maxNodes-nodes||stack.length+value.length>maxNodes){complete=false;break;}
for(const child of value)stack.push({value:child,depth:depth+1});
}else if(value&&typeof value==='object'){
// Avoid Object.entries allocating an unbounded array of key/value pairs
// before the traversal budget is checked on a hostile JSON collection.
for(const key in value){if(!Object.hasOwn(value,key))continue;scanText(key);stack.push({value:value[key],depth:depth+1});if(stack.length>maxNodes||!complete){complete=false;break;}}
}
}
}else{
scanText(trimmed);
}
}catch{complete=false;}
return result();
}
module.exports={extractEncodedDocuments};
+2 -2
View File
@@ -20,7 +20,7 @@ export function toECS(event,installationId='safetynet') {
'@timestamp':event.time,ecs:{version:'8.17.0'},
event:{id:`${installationId}:${event.id}`,kind:alert?'alert':'event',module:'safetynet',dataset:'safetynet.security',
action:event.kind,category:[alert?'intrusion_detection':auth?'authentication':config?'configuration':clipboard?'process':'web'],
type:[transportEvent||alert||interaction?.outcome==='observed'?'info':failure||brokerFailure?'error':auth?'start':config?'change':block?'denied':clipboard?'access':'allowed'],outcome:transportEvent||interaction?.outcome==='observed'||broker?.phase==='request.authorized'?'unknown':failure||brokerFailure?'failure':'success',...(alert?{severity:severity(m.level)}:{})},
type:[transportEvent||alert||interaction?.outcome==='observed'?'info':failure||brokerFailure?'error':auth?'start':config?'change':block?'denied':clipboard?'access':'allowed'],outcome:transportEvent||interaction?.outcome==='observed'||broker?.phase==='request.authorized'?'unknown':failure||brokerFailure||broker?.phase==='response.block'?'failure':'success',...(alert?{severity:severity(m.level)}:{})},
observer:{vendor:'SafetyNet',product:'Managed Enterprise Browser',type:'dlp',version:'0.1.0'},
...(event.device_id?{host:{id:event.device_id}}:{}),
...(m.actor?{user:{id:m.actor}}:{}),...(m.host&&(!transportEvent||transport)?{destination:{domain:m.host}}:{}),
@@ -53,7 +53,7 @@ export function toCEF(event,installationId='safetynet') {
...(event.kind.startsWith('download.')&&facts(m)?{cn2Label:'FileBytes',cn2:facts(m).bytes,cs6Label:'DetectedFileType',cs6:facts(m).detectedType,fileHash:facts(m).sha256}:{}),
...(/^[a-f0-9]{32}$/.test(m.traceId||'')?{cs4Label:'TraceId',cs4:m.traceId}:{}),
...(/^[a-f0-9]{16}$/.test(m.spanId||'')?{cs5Label:'SpanId',cs5:m.spanId}:{}),
...(broker?{cs2Label:'BrokerLifecycle',cs2:JSON.stringify(broker),cs3Label:'BrokerEventId',cs3:broker.eventId,requestContext:broker.requestId,reason:broker.reason,outcome:broker.phase==='request.authorized'?'unknown':['request.fault','forward.fault'].includes(broker.phase)?'failure':'success'}:{}),
...(broker?{cs2Label:'BrokerLifecycle',cs2:JSON.stringify(broker),cs3Label:'BrokerEventId',cs3:broker.eventId,requestContext:broker.requestId,reason:broker.reason,outcome:broker.phase==='request.authorized'?'unknown':['request.fault','forward.fault','response.block'].includes(broker.phase)?'failure':'success'}:{}),
...(alert?{cs2Label:'SourceEventId',cs2:`${installationId}:${m.sourceEventId}`,cs3Label:'AlertLevel',cs3:m.level}:{})};
return `CEF:0|SafetyNet|Enterprise Browser|0.1.0|${header(event.kind)}|${header(event.kind)}|${alert?severity(m.level):!transportEvent&&blocked?7:3}|`+Object.entries(fields).map(([k,v])=>`${k}=${ext(v)}`).join(' ');
}
+71
View File
@@ -0,0 +1,71 @@
'use strict';
// Signed external manifests avoid embedding the final browser hash in its own
// native helper. This classical P-256 contract is not a post-quantum signature.
const {createPublicKey,createPrivateKey,createHash,sign,verify}=require('node:crypto');
const {isIP}=require('node:net');
const MAX_RELEASE_BYTES=2097152,MAX_DEPLOYMENT_BYTES=262144;
const DOMAINS=Object.freeze({release:'SafetyNet Windows release v1\0',deployment:'SafetyNet Windows deployment v1\0'});
const verifiedReleases=new WeakSet(),verifiedDeployments=new WeakSet();
const MANAGEMENT_PROTOCOL=require('./policy-channel.cjs').VERSION;
const UUID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
function fail(code){throw Object.assign(new Error(code),{code});}
function exact(value,keys){return value&&typeof value==='object'&&!Array.isArray(value)&&Object.getPrototypeOf(value)===Object.prototype&&Object.keys(value).sort().join(',')===keys.slice().sort().join(',');}
function positive(value){return Number.isSafeInteger(value)&&value>0;}
function relative(value){return typeof value==='string'&&value.length<=240&&value.split('/').every(part=>/^[A-Za-z0-9_.-]+$/.test(part)&&part!=='.'&&part!=='..'&&!/[. ]$/.test(part)&&!/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part));}
function freeze(value){if(value&&typeof value==='object'){for(const item of Object.values(value))freeze(item);Object.freeze(value);}return value;}
function canonical(value){if(Array.isArray(value))return '['+value.map(canonical).join(',')+']';if(value&&typeof value==='object')return '{'+Object.keys(value).sort().map(key=>JSON.stringify(key)+':'+canonical(value[key])).join(',')+'}';return JSON.stringify(value);}
function publicKey(pem,type){
if(typeof pem!=='string'||pem.length>(type==='ml-dsa-87'?20000:4096)||!pem.startsWith('-----BEGIN PUBLIC KEY-----\n'))fail('windows-trust-key-invalid');
const key=createPublicKey(pem);if(key.export({format:'pem',type:'spki'})!==pem||key.asymmetricKeyType!==type||type==='ec'&&key.asymmetricKeyDetails?.namedCurve!=='prime256v1')fail('windows-trust-key-invalid');return key;
}
function releaseSchema(value){
if(!exact(value,['format','releaseId','releaseSequence','serviceName','deploymentTenant','deploymentPublicKey','files'])||value.format!=='safetynet-windows-release-v1'||!UUID.test(value.releaseId)||!UUID.test(value.deploymentTenant)||!positive(value.releaseSequence)||value.serviceName!=='SafetyNetNetworkBroker'||!Array.isArray(value.files)||!value.files.length||value.files.length>10000)fail('windows-release-invalid');
publicKey(value.deploymentPublicKey,'ec');let previous='';const seen=new Set();
for(const file of value.files){if(!exact(file,['path','bytes','sha256'])||!relative(file.path)||!Number.isSafeInteger(file.bytes)||file.bytes<0||!/^[a-f0-9]{64}$/.test(file.sha256)||file.path<=previous||seen.has(file.path.toLowerCase()))fail('windows-release-invalid');previous=file.path;seen.add(file.path.toLowerCase());}
for(const required of ['SafetyNet.exe','safetynet-service-host.exe','resources/app.asar','resources/broker/node.exe','resources/python/python.exe','resources/inspector/server.py'])if(!value.files.some(file=>file.path===required))fail('windows-release-invalid');
if(!value.files.some(file=>file.path.startsWith('resources/semantic/'))||value.files.some(file=>['release-envelope.json','deployment-envelope.json'].includes(file.path)))fail('windows-release-invalid');
return value;
}
function managementSchema(value){
if(!exact(value,['origin','signatureKey','protocol'])||typeof value.origin!=='string'||value.origin.length>2048||[...value.origin].some(character=>character.charCodeAt(0)<=32||character.charCodeAt(0)>=127)||value.protocol!==MANAGEMENT_PROTOCOL)fail('windows-management-invalid');
const url=new URL(value.origin);
if(url.protocol!=='https:'||url.origin!==value.origin||url.username||url.password||url.search||url.hash||url.pathname!=='/'||url.hostname.endsWith('.'))fail('windows-management-invalid');
const hostname=url.hostname.startsWith('[')?url.hostname.slice(1,-1):url.hostname;
if(!isIP(hostname)&&(hostname.length>253||hostname.split('.').some(label=>!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label)||/^0x[0-9a-f]+$/i.test(label))))fail('windows-management-invalid');
publicKey(value.signatureKey,'ml-dsa-87');return value;
}
function deploymentSchema(value){
const v2=value?.format==='safetynet-windows-deployment-v2';
if(!exact(value,['format','releaseId','deploymentTenant','deploymentSequence','policyPublicKey','expiresAt','users',...(v2?['management']:[])])||!['safetynet-windows-deployment-v1','safetynet-windows-deployment-v2'].includes(value.format)||!UUID.test(value.releaseId)||!UUID.test(value.deploymentTenant)||!positive(value.deploymentSequence)||!positive(value.expiresAt)||!Array.isArray(value.users)||!value.users.length||value.users.length>1024)fail('windows-deployment-invalid');
if(v2)managementSchema(value.management);
publicKey(value.policyPublicKey,'ed25519');let previous='';const devices=new Set();
for(const user of value.users){if(!exact(user,['sid','deviceId','minimumPolicyVersion'])||typeof user.sid!=='string'||!/^(?:S-1-5-21|S-1-12-1)-(?:(?:0|[1-9][0-9]{0,9})-){3}(?:0|[1-9][0-9]{0,9})$/.test(user.sid)||user.sid.split('-').slice(4).some(part=>Number(part)>4294967295)||user.sid<=previous||typeof user.deviceId!=='string'||!/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(user.deviceId)||devices.has(user.deviceId)||!positive(user.minimumPolicyVersion))fail('windows-deployment-invalid');previous=user.sid;devices.add(user.deviceId);}
return value;
}
function decodeEnvelope(envelope,kind,key){
const cap=kind==='release'?MAX_RELEASE_BYTES:MAX_DEPLOYMENT_BYTES;
if(!exact(envelope,['payload','signature'])||typeof envelope.payload!=='string'||envelope.payload.length>Math.ceil(cap*4/3)||! /^[A-Za-z0-9_-]+$/.test(envelope.payload)||typeof envelope.signature!=='string'||! /^[A-Za-z0-9_-]{86}$/.test(envelope.signature))fail('windows-trust-envelope-invalid');
const bytes=Buffer.from(envelope.payload,'base64url'),signature=Buffer.from(envelope.signature,'base64url');
if(bytes.length>cap||bytes.toString('base64url')!==envelope.payload||signature.length!==64||signature.toString('base64url')!==envelope.signature||!verify('sha256',Buffer.concat([Buffer.from(DOMAINS[kind]),bytes]),{key,dsaEncoding:'ieee-p1363'},signature))fail('windows-trust-signature-invalid');
const text=new TextDecoder('utf-8',{fatal:true}).decode(bytes),value=JSON.parse(text);if(canonical(value)!==text)fail('windows-trust-canonical-invalid');
return {value,digest:createHash('sha256').update(bytes).digest('hex')};
}
function checkFloor(sequence,digest,{minimumSequence=0,acceptedDigest}={}){
if(!Number.isSafeInteger(minimumSequence)||minimumSequence<0||acceptedDigest!==undefined&&!/^[a-f0-9]{64}$/.test(acceptedDigest))fail('windows-trust-floor-invalid');
if(sequence<minimumSequence||sequence===minimumSequence&&acceptedDigest!==undefined&&digest!==acceptedDigest)fail('windows-trust-rollback');
}
function verifyReleaseEnvelope(envelope,{publicKey:anchor,floor}={}){
try{const decoded=decodeEnvelope(envelope,'release',publicKey(anchor,'ec'));releaseSchema(decoded.value);checkFloor(decoded.value.releaseSequence,decoded.digest,floor);const result=freeze({manifest:decoded.value,digest:decoded.digest});verifiedReleases.add(result);return result;}catch(error){if(error.code?.startsWith('windows-'))throw error;fail('windows-release-invalid');}
}
function verifyDeploymentEnvelope(envelope,{release,floor,now=Date.now(),authenticatedSid}={}){
try{if(!verifiedReleases.has(release)||!Number.isSafeInteger(now)||now<0)fail('windows-deployment-invalid');const decoded=decodeEnvelope(envelope,'deployment',publicKey(release.manifest.deploymentPublicKey,'ec'));deploymentSchema(decoded.value);const value=decoded.value;if(value.releaseId!==release.manifest.releaseId||value.deploymentTenant!==release.manifest.deploymentTenant||value.expiresAt<=now)fail('windows-deployment-binding-invalid');checkFloor(value.deploymentSequence,decoded.digest,floor);const user=value.users.find(item=>item.sid===authenticatedSid);if(!user)fail('windows-deployment-user-denied');const result=freeze({manifest:value,digest:decoded.digest,bootstrap:{publicKey:value.policyPublicKey,deviceId:user.deviceId,minimumVersion:user.minimumPolicyVersion}});verifiedDeployments.add(result);return result;}catch(error){if(error.code?.startsWith('windows-'))throw error;fail('windows-deployment-invalid');}
}
function verifiedManagementBinding(deployment){
if(!verifiedDeployments.has(deployment)||deployment.manifest.format!=='safetynet-windows-deployment-v2'||deployment.manifest.expiresAt<=Date.now())fail('windows-management-denied');
return deployment.manifest.management;
}
function signManifest(value,privatePem,kind){
try{if(!Object.hasOwn(DOMAINS,kind))fail('windows-trust-kind-invalid');(kind==='release'?releaseSchema:deploymentSchema)(value);const payload=Buffer.from(canonical(value)),key=createPrivateKey(privatePem);if(key.asymmetricKeyType!=='ec'||key.asymmetricKeyDetails?.namedCurve!=='prime256v1')fail('windows-trust-key-invalid');if(payload.length>(kind==='release'?MAX_RELEASE_BYTES:MAX_DEPLOYMENT_BYTES))fail('windows-trust-envelope-invalid');return Object.freeze({payload:payload.toString('base64url'),signature:sign('sha256',Buffer.concat([Buffer.from(DOMAINS[kind]),payload]),{key,dsaEncoding:'ieee-p1363'}).toString('base64url')});}catch(error){if(error.code?.startsWith('windows-'))throw error;fail('windows-trust-signing-invalid');}
}
function publicKeyCoordinates(pem){try{const key=publicKey(pem,'ec'),jwk=key.export({format:'jwk'});return Buffer.concat([Buffer.from(jwk.x,'base64url'),Buffer.from(jwk.y,'base64url')]);}catch{fail('windows-trust-key-invalid');}}
module.exports={verifyReleaseEnvelope,verifyDeploymentEnvelope,signManifest,publicKeyCoordinates,verifiedManagementBinding,DOMAINS};