// Copyright 2017 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // 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 rand::{thread_rng, Rng}; use std::collections::HashMap; use std::sync::{Arc, RwLock}; use util::secp; use util::secp::{Message, Secp256k1, Signature}; use util::secp::key::{SecretKey, PublicKey}; use util::secp::pedersen::{Commitment, ProofMessage, ProofInfo, RangeProof}; use util::secp::aggsig; use util::logger::LOGGER; use util::kernel_sig_msg; use blake2; use uuid::Uuid; use blind::{BlindSum, BlindingFactor}; use extkey::{self, Identifier}; #[derive(PartialEq, Eq, Clone, Debug)] pub enum Error { ExtendedKey(extkey::Error), Secp(secp::Error), KeyDerivation(String), Transaction(String), } impl From for Error { fn from(e: secp::Error) -> Error { Error::Secp(e) } } impl From for Error { fn from(e: extkey::Error) -> Error { Error::ExtendedKey(e) } } /// Holds internal information about an aggsig operation #[derive(Clone, Debug)] pub struct AggSigTxContext { // Secret key (of which public is shared) pub sec_key: SecretKey, // Secret nonce (of which public is shared) // (basically a SecretKey) pub sec_nonce: SecretKey, // If I'm the recipient, store my outputs between invocations (that I need to sum) pub output_ids: Vec, } #[derive(Clone, Debug)] pub struct Keychain { secp: Secp256k1, extkey: extkey::ExtendedKey, pub aggsig_contexts: Arc>>>, key_overrides: HashMap, key_derivation_cache: Arc>>, } impl Keychain { pub fn root_key_id(&self) -> Identifier { self.extkey.root_key_id.clone() } // For tests and burn only, associate a key identifier with a known secret key. pub fn burn_enabled(keychain: &Keychain, burn_key_id: &Identifier) -> Keychain { let mut key_overrides = HashMap::new(); key_overrides.insert( burn_key_id.clone(), SecretKey::from_slice(&keychain.secp, &[1; 32]).unwrap(), ); Keychain { key_overrides: key_overrides, ..keychain.clone() } } pub fn from_seed(seed: &[u8]) -> Result { let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); let extkey = extkey::ExtendedKey::from_seed(&secp, seed)?; let keychain = Keychain { secp: secp, extkey: extkey, aggsig_contexts: Arc::new(RwLock::new(None)), key_overrides: HashMap::new(), key_derivation_cache: Arc::new(RwLock::new(HashMap::new())), }; Ok(keychain) } /// For testing - probably not a good idea to use outside of tests. pub fn from_random_seed() -> Result { let seed: String = thread_rng().gen_ascii_chars().take(16).collect(); let seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes()); Keychain::from_seed(seed.as_bytes()) } pub fn derive_key_id(&self, derivation: u32) -> Result { let child_key = self.extkey.derive(&self.secp, derivation)?; Ok(child_key.key_id) } fn derived_key(&self, key_id: &Identifier) -> Result { // first check our overrides and just return the key if we have one in there if let Some(key) = self.key_overrides.get(key_id) { trace!(LOGGER, "... Derived Key (using override) key_id: {}", key_id); return Ok(*key); } let child_key = self.derived_child_key(key_id)?; Ok(child_key.key) } fn derived_child_key(&self, key_id: &Identifier) -> Result { trace!(LOGGER, "Derived Key by key_id: {}", key_id); // then check the derivation cache to see if we have previously derived this key // if so use the derivation from the cache to derive the key { let cache = self.key_derivation_cache.read().unwrap(); if let Some(derivation) = cache.get(key_id) { trace!(LOGGER, "... Derived Key (cache hit) key_id: {}, derivation: {}", key_id, derivation); return Ok(self.derived_key_from_index(*derivation)?) } } // otherwise iterate over a large number of derivations looking for our key // cache the resulting derivations by key_id for faster lookup later // TODO - remove hard limit (within reason) // TODO - do we benefit here if we track our max known n_child? { let mut cache = self.key_derivation_cache.write().unwrap(); for i in 1..100_000 { let child_key = self.extkey.derive(&self.secp, i)?; // let child_key_id = extkey.identifier(&self.secp)?; if !cache.contains_key(&child_key.key_id) { trace!( LOGGER, "... Derived Key (cache miss) key_id: {}, derivation: {}", child_key.key_id, child_key.n_child, ); cache.insert(child_key.key_id.clone(), child_key.n_child); } if child_key.key_id == *key_id { return Ok(child_key); } } } Err(Error::KeyDerivation( format!("failed to derive child_key for {:?}", key_id), )) } // if we know the derivation index we can just straight to deriving the key fn derived_key_from_index( &self, derivation: u32, ) -> Result { trace!(LOGGER, "Derived Key (fast) by derivation: {}", derivation); let child_key = self.extkey.derive(&self.secp, derivation)?; return Ok(child_key) } pub fn commit(&self, amount: u64, key_id: &Identifier) -> Result { let skey = self.derived_key(key_id)?; let commit = self.secp.commit(amount, skey)?; Ok(commit) } pub fn commit_with_key_index( &self, amount: u64, derivation: u32, ) -> Result { let child_key = self.derived_key_from_index(derivation)?; let commit = self.secp.commit(amount, child_key.key)?; Ok(commit) } pub fn switch_commit(&self, key_id: &Identifier) -> Result { let skey = self.derived_key(key_id)?; let commit = self.secp.switch_commit(skey)?; Ok(commit) } pub fn switch_commit_from_index(&self, index:u32) -> Result { // just do this directly, because cache seems really slow for wallet reconstruct let skey = self.extkey.derive(&self.secp, index)?; let skey = skey.key; let commit = self.secp.switch_commit(skey)?; Ok(commit) } pub fn switch_commit_hash_key(&self, key_id: &Identifier) -> Result<[u8; 32], Error> { // first check our overrides and just return zero key if we have an override // we allow keys to be overridden for testing // and do not care about switch_commit_hash_keys in this case if let Some(_) = self.key_overrides.get(key_id) { let key: [u8; 32] = Default::default(); return Ok(key); } let child_key = self.derived_child_key(key_id)?; Ok(child_key.switch_key) } pub fn range_proof( &self, amount: u64, key_id: &Identifier, commit: Commitment, msg: ProofMessage, ) -> Result { let skey = self.derived_key(key_id)?; let range_proof = self.secp.range_proof(0, amount, skey, commit, msg); Ok(range_proof) } pub fn rewind_range_proof( &self, key_id: &Identifier, commit: Commitment, proof: RangeProof, ) -> Result { let nonce = self.derived_key(key_id)?; Ok(self.secp.rewind_range_proof(commit, proof, nonce)) } pub fn blind_sum(&self, blind_sum: &BlindSum) -> Result { let mut pos_keys: Vec = blind_sum .positive_key_ids .iter() .filter_map(|k| self.derived_key(&k).ok()) .collect(); let mut neg_keys: Vec = blind_sum .negative_key_ids .iter() .filter_map(|k| self.derived_key(&k).ok()) .collect(); pos_keys.extend(&blind_sum .positive_blinding_factors .iter() .map(|b| b.secret_key()) .collect::>()); neg_keys.extend(&blind_sum .negative_blinding_factors .iter() .map(|b| b.secret_key()) .collect::>()); let blinding = self.secp.blind_sum(pos_keys, neg_keys)?; Ok(BlindingFactor::new(blinding)) } pub fn aggsig_create_context(&self, transaction_id: &Uuid, sec_key:SecretKey) -> Result<(), Error>{ let mut contexts = self.aggsig_contexts.write().unwrap(); if contexts.is_none() { *contexts = Some(HashMap::new()) } if contexts.as_mut().unwrap().contains_key(transaction_id) { return Err(Error::Transaction(String::from("Duplication transaction id"))); } contexts.as_mut().unwrap().insert(transaction_id.clone(), AggSigTxContext{ sec_key: sec_key, sec_nonce: aggsig::export_secnonce_single(&self.secp).unwrap(), output_ids: vec![], }); Ok(()) } /// Tracks an output contributing to my excess value (if it needs to /// be kept between invocations pub fn aggsig_add_output(&self, transaction_id: &Uuid, output_id:&Identifier){ let mut agg_contexts = self.aggsig_contexts.write().unwrap(); let mut agg_contexts_local = agg_contexts.as_mut().unwrap().clone(); let mut agg_context = agg_contexts_local.get(transaction_id).unwrap().clone(); agg_context.output_ids.push(output_id.clone()); agg_contexts_local.insert(transaction_id.clone(), agg_context); *agg_contexts = Some(agg_contexts_local); } /// Returns all stored outputs pub fn aggsig_get_outputs(&self, transaction_id: &Uuid) -> Vec { let contexts = self.aggsig_contexts.clone(); let contexts_read = contexts.read().unwrap(); let agg_context = contexts_read.as_ref().unwrap(); let agg_context_return = agg_context.get(transaction_id); agg_context_return.unwrap().output_ids.clone() } /// Returns private key, private nonce pub fn aggsig_get_private_keys(&self, transaction_id: &Uuid) -> (SecretKey, SecretKey) { let contexts = self.aggsig_contexts.clone(); let contexts_read=contexts.read().unwrap(); let agg_context = contexts_read.as_ref().unwrap(); let agg_context_return = agg_context.get(transaction_id); (agg_context_return.unwrap().sec_key.clone(), agg_context_return.unwrap().sec_nonce.clone()) } /// Returns public key, public nonce pub fn aggsig_get_public_keys(&self, transaction_id: &Uuid) -> (PublicKey, PublicKey) { let contexts = self.aggsig_contexts.clone(); let contexts_read=contexts.read().unwrap(); let agg_context = contexts_read.as_ref().unwrap(); let agg_context_return = agg_context.get(transaction_id); (PublicKey::from_secret_key(&self.secp, &agg_context_return.unwrap().sec_key).unwrap(), PublicKey::from_secret_key(&self.secp, &agg_context_return.unwrap().sec_nonce).unwrap()) } /// Note 'secnonce' here is used to perform the signature, while 'pubnonce' just allows you to /// provide a custom public nonce to include while calculating e /// nonce_sum is the sum used to decide whether secnonce should be inverted during sig time pub fn aggsig_sign_single(&self, transaction_id: &Uuid, msg: &Message, secnonce:Option<&SecretKey>, pubnonce: Option<&PublicKey>, nonce_sum: Option<&PublicKey>) -> Result { let contexts = self.aggsig_contexts.clone(); let contexts_read=contexts.read().unwrap(); let agg_context = contexts_read.as_ref().unwrap(); let agg_context_return = agg_context.get(transaction_id); let sig = aggsig::sign_single(&self.secp, msg, &agg_context_return.unwrap().sec_key, secnonce, pubnonce, nonce_sum)?; Ok(sig) } //Verifies an aggsig signature pub fn aggsig_verify_single(&self, sig: &Signature, msg: &Message, pubnonce:Option<&PublicKey>, pubkey:&PublicKey, is_partial:bool) -> bool { aggsig::verify_single(&self.secp, sig, msg, pubnonce, pubkey, is_partial) } //Verifies other final sig corresponds with what we're expecting pub fn aggsig_verify_final_sig_build_msg(&self, sig: &Signature, pubkey: &PublicKey, fee: u64, lock_height:u64) -> bool { let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap(); self.aggsig_verify_single(sig, &msg, None, pubkey, true) } //Verifies other party's sig corresponds with what we're expecting pub fn aggsig_verify_partial_sig(&self, transaction_id: &Uuid, sig: &Signature, other_pub_nonce:&PublicKey, pubkey:&PublicKey, fee: u64, lock_height:u64) -> bool { let (_, sec_nonce) = self.aggsig_get_private_keys(transaction_id); let mut nonce_sum = other_pub_nonce.clone(); let _ = nonce_sum.add_exp_assign(&self.secp, &sec_nonce); let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap(); self.aggsig_verify_single(sig, &msg, Some(&nonce_sum), pubkey, true) } pub fn aggsig_calculate_partial_sig(&self, transaction_id: &Uuid, other_pub_nonce:&PublicKey, fee:u64, lock_height:u64) -> Result{ // Add public nonces kR*G + kS*G let (_, sec_nonce) = self.aggsig_get_private_keys(transaction_id); let mut nonce_sum = other_pub_nonce.clone(); let _ = nonce_sum.add_exp_assign(&self.secp, &sec_nonce); let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?; //Now calculate signature using message M=fee, nonce in e=nonce_sum self.aggsig_sign_single(transaction_id, &msg, Some(&sec_nonce), Some(&nonce_sum), Some(&nonce_sum)) } /// Helper function to calculate final singature pub fn aggsig_calculate_final_sig(&self, transaction_id: &Uuid, their_sig: &Signature, our_sig: &Signature, their_pub_nonce: &PublicKey) -> Result { // Add public nonces kR*G + kS*G let (_, sec_nonce) = self.aggsig_get_private_keys(transaction_id); let mut nonce_sum = their_pub_nonce.clone(); let _ = nonce_sum.add_exp_assign(&self.secp, &sec_nonce); let sig = aggsig::add_signatures_single(&self.secp, their_sig, our_sig, &nonce_sum)?; Ok(sig) } /// Helper function to calculate final public key pub fn aggsig_calculate_final_pubkey( &self, transaction_id: &Uuid, their_public_key: &PublicKey, ) -> Result { let (our_sec_key, _) = self.aggsig_get_private_keys(transaction_id); let mut pk_sum = their_public_key.clone(); let _ = pk_sum.add_exp_assign(&self.secp, &our_sec_key); Ok(pk_sum) } /// Just a simple sig, creates its own nonce, etc pub fn aggsig_sign_from_key_id( &self, msg: &Message, key_id: &Identifier, ) -> Result { let skey = self.derived_key(key_id)?; let sig = aggsig::sign_single(&self.secp, &msg, &skey, None, None, None)?; Ok(sig) } /// Verifies a sig given a commitment pub fn aggsig_verify_single_from_commit( secp:&Secp256k1, sig: &Signature, msg: &Message, commit: &Commitment, ) -> bool { // Extract the pubkey, unfortunately we need this hack for now, (we just hope one is valid) // TODO: Create better secp256k1 API to do this let pubkeys = commit.to_two_pubkeys(secp); let mut valid = false; for i in 0..pubkeys.len() { valid = aggsig::verify_single(secp, &sig, &msg, None, &pubkeys[i], false); if valid { break; } } valid } /// Just a simple sig, creates its own nonce, etc pub fn aggsig_sign_with_blinding( secp: &Secp256k1, msg: &Message, blinding: &BlindingFactor, ) -> Result { let sig = aggsig::sign_single(secp, &msg, &blinding.secret_key(), None, None, None)?; Ok(sig) } pub fn sign(&self, msg: &Message, key_id: &Identifier) -> Result { let skey = self.derived_key(key_id)?; let sig = self.secp.sign(msg, &skey)?; Ok(sig) } pub fn sign_with_blinding( &self, msg: &Message, blinding: &BlindingFactor, ) -> Result { let sig = self.secp.sign(msg, &blinding.secret_key())?; Ok(sig) } pub fn secp(&self) -> &Secp256k1 { &self.secp } } #[cfg(test)] mod test { use keychain::{BlindSum, BlindingFactor, Keychain}; use util::secp; use util::secp::pedersen::ProofMessage; use util::secp::key::SecretKey; #[test] fn test_key_derivation() { let keychain = Keychain::from_random_seed().unwrap(); let secp = keychain.secp(); // use the keychain to derive a "key_id" based on the underlying seed let key_id = keychain.derive_key_id(1).unwrap(); let msg_bytes = [0; 32]; let msg = secp::Message::from_slice(&msg_bytes[..]).unwrap(); // now create a zero commitment using the key on the keychain associated with // the key_id let commit = keychain.commit(0, &key_id).unwrap(); // now check we can use our key to verify a signature from this zero commitment let sig = keychain.sign(&msg, &key_id).unwrap(); secp.verify_from_commit(&msg, &sig, &commit).unwrap(); } #[test] fn test_rewind_range_proof() { let keychain = Keychain::from_random_seed().unwrap(); let key_id = keychain.derive_key_id(1).unwrap(); let commit = keychain.commit(5, &key_id).unwrap(); let msg = ProofMessage::empty(); let proof = keychain.range_proof(5, &key_id, commit, msg).unwrap(); let proof_info = keychain.rewind_range_proof(&key_id, commit, proof).unwrap(); assert_eq!(proof_info.success, true); assert_eq!(proof_info.value, 5); // now check the recovered message is "empty" (but not truncated) i.e. all // zeroes assert_eq!( proof_info.message, secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::PROOF_MSG_SIZE]) ); let key_id2 = keychain.derive_key_id(2).unwrap(); // cannot rewind with a different nonce let proof_info = keychain .rewind_range_proof(&key_id2, commit, proof) .unwrap(); assert_eq!(proof_info.success, false); assert_eq!(proof_info.value, 0); // cannot rewind with a commitment to the same value using a different key let commit2 = keychain.commit(5, &key_id2).unwrap(); let proof_info = keychain .rewind_range_proof(&key_id, commit2, proof) .unwrap(); assert_eq!(proof_info.success, false); assert_eq!(proof_info.value, 0); // cannot rewind with a commitment to a different value let commit3 = keychain.commit(4, &key_id).unwrap(); let proof_info = keychain .rewind_range_proof(&key_id, commit3, proof) .unwrap(); assert_eq!(proof_info.success, false); assert_eq!(proof_info.value, 0); } // We plan to "offset" the key used in the kernel commitment // so we are going to be doing some key addition/subtraction. // This test is mainly to demonstrate that idea that summing commitments // and summing the keys used to commit to 0 have the same result. #[test] fn secret_key_addition() { let keychain = Keychain::from_random_seed().unwrap(); let skey1 = SecretKey::from_slice( &keychain.secp, &[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ], ).unwrap(); let skey2 = SecretKey::from_slice( &keychain.secp, &[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, ], ).unwrap(); // adding secret keys 1 and 2 to give secret key 3 let mut skey3 = skey1.clone(); let _ = skey3.add_assign(&keychain.secp, &skey2).unwrap(); // create commitments for secret keys 1, 2 and 3 // all committing to the value 0 (which is what we do for tx_kernels) let commit_1 = keychain.secp.commit(0, skey1).unwrap(); let commit_2 = keychain.secp.commit(0, skey2).unwrap(); let commit_3 = keychain.secp.commit(0, skey3).unwrap(); // now sum commitments for keys 1 and 2 let sum = keychain.secp.commit_sum( vec![commit_1.clone(), commit_2.clone()], vec![], ).unwrap(); // confirm the commitment for key 3 matches the sum of the commitments 1 and 2 assert_eq!(sum, commit_3); // now check we can sum keys up using keychain.blind_sum() // in the same way (convenience function) assert_eq!( keychain.blind_sum(&BlindSum::new() .add_blinding_factor(BlindingFactor::new(skey1)) .add_blinding_factor(BlindingFactor::new(skey2)) ).unwrap(), BlindingFactor::new(skey3) ); } }