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

Improve ssi-crypto. #642

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open

Improve ssi-crypto. #642

wants to merge 24 commits into from

Conversation

timothee-haudebourg
Copy link
Contributor

@timothee-haudebourg timothee-haudebourg commented Mar 18, 2025

Description

Improves the ssi-crypto library:

  • Make the purpose of the library clearer: a flexible dynamic interface for cryptographic primitives built on top of RustCrypto (and ring), where algorithms can be selected at run time instead of compile time.
  • Add signing and verification traits that can be used by JWK, COSE and Data Integrity (in the future), instead of format specific traits that must be re-implemented (e.g. JwsSigner).
  • Re-export RustCrypto libraries (depending on the features), so that dependents don't have to depend on dozens of crypto libraries themselves.

The important changes are in the ssi-crypto library. The only reason why there are so many changes in other files is because I renamed a few algorithm variants to follow Rust naming conventions, and merged the ssi-crypto error types into one.

Tested

  • Original test suite still passes.
  • Added unit test for each supported cryptographic function.

@timothee-haudebourg timothee-haudebourg changed the base branch from main to make-clippy-happy March 18, 2025 14:44
Base automatically changed from make-clippy-happy to main March 18, 2025 17:38
@timothee-haudebourg timothee-haudebourg marked this pull request as ready for review March 19, 2025 13:24
Copy link
Member

@sbihel sbihel left a comment

Choose a reason for hiding this comment

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

Are the changes to the algorithms casing really that important? I understand it's not exactly idiomatic, but at the same time they're essentially acronyms so it doesn't matter that much, it's additional breaking changes, and it makes this PR harder to review

@timothee-haudebourg
Copy link
Contributor Author

timothee-haudebourg commented Mar 19, 2025

Are the changes to the algorithms casing really that important?

No its not important, but it's the convention and I feel like it's now or never to make breaking changes. I can make that in another PR, but would that really help?

Alternatively I can revert the names for the ssi_jwk::Algorithm type only. It wasn't really my intention to change them, but they are defined with a macro that binds the names to the ssi_crypto::Algorithm names, so my IDE automatically changed them. It's really the change to ssi_jwk::Algorithm that bleeds out to the rest of the crates.

Comment on lines 36 to 37
// pub struct

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// pub struct

/// Key type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum EcdsaKeyType {
Copy link
Contributor

Choose a reason for hiding this comment

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

Curve instead of KeyType?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went back and forth between KeyType and Curve 😅 If you prefer Curve I'll use that instead.

Comment on lines 503 to 504
// let padding;
// let hashed;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// let padding;
// let hashed;


/// ECDSA using secp256k1 (K-256) and SHA-256, with or without recovery bit.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AnyES {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub enum AnyES {
pub enum AnyES256K {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I intend to remove what's in the static module. Those types are used by the Data Integrity libs, but now that we have low-level Signer and Verifier traits we'll be able to use the Algorithm type directly. Also I feel like defining static types for crypto algorithm is RustCrypto's job, not ssi-crypto's.

Comment on lines +32 to +35
bytes.push(0x04);
bytes.extend(x);
bytes.extend(y);
Self::from_k256_sec1_bytes(&bytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW you can use k256::EncodedPoint::from_untagged_bytes or from_affine_coordinates to avoid messing around with SEC1 tags.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of the key conversion functions are coming straight from ssi_jwk.

avoid messing around with SEC1 tags

Oh that's what the 0x04 is? Yeah that's why I did change anything, I wasn't sure what format that was.

use super::KeyType;

#[derive(Clone, ZeroizeOnDrop)]
pub struct SymmetricKey(Box<[u8]>);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why Box<[u8]> instead of Vec?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Vec provides more capabilities like resizing that we don't need here. So to me the question is more "Why Vec when Box<[u8]> is enough"?

Copy link
Contributor

@cobward cobward Mar 20, 2025

Choose a reason for hiding this comment

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

Because in most applications an owned slice will be a Vec, so we're adding unnecessary overhead by requiring conversion between Box<[u8]> and Vec.

Additional capabilities on a Vec don't actually cause any issues right? There's just an extra byte of memory used to store the capacity of the slice.

Conversion from Vec to Box<[u8]> requires shrinking the Vec to capacity, manually dropping and reading the vec into a new buffer. I'm not an expert but I think that also implies that the original Vec buffer is not reused and therefore not zeroized. So overall I think it makes more sense to take ownership of the Vec. I was wrong about that part, the Box manually drops the original buffer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RustCrypo always use fixed-length arrays, not Vec, so I'm not worried about shrinking stuff. But it's true that many functions are expecting Vecs so I'm not going to fight for it, I'll remove the Box<[u8]>s.

Comment on lines 43 to 53
*<Box<dyn Send + Sync + Any>>::downcast(t).unwrap()
}

#[allow(clippy::borrowed_box)]
fn downcast_ref<T: 'static>(t: &Box<dyn Send + Sync + Any>) -> &T {
t.downcast_ref().unwrap()
}

#[allow(clippy::borrowed_box)]
fn downcast_mut<T: 'static>(t: &mut Box<dyn Send + Sync + Any>) -> &mut T {
t.downcast_mut().unwrap()
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you leave comments describing why the unwrap won't ever panic.

@timothee-haudebourg
Copy link
Contributor Author

@cobward, @sbihel, ready for another review.

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.

3 participants