Secure Binaries
fnc deliverables are verified by SHA256 checksum—and it is the SHA256 file that is signed. Working under the premise that SHA256 checksums cannot be forged, this signing process creates a chain of trust. To wit, if the signature matches, then the checksums must be the same checksums that were signed on the build box. And if the checksums match, then it follows that the tarball is the same set of files that were archived and hashed on said build machine.
The caveat is that to seed this chain of trust, initially, the public key must
be trusted; but how can you be sure that your copy of the key hasn't been
interdicted and used to sign a forged set of files, thus rendering you with a
verifiable, albeit ¬fnc
, tarball? The fact that just 56 base-64 encoded
characters constitute the key means that it is small and easily accessible. We
have, therefore, made it widely available in locations that have a very high
likelihood of being under our control—this website and our own DNS server, for
example—which enables you to validate your copy of the key. Further, at any
point in time, the current public key—and the next for a future fnc
release—are in the source tree, having been committed to the repository's
blockchain—a traceable and unfalsifiable historical record. In addition, each
signed deliverable is linked to via a corresponding technote that contains
the signed checksum. Such dissemination increases the difficulty for threat
agents to intercept your public key retrieval, and decreases the risk that any
such attempt would go undetected, thus mitigating this attack vector.
Signing Process
The following details how tarballs of fnc's binaries and source code are
created and signed during the release process, which is performed by a script
that comprises the below steps. Although the example demonstrates an OpenBSD
build, the process is identical on macOS and Linux except for the use of
OpenBSD's sha256
(1) counterpart; namely, gsha256sum --tag
and
sha256sum --tag
on macOS and Linux, respectively. The --tag
option is
needed because signify
(1) expects BSD-style checksums. Similarly, the
openbsd
string in file paths is replaced with its corresponding
platform—darwin
or linux
—as reported by uname
(1).
- From the root of the source tree, build the tip of trunk
(builds are done with both clang and gcc):
fossil update trunk make clean CC="ccache egcc" make -j4 make clean CC="ccache clang" make -j4
- Strip the resulting binary:
strip ./fnc/fnc
- Create tarball of the binary executable and manual page:
tar zcvf fnc15-openbsd.tar.gz fnc/fnc fnc/fnc.1
- Compute the SHA256 checksum of the tarball:
sha256 fnc15-openbsd.tar.gz > fnc15-openbsd.tar.gz.SHA256
- Sign checksum and write the signature to file:
signify -Ses $private_key -m fnc15-openbsd.tar.gz.SHA256
- Move the tarball and signature file to the repository's unversioned
downloadable content:
mv fnc15-openbsd.tar.gz* dl/
- Update and sync the files:
fossil uv add dl/fnc15-openbsd* fossil uv sync
- Commit a technote with the signed checksum linked to the tarball:
fossil wiki create "signed checksum: [/uv/dl/fnc15-openbsd.tar.gz|fnc15-openbsd.tar.gz]" -M plain -t now --technote-tags 0.15 fnc15-openbsd.tar.gz.SHA256.sig
◆ n.b. The script is run on all supported platforms to produce binary
releases, and the source tarball is the same except steps 1–3 are replaced
with fossil update trunk && make clean && make release
from the root of the
work tree.