Open Geal opened 4 years ago
thinking a bit more about it now, it might be smarter to let the verifier's side handle that, ie patterns like this:
Currently the algorithms I'd like support for are:
It would also be neat to support U2F/WebAuthN assertions (a P-256 ECDSA signature over a specific data structure).
with https://github.com/CleverCloud/biscuit/issues/40 there will be a way to represent keys, so now we can think more about how to represent it. A key point in implementing it is that a verifier can do queries before checking the caveats. So the idea would be to have the verifier query for any signatures to check, then we create facts to represent a valid signature, and the token has caveats to check those signatures. I think it's better to provide the pattern as a kind of "cookbook" instead of integrated inside Biscuit's basic API, since it is unlikely to match all use cases.
Facts needed for the query:
should_sign(data_index: int, algorithm: symbol, public_key: bytes)
with data index that would either match a fact data(data_index, content:bytes)
or some additional data that will accompany the token. The algorithm can be a symbol since it will probably be repeatedsignature(data_index: int, algorithm: symbol, public_key: bytes, content: bytes)
. I thought about having the public key be an index to match a public_key(key_index: int, content: bytes)
, but someone could try to add any key for this, so it's better to just repeat itshould_hash(data_index: int, algorithm: symbol, digest: bytes)
. I'm not sure that one is needed, but it would be easy enough to addQueries from the verifier:
should_sign
factsdata
facts with matching index (or get them from data coming with the token). If there are multiple facts with the same index, failsignature
factsdata
, should_sign
and signature
, check the signature, and add a fact valid_signature(#ambient, data_index: int, algorithm: symbol, public_key: bytes)
Caveats in the token:
*valid(0?)<- should_sign(0?, 1?, 2?), valid_signature(#ambient, $0, $1, $2)
The verifier will then check the caveats and the token will succeed if the required signatures are here.
@titanous what do you think?
This all makes a lot of sense! The main thing I'm thinking about is how the data to be signed will be formatted. I'm going to drop some notes below.
["biscuit-pop-v0", biscuit_challenge, signer_nonce, signer_timestamp]
.
"biscuit-pop-v0"
- static context bytes to prevent key misusebiscuit_challenge
- a random nonce taken from the block to bind the signature to itsigner_nonce
- a random nonce generated by the signer, to allow replay preventionsigner_timestamp
- the current timestamp, added by the signer, to allow windowing the replay prevention lookup (the server should reject timestamps outside of the current window)data
facts with multiple arguments that define the expected template for the data.M.PrepareForRequest(M)
, which binds the first party macaroon together with all of the discharge macaroons, to ensure that any discharges cannot be reused outside of the expected context. It seems like we should allow a construction that enables this, though I'm not sure what the best way to do it is.challenge
- this is where the hash of the data we want signed goes, in most cases this would be a version of the above PoP scheme.rpId
- usually the domain name of the relying party, limited control when going through the JS/browser interface, complete control when talking to the hardware directly.allowCredentials
- this is the list of credential IDs (and their associated transport) that are valid, this would likely be provided in the biscuit, listing authenticators that have been enrolled.userVerification
- whether to require a test of user presence, one of required
, preferred
, discouraged
, defaults to preferred
.extensions
- we can ignore this for nowshould_webauthn_auth(data_index: int, credential: bytes, rp_id: string, user_verification: symbol)
and a corresponding fact added by the verifier: valid_webauthn_auth(#ambient, data_index: int, credential: bytes, rp_id: string, user_verified: symbol)
@daeMOn63 has implemented an experimental proof of possession scheme here: https://github.com/flynn/biscuit-go/blob/master/experiments/pop_test.go
Are these use-cases covered by the third-party blocks? They allow to handle signatures / verifications outside of datalog, avoiding a lot of issues related to exposing crypto primitives from within datalog.
To implement use cases such as third party caveats, or using a biscuit token as attestation accumulating acknowledgement from multiple parties, caveats should be able to verify a public key signature.
A few questions here:
Signature verification would work well as a constraint affecting 3 elements (message, key, signature) that could be filled in various ways:
verify(message, key, sig?)
: a signature from a specific key must be providedverify(message, key?, sig?)
: a signature must be provided, from any key, but we could have other constraints on that key here (like the key must be in a certain set)verify(message?, key, sig?)
: specify we must have a valid signature from a key, with other constraints on the message