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

Add crypto provider #1214

Closed
wants to merge 13 commits into from

Conversation

blasrodri
Copy link
Contributor

Draft definition of the CryptoProvider trait defined in #1213 + an actual instance of the trait
for a HostFunction* implementation

@tony-iqlusion
Copy link
Collaborator

It might make more sense to put this in the tendermint crate itself, since it already contains high-level functions for working with private and public keys as well as signing and verifying messages.

Then downstream crates like light-client-verifier can use those high-level APIs.

This keeps the core cryptographic functionality in one crate, instead of spread out.

@@ -51,14 +52,14 @@ tendermint-proto = { version = "0.25.0", default-features = false, path = "../pr
time = { version = "0.3", default-features = false, features = ["macros", "parsing"] }
zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] }
flex-error = { version = "0.4.4", default-features = false }
k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] }
k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this one optional depends on whether CryptoProvider will be under a feature flag.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it now, we lock the signature type in CryptoProvider to the one defined by k256. If we cannot abstract this associated type to less specific rust-crypto bounds ("must be fixed array of 256 bits"?) and still keep it usable in generic code, then k256 should become a hard dependency. But I thought dependency management was the whole purpose of this rework.

@blasrodri
Copy link
Contributor Author

It might make more sense to put this in the tendermint crate itself, since it already contains high-level functions for working with private and public keys as well as signing and verifying messages.

Then downstream crates like light-client-verifier can use those high-level APIs.

This keeps the core cryptographic functionality in one crate, instead of spread out.

Moved it on 100fc53

@codecov-commenter
Copy link

codecov-commenter commented Oct 24, 2022

Codecov Report

Merging #1214 (0cafbb9) into main (d102af4) will decrease coverage by 0.1%.
The diff coverage is 36.0%.

@@           Coverage Diff           @@
##            main   #1214     +/-   ##
=======================================
- Coverage   64.2%   64.1%   -0.2%     
=======================================
  Files        244     245      +1     
  Lines      21364   21401     +37     
=======================================
- Hits       13734   13731      -3     
- Misses      7630    7670     +40     
Impacted Files Coverage Δ
light-client/src/builder/light_client.rs 0.0% <0.0%> (ø)
tendermint/src/crypto.rs 0.0% <0.0%> (ø)
tendermint/src/lib.rs 100.0% <ø> (ø)
light-client-verifier/src/verifier.rs 82.6% <17.6%> (-5.3%) ⬇️
...client-verifier/src/operations/commit_validator.rs 98.2% <100.0%> (+0.2%) ⬆️
light-client-verifier/src/predicates.rs 97.4% <100.0%> (ø)
testgen/src/vote.rs 84.0% <0.0%> (-1.7%) ⬇️
abci/src/codec.rs 88.8% <0.0%> (-1.3%) ⬇️
abci/src/application/kvstore.rs 73.3% <0.0%> (-1.1%) ⬇️
testgen/src/commit.rs 90.6% <0.0%> (-0.7%) ⬇️
... and 5 more

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@mzabaluev
Copy link
Contributor

The new API looks good in general, will review once more when undrafted.
Would there be an example of how this can be used in a light client verifier?

@blasrodri
Copy link
Contributor Author

The new API looks good in general, will review once more when undrafted.
Would there be an example of how this can be used in a light client verifier?

I'll add this, and un-draft it :)

@@ -0,0 +1,134 @@
use crate::signature::Verifier;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general thought, "host functions" that contains only a CryptoProvider trait still feels like YAGNI to me.

I would personally suggest putting it under a crypto module (which should probably have been created a long time ago, with all of the existing cryptography code in the tendermint crate factored into it).

@tony-iqlusion
Copy link
Collaborator

The trait itself looks OK, aside from the commented out part.

However, it isn't terribly useful unless the existing cryptography in the crate(s) is changed to use it.

And that's where this sort of change might get a bit onerous: you either need to add a generic type parameter with a default, or a generic type parameter with feature-gated type aliases. I'd probably suggest the latter.

@blasrodri
Copy link
Contributor Author

The new API looks good in general, will review once more when undrafted. Would there be an example of how this can be used in a light client verifier?

Please check it out here: ComposableFi/near-rs#16

@mzabaluev
Copy link
Contributor

However, it isn't terribly useful unless the existing cryptography in the crate(s) is changed to use it.

