From 26c70a1f145877282c8fc4d5b3c8b98aceeb2912 Mon Sep 17 00:00:00 2001 From: EauDoon <47585778+EauDoon@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:44:48 +0000 Subject: [PATCH] Reject weak Ed25519 keys in CasePack checkpoint verification --- src/casepack/validate.ts | 8 ++------ test/casepack-v11.test.mjs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/casepack/validate.ts b/src/casepack/validate.ts index 0516b06..975b4bc 100644 --- a/src/casepack/validate.ts +++ b/src/casepack/validate.ts @@ -1,8 +1,5 @@ import { Buffer } from "node:buffer"; -import { - createPublicKey, - type JsonWebKeyInput, -} from "node:crypto"; +import { jwkThumbprint } from "../crypto.js"; import { isSha256Digest, } from "../canonical.js"; @@ -388,8 +385,7 @@ function validatePublicJwk(value: unknown, path: string, issues: ValidationIssue return false; } try { - const key = createPublicKey({ key: value as unknown as JsonWebKeyInput["key"], format: "jwk" }); - if (key.asymmetricKeyType !== "ed25519") throw new TypeError("Unexpected key type"); + jwkThumbprint(value as unknown as Ed25519PublicJwk); } catch { addIssue(issues, path, "MBCP_SCHEMA_INVALID", "External public key is malformed"); return false; diff --git a/test/casepack-v11.test.mjs b/test/casepack-v11.test.mjs index 8894621..af8cd00 100644 --- a/test/casepack-v11.test.mjs +++ b/test/casepack-v11.test.mjs @@ -790,6 +790,37 @@ test("caller-pinned external trust verifies a signed source checkpoint without e assert.equal(report.coverageStatus, "satisfied"); assert.equal(report.nativeBundle.trustChecked, false); + // Identity-point public keys permit R=identity, S=0 without a private key. + // Pinning such a snapshot must not turn this forgery into source evidence. + const identity = Buffer.alloc(32); + identity[0] = 1; + const identitySnapshot = sealExternalTrustSnapshot({ + ...externalTrustSnapshot, + keys: [{ + ...externalTrustSnapshot.keys[0], + publicJwk: { kty: "OKP", crv: "Ed25519", x: identity.toString("base64url") }, + }], + snapshotDigest: undefined, + }); + const forgedCheckpoint = { + ...checkpoint, + proofs: [{ + ...checkpoint.proofs[0], + signature: Buffer.concat([identity, Buffer.alloc(32)]).toString("base64url"), + }], + }; + const identityReport = verifyMandateBoundCasePack( + resealPack({ + ...candidate, + externalTrustSnapshot: identitySnapshot, + sourceCheckpoints: [forgedCheckpoint], + }), + { ...anchors, externalTrustSnapshotDigest: identitySnapshot.snapshotDigest }, + ); + assert.equal(identityReport.valid, false); + assert.ok(identityReport.issues.some((issue) => + issue.code === "MBCP_SCHEMA_INVALID" && issue.path.endsWith(".publicJwk"))); + const lateKeySnapshot = sealExternalTrustSnapshot({ ...externalTrustSnapshot, keys: [{