Where are the cryptographic signatures? (SHA256SUMS, Release.gpg)

Can someone please help me understand how nix ensures the authenticity of everything that it downloads (before building/installing) using cryptographic signatures?

Specifically, I come from Debian (in peace). I’m used to apt. Perhaps it’s my bias, but I think it’s really easy to understand how apt ensures the cryptographic authenticity of its packages:

  1. An apt repo is just a simple http (or ftp) server. You can view the URLs of these repos by viewing the contents of your /etc/apt/ directory. For example, Index of /debian/dists/bookworm
  2. We can just open the above URL in our web-browser, and see the files Release and Release.gpg. We can download both these files and confirm the authenticity of Release file with gpg.
  3. The release file (now verified as authentic, even if https is compromised) contains the cryptographic hashes of all the other files on this http server. For example, main/binary-amd64/Packages.gz
  4. The Packages.gz file contains the cryptographic hashes of all our actual .deb files.

From ^ this, it’s easy for a user to simply download the signed files using their web browser and see how Debian confirms the authenticity of all the packages.

I cannot figure out how to do the same with Nix.

Nix documentation promises that it has cryptographic signatures enabled by default. That’s great, but how do I view and actually test this (like above)?

Example

Let’s consider the package vim.

In Debian

In Debian, I search my /etc/apt/ dir for the repos that I’m using. For example, we find Index of /debian

user@debian:~$ grep -ir deb /etc/apt
...
/etc/apt/sources.list:deb http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware
...
user@debian:~$ 

Then I can just copy & paste this URL (Index of /debian) and see some directories. If I click on the dists/ directory, then I see a list of versions. The file above indicates we’re using bookworm (the codename for Debian 12), so I again click on the bookworm/ directory in the web browser. Finally, I see the Release and Release.gpg files.

We can copy & paste the URL of these files from the web browser and then download them on the command line (for clarity)

wget https://ftp.debian.org/debian/dists/bullseye/Release
wget https://ftp.debian.org/debian/dists/bullseye/Release.gpg

Now we can verify the authenticity of this file cryptographically with gpg

