Workbooks

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 locally

Start here

  1. 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.
  2. 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.
  3. 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.

Assignments

5 easy 7 medium 4 hard
#TitleConceptsDifficulty
01 Modular arithmetic
modular exponentiation (square-and-multiply)modular inverse (extended Euclid)
Easy
02 Add points on a toy curve
elliptic-curve point addition and doublingthe point at infinity
Easy
03 Multiply a point by a number
elliptic-curve scalar multiplication (double-and-add)private and public keys
Medium
04 Verify an ECDSA signature
ECDSA signaturesmodular inverse
Medium
05 Read DER
ASN.1DER (Distinguished Encoding Rules)
Medium
06 Read a certificate
X.509ASN.1
Medium
07 Hash the public key (SPKI)
SubjectPublicKeyInfoSPKI pinning
Easy
08 Check a certificate's signature
tbsCertificateECDSA verification
Hard
09 Validate a certificate chain
chain of trusttrust anchors
Hard
10 Find a weak private key
elliptic-curve public and private keyspoint addition and doubling
Medium
11 Sign a certificate with a recovered key
ECDSA signingthe private key's role
Hard
12 Generate a key correctly
uniformly random private keysrejection sampling
Easy
13 Lint a public key
curve membershipweak-key search (assignment 10)
Medium
14 Check a key ceremony
chain of custodykey wrap/unwrap
Medium
15 Block and distrust
SPKI pinning/blockingroot distrust
Easy
16 Audit a PKI
key lintingchain validation
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.

solutions/src/pkiaudit/
__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`.
solutions/src/pkiaudit/asn1/
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.
solutions/src/pkiaudit/attack/
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.
solutions/src/pkiaudit/ecc/
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.
solutions/src/pkiaudit/pki/
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.
solutions/src/pkiaudit/x509/
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.
solutions/assignments/
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.