46 lines
12 KiB
JavaScript
46 lines
12 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {createRequire} from 'node:module';
|
|
import {generateKeyPairSync,sign} from 'node:crypto';
|
|
const require=createRequire(import.meta.url);
|
|
const {verifyReleaseEnvelope,verifyDeploymentEnvelope,signManifest,publicKeyCoordinates,verifiedManagementBinding,DOMAINS}=require('../packages/core/windows-trust.cjs');
|
|
function key(type='ec'){const value=generateKeyPairSync(type,type==='ec'?{namedCurve:'prime256v1'}:{});return {publicKey:value.publicKey.export({type:'spki',format:'pem'}),privateKey:value.privateKey.export({type:'pkcs8',format:'pem'})};}
|
|
function fixture(){
|
|
const vendor=key(),customer=key(),policy=key('ed25519');
|
|
const release={format:'safetynet-windows-release-v1',releaseId:'12345678-1234-1234-1234-123456789abc',releaseSequence:2,serviceName:'SafetyNetNetworkBroker',deploymentTenant:'22345678-1234-1234-1234-123456789abc',deploymentPublicKey:customer.publicKey,files:['SafetyNet.exe','resources/app.asar','resources/broker/node.exe','resources/inspector/server.py','resources/python/python.exe','resources/semantic/model.bin','safetynet-service-host.exe'].map(path=>({path,bytes:4,sha256:'a'.repeat(64)}))};
|
|
const deployment={format:'safetynet-windows-deployment-v1',releaseId:release.releaseId,deploymentTenant:release.deploymentTenant,deploymentSequence:3,policyPublicKey:policy.publicKey,expiresAt:Date.now()+60000,users:[{sid:'S-1-5-21-1-2-3-1001',deviceId:'synthetic-workstation',minimumPolicyVersion:7}]};
|
|
const verified=verifyReleaseEnvelope(signManifest(release,vendor.privateKey,'release'),{publicKey:vendor.publicKey});
|
|
return {vendor,customer,policy,release,deployment,verified};
|
|
}
|
|
function deploy(f,value=f.deployment,options={}){return verifyDeploymentEnvelope(signManifest(value,f.customer.privateKey,'deployment'),{release:f.verified,authenticatedSid:f.deployment.users[0].sid,...options});}
|
|
test('vendor release delegates independent customer signer and actual SID produces exact pinned bootstrap',()=>{const f=fixture(),verified=deploy(f);assert.deepEqual(verified.bootstrap,{publicKey:f.policy.publicKey,deviceId:'synthetic-workstation',minimumVersion:7});assert.ok(Object.isFrozen(verified)&&Object.isFrozen(verified.bootstrap)&&Object.isFrozen(f.verified.manifest.files));assert.equal(publicKeyCoordinates(f.vendor.publicKey).length,64);});
|
|
test('vendor key cannot sign delegated customer deployment and fake verified releases are rejected',()=>{const f=fixture();assert.throws(()=>verifyDeploymentEnvelope(signManifest(f.deployment,f.vendor.privateKey,'deployment'),{release:f.verified,authenticatedSid:f.deployment.users[0].sid}),{code:'windows-trust-signature-invalid'});assert.throws(()=>verifyDeploymentEnvelope(signManifest(f.deployment,f.customer.privateKey,'deployment'),{release:structuredClone(f.verified),authenticatedSid:f.deployment.users[0].sid}),{code:'windows-deployment-invalid'});});
|
|
test('release signature tamper, unpinned signer and invalid public key types fail with fixed errors',()=>{const f=fixture(),envelope=signManifest(f.release,f.vendor.privateKey,'release');const tampered={...envelope,payload:Buffer.from(JSON.stringify({...f.release,releaseSequence:99})).toString('base64url')};assert.throws(()=>verifyReleaseEnvelope(tampered,{publicKey:f.vendor.publicKey}),{code:'windows-trust-signature-invalid'});assert.throws(()=>verifyReleaseEnvelope(envelope,{publicKey:f.customer.publicKey}),{code:'windows-trust-signature-invalid'});for(const pem of [f.vendor.privateKey,f.policy.publicKey,'secret invalid key'])assert.throws(()=>verifyReleaseEnvelope(envelope,{publicKey:pem}),error=>error.message===error.code&&!error.message.includes('secret'));});
|
|
test('strict canonical signed bytes reject duplicate fields, envelope extras and cross-domain signatures',()=>{
|
|
const f=fixture(),text=JSON.stringify({...f.release,extra:'secret'}),payload=Buffer.from(text).toString('base64url');
|
|
const signed=text=>sign('sha256',Buffer.concat([Buffer.from(DOMAINS.release),Buffer.from(text)]),{key:f.vendor.privateKey,dsaEncoding:'ieee-p1363'}).toString('base64url');
|
|
assert.throws(()=>verifyReleaseEnvelope({payload,signature:signed(text)},{publicKey:f.vendor.publicKey}),{code:'windows-trust-canonical-invalid'});
|
|
const good=signManifest(f.release,f.vendor.privateKey,'release'),canonical=Buffer.from(good.payload,'base64url').toString(),duplicate=canonical.replace('"releaseSequence":2','"releaseSequence":2,"releaseSequence":2');assert.throws(()=>verifyReleaseEnvelope({payload:Buffer.from(duplicate).toString('base64url'),signature:signed(duplicate)},{publicKey:f.vendor.publicKey}),{code:'windows-trust-canonical-invalid'});
|
|
assert.throws(()=>verifyReleaseEnvelope({...good,key:f.vendor.publicKey},{publicKey:f.vendor.publicKey}),{code:'windows-trust-envelope-invalid'});
|
|
const deploymentSignature=sign('sha256',Buffer.concat([Buffer.from(DOMAINS.deployment),Buffer.from(good.payload,'base64url')]),{key:f.vendor.privateKey,dsaEncoding:'ieee-p1363'}).toString('base64url');assert.throws(()=>verifyReleaseEnvelope({...good,signature:deploymentSignature},{publicKey:f.vendor.publicKey}),{code:'windows-trust-signature-invalid'});
|
|
});
|
|
test('protected release/deployment floors reject rollback and same sequence equivocation',()=>{const f=fixture(),envelope=signManifest(f.release,f.vendor.privateKey,'release');assert.throws(()=>verifyReleaseEnvelope(envelope,{publicKey:f.vendor.publicKey,floor:{minimumSequence:3}}),{code:'windows-trust-rollback'});assert.throws(()=>verifyReleaseEnvelope(envelope,{publicKey:f.vendor.publicKey,floor:{minimumSequence:2,acceptedDigest:'0'.repeat(64)}}),{code:'windows-trust-rollback'});assert.ok(verifyReleaseEnvelope(envelope,{publicKey:f.vendor.publicKey,floor:{minimumSequence:2,acceptedDigest:f.verified.digest}}));const d=deploy(f);assert.throws(()=>deploy(f,f.deployment,{floor:{minimumSequence:4}}),{code:'windows-trust-rollback'});assert.throws(()=>deploy(f,{...f.deployment,expiresAt:f.deployment.expiresAt+1},{floor:{minimumSequence:3,acceptedDigest:d.digest}}),{code:'windows-trust-rollback'});});
|
|
test('release/tenant/expiry/SID binding rejects caller-selected authority or unknown peer',()=>{const f=fixture();for(const change of [{releaseId:'32345678-1234-1234-1234-123456789abc'},{deploymentTenant:'32345678-1234-1234-1234-123456789abc'},{expiresAt:1}])assert.throws(()=>deploy(f,{...f.deployment,...change}),{code:'windows-deployment-binding-invalid'});assert.throws(()=>deploy(f,f.deployment,{authenticatedSid:'S-1-5-21-1-2-3-1002'}),{code:'windows-deployment-user-denied'});assert.throws(()=>signManifest({...f.deployment,publicKey:f.policy.publicKey},f.customer.privateKey,'deployment'),{code:'windows-deployment-invalid'});});
|
|
test('strict inventory and user schemas reject traversal, case duplicates, unsafe bounds and ambiguous accounts',()=>{const f=fixture();for(const files of [[{path:'../SafetyNet.exe',bytes:1,sha256:'a'.repeat(64)}],[...f.release.files,{path:'safetynet.exe',bytes:4,sha256:'a'.repeat(64)}]])assert.throws(()=>signManifest({...f.release,files},f.vendor.privateKey,'release'),{code:'windows-release-invalid'});for(const sid of ['S-1-5-18','S-1-5-21-1-2-3-01001','S-1-5-21-1-2-3-4294967296'])assert.throws(()=>signManifest({...f.deployment,users:[{...f.deployment.users[0],sid}]},f.customer.privateKey,'deployment'),{code:'windows-deployment-invalid'});assert.throws(()=>deploy(f,f.deployment,{floor:{minimumSequence:-1}}),{code:'windows-trust-floor-invalid'});});
|
|
test('canonical Entra user SID binds actual peer while service aliases remain rejected',()=>{const f=fixture(),sid='S-1-12-1-123-456-789-1000',deployment={...f.deployment,users:[{...f.deployment.users[0],sid}]};assert.equal(deploy(f,deployment,{authenticatedSid:sid}).bootstrap.deviceId,'synthetic-workstation');for(const invalid of ['S-1-12-1-0123-456-789-1000','S-1-12-1-4294967296-456-789-1000','S-1-5-80-1-2-3-4-5','S-1-5-32-544'])assert.throws(()=>signManifest({...deployment,users:[{...deployment.users[0],sid:invalid}]},f.customer.privateKey,'deployment'),{code:'windows-deployment-invalid'});});
|
|
function v2(f){const pq=key('ml-dsa-87'),channel=require('../packages/core/policy-channel.cjs');return {pq,channel,deployment:{...f.deployment,format:'safetynet-windows-deployment-v2',management:{origin:'https://management.example',signatureKey:pq.publicKey,protocol:channel.VERSION}}};}
|
|
test('signed v2 returns only provenance-verified frozen management binding compatible with actual PQ channel',()=>{const f=fixture(),{pq,channel,deployment}=v2(f),verified=deploy(f,deployment),binding=verifiedManagementBinding(verified);assert.deepEqual(binding,deployment.management);assert.ok(Object.isFrozen(binding));const identity=channel.identity(f.policy.privateKey,pq.privateKey,pq.publicKey);assert.equal(channel.verifyIdentity(identity,verified.bootstrap.publicKey,binding.signatureKey),pq.publicKey);assert.throws(()=>verifiedManagementBinding(structuredClone(verified)),{code:'windows-management-denied'});assert.throws(()=>verifiedManagementBinding(deploy(f)),{code:'windows-management-denied'});});
|
|
test('v2 management rejects noncanonical origins, credential fields and channel algorithm downgrade',()=>{const f=fixture(),{pq,deployment}=v2(f),management=deployment.management;for(const origin of ['http://management.example','https://management.example/','https://management.example/path','https://user:[email protected]','https://management.example?query=1','https://management.example#fragment','https://MANAGEMENT.example','https://management.example:443','https://management.example.','https://\u7ba1\u7406.example'])assert.throws(()=>signManifest({...deployment,management:{...management,origin}},f.customer.privateKey,'deployment'));for(const changed of [{...management,token:'synthetic-disallowed'},{...management,protocol:'safetynet-policy-mlkem768-mldsa65-v1'},{...management,signatureKey:key('ml-dsa-65').publicKey},{...management,signatureKey:f.policy.publicKey},{...management,signatureKey:pq.privateKey},{...management,signatureKey:pq.publicKey.trim()}])assert.throws(()=>signManifest({...deployment,management:changed},f.customer.privateKey,'deployment'));});
|
|
test('management format/tampering cannot bypass delegated signatures or v1 historical capability boundary',()=>{const f=fixture(),{deployment}=v2(f),envelope=signManifest(deployment,f.customer.privateKey,'deployment'),tampered={...envelope,payload:Buffer.from(JSON.stringify({...deployment,management:{...deployment.management,origin:'https://attacker.example'}})).toString('base64url')};assert.throws(()=>verifyDeploymentEnvelope(tampered,{release:f.verified,authenticatedSid:f.deployment.users[0].sid}),{code:'windows-trust-signature-invalid'});for(const value of [{...deployment,format:'safetynet-windows-deployment-v3'},{...deployment,format:'safetynet-windows-deployment-v1'},{...deployment,extra:'unknown'},Object.fromEntries(Object.entries(deployment).filter(([name])=>name!=='management'))])assert.throws(()=>signManifest(value,f.customer.privateKey,'deployment'),{code:'windows-deployment-invalid'});assert.throws(()=>verifyDeploymentEnvelope(signManifest(deployment,f.vendor.privateKey,'deployment'),{release:f.verified,authenticatedSid:f.deployment.users[0].sid}),{code:'windows-trust-signature-invalid'});assert.equal(verifiedManagementBinding(deploy(f,deployment)).origin,'https://management.example');});
|
|
|
|
test('verified management projection cannot grant an expired deployment capability',t=>{
|
|
const f=fixture(),{deployment}=v2(f),verified=deploy(f,deployment);
|
|
t.mock.method(Date,'now',()=>deployment.expiresAt);
|
|
assert.throws(()=>verifiedManagementBinding(verified),{code:'windows-management-denied'});
|
|
});
|
|
test('management origin host subset matches routed DNS and canonical IPv4/IPv6',()=>{
|
|
const f=fixture(),{deployment}=v2(f);
|
|
for(const origin of ['https://localhost:8443','https://127.0.0.1','https://[::1]','https://[::ffff:c0a8:1]'])assert.equal(verifiedManagementBinding(deploy(f,{...deployment,management:{...deployment.management,origin}})).origin,origin);
|
|
for(const origin of ['https://bad_host.example','https://0xabc.example','https://[0:0:0:0:0:0:0:1]','https://[::ffff:192.168.0.1]','https://127.1','https://management.example:','https://[::1]:'])assert.throws(()=>signManifest({...deployment,management:{...deployment.management,origin}},f.customer.privateKey,'deployment'));
|
|
});
|