Skip to content

Commit fdc219f

Browse files
test(nostr-cid-vm): make JWK-y corruption provably off-curve (#438) (#504)
The "rejects a JWK with the right x but wrong y" test flipped the last base64url char of y from 'A'→'B' (or x→'A'). Because the final base64url char of a 32-byte field carries only 4 high bits — the bottom 2 are padding — 'A' (000000) and 'B' (000001) decode to the SAME bytes whenever y ended in 'A' (≈1/16 of randomly generated keys), turning the "corruption" into a no-op so the test occasionally accepted the JWK as valid. (A second, far rarer path: the flip landed on -y mod p.) Set y to 32 zero bytes instead. (x, 0) is on secp256k1 iff x³ ≡ -7 mod p, which has exactly 3 solutions in ~2²⁵⁶ — provably off-curve for any practical random x. Verified across 30 consecutive runs.
1 parent 63690e5 commit fdc219f

1 file changed

Lines changed: 6 additions & 3 deletions

File tree

test/nostr-cid-vm.test.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,12 @@ describe('NIP-98 + CID verificationMethod lookup (#399)', () => {
210210

211211
it('rejects a JWK with the right x but wrong y (curve-point integrity)', async () => {
212212
const goodJwk = evenYJwk(pk);
213-
// Flip the y to invalid — same x, different y → not on canonical
214-
// BIP-340 point, so should NOT match the Nostr key.
215-
const badJwk = { ...goodJwk, y: goodJwk.y.slice(0, -1) + (goodJwk.y.endsWith('A') ? 'B' : 'A') };
213+
// Force y = 0 (43 'A's = 32 zero bytes). (x, 0) is on secp256k1
214+
// iff x³ ≡ -7 mod p, which has exactly 3 solutions in ~2²⁵⁶ — so
215+
// for any practical x this is provably off-curve. Avoids the flake
216+
// where a single-char flip in y was either a base64url padding-bit
217+
// no-op or, far more rarely, landed on the negation -y mod p.
218+
const badJwk = { ...goodJwk, y: 'A'.repeat(43) };
216219
nextProfile = buildProfile({ pubkey: pk, jwk: badJwk });
217220
const url = `https://${POD_HOST}/private/data.ttl`;
218221
const { authHeader } = nip98Authorization({ method: 'GET', url, secretKey: sk });

0 commit comments

Comments
 (0)