Sign kernel features (#2104)

* include kernel features in msg being signed
hash the msg before signing it (for consistent 32 bytes)

* rustfmt

* fix various tests

* no HF for this (mainnet only)
This commit is contained in:
Antioch Peverell 2018-12-12 09:19:36 +00:00 committed by GitHub
parent ec1713320b
commit a0b9aa5967
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 81 additions and 59 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ grin.log
wallet.seed wallet.seed
test_output test_output
wallet_data wallet_data
wallet/db

View file

@ -34,7 +34,7 @@ pub const ZERO_HASH: Hash = Hash([0; 32]);
/// A hash to uniquely (or close enough) identify one of the main blockchain /// A hash to uniquely (or close enough) identify one of the main blockchain
/// constructs. Used pervasively for blocks, transactions and outputs. /// constructs. Used pervasively for blocks, transactions and outputs.
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
pub struct Hash(pub [u8; 32]); pub struct Hash([u8; 32]);
impl fmt::Debug for Hash { impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -71,6 +71,11 @@ impl Hash {
self.0.to_vec() self.0.to_vec()
} }
/// Returns a byte slice of the hash contents.
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
/// Convert a hash to hex string format. /// Convert a hash to hex string format.
pub fn to_hex(&self) -> String { pub fn to_hex(&self) -> String {
util::to_hex(self.to_vec()) util::to_hex(self.to_vec())

View file

@ -51,8 +51,8 @@ impl<H: Hashed> ShortIdentifiable for H {
use std::hash::Hasher; use std::hash::Hasher;
// extract k0/k1 from the block_hash // extract k0/k1 from the block_hash
let k0 = LittleEndian::read_u64(&hash_with_nonce.0[0..8]); let k0 = LittleEndian::read_u64(&hash_with_nonce.as_bytes()[0..8]);
let k1 = LittleEndian::read_u64(&hash_with_nonce.0[8..16]); let k1 = LittleEndian::read_u64(&hash_with_nonce.as_bytes()[8..16]);
// initialize a siphasher24 with k0/k1 // initialize a siphasher24 with k0/k1
let mut sip_hasher = SipHasher24::new_with_keys(k0, k1); let mut sip_hasher = SipHasher24::new_with_keys(k0, k1);

View file

@ -18,6 +18,7 @@ use crate::consensus;
use crate::core::hash::Hashed; use crate::core::hash::Hashed;
use crate::core::verifier_cache::VerifierCache; use crate::core::verifier_cache::VerifierCache;
use crate::core::{committed, Committed}; use crate::core::{committed, Committed};
use crate::global;
use crate::keychain::{self, BlindingFactor}; use crate::keychain::{self, BlindingFactor};
use crate::ser::{ use crate::ser::{
self, read_multi, FixedLength, PMMRable, Readable, Reader, VerifySortedAndUnique, Writeable, self, read_multi, FixedLength, PMMRable, Readable, Reader, VerifySortedAndUnique, Writeable,
@ -46,6 +47,13 @@ bitflags! {
} }
} }
impl Writeable for KernelFeatures {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u8(self.bits())?;
Ok(())
}
}
/// Errors thrown by Transaction validation /// Errors thrown by Transaction validation
#[derive(Clone, Eq, Debug, PartialEq)] #[derive(Clone, Eq, Debug, PartialEq)]
pub enum Error { pub enum Error {
@ -163,13 +171,9 @@ impl ::std::hash::Hash for TxKernel {
impl Writeable for TxKernel { impl Writeable for TxKernel {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> { fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
ser_multiwrite!( self.features.write(writer)?;
writer, ser_multiwrite!(writer, [write_u64, self.fee], [write_u64, self.lock_height]);
[write_u8, self.features.bits()], self.excess.write(writer)?;
[write_u64, self.fee],
[write_u64, self.lock_height],
[write_fixed_bytes, &self.excess]
);
self.excess_sig.write(writer)?; self.excess_sig.write(writer)?;
Ok(()) Ok(())
} }
@ -207,7 +211,7 @@ impl TxKernel {
/// The msg signed as part of the tx kernel. /// The msg signed as part of the tx kernel.
/// Consists of the fee and the lock_height. /// Consists of the fee and the lock_height.
pub fn msg_to_sign(&self) -> Result<secp::Message, Error> { pub fn msg_to_sign(&self) -> Result<secp::Message, Error> {
let msg = kernel_sig_msg(self.fee, self.lock_height)?; let msg = kernel_sig_msg(self.fee, self.lock_height, self.features)?;
Ok(msg) Ok(msg)
} }
@ -272,29 +276,14 @@ pub struct TxKernelEntry {
impl Writeable for TxKernelEntry { impl Writeable for TxKernelEntry {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> { fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
ser_multiwrite!( self.kernel.write(writer)?;
writer,
[write_u8, self.kernel.features.bits()],
[write_u64, self.kernel.fee],
[write_u64, self.kernel.lock_height],
[write_fixed_bytes, &self.kernel.excess]
);
self.kernel.excess_sig.write(writer)?;
Ok(()) Ok(())
} }
} }
impl Readable for TxKernelEntry { impl Readable for TxKernelEntry {
fn read(reader: &mut dyn Reader) -> Result<TxKernelEntry, ser::Error> { fn read(reader: &mut Reader) -> Result<TxKernelEntry, ser::Error> {
let features = let kernel = TxKernel::read(reader)?;
KernelFeatures::from_bits(reader.read_u8()?).ok_or(ser::Error::CorruptedData)?;
let kernel = TxKernel {
features: features,
fee: reader.read_u64()?,
lock_height: reader.read_u64()?,
excess: Commitment::read(reader)?,
excess_sig: secp::Signature::read(reader)?,
};
Ok(TxKernelEntry { kernel }) Ok(TxKernelEntry { kernel })
} }
} }
@ -1293,12 +1282,28 @@ impl From<Output> for OutputIdentifier {
} }
} }
/// Construct msg from tx fee and lock_height. /// Construct msg from tx fee, lock_height and kernel features.
pub fn kernel_sig_msg(fee: u64, lock_height: u64) -> Result<secp::Message, Error> { /// In testnet4 we did not include the kernel features in the message being signed.
/// In mainnet we changed this to include features and we hash (fee || lock_height || features)
/// to produce a 32 byte message to sign.
///
/// testnet4: msg = (fee || lock_height)
/// mainnet: msg = hash(fee || lock_height || features)
///
pub fn kernel_sig_msg(
fee: u64,
lock_height: u64,
features: KernelFeatures,
) -> Result<secp::Message, Error> {
let msg = if global::is_mainnet() {
let hash = (fee, lock_height, features).hash();
secp::Message::from_slice(&hash.as_bytes())?
} else {
let mut bytes = [0; 32]; let mut bytes = [0; 32];
BigEndian::write_u64(&mut bytes[16..24], fee); BigEndian::write_u64(&mut bytes[16..24], fee);
BigEndian::write_u64(&mut bytes[24..], lock_height); BigEndian::write_u64(&mut bytes[24..], lock_height);
let msg = secp::Message::from_slice(&bytes)?; secp::Message::from_slice(&bytes)?
};
Ok(msg) Ok(msg)
} }

View file

@ -229,7 +229,7 @@ pub fn verify_partial_sig(
/// use util::secp::key::{PublicKey, SecretKey}; /// use util::secp::key::{PublicKey, SecretKey};
/// use util::secp::{ContextFlag, Secp256k1}; /// use util::secp::{ContextFlag, Secp256k1};
/// use core::libtx::{aggsig, proof}; /// use core::libtx::{aggsig, proof};
/// use core::core::transaction::kernel_sig_msg; /// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
/// use core::core::{Output, OutputFeatures}; /// use core::core::{Output, OutputFeatures};
/// use keychain::{Keychain, ExtKeychain}; /// use keychain::{Keychain, ExtKeychain};
/// ///
@ -248,7 +248,7 @@ pub fn verify_partial_sig(
/// let height = 20; /// let height = 20;
/// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let over_commit = secp.commit_value(reward(fees)).unwrap();
/// let out_commit = output.commitment(); /// let out_commit = output.commitment();
/// let msg = kernel_sig_msg(0, height).unwrap(); /// let msg = kernel_sig_msg(0, height, KernelFeatures::DEFAULT_KERNEL).unwrap();
/// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap();
/// let pubkey = excess.to_pubkey(&secp).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap(); /// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap();
@ -301,7 +301,7 @@ where
/// use core::libtx::{aggsig, proof}; /// use core::libtx::{aggsig, proof};
/// use util::secp::key::{PublicKey, SecretKey}; /// use util::secp::key::{PublicKey, SecretKey};
/// use util::secp::{ContextFlag, Secp256k1}; /// use util::secp::{ContextFlag, Secp256k1};
/// use core::core::transaction::kernel_sig_msg; /// use core::core::transaction::{kernel_sig_msg, KernelFeatures};
/// use core::core::{Output, OutputFeatures}; /// use core::core::{Output, OutputFeatures};
/// use keychain::{Keychain, ExtKeychain}; /// use keychain::{Keychain, ExtKeychain};
/// ///
@ -321,7 +321,7 @@ where
/// let height = 20; /// let height = 20;
/// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let over_commit = secp.commit_value(reward(fees)).unwrap();
/// let out_commit = output.commitment(); /// let out_commit = output.commitment();
/// let msg = kernel_sig_msg(0, height).unwrap(); /// let msg = kernel_sig_msg(0, height, KernelFeatures::DEFAULT_KERNEL).unwrap();
/// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap();
/// let pubkey = excess.to_pubkey(&secp).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap();
/// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap(); /// let sig = aggsig::sign_from_key_id(&secp, &keychain, &msg, &key_id, Some(&pubkey)).unwrap();

View file

@ -58,7 +58,7 @@ where
// not the lock_height of the tx (there is no tx for a coinbase output). // not the lock_height of the tx (there is no tx for a coinbase output).
// This output will not be spendable earlier than lock_height (and we sign this // This output will not be spendable earlier than lock_height (and we sign this
// here). // here).
let msg = kernel_sig_msg(0, height)?; let msg = kernel_sig_msg(0, height, KernelFeatures::COINBASE_KERNEL)?;
let sig = aggsig::sign_from_key_id(&secp, keychain, &msg, &key_id, Some(&pubkey))?; let sig = aggsig::sign_from_key_id(&secp, keychain, &msg, &key_id, Some(&pubkey))?;
let proof = TxKernel { let proof = TxKernel {

View file

@ -17,9 +17,9 @@
use crate::blake2::blake2b::blake2b; use crate::blake2::blake2b::blake2b;
use crate::core::committed::Committed; use crate::core::committed::Committed;
use crate::core::transaction::kernel_sig_msg; use crate::core::transaction::{kernel_sig_msg, KernelFeatures, Transaction};
use crate::core::verifier_cache::LruVerifierCache; use crate::core::verifier_cache::LruVerifierCache;
use crate::core::{amount_to_hr_string, Transaction}; use crate::core::amount_to_hr_string;
use crate::keychain::{BlindSum, BlindingFactor, Keychain}; use crate::keychain::{BlindSum, BlindingFactor, Keychain};
use crate::libtx::error::{Error, ErrorKind}; use crate::libtx::error::{Error, ErrorKind};
use crate::libtx::{aggsig, build, tx_fee}; use crate::libtx::{aggsig, build, tx_fee};
@ -159,7 +159,10 @@ impl Slate {
// This is the msg that we will sign as part of the tx kernel. // This is the msg that we will sign as part of the tx kernel.
// Currently includes the fee and the lock_height. // Currently includes the fee and the lock_height.
fn msg_to_sign(&self) -> Result<secp::Message, Error> { fn msg_to_sign(&self) -> Result<secp::Message, Error> {
let msg = kernel_sig_msg(self.fee, self.lock_height)?; // Currently we only support interactively creating a tx with a "default" kernel.
let features = KernelFeatures::DEFAULT_KERNEL;
let msg = kernel_sig_msg(self.fee, self.lock_height, features)?;
Ok(msg) Ok(msg)
} }

View file

@ -18,6 +18,7 @@ use crate::core::consensus::{BLOCK_OUTPUT_WEIGHT, MAX_BLOCK_WEIGHT};
use crate::core::core::block::Error; use crate::core::core::block::Error;
use crate::core::core::hash::Hashed; use crate::core::core::hash::Hashed;
use crate::core::core::id::ShortIdentifiable; use crate::core::core::id::ShortIdentifiable;
use crate::core::core::transaction;
use crate::core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use crate::core::core::verifier_cache::{LruVerifierCache, VerifierCache};
use crate::core::core::Committed; use crate::core::core::Committed;
use crate::core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures}; use crate::core::core::{Block, BlockHeader, CompactBlock, KernelFeatures, OutputFeatures};
@ -193,14 +194,17 @@ fn remove_coinbase_kernel_flag() {
.features .features
.remove(KernelFeatures::COINBASE_KERNEL); .remove(KernelFeatures::COINBASE_KERNEL);
// Flipping the coinbase flag results in kernels not summing correctly.
assert_eq!( assert_eq!(
b.verify_coinbase(), b.verify_coinbase(),
Err(Error::Secp(secp::Error::IncorrectCommitSum)) Err(Error::Secp(secp::Error::IncorrectCommitSum))
); );
// Also results in the block no longer validating correctly
// because the message being signed on each tx kernel includes the kernel features.
assert_eq!( assert_eq!(
b.validate(&BlindingFactor::zero(), verifier_cache()), b.validate(&BlindingFactor::zero(), verifier_cache()),
Err(Error::Secp(secp::Error::IncorrectCommitSum)) Err(Error::Transaction(transaction::Error::IncorrectSignature))
); );
} }

View file

@ -12,7 +12,7 @@
// limitations under the License. // limitations under the License.
//! core::libtx specific tests //! core::libtx specific tests
use self::core::core::transaction::kernel_sig_msg; use self::core::core::transaction;
use self::core::libtx::{aggsig, proof}; use self::core::libtx::{aggsig, proof};
use self::keychain::{BlindSum, BlindingFactor, ExtKeychain, Keychain}; use self::keychain::{BlindSum, BlindingFactor, ExtKeychain, Keychain};
use self::util::secp; use self::util::secp;
@ -25,6 +25,10 @@ use grin_util as util;
use grin_wallet as wallet; use grin_wallet as wallet;
use rand::thread_rng; use rand::thread_rng;
fn kernel_sig_msg() -> secp::Message {
transaction::kernel_sig_msg(0, 0, transaction::KernelFeatures::DEFAULT_KERNEL).unwrap()
}
#[test] #[test]
fn aggsig_sender_receiver_interaction() { fn aggsig_sender_receiver_interaction() {
let sender_keychain = ExtKeychain::from_random_seed().unwrap(); let sender_keychain = ExtKeychain::from_random_seed().unwrap();
@ -105,7 +109,7 @@ fn aggsig_sender_receiver_interaction() {
) )
.unwrap(); .unwrap();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_part = aggsig::calculate_partial_sig( let sig_part = aggsig::calculate_partial_sig(
&keychain.secp(), &keychain.secp(),
&rx_cx.sec_key, &rx_cx.sec_key,
@ -122,7 +126,7 @@ fn aggsig_sender_receiver_interaction() {
// received in the response back from the receiver // received in the response back from the receiver
{ {
let keychain = sender_keychain.clone(); let keychain = sender_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_verifies = aggsig::verify_partial_sig( let sig_verifies = aggsig::verify_partial_sig(
&keychain.secp(), &keychain.secp(),
&rx_sig_part, &rx_sig_part,
@ -137,7 +141,7 @@ fn aggsig_sender_receiver_interaction() {
// now sender signs with their key // now sender signs with their key
let sender_sig_part = { let sender_sig_part = {
let keychain = sender_keychain.clone(); let keychain = sender_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_part = aggsig::calculate_partial_sig( let sig_part = aggsig::calculate_partial_sig(
&keychain.secp(), &keychain.secp(),
&s_cx.sec_key, &s_cx.sec_key,
@ -154,7 +158,7 @@ fn aggsig_sender_receiver_interaction() {
// received by the sender // received by the sender
{ {
let keychain = receiver_keychain.clone(); let keychain = receiver_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_verifies = aggsig::verify_partial_sig( let sig_verifies = aggsig::verify_partial_sig(
&keychain.secp(), &keychain.secp(),
&sender_sig_part, &sender_sig_part,
@ -170,7 +174,7 @@ fn aggsig_sender_receiver_interaction() {
let (final_sig, final_pubkey) = { let (final_sig, final_pubkey) = {
let keychain = receiver_keychain.clone(); let keychain = receiver_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let our_sig_part = aggsig::calculate_partial_sig( let our_sig_part = aggsig::calculate_partial_sig(
&keychain.secp(), &keychain.secp(),
&rx_cx.sec_key, &rx_cx.sec_key,
@ -205,7 +209,7 @@ fn aggsig_sender_receiver_interaction() {
// Receiver checks the final signature verifies // Receiver checks the final signature verifies
{ {
let keychain = receiver_keychain.clone(); let keychain = receiver_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
// Receiver check the final signature verifies // Receiver check the final signature verifies
let sig_verifies = aggsig::verify_completed_sig( let sig_verifies = aggsig::verify_completed_sig(
@ -221,7 +225,7 @@ fn aggsig_sender_receiver_interaction() {
// Check we can verify the sig using the kernel excess // Check we can verify the sig using the kernel excess
{ {
let keychain = ExtKeychain::from_random_seed().unwrap(); let keychain = ExtKeychain::from_random_seed().unwrap();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_verifies = let sig_verifies =
aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess); aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess);
@ -321,7 +325,7 @@ fn aggsig_sender_receiver_interaction_offset() {
) )
.unwrap(); .unwrap();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_part = aggsig::calculate_partial_sig( let sig_part = aggsig::calculate_partial_sig(
&keychain.secp(), &keychain.secp(),
&rx_cx.sec_key, &rx_cx.sec_key,
@ -338,7 +342,7 @@ fn aggsig_sender_receiver_interaction_offset() {
// received in the response back from the receiver // received in the response back from the receiver
{ {
let keychain = sender_keychain.clone(); let keychain = sender_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_verifies = aggsig::verify_partial_sig( let sig_verifies = aggsig::verify_partial_sig(
&keychain.secp(), &keychain.secp(),
&sig_part, &sig_part,
@ -353,7 +357,7 @@ fn aggsig_sender_receiver_interaction_offset() {
// now sender signs with their key // now sender signs with their key
let sender_sig_part = { let sender_sig_part = {
let keychain = sender_keychain.clone(); let keychain = sender_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_part = aggsig::calculate_partial_sig( let sig_part = aggsig::calculate_partial_sig(
&keychain.secp(), &keychain.secp(),
&s_cx.sec_key, &s_cx.sec_key,
@ -370,7 +374,7 @@ fn aggsig_sender_receiver_interaction_offset() {
// received by the sender // received by the sender
{ {
let keychain = receiver_keychain.clone(); let keychain = receiver_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_verifies = aggsig::verify_partial_sig( let sig_verifies = aggsig::verify_partial_sig(
&keychain.secp(), &keychain.secp(),
&sender_sig_part, &sender_sig_part,
@ -385,7 +389,7 @@ fn aggsig_sender_receiver_interaction_offset() {
// Receiver now builds final signature from sender and receiver parts // Receiver now builds final signature from sender and receiver parts
let (final_sig, final_pubkey) = { let (final_sig, final_pubkey) = {
let keychain = receiver_keychain.clone(); let keychain = receiver_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let our_sig_part = aggsig::calculate_partial_sig( let our_sig_part = aggsig::calculate_partial_sig(
&keychain.secp(), &keychain.secp(),
&rx_cx.sec_key, &rx_cx.sec_key,
@ -420,7 +424,7 @@ fn aggsig_sender_receiver_interaction_offset() {
// Receiver checks the final signature verifies // Receiver checks the final signature verifies
{ {
let keychain = receiver_keychain.clone(); let keychain = receiver_keychain.clone();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
// Receiver check the final signature verifies // Receiver check the final signature verifies
let sig_verifies = aggsig::verify_completed_sig( let sig_verifies = aggsig::verify_completed_sig(
@ -436,7 +440,7 @@ fn aggsig_sender_receiver_interaction_offset() {
// Check we can verify the sig using the kernel excess // Check we can verify the sig using the kernel excess
{ {
let keychain = ExtKeychain::from_random_seed().unwrap(); let keychain = ExtKeychain::from_random_seed().unwrap();
let msg = kernel_sig_msg(0, 0).unwrap(); let msg = kernel_sig_msg();
let sig_verifies = let sig_verifies =
aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess); aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess);