This website can be verified

What the signed inventory of this website contains, how it is rebuilt every day, how to verify the signature and what it does not prove.

What you cannot see from outside

When you open a website, you see the result: text, images, a padlock in the address bar. You do not see which operating system runs underneath, which programs are installed, what the pages were built with, or whether the file you are loading is still the same as yesterday's. For a provider people trust with their mail, those are not side issues.

So for this website, privymail.eu publishes an inventory that you can download and check yourself: the manifest. It is dated and digitally signed. This post explains what it contains, how it is produced and where its evidential value ends.

Seven files, one signature

Seven files are available for download under Transparency.

FileContents
manifest.jsonthe snapshot: capture time, release, server addresses, operating-system and tool versions, confirmed operator details, plus the hashes of four other files
manifest.sigthe signature over exactly this file (Ed25519)
manifest-public-key.pemthe public key you verify the signature with
manifest.schema.jsonthe schema that defines the manifest's structure
website.cdx.jsonthe website's bill of materials: tools and font files (CycloneDX)
host-packages.cdx.jsonthe Debian packages installed on the server (CycloneDX)
public-files.jsonhash and size of every published file

Only the manifest is signed. But it contains the hashes of four of the other files: both bills of materials, the file list and the schema. The file list in turn gives a SHA-256 hash for every page, every font and every image. In this way a single signature reaches every file your browser loads from here. The seven files themselves are not in the file list; otherwise a list would have to contain its own hash.

The manifest relies on what can be established on the server and on confirmed details. Where both are missing, the field stays empty and is named as unknown; nothing is filled in from an IP address or a guess.

New every morning. Check first, then switch.

A program on the server generates the manifest from what is actually there: what is installed and which files belong to the release. Every publication is built in a new directory next to the running one, in a fixed order:

  1. Record the state of the server.
  2. Check internal links and build the website.
  3. Record the built files, sign the manifest and verify the signature straight away.
  4. Check the website's rules, for example: no script, no third-party address.
  5. Switch. Only then does the web server deliver the new release, in a single step.

If any step fails, the previous release stays online.

The server installs operating-system updates every day. Yesterday's package inventory would therefore quickly be wrong. So the same procedure runs by itself every morning at around 5 o'clock (Europe/Berlin time) and publishes a new, freshly signed snapshot. If someone has published a newer release by hand in the meantime, the daily run discards its draft. It remains a daily run: no promise that the inventory is right immediately after every update.

Check it yourself: three files, two commands

You need a Linux machine with OpenSSL 3 and three of the seven files: manifest, signature and key. Download them unchanged. The signature covers the exact bytes; if an editor reformats the file, it no longer matches.

openssl pkey -pubin -in manifest-public-key.pem -outform DER | sha256sum
openssl pkeyutl -verify -pubin -inkey manifest-public-key.pem -rawin -in manifest.json -sigfile manifest.sig

The first command computes the key's fingerprint: the SHA-256 hash of its DER form, not of the PEM file. Compare it with the fingerprint on the Transparency page and make a note of it. The second command checks whether the signature matches the manifest and the key. If the release changes exactly while you are downloading, the files may belong to different snapshots; in that case download all three again.

The price: every change is a release

Even a corrected typo goes through build, checks and signing before it becomes visible. Patching the running release by hand is ruled out: the hashes in the inventory would no longer match.

The inventory shows what is running. Anyone reading the package list sees the installed versions, and that includes someone looking for vulnerable versions. Against that, the server installs its updates daily. The list is not evidence that every known vulnerability has been fixed.

What the signature does not prove

  • Not that the details are true. The manifest is a statement by the operator about itself. The signature only shows that it has not changed since signing.
  • Not where it comes from, as long as you know the key only from here. Anyone able to alter this website could swap the key and the fingerprint along with it. Only a fingerprint you know through another channel establishes the origin. One you noted earlier at least shows whether the same key is signing as last time.
  • Not an audit. The signature replaces neither checks of contracts and data centres nor an external security or privacy audit.
  • Not availability. A snapshot says nothing about whether the website was reachable yesterday.
  • Not a supply chain. A server's addresses do not establish a complete supply chain, and the bills of materials do not record every library inside a program.
  • Nothing about the mail service. The manifest covers only the website and its server.

Further reading

The downloads are under Transparency, the software and the limits of the bills of materials under Open source. The transparency files and their signature are in scope for vulnerability reports. The post A website without JavaScript and without third-party servers follows on from this one.