121 lines
5.6 KiB
TypeScript
121 lines
5.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { generateKeyPairSync, sign } from 'node:crypto';
|
|
import test from 'node:test';
|
|
import {
|
|
PERSONA_CONTROL_AUTHORIZATION_SCHEMA,
|
|
personaControlAuthorizationSigningBytes,
|
|
parsePersonaControlAuthorizationSignerRegistry,
|
|
verifyPersonaControlAuthorization,
|
|
} from './persona-control-authorization.js';
|
|
|
|
const COMMIT = 'd'.repeat(40);
|
|
const source = {
|
|
repositoryId: 'REPO-012',
|
|
sourceCommit: COMMIT,
|
|
sourcePath: 'routing/persona-control-authorization-signers.json',
|
|
sourceUrl: `https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/commit/${COMMIT}/routing/persona-control-authorization-signers.json`,
|
|
} as const;
|
|
const keys = generateKeyPairSync('ed25519');
|
|
const publicKeyPem = keys.publicKey.export({ format: 'pem', type: 'spki' }).toString();
|
|
|
|
function registry(status: 'ACTIVE' | 'REVOKED' = 'ACTIVE') {
|
|
return {
|
|
registryId: 'GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001',
|
|
schema: 'gh-aios.persona-control-authorization-signers/v1',
|
|
signers: [{
|
|
algorithm: 'Ed25519',
|
|
humanResponsibilitySubjects: ['BINGSHUO'],
|
|
personaIds: ['ICE-P-ZY001'],
|
|
publicKeyPem,
|
|
scopes: ['PERSONA_PRIMARY_LANGUAGE_PLANNING'],
|
|
signerId: 'GH-AIOS-AUTHORIZER-001',
|
|
status,
|
|
}],
|
|
state: 'CURRENT',
|
|
};
|
|
}
|
|
|
|
const unsigned = {
|
|
schema: PERSONA_CONTROL_AUTHORIZATION_SCHEMA,
|
|
outcome: 'VERIFIED' as const,
|
|
authorizationId: 'AUTH-001',
|
|
verifier: 'GUANGHU_OS' as const,
|
|
scope: 'PERSONA_PRIMARY_LANGUAGE_PLANNING' as const,
|
|
personaId: 'ICE-P-ZY001',
|
|
humanResponsibilitySubject: 'BINGSHUO',
|
|
repositoryHead: 'a'.repeat(40),
|
|
modelInstanceId: 'MODEL-INSTANCE-001',
|
|
requestId: 'REQ-001',
|
|
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-12.12',
|
|
issuedAt: '2026-08-12T05:00:00+08:00',
|
|
validUntil: '2026-08-12T05:10:00+08:00',
|
|
evidenceDigest: 'b'.repeat(64),
|
|
signerId: 'GH-AIOS-AUTHORIZER-001',
|
|
signatureAlgorithm: 'Ed25519' as const,
|
|
};
|
|
|
|
function receipt(overrides: Record<string, unknown> = {}) {
|
|
const candidate = { ...unsigned, ...overrides };
|
|
return {
|
|
...candidate,
|
|
signature: sign(null, personaControlAuthorizationSigningBytes(candidate), keys.privateKey).toString('base64url'),
|
|
};
|
|
}
|
|
|
|
const expected = {
|
|
personaId: unsigned.personaId,
|
|
humanResponsibilitySubject: unsigned.humanResponsibilitySubject,
|
|
repositoryHead: unsigned.repositoryHead,
|
|
modelInstanceId: unsigned.modelInstanceId,
|
|
requestId: unsigned.requestId,
|
|
sourceLanguageAnchor: unsigned.sourceLanguageAnchor,
|
|
observedAt: Date.parse('2026-08-12T05:05:00+08:00'),
|
|
};
|
|
|
|
test('an empty published registry trusts no signer', () => {
|
|
const parsed = parsePersonaControlAuthorizationSignerRegistry({ ...registry(), signers: [] }, source);
|
|
assert.equal(verifyPersonaControlAuthorization(receipt(), parsed, expected), null);
|
|
});
|
|
|
|
test('an exact current Ed25519 receipt verifies against its scoped registered signer', () => {
|
|
const parsed = parsePersonaControlAuthorizationSignerRegistry(registry(), source);
|
|
const verified = verifyPersonaControlAuthorization(receipt(), parsed, expected);
|
|
assert.equal(verified?.signerId, unsigned.signerId);
|
|
assert.equal(verified?.sourceCommit, COMMIT);
|
|
});
|
|
|
|
test('tampering any signed request binding fails closed', () => {
|
|
const parsed = parsePersonaControlAuthorizationSignerRegistry(registry(), source);
|
|
const signed = receipt();
|
|
assert.equal(verifyPersonaControlAuthorization({ ...signed, requestId: 'REQ-OTHER' }, parsed, { ...expected, requestId: 'REQ-OTHER' }), null);
|
|
});
|
|
|
|
test('revoked, wrong-scope and wrong-subject signers never authorize', () => {
|
|
const revoked = parsePersonaControlAuthorizationSignerRegistry(registry('REVOKED'), source);
|
|
assert.equal(verifyPersonaControlAuthorization(receipt(), revoked, expected), null);
|
|
const wrongSubjectInput = registry();
|
|
wrongSubjectInput.signers[0].humanResponsibilitySubjects = ['OTHER'];
|
|
const wrongSubject = parsePersonaControlAuthorizationSignerRegistry(wrongSubjectInput, source);
|
|
assert.equal(verifyPersonaControlAuthorization(receipt(), wrongSubject, expected), null);
|
|
});
|
|
|
|
test('stale, future and mismatched request evidence fail closed', () => {
|
|
const parsed = parsePersonaControlAuthorizationSignerRegistry(registry(), source);
|
|
assert.equal(verifyPersonaControlAuthorization(receipt(), parsed, { ...expected, observedAt: Date.parse('2026-08-12T05:10:01+08:00') }), null);
|
|
assert.equal(verifyPersonaControlAuthorization(receipt(), parsed, { ...expected, observedAt: Date.parse('2026-08-12T04:59:59+08:00') }), null);
|
|
assert.equal(verifyPersonaControlAuthorization(receipt(), parsed, { ...expected, repositoryHead: 'c'.repeat(40) }), null);
|
|
});
|
|
|
|
test('registry provenance is pinned to the exact REPO-012 commit path', () => {
|
|
assert.throws(() => parsePersonaControlAuthorizationSignerRegistry(registry(), { ...source, sourceCommit: 'short' }), /source_invalid/);
|
|
assert.throws(() => parsePersonaControlAuthorizationSignerRegistry(registry(), { ...source, sourceUrl: 'https://example.com/registry.json' }), /source_invalid/);
|
|
});
|
|
|
|
test('duplicate signers, private keys and unknown fields fail closed', () => {
|
|
assert.throws(() => parsePersonaControlAuthorizationSignerRegistry({ ...registry(), signers: [...registry().signers, ...registry().signers] }, source), /registry_invalid/);
|
|
const privateKeyPem = keys.privateKey.export({ format: 'pem', type: 'pkcs8' }).toString();
|
|
const privateRegistry = registry();
|
|
privateRegistry.signers[0].publicKeyPem = privateKeyPem;
|
|
assert.throws(() => parsePersonaControlAuthorizationSignerRegistry(privateRegistry, source), /registry_invalid/);
|
|
assert.throws(() => parsePersonaControlAuthorizationSignerRegistry({ ...registry(), extra: true }, source), /registry_invalid/);
|
|
});
|