CRUX : Home

Home :: Documentation :: Download :: Development :: Community :: Wiki :: Ports :: Bugs :: Links :: About

Back to wiki start page Categories: Development

Port Signing and Verification

Thomas Penteker


Current Situation & Issues

The current pkgutils use MD5 hashed checksums in its .md5sum files. The problem with these is threefold:

  1. while currently still safe for our use case, md5 has a load of issues and may be broken completely in the near future
  2. hashing strictly is meant to guarantee the integrity of source files as specified in the checksum file, it is not authenticated (and can be forged)
  3. Pkgfiles and .footprint files are not protected in any way

The only attacker model this really protects against is the one of accidental bit flips and the likes usually involving no attackers at all.

New Port Integrity and Authenticity Model

To not re-invent the wheel, I decided to fork and reuse the fine signify util from the OpenBSD project. pkgmk was modified to use signify transparently and similarly as the md5 mechanism, that served for many years (creating signatures if missing and private key present, check || abort during builds etc.). It fixes several problems when distributing ports. It offers cryptographic signing and verification (through ed25519) in conjunction with the more modern and robust SHA256 checksumming algorithm. Assuming the public keys were distributed securely (through HTTPS verified channels, for example), this provides very high levels of both integrity and authenticity for all port files. Keys need to be rotated if a security breach happened at one of the developer's machines:

Note that his operation can be executed really quickly, as -rs will make pkgmk really only regenerate the new signature, hashsums are used as already present in the signature files.

Key Distribution and Handling

The keys for our official repos are available at https://crux.nu/keys/ were the file name directly denotes the repository the key belongs to. Note, thtat at no time, the private keys need or may stay on the crux.nu server. These have to be distributed through a separate, secure channel. For the sake of simplicity and also flexibility, the current model makes use of exactly one public/private key pair for each repository (as opposed to a one key per maintainer model). This was a deliberate choice. Keys can be rotated on a per release basis and made available at an archive for future reference. It would be good practice, to have the keys spread to all devs' machines and another public (like github) and private (backups) sources for easier verification in case of a security breach of crux.nu. It would also be possible to sign all keys by a master key (as suggested on the mailing list).

Test-Driving the new Port Signing and Checking

  1. install contrib/signify (this is going to move to core once my proposal is accepted)
  2. get the signed snapshot of core and extract as /usr/ports/core
  3. build and install (at least pkgmk) the new pkgutils supporting signing and checking
git clone -b signed git://crux.nu/tools/pkgutils.git && cd pkgutils
make

The new version of pkgmk supports several signing-relevant options and operations:

  % pkgmk --help | grep sign
  -us,  --update-signature    update signature including sha256 checksums
  -cs,  --check-signature     check the validity of the port files
  -is,  --ignore-signature    build package without checking the signature
  -rs,  --refresh-signature   create new signature and keep existing sha256 checksums
  -sk,  --secret-key <file>   use <file> to sign the port

The -sk option is of special note as it overrides the secret key selection mechanism. While it allows to use a certain private key for signing, By default, pkgmk tries to deduct the repository name and thus the private key file name by looking at the port's parent folder. If signing takes place in .../opt/someport or ../opt.git/someport, pkgmk will automatically select and try to use /etc/ports/opt.sec for signing.

Key Handling

To create a new public/private key pair, run

  % signify -G -n -c port -p port.pub -s port.sec                                          
  % cat port.pub
  untrusted comment: port public key
  RWT+QwJzmzrGNkBZx64pjK98w3mhbx+VCGbvJusfgu4mwcKny90BMEe+
  % cat port.sec 
  untrusted comment: port secret key
  RWRCSwAAAAABwKo7Oe3xGFiZlTYuqYA+kWlQ6p53kHb+QwJzmzrGNljXUK499q4QyVHQ2dPMhEYEDUQJ72xn64tvr/J4F21oQFnHrimMr3zDeaFvH5UIZu8m6x+C7ibBwqfL3QEwR74=

It is recommended to put the public/private key pair into /etc/ports/ as repo.{pub,sec}. This bears the advantage that signatures will contain the right path to the public files. As repository names are unique in our portdb, users of repositories can put the public key into /etc/ports and pkgmk will pick them up for verification automatically. From now on, public keys are both displayed and downloadable from the port database. Take great care to keep port.sec private and distribute port.pub as much as you can.

To verify a single file against the signed list of files (the embedded format containing both the signature and the checksums is used by pkgutils) run

  core/dash% signify -C -x .signature Pkgfile 
  Signature Verified
  Pkgfile: OK
  core/dash% pkgmk -cs
  =======> Signature ok.

Note, that checking against the coplete list signed files fails as soon as you put downloaded source files in a dedicated directory (i.e. by using PKGMK_SOURCE_DIR in pkgmk.conf). pkgmk takes care to make signify succeed any way, ports can be checked by running pkgmk -cs. The same holds true for signature creation, where pkgmk will strip paths from files listed in .signature.

The embedded messages and signatures in .signature look like this:

  untrusted comment: verify with /etc/ports/core.pub
  RWRZAkQEVz8SoVWpZpkcaxB3qwKzneYEKhIXbyMq9mWdyU+v3tsG331tmpEHljXkQWfiVJtmJQH9OqM/1wLmCV3bmxnFjq0LLAg=
  SHA256 (Pkgfile) = d0590b46868ed7392481a1fbb1c32f6c61e9e31004402530ad735cf91773ee95
  SHA256 (.footprint) = 3a7bbfc94c40691a4871ab86fb334dfcbc7f98f74ed06847f091ba0b1721d540
  SHA256 (dash-20150713.tar.xz) = 0eeabfde5494bd3dda57155f996729adb3fafddffb9c89f29b5ef208788f071f

.md5sum Compatibility

The last peace of code missing in pkgmk is the code and options to control how pkgmk handles .md5sums and making signatures mandatory for the official repositories. The default should be to require signatures for future releases of pkgutils. This is now implemented in three different ways, set options in pkgmk.conf accordingly.

use package signatures only
set PKGMK_IGNORE_MD5SUM="yes" (and PKGMK_IGNORE_SIGNATURE="no" (default))
use package signatures but fall back to md5 checking if signatures are unavailable (default setting)
use defaults PKGMK_IGNORE_MD5SUM="no" and PKGMK_IGNORE_SIGNATURE="no"
use package md5 checking only
PKGMK_IGNORE_SIGNATURE="yes" (and PKGMK_IGNORE_MD5SUM="no" (default))

Future Extensions

must haves

maybe

optional