aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2018-12-13 14:30:52 +0100
committerEelco Dolstra <edolstra@gmail.com>2018-12-13 14:30:52 +0100
commit6024dc1d97212130c19d3ff5ce6b1d102837eee6 (patch)
tree71ad514b53863fc00ed4b8cff710dec1e792b056
parentc37e6d77ea86df249aeaf65d329a6be3b837ad62 (diff)
Support SRI hashes
SRI hashes (https://www.w3.org/TR/SRI/) combine the hash algorithm and a base-64 hash. This allows more concise and standard hash specifications. For example, instead of import <nix/fetchurl.nl> { url = https://nixos.org/releases/nix/nix-2.1.3/nix-2.1.3.tar.xz; sha256 = "5d22dad058d5c800d65a115f919da22938c50dd6ba98c5e3a183172d149840a4"; }; you can write import <nix/fetchurl.nl> { url = https://nixos.org/releases/nix/nix-2.1.3/nix-2.1.3.tar.xz; hash = "sha256-XSLa0FjVyADWWhFfkZ2iKTjFDda6mMXjoYMXLRSYQKQ="; }; In fixed-output derivations, the outputHashAlgo is no longer mandatory if outputHash specifies the hash (either as an SRI or in the old "<type>:<hash>" format). 'nix hash-{file,path}' now print hashes in SRI format by default. I also reverted them to use SHA-256 by default because that's what we're using most of the time in Nixpkgs. Suggested by @zimbatm.
-rw-r--r--corepkgs/fetchurl.nix8
-rw-r--r--src/libexpr/primops.cc10
-rw-r--r--src/libutil/hash.cc34
-rw-r--r--src/libutil/hash.hh7
-rw-r--r--src/nix-store/nix-store.cc3
-rw-r--r--src/nix/hash.cc23
-rw-r--r--tests/fetchurl.sh11
-rw-r--r--tests/hash.sh12
8 files changed, 73 insertions, 35 deletions
diff --git a/corepkgs/fetchurl.nix b/corepkgs/fetchurl.nix
index 0ce1bab11..a84777f57 100644
--- a/corepkgs/fetchurl.nix
+++ b/corepkgs/fetchurl.nix
@@ -1,10 +1,14 @@
{ system ? "" # obsolete
, url
+, hash ? "" # an SRI ash
+
+# Legacy hash specification
, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""
, outputHash ?
- if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
+ if hash != "" then hash else if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
, outputHashAlgo ?
- if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
+ if hash != "" then "" else if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
+
, executable ? false
, unpack ? false
, name ? baseNameOf (toString url)
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 7372134e2..60698f740 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -724,16 +724,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (outputs.size() != 1 || *(outputs.begin()) != "out")
throw Error(format("multiple outputs are not supported in fixed-output derivations, at %1%") % posDrvName);
- HashType ht = parseHashType(outputHashAlgo);
- if (ht == htUnknown)
- throw EvalError(format("unknown hash algorithm '%1%', at %2%") % outputHashAlgo % posDrvName);
+ HashType ht = outputHashAlgo.empty() ? htUnknown : parseHashType(outputHashAlgo);
Hash h(*outputHash, ht);
- outputHash = h.to_string(Base16, false);
- if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
Path outPath = state.store->makeFixedOutputPath(outputHashRecursive, h, drvName);
if (!jsonObject) drv.env["out"] = outPath;
- drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, *outputHash);
+ drv.outputs["out"] = DerivationOutput(outPath,
+ (outputHashRecursive ? "r:" : "") + printHashType(h.type),
+ h.to_string(Base16, false));
}
else {
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc
index 9d82f13a5..1c14ebb18 100644
--- a/src/libutil/hash.cc
+++ b/src/libutil/hash.cc
@@ -105,9 +105,9 @@ string printHash16or32(const Hash & hash)
std::string Hash::to_string(Base base, bool includeType) const
{
std::string s;
- if (includeType) {
+ if (base == SRI || includeType) {
s += printHashType(type);
- s += ':';
+ s += base == SRI ? '-' : ':';
}
switch (base) {
case Base16:
@@ -117,6 +117,7 @@ std::string Hash::to_string(Base base, bool includeType) const
s += printHash32(*this);
break;
case Base64:
+ case SRI:
s += base64Encode(std::string((const char *) hash, hashSize));
break;
}
@@ -127,28 +128,33 @@ std::string Hash::to_string(Base base, bool includeType) const
Hash::Hash(const std::string & s, HashType type)
: type(type)
{
- auto colon = s.find(':');
-
size_t pos = 0;
-
- if (colon == string::npos) {
- if (type == htUnknown)
+ bool isSRI = false;
+
+ auto sep = s.find(':');
+ if (sep == string::npos) {
+ sep = s.find('-');
+ if (sep != string::npos) {
+ isSRI = true;
+ } else if (type == htUnknown)
throw BadHash("hash '%s' does not include a type", s);
- } else {
- string hts = string(s, 0, colon);
+ }
+
+ if (sep != string::npos) {
+ string hts = string(s, 0, sep);
this->type = parseHashType(hts);
if (this->type == htUnknown)
throw BadHash("unknown hash type '%s'", hts);
if (type != htUnknown && type != this->type)
throw BadHash("hash '%s' should have type '%s'", s, printHashType(type));
- pos = colon + 1;
+ pos = sep + 1;
}
init();
size_t size = s.size() - pos;
- if (size == base16Len()) {
+ if (!isSRI && size == base16Len()) {
auto parseHexDigit = [&](char c) {
if (c >= '0' && c <= '9') return c - '0';
@@ -164,7 +170,7 @@ Hash::Hash(const std::string & s, HashType type)
}
}
- else if (size == base32Len()) {
+ else if (!isSRI && size == base32Len()) {
for (unsigned int n = 0; n < size; ++n) {
char c = s[pos + size - n - 1];
@@ -187,10 +193,10 @@ Hash::Hash(const std::string & s, HashType type)
}
}
- else if (size == base64Len()) {
+ else if (isSRI || size == base64Len()) {
auto d = base64Decode(std::string(s, pos));
if (d.size() != hashSize)
- throw BadHash("invalid base-64 hash '%s'", s);
+ throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", s);
assert(hashSize);
memcpy(hash, d.data(), hashSize);
}
diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh
index fd7a61df8..2dbc3b630 100644
--- a/src/libutil/hash.hh
+++ b/src/libutil/hash.hh
@@ -20,7 +20,7 @@ const int sha512HashSize = 64;
extern const string base32Chars;
-enum Base : int { Base64, Base32, Base16 };
+enum Base : int { Base64, Base32, Base16, SRI };
struct Hash
@@ -38,8 +38,9 @@ struct Hash
Hash(HashType type) : type(type) { init(); };
/* Initialize the hash from a string representation, in the format
- "[<type>:]<base16|base32|base64>". If the 'type' argument is
- htUnknown, then the hash type must be specified in the
+ "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
+ Subresource Integrity hash expression). If the 'type' argument
+ is htUnknown, then the hash type must be specified in the
string. */
Hash(const std::string & s, HashType type = htUnknown);
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index a9ad14762..5b37237eb 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -1000,6 +1000,9 @@ static int _main(int argc, char * * argv)
Strings opFlags, opArgs;
Operation op = 0;
+ Hash h("sha512-Q2bFTOhEALkN8hOms2FKTDLy7eugP2zFZ1T8LCvX42Fp3WoNr3bjZSAHeOsHrbV1Fu9/A0EzCinRE7Af1ofPrw==");
+ printError("GOT HASH %s", h.to_string(Base64));
+
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
Operation oldOp = op;
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
index 64062fb97..af4105e28 100644
--- a/src/nix/hash.cc
+++ b/src/nix/hash.cc
@@ -9,13 +9,14 @@ struct CmdHash : Command
{
enum Mode { mFile, mPath };
Mode mode;
- Base base = Base16;
+ Base base = SRI;
bool truncate = false;
- HashType ht = htSHA512;
+ HashType ht = htSHA256;
std::vector<std::string> paths;
CmdHash(Mode mode) : mode(mode)
{
+ mkFlag(0, "sri", "print hash in SRI format", &base, SRI);
mkFlag(0, "base64", "print hash in base-64", &base, Base64);
mkFlag(0, "base32", "print hash in base-32 (Nix-specific)", &base, Base32);
mkFlag(0, "base16", "print hash in base-16", &base, Base16);
@@ -43,7 +44,7 @@ struct CmdHash : Command
Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first;
if (truncate && h.hashSize > 20) h = compressHash(h, 20);
std::cout << format("%1%\n") %
- h.to_string(base, false);
+ h.to_string(base, base == SRI);
}
}
};
@@ -54,7 +55,7 @@ static RegisterCommand r2(make_ref<CmdHash>(CmdHash::mPath));
struct CmdToBase : Command
{
Base base;
- HashType ht = htSHA512;
+ HashType ht = htUnknown;
std::vector<std::string> args;
CmdToBase(Base base) : base(base)
@@ -70,26 +71,30 @@ struct CmdToBase : Command
return
base == Base16 ? "to-base16" :
base == Base32 ? "to-base32" :
- "to-base64";
+ base == Base64 ? "to-base64" :
+ "to-sri";
}
std::string description() override
{
- return fmt("convert a hash to base-%d representation",
- base == Base16 ? 16 :
- base == Base32 ? 32 : 64);
+ return fmt("convert a hash to %s representation",
+ base == Base16 ? "base-16" :
+ base == Base32 ? "base-32" :
+ base == Base64 ? "base-64" :
+ "SRI");
}
void run() override
{
for (auto s : args)
- std::cout << fmt("%s\n", Hash(s, ht).to_string(base, false));
+ std::cout << fmt("%s\n", Hash(s, ht).to_string(base, base == SRI));
}
};
static RegisterCommand r3(make_ref<CmdToBase>(Base16));
static RegisterCommand r4(make_ref<CmdToBase>(Base32));
static RegisterCommand r5(make_ref<CmdToBase>(Base64));
+static RegisterCommand r6(make_ref<CmdToBase>(SRI));
/* Legacy nix-hash command. */
static int compatNixHash(int argc, char * * argv)
diff --git a/tests/fetchurl.sh b/tests/fetchurl.sh
index 9bbf044f7..d51d081f5 100644
--- a/tests/fetchurl.sh
+++ b/tests/fetchurl.sh
@@ -18,6 +18,17 @@ outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh
cmp $outPath fetchurl.sh
+# Now using an SRI hash.
+clearStore
+
+hash=$(nix hash-file ./fetchurl.sh)
+
+[[ $hash =~ ^sha512- ]]
+
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr hash $hash --no-out-link --hashed-mirrors '')
+
+cmp $outPath fetchurl.sh
+
# Test the hashed mirror feature.
clearStore
diff --git a/tests/hash.sh b/tests/hash.sh
index 9f234bc63..4cfc97901 100644
--- a/tests/hash.sh
+++ b/tests/hash.sh
@@ -2,7 +2,7 @@ source common.sh
try () {
printf "%s" "$2" > $TEST_ROOT/vector
- hash=$(nix-hash $EXTRA --flat --type "$1" $TEST_ROOT/vector)
+ hash=$(nix hash-file --base16 $EXTRA --type "$1" $TEST_ROOT/vector)
if test "$hash" != "$3"; then
echo "hash $1, expected $3, got $hash"
exit 1
@@ -33,6 +33,12 @@ EXTRA=--base32
try sha256 "abc" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
EXTRA=
+EXTRA=--sri
+try sha512 "" "sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg=="
+try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw=="
+try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha512-IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
+try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha256-JI1qYdIGOLjlwCaTDD5gOaM85Flk/yFn9uzt1BnbBsE="
+
try2 () {
hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path)
if test "$hash" != "$2"; then
@@ -65,12 +71,16 @@ try2 md5 "f78b733a68f5edbdf9413899339eaa4a"
try3() {
h64=$(nix to-base64 --type "$1" "$2")
[ "$h64" = "$4" ]
+ sri=$(nix to-sri --type "$1" "$2")
+ [ "$sri" = "$1-$4" ]
h32=$(nix-hash --type "$1" --to-base32 "$2")
[ "$h32" = "$3" ]
h16=$(nix-hash --type "$1" --to-base16 "$h32")
[ "$h16" = "$2" ]
h16=$(nix to-base16 --type "$1" "$h64")
[ "$h16" = "$2" ]
+ h16=$(nix to-base16 "$sri")
+ [ "$h16" = "$2" ]
}
try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8="
try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="