Skip to content

Commit

Permalink
Add PrivateKeySecretManager
Browse files Browse the repository at this point in the history
  • Loading branch information
thibault-martinez committed Jul 26, 2023
1 parent 5260caf commit facd437
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 0 deletions.
39 changes: 39 additions & 0 deletions sdk/src/client/secret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
pub mod ledger_nano;
/// Module for signing with a mnemonic or seed
pub mod mnemonic;
pub mod private_key;
/// Module for signing with a Stronghold vault
#[cfg(feature = "stronghold")]
#[cfg_attr(docsrs, doc(cfg(feature = "stronghold")))]
Expand All @@ -30,6 +31,7 @@ use zeroize::Zeroizing;
#[cfg(feature = "ledger_nano")]
use self::ledger_nano::LedgerSecretManager;
use self::mnemonic::MnemonicSecretManager;
use self::private_key::PrivateKeySecretManager;
#[cfg(feature = "stronghold")]
use self::stronghold::StrongholdSecretManager;
pub use self::types::{GenerateAddressOptions, LedgerNanoStatus};
Expand Down Expand Up @@ -137,6 +139,8 @@ pub enum SecretManager {
/// LedgerNano or Stronghold instead.
Mnemonic(MnemonicSecretManager),

PrivateKey(PrivateKeySecretManager),

/// Secret manager that's just a placeholder, so it can be provided to an online wallet, but can't be used for
/// signing.
Placeholder,
Expand All @@ -150,6 +154,7 @@ impl Debug for SecretManager {
#[cfg(feature = "ledger_nano")]
Self::LedgerNano(_) => f.debug_tuple("LedgerNano").field(&"...").finish(),
Self::Mnemonic(_) => f.debug_tuple("Mnemonic").field(&"...").finish(),
Self::PrivateKey(_) => f.debug_tuple("PrivateKey").field(&"...").finish(),
Self::Placeholder => f.debug_struct("Placeholder").finish(),
}
}
Expand Down Expand Up @@ -180,6 +185,9 @@ pub enum SecretManagerDto {
/// Mnemonic
#[serde(alias = "mnemonic")]
Mnemonic(Zeroizing<String>),
/// Private Key
#[serde(alias = "privateKey")]
PrivateKey(Zeroizing<String>),
/// Hex seed
#[serde(alias = "hexSeed")]
HexSeed(Zeroizing<String>),
Expand Down Expand Up @@ -215,6 +223,11 @@ impl TryFrom<SecretManagerDto> for SecretManager {
Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic.as_str().to_owned())?)
}

SecretManagerDto::PrivateKey(_private_key) => {
panic!()
// Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(private_key.as_str().to_owned())?)
}

SecretManagerDto::HexSeed(hex_seed) => {
// `SecretManagerDto` is `ZeroizeOnDrop` so it will take care of zeroizing the original.
Self::Mnemonic(MnemonicSecretManager::try_from_hex_seed(hex_seed)?)
Expand Down Expand Up @@ -247,6 +260,9 @@ impl From<&SecretManager> for SecretManagerDto {
// the client/wallet we also don't need to convert it in this direction with the mnemonic/seed, we only need
// to know the type
SecretManager::Mnemonic(_mnemonic) => Self::Mnemonic("...".to_string().into()),

SecretManager::PrivateKey(_private_key) => Self::PrivateKey("...".to_string().into()),

SecretManager::Placeholder => Self::Placeholder,
}
}
Expand Down Expand Up @@ -277,6 +293,11 @@ impl SecretManage for SecretManager {
.generate_ed25519_addresses(coin_type, account_index, address_indexes, options)
.await
}
Self::PrivateKey(secret_manager) => {
secret_manager
.generate_ed25519_addresses(coin_type, account_index, address_indexes, options)
.await
}
Self::Placeholder => Err(Error::PlaceholderSecretManager),
}
}
Expand All @@ -302,6 +323,11 @@ impl SecretManage for SecretManager {
.generate_evm_addresses(coin_type, account_index, address_indexes, options)
.await
}
Self::PrivateKey(secret_manager) => {
secret_manager
.generate_evm_addresses(coin_type, account_index, address_indexes, options)
.await
}
Self::Placeholder => Err(Error::PlaceholderSecretManager),
}
}
Expand All @@ -313,6 +339,7 @@ impl SecretManage for SecretManager {
#[cfg(feature = "ledger_nano")]
Self::LedgerNano(secret_manager) => Ok(secret_manager.sign_ed25519(msg, chain).await?),
Self::Mnemonic(secret_manager) => secret_manager.sign_ed25519(msg, chain).await,
Self::PrivateKey(secret_manager) => secret_manager.sign_ed25519(msg, chain).await,
Self::Placeholder => Err(Error::PlaceholderSecretManager),
}
}
Expand All @@ -328,6 +355,7 @@ impl SecretManage for SecretManager {
#[cfg(feature = "ledger_nano")]
Self::LedgerNano(secret_manager) => Ok(secret_manager.sign_secp256k1_ecdsa(msg, chain).await?),
Self::Mnemonic(secret_manager) => secret_manager.sign_secp256k1_ecdsa(msg, chain).await,
Self::PrivateKey(secret_manager) => secret_manager.sign_secp256k1_ecdsa(msg, chain).await,
Self::Placeholder => Err(Error::PlaceholderSecretManager),
}
}
Expand All @@ -351,6 +379,11 @@ impl SecretManage for SecretManager {
.sign_transaction_essence(prepared_transaction_data, time)
.await
}
Self::PrivateKey(secret_manager) => {
secret_manager
.sign_transaction_essence(prepared_transaction_data, time)
.await
}
Self::Placeholder => Err(Error::PlaceholderSecretManager),
}
}
Expand All @@ -365,6 +398,7 @@ impl SecretManage for SecretManager {
#[cfg(feature = "ledger_nano")]
Self::LedgerNano(secret_manager) => Ok(secret_manager.sign_transaction(prepared_transaction_data).await?),
Self::Mnemonic(secret_manager) => secret_manager.sign_transaction(prepared_transaction_data).await,
Self::PrivateKey(secret_manager) => secret_manager.sign_transaction(prepared_transaction_data).await,
Self::Placeholder => Err(Error::PlaceholderSecretManager),
}
}
Expand All @@ -390,6 +424,7 @@ impl SecretManagerConfig for SecretManager {
#[cfg(feature = "ledger_nano")]
Self::LedgerNano(s) => s.to_config().map(Self::Config::LedgerNano),
Self::Mnemonic(_) => None,
Self::PrivateKey(_) => None,
Self::Placeholder => None,
}
}
Expand All @@ -406,6 +441,10 @@ impl SecretManagerConfig for SecretManager {
SecretManagerDto::Mnemonic(mnemonic) => {
Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic.as_str().to_owned())?)
}
SecretManagerDto::PrivateKey(_private_key) => panic!(),
// Self::Mnemonic(PrivateKeySecretManager::try_from_base58(
// private_key.as_str().to_owned(),
// )?),
SecretManagerDto::Placeholder => Self::Placeholder,
})
}
Expand Down
112 changes: 112 additions & 0 deletions sdk/src/client/secret/private_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2021 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! Implementation of [`PrivateKeySecretManager`].

