Skip to content

Reference verifier accepts non-Ed25519 signatures labeled as ed25519 #52

Description

@endscene665

Summary

The pp-cli reference verifier accepts signatures made with non-Ed25519 keys while reporting signatureAlg: "ed25519" as verified.

verifyReceipt() checks the receipt's signed algorithm label, but the key resolver accepts any SPKI public key and passes it to Node's verify(null, ...). Node verifies RSA, ECDSA, and Ed448 signatures through that same call. A receipt signed with any of those key types can therefore pass the local verifier as an Ed25519 receipt if the resolved key record matches signatureKeyId.

This is distinct from #49, #50, and #51: the issue is algorithm/key-type confusion, not receipt lifecycle state, key lifecycle state, or canonicalization.

Affected code

Repository: permission-protocol/pp-cli
Current main: 0d282b947a4a3d8080182ae954b3c7d8c84059bd

src/verify.ts enforces only the label:

if (receipt.signatureAlg !== 'ed25519') {
  return { verified: false, ... };
}

const ok = verifySignature(null, payloadBytes, keyResult.key, signatureBytes);

src/keyResolver.ts parses PEM or DER public keys without checking KeyObject.asymmetricKeyType.

Reproduction

I opened a focused patch with a regression test:

permission-protocol/pp-cli#5

The test:

  1. Loads a known-valid receipt fixture.
  2. Generates an RSA key pair.
  3. Keeps the signed receipt field signatureAlg: "ed25519" unchanged.
  4. Changes signatureKeyId to a matching RSA key record and signs the canonical bytes with the RSA private key.
  5. Resolves the RSA public key from a local data: key URL and calls verifyReceipt().

Observed before the fix:

expected true to be false
Received: true

I also reproduced successful verification with generated ECDSA and Ed448 key pairs. The patch fails closed for every parsed public key whose asymmetricKeyType is not ed25519.

Scope

This demonstrates a flaw in the local Ed25519 signing/verification flow. I have not demonstrated a forged receipt against the hosted /api/v1/receipts/verify endpoint, which requires a tenant API key and has a separate implementation.

A malicious or misconfigured remote key-discovery response, or an offline verifier given a non-Ed25519 key file, can cause the local reference verifier to accept and report a receipt as Ed25519-verified when it was signed with another algorithm.

Suggested fix

After parsing the public key, require:

key.asymmetricKeyType === 'ed25519'

The linked PR applies this check in the shared key-loading path so both remote discovery and offline key files fail closed.

Validation

  • npm test -- --run
  • npm run build
  • git diff --check

Bounty note

Submitted for assessment under #36 as a distinct Ed25519 verification-flow flaw. Payout details can be provided privately after validation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions