{"slug": "in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-its", "title": "In 2013 I wrote a diploma on elliptic curves. Last night my tool used them to check its own release.", "summary": "A developer built croak, a Claude Code hook that intercepts shell commands before an AI agent runs them, having npm or pnpm plan the install without executing it, then inspecting install scripts, lockfile, registry metadata and unpacked package code in memory to issue an explained verdict. The tool also verifies npm provenance signatures using Node's built-in crypto, confirming its own release as valid and rejecting a mismatched tarball. On its own eval it flagged all 36 attacks but blocked only 27, sending the remaining nine to a human for review.", "body_md": "**TL;DR**: In 2013 I wrote a university diploma on elliptic-curve cryptography, and it went into a drawer. Thirteen years later the same math signs most of what you `npm install`. I taught **croak** 🐸, my pre-install gate for AI agents, to check those signatures itself. Last night it checked its own fresh release: `verified`. Then I gave it the same signature with a different tarball: `invalid`. Below is what that check actually involves, and why the curve is the easy part.\n\nIn 2013: a diploma on elliptic curves and key exchange. Points, groups, a lot of modular arithmetic. Defended, filed, forgotten.\n\nI went into test automation, and the curves stayed in the drawer.\n\nThen AI agents started running `npm install` on their own.\n\nAn install doesn't just download code. It **runs** it: `postinstall`, native builds, whatever a package wants. An agent does this all day, in your terminal, usually without reading anything. It also invents package names that sound right, and attackers register those names.\n\n**croak** sits between the agent and the install. It's a Claude Code hook: before a shell command runs, croak has npm or pnpm *plan* the install (resolve it, run nothing), then reads that plan as data: install scripts, the lockfile, registry metadata, and the code of every new package, unpacked in memory and never executed. It gives a verdict and **explains it to the agent**:\n\n```\nagent:  npm install reactt-domm\n\ncroak:  🐸 croak blocked this command before it ran.\n        reactt-domm: not on npm; did you mean `react-dom`?\n        Do not work around this check (another package manager, a different\n        command, or editing files to install it); tell the user what croak\n        found and let them decide.\n```\n\n`postinstall` that downloads a binary lands here.\nIt also scans an unfamiliar repo before you open it (`croak repo`: VS Code tasks that run on open, code hidden after hundreds of spaces, obfuscation), because \"clone this take-home assignment and run it\" is a real attack too.\n\nIt's early, and it misses things. On its own eval it flags all 36 attacks but blocks only 27; the other nine go to a human as 🟡. I'd rather publish that number than a prettier one.\n\nMost attacks give themselves away in the code: a `postinstall` that pipes `curl` into `sh`, `process.env` posted somewhere. And install scripts are losing ground anyway: pnpm 10 and npm 12 no longer run a dependency's scripts unless the project allows them. I checked with a bait package that writes a marker file: npm 11 still runs them; npm 12 doesn't.\n\nThat pushes attacks into code you *import*, and that code can look exactly like the real thing. Someone steals a maintainer's token, publishes from a laptop, and the tarball looks like every release before it.\n\nThat is the question code can't answer: **who built this tarball, and from what?**\n\nnpm has an answer. It's called provenance. And it's elliptic curves all the way down.\n\nWhen a package is published from CI with provenance, a few things happen in a couple of seconds:\n\n`release.yml` in repo X, at commit Y\"\nHere's my own release from last night, read straight from the certificate:\n\n```\nleaf key:  ec { namedCurve: 'prime256v1' }      // P-256\nvalid:     Nov  1 23:13:03 2026 GMT -> Nov  1 23:23:03 2026 GMT   // 10 minutes\nissuer:    O=sigstore.dev, CN=sigstore-intermediate\n```\n\nA key that lives for ten minutes and is then thrown away. The trust comes from the log, not from keeping a secret for years.\n\nCount the curves. The registry's own signature on every package: **P-256**. The build's ten-minute key: **P-256**. Rekor's key: **P-256**. (Fulcio's CA uses its bigger sibling, P-384.)\n\nMy diploma, signing my `node_modules`.\n\nHere is the elliptic-curve part of croak's verifier:\n\n```\nverify(\"sha256\", message, leaf.publicKey, signature)\n```\n\nOne line. `node:crypto` does the curve math, and it does it better than I would. No point addition by hand, no hand-rolled anything. In 2026, writing your own ECDSA would be a red flag, not a skill.\n\n**All the hard parts moved into the plumbing around that line.** A signature verifies *something*. The question is whether it is the thing you think. croak's check, in order:\n\n``` js\nconst logged = readLogEntry(entries[0], trust.rekor); // 1. Rekor promised it saw this, and when\nconst leaf = leafOf(material);\ncheckCertificate(leaf, trust.fulcio, logged.loggedAt); // 2. Fulcio issued it, valid at that moment\n// 3. the envelope's signature verifies with the leaf key\n// 4. the log entry is for this exact signature, certificate and payload\ncheckSubject(payload, subject);                        // 5. the statement is about THIS tarball\nreturn { status: \"verified\", identity: fulcioIdentity(leaf.raw) };\n```\n\nA few things I only learned by building it:\n\n`\"DSSEv1 <type length> <type> <payload length> <payload>\"`. Sign the raw JSON instead and a valid signature could be reinterpreted as a different type.`unverifiable`, not `verified`. Only a bundle that claims something its signatures don't back is `invalid`.\nLast night I tagged croak 0.1.4. The release workflow built it, packed it once, and published that exact tarball with provenance. Then I asked croak, through its own verifier, about itself:\n\n```\nlatest: 0.1.4\n0.1.4: verified https://github.com/vola-trebla/croak/.github/workflows/release.yml@refs/tags/v0.1.4\n       @ a598a5863c9146429859323e2858421e9db16f21 logged 2026-11-01T23:13:03.000Z\n```\n\nThe workflow file, the tag, the commit. Logged at 23:13:03, the same second the ten-minute certificate was born.\n\nThen the check that matters: the same signature, but compared against the previous release's tarball hash.\n\n```\n0.1.4 bundle against 0.1.3's tarball: invalid\n```\n\nA real signature over the wrong bytes. That's the tampered-tarball case, and it has to say no.\n\nThe first time croak looked at itself, it flagged itself.\n\nVersion 0.1.0 shipped with my source comments in it. One of those comments explained the obfuscation detector by quoting the pattern it hunts for, `globalThis[key](…)`. croak scanned its own installed files and found \"obfuscated code\".\n\nThe fix was one compiler flag, `removeComments`. Now CI installs the packed tarball and runs croak on it before every release. A security tool that can't pass its own scan doesn't get published.\n\nThis is the part that's easy to oversell.\n\nProvenance proves **where a tarball came from**: this workflow, this repo, this commit. It does not prove **the code is safe**. A malicious maintainer can publish malware with perfect provenance from their own CI.\n\nSo in croak it's one signal among many, and it's used as a comparison:\n\n`npm audit signatures` verifies registry signatures and provenance for what's in your lockfile. It's already in npm.`npm trust github <pkg> --file release.yml --repo you/repo`). No long-lived token to steal, provenance for free.` npm publish` repack after your tests ran.`npm i -g @vola-trebla/croak`.\nThirteen years ago I thought that diploma was the end of elliptic curves for me. Turns out they were just waiting for `npm install`.\n\ncroak is open source and early: [github.com/vola-trebla/croak](https://github.com/vola-trebla/croak). Issues, and attacks it misses, are welcome.\n\n🐸", "url": "https://wpnews.pro/news/in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-its", "canonical_source": "https://dev.to/vola-trebla/in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-check-its-own-release-1k3i", "published_at": "2026-11-01 23:32:06+00:00", "updated_at": "2026-11-01 23:51:54.263102+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-safety", "developer-tools", "artificial-intelligence"], "entities": ["croak", "npm", "pnpm", "Claude Code", "sigstore", "Rekor", "Fulcio"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-its", "markdown": "https://wpnews.pro/news/in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-its.md", "text": "https://wpnews.pro/news/in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-its.txt", "jsonld": "https://wpnews.pro/news/in-2013-i-wrote-a-diploma-on-elliptic-curves-last-night-my-tool-used-them-to-its.jsonld"}}