[BREAKING] Abstract biscuit-auth over crypto implementation - #334
Conversation
KeyPair is really just the same as PrivateKey, it's not necessary to have two distinct types for these purposes and it makes trait abstraction more complex.
|
I think there is more work to be done to fully integrate the traits:
Before I go through all of that with a fine tooth comb I wanted to get confirmation that this is the right direction. |
|
I like this approach as it neatly handles both KMS and pluggable crypto providers. |
This commit adds four traits to biscuit-auth: `Sign`, `Verify`, `SerializePublicKey` and `SerializePrivateKey`. Together, these represent the behaviors of private keys and public keys, including those which can be serialized into the token and those which can't. Biscuit themselves become parameterized by the private key type that their proof uses (the public keys used in the token being determined as associated types of that private key type). These public and private keys must be serializable. For issuing a token, the keys used do not need to be serializable, and do not need to be the same as used for the token-internal keys. For third party signing, only the public key needs to be serializable. Some refactors were made to enable this change: 1. The `Keypair` type was eliminated from the code; it's function is fulfilled by the private key type and a third type was not necessary. 2. The useage of public keys in datalog authorizer running doesn't need an actual crypto implementation, just key data, so its replaced by an inert public key type that contains the algorithm tag and bytes.
c385e59 to
57c6ce4
Compare
|
Updated, I think this is ready now. |
divarvel
left a comment
There was a problem hiding this comment.
This generally looks good to me. got a few questions and nit-level comments.
wbourne0
left a comment
There was a problem hiding this comment.
I don't fully understand what the point of this is, since it doesn't allow for HSMs or TPMs. The private keys are still required to be serializable. Which means we must have the private material outside of it.
I don't know the codebase that well (first time looking at it today) but I don't think that having a custom public key trait makes much sense here, as there's currently only 2 algorithms.
I haven't looked through all of the library yet, but I'm also not convinced that this needs to be a breaking change.
I think this should be started off by just add a Signer trait and allowing issuance with root keys (a simple switch to &impl Signer for the root key reference, as long as the KeyPair type impl this trait, all previous calls should be compatible).
Distinguish between the root key and the internal key used by a biscuit in the builder APIs, so a biscuit can have a root key which cannot be serialized (such as one backed by an HSM). Rename the inert public key type to `PublicKeyData`, give it a more complete public API.
The original goal of this work was to support other crypto implementations (not algorithms) if users want to use a different library from p256/ed25519-dalek for either performance or security assurance reasons. That's what your change in #340 doesn't fully support; you could swap the issuer key but all internal signature verifications have to route through p256/ed255190-dalek. p256 in particular is on the order of 5x slower than some other implementations (see benchmarks in #320). But being forward compatible with HSMs was definitely also a goal of this change, which is why we went this route instead of the route in #320. You spotted that the builder APIs as expressed actually aren't compatible with that, which was a big oversight on my part. To fix that mistake, This introduced a new issue that the builder APIs don't have an obvious way to infer the
I've also renamed the The changelog is also more complete and I've responded to @divarvel's nits. Other than the question of trait naming, if we're happy with this direction I think this PR is ready to merge. |
We get rid of `ToAnyParam` and `AnyParam` because they're no longer doing anything: the macros call different methods on term params and scope params. Instead, term params should be any `Into<Term>` and scope params should be any `Into<PublicKeyData>`. As a result, you can also pass custom public key types to these macros as well.
|
One more set of related changes: I wanted to make it so you could pass any public key type (including your own custom ones) to the Datalog macros. In the process I realised that |
Thanks! I actually worked on this in #314 but it was stalled because at that time it was a breaking change. |
|
Thanks for the effort @saoirse-a, and thanks for the valuable feedback @wbourne0 I’ll merge as is, i’m still open to naming improvements before cutting a release, but i think this is fine as it is right now. This solves two huge pain points so I’m glad to see this move forward. |
|
Could you have a look at the failing coverage test? |
Yes, #340 is intended to be a gradual change with no breaking changes, I was intentionally keeping it minimal to avoid locking into specific architectural decisions. I only added I also don't see why this needs to be a breaking change again, especially if you're setting default parameters. Adding traits doesn't need to be a breaking change; though removing them usually is. As for supporting other implementations of ed25519 and p256, personally I'm of the opinion that other options should be implemented via feature flags and optional dependencies (with current config under the This may be addressed but I'm also a little concerned about this implementation if a biscuit has mixed key types; I wonder if the ideal type for the validator type should enforce that validators for both be written (e.g. with associated types). Anyhow, this is a large change to add all at once (and I'm not super familiar with the codebase in general) so I have a hard time visually validating it. I don't own this repo so do as you will, but I do think it'd be good to see some e2e examples of using this before merging it. It's probably one of the best ways to test a change like this. |
This adds traits to the biscuit-auth public interface for cryptographic operations and abstracts the Biscuit and related types over those traits.
The traits added are
Sign,Verify,SerializePublicKeyandSerializePrivateKey. Together, these represent the necessary behavior for public and private keys, distinguishing those which can be serialised into the token and those which can't:The
Biscuittype is parameterised by aSerializePrivateKey, which is the type of the key contain in its proof. All of the public keys in the token are the associated serialisable public key type for that private key.The issuer key and third party signing keys can be different types from the key contained in the token; for the issuer neither the private key nor the public key need be serialisable, for the third party key only the public key needs to be serialisable because it appears in the token.
The existing
PublicKeyandPrivateKeytypes implement these traits; no other implementations are added directly to biscuit-auth with this PR (we could either add implementations for other crypto libraries like aws-lc-rs, maybe feature gated, or just require users to write their own implementations).To support this change, two significant refactors were performed:
Keypairtype was eliminated. This was effectively redundant with thePrivateKeytype, removing it made the set of traits required simpler.PublicKeystable used in authorization execution now stores an inert representation of the public key instead of thePublicKeytype, because it doesn't need to verify signatures, just compare keys for equality. This avoids more abstraction points over crypto types in the Datalog code.