user@debian:~$ gpg --import /etc/apt/trusted.gpg.d/*
...
user@debian:~$ 

user@debian:~$ gpg --verify Release.gpg Release
gpg: Signature made Sat 09 Nov 2024 05:11:43 AM -05
gpg:                using RSA key A7236886F3CCCAAD148A27F80E98404D386FA1D9
gpg: Good signature from "Debian Archive Automatic Signing Key (11/bullseye) <ftpmaster@debian.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 1F89 983E 0081 FDE0 18F3  CC96 73A4 F27B 8DD4 7936
     Subkey fingerprint: A723 6886 F3CC CAAD 148A  27F8 0E98 404D 386F A1D9
gpg: Signature made Sat 09 Nov 2024 05:11:43 AM -05
gpg:                using RSA key 4CB50190207B4758A3F73A796ED0E7B82643E131
gpg: Good signature from "Debian Archive Automatic Signing Key (12/bookworm) <ftpmaster@debian.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: B8B8 0B5B 623E AB6A D877  5C45 B7C5 D7D6 3509 47F8
     Subkey fingerprint: 4CB5 0190 207B 4758 A3F7  3A79 6ED0 E7B8 2643 E131
gpg: Signature made Sat 09 Nov 2024 05:14:43 AM -05
gpg:                using EDDSA key 4D64FEC119C2029067D6E791F8D2585B8783D481
gpg:                issuer "debian-release@lists.debian.org"
gpg: Good signature from "Debian Stable Release Key (12/bookworm) <debian-release@lists.debian.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 4D64 FEC1 19C2 0290 67D6  E791 F8D2 585B 8783 D481
user@debian:~$ 

Only now that we’ve confirmed the file is authentic, we can trust its file content’s cryptographic hashes. Here we have the MD5 and SHA256 checksums for the “main” packages for amd64 architecture:

user@debian:~$ grep main/binary-amd64/Packages.gz Release
 63997849d2fd6076c844ddf0622f60f2 12079357 main/binary-amd64/Packages.gz
 2f674d057c5f274c5a863664a586ef62a0deb571993914ccfe4e2cd784a4840d 12079357 main/binary-amd64/Packages.gz
user@debian:~$ 

Back in the web browser, we can click-through the main/ and binary-amd64/ directories to download the Packages.gz file

wget https://ftp.debian.org/debian/dists/stable/main/binary-amd64/Packages.gz

And get its SHA256 checksum:

user@debian:~$ sha256sum Packages.gz
2f674d057c5f274c5a863664a586ef62a0deb571993914ccfe4e2cd784a4840d  Packages.gz
user@debian:~$ 

It’s easy to see that this file’s checksum matches the checksum listed in the Release file, whoose authenticity we verified with gpg.

Now we can get the checksum of the vim

user@debian:~$ gunzip Packages.gz
user@debian:~$ 

user@debian:~$ grep -EA 20 'Package: vim$' Packages
Package: vim
Version: 2:9.0.1378-2
Installed-Size: 3650
Maintainer: Debian Vim Maintainers <team+vim@tracker.debian.org>
Architecture: amd64
Provides: editor
Depends: vim-common (= 2:9.0.1378-2), vim-runtime (= 2:9.0.1378-2), libacl1 (>= 2.2.23), libc6 (>= 2.34), libgpm2 (>= 1.20.7), libselinux1 (>= 3.1~), libsodium23 (>= 1.0.14), libtinfo6 (>= 6)
Suggests: ctags, vim-doc, vim-scripts
Description: Vi IMproved - enhanced vi editor
Homepage: https://www.vim.org/
Description-md5: 59e8b8f7757db8b53566d5d119872de8
Tag: devel::editor, implemented-in::c, interface::commandline,
 interface::text-mode, role::program, scope::application,
 uitoolkit::ncurses, use::editing, works-with::text, works-with::unicode
Section: editors
Priority: optional
Filename: pool/main/v/vim/vim_9.0.1378-2_amd64.deb
Size: 1567304
MD5sum: cdcefcf9e364332e6ecef182976e096e
SHA256: 1ca56e3724191401f6598890bfbe3655174934fa3735ec2fb55f38b100087d46

user@debian:~$ 

And we now see it’s possible to download vim_9.0.1378-2_amd64.deb and confirm its checksum, which can be trusted because of the gpg verification from above.

In NixOS

It looks like here is the file info for the vim package in nix:

…but I can’t find any signatures, nor find any documentation that spells out how nix actually verifies the cryptographic authenticity of an example package (like vim).

How can I download the vim package from nix and verify its signatures in bash the same as nix does it?

1 Like

See also:

You will either build it from sources or pull the pre-built binary from a binary cache like cache.nixos.org. In this case a combination of the path and a hash of the content gets signed.

You can verify the integrity and signature of a path in your store using nix store verify, see nix store verify - Nix Reference Manual

4 Likes

Can you please paste a hyperlink in this thread to:

  1. The signature of the sources
  2. The signature of the binary

for one example package (eg vim)?

I want to actually see it and reproduce the process myself (without using the nix command), to confirm it’s not smoke & mirrors. I also think this would be beneficial to the community, in general.

The signature for a given store path is in its .narinfo file on cache.nixos.org; so on this system, currently:

ls -l /run/current-system/sw/bin/vim
lrwxrwxrwx 1 root root 64 Jan  1  1970 /run/current-system/sw/bin/vim -> /nix/store/zr70kfh830qgwz4ld8c3fc01va8wd7va-vim-9.1.0765/bin/vim
curl https://cache.nixos.org/zr70kfh830qgwz4ld8c3fc01va8wd7va.narinfo

In the narinfo there will be one or more lines starting Sig: :-

StorePath: /nix/store/zr70kfh830qgwz4ld8c3fc01va8wd7va-vim-9.1.0765
URL: nar/1p5yhz7x8mnbi2i6g0aij4fm66gmivvqmby0d8n2ww48izdb5asd.nar.xz
Compression: xz
FileHash: sha256:1p5yhz7x8mnbi2i6g0aij4fm66gmivvqmby0d8n2ww48izdb5asd
FileSize: 8562616
NarHash: sha256:1a9wyq4y9klqi4lz9z4az9x6q3lqjypgbgcklyy3x1c2v3zlxfww
NarSize: 42777576
References: ci63zxxcf7dlfx25g8g5971r0cdx7nj1-gawk-5.2.2 k2wspf0nq81hxjwxsnb8r9cg08qngdk2-bash-5.2p32 mcln7vd9r6p72s6m1lm1jbjnmfbwxj3l-ncurses-6.4.20221231 qii6jadf3xrhg4y0f5m09k11lh5ymwnv-glibc-2.40-36 zr70kfh830qgwz4ld8c3fc01va8wd7va-vim-9.1.0765
Deriver: drzvdhhy60vw5vxp4avzbiqzim9y54d0-vim-9.1.0765.drv
Sig: cache.nixos.org-1:HemTnbuCmk1bg9cQRxUvgalZbZgcgKU7x1ETGOdAIZksyn3sre6SCwtVVrSo4XsDy4HsuQzSKmNXw1UN0HwJBQ==
1 Like

For vim its not that easy, as the “build recipe” is a bit convoluted…

Though if I read it correctly the source hash is mentioned at nixpkgs/pkgs/applications/editors/vim/common.nix at f6a2e6accde6d330de111be91056638e123c771c · NixOS/nixpkgs · GitHub.

The signage of the build artiefact is harder. As even though the version of a package like vim might remain untouched for a while, it will be rebuilt every time any of its dependencies changes, and then that built artifact is provided via a global build cache.

This artefact is signed by the private key of the build farm and its keys are trusted by default, though if you really want to, you can untrust it, or remove the cache at all, in which case you would build from source transparently.

With nix, the general assumption is, that building from source and using the artifact from the cache are the same thing.

This is not apt, where a program gets built after a release and a DEB is provided untouched for the next 5 years till a new release appears. Nix is fundamentally different, and much more closer to make than to apt in how it works.

2 Likes

If you want to verify these by hand, I’ve used vi/curve25519tool successfully in the past. The cache.nixos.org public key is here and needs turning into hex:

echo '6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' | base64 -d | xxd -ps -c 999
e8d087743e7d5f8df5a34816ca96ccac051191b275e993cc4051aca5c0d28636

Then you need to do the same the signature in the narinfo; using the above vim example:

echo 'HemTnbuCmk1bg9cQRxUvgalZbZgcgKU7x1ETGOdAIZksyn3sre6SCwtVVrSo4XsDy4HsuQzSKmNXw1UN0HwJBQ==' | base64 -d | xxd -ps -c 9999
1de9939dbb829a4d5b83d71047152f81a9596d981c80a53bc7511318e74021992cca7decadee920b0b5556b4a8e17b03cb81ecb90cd22a6357c3550dd07c0905

Finally verify the signature over the narinfo ‘fingerprint’ which is “1;<store path>;<narhash>;<narsize>;<comma separated references>”:

echo -n "1;/nix/store/zr70kfh830qgwz4ld8c3fc01va8wd7va-vim-9.1.0765;sha256:1a9wyq4y9klqi4lz9z4az9x6q3lqjypgbgcklyy3x1c2v3zlxfww;42777576;"\
"/nix/store/ci63zxxcf7dlfx25g8g5971r0cdx7nj1-gawk-5.2.2,/nix/store/k2wspf0nq81hxjwxsnb8r9cg08qngdk2-bash-5.2p32,/nix/store/mcln7vd9r6p72s6m1lm1jbjnmfbwxj3l-ncurses-6.4.20221231,"\
"/nix/store/qii6jadf3xrhg4y0f5m09k11lh5ymwnv-glibc-2.40-36,/nix/store/zr70kfh830qgwz4ld8c3fc01va8wd7va-vim-9.1.0765"  | \
 ./curve25519tool_linux64 verify e8d087743e7d5f8df5a34816ca96ccac051191b275e993cc4051aca5c0d28636 \
1de9939dbb829a4d5b83d71047152f81a9596d981c80a53bc7511318e74021992cca7decadee920b0b5556b4a8e17b03cb81ecb90cd22a6357c3550dd07c0905  \
 --ed && echo ok

the source hash is mentioned at nixpkgs/pkgs/applications/editors/vim/common.nix at f6a2e6accde6d330de111be91056638e123c771c · NixOS/nixpkgs · GitHub.

I saw that too, but that checksum hash can’t be trusted unless it’s signed. I couldn’t find the signature in that repo.

With regard to an equivalent of source signing, that’s a little murkier. The source hashes end up in nixpkgs, but the nixpkgs ‘channel’ doesn’t seem to be explicitly signed; various bits of discussion I found:

Its a content hash, not a signature.

If you can not trust your package repo, you can not trust anyone.

Also why should we sign upstreams sources just for internal verification? Upstream doesn’t sign in most cases, so who cares…

1 Like

Sorry, you lost me at this. Where did you get HemTnbuCmk1bg9cQRxUvgalZbZgcgKU7x1ETGOdAIZksyn3sre6SCwtVVrSo4XsDy4Hs?

It’s in the Sig: line in the .narinfo:

curl -s https://cache.nixos.org/zr70kfh830qgwz4ld8c3fc01va8wd7va.narinfo | grep ^Sig:
Sig: cache.nixos.org-1:HemTnbuCmk1bg9cQRxUvgalZbZgcgKU7x1ETGOdAIZksyn3sre6SCwtVVrSo4XsDy4HsuQzSKmNXw1UN0HwJBQ==
2 Likes

How did you determine this URL?

It’s the hash from the store path for vim on the system I happen to be using - yours will likely be different, if for no other reason than this is an aarch64 machine :slight_smile:

ok, so there’s no way to figure out the URL without installing nix? That’s unfortunate.

I did just install nix with apt-get install nix-bin on Debian 12, but I have no idea what I’m doing. It yells at me if I try nix search vim.

user@disp1974:~$ nix search vim
error: experimental Nix feature 'nix-command' is disabled; use '--extra-experimental-features nix-command' to override
user@disp1974:~$ 

Can you please walk me through how I can figure out the cache.nixos.org URL for my arch? Without nix would be preferred, but I’m willing to install and use the nix cli tool, if it’s necessary.

The store paths are going to change pretty frequently - each time the package or one of its run- or build-time dependencies change. Also with nix you can reasonably easily patch the whole nixpkgs tree (overlays) - if you happened to fiddle with a package that was a dependency that would also shift all the hashes.

That having been said, you might be able to search hydra.nixos.org, but I’m still not entirely comfortable that I know my way around it :slight_smile:

1 Like

Digging around on hydra:
Hydra - nixpkgs:trunk:vim.aarch64-linux - is the job that builds the default vim package for nixpkgs-unstable for aarch64, and it looks like my machine ended up with Hydra - Build 275023271 of job nixpkgs:trunk:vim.aarch64-linux - you can see one of the output store paths has the same hash as above.

1 Like

I see how you jumped from the first URL (Hydra - nixpkgs:trunk:vim.aarch64-linux) to the second (Hydra - Build 275023271 of job nixpkgs:trunk:vim.aarch64-linux), but how did you find the first URL?

You can search the job list here, but it’s huge. I’m not entirely sure if the package names map 1:1 to job names - I think it’s more likely they’re the “attribute names”, I get a bit muddled about that. search.nixos.org shows both, I think. Obviously the second component of the job name is the architecture.

Regarding the jobsets themselves, I’m pretty sure hydra.nixos.org/jobset/nixpkgs/trunk is the jobset for nixpkgs-unstable, and that Hydra - nixos:release-24.11 is for 24.11, etc.

ok, that helps. So:

  1. I load this page https://hydra.nixos.org/jobset/nixpkgs/trunk#tabs-jobs
  2. I type the name of the package (“vim”) in the search input field (not the one at the top, the one auto-filled with Search jobs by name...) and click the Show inactive jobs button
  3. I scroll-down until I see vim-full.x86_64-linux. I click on it, which brings me to Hydra - nixpkgs:trunk:vim-full.x86_64-linux
  4. I click on the latest job id at the top
  5. I click on the Details tab
  6. I copy the first line on ^ this page after Output store paths: (eg /nix/store/v5rz9y25dapc1j6q2xmgawgh5fgfa59q-vim-9.1.0905)
  7. I just extract the hash from the above string (eg v5rz9y25dapc1j6q2xmgawgh5fgfa59q)
  8. I prepend https://cache.nixos.org/ and append .narinfo to the above string (eg https://cache.nixos.org/v5rz9y25dapc1j6q2xmgawgh5fgfa59q.narinfo)
  9. I load ^ that URL in the browser, and look for the line that starts with Sig: . That’s the signature.
  10. I can then confirm this signature using the public key found here nixpkgs/nixos/modules/services/misc/nix-daemon.nix at 1f949558617ebb18bbf7005c1c4dc3407d391e93 · NixOS/nixpkgs · GitHub

That URL above also includes the path to the vim binary, and its checksum (FileHash). Since we can verify the signature of this metadata, we can then safely download the binary, confirm its checksum matches the expected, signed, verified and now-trusted checksum.

I still need to actually execute it, but at least I can now see the signatures and the process. Thanks!

So that does solve the question about binaries, but what about sources?

Is this to suggest that nix relies on TLS alone for packages built from sources? So, for example, if an APT had a compromised CA in the mozilla root store, then they’d be able to successfully do a MITM attack and inject malicious sourcecode that was downloaded by nix?

Or does nix (by default, for all packages) provide some protections against that (eg signatures)?