This program was designed to build Android app releases on CI without having to store and manage any certificates or private keys.
Instead, certificate authority is derived from
SEED_CA environment variable,
which is an arbitrary string. You can generate one with, for example,
head -c 30 /dev/urandom | base64.
Running the program with the environment variable set will deterministically
ca.crt files in the current working directory
ca.key\nca.crt\n to standard output.
Most CI dispatchers support protected variables, environment variables that can only be seen by people with ownership rights over designated repository.
Safely provisioning a file to a stateless environment such as CI would require setting protected variables anyway, but the sheer complexity and coupling potential of that alone is going to be a major centralizing/fork-hostile power.
This work is part of the effort to make infrastructure concrete, stateless, and forkable just like projects that it serves. All effective build state is encapsulated into a tiny concrete string, with no additional setup involved other than setting it to some (any) value.
Seed is stretched to 73 bytes via Argon2id KDF with parameters t = 2,
m = 1536MB, p = 4 and salt
jcD_:}VIy'_q)$>^@=q&2ywx6. Resulting byte buffer is
then used as an entropy pool to generate ECDSA private key on NIST P-521 curve
Private key self-signs SHA-512 hash of a CA certificate following deterministic RFC 6979 scheme. Not before date matches Unix epoch, not after date is not specified (see RFC 5280 184.108.40.206), CA subject is X.250 compatible with every field being blank.
Why not Argon2d?
Timing attacks are not a threat for this program, so Argon2d could have been
a better choice. Unfortunately,
golang.org/x/crypto/argon2 package doesn't
expose Argon2d, only Argon2i and Argon2id.
Why not EdDSA?
EdDSA over Curve25519 is a way more misimplementation-resistant signature scheme than ECDSA over any of the NIST curves. Please prefer the former when designing new formats and protocols.
ECDSA was chosen because it has better compatibility, especially with older software. Also, this program being non-interactive and deterministic protects against most attack vectors on ECDSA.
Among NIST curves, P-521 seems to be the safest bet, even with DJB saying
it's the only one using a "nice" prime (grep for
Some projects might warrant different KDF parameters, curve selection, or X.509
certificate fields. If you think that applies to your use case, fork this repo,
change parameters in
main.go file, commit, and push to something like GitHub
Please make sure to back up your seed. If you lose it, it won't be possible to push any updates to Android apps that were signed with the lost seed, other uses may share similar consequences.
Sign with smartcards for high-threat models. Be aware that whoever hosts CI dispatcher and builders will be able to access the seed and forge signatures.
This program has not stabilized yet. Once it does, it will be moved from
unstable namespace and will be effectively set in stone.
Output of this program must never change between versions. If that ever happens, please file a bug report. Non-compatible versions of this program will go to a separate repository.
golang.org/x/crypto is pinned down. If any breaking changes happen to Go
standard library, which is the only effectively unpinned dependency, the latest
good versions of required broken packages will be vendored into this project.
The most likely thing to warrant vendoring is
crypto/ecdsa key generation.
This is a list of projects that use Seed CA (if you do, send a merge request):
- Noise, rebranded and deblobbed Signal
client for Android (used to sign the
.apkand to authenticate F-Droid repo)
Coda Hale, for implementing RFC 6979 in Go.
That's what local
ecdsa package is based on.
Thomas Pornin, for designing a safe reverse-compatible deterministic scheme for ECDSA signatures. None of this would be possible without that great and relatively recent work.