Bind runtime handoff to node proof
This commit is contained in:
parent
ca67ba92d2
commit
46d19aeb45
7 changed files with 249 additions and 55 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue