Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions doc/manual/source/protocols/json/hash.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,16 @@

## Examples

### SHA-256 with Base64 encoding

```json
{{#include schema/hash-v1/sha256-base64.json}}
```

### SHA-256 with Base16 (hexadecimal) encoding
### SHA-256

```json
{{#include schema/hash-v1/sha256-base16.json}}
```

### SHA-256 with Nix32 encoding

```json
{{#include schema/hash-v1/sha256-nix32.json}}
```

### BLAKE3 with Base64 encoding
### BLAKE3

```json
{{#include schema/hash-v1/blake3-base64.json}}
{{#include schema/hash-v1/blake3-base16.json}}
```

<!-- need to convert YAML to JSON first
Expand Down
10 changes: 3 additions & 7 deletions doc/manual/source/protocols/json/schema/hash-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@ properties:
format:
type: string
enum:
- base64
- nix32
- base16
- sri
title: Hash format
description: |
The encoding format of the hash value.

- `base64` uses standard Base64 encoding [RFC 4648, section 4](https://datatracker.ietf.org/doc/html/rfc4648#section-4)
- `nix32` is Nix-specific base-32 encoding
- `base16` is lowercase hexadecimal
- `sri` is the [Subresource Integrity format](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity).
`base16` (lowercase hexadecimal) is the only format that is currently supported for JSON serialization.
This field exists primarily to reduce ambiguity about what the hash means.
It would also help us support other formats in the future, but there are no concrete plans to do so at this.
hash:
type: string
title: Hash
Expand Down
4 changes: 1 addition & 3 deletions src/json-schema-checks/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ schemas = [
'stem' : 'hash',
'schema' : schema_dir / 'hash-v1.yaml',
'files' : [
'sha256-base64.json',
'sha256-base16.json',
'sha256-nix32.json',
'blake3-base64.json',
'blake3-base16.json',
],
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/libstore-tests/data/content-address/nar.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "9vLqj0XYoFfJVmoz+ZR02i5camYE1zYSFlDicwxvsKM="
"format": "base16",
"hash": "f6f2ea8f45d8a057c9566a33f99474da2e5c6a6604d736121650e2730c6fb0a3"
},
"method": "nar"
}
4 changes: 2 additions & 2 deletions src/libstore-tests/data/content-address/text.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "8OTC92xYkW7CWPJGhRvqCR0U1CR6L8PhhpRGGxgW4Ts="
"format": "base16",
"hash": "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"
},
"method": "text"
}
4 changes: 2 additions & 2 deletions src/libstore-tests/data/derivation/output-caFixedFlat.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
"format": "base16",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f"
},
"method": "flat"
}
4 changes: 2 additions & 2 deletions src/libstore-tests/data/derivation/output-caFixedNAR.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
"format": "base16",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f"
},
"method": "nar"
}
4 changes: 2 additions & 2 deletions src/libstore-tests/data/derivation/output-caFixedText.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="
"format": "base16",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f"
},
"method": "text"
}
12 changes: 6 additions & 6 deletions src/libstore-tests/data/nar-info/impure.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
"format": "base16",
"hash": "10c209fa0890fe02c85a8c663ca8e7a37cc766bc5b1b29a0cc61b266f64121d3"
},
"method": "nar"
},
"compression": "xz",
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"downloadSize": 4029176,
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"narSize": 34878,
"references": [
Expand Down
8 changes: 4 additions & 4 deletions src/libstore-tests/data/nar-info/pure.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
"format": "base16",
"hash": "10c209fa0890fe02c85a8c663ca8e7a37cc766bc5b1b29a0cc61b266f64121d3"
},
"method": "nar"
},
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"narSize": 34878,
"references": [
Expand Down
4 changes: 2 additions & 2 deletions src/libstore-tests/data/path-info/empty_impure.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"deriver": null,
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"narSize": 0,
"references": [],
Expand Down
4 changes: 2 additions & 2 deletions src/libstore-tests/data/path-info/empty_pure.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"ca": null,
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"narSize": 0,
"references": [],
Expand Down
8 changes: 4 additions & 4 deletions src/libstore-tests/data/path-info/impure.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
"format": "base16",
"hash": "10c209fa0890fe02c85a8c663ca8e7a37cc766bc5b1b29a0cc61b266f64121d3"
},
"method": "nar"
},
"deriver": "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"narSize": 34878,
"references": [
Expand Down
8 changes: 4 additions & 4 deletions src/libstore-tests/data/path-info/pure.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"ca": {
"hash": {
"algorithm": "sha256",
"format": "base64",
"hash": "EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM="
"format": "base16",
"hash": "10c209fa0890fe02c85a8c663ca8e7a37cc766bc5b1b29a0cc61b266f64121d3"
},
"method": "nar"
},
"narHash": {
"algorithm": "sha256",
"format": "base64",
"hash": "FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="
"format": "base16",
"hash": "15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527"
},
"narSize": 34878,
"references": [
Expand Down
5 changes: 5 additions & 0 deletions src/libutil-tests/data/hash/blake3-base16.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"algorithm": "blake3",
"format": "base16",
"hash": "9e70ee1449965fb62d049040a1ed06ec377430da6ec13173e7c4fffcd28be980"
}
5 changes: 0 additions & 5 deletions src/libutil-tests/data/hash/blake3-base64.json

This file was deleted.

4 changes: 2 additions & 2 deletions src/libutil-tests/data/hash/simple.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"algorithm": "sha256",
"format": "base64",
"hash": "8OTC92xYkW7CWPJGhRvqCR0U1CR6L8PhhpRGGxgW4Ts="
"format": "base16",
"hash": "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"
}
38 changes: 14 additions & 24 deletions src/libutil-tests/hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,7 @@ struct HashJsonTest : virtual HashTest,
::testing::WithParamInterface<std::pair<std::string_view, Hash>>
{};