Is this PR meant to start a piecemeal introduction of the larger set of changes first submitted in #1138?

add a generic type parameter with a default, or a generic type parameter with feature-gated type aliases.

I think a ready-to-use implementation of the crypto functions, using widely adopted pure Rust dependencies, should be provided in the tendermint crate for those users who don't need to bother with the alternatives. In the interests of dependency management, it needs to be feature-gated.

@blasrodri
Copy link
Contributor Author

Can you please be more specific on how to make if feature-gated friendly?
Happy to implement it

@mzabaluev
Copy link
Contributor

Can you please be more specific on how to make if feature-gated friendly? Happy to implement it

Cribbing from #1138 again, you could have a generic CommitValidator parameterized over the CryptoProvider trait, and a feature-gated production instance:

#[cfg(feature = "rust-crypto")]
pub type ProdCommitValidator = CommitValidator<RustCryptoProvider>;

But perhaps it's work for a follow-up PR. I will look into ComposableFi/near-rs#16 and the fork of tendermint-rs used in Hyperspace.

@blasrodri
Copy link
Contributor Author

@mzabaluev gave my first shot on feature gating this. Would def appreciate some guidance :)

Comment on lines 17 to 19
#[cfg(feature = "rust-crypto")]
type RustCryptoProvider: CryptoProvider;

Copy link
Contributor

@mzabaluev mzabaluev Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feature-gating trait members is not a good idea, I think.

Perhaps I did not explain the idea more carefully. It's OK to work the abstract CryptoProvider generics wherever needed without feature-gating. So you could have:

// Kill the CommitValidator trait, its methods become inherent methods of the struct:

pub struct CommitValidator<C> {
    // ...
}

impl<C: CryptoProvider> CommitValidator<C> {
    // The API that belonged to CommitValidator trait goes here
}

/// The batteries-included validator, for when you don't mind the dependencies on
/// the full rust-crypto stack.
#[cfg(feature = "rust-crypto")]
pub type ProdCommitValidator = CommitValidator<RustCryptoProvider>;

@thanethomson I think all those traits having a single empty ProdFoo implementation don't carry much weight in the current design, since all the logic is provided in the default trait method bodies. So it's fair to replace them with generic types parameterized around the crypto provider, like in the example above.

@blasrodri
Copy link
Contributor Author

@mzabaluev any comments?

Comment on lines 97 to 101
trait DefaultHostFunctions: CryptoProvider {
fn sha2_256(preimage: &[u8]) -> [u8; 32];
fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>;
fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this trait is not clear. Is it meant to be a "god trait" for all future functions that could be host-provided? Now, it only adds the crypto methods that I think could well be defined in CryptoProvider itself, because the implementation is tightly coupled with the associated type definitions there.

I agree with @tony-iqlusion that this umbrella trait is not necessarily needed until we realise a clear purpose for it.

light-client-verifier/Cargo.toml Show resolved Hide resolved
Comment on lines 12 to 13
// type Ed25519Signer: Signer<ed25519::Signature>;
// type Ed25519Verifier: Verifier<ed25519::Signature>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed, to be added later when/if needed.

@@ -51,14 +52,14 @@ tendermint-proto = { version = "0.25.0", default-features = false, path = "../pr
time = { version = "0.3", default-features = false, features = ["macros", "parsing"] }
zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] }
flex-error = { version = "0.4.4", default-features = false }
k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] }
k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it now, we lock the signature type in CryptoProvider to the one defined by k256. If we cannot abstract this associated type to less specific rust-crypto bounds ("must be fixed array of 256 bits"?) and still keep it usable in generic code, then k256 should become a hard dependency. But I thought dependency management was the whole purpose of this rework.

@mzabaluev
Copy link
Contributor

I think the functionality abstracted by CryptoProvider should cut deeper, if the goals of this change are to be realized. One instance where Sha256 is still used unconditionally is the hasher, which simply calls into intrinsic hash methods of tendermint domain types. An alternative hasher cannot replicate that without duplicating the implementation of those methods. I'm doing some experiments to see if we can come up with a more comprehensive redesign and maybe simplify the generics in light-client-verifier at the same time.

@mzabaluev
Copy link
Contributor

Here's my crack at it: #1238

@romac
Copy link
Member

romac commented Jan 19, 2023

Superseded by #1238

@romac romac closed this Jan 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants