Skip to content

Commit

Permalink
Create the signature facilities for arbitrary messages
Browse files Browse the repository at this point in the history
  • Loading branch information
TheQuantumPhysicist committed Jan 30, 2024
1 parent 5aac91b commit d6f20d6
Show file tree
Hide file tree
Showing 20 changed files with 294 additions and 162 deletions.
4 changes: 2 additions & 2 deletions chainstate/test-suite/src/tests/delegation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ fn check_signature_on_spend_share(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -869,7 +869,7 @@ fn check_signature_on_spend_share(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down
26 changes: 13 additions & 13 deletions chainstate/test-suite/src/tests/fungible_tokens_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3278,7 +3278,7 @@ fn check_signature_on_mint(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -3317,7 +3317,7 @@ fn check_signature_on_mint(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down Expand Up @@ -3463,7 +3463,7 @@ fn check_signature_on_unmint(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -3513,7 +3513,7 @@ fn check_signature_on_unmint(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down Expand Up @@ -3603,7 +3603,7 @@ fn check_signature_on_lock_supply(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -3642,7 +3642,7 @@ fn check_signature_on_lock_supply(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down Expand Up @@ -4491,7 +4491,7 @@ fn check_signature_on_freeze_unfreeze(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -4534,7 +4534,7 @@ fn check_signature_on_freeze_unfreeze(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down Expand Up @@ -4572,7 +4572,7 @@ fn check_signature_on_freeze_unfreeze(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -4600,7 +4600,7 @@ fn check_signature_on_freeze_unfreeze(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down Expand Up @@ -4681,7 +4681,7 @@ fn check_signature_on_change_authority(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureNotFound
common::chain::signature::DestinationSigError::SignatureNotFound
)
))
);
Expand Down Expand Up @@ -4720,7 +4720,7 @@ fn check_signature_on_change_authority(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down Expand Up @@ -4798,7 +4798,7 @@ fn check_signature_on_change_authority(#[case] seed: Seed) {
result.unwrap_err(),
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::SignatureVerificationFailed
common::chain::signature::DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down
4 changes: 2 additions & 2 deletions chainstate/test-suite/src/tests/signature_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ fn signed_classical_multisig_tx_missing_sigs(#[case] seed: Seed) {
chainstate::ChainstateError::ProcessBlockError(
chainstate::BlockError::StateUpdateFailed(
chainstate::ConnectTransactionError::SignatureVerificationFailed(
common::chain::signature::TransactionSigError::IncompleteClassicalMultisigSignature
common::chain::signature::DestinationSigError::IncompleteClassicalMultisigSignature
)
)
)
Expand Down Expand Up @@ -550,7 +550,7 @@ fn try_to_spend_with_no_signature_on_mainnet(#[case] seed: Seed) {
chainstate::ChainstateError::ProcessBlockError(
chainstate::BlockError::StateUpdateFailed(
chainstate::ConnectTransactionError::SignatureVerificationFailed(
chain::signature::TransactionSigError::SignatureNotFound
chain::signature::DestinationSigError::SignatureNotFound
)
)
)
Expand Down
4 changes: 2 additions & 2 deletions chainstate/test-suite/src/tests/stake_pool_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use common::{
output_value::OutputValue,
signature::{
inputsig::{standard_signature::StandardInputSignature, InputWitness},
TransactionSigError,
DestinationSigError,
},
stakelock::StakePoolData,
timelock::OutputTimeLock,
Expand Down Expand Up @@ -744,7 +744,7 @@ fn decommission_from_stake_pool_with_staker_key(#[case] seed: Seed) {
result,
ChainstateError::ProcessBlockError(BlockError::StateUpdateFailed(
ConnectTransactionError::SignatureVerificationFailed(
TransactionSigError::SignatureVerificationFailed
DestinationSigError::SignatureVerificationFailed
)
))
);
Expand Down
4 changes: 2 additions & 2 deletions chainstate/tx-verifier/src/transaction_verifier/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use chainstate_types::GetAncestorError;
use common::{
chain::{
block::{Block, GenBlock},
signature::TransactionSigError,
signature::DestinationSigError,
tokens::TokenId,
AccountNonce, AccountType, DelegationId, OutPointSourceId, PoolId, TokenIssuanceVersion,
Transaction, UtxoOutPoint,
Expand Down Expand Up @@ -65,7 +65,7 @@ pub enum ConnectTransactionError {
#[error("Fee calculation failed (total inputs: `{0:?}` vs total outputs `{1:?}`")]
TxFeeTotalCalcFailed(Amount, Amount),
#[error("Signature verification failed in transaction: {0}")]
SignatureVerificationFailed(#[from] TransactionSigError),
SignatureVerificationFailed(#[from] DestinationSigError),
#[error("Error while calculating block height; possibly an overflow")]
BlockHeightArithmeticError,
#[error("Error while calculating timestamps; possibly an overflow")]
Expand Down
131 changes: 131 additions & 0 deletions common/src/chain/transaction/signature/inputsig/arbitrary_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) 2023 RBB S.r.l
// [email protected]
// SPDX-License-Identifier: MIT
// Licensed under the MIT License;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use serialization::Encode;

use crate::{
chain::{signature::DestinationSigError, ChainConfig, Destination},
primitives::{id::default_hash, H256},
};

use super::{
authorize_pubkey_spend::{
sign_pubkey_spending, verify_public_key_spending, AuthorizedPublicKeySpend,
},
authorize_pubkeyhash_spend::{
sign_address_spending, verify_address_spending, AuthorizedPublicKeyHashSpend,
},
classical_multisig::authorize_classical_multisig::{
verify_classical_multisig_spending, AuthorizedClassicalMultisigSpend,
},
};

#[derive(Debug, thiserror::Error)]
pub enum SignArbitraryMessageError {
#[error("Destination signature error: {0}")]
DestinationSigError(#[from] DestinationSigError),
#[error("AnyoneCanSpend should not use standard signatures, so producing a signature for it is not possible")]
AttemptedToProduceSignatureForAnyoneCanSpend,
#[error("Classical multisig signature attempted in uni-party function")]
AttemptedToProduceClassicalMultisigSignatureInUnipartySignatureCode,
#[error("Unsupported yet!")]
Unsupported,
}

pub struct SignedArbitraryMessage {
raw_signature: Vec<u8>,
}

fn produce_message_challenge(message: Vec<u8>) -> H256 {
let hashed_message = default_hash(&message);

// Concatenate the message with its hash to prevent abusing signatures to trick users into signing transactions
let message = message
.into_iter()
.chain(hashed_message.as_bytes().to_vec())
.collect::<Vec<_>>();

// Harden it further by hashing that twice. Now it's impossible to make this useful for a transaction
default_hash(default_hash(message))
}

impl SignedArbitraryMessage {
pub fn from_data(raw_signature: Vec<u8>) -> Self {
Self { raw_signature }
}

pub fn verify_signature(
&self,
chain_config: &ChainConfig,
outpoint_destination: &Destination,
challenge: &H256,
) -> Result<(), DestinationSigError> {
match outpoint_destination {
Destination::Address(addr) => {
let sig_components = AuthorizedPublicKeyHashSpend::from_data(&self.raw_signature)?;
verify_address_spending(addr, &sig_components, challenge)?
}
Destination::PublicKey(pubkey) => {
let sig_components = AuthorizedPublicKeySpend::from_data(&self.raw_signature)?;
verify_public_key_spending(pubkey, &sig_components, challenge)?
}
Destination::ScriptHash(_) => return Err(DestinationSigError::Unsupported),
Destination::AnyoneCanSpend => {
// AnyoneCanSpend must use InputWitness::NoSignature, so this is unreachable
return Err(
DestinationSigError::AttemptedToVerifyStandardSignatureForAnyoneCanSpend,
);
}
Destination::ClassicMultisig(h) => {
let sig_components =
AuthorizedClassicalMultisigSpend::from_data(&self.raw_signature)?;
verify_classical_multisig_spending(chain_config, h, &sig_components, challenge)?
}
}
Ok(())
}

pub fn produce_uniparty_signature(
private_key: &crypto::key::PrivateKey,
outpoint_destination: Destination,
message: Vec<u8>,
) -> Result<Self, SignArbitraryMessageError> {
let challenge = produce_message_challenge(message);
let signature =
match outpoint_destination {
Destination::Address(ref addr) => {
let sig = sign_address_spending(private_key, addr, &challenge)?;
sig.encode()
}
Destination::PublicKey(ref pubkey) => {
let sig = sign_pubkey_spending(private_key, pubkey, &challenge)?;
sig.encode()
}
Destination::ScriptHash(_) => return Err(SignArbitraryMessageError::Unsupported),

Destination::AnyoneCanSpend => {
// AnyoneCanSpend must use InputWitness::NoSignature, so this is unreachable
return Err(SignArbitraryMessageError::AttemptedToProduceSignatureForAnyoneCanSpend);
}
Destination::ClassicMultisig(_) => return Err(
// This function doesn't support this kind of signature
SignArbitraryMessageError::AttemptedToProduceClassicalMultisigSignatureInUnipartySignatureCode,
),
};
Ok(Self {
raw_signature: signature,
})
}
}
Loading

0 comments on commit d6f20d6

Please sign in to comment.