Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Change script_signature type to ComSig #3016

Merged
merged 1 commit into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion applications/tari_app_grpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ edition = "2018"
tari_common_types = { version = "^0.8", path = "../../base_layer/common_types"}
tari_core = { path = "../../base_layer/core"}
tari_wallet = { path = "../../base_layer/wallet"}
tari_crypto = { git = "ssh://[email protected]/tari-project/tari-crypto.git", branch = "main" }
tari_crypto = { git = "ssh://[email protected]/tari-project/tari-crypto.git", branch = "main" } #switch back to official after merge
tari_comms = { path = "../../comms"}

chrono = "0.4.6"
Expand Down
16 changes: 12 additions & 4 deletions applications/tari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ message HistoricalBlock {
}


// The NewBlockHeaderTemplate is used for the construction of a new mineable block. It contains all the metadata for the block that the Base Node is able to complete on behalf of a Miner.
// The NewBlockHeaderTemplate is used for the construction of a new mine-able block. It contains all the metadata for the block that the Base Node is able to complete on behalf of a Miner.
message NewBlockHeaderTemplate {
// Version of the block
uint32 version = 1;
Expand Down Expand Up @@ -167,8 +167,8 @@ message TransactionInput {
// The block height that the UTXO was mined
uint64 height = 6;
// A signature with k_s, signing the script, input data, and mined height
Signature script_signature = 7;
// The offset pubkey, K_O
ComSignature script_signature = 7;
// The offset public key, K_O
bytes script_offset_public_key = 8;
}

Expand All @@ -186,7 +186,7 @@ message TransactionOutput {
bytes hash = 4;
// Tari script serialised script
bytes script = 5;
// Tari script offset pubkey, K_O
// Tari script offset public key, K_O
bytes script_offset_public_key = 6;
// UTXO signature with the script offset private key, k_O
Signature sender_metadata_signature = 7;
Expand Down Expand Up @@ -227,6 +227,14 @@ message Signature {
bytes signature = 2;
}

// Define the explicit ComSignature implementation for the Tari base layer. A different signature scheme can be
// employed by redefining this type.
message ComSignature {
bytes public_nonce_commitment = 1;
bytes signature_u = 2;
bytes signature_v = 3;
}

/// Consensus Constants response
message ConsensusConstants {
/// The min height maturity a coinbase utxo must have
Expand Down
42 changes: 42 additions & 0 deletions applications/tari_app_grpc/src/conversions/com_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2020. The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// 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::convert::TryFrom;
use tari_crypto::tari_utilities::ByteArray;

use crate::tari_rpc as grpc;
use tari_core::transactions::types::{ComSignature, Commitment, PrivateKey};

impl TryFrom<grpc::ComSignature> for ComSignature {
type Error = String;

fn try_from(sig: grpc::ComSignature) -> Result<Self, Self::Error> {
let public_nonce = Commitment::from_bytes(&sig.public_nonce_commitment)
.map_err(|_| "Could not get public nonce commitment".to_string())?;
let signature_u =
PrivateKey::from_bytes(&sig.signature_u).map_err(|_| "Could not get partial signature u".to_string())?;
let signature_v =
PrivateKey::from_bytes(&sig.signature_v).map_err(|_| "Could not get partial signature v".to_string())?;

Ok(Self::new(public_nonce, signature_u, signature_v))
}
}
2 changes: 2 additions & 0 deletions applications/tari_app_grpc/src/conversions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod aggregate_body;
mod block;
mod block_header;
mod chain_metadata;
mod com_signature;
mod consensus_constants;
mod historical_block;
mod new_block_template;
Expand All @@ -42,6 +43,7 @@ pub use self::{
block::*,
block_header::*,
chain_metadata::*,
com_signature::*,
consensus_constants::*,
historical_block::*,
new_block_template::*,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ impl From<TransactionInput> for grpc::TransactionInput {
script: input.script.as_bytes(),
input_data: input.input_data.as_bytes(),
height: input.height,
script_signature: Some(grpc::Signature {
public_nonce: Vec::from(input.script_signature.get_public_nonce().as_bytes()),
signature: Vec::from(input.script_signature.get_signature().as_bytes()),
script_signature: Some(grpc::ComSignature {
public_nonce_commitment: Vec::from(input.script_signature.public_nonce().as_bytes()),
signature_u: Vec::from(input.script_signature.u().as_bytes()),
signature_v: Vec::from(input.script_signature.v().as_bytes()),
}),
script_offset_public_key: input.script_offset_public_key.as_bytes().to_vec(),
}
Expand Down
1 change: 0 additions & 1 deletion applications/tari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@ features = ["transactions", "mempool_proto", "base_node_proto"]
version = "^0.12"
default-features = false
features = ["crossterm"]

4 changes: 2 additions & 2 deletions base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn get_mainnet_genesis_block() -> ChainBlock {
pub fn get_stibbons_genesis_block() -> ChainBlock {
// lets get the block
let mut block = get_stibbons_genesis_block_raw();
// Lets load in the stibbons faucet tx's
// Lets load in the stibbons faucet transactions
let mut utxos = Vec::new();
let file = include_str!("faucets/stibbons_faucet.json");
// last 2 lines are used for the kernel creation
Expand Down Expand Up @@ -88,7 +88,7 @@ pub fn get_stibbons_genesis_block() -> ChainBlock {
pub fn get_weatherwax_genesis_block() -> ChainBlock {
// lets get the block
let mut block = get_weatherwax_genesis_block_raw();
// Lets load in the weatherwax faucet tx's
// Lets load in the weatherwax faucet transactions
let mut utxos = Vec::new();
let file = include_str!("faucets/weatherwax_faucet.json");
// last 2 lines are used for the kernel creation
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/proto/transaction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ message TransactionInput {
// The block height that the UTXO was mined
uint64 height = 5;
// A signature with k_s, signing the script, input data, and mined height
Signature script_signature = 6;
ComSignature script_signature = 6;
// The offset pubkey, K_O
bytes script_offset_public_key = 7;
}
Expand Down
8 changes: 8 additions & 0 deletions base_layer/core/src/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ message Signature {
bytes signature = 2;
}

// Define the explicit ComSignature implementation for the Tari base layer. A different signature scheme can be
// employed by redefining this type.
message ComSignature {
bytes public_nonce_commitment = 1;
bytes signature_u = 2;
bytes signature_v = 3;
}

// BlindingFactor wrapper
message BlindingFactor {
bytes data = 1;
Expand Down
34 changes: 33 additions & 1 deletion base_layer/core/src/proto/types_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use super::types as proto;
use crate::transactions::types::{BlindingFactor, Commitment, HashOutput, PrivateKey, PublicKey, Signature};
use crate::transactions::types::{
BlindingFactor,
ComSignature,
Commitment,
HashOutput,
PrivateKey,
PublicKey,
Signature,
};
use std::convert::TryFrom;
use tari_crypto::tari_utilities::{ByteArray, ByteArrayError};

Expand Down Expand Up @@ -65,6 +73,30 @@ impl From<Signature> for proto::Signature {
}
}

//---------------------------------- ComSignature --------------------------------------------//

impl TryFrom<proto::ComSignature> for ComSignature {
type Error = ByteArrayError;

fn try_from(sig: proto::ComSignature) -> Result<Self, Self::Error> {
let public_nonce = Commitment::from_bytes(&sig.public_nonce_commitment)?;
let signature_u = PrivateKey::from_bytes(&sig.signature_u)?;
let signature_v = PrivateKey::from_bytes(&sig.signature_v)?;

Ok(Self::new(public_nonce, signature_u, signature_v))
}
}

impl From<ComSignature> for proto::ComSignature {
fn from(sig: ComSignature) -> Self {
Self {
public_nonce_commitment: sig.public_nonce().to_vec(),
signature_u: sig.u().to_vec(),
signature_v: sig.v().to_vec(),
}
}
}

//---------------------------------- HashOutput --------------------------------------------//

impl From<proto::HashOutput> for HashOutput {
Expand Down
10 changes: 7 additions & 3 deletions base_layer/core/src/transactions/aggregated_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ impl AggregateBody {

self.validate_range_proofs(&factories.range_proof)?;
self.validate_sender_signatures()?;
self.validate_script_offset(script_offset_g)
self.validate_script_offset(script_offset_g, &factories.commitment)
}

pub fn dissolve(self) -> (Vec<TransactionInput>, Vec<TransactionOutput>, Vec<TransactionKernel>) {
Expand Down Expand Up @@ -378,12 +378,16 @@ impl AggregateBody {
}

/// this will validate the script offset of the aggregate body.
fn validate_script_offset(&self, script_offset: PublicKey) -> Result<(), TransactionError> {
fn validate_script_offset(
&self,
script_offset: PublicKey,
factory: &CommitmentFactory,
) -> Result<(), TransactionError> {
trace!(target: LOG_TARGET, "Checking script offset");
// lets count up the input script public keys
let mut input_keys = PublicKey::default();
for input in &self.inputs {
input_keys = input_keys + input.run_and_verify_script()?;
input_keys = input_keys + input.run_and_verify_script(factory)?;
}

// Now lets gather the output public keys and hashes.
Expand Down
58 changes: 38 additions & 20 deletions base_layer/core/src/transactions/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::transactions::{
types::{
BlindingFactor,
Challenge,
ComSignature,
Commitment,
CommitmentFactory,
CryptoFactories,
Expand Down Expand Up @@ -244,18 +245,27 @@ impl UnblindedOutput {
/// Commits an UnblindedOutput into a Transaction input
pub fn as_transaction_input(&self, factory: &CommitmentFactory) -> Result<TransactionInput, TransactionError> {
let commitment = factory.commit(&self.spending_key, &self.value.into());
let script_nonce = PrivateKey::random(&mut OsRng);
let public_script_nonce = PublicKey::from_secret_key(&script_nonce);
let script_nonce_a = PrivateKey::random(&mut OsRng);
let script_nonce_b = PrivateKey::random(&mut OsRng);
let nonce_commitment = factory.commit(&script_nonce_b, &script_nonce_a);

let e = Challenge::new()
.chain(public_script_nonce.as_bytes())
.chain(nonce_commitment.as_bytes())
.chain(self.script.as_bytes().as_slice())
.chain(self.input_data.as_bytes().as_slice())
//.chain(&self.height.to_le_bytes()) //TODO decide if the height should remain in this signature
.chain(PublicKey::from_secret_key(&self.script_private_key).as_bytes())
.chain(commitment.as_bytes())
.result()
.to_vec();

let script_signature = Signature::sign(self.script_private_key.clone(), script_nonce, &e)
.map_err(|_| TransactionError::InvalidSignatureError("Generating script signature".to_string()))?;
let script_signature = ComSignature::sign(
self.value.into(),
self.script_private_key.clone() + self.spending_key.clone(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are script_private_key and spending_key always generated by the same individual? If not, we've got to be very careful we're not introducing any opportunities here for rogue key attacks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are sticking with MW rules, yes. This is specifically to force the user who generated both to be the same individual, as there are attacks that someone not being the spender be able to change the data here.

For example:
Alice spends her UTXO with a NOP script on it, which allows any input on it. Basically mimicking vanilla MW behavior. Bon can then take Alice's input and change the input data and thus change the script_private_key. If Bob cab changes this, he can the spending conditions on his utxo. This is discussed a bit more here in detail: https://github.com/tari-project/tari/blob/tari-script/RFC/src/RFC-0201_TariScript.md#script-offset-security

script_nonce_a,
script_nonce_b,
&e,
factory,
)
.map_err(|_| TransactionError::InvalidSignatureError("Generating script signature".to_string()))?;

Ok(TransactionInput {
features: self.features.clone(),
Expand Down Expand Up @@ -373,7 +383,7 @@ pub struct TransactionInput {
/// The block height that the UTXO was mined
pub height: u64,
/// A signature with k_s, signing the script, input data, and mined height
pub script_signature: Signature,
pub script_signature: ComSignature,
/// The offset pubkey, K_O
pub script_offset_public_key: PublicKey,
}
Expand All @@ -387,7 +397,7 @@ impl TransactionInput {
script: TariScript,
input_data: ExecutionStack,
height: u64,
script_signature: Signature,
script_signature: ComSignature,
script_offset_public_key: PublicKey,
) -> TransactionInput {
TransactionInput {
Expand Down Expand Up @@ -428,16 +438,24 @@ impl TransactionInput {
}
}

pub fn validate_script_signature(&self, key: &PublicKey) -> Result<(), TransactionError> {
let r = self.script_signature.get_public_nonce();
let m = HashDigest::new()
.chain(r.as_bytes())
.chain(self.script.as_bytes())
.chain(self.input_data.as_bytes())
//.chain(self.height.to_le_bytes()) //TODO decide if the height should remain in the script signature
pub fn validate_script_signature(
&self,
public_script_key: &PublicKey,
factory: &CommitmentFactory,
) -> Result<(), TransactionError> {
let nonce_commitment = self.script_signature.public_nonce();
let m = Challenge::new()
.chain(nonce_commitment.as_bytes())
.chain(self.script.as_bytes().as_slice())
.chain(self.input_data.as_bytes().as_slice())
.chain(public_script_key.as_bytes())
.chain(self.commitment.as_bytes())
.result()
.to_vec();
if self.script_signature.verify_challenge(key, &m) {
if self
.script_signature
.verify_challenge(&(&self.commitment + public_script_key), &m, factory)
{
Ok(())
} else {
Err(TransactionError::InvalidSignatureError(
Expand All @@ -448,9 +466,9 @@ impl TransactionInput {

/// This will run the script and verify the script signature. If its valid, it will return the resulting public key
/// from the script.
pub fn run_and_verify_script(&self) -> Result<PublicKey, TransactionError> {
pub fn run_and_verify_script(&self, factory: &CommitmentFactory) -> Result<PublicKey, TransactionError> {
let key = self.run_script()?;
self.validate_script_signature(&key)?;
self.validate_script_signature(&key, factory)?;
Ok(key)
}
}
Expand Down Expand Up @@ -1316,7 +1334,7 @@ mod test {
let script = TariScript::default();
let input_data = ExecutionStack::default();
let height = 0;
let script_signature = Signature::default();
let script_signature = ComSignature::default();
let offset_pub_key = PublicKey::default();
let mut input = TransactionInput::new(
OutputFeatures::default(),
Expand Down
3 changes: 3 additions & 0 deletions base_layer/core/src/transactions/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use tari_crypto::{
ristretto::{
dalek_range_proof::DalekRangeProofService,
pedersen::{PedersenCommitment, PedersenCommitmentFactory},
RistrettoComSig,
RistrettoPublicKey,
RistrettoSchnorr,
RistrettoSecretKey,
Expand All @@ -36,6 +37,8 @@ use tari_crypto::{
/// Define the explicit Signature implementation for the Tari base layer. A different signature scheme can be
/// employed by redefining this type.
pub type Signature = RistrettoSchnorr;
/// Define the explicit Commitment Signature implementation for the Tari base layer.
pub type ComSignature = RistrettoComSig;

/// Define the explicit Commitment implementation for the Tari base layer.
pub type Commitment = PedersenCommitment;
Expand Down
Loading