Backend
Zero Dependencies Sounds Easy Until You Have to Build Everything Yourself
Zunairah Maasfa Khan DEV Community
3 views
We built a dead-man's-switch for your crypto wallet. Then we broke our own encryption on purpose, just to make sure we knew how to fix it.
Team Name: FemTech
Team Members: Umaima, Zunairah, Alizah
Track E — Security & Crypto Utilities
Zero Dependency 2026
Repo: github.com/umaima06/Quorum
Here's the pitch we kept giving people during the 72 hours: if I go dark for 30 days, my co-founder and my lawyer each get half of what they need to unlock my company's wallet — but neither one of them, alone, can touch it.
That's Quorum. A CLI and local dashboard that splits a secret into N pieces, requires K of them to put it back together, and quietly checks on you in the background so the right people find out if you stop checking in. Every import in it is something you already have installed, because it's Python's own standard library. requirements.txt is empty on purpose — that's not a limitation we worked around, it's the entire assignment.
We picked Track E because crypto felt like where cutting corners actually hurts someone.
What we reimplemented
The part we're proudest of is also the part that scared us the most going in: Shamir's Secret Sharing, from the actual math up. Not a wrapper, not a simplified toy version — a real FiniteField class doing modular arithmetic over a 521-bit prime, a Polynomial class, and reconstruction via Lagrange interpolation. We spent a full day just proving, that three points really do reconstruct a degree-2 polynomial and that fewer than three points genuinely reveal nothing — not a fingerprint, not a bias, nothing. That proof is the entire security guarantee everything else sits on, so it had to be right first.
This is also our Package Killer entry. It replaces what a team would normally reach for in secretsharing or the secret-splitting helpers bundled into pycryptodome — real, pip install-able packages that exist specifically so nobody has to do what we just described.
On top of it sits a Canary Trap: decoy shares, generated at x-coordinates reserved far above the real ones, that look completely legitimate but trip a visible alert the moment one is submitted during reconstruction. If a share leaks, or a trustee gets pressured, you find out. That mechanism doesn't come from any package. It's just paranoia, encoded.
Shares reach trustees over Diffie-Hellman - the standard RFC 3526 Group 14 parameters, publicly vetted, not invented by us - so the secret itself never crosses the wire in any recoverable form. Only public keys do.
What the standard library made genuinely painful
http.server. We'll die on this hill.
Quorum ships a full local web dashboard — six tabs, live countdown, a real-time visualization of the actual polynomial your secret was just split against, trustee management, key generation — served entirely off BaseHTTPRequestHandler. No Flask. No Express. Every do_GET and do_POST is routing by hand, string-matching paths, writing our own JSON headers, building session cookies out of http.cookies.SimpleCookie because there's no Flask-Login to hand that to.
The part that made this actually painful, not just tedious: sessions. We needed the dashboard to log an owner or a trustee in once, keep them logged in across every tab and every API call for up to four hours, and enforce their role server-side — not just hide a button in the UI, but actually reject the request at the handler if the session token didn't check out. That meant hand-rolling an in-memory session store (_sessions, a dict mapping a secrets.token_hex(32) token to a role and an expiry), a lock around it, and a _require_role check that runs in front of every single endpoint, including the read-only ones — status, the audit log, the trustee list, even the polynomial demo tab. It would've been three lines with a decorator in Flask. It took us the better part of a day in raw stdlib, and we're not even mad about it, because now we know exactly what that decorator was hiding from us.
The edge case that ate an afternoon
It wasn't a bug we found in a day. We reviewed our encryption design partway through and came back with three things wrong.
We were deriving the AES-adjacent encryption key straight from a single SHA-256 hash of the Diffie-Hellman shared secret. One hash, one key, done — and that's exactly the kind of thing that looks fine until someone who actually knows what they're looking at points out it isn't a real key-derivation function, it's a shortcut wearing one's clothes. We were also generating our keystream by hashing the key once and reusing that same block across the whole message, which is a repeating-keystream bug — the kind of thing that, in a real stream cipher, is catastrophic, because two ciphertexts encrypted under the same keystream can be XORed against each other to leak both plaintexts. And we had no authentication on the ciphertext at all — a tampered payload would've decrypted into silent garbage instead of getting rejected.
None of those are the kind of bug you catch by looking at your own code once. We spent that afternoon ripping out the shortcut version and rebuilding it properly: hashlib.pbkdf2_hmac at 100,000 iterations to derive separate encryption and MAC keys, a counter-mode-style keystream where every message gets a fresh random nonce that's folded into the derivation so no block is ever reused, and encrypt-then-MAC with hmac.compare_digest so a tampered payload gets refused outright instead of decrypting into noise. We didn't invent any of those constructions — Track E is explicit that you don't get to roll your own cipher, and we agree with the rule even where it made our afternoon worse. We just finally composed the standard ones correctly instead of the shortcut version we'd talked ourselves into.
That review is also the reason THREAT_MODEL.md exists in the form it does — not as a formality, but because getting caught with a real gap made it obvious we needed to write down, in plain language, exactly what we do and don't claim to protect against, before a judge had to ask.
The package we made look unnecessary
Beyond the Shamir implementation, the STDLIB.md file has more than a dozen substitutions we're genuinely proud of, but three stand out for how invisible the replaced package usually is:
Password/credential hashing — no bcrypt, no passlib, no argon2-cffi. Every owner passphrase and every auto-generated trustee credential goes through hashlib.pbkdf2_hmac with a locally generated salt, verified with constant-time comparison. The library everyone reaches for here exists almost entirely to save you from writing four lines correctly. We wrote the four lines correctly.
Session/login handling — no Flask-Login, no itsdangerous. Just secrets.token_hex, a dict, and a lock.
Trustee notifications — no sendgrid, no yagmail. Real SMTP over TLS through smtplib and email.message.EmailMessage, with a local quorum_mailbox.log fallback so the tool never crashes or silently fails if you haven't configured a mail server. Reminders, trigger notifications, and trustee credential delivery all go through the same path.
The stuff that isn't in the demo but is in the repo
Three dashboard bugs that only showed up once the tool was actually being used instead of just described: the background watcher wasn't re-reading state after a check-in, so the countdown lied to you. Hitting "stop watching" didn't actually stop the thread, it just stopped showing you that it was running. And you could split an empty string as a "secret" and the tool would happily hand you five shares of nothing. All three are fixed, and the reason we're mentioning them here instead of hiding them is the same reason THREAT_MODEL.md exists: we'd rather tell you what we found and fixed than have a judge find it first.
We also built a hash-chained Chain of Custody log — every security-relevant action links to the SHA-256 hash of the one before it, so verify-log can tell you exactly where in the history someone tampered, if anyone ever does. And a reproducible-build check, because "pure Python has no compile step" felt like it was letting us off too easy for the Reproducible Build bonus — so we hash the source file twice, independently, and publish both SHA-256s so anyone can confirm they match.
Why this, why now
The hackathon's own numbers make the case better than we can: the average npm project drags in over a thousand transitive dependencies, and roughly one in five packages an AI model suggests doesn't even exist — which means the fake names are sitting there, pre-registered, waiting for someone to pip install them by accident. A security tool built on top of that supply chain is a security tool with a thousand strangers standing behind it. Quorum has zero. Every line that touches your secret, your trustees, or your encryption key was written by the three of us this week, and every function it calls is something you already have on your machine.
We're not claiming Quorum is production-ready — THREAT_MODEL.md says so in plain words: it doesn't stop K trustees from colluding, it can't protect a device that's already compromised, and http.server is a local tool by design, not a public-facing one. But for what it actually claims to do — split a secret, keep any one piece from being enough, and make sure the right people hear about it if you go quiet — it does that honestly, with nothing hidden behind a manifest, and we can explain every single line of why.
Built for the Zero Dependency 2026 Hackathon run by Hackathon Raptors @partnerships_raptors
— Umaima, Zunairah, Alizah
#ZeroDependency #ZeroDeps #HackathonRaptors #Unstop #BuildFromScratch #72Hours
Read original: https://dev.to/zunairah_k/zero-dependencies-sounds-easy-until-you-have-to-build-everything-yourself-5cnm
← Previous
Mastering React Server Components: Understanding the Server/Client Boundary
Next →
The Hidden Instructions That Can Hijack AI Agents
Related
We Replaced the Entire Secrets Management Stack with Go's Standard Library
Backend
1
Dev.to (EN Zone)
Zero Dependencies, 456 Tests, and One Bug All of Them Missed
Backend
4
DEV Community
7 Ways to Make Your API Faster
Backend
1
DEV Community
Laravel 13: A Practical Guide for PHP Developers
Backend
2
Dev.to (EN Zone)
Comments0
No comments yet — be the first