Skip to content

Commit

Permalink
derive script key (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
SWvheerden authored and brianp committed May 15, 2024
1 parent 70b1a18 commit 8f6b3ad
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 28 deletions.
72 changes: 55 additions & 17 deletions base_layer/core/src/transactions/key_manager/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use std::{collections::HashMap, ops::Shl, thread, thread::sleep, time::Duration}

use blake2::Blake2b;
use digest::consts::U64;
use futures::AsyncReadExt;
use log::*;
use minotari_ledger_wallet_comms::ledger_wallet::get_transport;
#[cfg(feature = "ledger")]
Expand Down Expand Up @@ -87,6 +88,7 @@ use crate::{
CryptoFactories,
},
};
use crate::transactions::key_manager::interface::{TransactionKeyManagerLabel};

hash_domain!(
KeyManagerHashingDomain,
Expand Down Expand Up @@ -201,26 +203,43 @@ where TBackend: KeyManagerBackend<PublicKey> + 'static
.await;
Ok(km.derive_public_key(*index)?.key)
},
KeyId::Derived { branch, index } => match &self.wallet_type {
WalletType::Software(k, pk) => Ok(pk.clone()),
WalletType::Ledger(ledger) => {
#[cfg(not(feature = "ledger"))]
return Err(TransactionError::LedgerDeviceError(LedgerDeviceError::NotSupported));

#[cfg(feature = "ledger")]
{
match &ledger.pubkey {
Some(pk) => Ok(pk.clone()),
None => Err(KeyManagerServiceError::UnknownKeyBranch),
}
}
},
KeyId::Derived { branch, label, index } => {
let public_alpha = match &self.wallet_type {
WalletType::Software(_k, pk) => pk,
WalletType::Ledger(ledger) => ledger.pubkey.as_ref().ok_or(KeyManagerServiceError::LedgerError(
"Key manager set to use ledger, ledger alpha public key missing".to_string(),
))?,
};
let km = self
.key_managers
.get(branch)
.ok_or(KeyManagerServiceError::UnknownKeyBranch)?
.read()
.await;
let branch_key = km.get_private_key(*index)?;
let hasher = Self::get_domain_hasher(label)?;
let hasher = hasher.chain(branch_key.as_bytes()).finalize();
let private_key = PrivateKey::from_uniform_bytes(hasher.as_ref()).map_err(|_| {
KeyManagerServiceError::UnknownError(
"Invalid private key for sender offset private key".to_string(),
)
})?;
let public_key = PublicKey::from_secret_key(&private_key);
let public_key = public_alpha + &public_key;
Ok(public_key)
},
KeyId::Imported { key } => Ok(key.clone()),
KeyId::Zero => Ok(PublicKey::default()),
}
}

fn get_domain_hasher(label: &str)->Result<DomainSeparatedHasher<Blake2b<U64>, KeyManagerHashingDomain>,KeyManagerServiceError>{
let tx_label = label.parse::<TransactionKeyManagerLabel>().map_err(|e| KeyManagerServiceError::UnknownError(format!("Could not retrieve label for derived key: {}", e)))?;
match tx_label{
TransactionKeyManagerLabel::ScriptKey => {Ok(DomainSeparatedHasher::<Blake2b<U64>, KeyManagerHashingDomain>::new_with_label("script key"))},
}
}

