//! Helpers for creating wallets for YubiHSM2 use super::Wallet; use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; use ethers_core::{ k256::{PublicKey, Secp256k1}, types::Address, utils::keccak256, }; use yubihsm::{ asymmetric::Algorithm::EcK256, ecdsa::Signer as YubiSigner, object, object::Label, Capability, Client, Connector, Credentials, Domain, }; impl Wallet> { /// Connects to a yubi key's ECDSA account at the provided id pub fn connect(connector: Connector, credentials: Credentials, id: object::Id) -> Self { let client = Client::open(connector, credentials, true).unwrap(); let signer = YubiSigner::create(client, id).unwrap(); signer.into() } /// Creates a new random ECDSA keypair on the yubi at the provided id pub fn new( connector: Connector, credentials: Credentials, id: object::Id, label: Label, domain: Domain, ) -> Self { let client = Client::open(connector, credentials, true).unwrap(); let id = client .generate_asymmetric_key(id, label, domain, Capability::SIGN_ECDSA, EcK256) .unwrap(); let signer = YubiSigner::create(client, id).unwrap(); signer.into() } /// Uploads the provided keypair on the yubi at the provided id pub fn from_key( connector: Connector, credentials: Credentials, id: object::Id, label: Label, domain: Domain, key: impl Into>, ) -> Self { let client = Client::open(connector, credentials, true).unwrap(); let id = client .put_asymmetric_key(id, label, domain, Capability::SIGN_ECDSA, EcK256, key) .unwrap(); let signer = YubiSigner::create(client, id).unwrap(); signer.into() } } impl From> for Wallet> { fn from(signer: YubiSigner) -> Self { // this will never fail let public_key = PublicKey::from_encoded_point(signer.public_key()).unwrap(); let public_key = public_key.to_encoded_point(/* compress = */ false); let public_key = public_key.as_bytes(); debug_assert_eq!(public_key[0], 0x04); let hash = keccak256(&public_key[1..]); let address = Address::from_slice(&hash[12..]); Self { signer, address, chain_id: 1 } } } #[cfg(test)] #[cfg(not(target_arch = "wasm32"))] mod tests { use super::*; use crate::Signer; use std::str::FromStr; #[tokio::test] async fn from_key() { let key = hex::decode("2d8c44dc2dd2f0bea410e342885379192381e82d855b1b112f9b55544f1e0900") .unwrap(); let connector = yubihsm::Connector::mockhsm(); let wallet = Wallet::from_key( connector, Credentials::default(), 0, Label::from_bytes(&[]).unwrap(), Domain::at(1).unwrap(), key, ); let msg = "Some data"; let sig = wallet.sign_message(msg).await.unwrap(); assert_eq!(sig.recover(msg).unwrap(), wallet.address()); assert_eq!( wallet.address(), Address::from_str("2DE2C386082Cff9b28D62E60983856CE1139eC49").unwrap() ); } #[tokio::test] async fn new_key() { let connector = yubihsm::Connector::mockhsm(); let wallet = Wallet::>::new( connector, Credentials::default(), 0, Label::from_bytes(&[]).unwrap(), Domain::at(1).unwrap(), ); let msg = "Some data"; let sig = wallet.sign_message(msg).await.unwrap(); assert_eq!(sig.recover(msg).unwrap(), wallet.address()); } }