Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signature payload format v1 #175

Merged
merged 15 commits into from
Nov 19, 2024
103 changes: 89 additions & 14 deletions SPECIFICATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ message SignedBlock {
required PublicKey nextKey = 2;
required bytes signature = 3;
optional ExternalSignature externalSignature = 4;
optional uint32 version = 5;
}

message ExternalSignature {
Expand Down Expand Up @@ -729,7 +730,8 @@ for signature verification.

Each block contains a serialized byte array of the Datalog data (`block`),
the next public key (`nextKey`) and the signature of that block and key
by the previous key.
by the previous key. The `version` field indicates the version of the signature
payload format.

The `proof` field contains either the private key corresponding to the
public key in the last block (attenuable tokens) or a signature of the last
Expand Down Expand Up @@ -787,13 +789,88 @@ corresponding to the last public key, to sign a new block and attenuate the
token, or a signature of the last block by the last private key, to seal the
token.

#### Signed payload generation

The data covered by the signature algorithm depends on the `version` field of
the `SignedBlock` message. If the field is absent, it defaults to version 0.

##### Version 0 (deprecated)

This defines the block signature payload v0.

The authority block signature payload v0 is the concatenation of:
- `data_0`: the serialized Datalog
- `pk_1`: the next public key
- `alg_1`: the little endian representation of the signature algorithm for `pk_1`

To sign the block at index `n+1`, the signed payload format is the concatenation of:
- `data_n+1`: the serialized Datalog
- `pk_n+2`: the next public key
- `alg_n+2`: the little endian representation of the signature algorithm for `pk_n+2`

if `external_sig_n+1` is present, the signed payload format is instead the concatenation of:
- `data_n+1`: the serialized Datalog
- `external_sig_n+1`: the optional external signature of the block
- `pk_n+2`: the next public key
- `alg_n+2`: the little endian representation of the signature algorithm for `pk_n+2`

This format is deprecated and will be replaced by version 1 in the future.

the signed payload format for external signatures, thereafter referred as "external signature payload v0", is the concatenation of:
- `data_n+1`: the serialized Datalog
- `pk_n+1`: the public key for the next block
- `alg_n+1`: the little endian representation of the signature algorithm for `pk_n+1`

This format is not supported anymore and should be replaced by version 1.

##### Version 1

This defines the block signature payload v1.

The authority block signature payload v1 is the concatenation of:
- the binary representation of the ASCII string "\0BLOCK\0"
- the binary representation of the ASCII string "\0VERSION\0"
- the little endian representation of the version of the signature payload format
- the binary representation of the ASCII string "\0PAYLOAD\0"
- `data_0`: the serialized Datalog
- the binary representation of the ASCII string "\0ALGORITHM\0"
- `alg_1`: the little endian representation of the signature algorithm for `pk_1`
- the binary representation of the ASCII string "\0NEXTKEY\0"
- `pk_1`: the next public key

To sign the block at index `n+1`, the signed payload format is the concatenation of:
- the binary representation of the ASCII string "\0BLOCK\0"
- the binary representation of the ASCII string "\0VERSION\0"
- the little endian representation of the version of the signature payload format
- the binary representation of the ASCII string "\0PAYLOAD\0"
- `data_n+1`: the serialized Datalog
- the binary representation of the ASCII string "\0ALGORITHM\0"
- `alg_n+2`: the little endian representation of the signature algorithm for `pk_n+2`
- the binary representation of the ASCII string "\0NEXTKEY\0"
- `pk_n+2`: the next public key
- the binary representation of the ASCII string "\0PREVSIG\0"
- `sig_n`: the signature of the previous block
- if `external_sig_n+1` is present:
- the binary representation of the ASCII string "\0EXTERNALSIG\0"
- `external_sig_n+1`: the optional external signature of the block

the signed payload format for external signatures, thereafter referred as "external signature payload v1", is the concatenation of:
- the binary representation of the ASCII string "\0EXTERNAL\0"
- the binary representation of the ASCII string "\0VERSION\0"
- the little endian representation of the version of the signature payload format
- the binary representation of the ASCII string "\0PAYLOAD\0"
- `data_n+1`: the serialized Datalog
- the binary representation of the ASCII string "\0PREVSIG\0"
- `sig_n`: the signature of the previous block

#### Signature (one block)

- `(pk_0, sk_0)` the root public and private Ed25519 keys
- `data_0` the serialized Datalog
- `(pk_1, sk_1)` the next key pair, generated at random
- `alg_1` the little endian representation of the signature algorithm fr `pk1, sk1` (see protobuf schema)
- `sig_0 = sign(sk_0, data_0 + alg_1 + pk_1)`
- the signed block version indicates the version of the signature payload format, either "block signature payload v0" or "block signature payload v1"
- `sig_0` is the signature of the payload by `sk_0`

The token will contain:

Expand Down Expand Up @@ -826,7 +903,9 @@ The token also contains `sk_n+1`.

The new block can optionally be signed by an external keypair `(epk, esk)` and carry an external signature `esig`.

We generate at random `(pk_n+2, sk_n+2)` and the signature `sig_n+1 = sign(sk_n+1, data_n+1 + esig? + alg_n+2 + pk_n+2)`. If the block is not signed by an external keypair, then `esig` is not part of the signed payload.
the signed block version indicates the version of the signature payload format, either "block signature payload v0" or "block signature payload v1".

We generate at random `(pk_n+2, sk_n+2)` and the signature `sig_n+1` is the signature of the payload by `sk_n+1`.

The token will contain:

Expand All @@ -852,12 +931,8 @@ Token {
Blocks generated by a trusted third party can carry an *extra* signature to provide a proof of their
origin. Same as regular signatures, they rely on Ed25519.

The external signature for block `n+1`, with `(external_pk, external_sk)` is `external_sig_n+1 = sign(external_sk, data_n+1 + alg_n+1 + pk_n+1)`.
It's quite similar to the regular signature, with a crucial difference: the public key appended to the block payload is the one _carried_ by block `n` (and which is used to verify block `n+1`).
This means that the authority block can't carry an external signature (that would be useless, since
the root key is not ephemeral and can be trusted directly).

This is necessary to make sure an external signature can't be used for any other token.
The external signature for block `n+1`, with `(external_pk, external_sk)` is `external_sig_n+1`, the signature of the payload in format "external signature payload v1" by `external_sk`.
The authority block can't carry an external signature. This is necessary to make sure an external signature can't be used for any other token.

The presence of an external signature affects the regular signature: the external signature is part of the payload signed by the regular signature.

Expand All @@ -881,12 +956,11 @@ Token {
}
```


#### Verifying

For each block i from 0 to n:

- verify(pk_i, sig_i, data_i + alg_i+1 + pk_i+1)
- `payload_i`: the signature payload for block i
- `verify(pk_i, sig_i, payload_i)`

If all signatures are verified, extract pk_n+1 from the last block and
sk_n+1 from the proof field, and check that they are from the same
Expand All @@ -895,8 +969,8 @@ key pair.
##### Verifying external signatures

For each block i from 1 to n, _where an external signature is present_:

- verify(external_pk_i, external_sig_i, data_i + alg_i + pk_i)
- `external_payload_i`: the external signature payload for block i
- `verify(external_pk_i, external_sig_i, external_payload_i)`

#### Signature (sealing)

Expand Down Expand Up @@ -1062,6 +1136,7 @@ To support this use-case, the protobuf schema defines two message types: `ThirdP
message ThirdPartyBlockRequest {
required PublicKey previousKey = 1;
repeated PublicKey publicKeys = 2;
required bytes previousSignature = 3;
}

message ThirdPartyBlockContents {
Expand Down
117 changes: 45 additions & 72 deletions samples/current/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1835,7 +1835,7 @@ allow if true;

revocation ids:
- `470e4bf7aa2a01ab39c98150bd06aa15b4aa5d86509044a8809a8634cd8cf2b42269a51a774b65d10bac9369d013070b00187925196a8e680108473f11cf8f03`
- `342167bc54bc642b6718a276875e55b6d39e9b21e4ce13b926a3d398b6c057fc436385bf4c817a16f9ecdf0b0d950e8b8258a20aeb3fd8896c5e9c1f0a53da03`
- `901b2af4dacf33458d2d91ac484b60bad948e8d10faa9695b096054d5b46e832a977b60b17464cacf545ad0801f549ea454675f0ac88c413406925e2af83ff08`

authorizer world:
```
Expand Down Expand Up @@ -2097,9 +2097,9 @@ allow if true;

revocation ids:
- `3771cefe71beb21ead35a59c8116ee82627a5717c0295f35980662abccb159fe1b37848cb1818e548656bd4fd882d0094a2daab631c76b2b72e3a093914bfe04`
- `6528db2c9a561ada9086268549a600a8a52ff434ea8183812623eec0e9b6c5d3c41ab7868808623021d92294d583afdf92f4354bcdaa1bc50453e1b89afd630d`
- `5d5679fe69bfe74b7919323515e9ecba9d01422b16be9341b57f88e695b2bb0bd7966b781001d2b9e00ee618fdc239c96e17e32cb379f13f12d6bd7b1b47ad04`
- `c37bf24c063f0310eccab8864e48dbeffcdd7240b4f8d1e01eba4fc703e6c9082b845bb55543b10f008dc7f4e78540411912ac1f36fa2aa90011dca40f323b09`
- `7113d4dbb3b688b80e941f365a2c6342d480c77ed03937bccf85dc5cc3554c7517887b1b0c9021388a71e6ca9047aabaaad5ae5b511a2880902568444a98e50b`
- `d0e3fc4bbd1b7320022800af909585aa906f677c4ca79c275a10b6779f669384c464ee84a1b04f13877a25761a874748362c065f4d15a8cab5c5e16c34074403`
- `29b7e0a1f118a6185814a552660c516c43482044e280e7a8de85b8e7e54947e0ae82eb39d7b524d4b72cb9812a7a4b8871964f8f825b1c1ed85d344c05281d0d`
- `3f675d6c364e06405d4868c904e40f3d81c32b083d91586db814d4cb4bf536b4ba209d82f11b4cb6da293b60b20d6122fc3e0e08e80c381dee83edd848211900`

authorizer world:
Expand Down Expand Up @@ -2657,97 +2657,70 @@ result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedB
### token

authority:
symbols: ["fact", "value", "fact2"]
symbols: ["abcD12", "abcD12x"]

public keys: []

```
check if fact(1, $value), 1 == $value;
check if fact2(1, $value), 1 != $value;
check if true == true;
check if false != false;
check if 1 != true;
check if 1 == 1;
check if 1 != 3;
check if 1 != true;
check if "abcD12" == "abcD12";
check if "abcD12x" != "abcD12";
check if "abcD12x" != true;
check if 2022-12-04T09:46:41Z == 2022-12-04T09:46:41Z;
check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z;
check if 2022-12-04T09:46:41Z != true;
check if hex:12abcd == hex:12abcd;
check if hex:12abcd != hex:12ab;
check if hex:12abcd != true;
check if {1, 2} == {1, 2};
check if {1, 4} != {1, 2};
check if {1, 4} != true;
```

### validation for "authorized same type"

authorizer code:
```
fact(1, 1);
fact2(1, 2);

allow if true;
```

revocation ids:
- `d65b3aeceb6268124190f5eb87788a5eb81c89a3fc8370c9a3ea362731c55660b2b390ca6270e68afab90862bd2bbb808aa6b5576c975ae773a992a2434c930d`

authorizer world:
```
World {
facts: [
Facts {
origin: {
None,
},
facts: [
"fact(1, 1)",
"fact2(1, 2)",
],
},
]
rules: []
checks: [
Checks {
origin: Some(
0,
),
checks: [
"check if fact(1, $value), 1 == $value",
"check if fact2(1, $value), 1 != $value",
],
},
]
policies: [
"allow if true",
]
}
```

result: `Ok(0)`
### validation for "unauthorized failed logic different type"
### validation

authorizer code:
```
fact(1, true);
fact2(1, false);

allow if true;
```

revocation ids:
- `d65b3aeceb6268124190f5eb87788a5eb81c89a3fc8370c9a3ea362731c55660b2b390ca6270e68afab90862bd2bbb808aa6b5576c975ae773a992a2434c930d`
- `4af245a2504ec00809bd0cd8d20ceaaac35f8ec5aaa8c7d3fd6652b126d2bf246d64fec8f0e65c409b196d4a60c9723dd4fbb3328988790e97fc4e08e9528208`

authorizer world:
```
World {
facts: [
Facts {
origin: {
None,
},
facts: [
"fact(1, true)",
"fact2(1, false)",
],
},
]
facts: []
rules: []
checks: [
Checks {
origin: Some(
0,
),
checks: [
"check if fact(1, $value), 1 == $value",
"check if fact2(1, $value), 1 != $value",
"check if \"abcD12\" == \"abcD12\"",
"check if \"abcD12x\" != \"abcD12\"",
"check if \"abcD12x\" != true",
"check if 1 != 3",
"check if 1 != true",
"check if 1 != true",
"check if 1 == 1",
"check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z",
"check if 2022-12-04T09:46:41Z != true",
"check if 2022-12-04T09:46:41Z == 2022-12-04T09:46:41Z",
"check if false != false",
"check if hex:12abcd != hex:12ab",
"check if hex:12abcd != true",
"check if hex:12abcd == hex:12abcd",
"check if true == true",
"check if {1, 2} == {1, 2}",
"check if {1, 4} != true",
"check if {1, 4} != {1, 2}",
],
},
]
Expand All @@ -2757,7 +2730,7 @@ World {
}
```

result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "check if fact(1, $value), 1 == $value" })] }))`
result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 1, rule: "check if false != false" })] }))`


------------------------------
Expand Down
8 changes: 4 additions & 4 deletions samples/current/samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -1795,7 +1795,7 @@
"authorizer_code": "allow if true;\n",
"revocation_ids": [
"470e4bf7aa2a01ab39c98150bd06aa15b4aa5d86509044a8809a8634cd8cf2b42269a51a774b65d10bac9369d013070b00187925196a8e680108473f11cf8f03",
"342167bc54bc642b6718a276875e55b6d39e9b21e4ce13b926a3d398b6c057fc436385bf4c817a16f9ecdf0b0d950e8b8258a20aeb3fd8896c5e9c1f0a53da03"
"901b2af4dacf33458d2d91ac484b60bad948e8d10faa9695b096054d5b46e832a977b60b17464cacf545ad0801f549ea454675f0ac88c413406925e2af83ff08"
]
}
}
Expand Down Expand Up @@ -2087,9 +2087,9 @@
"authorizer_code": "check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463;\n\ndeny if query(3);\ndeny if query(1, 2);\ndeny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189;\nallow if true;\n",
"revocation_ids": [
"3771cefe71beb21ead35a59c8116ee82627a5717c0295f35980662abccb159fe1b37848cb1818e548656bd4fd882d0094a2daab631c76b2b72e3a093914bfe04",
"6528db2c9a561ada9086268549a600a8a52ff434ea8183812623eec0e9b6c5d3c41ab7868808623021d92294d583afdf92f4354bcdaa1bc50453e1b89afd630d",
"5d5679fe69bfe74b7919323515e9ecba9d01422b16be9341b57f88e695b2bb0bd7966b781001d2b9e00ee618fdc239c96e17e32cb379f13f12d6bd7b1b47ad04",
"c37bf24c063f0310eccab8864e48dbeffcdd7240b4f8d1e01eba4fc703e6c9082b845bb55543b10f008dc7f4e78540411912ac1f36fa2aa90011dca40f323b09",
"7113d4dbb3b688b80e941f365a2c6342d480c77ed03937bccf85dc5cc3554c7517887b1b0c9021388a71e6ca9047aabaaad5ae5b511a2880902568444a98e50b",
"d0e3fc4bbd1b7320022800af909585aa906f677c4ca79c275a10b6779f669384c464ee84a1b04f13877a25761a874748362c065f4d15a8cab5c5e16c34074403",
"29b7e0a1f118a6185814a552660c516c43482044e280e7a8de85b8e7e54947e0ae82eb39d7b524d4b72cb9812a7a4b8871964f8f825b1c1ed85d344c05281d0d",
"3f675d6c364e06405d4868c904e40f3d81c32b083d91586db814d4cb4bf536b4ba209d82f11b4cb6da293b60b20d6122fc3e0e08e80c381dee83edd848211900"
]
}
Expand Down
Binary file modified samples/current/test024_third_party.bc
Binary file not shown.
Binary file modified samples/current/test026_public_keys_interning.bc
Binary file not shown.
Binary file removed samples/current/test30_null.bc
Binary file not shown.
3 changes: 3 additions & 0 deletions schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ message SignedBlock {
required PublicKey nextKey = 2;
required bytes signature = 3;
optional ExternalSignature externalSignature = 4;
optional uint32 version = 5;
}

message ExternalSignature {
Expand Down Expand Up @@ -218,6 +219,8 @@ message AuthorizerPolicies {
message ThirdPartyBlockRequest {
required PublicKey previousKey = 1;
repeated PublicKey publicKeys = 2;
required bytes previousSignature = 3;

}

message ThirdPartyBlockContents {
Expand Down