Nix Hash: Difference between revisions

From NixOS Wiki
imported>Piegamesde
Corrected SRI hash definition
imported>Sternenseemann
Add in depth comparison between nix hash{-path,-file}, nix to-{sri,base32,base16,base64} and nix-hash and correct mistake about nix-hash not supporting sha512
Line 41: Line 41:
=== Tools ===
=== Tools ===


The tool of choice for hashing is <code>nix-hash</code>, although it will be deprecated [https://github.com/NixOS/nix/issues/1191#issuecomment-273839319 one day]. The new alternatives are [[Nix_command/hash-file]] and [[Nix_command/hash-path]], which however are "EXPERIMENTAL and subject to change". <code>nix-hash</code> offers the <code>--to-base32</code> flag (for which [[Nix_command/to-sri]] is the new alternative) that takes in a hex hash and converts that hash to custom-base32. On the other hand, <code>nix-hash</code> does not support the new <code>sha512</code> algorithm or the base64 encoding.
The tool of choice for hashing is <code>nix-hash</code>, although it will be deprecated [https://github.com/NixOS/nix/issues/1191#issuecomment-273839319 one day] and replaced by subcommands of the [[Nix_command]]. Below is a comparison between the current <code>nix-hash</code> and the '''experimental''' replacements which already can be used today, but are subject to change.
 
{|class="wikitable"
!nix-hash
!nix command
!explanation
|-
| <code>nix-hash --flat --type $HASHTYPE</code>
| <code>nix hash-file --base16 --type $HASHTYPE</code>, see [[Nix_command/hash-file]]
| Hash a file by using a “flat” hash which directly hashes a file and behaves like the <code>{md5,sha1,sha256,sha512}sum</code> utilities.
|-
| <code>nix-hash --flat --base32 --type $HASHTYPE</code>
| <code> nix hash-file --base32 --type $HASHTYPE</code>
| Like above, but with the more used base32 output.
|-
| <code>nix-hash --type $HASHTYPE</code>
| <code> nix hash-path --base16 --type $HASHTYPE</code>, see [[Nix_command/hash-path]]
| Compute the hash of a given path's dump in the NAR format.
|-
| <code>nix-hash --base32 --type $HASHTYPE</code>
| <code> nix hash-path --base32 --type $HASHTYPE</code>
| Like above, but with the more common base32 representation.
|-
| <code>nix-hash --to-base32 --type $HASHTYPE</code>
| <code>nix to-base32 --type $HASHTYPE</code>, see [[Nix_command/to-base32]]
| Convert a hash of <code>$HASHTYPE</code> to its (nix-specific) base32 representation.
|-
| <code>nix-hash --to-base16 --type $HASHTYPE</code>
| <code>nix to-base16 --type $HASHTYPE</code>, see [[Nix_command/to-base16]]
| Convert a hash of <code>$HASHTYPE</code> to its (nix-specific) base32 representation.
|-
| (not supported)
| <code>nix to-sri --type $HASHTYPE</code>, see [[Nix_command/to-sri]]
| Convert a hash of <code>$HASHTYPE</code> to its SRI representation.
|-
| (not supported)
| <code>nix to-base64 --type $HASHTYPE</code>, see [[Nix_command/to-base64]]
| Convert a hash of <code>$HASHTYPE</code> to its base64 representation which is the SRI representation without the hash type indication.
|}
 
<code>$HASHTYPE</code> is either <code>md5</code> (deprecated in nixpkgs), <code>sha1</code>, <code>sha256</code> (current nixpkgs standard) or <code>sha512</code> ([https://github.com/NixOS/nix/issues/1191#issuecomment-273839319 candidate for the next standard hash]).  The main differences between <code>nix-hash</code> and the <code>nix</code> subcommands is the lack of support for SRI and base64 in the former and the lack of stability in the latter. The defaults settings for the two tools are as follows:
 
{|class="wikitable"
!setting
!nix-hash default
! nix command default
|-
| output format
| base16
| SRI with base64 hash representation
|-
| hash algorithm
| md5
| sha256
|}


When dealing with remote files, <code>nix-prefetch-url</code> offers a handy shortcut for downloading the file into the Nix store and printing out its hash. (<code>nix-prefetch-url --unpack</code> is its <code>fetchzip</code> equivalent.)
When dealing with remote files, <code>nix-prefetch-url</code> offers a handy shortcut for downloading the file into the Nix store and printing out its hash. (<code>nix-prefetch-url --unpack</code> is its <code>fetchzip</code> equivalent.)

Revision as of 17:44, 14 January 2021

Hashes in Nix

Cryptographic hashes play an essential role in a lot of places in the Nix ecosystem. When using a hash somewhere, two criteria are essential to do so properly: the algorithm used and the encoding (and, to some extend, what is hashed).

Supported algorithms are md5, sha1, sha256, sha512. The first two are deprecated and should not be used anymore, but you may still stumble upon them in existing code.

A hash – which is simply a sequence of bytes – is usually encoded in order to be representable as string. Common encodings are base16 (commonly called "hex"), base32 and base64. Note that the base32 is a custom one that is not documented nor standardized in any way! If possible, use the provided hashing tools to convert hashes to it (see below). base32 is used by Nix in a lot of places because it is shorter than hex but can still safely be part of a file path (as it contains no slashes).

Usage

Many derivations are so-called fixed-output derivations, meaning that you need to know and specify the hash of the output in advance. As an example, let's look at fetchurl:

src = fetchurl {
  url = "https://example.org/downloads/source-code.zip";
  sha256 = "1g6ycnji10q5dd0avm6bz4lqpif82ppxjjq4x7vd8xihpgg3dm91";
};

You can specify the hash in any base that's supported. Thus, sha256 = "21d536debb3076d4f6e9044bd9ef15c8c58b29f9cbd4ad406b058310a565debc"; is equally allowed.

An alternative – and supposedly preferred – way of specifying hashes are so-called "SRI hashes". They're pretty simple, as the hash contains the algorithm used and always is in base64:

src = fetchurl {
  url = "https://example.org/downloads/source-code.zip";
  hash = "sha256-IdU23rswdtT26QRL2e8VyMWLKfnL1K1AawWDEKVl3rw=";
};

If you find a hash that uses colon as a separator (<type>:<hash>), don't use that. This relies on undocumented behavior and is not officially supported.

What exactly is hashed

Some content can either be hashed "flat" or "recursively". "flat" (sometimes also called "file") is simply taking the hash of the file, byte by byte, and will give you the same result as for example `sha256sum -b myfile.zip`. "recursive" (or sometimes "path") hashing takes multiple files, path names and metadata (attributes) into consideration. It works by NARing the input before hashing.

For fetchurl, the option to switch between both is called recursiveHash and defaults to false.

fetchzip on the other hand will download the file, unzip it and then recursively hash the output. There's no option.

Tools

The tool of choice for hashing is nix-hash, although it will be deprecated one day and replaced by subcommands of the Nix_command. Below is a comparison between the current nix-hash and the experimental replacements which already can be used today, but are subject to change.

nix-hash nix command explanation
nix-hash --flat --type $HASHTYPE nix hash-file --base16 --type $HASHTYPE, see Nix_command/hash-file Hash a file by using a “flat” hash which directly hashes a file and behaves like the {md5,sha1,sha256,sha512}sum utilities.
nix-hash --flat --base32 --type $HASHTYPE nix hash-file --base32 --type $HASHTYPE Like above, but with the more used base32 output.
nix-hash --type $HASHTYPE nix hash-path --base16 --type $HASHTYPE, see Nix_command/hash-path Compute the hash of a given path's dump in the NAR format.
nix-hash --base32 --type $HASHTYPE nix hash-path --base32 --type $HASHTYPE Like above, but with the more common base32 representation.
nix-hash --to-base32 --type $HASHTYPE nix to-base32 --type $HASHTYPE, see Nix_command/to-base32 Convert a hash of $HASHTYPE to its (nix-specific) base32 representation.
nix-hash --to-base16 --type $HASHTYPE nix to-base16 --type $HASHTYPE, see Nix_command/to-base16 Convert a hash of $HASHTYPE to its (nix-specific) base32 representation.
(not supported) nix to-sri --type $HASHTYPE, see Nix_command/to-sri Convert a hash of $HASHTYPE to its SRI representation.
(not supported) nix to-base64 --type $HASHTYPE, see Nix_command/to-base64 Convert a hash of $HASHTYPE to its base64 representation which is the SRI representation without the hash type indication.

$HASHTYPE is either md5 (deprecated in nixpkgs), sha1, sha256 (current nixpkgs standard) or sha512 (candidate for the next standard hash). The main differences between nix-hash and the nix subcommands is the lack of support for SRI and base64 in the former and the lack of stability in the latter. The defaults settings for the two tools are as follows:

setting nix-hash default nix command default
output format base16 SRI with base64 hash representation
hash algorithm md5 sha256

When dealing with remote files, nix-prefetch-url offers a handy shortcut for downloading the file into the Nix store and printing out its hash. (nix-prefetch-url --unpack is its fetchzip equivalent.)

Libraries

Further reading

  • Eelco Dolstra's phd thesis, section 5.1.
  • Github Issue about which encoding is used where, and what pitfalls can arise from it.