use std::ops::Range;

use async_trait::async_trait;
use crypto::{
hashes::{blake2b::Blake2b256, Digest},
keys::bip44::Bip44,
signatures::{
ed25519,
secp256k1_ecdsa::{self, EvmAddress},
},
};

use super::{GenerateAddressOptions, SecretManage};
use crate::{
client::{api::PreparedTransactionData, Error},
types::block::{
address::Ed25519Address, payload::transaction::TransactionPayload, signature::Ed25519Signature, unlock::Unlocks,
},
};

/// Secret manager based on a single private key.
pub struct PrivateKeySecretManager(ed25519::SecretKey);

impl std::fmt::Debug for PrivateKeySecretManager {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("PrivateKeySecretManager").finish()
}
}

#[async_trait]
impl SecretManage for PrivateKeySecretManager {
type Error = Error;

async fn generate_ed25519_addresses(
&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).try_into().map_err(|_e| {
crate::client::Error::Blake2b256("hashing the public key while generating the address failed.")
})?;

crate::client::Result::Ok(vec![Ed25519Address::new(result)])
}

async fn generate_evm_addresses(
&self,
_coin_type: u32,
_account_index: u32,
_address_indexes: Range<u32>,
_options: impl Into<Option<GenerateAddressOptions>> + Send,
) -> Result<Vec<EvmAddress>, Self::Error> {
panic!()
}

async fn sign_ed25519(&self, msg: &[u8], _chain: Bip44) -> Result<Ed25519Signature, Self::Error> {
let public_key = self.0.public_key();
let signature = self.0.sign(msg);

Ok(Ed25519Signature::new(public_key, signature))
}

async fn sign_secp256k1_ecdsa(
&self,
_msg: &[u8],
_chain: Bip44,
) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::RecoverableSignature), Self::Error> {
panic!()
}

async fn sign_transaction_essence(
&self,
prepared_transaction_data: &PreparedTransactionData,
time: Option<u32>,
) -> Result<Unlocks, Self::Error> {
super::default_sign_transaction_essence(self, prepared_transaction_data, time).await
}

async fn sign_transaction(
&self,
prepared_transaction_data: PreparedTransactionData,
) -> Result<TransactionPayload, Self::Error> {
super::default_sign_transaction(self, prepared_transaction_data).await
}
}

// impl PrivateKeySecretManager {
// /// Create a new [`PrivateKeySecretManager`] from a BIP-39 mnemonic in English.
// ///
// /// For more information, see <https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki>.
// pub fn try_from_mnemonic(mnemonic: impl Into<Mnemonic>) -> Result<Self, Error> {
// Ok(Self(Client::mnemonic_to_seed(mnemonic.into())?.into()))
// }

// /// Create a new [`PrivateKeySecretManager`] from a hex-encoded raw seed string.
// pub fn try_from_hex_seed(hex: impl Into<Zeroizing<String>>) -> Result<Self, Error> {
// let hex = hex.into();
// let bytes = Zeroizing::new(prefix_hex::decode::<Vec<u8>>(hex.as_str())?);
// let seed = Seed::from_bytes(bytes.as_ref());
// Ok(Self(seed))
// }
// }
10 changes: 10 additions & 0 deletions sdk/src/wallet/core/operations/address_generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ impl Wallet {
)
.await?
}
SecretManager::PrivateKey(private_key) => {
private_key
.generate_ed25519_addresses(
self.coin_type.load(Ordering::Relaxed),
account_index,
address_index..address_index + 1,
options,
)
.await?
}
SecretManager::Placeholder => return Err(crate::client::Error::PlaceholderSecretManager.into()),
};

Expand Down

0 comments on commit facd437

Please sign in to comment.