From bbb14ee583cce906fac37ea53747c86d58bca16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A2=9D=E6=88=91=E5=8F=91=E4=B8=AA=E4=BA=BA?= <3646477@> Date: Tue, 12 May 2026 17:18:41 +0800 Subject: [PATCH] fix: accept raw Ed25519 public keys --- src/cli.ts | 2 +- src/keyResolver.ts | 4 ++++ tests/fixtures/public-key.raw-base64 | 1 + tests/verify.test.ts | 10 ++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/public-key.raw-base64 diff --git a/src/cli.ts b/src/cli.ts index 72d458f..a04fb28 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -96,7 +96,7 @@ program .command('verify') .argument('', 'Receipt file path, -, receipt id, or share URL') .option('--key-url ', 'Public key endpoint URL') - .option('--key-file ', 'Path to public key PEM or base64 DER') + .option('--key-file ', 'Path to public key PEM, base64 DER, or raw Ed25519 base64') .option('--key-id ', 'Expected key id') .option('--json', 'Emit machine-readable JSON output') .option('-q, --quiet', 'Suppress stdout and return exit code only') diff --git a/src/keyResolver.ts b/src/keyResolver.ts index 8a8b65a..d45601e 100644 --- a/src/keyResolver.ts +++ b/src/keyResolver.ts @@ -2,6 +2,7 @@ import { createPublicKey } from 'node:crypto'; import { readFile } from 'node:fs/promises'; export const DEFAULT_KEY_URL = 'https://app.permissionprotocol.com/api/v1/keys/current'; +const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex'); export type ResolveKeyOptions = { keyFile?: string; @@ -31,6 +32,9 @@ function asPublicKey(raw: string): ReturnType { return createPublicKey(trimmed); } const der = Buffer.from(trimmed, 'base64'); + if (der.length === 32) { + return createPublicKey({ key: Buffer.concat([ED25519_SPKI_PREFIX, der]), format: 'der', type: 'spki' }); + } return createPublicKey({ key: der, format: 'der', type: 'spki' }); } diff --git a/tests/fixtures/public-key.raw-base64 b/tests/fixtures/public-key.raw-base64 new file mode 100644 index 0000000..255ceb2 --- /dev/null +++ b/tests/fixtures/public-key.raw-base64 @@ -0,0 +1 @@ +utC/MffkkEO2hXR37Ilq4ut4UpZwdrwmtbGw/eEymo0= diff --git a/tests/verify.test.ts b/tests/verify.test.ts index 0fd26db..6ca6254 100644 --- a/tests/verify.test.ts +++ b/tests/verify.test.ts @@ -9,6 +9,7 @@ async function loadJson(name: string): Promise { } const keyFile = resolve(process.cwd(), 'tests/fixtures/public-key.pem'); +const rawKeyFile = resolve(process.cwd(), 'tests/fixtures/public-key.raw-base64'); describe('verifyReceipt', () => { it('verifies a valid receipt', async () => { @@ -20,6 +21,15 @@ describe('verifyReceipt', () => { } }); + it('verifies a valid receipt with a raw Ed25519 base64 public key', async () => { + const receipt = await loadJson('valid.json'); + const result = await verifyReceipt(receipt, { keyFile: rawKeyFile, noNetwork: true }); + expect(result.verified).toBe(true); + if (result.verified) { + expect(result.receiptId).toBe('rcpt_valid_001'); + } + }); + it('returns expired for expired receipt', async () => { const receipt = await loadJson('expired.json'); const result = await verifyReceipt(receipt, { keyFile, noNetwork: true });