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

feat: Signing with Yubihsm2 #5625

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

Conversation

keefertaylor
Copy link

Description

YubiHSMs are a physical security module utilized by many validators. This PR adds support for signing Hyperlane validator messages on ethereum with a yubihsm.

YubiHSMs are accessible via an HTTP(s) connector server, or directly via USB. This implementation opts to use a connector server because the underlying yubihsm.rs crate will monopolize the USB connection to the device, which means it can't be used to sign for other purposes.

Yubihsm's also support other signature algorithms (such as ed25519), which may be useful for other Hyperlane signing configurations outside of ethereum chains. If desired we could extend this PR to add support.

Testing

Tessellated has been rolled out this change and is signing exclusively with YubiHSMs in our production setups for Hyperlane for 3+ weeks.

/// This function does not use a cache. Care should be taken to not exhaust the number of sessions on the yubihsm when calling this function.
fn connect(http_config: HttpConfig, credentials: Credentials) -> Client {
let connector = ethers::signers::yubihsm::Connector::http(&http_config);
Client::open(connector.clone(), credentials.clone(), true).unwrap()
Copy link
Author

Choose a reason for hiding this comment

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

Original comment

I wonder if we want to return a Result here and let user of the method to decide what to do in the case of an error?

We could make this change but I'm not sure what we would do to handle it. If connection has failed, something has gone quite wrong.

The hyperlane validator agent generally fatally crashes if it can't get some mission critical resource at startup (DB access is locked, S3 creds misconfigured) and I imagine the scraper/relayer do the same though I don't have experience with them. Crashing here feels in line with the existing behavior.

Let me know if you'd like me to change this though. If you think we should, is there a specific place in the call stack you'd want to propagate it up to before crashing?

/// A thread safe cache for signers against a yubihsm.
/// Yubihsms have a limited number of sessions (16), which remain open for at least 30 seconds. If a number of connections is requested
/// too quickly, the devices resources will become exhausted leading to failures.
static YUBIHSM_CLIENT_CACHE: LazyLock<Mutex<HashMap<YubihsmClientCacheKey, WrappedSigner>>> =
Copy link
Author

Choose a reason for hiding this comment

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

Original Comment

I wonder if it would make sense to get a shared access for readers and exclusive access to writers. I suppose that majority of the times we need only read a value from this hash map. Probably, RwLock is more appropriate here?

Done

Copy link

changeset-bot bot commented Mar 6, 2025

⚠️ No Changeset found

Latest commit: 87e6b93

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

let client = YubiHsmSigner::connect(http_config, credentials);
let signer = Arc::new(YubiSigner::create(client, id).unwrap());
{
let mut cache = YUBIHSM_CLIENT_CACHE.write().unwrap();
Copy link
Author

@keefertaylor keefertaylor Mar 7, 2025

Choose a reason for hiding this comment

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

Original comment

I would prefer to use expect with clear message if app is crushed.

Done. Also updated the connect function below from unwrap -> expect.

@@ -1,17 +1,17 @@
use super::aws_credentials::AwsChainCredentialsProvider;
Copy link
Author

Choose a reason for hiding this comment

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

Original Comment

We don't follow the following convention everywhere, but let's do it in new changes. > Could you please order imports in the following order:

  1. std imports sorted alphabetically followed by empty line
  2. external imports sorted alphabetically followed by empty line
  3. imports from hyperlane_* sorted alphabetically followed by empty line
  4. imports from crate::* sorted alphabetically followed by empty line
  5. imports from super::* sorted alphabetically followed by empty line

Done. Modified imports of other files I touched as well.

I'm not sure about the correct ordering for the pub statements in src/signer/mod.rs, so feel free to bug me if it's still wrong.

@@ -30,16 +30,20 @@ tracing-futures.workspace = true
tracing.workspace = true
url.workspace = true

ecdsa = { version = "0.14", default-features = false }
Copy link
Author

Choose a reason for hiding this comment

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

Original Comment

These dependencies should be moved to workspace Cargo.toml file and referred as dependencies above. These dependencies are external, so, they should mentioned in the previous list.

Done.

It appears (I am new to rust) that in order to get the traits I need across the libraries to be recognized properly, that yubihsm, ethers-rs and this workspace must have the same dependencies of elliptic-curve, ecdsa and k256.

Since they hyperlane fork of ethers-res has k256 version 0.11 and elliptic-curve version 0.12.3 we need to downgrade the dependencies here.

The alternative (I think) is upgrading ethers-rs. I briefly banged on that for a while, and it seems like a non-trivial amount of work. WDYT?

@keefertaylor keefertaylor changed the title Feat: Signing with Yubihsm2 feat: Signing with Yubihsm2 Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Review
Development

Successfully merging this pull request may close these issues.

1 participant