Bind runtime handoff to node proof

This commit is contained in:
冰朔 2026-08-10 08:25:34 +08:00
commit 46d19aeb45
7 changed files with 249 additions and 55 deletions

View file

@ -17,6 +17,7 @@ const IDENTIFIER_PATTERN = /^[A-Z0-9][A-Z0-9._:-]{1,159}$/;
const NONCE_PATTERN = /^[A-Za-z0-9_-]{43}$/;
const issuedChallenges = new WeakSet<object>();
const consumedChallenges = new WeakSet<object>();
const verifiedPossessions = new WeakSet<object>();
export interface NodePossessionChallenge {
accountId: string;
@ -43,6 +44,13 @@ export interface NodePossessionProofSource {
prove(challenge: Readonly<NodePossessionChallenge>): Promise<unknown | null>;
}
export interface VerifiedNodePossession {
challenge: Readonly<NodePossessionChallenge>;
registration: Readonly<VerifiedNodeRegistration>;
response: Readonly<NodePossessionResponse>;
verifiedAt: number;
}
function invalidProof(): never {
throw new Error('node_possession_proof_invalid');
}
@ -113,7 +121,7 @@ export function verifyNodePossessionResponse(
challenge: NodePossessionChallenge,
registration: VerifiedNodeRegistration,
now = Date.now(),
): true {
): Readonly<VerifiedNodePossession> {
try {
assertVerifiedNodeRegistration(registration);
} catch {
@ -140,7 +148,11 @@ export function verifyNodePossessionResponse(
|| input.publicKeyPem.includes('PRIVATE KEY')
|| typeof input.signature !== 'string') invalidProof();
let publicKeyPem: string;
let signatureText: string;
try {
publicKeyPem = input.publicKeyPem as string;
signatureText = input.signature as string;
const publicKey = createPublicKey({ key: input.publicKeyPem, format: 'pem' });
if (publicKey.asymmetricKeyType !== 'ed25519') invalidProof();
const fingerprint = createHash('sha256')
@ -154,5 +166,24 @@ export function verifyNodePossessionResponse(
invalidProof();
}
consumedChallenges.add(challenge);
return true;
const verified = Object.freeze({
challenge,
registration,
response: Object.freeze({
challengeId: challenge.challengeId,
publicKeyPem: publicKeyPem!,
schema: RESPONSE_SCHEMA,
signature: signatureText!,
}),
verifiedAt: now,
});
verifiedPossessions.add(verified);
return verified;
}
export function assertVerifiedNodePossession(
possession: VerifiedNodePossession,
): VerifiedNodePossession {
if (!verifiedPossessions.has(possession)) invalidProof();
return possession;
}