Skip to content

Commit fc83c6c

Browse files
authored
Merge pull request #12379 from silvanshade/blake3-c
Add BLAKE3 hashing algorithm
2 parents a562d0b + 7fd2125 commit fc83c6c

File tree

13 files changed

+93
-21
lines changed

13 files changed

+93
-21
lines changed

doc/manual/source/command-ref/nix-hash.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ md5sum`.
6767
- `--type` *hashAlgo*
6868

6969
Use the specified cryptographic hash algorithm, which can be one of
70-
`md5`, `sha1`, `sha256`, and `sha512`.
70+
`blake3`, `md5`, `sha1`, `sha256`, and `sha512`.
7171

7272
- `--to-base16`
7373

doc/manual/source/command-ref/nix-prefetch-url.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ the path of the downloaded file in the Nix store is also printed.
4242
- `--type` *hashAlgo*
4343

4444
Use the specified cryptographic hash algorithm,
45-
which can be one of `md5`, `sha1`, `sha256`, and `sha512`.
45+
which can be one of `blake3`, `md5`, `sha1`, `sha256`, and `sha512`.
4646
The default is `sha256`.
4747

4848
- `--print-path`

doc/manual/source/language/advanced-attributes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ Derivations can declare some infrequently used optional attributes.
192192
The [`convertHash`](@docroot@/language/builtins.md#builtins-convertHash) function shows how to convert between different encodings, and the [`nix-hash` command](../command-ref/nix-hash.md) has information about obtaining the hash for some contents, as well as converting to and from encodings.
193193
194194
The `outputHashAlgo` attribute specifies the hash algorithm used to compute the hash.
195-
It can currently be `"sha1"`, `"sha256"`, `"sha512"`, or `null`.
195+
It can currently be `"blake3", "sha1"`, `"sha256"`, `"sha512"`, or `null`.
196196
`outputHashAlgo` can only be `null` when `outputHash` follows the SRI format.
197197
198198
The `outputHashMode` attribute determines how the hash is computed.

doc/manual/source/protocols/json/derivation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ is a JSON object with the following fields:
3838
For an output which will be [content addresed], the name of the hash algorithm used.
3939
Valid algorithm strings are:
4040

41+
- `blake3`
4142
- `md5`
4243
- `sha1`
4344
- `sha256`

src/libcmd/misc-store-flags.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Args::Flag hashAlgo(std::string && longName, HashAlgorithm * ha)
5050
{
5151
return Args::Flag {
5252
.longName = std::move(longName),
53-
.description = "Hash algorithm (`md5`, `sha1`, `sha256`, or `sha512`).",
53+
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`).",
5454
.labels = {"hash-algo"},
5555
.handler = {[ha](std::string s) {
5656
*ha = parseHashAlgo(s);
@@ -63,7 +63,7 @@ Args::Flag hashAlgoOpt(std::string && longName, std::optional<HashAlgorithm> * o
6363
{
6464
return Args::Flag {
6565
.longName = std::move(longName),
66-
.description = "Hash algorithm (`md5`, `sha1`, `sha256`, or `sha512`). Can be omitted for SRI hashes.",
66+
.description = "Hash algorithm (`blake3`, `md5`, `sha1`, `sha256`, or `sha512`). Can be omitted for SRI hashes.",
6767
.labels = {"hash-algo"},
6868
.handler = {[oha](std::string s) {
6969
*oha = std::optional<HashAlgorithm>{parseHashAlgo(s)};

src/libexpr-tests/error_traces.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,7 @@ namespace nix {
11521152

11531153
ASSERT_TRACE1("hashString \"foo\" \"content\"",
11541154
UsageError,
1155-
HintFmt("unknown hash algorithm '%s', expect 'md5', 'sha1', 'sha256', or 'sha512'", "foo"));
1155+
HintFmt("unknown hash algorithm '%s', expect 'blake3', 'md5', 'sha1', 'sha256', or 'sha512'", "foo"));
11561156

11571157
ASSERT_TRACE2("hashString \"sha256\" {}",
11581158
TypeError,

src/libutil-tests/hash.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,52 @@
66

77
namespace nix {
88

9+
class BLAKE3HashTest : public virtual ::testing::Test
10+
{
11+
public:
12+
13+
/**
14+
* We set these in tests rather than the regular globals so we don't have
15+
* to worry about race conditions if the tests run concurrently.
16+
*/
17+
ExperimentalFeatureSettings mockXpSettings;
18+
19+
private:
20+
21+
void SetUp() override
22+
{
23+
mockXpSettings.set("experimental-features", "blake3-hashes");
24+
}
25+
};
26+
927
/* ----------------------------------------------------------------------------
1028
* hashString
1129
* --------------------------------------------------------------------------*/
1230

31+
TEST_F(BLAKE3HashTest, testKnownBLAKE3Hashes1) {
32+
// values taken from: https://tools.ietf.org/html/rfc4634
33+
auto s = "abc";
34+
auto hash = hashString(HashAlgorithm::BLAKE3, s, mockXpSettings);
35+
ASSERT_EQ(hash.to_string(HashFormat::Base16, true),
36+
"blake3:6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85");
37+
}
38+
39+
TEST_F(BLAKE3HashTest, testKnownBLAKE3Hashes2) {
40+
// values taken from: https://tools.ietf.org/html/rfc4634
41+
auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
42+
auto hash = hashString(HashAlgorithm::BLAKE3, s, mockXpSettings);
43+
ASSERT_EQ(hash.to_string(HashFormat::Base16, true),
44+
"blake3:c19012cc2aaf0dc3d8e5c45a1b79114d2df42abb2a410bf54be09e891af06ff8");
45+
}
46+
47+
TEST_F(BLAKE3HashTest, testKnownBLAKE3Hashes3) {
48+
// values taken from: https://www.ietf.org/archive/id/draft-aumasson-blake3-00.txt
49+
auto s = "IETF";
50+
auto hash = hashString(HashAlgorithm::BLAKE3, s, mockXpSettings);
51+
ASSERT_EQ(hash.to_string(HashFormat::Base16, true),
52+
"blake3:83a2de1ee6f4e6ab686889248f4ec0cf4cc5709446a682ffd1cbb4d6165181e2");
53+
}
54+
1355
TEST(hashString, testKnownMD5Hashes1) {
1456
// values taken from: https://tools.ietf.org/html/rfc1321
1557
auto s1 = "";

src/libutil/experimental-features.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct ExperimentalFeatureDetails
2424
* feature, we either have no issue at all if few features are not added
2525
* at the end of the list, or a proper merge conflict if they are.
2626
*/
27-
constexpr size_t numXpFeatures = 1 + static_cast<size_t>(Xp::PipeOperators);
27+
constexpr size_t numXpFeatures = 1 + static_cast<size_t>(Xp::BLAKE3Hashes);
2828

2929
constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails = {{
3030
{
@@ -302,6 +302,14 @@ constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails
302302
)",
303303
.trackingUrl = "https://github.com/NixOS/nix/milestone/55",
304304
},
305+
{
306+
.tag = Xp::BLAKE3Hashes,
307+
.name = "blake3-hashes",
308+
.description = R"(
309+
Enables support for BLAKE3 hashes.
310+
)",
311+
.trackingUrl = "",
312+
},
305313
}};
306314

307315
static_assert(

src/libutil/experimental-features.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum struct ExperimentalFeature
3737
MountedSSHStore,
3838
VerifiedFetches,
3939
PipeOperators,
40+
BLAKE3Hashes,
4041
};
4142

4243
/**

src/libutil/hash.cc

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#include <iostream>
22
#include <cstring>
33

4+
#include <blake3.h>
45
#include <openssl/crypto.h>
56
#include <openssl/md5.h>
67
#include <openssl/sha.h>
78

89
#include "args.hh"
910
#include "hash.hh"
1011
#include "archive.hh"
12+
#include "config.hh"
1113
#include "split.hh"
1214

1315
#include <sys/types.h>
@@ -20,6 +22,7 @@ namespace nix {
2022

2123
static size_t regularHashSize(HashAlgorithm type) {
2224
switch (type) {
25+
case HashAlgorithm::BLAKE3: return blake3HashSize;
2326
case HashAlgorithm::MD5: return md5HashSize;
2427
case HashAlgorithm::SHA1: return sha1HashSize;
2528
case HashAlgorithm::SHA256: return sha256HashSize;
@@ -29,12 +32,15 @@ static size_t regularHashSize(HashAlgorithm type) {
2932
}
3033

3134

32-
const std::set<std::string> hashAlgorithms = {"md5", "sha1", "sha256", "sha512" };
35+
const std::set<std::string> hashAlgorithms = {"blake3", "md5", "sha1", "sha256", "sha512" };
3336

3437
const std::set<std::string> hashFormats = {"base64", "nix32", "base16", "sri" };
3538

36-
Hash::Hash(HashAlgorithm algo) : algo(algo)
39+
Hash::Hash(HashAlgorithm algo, const ExperimentalFeatureSettings & xpSettings) : algo(algo)
3740
{
41+
if (algo == HashAlgorithm::BLAKE3) {
42+
xpSettings.require(Xp::BLAKE3Hashes);
43+
}
3844
hashSize = regularHashSize(algo);
3945
assert(hashSize <= maxHashSize);
4046
memset(hash, 0, maxHashSize);
@@ -284,6 +290,7 @@ Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashAlgorithm> ha
284290

285291
union Ctx
286292
{
293+
blake3_hasher blake3;
287294
MD5_CTX md5;
288295
SHA_CTX sha1;
289296
SHA256_CTX sha256;
@@ -293,7 +300,8 @@ union Ctx
293300

294301
static void start(HashAlgorithm ha, Ctx & ctx)
295302
{
296-
if (ha == HashAlgorithm::MD5) MD5_Init(&ctx.md5);
303+
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_init(&ctx.blake3);
304+
else if (ha == HashAlgorithm::MD5) MD5_Init(&ctx.md5);
297305
else if (ha == HashAlgorithm::SHA1) SHA1_Init(&ctx.sha1);
298306
else if (ha == HashAlgorithm::SHA256) SHA256_Init(&ctx.sha256);
299307
else if (ha == HashAlgorithm::SHA512) SHA512_Init(&ctx.sha512);
@@ -303,7 +311,8 @@ static void start(HashAlgorithm ha, Ctx & ctx)
303311
static void update(HashAlgorithm ha, Ctx & ctx,
304312
std::string_view data)
305313
{
306-
if (ha == HashAlgorithm::MD5) MD5_Update(&ctx.md5, data.data(), data.size());
314+
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_update(&ctx.blake3, data.data(), data.size());
315+
else if (ha == HashAlgorithm::MD5) MD5_Update(&ctx.md5, data.data(), data.size());
307316
else if (ha == HashAlgorithm::SHA1) SHA1_Update(&ctx.sha1, data.data(), data.size());
308317
else if (ha == HashAlgorithm::SHA256) SHA256_Update(&ctx.sha256, data.data(), data.size());
309318
else if (ha == HashAlgorithm::SHA512) SHA512_Update(&ctx.sha512, data.data(), data.size());
@@ -312,24 +321,24 @@ static void update(HashAlgorithm ha, Ctx & ctx,
312321

313322
static void finish(HashAlgorithm ha, Ctx & ctx, unsigned char * hash)
314323
{
315-
if (ha == HashAlgorithm::MD5) MD5_Final(hash, &ctx.md5);
324+
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_finalize(&ctx.blake3, hash, BLAKE3_OUT_LEN);
325+
else if (ha == HashAlgorithm::MD5) MD5_Final(hash, &ctx.md5);
316326
else if (ha == HashAlgorithm::SHA1) SHA1_Final(hash, &ctx.sha1);
317327
else if (ha == HashAlgorithm::SHA256) SHA256_Final(hash, &ctx.sha256);
318328
else if (ha == HashAlgorithm::SHA512) SHA512_Final(hash, &ctx.sha512);
319329
}
320330

321-
322-
Hash hashString(HashAlgorithm ha, std::string_view s)
331+
Hash hashString(
332+
HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings)
323333
{
324334
Ctx ctx;
325-
Hash hash(ha);
335+
Hash hash(ha, xpSettings);
326336
start(ha, ctx);
327337
update(ha, ctx, s);
328338
finish(ha, ctx, hash.hash);
329339
return hash;
330340
}
331341

332-
333342
Hash hashFile(HashAlgorithm ha, const Path & path)
334343
{
335344
HashSink sink(ha);
@@ -426,6 +435,7 @@ std::string_view printHashFormat(HashFormat HashFormat)
426435

427436
std::optional<HashAlgorithm> parseHashAlgoOpt(std::string_view s)
428437
{
438+
if (s == "blake3") return HashAlgorithm::BLAKE3;
429439
if (s == "md5") return HashAlgorithm::MD5;
430440
if (s == "sha1") return HashAlgorithm::SHA1;
431441
if (s == "sha256") return HashAlgorithm::SHA256;
@@ -439,12 +449,13 @@ HashAlgorithm parseHashAlgo(std::string_view s)
439449
if (opt_h)
440450
return *opt_h;
441451
else
442-
throw UsageError("unknown hash algorithm '%1%', expect 'md5', 'sha1', 'sha256', or 'sha512'", s);
452+
throw UsageError("unknown hash algorithm '%1%', expect 'blake3', 'md5', 'sha1', 'sha256', or 'sha512'", s);
443453
}
444454

445455
std::string_view printHashAlgo(HashAlgorithm ha)
446456
{
447457
switch (ha) {
458+
case HashAlgorithm::BLAKE3: return "blake3";
448459
case HashAlgorithm::MD5: return "md5";
449460
case HashAlgorithm::SHA1: return "sha1";
450461
case HashAlgorithm::SHA256: return "sha256";

0 commit comments

Comments
 (0)