Build a PKI auditor yourself
How to not f*ck up like eMudhra
Built on eMudhra Statement on Recent Certificate Security Incident by eMudhra. Read the post alongside; each assignment links to the part it covers.
Run it locallyStart here
- Get the workbook onto your computer: clone it, or hand the prompt under Run it locally to your coding assistant. The book runs your code on your machine.
- Open the book and set up your Python project. In the book, each assignment's Start here card lists the files to write and your job in each.
- Begin with assignment 01 (Modular arithmetic) and go in order: each one adds files to the project, and later ones use them. Read it now.
| # | Title | Difficulty |
|---|---|---|
| 01 | Modular arithmetic | Easy |
| 02 | Add points on a toy curve | Easy |
| 03 | Multiply a point by a number | Medium |
| 04 | Verify an ECDSA signature | Medium |
| 05 | Read DER | Medium |
| 06 | Read a certificate | Medium |
| 07 | Hash the public key (SPKI) | Easy |
| 08 | Check a certificate's signature | Hard |
| 09 | Validate a certificate chain | Hard |
| 10 | Find a weak private key | Medium |
| 11 | Sign a certificate with a recovered key | Hard |
| 12 | Generate a key correctly | Easy |
| 13 | Lint a public key | Medium |
| 14 | Check a key ceremony | Medium |
| 15 | Block and distrust | Easy |
| 16 | Audit a PKI | Hard |
The project you build
Your Python project lives in solutions/ and grows one assignment at a time. The number by each file is the assignment that creates it, and a + marks the assignments that add to it later.
__main__.py 16 This is the finished command-line program. `pkiaudit audit <pki-file>`, or `python -m pkiaudit audit <pki-file>`, prints the audit report for a PKI file, using your `pki/audit.py`. der.py 05 This module reads DER, the tag-length-value encoding that holds every certificate in the project, into a tree of nodes, and decodes INTEGER, OBJECT IDENTIFIER and BIT STRING values. Your `x509/certificate.py` (assignment 06), `x509/spki.py` (07) and `x509/signature.py` (08) read certificates through it. weak_key.py 10 This module recovers a private key from its public key when the private key is small, by trying every value up to a bound. Assignment 13's linter calls it to flag a weak key before a CA trusts or publishes it. keys.py 12 This module turns random bytes into a private key the way FIPS 186-5 and SEC 1 describe, so that every key from 1 to n - 1 is equally likely. It takes the random source as an argument, so the tests can replay recorded draws through it. modmath.py 01 This module holds the project's arithmetic mod a number: powers by square-and-multiply, and inverses by the extended Euclidean algorithm. Your `ecc/curve.py` from assignment 02 divides mod p with the inverse, and your `ecc/ecdsa.py` divides mod n with it in assignments 04 and 11. curve.py 02 +03 This module holds the elliptic-curve group law: the curve type, points, the point at infinity and the rule that adds two points. Assignment 03 adds scalar multiplication to it, and your ECDSA code from assignment 04 and the weak-key search in assignment 10 run on its point addition. curves.py 02 +03 This module holds the constants of every curve the project uses: the toy curve now, and P-256 and P-384, which assignment 03 adds. Later assignments look up P-256 and P-384 here, from the certificate reader in assignment 06 to the PKI audit in assignment 16. ecdsa.py 04 +11 This module holds ECDSA: checking a signature now, and making one in assignment 11. Your certificate-signature check in assignment 08 calls this verification, and through it so do the chain validation in assignment 09 and the PKI audit in assignment 16. lint.py 13 It decides whether a public key is safe to trust: the point is on its curve, it is not on a blocklist of known-compromised keys, and its private key is not small enough to find by search. Assignment 16 lints every key in a PKI with it. ceremony.py 14 It replays the log of a key generation ceremony and follows each key from the step that made it to the step that publishes it, so a reused label or a key that changed on the way gets flagged. It needs none of your earlier modules, and no later assignment uses it. trust.py 15 It decides whether a browser still trusts a certificate chain once some keys are blocked by their SPKI hash and some roots are distrusted, the two ways a browser withdraws trust after an incident. It needs none of your earlier modules, and no later assignment uses it. audit.py 16 It audits a whole PKI file: a lint finding for every key, a verdict for every chain, and the chains each signing key would break if it leaked. The checker's program and the `pkiaudit audit` command both print its report. chain.py 09 This module decides whether a verifier should trust a chain of certificates, leaf first, that ends at one of its trust anchors, and names the first check that fails if not. Assignment 16 runs it over every chain in a PKI to find the chains a bad key breaks. certificate.py 06 This module reads the fields of an X.509 certificate that the project needs: the subject and issuer names, the validity window, the elliptic-curve public key and whether the certificate is a CA. Assignment 07 finds the public key field through it, 08 the signed bytes, and 09 and 16 validate chains with the fields it reads. spki.py 07 This module computes a key's SPKI hash, the SHA-256 of a certificate's subjectPublicKeyInfo bytes, which is how browsers name a key they block. The hash stays the same in every certificate that carries the key, and assignment 15 blocks whole chains by hashes like these. signature.py 08 This module checks that an issuer's public key signed a certificate: it reads the signed tbsCertificate bytes and the signature, hashes the bytes with SHA-384 and runs your ECDSA verify. Assignment 09 checks every link of a chain with it, and assignment 16 does the same across a whole PKI. assignment_NN_….*allOne small program per assignment: it reads a test input, calls your project and writes the answer the checker compares.About this workbook
Each assignment has you write what one part of the incident calls for, as files in a Python project of your own: the elliptic-curve maths, certificates and chains, then the checks that would have caught the weak key. The checker feeds your program test inputs and compares what it writes with answers from independent sources.
Most expected outputs come from OpenSSL and the cryptography library.