feat: define signed persona authorization trust contract
This commit is contained in:
parent
a118300791
commit
c4504fa115
13 changed files with 470 additions and 14 deletions
|
|
@ -0,0 +1,121 @@
|
|||
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/);
|
||||
});
|
||||
Loading…
Reference in a new issue