mirror of
https://github.com/biscuit-auth/biscuit.git
synced 2024-10-26 06:40:35 +03:00
new cryptographic scheme
This commit is contained in:
parent
7bd158a3ed
commit
d91e055156
472
DESIGN.md
472
DESIGN.md
@ -440,418 +440,124 @@ It reuses the token's symbol table. If new symbols must be added to the
|
|||||||
table when adding facts and rules, the new block will only hold the new
|
table when adding facts and rules, the new block will only hold the new
|
||||||
symbols.
|
symbols.
|
||||||
When serializing the new token, the new block must first be serialized
|
When serializing the new token, the new block must first be serialized
|
||||||
to a byte array via Protobuf encoding. Then a new aggregated signature is created
|
to a byte array via Protobuf encoding. Then a new signature is created
|
||||||
from the previous blocks, the previous aggregated signature and the
|
from the previous blocks, and the next key pair is generated. The new
|
||||||
new key pair for this block. The new serialized token will have the same
|
serialized token will have the same authority block as the previous one,
|
||||||
authority block as the previous one, its blocks field will have the previous
|
its blocks field will have the previous one's blocks with the new block
|
||||||
one's blocks with the new block appended, and the new signature.
|
appended, and the new signature.
|
||||||
|
|
||||||
## Cryptography
|
## Cryptography
|
||||||
|
|
||||||
This design requires a non interactive signature aggregation scheme.
|
This design requires a signature scheme that can be extended without
|
||||||
We have multiple propositions, described in annex to the document.
|
interaction with the origin token creator, so that delegation can
|
||||||
We have not chosen yet which scheme will be used. The choice will
|
|
||||||
depend on the speed on the algorithm (for signature, aggregation and
|
|
||||||
verification), the size of the keys and signatures, and pending
|
|
||||||
an audit.
|
|
||||||
|
|
||||||
The system needs to be non interactive, so that delegation can
|
|
||||||
be done "offline", without talking to the initial authorization
|
be done "offline", without talking to the initial authorization
|
||||||
system, or any of the other participants in the delegation chain.
|
system, or any of the other participants in the delegation chain.
|
||||||
|
|
||||||
A signature aggregation scheme, can take a list of tuples
|
|
||||||
(message, signature, public key), and produce one signature
|
|
||||||
that can be verified with the list of messages and public keys.
|
|
||||||
An additional important property we need here: we cannot get
|
|
||||||
the original signatures from an aggregated one.
|
|
||||||
|
|
||||||
### Biscuit signature scheme
|
### Biscuit signature scheme
|
||||||
|
|
||||||
Assuming we have the following primitives:
|
Biscuit tokens are based on public key cryptography, with a chain of Ed25519
|
||||||
|
signatures. Each block contains the serialized Datalog, the next public key,
|
||||||
|
and the signature by the previous key. The token also contains the private key
|
||||||
|
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.
|
||||||
|
|
||||||
- `Keygen()` can give use a publick key `pk` and a private key `sk`
|
#### Signature (one block)
|
||||||
- `Sign(sk, message)` can give us a signature `S`, with `message` a byte array or arbitrary length
|
|
||||||
- `Aggregate(S1, S2)` can give us an aggregated signature `S`. Additionally, `Aggregate`
|
|
||||||
can be called with an aggregated signature `S` and a single signature `S'`, and return a new
|
|
||||||
aggregated signature `S"`
|
|
||||||
- `Verify([message], [pk], S)` will return true if the signature `S`
|
|
||||||
is valid for the list of messages `[message]` and the list of public keys `[pk]`
|
|
||||||
|
|
||||||
#### First layer of the authorization token
|
* `(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
|
||||||
|
* `sig_0 = sign(sk_0, data_0 + pk_1)`
|
||||||
|
|
||||||
The issuing server performs the following steps:
|
The token will contain:
|
||||||
|
|
||||||
- `(pk1, sk1) <- Keygen()` (done once)
|
|
||||||
- create the first block (we can omit `pk1` from that block, since we assume the
|
|
||||||
token will be verified on a system that knows that public key)
|
|
||||||
- Serialize that first block to `m1`
|
|
||||||
- `S <- Sign(sk1, m1)`
|
|
||||||
- `token1 <- m1||S`
|
|
||||||
|
|
||||||
#### Adding a block to the token
|
|
||||||
|
|
||||||
The holder of a token can attenuate it by adding a new block and
|
|
||||||
signing it, with the following steps:
|
|
||||||
|
|
||||||
- With `token1` containing `[messages]||S`, and a way to get
|
|
||||||
the list of public keys `[pk]` for each block from the blocks, or
|
|
||||||
from the environment
|
|
||||||
- `(pk2, sk2) <- Keygen()`
|
|
||||||
- With `message2` the block we want to add (containing `pk2`, so it
|
|
||||||
can be found in further verifications)`
|
|
||||||
- `S2 <- Sign(sk2, message2)`
|
|
||||||
- `S' <- Aggregate(S, S2)`
|
|
||||||
- `token2 <- [messages]||message2||S'`
|
|
||||||
|
|
||||||
Note: the block can contain `sealed: true` in its keys and values, to
|
|
||||||
indicate a token should not be attenuated further.
|
|
||||||
|
|
||||||
Question: should the previous signature be verified before adding the
|
|
||||||
new block?
|
|
||||||
|
|
||||||
#### Verifying the token
|
|
||||||
|
|
||||||
- With `token` containing `[messages]||S`
|
|
||||||
- extract `[pk]` from `[messages]` and the environment: the first public
|
|
||||||
key should already be known, and for performance reasons, some public keys
|
|
||||||
could also be present in the list of common keys and values
|
|
||||||
- `b <- Verify([messages], [pk], S)`
|
|
||||||
- if `b` is true, the signature is valid
|
|
||||||
- proceed to validating rights
|
|
||||||
|
|
||||||
### Sealed Biscuit scheme
|
|
||||||
|
|
||||||
In some cases, we might want to convert the token to a symmetric key based
|
|
||||||
token that cannot be attenuated further. Common use case: contact the verifier
|
|
||||||
once, the verifier checks the signature, and generates from it a short lived
|
|
||||||
token with the same authorization, but that can be checked much faster than
|
|
||||||
public key based tokens.
|
|
||||||
|
|
||||||
TODO: specify an AEAD scheme that would be usable for this
|
|
||||||
|
|
||||||
## Annex 1: Cryptographic design proposals
|
|
||||||
|
|
||||||
### Pairing based cryptography
|
|
||||||
|
|
||||||
proposed by @geal
|
|
||||||
|
|
||||||
Assuming we have a pairing e: G1 x G2 -> Gt with G1 and G2 two additive cyclic groups of prime order q, Gt a multiplicative cyclic group of order q
|
|
||||||
with a, b from Fq* finite field of order q
|
|
||||||
with P from G1, Q from G2
|
|
||||||
|
|
||||||
We have the following properties:
|
|
||||||
- `e(aP, bQ) == e(P, Q)^(ab)`
|
|
||||||
- `e != 1`
|
|
||||||
|
|
||||||
More specifically:
|
|
||||||
|
|
||||||
- `e(aP, Q) == e(P, aQ) == e(P,Q)^a`
|
|
||||||
- `e(P1 + P2, Q) == e(P1, Q) * e(P2, Q)`
|
|
||||||
|
|
||||||
#### Signature
|
|
||||||
|
|
||||||
- choose k from Fq* as private key, g2 a generator of G2
|
|
||||||
- public key P = k*g2
|
|
||||||
|
|
||||||
- Signature S = k*H1(message) with H1 function to hash message to G1
|
|
||||||
- Verifying: knowing message, P and S
|
|
||||||
```
|
|
||||||
e(S, g2) == e( k*H1(message), g2)
|
|
||||||
== e( H1(message), k*g2)
|
|
||||||
== e( H1(message), P)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Signature aggregation
|
|
||||||
|
|
||||||
- knowing messages m1 and m2, public keys P1 and P2
|
|
||||||
- signatures S1 = Sign(k1, m1), S2 = Sign(k2, m2)
|
|
||||||
- the aggregated signature S = S1 + S2
|
|
||||||
|
|
||||||
Verifying:
|
|
||||||
```
|
|
||||||
e(S, g2) == e(S1+S2, g2)
|
|
||||||
== e(S1, g2)*e(S2, g2)
|
|
||||||
== e(k1*H1(m1), g2) * e(k2*HA(m2), g2)
|
|
||||||
== e(H1(m1), k1*g2) * e(H1(m2), k2*g2)
|
|
||||||
== e(H1(m1), P1) * e(H1(m2), P2)
|
|
||||||
```
|
|
||||||
|
|
||||||
so we calculate signature verification pairing for every caveat
|
|
||||||
then we multiply the result and check equality
|
|
||||||
|
|
||||||
we use curve BLS12-381 (Boneh Lynn Shacham) for security reasons
|
|
||||||
(cf https://github.com/zcash/zcash/issues/2502
|
|
||||||
for comparions with Barreto Naehrig curves)
|
|
||||||
assumes computational Diffe Hellman is hard
|
|
||||||
|
|
||||||
Performance is not stellar (with the pairing crate, we can
|
|
||||||
spend 30ms verifying a token with 3 blocks, with mcl 1.7ms).
|
|
||||||
|
|
||||||
Example of library this can be implemented with:
|
|
||||||
- pairing crate: https://github.com/zkcrypto/pairing
|
|
||||||
- mcl: https://github.com/herumi/mcl
|
|
||||||
|
|
||||||
### Elliptic curve verifiable random functions
|
|
||||||
|
|
||||||
proposed by @KellerFuchs
|
|
||||||
|
|
||||||
https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04
|
|
||||||
|
|
||||||
Using the primitives defined in https://tools.ietf.org/html/draft-irtf-cfrg-vrf-04#section-5 :
|
|
||||||
|
|
||||||
```
|
```
|
||||||
F - finite field
|
Token {
|
||||||
2n - length, in octets, of a field element in F
|
root_key_id: <optional number indicating the root key to use for verification>
|
||||||
E - elliptic curve (EC) defined over F
|
authority: Block {
|
||||||
m - length, in octets, of an EC point encoded as an octet string
|
data_0,
|
||||||
G - subgroup of E of large prime order
|
pk_1,
|
||||||
q - prime order of group G
|
sig_0,
|
||||||
cofactor - number of points on E divided by q
|
}
|
||||||
g - generator of group G
|
blocks: [],
|
||||||
Hash - cryptographic hash function
|
proof: Proof {
|
||||||
hLen - output length in octets of Hash
|
nextSecret: sk_1,
|
||||||
```
|
},
|
||||||
|
}```
|
||||||
|
|
||||||
Constraints on options:
|
#### Signature (appending)
|
||||||
|
|
||||||
Field elements in F have bit lengths divisible by 16
|
With a token containing blocks 0 to n:
|
||||||
|
|
||||||
hLen is equal to 2n
|
Block n contains:
|
||||||
|
- `data_n`
|
||||||
|
- `pk_n+1`
|
||||||
|
- `sig_n`
|
||||||
|
|
||||||
Steps:
|
The token also contains `sk_n+1`
|
||||||
|
|
||||||
Keygen:
|
We generate at random `(pk_n+2, sk_n+2)` and the signature `sig_n+1 = sign(sk_n+1, data_n+1 + pk_n+2)`
|
||||||
`(pk, sk) <- Keygen()`: sk random x with 0 < x < q
|
|
||||||
|
|
||||||
#### Basic EC-VRF behaviour
|
The token will contain:
|
||||||
|
|
||||||
Sign(pk, sk, message):
|
|
||||||
|
|
||||||
creating a proof pi = ECVRF_prove(pk, sk, message):
|
|
||||||
|
|
||||||
- h = ECVRF_hash_to_curve(pk, message)
|
|
||||||
- gamma = h^sk
|
|
||||||
- k = ECVRF_nonce(sk, h)
|
|
||||||
- c = ECVRF_hash_points(h, gamma, g^k, h^k)
|
|
||||||
- s = k + c * sk mod q
|
|
||||||
- pi = (gamma, c, s)
|
|
||||||
|
|
||||||
Verify(pk, pi, message) for one message and its signature:
|
|
||||||
|
|
||||||
- (gamma, c, s) = pi
|
|
||||||
```
|
|
||||||
u = pk^-c * g^s
|
|
||||||
= g^(sk*-c)*g^(k + c*sk)
|
|
||||||
= g^k
|
|
||||||
```
|
|
||||||
- h = ECVRF_hash_to_curve(pk, message)
|
|
||||||
```
|
|
||||||
v = gamma^-c * h^s
|
|
||||||
= h^(sk*-c)*h^(k + c*sk)
|
|
||||||
= h^k
|
|
||||||
```
|
|
||||||
- c' = ECVRF_hash_points(h, gamma, u, v)
|
|
||||||
- return c == c'
|
|
||||||
|
|
||||||
#### Aggregating signatures
|
|
||||||
|
|
||||||
Sign:
|
|
||||||
|
|
||||||
First block: Sign0(pk, sk, message)
|
|
||||||
- `h = ECVRF_hash_to_curve(pk, message)`
|
|
||||||
- `gamma = h^sk`
|
|
||||||
- `k = ECVRF_nonce(sk, h)`
|
|
||||||
- `c = ECVRF_hash_points(h, gamma, g^k, h^k)`
|
|
||||||
- `s = k + c * sk mod q`
|
|
||||||
- `W = 1`
|
|
||||||
- `S = s`
|
|
||||||
- `PI_0 = ([gamma], [c], S, W)`
|
|
||||||
|
|
||||||
Block n+1: Sign( pk_(n+1), sk_(n+1), message_(n+1), PI_n):
|
|
||||||
- `([gamma_i], [c_i], S_n, W_n) = PI_n`
|
|
||||||
- `h_(n+1) = ECVRF_hash_to_curve(pk_(n+1), message_(n+1))`
|
|
||||||
- `gamma_(n+1) = h_(n+1)^sk_(n+1)`
|
|
||||||
- `k = ECVRF_nonce(sk, h)`
|
|
||||||
```
|
|
||||||
u_n = pk_0^-c_0 * .. * pk_n^-c_n * g^S_n
|
|
||||||
= g^(sk_0*-c_0) * .. * g^(sk_n*-c_n) * g^(k_0 + sk0*c_0 + .. + k_n + sk_n*c_n)
|
|
||||||
= g^(k_0 + .. + k_n)
|
|
||||||
|
|
||||||
v_n = W* gamma_0^-c_0 * h_0^S * .. * gamma_n^-c_n * h_n^S
|
|
||||||
= h_0^(s_0 - S) * .. * h_n^(s_0 - S) * h_0^(sk_0*-c_0 + S) * .. * h_n^(sk_n*-c_n + S)
|
|
||||||
= h_0^(k_0 + sk_0*c_0 - S - sk_0*c_0 + S) * .. * h_n^(k_n + sk_n*c_n - S - sk_n*c_n + S)
|
|
||||||
= h_0^k_0 * .. * h_n^k_n
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
```
|
||||||
c_(n+1) = ECVRF_hash_points(g, h_(n+1), pk_0 * .. * pk_(n+1) ,
|
Token {
|
||||||
gamma_0 * .. * gamma_(n+1), u_n * g^k_(n+1), v_n * h_(n+1)^k_(n+1))
|
root_key_id: <optional number indicating the root key to use for verification>
|
||||||
```
|
authority: Block_0,
|
||||||
- `s_(n+1) = k_(n+1) + c_(n+1) * sk_(n+1) mod q`
|
blocks: [Block_1, .., Block_n,
|
||||||
- `S_(n+1) = S_n + s_(n+1)`
|
Block_n+1 {
|
||||||
- `W_(n+1) = W_n * (h_0 * .. * h_n)^(-s_(n+1)) * h_(n+1)^(-Sn) == h_0^(s_0 - S_(n+1)) * .. * h_(n+1)^(s_(n+1) - S_(n+1))`
|
data_n+1,
|
||||||
- `PI_(n+1) = ([gamma_i], [c_i], S_(n+1), W_(n+1))`
|
pk_n+2,
|
||||||
|
sig_n+1,
|
||||||
|
}]
|
||||||
|
proof: Proof {
|
||||||
|
nextSecret: sk_n+2,
|
||||||
|
},
|
||||||
|
}```
|
||||||
|
|
||||||
Verify([pk], PI, [message]) (with n blocks):
|
#### Verifying
|
||||||
|
|
||||||
|
For each block i from 0 to n:
|
||||||
|
|
||||||
Aggregate(pk', pi', [pk], PI) with [pk] list of public keys and PI aggregated signature:
|
- verify(pk_i, sig_i, data_i+pk_i+1)
|
||||||
- `([gamma], [c], S, W, C) = PI`
|
|
||||||
- check that `n = |[pk]| == |[message]| == |[gamma]| == |[c]|`
|
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
|
||||||
U = pk_0^-c_0 * .. * pk_n^-c_n * g^S
|
key pair.
|
||||||
= g^(sk_0*-c_0) * .. * g^(sk_n*-c_n) * g^(k_0 + sk0*c_0 + .. + k_n + sk_n*c_n)
|
|
||||||
= g^(k_0 + .. + k_n)
|
#### Signature (appending)
|
||||||
```
|
|
||||||
|
With a token containing blocks 0 to n:
|
||||||
|
|
||||||
|
Block n contains:
|
||||||
|
- `data_n`
|
||||||
|
- `pk_n+1`
|
||||||
|
- `sig_n`
|
||||||
|
|
||||||
|
The token also contains `sk_n+1`
|
||||||
|
|
||||||
|
We generate the signature `sig_n+1 = sign(sk_n+1, data_n + pk_n+1 + sig_n)` (we sign
|
||||||
|
the last block with the last private key).
|
||||||
|
|
||||||
|
The token will contain:
|
||||||
|
|
||||||
```
|
```
|
||||||
V = W* gamma_0^-c_0 * h_0^S * .. * gamma_n^-c_n * h_n^S
|
Token {
|
||||||
= h_0^(s_0 - S) * .. * h_n^(s_0 - S) * h_0^(sk_0*-c_0 + S) * .. * h_n^(sk_n*-c_n + S)
|
root_key_id: <optional number indicating the root key to use for verification>
|
||||||
= h_0^(k_0 + sk_0*c_0 - S - sk_0*c_0 + S) * .. * h_n^(k_n + sk_n*c_n - S - sk_n*c_n + S)
|
authority: Block_0,
|
||||||
= h_0^k_0 * .. * h_n^k_n
|
blocks: [Block_1, .., Block_n]
|
||||||
```
|
proof: Proof {
|
||||||
- `C = ECVRF_hash_points(h_n, gamma_0 * .. * gamma_n, U, V)`
|
finalSignature: sig_n+1
|
||||||
- verify that `C == c_n`
|
},
|
||||||
|
}
|
||||||
### Elliptic curve verifiable random functions: second method
|
|
||||||
|
|
||||||
This is a variant of the previous scheme, for which the product of
|
|
||||||
gamma points is precalculated, so that we do not need to do it to
|
|
||||||
aggregate a new signature or verify it. This also reduces the size
|
|
||||||
of the signature.
|
|
||||||
|
|
||||||
Same primitives as before:
|
|
||||||
|
|
||||||
```
|
|
||||||
F - finite field
|
|
||||||
2n - length, in octets, of a field element in F
|
|
||||||
E - elliptic curve (EC) defined over F
|
|
||||||
m - length, in octets, of an EC point encoded as an octet string
|
|
||||||
G - subgroup of E of large prime order
|
|
||||||
q - prime order of group G
|
|
||||||
cofactor - number of points on E divided by q
|
|
||||||
g - generator of group G
|
|
||||||
Hash - cryptographic hash function
|
|
||||||
hLen - output length in octets of Hash
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Constraints on options:
|
#### Verifying (sealed)
|
||||||
|
|
||||||
Field elements in F have bit lengths divisible by 16
|
For each block i from 0 to n:
|
||||||
|
|
||||||
hLen is equal to 2n
|
- verify(pk_i, sig_i, data_i+pk_i+1)
|
||||||
|
|
||||||
Steps:
|
If all signatures are verified, extract pk_n+1 from the last block and
|
||||||
|
sig from the proof field, and check `verify(pk_n+1, sig_n+1, data_n+pk_n+1+sig_n)`
|
||||||
Keygen:
|
|
||||||
`(pk, sk) <- Keygen()`: sk random x with 0 < x < q
|
|
||||||
|
|
||||||
#### Aggregating signatures
|
|
||||||
|
|
||||||
Sign:
|
|
||||||
|
|
||||||
First block: Sign0(pk, sk, message)
|
|
||||||
- `h = ECVRF_hash_to_curve(pk, message)`
|
|
||||||
- `gamma = h^sk`
|
|
||||||
- `k = ECVRF_nonce(sk, h)`
|
|
||||||
- `c = ECVRF_hash_points(h, pk, g^k, h^k)`
|
|
||||||
- `s = k + c * sk mod q`
|
|
||||||
- `W = 1`
|
|
||||||
- `S = s`
|
|
||||||
- `PI_0 = (-c * gamma, [c], S, W)`
|
|
||||||
|
|
||||||
Block n+1: Sign( pk_(n+1), sk_(n+1), message_(n+1), PI_n):
|
|
||||||
- `(gamma_agg, [c_i], S_n, W_n) = PI_n`
|
|
||||||
- `h_(n+1) = ECVRF_hash_to_curve(pk_(n+1), message_(n+1))`
|
|
||||||
- `gamma_(n+1) = h_(n+1)^sk_(n+1)`
|
|
||||||
- `k = ECVRF_nonce(sk, h)`
|
|
||||||
```
|
|
||||||
u_n = pk_0^-c_0 * .. * pk_n^-c_n * g^S
|
|
||||||
= g^(sk_0*-c_0) * .. * g^(sk_n*-c_n) * g^(k_0 + sk0*c_0 + .. + k_n + sk_n*c_n)
|
|
||||||
= g^(k_0 + .. + k_n)
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
v_n = W * gamma_agg * h_0^S * ... * h_n^S
|
|
||||||
= W * gamma_0^-c_0 * h_0^S * .. * gamma_n^-c_n * h_n^S
|
|
||||||
= h_0^(s_0 - S) * .. * h_n^(s_0 - S) * h_0^(sk_0*-c_0 + S) * .. * h_n^(sk_n*-c_n + S)
|
|
||||||
= h_0^(k_0 + sk_0*c_0 - S - sk_0*c_0 + S) * .. * h_n^(k_n + sk_n*c_n - S - sk_n*c_n + S)
|
|
||||||
= h_0^k_0 * .. * h_n^k_n
|
|
||||||
```
|
|
||||||
```
|
|
||||||
c_(n+1) = ECVRF_hash_points(g, h_(n+1), pk_0 * .. * pk_(n+1) ,
|
|
||||||
u_n * g^k, v_n * h^k)
|
|
||||||
```
|
|
||||||
- `s_(n+1) = k_(n+1) - c_(n+1) * sk_(n+1) mod q`
|
|
||||||
- `S_(n+1) = S_n + s_(n+1)`
|
|
||||||
- `W_(n+1) = W_n * (h_0 * .. * h_n)^(-s_(n+1)) * h_(n+1)^(-Sn) == h_0^(s_0 - S_(n+1)) * .. * h_(n+1)^(s_(n+1) - S_(n+1))`
|
|
||||||
- `PI_(n+1) = (gamma_agg * (-c_(n+1) * gamma_(n+1)), [c_i], S_(n+1), W_(n+1))`
|
|
||||||
|
|
||||||
|
|
||||||
Verify([pk], PI, [message]) (with n blocks):
|
|
||||||
|
|
||||||
|
|
||||||
Aggregate(pk', pi', [pk], PI) with [pk] list of public keys and PI aggregated signature:
|
|
||||||
- `([gamma], [c], S, W, C) = PI`
|
|
||||||
- check that `n = |[pk]| == |[message]| == |[gamma]| == |[c]|`
|
|
||||||
```
|
|
||||||
u = pk_0^-c_0 * .. * pk_n^-c_n * g^S
|
|
||||||
= g^(sk_0*-c_0) * .. * g^(sk_n*-c_n) * g^(k_0 + sk0*c_0 + .. + k_n + sk_n*c_n)
|
|
||||||
= g^(k_0 + .. + k_n)
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
v = W * gamma_agg * h_0^S * ... * h_n^S
|
|
||||||
= W * gamma_0^-c_0 * h_0^S * .. * gamma_n^-c_n * h_n^S
|
|
||||||
= h_0^(s_0 - S) * .. * h_n^(s_0 - S) * h_0^(sk_0*-c_0 + S) * .. * h_n^(sk_n*-c_n + S)
|
|
||||||
= h_0^(k_0 + sk_0*c_0 - S - sk_0*c_0 + S) * .. * h_n^(k_n + sk_n*c_n - S - sk_n*c_n + S)
|
|
||||||
= h_0^k_0 * .. * h_n^k_n
|
|
||||||
```
|
|
||||||
- `C = ECVRF_hash_points(h_n, pk_0 * ... pk_n, U, V)`
|
|
||||||
- verify that `C == c_n`
|
|
||||||
|
|
||||||
### Challenge tokens
|
|
||||||
|
|
||||||
Another method based on a more classical PKI, where the token contains
|
|
||||||
the secret key of the last caveat. To send the token for verification,
|
|
||||||
that key is used to sign the token with a nonce and current time, to
|
|
||||||
prove that we own it. We send the token without the key, but with the
|
|
||||||
signature. The verification token cannot be further attenuated.
|
|
||||||
|
|
||||||
Here's a description of the scheme:
|
|
||||||
|
|
||||||
```
|
|
||||||
(pk1, sk1) = keygen()
|
|
||||||
(pk2, sk2) = keygen()
|
|
||||||
s1 = sign(sk1, caveat1+pk2)
|
|
||||||
token1=caveat1+pk2+s1+sk2
|
|
||||||
```
|
|
||||||
|
|
||||||
Minting a new token
|
|
||||||
```
|
|
||||||
(pk3, sk3) = keygen()
|
|
||||||
s2 = sign(sk2, caveat2+pk3)
|
|
||||||
token2=caveat1+pk2+s1+caveat2+pk3+s2+sk3
|
|
||||||
```
|
|
||||||
|
|
||||||
Sending token2 for verification:
|
|
||||||
```
|
|
||||||
verif_token2=caveat1+pk2+s1+caveat2+pk3+s2
|
|
||||||
h = sign(sk3, nonce+time+verif_token2)
|
|
||||||
sending verif_token2+h
|
|
||||||
```
|
|
||||||
|
|
||||||
The verifier knows pk1 and can check the chain, and h allows checking that we hold sk3
|
|
||||||
|
|
||||||
### Gamma signatures
|
|
||||||
|
|
||||||
proposed by @tarcieri
|
|
||||||
|
|
||||||
Yao, A. C.-C., & Yunlei Zhao. (2013). Online/Offline Signatures for Low-Power Devices. IEEE Transactions on Information Forensics and Security, 8(2), 283–294.
|
|
||||||
Aggregation of Gamma-Signatures and Applications to Bitcoin, Yunlei Zhao https://eprint.iacr.org/2018/414.pdf
|
|
||||||
|
|
||||||
### BIP32 derived keys
|
|
||||||
|
|
||||||
proposed by @tarcieri
|
|
||||||
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
|
||||||
|
|
||||||
|
@ -437,43 +437,37 @@ transmitted over the wire is either the normal Biscuit wrapper:
|
|||||||
|
|
||||||
```proto
|
```proto
|
||||||
message Biscuit {
|
message Biscuit {
|
||||||
required bytes authority = 1;
|
optional uint32 rootKeyId = 1;
|
||||||
repeated bytes blocks = 2;
|
required SignedBlock authority = 2;
|
||||||
repeated bytes keys = 3;
|
repeated SignedBlock blocks = 3;
|
||||||
required Signature signature = 4;
|
required Proof proof = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Signature {
|
message SignedBlock {
|
||||||
repeated bytes parameters = 1;
|
required bytes block = 1;
|
||||||
required bytes z = 2;
|
required bytes nextKey = 2;
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `keys` and `parameters` arrays contain Ristretto points in their
|
|
||||||
canonical representation, serialized to a 32 bytes array[CompressedRistretto].
|
|
||||||
Thee `z` field is a 32 bytes array containing the canonical representation
|
|
||||||
of an element of Ristretto's scalar field[Scalar].
|
|
||||||
|
|
||||||
The `keys` field contains the public keys used to sign each block. It contains
|
|
||||||
as many elements as the `blocks` field plus one. The first element is the root key.
|
|
||||||
|
|
||||||
The `parameters` field must have as many elements as the `keys` field. All of
|
|
||||||
their elements must be distinct.
|
|
||||||
|
|
||||||
or the "sealed" Biscuit wrapper (a token that cannot be attenuated offline):
|
|
||||||
|
|
||||||
```proto
|
|
||||||
message SealedBiscuit {
|
|
||||||
required bytes authority = 1;
|
|
||||||
repeated bytes blocks = 2;
|
|
||||||
required bytes signature = 3;
|
required bytes signature = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Proof {
|
||||||
|
oneof Content {
|
||||||
|
bytes nextSecret = 1;
|
||||||
|
bytes finalSignature = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The signature part of those tokens covers the content of authority and
|
The `rootKeyId` is a hint to decide which root public key should be used
|
||||||
blocks members.
|
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.
|
||||||
|
|
||||||
Those members are byte arrays, containing `Block` structures serialized
|
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
|
||||||
|
block by the private key (sealed tokens).
|
||||||
|
|
||||||
|
The `block` field is a byte array, containing a `Block` structure serialized
|
||||||
in Protobuf format as well:
|
in Protobuf format as well:
|
||||||
|
|
||||||
```proto
|
```proto
|
||||||
@ -528,92 +522,111 @@ or `sealed-biscuit:` accordingly.
|
|||||||
|
|
||||||
### Cryptography
|
### Cryptography
|
||||||
|
|
||||||
#### Attenuable tokens
|
Biscuit tokens are based on public key cryptography, with a chain of Ed25519
|
||||||
|
signatures. Each block contains the serialized Datalog, the next public key,
|
||||||
|
and the signature by the previous key. The token also contains the private key
|
||||||
|
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.
|
||||||
|
|
||||||
Those tokens are based on public key cryptography, specifically aggregated
|
#### Signature (one block)
|
||||||
gamma signatures[Aggregated Gamma Signatures]. Signature aggregation allows
|
|
||||||
Biscuit to make a new token with a valid signature from an existing one,
|
|
||||||
by signing the new data and adding the new signature to the old one.
|
|
||||||
|
|
||||||
Every public key operation in Biscuit is defined over the Ristretto prime
|
* `(pk_0, sk_0)` the root public and private Ed25519 keys
|
||||||
order group[Ristretto], that is designed to prevent some implementation
|
* `data_0` the serialized Datalog
|
||||||
mistakes.
|
* `(pk_1, sk_1)` the next key pair, generated at random
|
||||||
|
* `sig_0 = sign(sk_0, data_0 + pk_1)`
|
||||||
|
|
||||||
Definitions:
|
The token will contain:
|
||||||
- `R`: Ristretto group
|
|
||||||
- `l`: order of the Ristretto group
|
|
||||||
- `Z/l`: scalar of order `l` associated to the Ristretto group
|
|
||||||
- `P`: Ristretto base point
|
|
||||||
- `H1`: point hashing function
|
|
||||||
- `H2`: message hashing function
|
|
||||||
|
|
||||||
##### Key generation
|
```
|
||||||
|
Token {
|
||||||
Private key:
|
root_key_id: <optional number indicating the root key to use for verification>
|
||||||
`x <- Z/l*` chosen at random
|
authority: Block {
|
||||||
|
data_0,
|
||||||
Public key:
|
pk_1,
|
||||||
`X = sk * P`
|
sig_0,
|
||||||
|
}
|
||||||
##### Signature (one block)
|
blocks: [],
|
||||||
|
proof: Proof {
|
||||||
With secret key `x`, public key `X`, message `message`:
|
nextSecret: sk_1,
|
||||||
|
},
|
||||||
* `r <- Z/l*` chosen at random
|
}```
|
||||||
* `A = r * P`
|
|
||||||
* `d = H1(A)`
|
|
||||||
* `e = H2(X, message)`
|
|
||||||
* `z = rd - ex mod l`
|
|
||||||
|
|
||||||
The signature is `([A], z)`. The `[A]` array corresponds to the `parameters`
|
|
||||||
field in the protobuf schema.
|
|
||||||
|
|
||||||
#### Signature (appending)
|
#### Signature (appending)
|
||||||
With `([A0, ..., An], s)` the current signature:
|
|
||||||
|
|
||||||
Same process as the signature for a single block,
|
With a token containing blocks 0 to n:
|
||||||
with secret key `x`, public key `X`, message `message`:
|
|
||||||
|
|
||||||
* `r <- Z/l*` chosen at random
|
Block n contains:
|
||||||
* `A = r * P`
|
- `data_n`
|
||||||
* `d = H1(A)`
|
- `pk_n+1`
|
||||||
* `e = H2(X, message)`
|
- `sig_n`
|
||||||
* `z = rd - ex mod l`
|
|
||||||
|
|
||||||
The new signature is `([A0, ..., An, A], s + z)`
|
The token also contains `sk_n+1`
|
||||||
|
|
||||||
|
We generate at random `(pk_n+2, sk_n+2)` and the signature `sig_n+1 = sign(sk_n+1, data_n+1 + pk_n+2)`
|
||||||
|
|
||||||
|
The token will contain:
|
||||||
|
|
||||||
|
```
|
||||||
|
Token {
|
||||||
|
root_key_id: <optional number indicating the root key to use for verification>
|
||||||
|
authority: Block_0,
|
||||||
|
blocks: [Block_1, .., Block_n,
|
||||||
|
Block_n+1 {
|
||||||
|
data_n+1,
|
||||||
|
pk_n+2,
|
||||||
|
sig_n+1,
|
||||||
|
}]
|
||||||
|
proof: Proof {
|
||||||
|
nextSecret: sk_n+2,
|
||||||
|
},
|
||||||
|
}```
|
||||||
|
|
||||||
#### Verifying
|
#### Verifying
|
||||||
|
|
||||||
With:
|
For each block i from 0 to n:
|
||||||
|
|
||||||
* `([A0, ..., An], s)` the current signature
|
- verify(pk_i, sig_i, data_i+pk_i+1)
|
||||||
* `[P0, ..., Pn]` the list of public keys
|
|
||||||
* `[m0, ..., mn]` the list of messages
|
|
||||||
|
|
||||||
We verify as follows:
|
If all signatures are verified, extract pk_n+1 from the last block and
|
||||||
* check that `|[A0, ..., An]| == |[P0, ..., Pn]| == |[m0, ..., mn]|`
|
sk_n+1 from the proof field, and check that they are from the same
|
||||||
* check that `P0` is the root public key we are expecting
|
key pair.
|
||||||
* check that `[A0, ..., An]` are distinct
|
|
||||||
* check that `[(P0, m0), ..., (Pn, mn)]` are distinct
|
|
||||||
* `X = H2(P0, m0) * P0 + ... + H2(Pn, mn) * Pn - ( H1(A0) * A0 + ... + H1(An) * An )`
|
|
||||||
* if `s * P + X` is the point at infinite, the signature is verified
|
|
||||||
|
|
||||||
##### Point hashing
|
#### Signature (appending)
|
||||||
|
|
||||||
`H1(X) = Scalar::from_hash(sha512(X.compress().to_bytes()))`
|
With a token containing blocks 0 to n:
|
||||||
|
|
||||||
##### Message hashing
|
Block n contains:
|
||||||
|
- `data_n`
|
||||||
|
- `pk_n+1`
|
||||||
|
- `sig_n`
|
||||||
|
|
||||||
`H2(X, message) = Scalar::from_hash(sha512(X.compress().to_bytes()|message))` (with `|` the concatenation operator)
|
The token also contains `sk_n+1`
|
||||||
|
|
||||||
#### Sealed tokens
|
We generate the signature `sig_n+1 = sign(sk_n+1, data_n + pk_n+1 + sig_n)` (we sign
|
||||||
|
the last block with the last private key).
|
||||||
|
|
||||||
A sealed token contains the same kind of block as regular tokens,
|
The token will contain:
|
||||||
but it cannot be attenuated offline, and can only be verified by
|
|
||||||
knowing the secret used to create it.
|
|
||||||
|
|
||||||
The signature is the HMAC-SHA256 hash of the secret key and the
|
```
|
||||||
concatenation of all the blocks.
|
Token {
|
||||||
|
root_key_id: <optional number indicating the root key to use for verification>
|
||||||
|
authority: Block_0,
|
||||||
|
blocks: [Block_1, .., Block_n]
|
||||||
|
proof: Proof {
|
||||||
|
finalSignature: sig_n+1
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verifying (sealed)
|
||||||
|
|
||||||
|
For each block i from 0 to n:
|
||||||
|
|
||||||
|
- verify(pk_i, sig_i, data_i+pk_i+1)
|
||||||
|
|
||||||
|
If all signatures are verified, extract pk_n+1 from the last block and
|
||||||
|
sig from the proof field, and check `verify(pk_n+1, sig_n+1, data_n+pk_n+1+sig_n)`
|
||||||
|
|
||||||
### Blocks
|
### Blocks
|
||||||
|
|
||||||
@ -685,8 +698,4 @@ We provide sample tokens and the expected result of their verification at
|
|||||||
- DATALOG: "Datalog with Constraints: A Foundation for Trust Management Languages" http://crypto.stanford.edu/~ninghui/papers/cdatalog_padl03.pdf
|
- DATALOG: "Datalog with Constraints: A Foundation for Trust Management Languages" http://crypto.stanford.edu/~ninghui/papers/cdatalog_padl03.pdf
|
||||||
- Trust Management Languages" https://www.cs.purdue.edu/homes/ninghui/papers/cdatalog_padl03.pdf
|
- Trust Management Languages" https://www.cs.purdue.edu/homes/ninghui/papers/cdatalog_padl03.pdf
|
||||||
- MACAROONS: "Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud" https://ai.google/research/pubs/pub41892
|
- MACAROONS: "Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud" https://ai.google/research/pubs/pub41892
|
||||||
- Aggregated Gamma Signatures: "Aggregation of Gamma-Signatures and Applications to Bitcoin, Yunlei Zhao" https://eprint.iacr.org/2018/414.pdf
|
|
||||||
- Ristretto: "Ristretto: prime order elliptic curve groups with non-malleable encodings" https://ristretto.group
|
|
||||||
- Scalar: https://doc.dalek.rs/curve25519_dalek/scalar/struct.Scalar.html
|
|
||||||
- CompressedRistretto: https://doc.dalek.rs/curve25519_dalek/ristretto/struct.CompressedRistretto.html
|
|
||||||
|
|
||||||
|
1048
samples/v2/README.md
Normal file
1048
samples/v2/README.md
Normal file
File diff suppressed because it is too large
Load Diff
611
samples/v2/samples.json
Normal file
611
samples/v2/samples.json
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
{
|
||||||
|
"root_private_key": "12aca40167fbdd1a11037e9fd440e3d510d9d9dea70a6646aa4aaf84d718d75a",
|
||||||
|
"root_public_key": "acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189",
|
||||||
|
"testcases": [
|
||||||
|
{
|
||||||
|
"title": "basic token",
|
||||||
|
"filename": "test1_basic.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\", \"write\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read),\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"resource(#ambient, \"file1\")",
|
||||||
|
"revocation_id(0, hex:415b9d4bcfbcc052eb30b66bed5151a7291bd3ededa8679140753f97d9a0b3e6)",
|
||||||
|
"revocation_id(1, hex:057ef57833aac9fb405ba1abadca1b088f2557700ea2004c79004ea688abeb47)",
|
||||||
|
"right(#authority, \"file1\", #read)",
|
||||||
|
"right(#authority, \"file1\", #write)",
|
||||||
|
"right(#authority, \"file2\", #read)",
|
||||||
|
"unique_revocation_id(0, hex:415b9d4bcfbcc052eb30b66bed5151a7291bd3ededa8679140753f97d9a0b3e6)",
|
||||||
|
"unique_revocation_id(1, hex:057ef57833aac9fb405ba1abadca1b088f2557700ea2004c79004ea688abeb47)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 1, check_id: 0, rule: \"check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "different root key",
|
||||||
|
"filename": "test2_different_root_key.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Format(Signature(InvalidSignature(\"signature error\")))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "invalid signature format",
|
||||||
|
"filename": "test3_invalid_signature_format.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\", \"write\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read),\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Format(InvalidSignatureSize(16))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "random block",
|
||||||
|
"filename": "test4_random_block.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\", \"write\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read),\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Format(Signature(InvalidSignature(\"signature error\")))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "invalid signature",
|
||||||
|
"filename": "test5_invalid_signature.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\", \"write\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read),\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Format(Signature(InvalidSignature(\"signature error\")))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "reordered blocks",
|
||||||
|
"filename": "test6_reordered_blocks.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\", \"write\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read),\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n }\n ]\n}",
|
||||||
|
"biscuit3 (2 checks)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\", \"check2\"]\n authority: Block[0] {\n symbols: [\"read\", \"write\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read),\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), operation(#ambient, #read), right(#authority, $0, #read)\n ]\n },\n\tBlock[2] {\n symbols: [\"check2\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, \"file1\")\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Format(Signature(InvalidSignature(\"signature error\")))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "invalid block fact with authority tag",
|
||||||
|
"filename": "test7_invalid_block_fact_authority.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"write\", \"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #write)\n ]\n rules: []\n checks: [\n check if operation(#ambient, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"FailedLogic(InvalidBlockFact(0, \"right(#authority, \\\"file1\\\", #write)\"))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "invalid block fact with ambient tag",
|
||||||
|
"filename": "test8_invalid_block_fact_ambient.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"write\", \"check1\", \"0\"]\n authority: Block[0] {\n symbols: [\"read\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"write\", \"check1\", \"0\"]\n version: 1\n context: \"\"\n facts: [\n right(#ambient, \"file1\", #write)\n ]\n rules: []\n checks: [\n check if operation(#ambient, #read)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"FailedLogic(InvalidBlockFact(0, \"right(#ambient, \\\"file1\\\", #write)\"))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "expired token",
|
||||||
|
"filename": "test9_expired_token.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"check1\", \"expiration\", \"date\", \"time\"]\n authority: Block[0] {\n symbols: []\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"expiration\", \"date\", \"time\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, \"file1\"),\n check if time(#ambient, $date), $date <= 2018-12-20T00:00:00+00:00\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"operation(#ambient, #read)",
|
||||||
|
"resource(#ambient, \"file1\")",
|
||||||
|
"revocation_id(0, hex:96123a8ee182336c4c63ad29f2b23549020da2a90841ac63ccec4c20413753b0)",
|
||||||
|
"revocation_id(1, hex:60e6f54cb7a20ee0859495abe176da0306dfe91b4ee270244dfecf954da340bb)",
|
||||||
|
"time(#ambient, SystemTime { tv_sec: 1608542592, tv_nsec: 0 })",
|
||||||
|
"unique_revocation_id(0, hex:96123a8ee182336c4c63ad29f2b23549020da2a90841ac63ccec4c20413753b0)",
|
||||||
|
"unique_revocation_id(1, hex:60e6f54cb7a20ee0859495abe176da0306dfe91b4ee270244dfecf954da340bb)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, \"file1\")",
|
||||||
|
"check if time(#ambient, $date), $date <= 2018-12-20T00:00:00+00:00"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 1, check_id: 1, rule: \"check if time(#ambient, $date), $date <= 2018-12-20T00:00:00+00:00\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "authority rules",
|
||||||
|
"filename": "test10_authority_rules.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"1\", \"read\", \"owner\", \"0\", \"write\", \"check1\", \"check2\", \"alice\"]\n authority: Block[0] {\n symbols: [\"1\", \"read\", \"owner\", \"0\", \"write\"]\n version: 1\n context: \"\"\n facts: []\n rules: [\n right(#authority, $1, #read) <- resource(#ambient, $1), owner(#ambient, $0, $1),\n right(#authority, $1, #write) <- resource(#ambient, $1), owner(#ambient, $0, $1)\n ]\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"check1\", \"check2\", \"alice\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if right(#authority, $0, $1), resource(#ambient, $0), operation(#ambient, $1),\n check if resource(#ambient, $0), owner(#ambient, #alice, $0)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"operation(#ambient, #read)",
|
||||||
|
"owner(#ambient, #alice, \"file1\")",
|
||||||
|
"resource(#ambient, \"file1\")",
|
||||||
|
"revocation_id(0, hex:7b1c49cfd08df0bca951d50aa6f5062db8e4decce6713974186abd050382ab67)",
|
||||||
|
"revocation_id(1, hex:c5fdfd4294c92dca9f14fa659c45c811828853bf913e71a5d18ef9eecd7a6cab)",
|
||||||
|
"right(#authority, \"file1\", #read)",
|
||||||
|
"right(#authority, \"file1\", #write)",
|
||||||
|
"unique_revocation_id(0, hex:7b1c49cfd08df0bca951d50aa6f5062db8e4decce6713974186abd050382ab67)",
|
||||||
|
"unique_revocation_id(1, hex:c5fdfd4294c92dca9f14fa659c45c811828853bf913e71a5d18ef9eecd7a6cab)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [
|
||||||
|
"right(#authority, $1, #read) <- resource(#ambient, $1), owner(#ambient, $0, $1)",
|
||||||
|
"right(#authority, $1, #write) <- resource(#ambient, $1), owner(#ambient, $0, $1)"
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, $0), owner(#ambient, #alice, $0)",
|
||||||
|
"check if right(#authority, $0, $1), resource(#ambient, $0), operation(#ambient, $1)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ok": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "verifier authority checks",
|
||||||
|
"filename": "test11_verifier_authority_caveats.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\"]\n authority: Block[0] {\n symbols: [\"read\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read)\n ]\n rules: []\n checks: []\n }\n blocks: [\n \n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"operation(#ambient, #read)",
|
||||||
|
"resource(#ambient, \"file2\")",
|
||||||
|
"revocation_id(0, hex:f3db615323f48dc225b793ec494c30c1d4a800ec8299aa7558fe769803f1446b)",
|
||||||
|
"right(#authority, \"file1\", #read)",
|
||||||
|
"unique_revocation_id(0, hex:f3db615323f48dc225b793ec494c30c1d4a800ec8299aa7558fe769803f1446b)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if right(#authority, $0, $1), resource(#ambient, $0), operation(#ambient, $1)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Verifier(FailedVerifierCheck { check_id: 0, rule: \"check if right(#authority, $0, $1), resource(#ambient, $0), operation(#ambient, $1)\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "authority checks",
|
||||||
|
"filename": "test12_authority_caveats.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"check1\"]\n authority: Block[0] {\n symbols: [\"check1\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, \"file1\")\n ]\n }\n blocks: [\n \n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"file1": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"operation(#ambient, #read)",
|
||||||
|
"resource(#ambient, \"file1\")",
|
||||||
|
"revocation_id(0, hex:a6d33a7c61185cc962a4100d17176b72a60e95490af7c3cccbd244f3cce02b85)",
|
||||||
|
"unique_revocation_id(0, hex:a6d33a7c61185cc962a4100d17176b72a60e95490af7c3cccbd244f3cce02b85)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, \"file1\")"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ok": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"file2": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"operation(#ambient, #read)",
|
||||||
|
"resource(#ambient, \"file2\")",
|
||||||
|
"revocation_id(0, hex:a6d33a7c61185cc962a4100d17176b72a60e95490af7c3cccbd244f3cce02b85)",
|
||||||
|
"unique_revocation_id(0, hex:a6d33a7c61185cc962a4100d17176b72a60e95490af7c3cccbd244f3cce02b85)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, \"file1\")"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: \"check if resource(#ambient, \\\"file1\\\")\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "block rules",
|
||||||
|
"filename": "test13_block_rules.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"read\", \"valid_date\", \"time\", \"0\", \"1\", \"check1\"]\n authority: Block[0] {\n symbols: [\"read\"]\n version: 1\n context: \"\"\n facts: [\n right(#authority, \"file1\", #read),\n right(#authority, \"file2\", #read)\n ]\n rules: []\n checks: []\n }\n blocks: [\n Block[1] {\n symbols: [\"valid_date\", \"time\", \"0\", \"1\", \"check1\"]\n version: 1\n context: \"\"\n facts: []\n rules: [\n valid_date(\"file1\") <- time(#ambient, $0), resource(#ambient, \"file1\"), $0 <= 2030-12-31T12:59:59+00:00,\n valid_date($1) <- time(#ambient, $0), resource(#ambient, $1), $0 <= 1999-12-31T12:59:59+00:00, ![\"file1\"].contains($1)\n ]\n checks: [\n check if valid_date($0), resource(#ambient, $0)\n ]\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"file1": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"resource(#ambient, \"file1\")",
|
||||||
|
"revocation_id(0, hex:d0e882a6d2405213cc7a8f2ac3f0041fecbf535177b6a6b4a581b48783a9d19b)",
|
||||||
|
"revocation_id(1, hex:cab9e5395e49e41c53c3418796f73379a167d9c2d1504c99dac5e9bb06ec02cc)",
|
||||||
|
"right(#authority, \"file1\", #read)",
|
||||||
|
"right(#authority, \"file2\", #read)",
|
||||||
|
"time(#ambient, SystemTime { tv_sec: 1608542592, tv_nsec: 0 })",
|
||||||
|
"unique_revocation_id(0, hex:d0e882a6d2405213cc7a8f2ac3f0041fecbf535177b6a6b4a581b48783a9d19b)",
|
||||||
|
"unique_revocation_id(1, hex:cab9e5395e49e41c53c3418796f73379a167d9c2d1504c99dac5e9bb06ec02cc)",
|
||||||
|
"valid_date(\"file1\")"
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
"valid_date(\"file1\") <- time(#ambient, $0), resource(#ambient, \"file1\"), $0 <= 2030-12-31T12:59:59+00:00",
|
||||||
|
"valid_date($1) <- time(#ambient, $0), resource(#ambient, $1), $0 <= 1999-12-31T12:59:59+00:00, ![\"file1\"].contains($1)"
|
||||||
|
],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if valid_date($0), resource(#ambient, $0)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ok": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"file2": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"resource(#ambient, \"file2\")",
|
||||||
|
"revocation_id(0, hex:d0e882a6d2405213cc7a8f2ac3f0041fecbf535177b6a6b4a581b48783a9d19b)",
|
||||||
|
"revocation_id(1, hex:cab9e5395e49e41c53c3418796f73379a167d9c2d1504c99dac5e9bb06ec02cc)",
|
||||||
|
"right(#authority, \"file1\", #read)",
|
||||||
|
"right(#authority, \"file2\", #read)",
|
||||||
|
"time(#ambient, SystemTime { tv_sec: 1608542592, tv_nsec: 0 })",
|
||||||
|
"unique_revocation_id(0, hex:d0e882a6d2405213cc7a8f2ac3f0041fecbf535177b6a6b4a581b48783a9d19b)",
|
||||||
|
"unique_revocation_id(1, hex:cab9e5395e49e41c53c3418796f73379a167d9c2d1504c99dac5e9bb06ec02cc)"
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
"valid_date(\"file1\") <- time(#ambient, $0), resource(#ambient, \"file1\"), $0 <= 2030-12-31T12:59:59+00:00",
|
||||||
|
"valid_date($1) <- time(#ambient, $0), resource(#ambient, $1), $0 <= 1999-12-31T12:59:59+00:00, ![\"file1\"].contains($1)"
|
||||||
|
],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if valid_date($0), resource(#ambient, $0)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 1, check_id: 0, rule: \"check if valid_date($0), resource(#ambient, $0)\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "regex_constraint",
|
||||||
|
"filename": "test14_regex_constraint.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"resource_match\", \"0\"]\n authority: Block[0] {\n symbols: [\"resource_match\", \"0\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, $0), $0.matches(\"file[0-9]+.txt\")\n ]\n }\n blocks: [\n \n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"file1": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"resource(#ambient, \"file1\")",
|
||||||
|
"revocation_id(0, hex:1da4cd4d7c60491948662acc237bb10599c6046e1ef09a867267b5e039a4d1b6)",
|
||||||
|
"unique_revocation_id(0, hex:1da4cd4d7c60491948662acc237bb10599c6046e1ef09a867267b5e039a4d1b6)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, $0), $0.matches(\"file[0-9]+.txt\")"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: \"check if resource(#ambient, $0), $0.matches(\\\"file[0-9]+.txt\\\")\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"file123": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"resource(#ambient, \"file123.txt\")",
|
||||||
|
"revocation_id(0, hex:1da4cd4d7c60491948662acc237bb10599c6046e1ef09a867267b5e039a4d1b6)",
|
||||||
|
"unique_revocation_id(0, hex:1da4cd4d7c60491948662acc237bb10599c6046e1ef09a867267b5e039a4d1b6)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, $0), $0.matches(\"file[0-9]+.txt\")"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ok": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "multi queries checks",
|
||||||
|
"filename": "test15_multi_queries_caveats.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"must_be_present\"]\n authority: Block[0] {\n symbols: [\"must_be_present\"]\n version: 1\n context: \"\"\n facts: [\n must_be_present(#authority, \"hello\")\n ]\n rules: []\n checks: []\n }\n blocks: [\n \n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"must_be_present(#authority, \"hello\")",
|
||||||
|
"revocation_id(0, hex:128099942c46fc6a4f9a8f8f0cc5d8b70c4d55d834255ef6065b62c967eef50c)",
|
||||||
|
"unique_revocation_id(0, hex:128099942c46fc6a4f9a8f8f0cc5d8b70c4d55d834255ef6065b62c967eef50c)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if must_be_present(#authority, $0) or must_be_present($0)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ok": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "check head name should be independent from fact names",
|
||||||
|
"filename": "test16_caveat_head_name.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"check1\", \"test\", \"hello\"]\n authority: Block[0] {\n symbols: [\"check1\", \"test\", \"hello\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if resource(#ambient, #hello)\n ]\n }\n blocks: [\n Block[1] {\n symbols: []\n version: 1\n context: \"\"\n facts: [\n check1(#test)\n ]\n rules: []\n checks: []\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"check1(#test)",
|
||||||
|
"revocation_id(0, hex:08321b952cecd6cc7ca5d3493ae391e44fcf3d3d55e63aa7e8b098217b7736c3)",
|
||||||
|
"revocation_id(1, hex:e166c05f9ec0632fe286df76048a527a621d7ca08e2cd9f3995b4ee33b1e001c)",
|
||||||
|
"unique_revocation_id(0, hex:08321b952cecd6cc7ca5d3493ae391e44fcf3d3d55e63aa7e8b098217b7736c3)",
|
||||||
|
"unique_revocation_id(1, hex:e166c05f9ec0632fe286df76048a527a621d7ca08e2cd9f3995b4ee33b1e001c)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if resource(#ambient, #hello)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: \"check if resource(#ambient, #hello)\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "test expression syntax and all available operations",
|
||||||
|
"filename": "test17_expressions.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"query\", \"abc\", \"hello\", \"world\"]\n authority: Block[0] {\n symbols: [\"query\", \"abc\", \"hello\", \"world\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if true,\n check if !false,\n check if false or true,\n check if 1 < 2,\n check if 2 > 1,\n check if 1 <= 2,\n check if 1 <= 1,\n check if 2 >= 1,\n check if 2 >= 2,\n check if 3 == 3,\n check if 1 + 2 * 3 - 4 / 2 == 5,\n check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\"),\n check if \"aaabde\".matches(\"a*c?.e\"),\n check if \"abcD12\" == \"abcD12\",\n check if 2019-12-04T09:46:41+00:00 < 2020-12-04T09:46:41+00:00,\n check if 2020-12-04T09:46:41+00:00 > 2019-12-04T09:46:41+00:00,\n check if 2019-12-04T09:46:41+00:00 <= 2020-12-04T09:46:41+00:00,\n check if 2020-12-04T09:46:41+00:00 >= 2020-12-04T09:46:41+00:00,\n check if 2020-12-04T09:46:41+00:00 >= 2019-12-04T09:46:41+00:00,\n check if 2020-12-04T09:46:41+00:00 >= 2020-12-04T09:46:41+00:00,\n check if 2020-12-04T09:46:41+00:00 == 2020-12-04T09:46:41+00:00,\n check if #abc == #abc,\n check if hex:12ab == hex:12ab,\n check if [1, 2].contains(2),\n check if [2019-12-04T09:46:41+00:00, 2020-12-04T09:46:41+00:00].contains(2020-12-04T09:46:41+00:00),\n check if [false, true].contains(true),\n check if [\"abc\", \"def\"].contains(\"abc\"),\n check if [hex:12ab, hex:34de].contains(hex:34de),\n check if [#hello, #world].contains(#hello)\n ]\n }\n blocks: [\n \n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"revocation_id(0, hex:09b4fab17d84885149e416bf10990d19b918a02854acd9ad96494994735cd25d)",
|
||||||
|
"unique_revocation_id(0, hex:09b4fab17d84885149e416bf10990d19b918a02854acd9ad96494994735cd25d)"
|
||||||
|
],
|
||||||
|
"rules": [],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if !false",
|
||||||
|
"check if \"aaabde\".matches(\"a*c?.e\")",
|
||||||
|
"check if \"abcD12\" == \"abcD12\"",
|
||||||
|
"check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")",
|
||||||
|
"check if #abc == #abc",
|
||||||
|
"check if 1 + 2 * 3 - 4 / 2 == 5",
|
||||||
|
"check if 1 < 2",
|
||||||
|
"check if 1 <= 1",
|
||||||
|
"check if 1 <= 2",
|
||||||
|
"check if 2 > 1",
|
||||||
|
"check if 2 >= 1",
|
||||||
|
"check if 2 >= 2",
|
||||||
|
"check if 2019-12-04T09:46:41+00:00 < 2020-12-04T09:46:41+00:00",
|
||||||
|
"check if 2019-12-04T09:46:41+00:00 <= 2020-12-04T09:46:41+00:00",
|
||||||
|
"check if 2020-12-04T09:46:41+00:00 == 2020-12-04T09:46:41+00:00",
|
||||||
|
"check if 2020-12-04T09:46:41+00:00 > 2019-12-04T09:46:41+00:00",
|
||||||
|
"check if 2020-12-04T09:46:41+00:00 >= 2019-12-04T09:46:41+00:00",
|
||||||
|
"check if 2020-12-04T09:46:41+00:00 >= 2020-12-04T09:46:41+00:00",
|
||||||
|
"check if 3 == 3",
|
||||||
|
"check if [\"abc\", \"def\"].contains(\"abc\")",
|
||||||
|
"check if [#hello, #world].contains(#hello)",
|
||||||
|
"check if [1, 2].contains(2)",
|
||||||
|
"check if [2019-12-04T09:46:41+00:00, 2020-12-04T09:46:41+00:00].contains(2020-12-04T09:46:41+00:00)",
|
||||||
|
"check if [false, true].contains(true)",
|
||||||
|
"check if [hex:12ab, hex:34de].contains(hex:34de)",
|
||||||
|
"check if false or true",
|
||||||
|
"check if hex:12ab == hex:12ab",
|
||||||
|
"check if true"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ok": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "invalid block rule with unbound_variables",
|
||||||
|
"filename": "test18_unbound_variables_in_rule.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"check1\", \"test\", \"read\", \"unbound\", \"any1\", \"any2\"]\n authority: Block[0] {\n symbols: [\"check1\", \"test\", \"read\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if operation(#ambient, #read)\n ]\n }\n blocks: [\n Block[1] {\n symbols: [\"unbound\", \"any1\", \"any2\"]\n version: 1\n context: \"\"\n facts: []\n rules: [\n operation($unbound, #read) <- operation($any1, $any2)\n ]\n checks: []\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"FailedLogic(InvalidBlockRule(0, \"operation($unbound, #read) <- operation($any1, $any2)\"))"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "invalid block rule generating an #authority or #ambient symbol with a variable",
|
||||||
|
"filename": "test19_generating_ambient_from_variables.bc",
|
||||||
|
"print_token": {
|
||||||
|
"biscuit2 (1 check)": "Biscuit {\n symbols: [\"authority\", \"ambient\", \"resource\", \"operation\", \"right\", \"current_time\", \"revocation_id\", \"check1\", \"test\", \"read\", \"any\"]\n authority: Block[0] {\n symbols: [\"check1\", \"test\", \"read\"]\n version: 1\n context: \"\"\n facts: []\n rules: []\n checks: [\n check if operation(#ambient, #read)\n ]\n }\n blocks: [\n Block[1] {\n symbols: [\"any\"]\n version: 1\n context: \"\"\n facts: []\n rules: [\n operation($ambient, #read) <- operation($ambient, $any)\n ]\n checks: []\n }\n ]\n}"
|
||||||
|
},
|
||||||
|
"validations": {
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
"operation(#ambient, #write)",
|
||||||
|
"revocation_id(0, hex:cfbc25eee0ffc9bca3930e88469c45b8aa43e856464fc401db213c3d9587783a)",
|
||||||
|
"revocation_id(1, hex:0e180a4400430a812b58751a3d3877af6ac2fe87559a32656c9ae78a4e973781)",
|
||||||
|
"unique_revocation_id(0, hex:cfbc25eee0ffc9bca3930e88469c45b8aa43e856464fc401db213c3d9587783a)",
|
||||||
|
"unique_revocation_id(1, hex:0e180a4400430a812b58751a3d3877af6ac2fe87559a32656c9ae78a4e973781)"
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
"operation($ambient, #read) <- operation($ambient, $any)"
|
||||||
|
],
|
||||||
|
"privileged_rules": [],
|
||||||
|
"checks": [
|
||||||
|
"check if operation(#ambient, #read)"
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
"allow if true"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Err": [
|
||||||
|
"Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: \"check if operation(#ambient, #read)\" })"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
samples/v2/test10_authority_rules.bc
Normal file
BIN
samples/v2/test10_authority_rules.bc
Normal file
Binary file not shown.
BIN
samples/v2/test11_verifier_authority_caveats.bc
Normal file
BIN
samples/v2/test11_verifier_authority_caveats.bc
Normal file
Binary file not shown.
BIN
samples/v2/test12_authority_caveats.bc
Normal file
BIN
samples/v2/test12_authority_caveats.bc
Normal file
Binary file not shown.
BIN
samples/v2/test13_block_rules.bc
Normal file
BIN
samples/v2/test13_block_rules.bc
Normal file
Binary file not shown.
BIN
samples/v2/test14_regex_constraint.bc
Normal file
BIN
samples/v2/test14_regex_constraint.bc
Normal file
Binary file not shown.
BIN
samples/v2/test15_multi_queries_caveats.bc
Normal file
BIN
samples/v2/test15_multi_queries_caveats.bc
Normal file
Binary file not shown.
BIN
samples/v2/test16_caveat_head_name.bc
Normal file
BIN
samples/v2/test16_caveat_head_name.bc
Normal file
Binary file not shown.
BIN
samples/v2/test17_expressions.bc
Normal file
BIN
samples/v2/test17_expressions.bc
Normal file
Binary file not shown.
BIN
samples/v2/test18_unbound_variables_in_rule.bc
Normal file
BIN
samples/v2/test18_unbound_variables_in_rule.bc
Normal file
Binary file not shown.
BIN
samples/v2/test19_generating_ambient_from_variables.bc
Normal file
BIN
samples/v2/test19_generating_ambient_from_variables.bc
Normal file
Binary file not shown.
BIN
samples/v2/test1_basic.bc
Normal file
BIN
samples/v2/test1_basic.bc
Normal file
Binary file not shown.
BIN
samples/v2/test2_different_root_key.bc
Normal file
BIN
samples/v2/test2_different_root_key.bc
Normal file
Binary file not shown.
BIN
samples/v2/test3_invalid_signature_format.bc
Normal file
BIN
samples/v2/test3_invalid_signature_format.bc
Normal file
Binary file not shown.
BIN
samples/v2/test4_random_block.bc
Normal file
BIN
samples/v2/test4_random_block.bc
Normal file
Binary file not shown.
BIN
samples/v2/test5_invalid_signature.bc
Normal file
BIN
samples/v2/test5_invalid_signature.bc
Normal file
Binary file not shown.
BIN
samples/v2/test6_reordered_blocks.bc
Normal file
BIN
samples/v2/test6_reordered_blocks.bc
Normal file
Binary file not shown.
BIN
samples/v2/test7_invalid_block_fact_authority.bc
Normal file
BIN
samples/v2/test7_invalid_block_fact_authority.bc
Normal file
Binary file not shown.
BIN
samples/v2/test8_invalid_block_fact_ambient.bc
Normal file
BIN
samples/v2/test8_invalid_block_fact_ambient.bc
Normal file
Binary file not shown.
BIN
samples/v2/test9_expired_token.bc
Normal file
BIN
samples/v2/test9_expired_token.bc
Normal file
Binary file not shown.
22
schema.proto
22
schema.proto
@ -3,21 +3,23 @@ syntax = "proto2";
|
|||||||
package biscuit.format.schema;
|
package biscuit.format.schema;
|
||||||
|
|
||||||
message Biscuit {
|
message Biscuit {
|
||||||
required bytes authority = 1;
|
optional uint32 rootKeyId = 1;
|
||||||
repeated bytes blocks = 2;
|
required SignedBlock authority = 2;
|
||||||
repeated bytes keys = 3;
|
repeated SignedBlock blocks = 3;
|
||||||
required Signature signature = 4;
|
required Proof proof = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SealedBiscuit {
|
message SignedBlock {
|
||||||
required bytes authority = 1;
|
required bytes block = 1;
|
||||||
repeated bytes blocks = 2;
|
required bytes nextKey = 2;
|
||||||
required bytes signature = 3;
|
required bytes signature = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Signature {
|
message Proof {
|
||||||
repeated bytes parameters = 1;
|
oneof Content {
|
||||||
required bytes z = 2;
|
bytes nextSecret = 1;
|
||||||
|
bytes finalSignature = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Block {
|
message Block {
|
||||||
|
Loading…
Reference in New Issue
Block a user