pub async fn get_next_spend_and_script_key_ids(
&self,
) -> Result<(TariKeyId, PublicKey, TariKeyId, PublicKey), KeyManagerServiceError> {
Expand All @@ -232,6 +251,7 @@ where TBackend: KeyManagerBackend<PublicKey> + 'static
.ok_or(KeyManagerServiceError::KyeIdWithoutIndex)?;
let script_key_id = KeyId::Derived {
branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(),
label: TransactionKeyManagerLabel::ScriptKey.get_branch_key(),
index,
};
let script_public_key = self.get_public_key_at_key_id(&script_key_id).await?;
Expand All @@ -247,12 +267,13 @@ where TBackend: KeyManagerBackend<PublicKey> + 'static
) -> Result<Option<TariKeyId>, KeyManagerServiceError> {
let index = match spend_key_id {
KeyId::Managed { index, .. } => *index,
KeyId::Derived { index, .. } => return Ok(None),
KeyId::Derived { .. } => return Ok(None),
KeyId::Imported { .. } => return Ok(None),
KeyId::Zero => return Ok(None),
};
let script_key_id = KeyId::Derived {
branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(),
label: TransactionKeyManagerLabel::ScriptKey.get_branch_key(),
index,
};

Expand Down Expand Up @@ -352,9 +373,26 @@ where TBackend: KeyManagerBackend<PublicKey> + 'static
let key = km.get_private_key(*index)?;
Ok(key)
},
KeyId::Derived { branch, index } => match &self.wallet_type {
KeyId::Derived { branch, label, index } => match &self.wallet_type {
WalletType::Ledger(_) => panic!(),
WalletType::Software(k, pk) => Ok(k.clone()),
WalletType::Software(k, _pk) => {
let km = self
.key_managers
.get(branch)
.ok_or(KeyManagerServiceError::UnknownKeyBranch)?
.read()
.await;
let branch_key = km.get_private_key(*index)?;
let hasher = Self::get_domain_hasher(label)?;
let hasher = hasher.chain(branch_key.as_bytes()).finalize();
let private_key = PrivateKey::from_uniform_bytes(hasher.as_ref()).map_err(|_| {
KeyManagerServiceError::UnknownError(
format!("Invalid private key for {}", label),
)
})?;
let private_key = private_key + k;
Ok(private_key)
},
},
KeyId::Imported { key } => {
let pvt_key = self.db.get_imported_key(key)?;
Expand Down
30 changes: 28 additions & 2 deletions base_layer/core/src/transactions/key_manager/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::str::FromStr;
use blake2::Blake2b;
use digest::consts::U64;
use strum_macros::EnumIter;
Expand Down Expand Up @@ -54,7 +55,6 @@ pub enum TxoStage {
pub enum TransactionKeyManagerBranch {
DataEncryption,
Coinbase,
CoinbaseScript,
CommitmentMask,
Nonce,
KernelNonce,
Expand All @@ -68,7 +68,6 @@ impl TransactionKeyManagerBranch {
match self {
TransactionKeyManagerBranch::DataEncryption => "data encryption".to_string(),
TransactionKeyManagerBranch::Coinbase => "coinbase".to_string(),
TransactionKeyManagerBranch::CoinbaseScript => "coinbase script".to_string(),
TransactionKeyManagerBranch::CommitmentMask => "commitment mask".to_string(),
TransactionKeyManagerBranch::Nonce => "nonce".to_string(),
TransactionKeyManagerBranch::KernelNonce => "kernel nonce".to_string(),
Expand All @@ -77,6 +76,33 @@ impl TransactionKeyManagerBranch {
}
}

#[derive(Clone, Copy, EnumIter)]
pub enum TransactionKeyManagerLabel {
ScriptKey
}

impl TransactionKeyManagerLabel {
/// Warning: Changing these strings will affect the backwards compatibility of the wallet with older databases or
/// recovery.
pub fn get_branch_key(self) -> String {
match self {
TransactionKeyManagerLabel::ScriptKey => "script key".to_string(),
}
}
}

impl FromStr for TransactionKeyManagerLabel
{
type Err = String;

fn from_str(id: &str) -> Result<Self, Self::Err> {
match id{
"script key" => Ok(TransactionKeyManagerLabel::ScriptKey),
_ => Err("Unknown label".to_string()),
}
}
}

#[async_trait::async_trait]
pub trait TransactionKeyManagerInterface: KeyManagerInterface<PublicKey> {
/// Gets the pedersen commitment for the specified index
Expand Down
1 change: 1 addition & 0 deletions base_layer/core/src/transactions/key_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub use interface::{
SecretTransactionKeyManagerInterface,
TariKeyId,
TransactionKeyManagerBranch,
TransactionKeyManagerLabel,
TransactionKeyManagerInterface,
TxoStage,
};
Expand Down
9 changes: 7 additions & 2 deletions base_layer/core/src/transactions/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use tari_common::configuration::Network;
use tari_common_sqlite::{error::SqliteStorageError, sqlite_connection_pool::PooledDbConnection};
use tari_common_types::types::{Commitment, PrivateKey, PublicKey, Signature};
use tari_crypto::keys::{PublicKey as PK, SecretKey};
use tari_key_manager::key_manager_service::{storage::sqlite_db::KeyManagerSqliteDatabase, KeyManagerInterface, KeyId};
use tari_key_manager::key_manager_service::{storage::sqlite_db::KeyManagerSqliteDatabase, KeyId, KeyManagerInterface};
use tari_script::{inputs, script, ExecutionStack, TariScript};

use super::transaction_components::{TransactionInputVersion, TransactionOutputVersion};
Expand All @@ -43,6 +43,7 @@ use crate::{
MemoryDbKeyManager,
TariKeyId,
TransactionKeyManagerBranch,
TransactionKeyManagerLabel,
TransactionKeyManagerInterface,
TransactionKeyManagerWrapper,
TxoStage,
Expand Down Expand Up @@ -729,7 +730,11 @@ pub async fn create_stx_protocol_internal(
.get_next_key(TransactionKeyManagerBranch::SenderOffset.get_branch_key())
.await
.unwrap();
let script_key_id = KeyId::Derived {branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(), index:spending_key.managed_index().unwrap() };
let script_key_id = KeyId::Derived {
branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(),
label: TransactionKeyManagerLabel::ScriptKey.get_branch_key(),
index: spending_key.managed_index().unwrap(),
};
let script_public_key = key_manager.get_public_key_at_key_id(&script_key_id).await.unwrap();
let input_data = match &schema.input_data {
Some(data) => data.clone(),
Expand Down
4 changes: 4 additions & 0 deletions base_layer/key_manager/src/key_manager_service/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ pub enum KeyManagerServiceError {
RangeProofError(String),
#[error("Tari Key Manager error: `{0}`")]
TariKeyManagerError(#[from] KMError),
#[error("Unknown error: `{0}`")]
UnknownError(String),
#[error("Ledger error: `{0}`")]
LedgerError(String),
}

impl From<RangeProofError> for KeyManagerServiceError {
Expand Down
21 changes: 20 additions & 1 deletion base_layer/key_manager/src/key_manager_service/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub enum KeyId<PK> {
},
Derived {
branch: String,
label: String,
index: u64,
},
Imported {
Expand Down Expand Up @@ -87,6 +88,7 @@ where PK: Clone
}

pub const MANAGED_KEY_BRANCH: &str = "managed";
pub const DERIVED_KEY_BRANCH: &str = "derived";
pub const IMPORTED_KEY_BRANCH: &str = "imported";
pub const ZERO_KEY_BRANCH: &str = "zero";

Expand All @@ -97,7 +99,11 @@ where PK: ByteArray
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
KeyId::Managed { branch: b, index: i } => write!(f, "{}.{}.{}", MANAGED_KEY_BRANCH, b, i),
KeyId::Derived { branch: b, index: i } => write!(f, "{}.{}.{}", MANAGED_KEY_BRANCH, b, i),
KeyId::Derived {
branch: b,
label: l,
index: i,
} => write!(f, "{}.{}.{}.{}", DERIVED_KEY_BRANCH, b, l, i),
KeyId::Imported { key: public_key } => write!(f, "{}.{}", IMPORTED_KEY_BRANCH, public_key.to_hex()),
KeyId::Zero => write!(f, "{}", ZERO_KEY_BRANCH),
}
Expand Down Expand Up @@ -134,6 +140,19 @@ where PK: ByteArray
Ok(KeyId::Imported { key })
},
ZERO_KEY_BRANCH => Ok(KeyId::Zero),
DERIVED_KEY_BRANCH =>{
if parts.len() != 4 {
return Err("Wrong format".to_string());
}
let index = parts[3]
.parse()
.map_err(|_| "Index for default, invalid u64".to_string())?;
Ok(KeyId::Derived {
branch: parts[1].into(),
label: parts[2].into(),
index,
})
}
_ => Err("Wrong format".to_string()),
},
}
Expand Down
27 changes: 23 additions & 4 deletions base_layer/key_manager/src/key_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
use std::collections::HashMap;

use blake2::Blake2b;
use digest::consts::U64;
use futures::lock::Mutex;
use log::*;
use tari_crypto::keys::PublicKey;
use tari_utilities::hex::Hex;
use tari_crypto::{
hash_domain,
hashing::DomainSeparatedHasher,
keys::{PublicKey, SecretKey},
};
use tari_utilities::{hex::Hex, ByteArray};

use crate::{
cipher_seed::CipherSeed,
Expand All @@ -41,6 +47,8 @@ use crate::{
const LOG_TARGET: &str = "key_manager::key_manager_service";
const KEY_MANAGER_MAX_SEARCH_DEPTH: u64 = 1_000_000;

hash_domain!(KeyManagerHashingDomain, "com.tari.base_layer.key_manager", 1);

pub struct KeyManagerInner<TBackend, PK: PublicKey> {
key_managers: HashMap<String, Mutex<KeyManager<PK, KeyDigest>>>,
db: KeyManagerDatabase<TBackend, PK>,
Expand Down Expand Up @@ -128,14 +136,25 @@ where
.await;
Ok(km.derive_public_key(*index)?.key)
},
KeyId::Derived { branch, index } => {
KeyId::Derived { branch, index, .. } => {
let km = self
.key_managers
.get(branch)
.ok_or(KeyManagerServiceError::UnknownKeyBranch)?
.lock()
.await;
Ok(km.derive_public_key(*index)?.key)
let branch_key = km.get_private_key(*index)?;

let public_key = {
let hasher = DomainSeparatedHasher::<Blake2b<U64>, KeyManagerHashingDomain>::new_with_label("Key manager derived key");
let hasher = hasher.chain(branch_key.as_bytes()).finalize();
let private_key = PK::K::from_uniform_bytes(hasher.as_ref()).map_err(|_| {
KeyManagerServiceError::UnknownError("Invalid private key for Key manager derived key".to_string()
)
})?;
PK::from_secret_key(&private_key)
};
Ok(public_key)
},
KeyId::Imported { key } => Ok(key.clone()),
KeyId::Zero => Ok(PK::default()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ use tari_core::transactions::{
tari_amount::MicroMinotari,
transaction_components::{OutputType, TransactionError, TransactionOutput, WalletOutput},
};
use tari_key_manager::key_manager_service::KeyId;
use tari_script::{inputs, script, ExecutionStack, Opcode, TariScript};
use tari_utilities::hex::Hex;
use tari_key_manager::key_manager_service::KeyId;
use tari_core::transactions::key_manager::TransactionKeyManagerLabel;

use crate::output_manager_service::{
error::{OutputManagerError, OutputManagerStorageError},
Expand Down Expand Up @@ -194,7 +195,11 @@ where
) -> Result<Option<(ExecutionStack, TariKeyId)>, OutputManagerError> {
let (input_data, script_key) = if script == &script!(Nop) {
// This is a nop, so we can just create a new key an create the input stack.
let key = KeyId::Derived {branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(), index:spending_key.managed_index().unwrap() };
let key = KeyId::Derived {
branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(),
label: TransactionKeyManagerLabel::ScriptKey.get_branch_key(),
index: spending_key.managed_index().unwrap(),
};
let public_key = self.master_key_manager.get_public_key_at_key_id(&key).await?;
(inputs!(public_key), key)
} else {
Expand Down Expand Up @@ -277,6 +282,7 @@ where

TariKeyId::Derived {
branch: TransactionKeyManagerBranch::CommitmentMask.get_branch_key(),
label: TransactionKeyManagerLabel::ScriptKey.get_branch_key(),
index: found_index,
}
};
Expand Down

0 comments on commit 8f6b3ad

Please sign in to comment.