mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { createHash, generateKeyPairSync, sign } = require('node:crypto');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
PluginPackagePromptOutputRecoveryAuthorizationUntrustedError,
|
||||
createPluginPackagePromptOutputExternalRecoveryAuthorization,
|
||||
normalizePluginPackagePromptOutputExternalRecoveryAuthorization,
|
||||
verifyAuthorizedPluginPackagePromptOutputRecoveredMaterial,
|
||||
verifyPluginPackagePromptOutputExternalRecoveryAuthorization,
|
||||
} = require('../dist/prompt-output/custody/pluginPackagePromptOutputExternalRecoveryAuthorization.js');
|
||||
const {
|
||||
createPluginPackagePromptOutputExternalCustodyReceipt,
|
||||
} = require('../dist/prompt-output/custody/pluginPackagePromptOutputExternalCustody.js');
|
||||
const {
|
||||
createPluginPackagePromptOutputArtifact,
|
||||
} = require('../dist/prompt-output/pluginPackagePromptOutputArtifact.js');
|
||||
const {
|
||||
pluginPackagePromptOutputKeyRotationMaterialProof,
|
||||
} = require('../dist/prompt-output/key-management/pluginPackagePromptOutputKeyRotation.js');
|
||||
|
||||
const NOW = 1_700_000_000_000;
|
||||
const MATERIAL = Buffer.alloc(32, 0x4a);
|
||||
const WRAPPED = Buffer.from('external-kms-wrapped-material');
|
||||
const CATALOG_DIGEST = '1'.repeat(64);
|
||||
const POLICY_DIGEST = '2'.repeat(64);
|
||||
const WRAPPING_KEY_REF_DIGEST = '3'.repeat(64);
|
||||
const custodyKeys = generateKeyPairSync('ed25519');
|
||||
const approverAKeys = generateKeyPairSync('ed25519');
|
||||
const approverBKeys = generateKeyPairSync('ed25519');
|
||||
|
||||
function artifact() {
|
||||
return createPluginPackagePromptOutputArtifact(
|
||||
{
|
||||
projectId: 'project-recovery',
|
||||
runId: 'run-recovery',
|
||||
stepRunId: 'step-recovery',
|
||||
invocationId: 'invocation-recovery',
|
||||
requestedBy: { type: 'user', id: 'requester-user' },
|
||||
result: {
|
||||
provider: 'openai-compatible',
|
||||
model: 'bounded-model',
|
||||
text: 'historical private answer',
|
||||
finishReason: 'stop',
|
||||
usage: {
|
||||
inputTokens: 5,
|
||||
outputTokens: 4,
|
||||
totalTokens: 9,
|
||||
costMicros: 12,
|
||||
},
|
||||
},
|
||||
retentionPolicy: {
|
||||
revision: 'recovery-retention-v1',
|
||||
retentionMs: 86_400_000,
|
||||
},
|
||||
keyId: 'prompt-key-recovery',
|
||||
key: Buffer.from(MATERIAL),
|
||||
sealedAtMs: NOW - 60_000,
|
||||
},
|
||||
() => Buffer.alloc(12, 0x31),
|
||||
);
|
||||
}
|
||||
|
||||
function receipt() {
|
||||
return createPluginPackagePromptOutputExternalCustodyReceipt(
|
||||
{
|
||||
custodyId: 'custody-recovery',
|
||||
keyId: 'prompt-key-recovery',
|
||||
materialProof: pluginPackagePromptOutputKeyRotationMaterialProof(
|
||||
'prompt-key-recovery',
|
||||
MATERIAL,
|
||||
),
|
||||
sourceGeneration: 7,
|
||||
sourceCatalogDigest: CATALOG_DIGEST,
|
||||
wrappingProvider: 'external-kms',
|
||||
wrappingKeyRefDigest: WRAPPING_KEY_REF_DIGEST,
|
||||
wrappedMaterialDigest: createHash('sha256').update(WRAPPED).digest('hex'),
|
||||
wrappedMaterialBytes: WRAPPED.length,
|
||||
createdAtMs: NOW - 120_000,
|
||||
},
|
||||
{
|
||||
publicKey: custodyKeys.publicKey,
|
||||
sign: (message) => sign(null, message, custodyKeys.privateKey),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function signer(userId, authenticationId, keys, approvedAtMs) {
|
||||
return {
|
||||
userId,
|
||||
authenticationId,
|
||||
authenticatedAtMs: approvedAtMs - 1_000,
|
||||
approvedAtMs,
|
||||
publicKey: keys.publicKey,
|
||||
sign: (message) => sign(null, message, keys.privateKey),
|
||||
};
|
||||
}
|
||||
|
||||
function authorization(encrypted, custody, overrides = {}) {
|
||||
return createPluginPackagePromptOutputExternalRecoveryAuthorization(
|
||||
{
|
||||
recoveryId: 'recovery-001',
|
||||
requestId: 'request-001',
|
||||
custodyId: custody.custodyId,
|
||||
custodyReceiptDigest: custody.receiptDigest,
|
||||
keyId: custody.keyId,
|
||||
artifactId: encrypted.artifactId,
|
||||
artifactDigest: encrypted.artifactDigest,
|
||||
policyDigest: POLICY_DIGEST,
|
||||
requestedBy: {
|
||||
userId: 'requester-user',
|
||||
authenticationId: 'requester-auth-001',
|
||||
authenticatedAtMs: NOW - 5_000,
|
||||
},
|
||||
requestedAtMs: NOW,
|
||||
expiresAtMs: NOW + 10 * 60_000,
|
||||
...overrides,
|
||||
},
|
||||
[
|
||||
signer('reviewer-b', 'reviewer-b-auth', approverBKeys, NOW + 2_000),
|
||||
signer('reviewer-a', 'reviewer-a-auth', approverAKeys, NOW + 1_000),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
function trustedApprovers() {
|
||||
return [
|
||||
{ userId: 'reviewer-a', publicKey: approverAKeys.publicKey },
|
||||
{ userId: 'reviewer-b', publicKey: approverBKeys.publicKey },
|
||||
];
|
||||
}
|
||||
|
||||
test('binds two distinct strong approvers to one exact recovery request', () => {
|
||||
const encrypted = artifact();
|
||||
const custody = receipt();
|
||||
const value = authorization(encrypted, custody);
|
||||
assert.deepEqual(
|
||||
normalizePluginPackagePromptOutputExternalRecoveryAuthorization(value),
|
||||
value,
|
||||
);
|
||||
assert.deepEqual(
|
||||
verifyPluginPackagePromptOutputExternalRecoveryAuthorization(
|
||||
value,
|
||||
trustedApprovers(),
|
||||
NOW + 3_000,
|
||||
),
|
||||
value,
|
||||
);
|
||||
assert.deepEqual(
|
||||
value.approvals.map(({ userId }) => userId),
|
||||
['reviewer-a', 'reviewer-b'],
|
||||
);
|
||||
assert.equal(value.permission, 'artifact.read');
|
||||
assert.equal(value.purpose, 'lost-key-recovery-verification');
|
||||
});
|
||||
|
||||
test('returns only an authorization-bound content-free recovery proof', () => {
|
||||
const encrypted = artifact();
|
||||
const custody = receipt();
|
||||
const proof = verifyAuthorizedPluginPackagePromptOutputRecoveredMaterial({
|
||||
authorization: authorization(encrypted, custody),
|
||||
trustedApprovers: trustedApprovers(),
|
||||
receipt: custody,
|
||||
trustedCustodyPublicKey: custodyKeys.publicKey,
|
||||
wrappedMaterial: WRAPPED,
|
||||
durableKeyFact: {
|
||||
keyId: custody.keyId,
|
||||
materialProof: custody.materialProof,
|
||||
catalogDigest: custody.sourceCatalogDigest,
|
||||
},
|
||||
material: MATERIAL,
|
||||
artifact: encrypted,
|
||||
verifiedAtMs: NOW + 3_000,
|
||||
});
|
||||
assert.equal(proof.artifactId, encrypted.artifactId);
|
||||
assert.equal(proof.authorizationDigest.length, 64);
|
||||
assert.equal(proof.recoveryProofDigest.length, 64);
|
||||
assert.equal(proof.proofDigest.length, 64);
|
||||
const serialized = JSON.stringify(proof);
|
||||
assert.equal(serialized.includes('historical private answer'), false);
|
||||
assert.equal(serialized.includes(MATERIAL.toString('base64url')), false);
|
||||
assert.equal(serialized.includes(WRAPPED.toString('base64url')), false);
|
||||
});
|
||||
|
||||
test('rejects requester self-approval, duplicate identity and stale authentication', () => {
|
||||
const encrypted = artifact();
|
||||
const custody = receipt();
|
||||
for (const signers of [
|
||||
[
|
||||
signer(
|
||||
'requester-user',
|
||||
'requester-review-auth',
|
||||
approverAKeys,
|
||||
NOW + 1_000,
|
||||
),
|
||||
signer('reviewer-b', 'reviewer-b-auth', approverBKeys, NOW + 2_000),
|
||||
],
|
||||
[
|
||||
signer('reviewer-a', 'same-auth', approverAKeys, NOW + 1_000),
|
||||
signer('reviewer-b', 'same-auth', approverBKeys, NOW + 2_000),
|
||||
],
|
||||
[
|
||||
{
|
||||
...signer('reviewer-a', 'reviewer-a-auth', approverAKeys, NOW + 1_000),
|
||||
authenticatedAtMs: NOW - 10 * 60_000,
|
||||
},
|
||||
signer('reviewer-b', 'reviewer-b-auth', approverBKeys, NOW + 2_000),
|
||||
],
|
||||
]) {
|
||||
assert.throws(() =>
|
||||
createPluginPackagePromptOutputExternalRecoveryAuthorization(
|
||||
{
|
||||
recoveryId: 'recovery-001',
|
||||
requestId: 'request-001',
|
||||
custodyId: custody.custodyId,
|
||||
custodyReceiptDigest: custody.receiptDigest,
|
||||
keyId: custody.keyId,
|
||||
artifactId: encrypted.artifactId,
|
||||
artifactDigest: encrypted.artifactDigest,
|
||||
policyDigest: POLICY_DIGEST,
|
||||
requestedBy: {
|
||||
userId: 'requester-user',
|
||||
authenticationId: 'requester-auth-001',
|
||||
authenticatedAtMs: NOW - 5_000,
|
||||
},
|
||||
requestedAtMs: NOW,
|
||||
expiresAtMs: NOW + 10 * 60_000,
|
||||
},
|
||||
signers,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects signature, trusted approver, expiry and exact fact drift', () => {
|
||||
const encrypted = artifact();
|
||||
const custody = receipt();
|
||||
const approved = authorization(encrypted, custody);
|
||||
const signature = Buffer.from(approved.approvals[0].signature, 'base64url');
|
||||
signature[0] ^= 1;
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputExternalRecoveryAuthorization(
|
||||
{
|
||||
...approved,
|
||||
approvals: [
|
||||
{
|
||||
...approved.approvals[0],
|
||||
signature: signature.toString('base64url'),
|
||||
},
|
||||
approved.approvals[1],
|
||||
],
|
||||
},
|
||||
trustedApprovers(),
|
||||
NOW + 3_000,
|
||||
),
|
||||
PluginPackagePromptOutputRecoveryAuthorizationUntrustedError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputExternalRecoveryAuthorization(
|
||||
approved,
|
||||
[
|
||||
trustedApprovers()[0],
|
||||
{
|
||||
userId: 'reviewer-b',
|
||||
publicKey: generateKeyPairSync('ed25519').publicKey,
|
||||
},
|
||||
],
|
||||
NOW + 3_000,
|
||||
),
|
||||
PluginPackagePromptOutputRecoveryAuthorizationUntrustedError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyPluginPackagePromptOutputExternalRecoveryAuthorization(
|
||||
approved,
|
||||
trustedApprovers(),
|
||||
NOW + 11 * 60_000,
|
||||
),
|
||||
PluginPackagePromptOutputRecoveryAuthorizationUntrustedError,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
verifyAuthorizedPluginPackagePromptOutputRecoveredMaterial({
|
||||
authorization: approved,
|
||||
trustedApprovers: trustedApprovers(),
|
||||
receipt: custody,
|
||||
trustedCustodyPublicKey: custodyKeys.publicKey,
|
||||
wrappedMaterial: WRAPPED,
|
||||
durableKeyFact: {
|
||||
keyId: custody.keyId,
|
||||
materialProof: custody.materialProof,
|
||||
catalogDigest: custody.sourceCatalogDigest,
|
||||
},
|
||||
material: MATERIAL,
|
||||
artifact: { ...encrypted, artifactDigest: '4'.repeat(64) },
|
||||
verifiedAtMs: NOW + 3_000,
|
||||
}),
|
||||
PluginPackagePromptOutputRecoveryAuthorizationUntrustedError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user