From 3a24d3c3deb7183c509f46f601fa1279cc6a58b1 Mon Sep 17 00:00:00 2001 From: endscene665 Date: Sun, 31 May 2026 02:58:53 +0500 Subject: [PATCH] fix: reject non-ed25519 signing keys --- src/keyResolver.ts | 12 ++++++++---- tests/verify.test.ts | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/keyResolver.ts b/src/keyResolver.ts index 8a8b65a..b779cc2 100644 --- a/src/keyResolver.ts +++ b/src/keyResolver.ts @@ -27,11 +27,15 @@ export type ResolveKeyResult = ResolveKeySuccess | ResolveKeyFailure; function asPublicKey(raw: string): ReturnType { const trimmed = raw.trim(); - if (trimmed.includes('BEGIN PUBLIC KEY')) { - return createPublicKey(trimmed); + const key = trimmed.includes('BEGIN PUBLIC KEY') + ? createPublicKey(trimmed) + : createPublicKey({ key: Buffer.from(trimmed, 'base64'), format: 'der', type: 'spki' }); + + if (key.asymmetricKeyType !== 'ed25519') { + throw new Error(`unsupported public key type: ${key.asymmetricKeyType ?? 'unknown'}`); } - const der = Buffer.from(trimmed, 'base64'); - return createPublicKey({ key: der, format: 'der', type: 'spki' }); + + return key; } type RemoteKeyRecord = { diff --git a/tests/verify.test.ts b/tests/verify.test.ts index 0fd26db..cf27b01 100644 --- a/tests/verify.test.ts +++ b/tests/verify.test.ts @@ -1,6 +1,8 @@ import { readFile } from 'node:fs/promises'; import { resolve } from 'node:path'; +import { generateKeyPairSync, sign } from 'node:crypto'; import { describe, expect, it } from 'vitest'; +import { canonicalizeReceiptBytes } from '../src/canonicalize.js'; import { verifyReceipt } from '../src/verify.js'; async function loadJson(name: string): Promise { @@ -59,4 +61,23 @@ describe('verifyReceipt', () => { expect(result.errorCode).toBe('SIGNATURE_INVALID'); } }); + + it('rejects non-Ed25519 signing keys', async () => { + const receipt = (await loadJson('valid.json')) as Record; + const { privateKey, publicKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }); + receipt.signatureKeyId = 'rsa-test'; + receipt.signatureValue = sign(null, canonicalizeReceiptBytes(receipt), privateKey).toString('base64'); + + const encodedKey = publicKey.export({ type: 'spki', format: 'der' }).toString('base64'); + const keyUrl = `data:application/json,${encodeURIComponent( + JSON.stringify({ keyId: receipt.signatureKeyId, publicKey: encodedKey }), + )}`; + const result = await verifyReceipt(receipt, { keyUrl }); + + expect(result.verified).toBe(false); + if (!result.verified) { + expect(result.exitCode).toBe(4); + expect(result.errorCode).toBe('KEY_RESOLUTION_FAILED'); + } + }); });