Skip to content

Commit

Permalink
Add SecretManage::generate_ed25519_public_keys (#1655)
Browse files Browse the repository at this point in the history
* Add SecretManage::generate_ed25519_public_keys

* Add doc
  • Loading branch information
thibault-martinez authored Nov 21, 2023
1 parent 9f40b1a commit 805f5df
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 85 deletions.
74 changes: 40 additions & 34 deletions sdk/src/client/secret/ledger_nano.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use std::{collections::HashMap, ops::Range};
use async_trait::async_trait;
use crypto::{
keys::{bip44::Bip44, slip10::Segment},
signatures::secp256k1_ecdsa::{self, EvmAddress},
signatures::{
ed25519,
secp256k1_ecdsa::{self, EvmAddress},
},
};
use iota_ledger_nano::{
api::errors::APIError, get_app_config, get_buffer_size, get_ledger, get_opened_app, LedgerBIP32Index,
Expand All @@ -26,7 +29,7 @@ use crate::{
LedgerNanoStatus, PreparedTransactionData,
},
types::block::{
address::{AccountAddress, Address, AnchorAddress, Ed25519Address, NftAddress},
address::{AccountAddress, Address, AnchorAddress, NftAddress},
output::Output,
payload::signed_transaction::SignedTransactionPayload,
signature::{Ed25519Signature, Signature},
Expand Down Expand Up @@ -131,41 +134,44 @@ impl TryFrom<u8> for LedgerDeviceType {
impl SecretManage for LedgerSecretManager {
type Error = crate::client::Error;

async fn generate_ed25519_addresses(
async fn generate_ed25519_public_keys(
&self,
// https://github.com/satoshilabs/slips/blob/master/slip-0044.md
// current ledger app only supports IOTA_COIN_TYPE, SHIMMER_COIN_TYPE and TESTNET_COIN_TYPE
coin_type: u32,
account_index: u32,
address_indexes: Range<u32>,
options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<Ed25519Address>, Self::Error> {
let options = options.into().unwrap_or_default();
let bip32_account = account_index.harden().into();

let bip32 = LedgerBIP32Index {
bip32_index: address_indexes.start.harden().into(),
bip32_change: u32::from(options.internal).harden().into(),
};

// lock the mutex to prevent multiple simultaneous requests to a ledger
let lock = self.mutex.lock().await;

// get ledger
let ledger = get_ledger(coin_type, bip32_account, self.is_simulator).map_err(Error::from)?;
if ledger.is_debug_app() {
ledger
.set_non_interactive_mode(self.non_interactive)
.map_err(Error::from)?;
}

let addresses = ledger
.get_addresses(options.ledger_nano_prompt, bip32, address_indexes.len())
.map_err(Error::from)?;

drop(lock);

Ok(addresses.into_iter().map(Ed25519Address::new).collect())
_coin_type: u32,
_account_index: u32,
_address_indexes: Range<u32>,
_options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<ed25519::PublicKey>, Self::Error> {
// need an update on the ledger C lib
todo!();
//
// let options = options.into().unwrap_or_default();
// let bip32_account = account_index.harden().into();

// let bip32 = LedgerBIP32Index {
// bip32_index: address_indexes.start.harden().into(),
// bip32_change: u32::from(options.internal).harden().into(),
// };

// // lock the mutex to prevent multiple simultaneous requests to a ledger
// let lock = self.mutex.lock().await;

// // get ledger
// let ledger = get_ledger(coin_type, bip32_account, self.is_simulator).map_err(Error::from)?;
// if ledger.is_debug_app() {
// ledger
// .set_non_interactive_mode(self.non_interactive)
// .map_err(Error::from)?;
// }

// let addresses = ledger
// .get_addresses(options.ledger_nano_prompt, bip32, address_indexes.len())
// .map_err(Error::from)?;

// drop(lock);

// Ok(addresses.into_iter().map(Ed25519Address::new).collect())
}

async fn generate_evm_addresses(
Expand Down
16 changes: 5 additions & 11 deletions sdk/src/client/secret/mnemonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::ops::Range;

use async_trait::async_trait;
use crypto::{
hashes::{blake2b::Blake2b256, Digest},
keys::{bip39::Mnemonic, bip44::Bip44, slip10::Seed},
signatures::{
ed25519,
Expand All @@ -20,8 +19,7 @@ use super::{GenerateAddressOptions, SecretManage};
use crate::{
client::{api::PreparedTransactionData, Client, Error},
types::block::{
address::Ed25519Address, payload::signed_transaction::SignedTransactionPayload, signature::Ed25519Signature,
unlock::Unlocks,
payload::signed_transaction::SignedTransactionPayload, signature::Ed25519Signature, unlock::Unlocks,
},
};

Expand All @@ -40,13 +38,13 @@ impl std::fmt::Debug for MnemonicSecretManager {
impl SecretManage for MnemonicSecretManager {
type Error = Error;

async fn generate_ed25519_addresses(
async fn generate_ed25519_public_keys(
&self,
coin_type: u32,
account_index: u32,
address_indexes: Range<u32>,
options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<Ed25519Address>, Self::Error> {
) -> Result<Vec<ed25519::PublicKey>, Self::Error> {
let internal = options.into().map(|o| o.internal).unwrap_or_default();

Ok(address_indexes
Expand All @@ -59,13 +57,9 @@ impl SecretManage for MnemonicSecretManager {
let public_key = chain
.derive(&self.0.to_master_key::<ed25519::SecretKey>())
.secret_key()
.public_key()
.to_bytes();

// Hash the public key to get the address
let result = Blake2b256::digest(public_key).into();
.public_key();

crate::client::Result::Ok(Ed25519Address::new(result))
crate::client::Result::Ok(public_key)
})
.collect::<Result<_, _>>()?)
}
Expand Down
38 changes: 30 additions & 8 deletions sdk/src/client/secret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ use std::{collections::HashMap, fmt::Debug, ops::Range, str::FromStr};

use async_trait::async_trait;
use crypto::{
hashes::{blake2b::Blake2b256, Digest},
keys::{bip39::Mnemonic, bip44::Bip44},
signatures::secp256k1_ecdsa::{self, EvmAddress},
signatures::{
ed25519,
secp256k1_ecdsa::{self, EvmAddress},
},
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use zeroize::Zeroizing;
Expand Down Expand Up @@ -66,6 +70,17 @@ use crate::{
pub trait SecretManage: Send + Sync {
type Error: std::error::Error + Send + Sync;

/// Generates public keys.
///
/// For `coin_type`, see also <https://github.com/satoshilabs/slips/blob/master/slip-0044.md>.
async fn generate_ed25519_public_keys(
&self,
coin_type: u32,
account_index: u32,
address_indexes: Range<u32>,
options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<ed25519::PublicKey>, Self::Error>;

/// Generates addresses.
///
/// For `coin_type`, see also <https://github.com/satoshilabs/slips/blob/master/slip-0044.md>.
Expand All @@ -75,7 +90,14 @@ pub trait SecretManage: Send + Sync {
account_index: u32,
address_indexes: Range<u32>,
options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<Ed25519Address>, Self::Error>;
) -> Result<Vec<Ed25519Address>, Self::Error> {
Ok(self
.generate_ed25519_public_keys(coin_type, account_index, address_indexes, options)
.await?
.iter()
.map(|public_key| Ed25519Address::new(Blake2b256::digest(public_key.to_bytes()).into()))
.collect())
}

async fn generate_evm_addresses(
&self,
Expand Down Expand Up @@ -308,31 +330,31 @@ impl From<&SecretManager> for SecretManagerDto {
impl SecretManage for SecretManager {
type Error = Error;

async fn generate_ed25519_addresses(
async fn generate_ed25519_public_keys(
&self,
coin_type: u32,
account_index: u32,
address_indexes: Range<u32>,
options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> crate::client::Result<Vec<Ed25519Address>> {
) -> Result<Vec<ed25519::PublicKey>, Self::Error> {
match self {
#[cfg(feature = "stronghold")]
Self::Stronghold(secret_manager) => Ok(secret_manager
.generate_ed25519_addresses(coin_type, account_index, address_indexes, options)
.generate_ed25519_public_keys(coin_type, account_index, address_indexes, options)
.await?),
#[cfg(feature = "ledger_nano")]
Self::LedgerNano(secret_manager) => Ok(secret_manager
.generate_ed25519_addresses(coin_type, account_index, address_indexes, options)
.generate_ed25519_public_keys(coin_type, account_index, address_indexes, options)
.await?),
Self::Mnemonic(secret_manager) => {
secret_manager
.generate_ed25519_addresses(coin_type, account_index, address_indexes, options)
.generate_ed25519_public_keys(coin_type, account_index, address_indexes, options)
.await
}
#[cfg(feature = "private_key_secret_manager")]
Self::PrivateKey(secret_manager) => {
secret_manager
.generate_ed25519_addresses(coin_type, account_index, address_indexes, options)
.generate_ed25519_public_keys(coin_type, account_index, address_indexes, options)
.await
}
Self::Placeholder => Err(Error::PlaceholderSecretManager),
Expand Down
15 changes: 4 additions & 11 deletions sdk/src/client/secret/private_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::ops::Range;

use async_trait::async_trait;
use crypto::{
hashes::{blake2b::Blake2b256, Digest},
keys::bip44::Bip44,
signatures::{
ed25519,
Expand All @@ -20,8 +19,7 @@ use super::{GenerateAddressOptions, SecretManage};
use crate::{
client::{api::PreparedTransactionData, Error},
types::block::{
address::Ed25519Address, payload::signed_transaction::SignedTransactionPayload, signature::Ed25519Signature,
unlock::Unlocks,
payload::signed_transaction::SignedTransactionPayload, signature::Ed25519Signature, unlock::Unlocks,
},
};

Expand All @@ -38,19 +36,14 @@ impl std::fmt::Debug for PrivateKeySecretManager {
impl SecretManage for PrivateKeySecretManager {
type Error = Error;

async fn generate_ed25519_addresses(
async fn generate_ed25519_public_keys(
&self,
_coin_type: u32,
_account_index: u32,
_address_indexes: Range<u32>,
_options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<Ed25519Address>, Self::Error> {
let public_key = self.0.public_key().to_bytes();

// Hash the public key to get the address
let result = Blake2b256::digest(public_key).into();

crate::client::Result::Ok(vec![Ed25519Address::new(result)])
) -> Result<Vec<ed25519::PublicKey>, Self::Error> {
crate::client::Result::Ok(vec![self.0.public_key()])
}

async fn generate_evm_addresses(
Expand Down
32 changes: 11 additions & 21 deletions sdk/src/client/stronghold/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::ops::Range;

use async_trait::async_trait;
use crypto::{
hashes::{blake2b::Blake2b256, Digest},
keys::{
bip39::{Mnemonic, MnemonicRef, Passphrase},
bip44::Bip44,
Expand Down Expand Up @@ -36,22 +35,21 @@ use crate::{
stronghold::Error,
},
types::block::{
address::Ed25519Address, payload::signed_transaction::SignedTransactionPayload, signature::Ed25519Signature,
unlock::Unlocks,
payload::signed_transaction::SignedTransactionPayload, signature::Ed25519Signature, unlock::Unlocks,
},
};

#[async_trait]
impl SecretManage for StrongholdAdapter {
type Error = crate::client::Error;

async fn generate_ed25519_addresses(
async fn generate_ed25519_public_keys(
&self,
coin_type: u32,
account_index: u32,
address_indexes: Range<u32>,
options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<Ed25519Address>, Self::Error> {
) -> Result<Vec<ed25519::PublicKey>, Self::Error> {
// Prevent the method from being invoked when the key has been cleared from the memory. Do note that Stronghold
// only asks for a key for reading / writing a snapshot, so without our cached key this method is invocable, but
// it doesn't make sense when it comes to our user (signing transactions / generating addresses without a key).
Expand All @@ -64,8 +62,8 @@ impl SecretManage for StrongholdAdapter {
// Stronghold arguments.
let seed_location = Slip10DeriveInput::Seed(Location::generic(SECRET_VAULT_PATH, SEED_RECORD_PATH));

// Addresses to return.
let mut addresses = Vec::new();
// Public keys to return.
let mut public_keys = Vec::new();
let internal = options.into().map(|o| o.internal).unwrap_or_default();

for address_index in address_indexes {
Expand Down Expand Up @@ -104,17 +102,11 @@ impl SecretManage for StrongholdAdapter {
.delete_secret(derive_location.record_path())
.map_err(Error::from)?;

// Hash the public key to get the address.
let hash = Blake2b256::digest(public_key);

// Convert the hash into [Address].
let address = Ed25519Address::new(hash.into());

// Collect it.
addresses.push(address);
public_keys.push(public_key);
}

Ok(addresses)
Ok(public_keys)
}

async fn generate_evm_addresses(
Expand Down Expand Up @@ -589,12 +581,10 @@ mod tests {
stronghold_adapter.clear_key().await;

// Address generation returns an error when the key is cleared.
assert!(
stronghold_adapter
.generate_ed25519_addresses(IOTA_COIN_TYPE, 0, 0..1, None,)
.await
.is_err()
);
assert!(stronghold_adapter
.generate_ed25519_addresses(IOTA_COIN_TYPE, 0, 0..1, None,)
.await
.is_err());

stronghold_adapter.set_password("drowssap".to_owned()).await.unwrap();

Expand Down

0 comments on commit 805f5df

Please sign in to comment.