Merge pull request #15009 from amaanq/signature-type

protocol: update JSON output with structured `Signature` type
This commit is contained in:
John Ericson
2026-03-03 02:10:44 +00:00
committed by GitHub
55 changed files with 475 additions and 104 deletions

View File

@@ -40,7 +40,7 @@ no longer need to implement the derivation hash modulo algorithm.
},
"value": {
"outPath": "xyz...-foo",
"signatures": ["..."]
"signatures": [{ "keyName": "cache.example.com-1", "sig": "..." }]
}
}
```
@@ -64,6 +64,12 @@ Non-CA builds are unaffected.
Stable code paths do use the realization fields (`BuildResult::Success::builtOutputs`), but only the output name and outpath parts of that.
For older protocols, we can fake enough of the realisation format to provide those two parts forthat map, which keeps operations like `--print-output-paths` working.
### Structured signatures
[Signatures](@docroot@/protocols/json/signature.md) in JSON formats are now represented as structured objects with `keyName` and `sig` fields, rather than colon-separated strings.
`nix path-info --json --json-format 3` opts into the new version for this command.
JSON parsing accepts both the old string format and new structured format for backwards compatibility.
### Impact
- **Non-CA derivation users**: No impact. This only affects the experimental `ca-derivations` feature.

View File

@@ -125,6 +125,7 @@
- [Hash](protocols/json/hash.md)
- [Content Address](protocols/json/content-address.md)
- [Store Path](protocols/json/store-path.md)
- [Signature](protocols/json/signature.md)
- [Store Object Info](protocols/json/store-object-info.md)
- [Derivation](protocols/json/derivation/index.md)
- [Derivation Options](protocols/json/derivation/options.md)

View File

@@ -1,21 +1,21 @@
{{#include build-trace-entry-v2-fixed.md}}
{{#include build-trace-entry-v3-fixed.md}}
## Examples
### Simple build trace entry
```json
{{#include schema/build-trace-entry-v2/simple.json}}
{{#include schema/build-trace-entry-v3/simple.json}}
```
### Build trace entry with signature
```json
{{#include schema/build-trace-entry-v2/with-signature.json}}
{{#include schema/build-trace-entry-v3/with-structured-signature.json}}
```
<!--
## Raw Schema
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v2.json)
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v3.json)
-->

View File

@@ -13,11 +13,12 @@ schemas = [
'hash-v1',
'content-address-v1',
'store-path-v1',
'store-object-info-v2',
'signature-v2',
'store-object-info-v3',
'derivation-v4',
'derivation-options-v1',
'deriving-path-v1',
'build-trace-entry-v2',
'build-trace-entry-v3',
'build-result-v1',
'store-v1',
]

View File

@@ -83,7 +83,7 @@ properties:
description: |
A mapping from output names to their build trace entries.
additionalProperties:
"$ref": "build-trace-entry-v2.yaml#/$defs/value"
"$ref": "build-trace-entry-v3.yaml#/$defs/value"
failure:
type: object

View File

@@ -1,5 +1,5 @@
"$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v2.json"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v3.json"
title: Build Trace Entry
description: |
A record of a successful build outcome for a specific derivation output.
@@ -12,14 +12,17 @@ description: |
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations)
> and subject to change.
Verision history:
## Version History
- Version 1: Original format
- Version 2:
- Use `drvPath` not `drvHash` to refer to derivation in a more conventional way.
- Remove `dependentRealisations`
- Version 3:
- Use `drvPath` not `drvHash` to refer to derivation in a more conventional way.
- Separate into `key` and `value`
- Use 2nd version of signatures format (objects, not strings)
type: object
required:
@@ -77,6 +80,4 @@ additionalProperties: false
description: |
A set of cryptographic signatures attesting to the authenticity of this build trace entry.
items:
type: string
title: Signature
description: A single cryptographic signature
"$ref": "signature-v2.yaml"

View File

@@ -0,0 +1 @@
../../../../../../src/libstore-tests/data/nar-info/json-3

View File

@@ -0,0 +1,33 @@
"$schema": "http://json-schema.org/draft-07/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/signature-v2.json"
title: Signature
description: |
A cryptographic signature along with the name of the key that produced it.
This schema describes the JSON representation of signatures as used in various Nix JSON APIs.
> **Warning**
>
> This JSON format is currently
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
> and subject to change.
## Version History
- Version 1: Colon-separated string in the format `<key-name>:<signature-in-Base64>`
- Version 2: Structured object with `keyName` and `sig` fields
type: object
required:
- keyName
- sig
properties:
keyName:
type: string
title: Key Name
description: The name of the key used to produce this signature
sig:
type: string
title: Signature Data
description: The raw signature bytes, Base64-encoded

View File

@@ -0,0 +1,4 @@
{
"keyName": "cache.nixos.org-1",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}

View File

@@ -1 +0,0 @@
../../../../../../src/libstore-tests/data/path-info/json-2

View File

@@ -0,0 +1 @@
../../../../../../src/libstore-tests/data/path-info/json-3

View File

@@ -1,6 +1,6 @@
"$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v2.json"
title: Store Object Info v2
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v3.json"
title: Store Object Info v3
description: |
Information about a [store object](@docroot@/store/store-object.md).
@@ -50,10 +50,10 @@ $defs:
properties:
version:
type: integer
const: 2
title: Format version (must be 2)
const: 3
title: Format version (must be 3)
description: |
Must be `2`.
Must be `3`.
This is a guard that allows us to continue evolving this format.
Here is the rough version history:
@@ -63,6 +63,8 @@ $defs:
- Version 2: Use structured JSON type for `ca`
- Version 3: Use structured JSON type for `signatures`
path:
"$ref": "./store-path-v1.yaml"
title: Store Path
@@ -174,7 +176,7 @@ $defs:
> This is an "impure" field that may not be included in certain contexts.
items:
type: string
"$ref": "./signature-v2.yaml"
# Computed closure fields
closureSize:

View File

@@ -37,7 +37,7 @@ properties:
- contents
properties:
info:
"$ref": "./store-object-info-v2.yaml#/$defs/impure"
"$ref": "./store-object-info-v3.yaml#/$defs/impure"
title: Store Object Info
description: |
Metadata about the [store object](@docroot@/store/store-object.md) including hash, size, references, etc.
@@ -70,7 +70,7 @@ properties:
"^[A-Za-z0-9+/]{43}=$":
type: object
additionalProperties:
"$ref": "./build-trace-entry-v2.yaml#/$defs/value"
"$ref": "./build-trace-entry-v3.yaml#/$defs/value"
additionalProperties: false
"$defs":

View File

@@ -0,0 +1,9 @@
{{#include signature-v2-fixed.md}}
## Examples
### Simple signature
```json
{{#include schema/signature-v2/simple.json}}
```

View File

@@ -1,45 +1,45 @@
{{#include store-object-info-v2-fixed.md}}
{{#include store-object-info-v3-fixed.md}}
## Examples
### Minimal store object (content-addressed)
```json
{{#include schema/store-object-info-v2/pure.json}}
{{#include schema/store-object-info-v3/pure.json}}
```
### Store object with impure fields
```json
{{#include schema/store-object-info-v2/impure.json}}
{{#include schema/store-object-info-v3/impure.json}}
```
### Minimal store object (empty)
```json
{{#include schema/store-object-info-v2/empty_pure.json}}
{{#include schema/store-object-info-v3/empty_pure.json}}
```
### Store object with all impure fields
```json
{{#include schema/store-object-info-v2/empty_impure.json}}
{{#include schema/store-object-info-v3/empty_impure.json}}
```
### NAR info (minimal)
```json
{{#include schema/nar-info-v2/pure.json}}
{{#include schema/nar-info-v3/pure.json}}
```
### NAR info (with binary cache fields)
```json
{{#include schema/nar-info-v2/impure.json}}
{{#include schema/nar-info-v3/impure.json}}
```
<!-- need to convert YAML to JSON first
## Raw Schema
[JSON Schema for Store Object Info v1](schema/store-object-info-v2.json)
[JSON Schema for Store Object Info v1](schema/store-object-info-v3.json)
-->

View File

@@ -51,6 +51,13 @@ schemas = [
'simple.json',
],
},
{
'stem' : 'signature',
'schema' : schema_dir / 'signature-v2.yaml',
'files' : [
'simple.json',
],
},
{
'stem' : 'deriving-path',
'schema' : schema_dir / 'deriving-path-v1.yaml',
@@ -62,10 +69,10 @@ schemas = [
},
{
'stem' : 'build-trace-entry',
'schema' : schema_dir / 'build-trace-entry-v2.yaml',
'schema' : schema_dir / 'build-trace-entry-v3.yaml',
'files' : [
'simple.json',
'with-signature.json',
'with-structured-signature.json',
],
},
{
@@ -151,20 +158,20 @@ schemas += [
# Match overall
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'schema' : schema_dir / 'store-object-info-v3.yaml',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'json-2' / 'empty_pure.json',
'json-2' / 'empty_impure.json',
'json-3' / 'pure.json',
'json-3' / 'impure.json',
'json-3' / 'empty_pure.json',
'json-3' / 'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'schema' : schema_dir / 'store-object-info-v3.yaml',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'json-3' / 'pure.json',
'json-3' / 'impure.json',
],
},
{
@@ -179,32 +186,32 @@ schemas += [
# Match exact variant
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'empty_pure.json',
'json-3' / 'pure.json',
'json-3' / 'empty_pure.json',
],
},
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/impure',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/impure',
'files' : [
'json-2' / 'impure.json',
'json-2' / 'empty_impure.json',
'json-3' / 'impure.json',
'json-3' / 'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
'files' : [
'json-2' / 'pure.json',
'json-3' / 'pure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/narInfo',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/narInfo',
'files' : [
'json-2' / 'impure.json',
'json-3' / 'impure.json',
],
},
]

View File

@@ -0,0 +1,4 @@
{
"keyName": "cache.nixos.org-1",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}

View File

@@ -10,8 +10,14 @@
"id": "sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
]

View File

@@ -23,7 +23,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -0,0 +1,31 @@
{
"ca": {
"hash": "sha256-EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM=",
"method": "nar"
},
"compression": "xz",
"deriver": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"downloadHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"downloadSize": 4029176,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"registrationTime": 23423,
"signatures": [
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
],
"storeDir": "/nix/store",
"ultimate": true,
"url": "nar/1w1fff338fvdw53sqgamddn1b2xgds473pv6y13gizdbqjv4i5p3.nar.xz",
"version": 3
}

View File

@@ -0,0 +1,14 @@
{
"ca": {
"hash": "sha256-EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM=",
"method": "nar"
},
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"storeDir": "/nix/store",
"version": 3
}

View File

@@ -0,0 +1,12 @@
{
"ca": null,
"deriver": null,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 0,
"references": [],
"registrationTime": null,
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 3
}

View File

@@ -0,0 +1,8 @@
{
"ca": null,
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 0,
"references": [],
"storeDir": "/nix/store",
"version": 3
}

View File

@@ -0,0 +1,27 @@
{
"ca": {
"hash": "sha256-EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM=",
"method": "nar"
},
"deriver": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"registrationTime": 23423,
"signatures": [
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
],
"storeDir": "/nix/store",
"ultimate": true,
"version": 3
}

View File

@@ -0,0 +1,14 @@
{
"ca": {
"hash": "sha256-EMIJ+giQ/gLIWoxmPKjno3zHZrxbGymgzGGyZvZBIdM=",
"method": "nar"
},
"narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=",
"narSize": 34878,
"references": [
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
"n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"
],
"storeDir": "/nix/store",
"version": 3
}

View File

@@ -0,0 +1,15 @@
{
"key": {
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"outputName": "foo"
},
"value": {
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"signatures": [
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
}

View File

@@ -4,7 +4,7 @@
"outputName": "foo"
},
"value": {
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
]

View File

@@ -0,0 +1,15 @@
{
"key": {
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
"outputName": "foo"
},
"value": {
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
}

View File

@@ -6,8 +6,14 @@
"value": {
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
}

View File

@@ -10,8 +10,14 @@
"id": "sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
]

View File

@@ -1,7 +1,13 @@
{
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}

View File

@@ -9,7 +9,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
},
{
"ca": null,
@@ -23,6 +23,6 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
]

View File

@@ -11,7 +11,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
},
{
"ca": {
@@ -27,11 +27,17 @@
],
"registrationTime": null,
"signatures": [
"fake-sig-1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"fake-sig-2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "fake-sig-1",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "fake-sig-2",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
]

View File

@@ -6,8 +6,14 @@
"value": {
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
}

View File

@@ -10,8 +10,14 @@
"id": "sha256:15e3c560894cbb27085cf65b5a2ecb18488c999497f4531b6907a7581ce6d527!baz",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}
]

View File

@@ -1,7 +1,13 @@
{
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
"signatures": [
"asdf:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"qwer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "asdf",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "qwer",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
]
}

View File

@@ -9,7 +9,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
},
{
"ca": null,
@@ -23,6 +23,6 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
]

View File

@@ -10,7 +10,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
},
{
"ca": null,
@@ -26,6 +26,6 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
]

View File

@@ -10,7 +10,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": true,
"version": 2
"version": 3
},
{
"ca": null,
@@ -24,12 +24,18 @@
],
"registrationTime": 23423,
"signatures": [
"fake-sig-1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"fake-sig-2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
{
"keyName": "fake-sig-1",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
{
"keyName": "fake-sig-2",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
}
],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
},
{
"ca": {
@@ -48,6 +54,6 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
]

View File

@@ -30,7 +30,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -30,7 +30,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -30,7 +30,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -36,7 +36,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
},
"w0yjpwh59kpbyc7hz9jgmi44r9br908i-dep-drv-out": {
@@ -58,7 +58,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -23,7 +23,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -23,7 +23,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
},
"k09ldq9fvxb6vfwq0cmv6j1jgqx08y1n-main": {
@@ -47,7 +47,7 @@
"signatures": [],
"storeDir": "/nix/store",
"ultimate": false,
"version": 2
"version": 3
}
}
},

View File

@@ -31,6 +31,16 @@ class NarInfoTestV2 : public CharacterizationTest, public LibStoreTest
}
};
class NarInfoTestV3 : public CharacterizationTest, public LibStoreTest
{
std::filesystem::path unitTestData = getUnitTestData() / "nar-info" / "json-3";
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / (testStem + ".json");
}
};
static NarInfo makeNarInfo(const Store & store, bool includeImpureInfo)
{
auto info = NarInfo::makeFromCA(
@@ -122,6 +132,31 @@ static NarInfo makeNarInfo(const Store & store, bool includeImpureInfo)
JSON_READ_TEST_V2(STEM, PURE) \
JSON_WRITE_TEST_V2(STEM, PURE)
#define JSON_READ_TEST_V3(STEM, PURE) \
TEST_F(NarInfoTestV3, NarInfo_##STEM##_from_json) \
{ \
readTest(#STEM, [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
auto expected = makeNarInfo(*store, PURE); \
auto got = UnkeyedNarInfo::fromJSON(nullptr, encoded); \
ASSERT_EQ(got, expected); \
}); \
}
#define JSON_WRITE_TEST_V3(STEM, PURE) \
TEST_F(NarInfoTestV3, NarInfo_##STEM##_to_json) \
{ \
writeTest( \
#STEM, \
[&]() -> json { return makeNarInfo(*store, PURE).toJSON(nullptr, PURE, PathInfoJsonFormat::V3); }, \
[](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
}
#define JSON_TEST_V3(STEM, PURE) \
JSON_READ_TEST_V3(STEM, PURE) \
JSON_WRITE_TEST_V3(STEM, PURE)
JSON_TEST_V1(pure, false)
JSON_TEST_V1(impure, true)
@@ -131,4 +166,7 @@ JSON_READ_TEST_V1(pure_noversion, false)
JSON_TEST_V2(pure, false)
JSON_TEST_V2(impure, true)
JSON_TEST_V3(pure, false)
JSON_TEST_V3(impure, true)
} // namespace nix

View File

@@ -30,6 +30,16 @@ class PathInfoTestV2 : public CharacterizationTest, public LibStoreTest
}
};
class PathInfoTestV3 : public CharacterizationTest, public LibStoreTest
{
std::filesystem::path unitTestData = getUnitTestData() / "path-info" / "json-3";
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / (testStem + ".json");
}
};
static UnkeyedValidPathInfo makeEmpty()
{
return {
@@ -129,6 +139,31 @@ static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo
JSON_READ_TEST_V2(STEM, OBJ) \
JSON_WRITE_TEST_V2(STEM, OBJ, PURE)
#define JSON_READ_TEST_V3(STEM, OBJ) \
TEST_F(PathInfoTestV3, PathInfo_##STEM##_from_json) \
{ \
readTest(#STEM, [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
UnkeyedValidPathInfo got = UnkeyedValidPathInfo::fromJSON(nullptr, encoded); \
auto expected = OBJ; \
ASSERT_EQ(got, expected); \
}); \
}
#define JSON_WRITE_TEST_V3(STEM, OBJ, PURE) \
TEST_F(PathInfoTestV3, PathInfo_##STEM##_to_json) \
{ \
writeTest( \
#STEM, \
[&]() -> json { return OBJ.toJSON(nullptr, PURE, PathInfoJsonFormat::V3); }, \
[](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
}
#define JSON_TEST_V3(STEM, OBJ, PURE) \
JSON_READ_TEST_V3(STEM, OBJ) \
JSON_WRITE_TEST_V3(STEM, OBJ, PURE)
JSON_TEST_V1(empty_pure, makeEmpty(), false)
JSON_TEST_V1(empty_impure, makeEmpty(), true)
JSON_TEST_V1(pure, makeFull(*store, false), false)
@@ -142,6 +177,11 @@ JSON_TEST_V2(empty_impure, makeEmpty(), true)
JSON_TEST_V2(pure, makeFull(*store, false), false)
JSON_TEST_V2(impure, makeFull(*store, true), true)
JSON_TEST_V3(empty_pure, makeEmpty(), false)
JSON_TEST_V3(empty_impure, makeEmpty(), true)
JSON_TEST_V3(pure, makeFull(*store, false), false)
JSON_TEST_V3(impure, makeFull(*store, true), true)
TEST_F(PathInfoTestV2, PathInfo_full_shortRefs)
{
ValidPathInfo it = makeFullKeyed(*store, true);

View File

@@ -54,6 +54,20 @@ Realisation simple{
},
};
Realisation withSignature{
{
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
.signatures =
{
Signature{.keyName = "asdf", .sig = std::string(64, '\0')},
},
},
{
.drvPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv"},
.outputName = "foo",
},
};
INSTANTIATE_TEST_SUITE_P(
RealisationJSON,
RealisationJsonTest,
@@ -63,15 +77,16 @@ INSTANTIATE_TEST_SUITE_P(
simple,
},
std::pair{
"with-signature",
[&] {
auto r = simple;
// FIXME actually sign properly
r.signatures = {
Signature{.keyName = "asdf", .sig = std::string(64, '\0')},
};
return r;
}(),
"with-signature-structured",
withSignature,
}));
/**
* Old signature format (string) should still be parseable.
*/
TEST_F(RealisationTest, with_signature_from_json)
{
readJsonTest("with-signature", withSignature);
}
} // namespace nix

View File

@@ -22,6 +22,8 @@ enum class PathInfoJsonFormat {
V1 = 1,
/// New format with structured hashes and store path base names
V2 = 2,
/// New format with structured signatures
V3 = 3,
};
/**

View File

@@ -15,8 +15,10 @@ PathInfoJsonFormat parsePathInfoJsonFormat(uint64_t version)
return PathInfoJsonFormat::V1;
case 2:
return PathInfoJsonFormat::V2;
case 3:
return PathInfoJsonFormat::V3;
default:
throw Error("unsupported path info JSON format version %d; supported versions are 1 and 2", version);
throw Error("unsupported path info JSON format version %d; supported versions are 1, 2 and 3", version);
}
}
@@ -211,7 +213,13 @@ UnkeyedValidPathInfo::toJSON(const StoreDirConfig * store, bool includeImpureInf
jsonObject["ultimate"] = ultimate;
jsonObject["signatures"] = sigs;
if (format == PathInfoJsonFormat::V3) {
jsonObject["signatures"] = sigs;
} else {
auto & sigsObj = jsonObject["signatures"] = json::array();
for (auto & sig : sigs)
sigsObj.push_back(sig.to_string());
}
}
return jsonObject;
@@ -313,7 +321,7 @@ UnkeyedValidPathInfo adl_serializer<UnkeyedValidPathInfo>::from_json(const json
void adl_serializer<UnkeyedValidPathInfo>::to_json(json & json, const UnkeyedValidPathInfo & c)
{
json = c.toJSON(nullptr, true, PathInfoJsonFormat::V2);
json = c.toJSON(nullptr, true, PathInfoJsonFormat::V3);
}
ValidPathInfo adl_serializer<ValidPathInfo>::from_json(const json & json0)

View File

@@ -178,12 +178,21 @@ bool verifyDetached(std::string_view data, const Signature & sig, const PublicKe
namespace nlohmann {
void adl_serializer<Signature>::to_json(json & j, const Signature & s)
{
j = s.to_string();
j = {
{"keyName", s.keyName},
{"sig", base64::encode(std::as_bytes(std::span<const char>{s.sig}))},
};
}
Signature adl_serializer<Signature>::from_json(const json & j)
{
return Signature::parse(getString(j));
if (j.is_string())
return Signature::parse(getString(j));
auto obj = getObject(j);
return Signature{
.keyName = getString(valueAt(obj, "keyName")),
.sig = base64::decode(getString(valueAt(obj, "sig"))),
};
}
} // namespace nlohmann