diff --git a/frame/nfts/src/common_functions.rs b/frame/nfts/src/common_functions.rs index 1ef6591db02b4..a3486edec23cb 100644 --- a/frame/nfts/src/common_functions.rs +++ b/frame/nfts/src/common_functions.rs @@ -18,6 +18,7 @@ //! Various pieces of common functionality. use crate::*; +use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { /// Get the owner of the item, if the item exists. @@ -30,6 +31,30 @@ impl, I: 'static> Pallet { Collection::::get(collection).map(|i| i.owner) } + /// Validate the `data` was signed by `signer` and the `signature` is correct. + pub fn validate_signature( + data: &Vec, + signature: &T::OffchainSignature, + signer: &T::AccountId, + ) -> DispatchResult { + if signature.verify(&**data, &signer) { + return Ok(()) + } + + // NOTE: for security reasons modern UIs implicitly wrap the data requested to sign into + // , that's why we support both wrapped and raw versions. + let prefix = b""; + let suffix = b""; + let mut wrapped: Vec = Vec::with_capacity(data.len() + prefix.len() + suffix.len()); + wrapped.extend(prefix); + wrapped.extend(data); + wrapped.extend(suffix); + + ensure!(signature.verify(&*wrapped, &signer), Error::::WrongSignature); + + Ok(()) + } + #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn set_next_id(id: T::CollectionId) { NextCollectionId::::set(Some(id)); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index f4d0e41593476..4796819df6d2c 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -50,7 +50,7 @@ use frame_support::traits::{ }; use frame_system::Config as SystemConfig; use sp_runtime::{ - traits::{Saturating, StaticLookup, Zero}, + traits::{IdentifyAccount, Saturating, StaticLookup, Verify, Zero}, RuntimeDebug, }; use sp_std::prelude::*; @@ -69,7 +69,6 @@ pub mod pallet { use super::*; use frame_support::{pallet_prelude::*, traits::ExistenceRequirement}; use frame_system::pallet_prelude::*; - use sp_runtime::traits::{IdentifyAccount, Verify}; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -1841,8 +1840,7 @@ pub mod pallet { signer: T::AccountId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let msg = Encode::encode(&mint_data); - ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); + Self::validate_signature(&Encode::encode(&mint_data), &signature, &signer)?; Self::do_mint_pre_signed(origin, mint_data, signer) } @@ -1868,8 +1866,7 @@ pub mod pallet { signer: T::AccountId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - let msg = Encode::encode(&data); - ensure!(signature.verify(&*msg, &signer), Error::::WrongSignature); + Self::validate_signature(&Encode::encode(&data), &signature, &signer)?; Self::do_set_attributes_pre_signed(origin, data, signer) } } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index d36f8b54e2b91..4ab12f0506056 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -3140,6 +3140,34 @@ fn add_remove_item_attributes_approval_should_work() { }) } +#[test] +fn validate_signature() { + new_test_ext().execute_with(|| { + let user_1_pair = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap(); + let user_1_signer = MultiSigner::Sr25519(user_1_pair.public()); + let user_1 = user_1_signer.clone().into_account(); + let mint_data: PreSignedMint = PreSignedMint { + collection: 0, + item: 0, + attributes: vec![], + metadata: vec![], + only_account: None, + deadline: 100000, + }; + let encoded_data = Encode::encode(&mint_data); + let signature = MultiSignature::Sr25519(user_1_pair.sign(&encoded_data)); + assert_ok!(Nfts::validate_signature(&encoded_data, &signature, &user_1)); + + let mut wrapped_data: Vec = Vec::new(); + wrapped_data.extend(b""); + wrapped_data.extend(&encoded_data); + wrapped_data.extend(b""); + + let signature = MultiSignature::Sr25519(user_1_pair.sign(&wrapped_data)); + assert_ok!(Nfts::validate_signature(&encoded_data, &signature, &user_1)); + }) +} + #[test] fn pre_signed_mints_should_work() { new_test_ext().execute_with(|| {