struct HashJsonParseOnlyTest : virtual HashTest,
JsonCharacterizationTest<Hash>,
::testing::WithParamInterface<std::pair<std::string_view, Hash>>
struct HashJsonParseFailureTest : virtual HashTest, ::testing::WithParamInterface<std::string_view>
{};

struct BLAKE3HashJsonTest : virtual HashTest,
Expand All @@ -238,10 +236,12 @@ TEST_P(HashJsonTest, to_json)
writeJsonTest(name, value);
}

TEST_P(HashJsonParseOnlyTest, from_json)
TEST_P(HashJsonParseFailureTest, from_json)
{
auto & [name, expected] = GetParam();
readJsonTest(name, expected);
auto & name = GetParam();
auto path = goldenMaster(Path{name} + ".json");
auto encoded = json::parse(readFile(path));
ASSERT_THROW(nlohmann::adl_serializer<Hash>::from_json(encoded), Error);
}

TEST_P(BLAKE3HashJsonTest, from_json)
Expand All @@ -256,8 +256,8 @@ TEST_P(BLAKE3HashJsonTest, to_json)
writeJsonTest(name, expected);
}

// Round-trip tests (from_json + to_json) for base64 format only
// (to_json always outputs base64)
// Round-trip tests (from_json + to_json) for base16 format only
// (to_json always outputs base16)
INSTANTIATE_TEST_SUITE_P(
HashJSON,
HashJsonTest,
Expand All @@ -267,31 +267,21 @@ INSTANTIATE_TEST_SUITE_P(
hashString(HashAlgorithm::SHA256, "asdf"),
},
std::pair{
"sha256-base64",
"sha256-base16",
hashString(HashAlgorithm::SHA256, "asdf"),
}));

// Parse-only tests for non-base64 formats
// These verify C++ can deserialize other formats correctly
// Failure tests for unsupported formats (base64, nix32, sri)
// These verify that non-base16 formats are rejected
INSTANTIATE_TEST_SUITE_P(
HashJSONParseOnly,
HashJsonParseOnlyTest,
::testing::Values(
std::pair{
"sha256-base16",
hashString(HashAlgorithm::SHA256, "asdf"),
},
std::pair{
"sha256-nix32",
hashString(HashAlgorithm::SHA256, "asdf"),
}));
HashJSONParseFailure, HashJsonParseFailureTest, ::testing::Values("sha256-base64", "sha256-nix32"));

INSTANTIATE_TEST_SUITE_P(BLAKE3HashJSONParseOnly, BLAKE3HashJsonTest, ([] {
INSTANTIATE_TEST_SUITE_P(BLAKE3HashJSON, BLAKE3HashJsonTest, ([] {
ExperimentalFeatureSettings mockXpSettings;
mockXpSettings.set("experimental-features", "blake3-hashes");
return ::testing::Values(
std::pair{
"blake3-base64",
"blake3-base16",
hashString(HashAlgorithm::BLAKE3, "asdf", mockXpSettings),
});
}()));
Expand Down
13 changes: 10 additions & 3 deletions src/libutil/hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -508,17 +508,24 @@ Hash adl_serializer<Hash>::from_json(const json & json, const ExperimentalFeatur
{
auto & obj = getObject(json);
auto algo = parseHashAlgo(getString(valueAt(obj, "algorithm")), xpSettings);
auto format = parseHashFormat(getString(valueAt(obj, "format")));
auto formatStr = getString(valueAt(obj, "format"));
auto format = parseHashFormat(formatStr);

// Only base16 format is supported for JSON serialization
if (format != HashFormat::Base16) {
throw Error("hash format '%s' is not supported in JSON; only 'base16' is currently supported", formatStr);
}

auto & hashS = getString(valueAt(obj, "hash"));
return Hash::parseExplicitFormatUnprefixed(hashS, algo, format, xpSettings);
}

void adl_serializer<Hash>::to_json(json & json, const Hash & hash)
{
json = {
{"format", printHashFormat(HashFormat::Base64)},
{"format", printHashFormat(HashFormat::Base16)},
{"algorithm", printHashAlgo(hash.algo)},
{"hash", hash.to_string(HashFormat::Base64, false)},
{"hash", hash.to_string(HashFormat::Base16, false)},
};
}

Expand Down
5 changes: 2 additions & 3 deletions tests/functional/fixed.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ nix-build fixed.nix -A bad --no-out-link && fail "should fail"
# a side-effect.
[[ -e $path ]]
nix path-info --json "$path" | jq -e \
--arg hash "$(nix hash convert --to base64 "md5:8ddd8be4b179a529afa5f2ffae4b9858")" \
'.[].ca == {
method: "flat",
hash: {
algorithm: "md5",
format: "base64",
hash: $hash
format: "base16",
hash: "8ddd8be4b179a529afa5f2ffae4b9858"
},
}'

Expand Down
4 changes: 2 additions & 2 deletions tests/functional/git-hashing/simple-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ try2 () {

nix path-info --json "$path" | jq -e \
--arg algo "$hashAlgo" \
--arg hash "$(nix hash convert --to base64 "$hashAlgo:$hashFromGit")" \
--arg hash "$hashFromGit" \
'.[].ca == {
method: "git",
hash: {
algorithm: $algo,
format: "base64",
format: "base16",
hash: $hash
},
}'
Expand Down
Loading
Loading