Skip to content

Commit

Permalink
feat(signers): add Wallet::encrypt_keystore (gakonst#2502)
Browse files Browse the repository at this point in the history
* feat(signers): add Wallet::encrypt_keystore

* chore(signers): use a non-random private key

* chore(signers): fix comment

* chore(signers): fix encrypted_json_keystore_from_pk test
  • Loading branch information
0xZerohero authored and AA committed Jul 27, 2023
1 parent 8d75a0f commit 7564f38
Showing 1 changed file with 57 additions and 10 deletions.
67 changes: 57 additions & 10 deletions ethers-signers/src/wallet/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ impl Wallet<SigningKey> {
Ok(Self { signer, address, chain_id: 1 })
}

/// Creates a new encrypted JSON with the provided private key and password and stores it in the
/// provided directory. Returns a tuple (Wallet, String) of the wallet instance for the
/// keystore with its random UUID. Accepts an optional name for the keystore file. If `None`,
/// the keystore is stored as the stringified UUID.
#[cfg(not(target_arch = "wasm32"))]
pub fn encrypt_keystore<P, R, B, S>(
keypath: P,
rng: &mut R,
pk: B,
password: S,
name: Option<&str>,
) -> Result<(Self, String), WalletError>
where
P: AsRef<Path>,
R: Rng + CryptoRng,
B: AsRef<[u8]>,
S: AsRef<[u8]>,
{
let uuid = eth_keystore::encrypt_key(keypath, rng, &pk, password, name)?;
let signer = SigningKey::from_slice(pk.as_ref())?;
let address = secret_key_to_address(&signer);
Ok((Self { signer, address, chain_id: 1 }, uuid))
}

/// Creates a new random keypair seeded with the provided RNG
pub fn new<R: Rng + CryptoRng>(rng: &mut R) -> Self {
let signer = SigningKey::random(rng);
Expand Down Expand Up @@ -183,27 +207,50 @@ mod tests {
}
}

#[tokio::test]
async fn encrypted_json_keystore() {
// create and store a random encrypted JSON keystore in this directory
let dir = tempdir().unwrap();
let mut rng = rand::thread_rng();
let (key, uuid) =
Wallet::<SigningKey>::new_keystore(&dir, &mut rng, "randpsswd", None).unwrap();

// sign a message using the above key
async fn test_encrypted_json_keystore(key: Wallet<SigningKey>, uuid: &str, dir: &Path) {
// sign a message using the given key
let message = "Some data";
let signature = key.sign_message(message).await.unwrap();

// read from the encrypted JSON keystore and decrypt it, while validating that the
// signatures produced by both the keys should match
let path = Path::new(dir.path()).join(uuid);
let path = Path::new(dir).join(uuid);
let key2 = Wallet::<SigningKey>::decrypt_keystore(path.clone(), "randpsswd").unwrap();

let signature2 = key2.sign_message(message).await.unwrap();
assert_eq!(signature, signature2);

std::fs::remove_file(&path).unwrap();
}

#[tokio::test]
async fn encrypted_json_keystore_new() {
// create and store an encrypted JSON keystore in this directory
let dir = tempdir().unwrap();
let mut rng = rand::thread_rng();
let (key, uuid) =
Wallet::<SigningKey>::new_keystore(&dir, &mut rng, "randpsswd", None).unwrap();

test_encrypted_json_keystore(key, &uuid, dir.path()).await;
}

#[tokio::test]
async fn encrypted_json_keystore_from_pk() {
// create and store an encrypted JSON keystore in this directory
let dir = tempdir().unwrap();
let mut rng = rand::thread_rng();

let private_key =
hex::decode("6f142508b4eea641e33cb2a0161221105086a84584c74245ca463a49effea30b")
.unwrap();

let (key, uuid) =
Wallet::<SigningKey>::encrypt_keystore(&dir, &mut rng, private_key, "randpsswd", None)
.unwrap();

test_encrypted_json_keystore(key, &uuid, dir.path()).await;
}

#[tokio::test]
async fn signs_msg() {
let message = "Some data";
Expand Down

0 comments on commit 7564f38

Please sign in to comment.