2017-10-03 03:02:31 +03:00
|
|
|
// 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};
|
2017-10-16 20:11:01 +03:00
|
|
|
use std::collections::HashMap;
|
2017-10-03 03:02:31 +03:00
|
|
|
|
2017-11-01 02:20:55 +03:00
|
|
|
use util::secp;
|
|
|
|
use util::secp::{Message, Secp256k1, Signature};
|
|
|
|
use util::secp::key::SecretKey;
|
|
|
|
use util::secp::pedersen::{Commitment, ProofMessage, ProofInfo, RangeProof};
|
2017-10-03 03:02:31 +03:00
|
|
|
use blake2;
|
|
|
|
use blind::{BlindingFactor, BlindSum};
|
2017-10-13 07:45:07 +03:00
|
|
|
use extkey::{self, Identifier};
|
2017-10-03 03:02:31 +03:00
|
|
|
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
ExtendedKey(extkey::Error),
|
|
|
|
Secp(secp::Error),
|
|
|
|
KeyDerivation(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<secp::Error> for Error {
|
|
|
|
fn from(e: secp::Error) -> Error {
|
|
|
|
Error::Secp(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<extkey::Error> for Error {
|
|
|
|
fn from(e: extkey::Error) -> Error {
|
|
|
|
Error::ExtendedKey(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct Keychain {
|
|
|
|
secp: Secp256k1,
|
|
|
|
extkey: extkey::ExtendedKey,
|
2017-10-16 20:11:01 +03:00
|
|
|
key_overrides: HashMap<Identifier, SecretKey>,
|
2017-10-03 03:02:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Keychain {
|
2017-10-13 07:45:07 +03:00
|
|
|
pub fn root_key_id(&self) -> Identifier {
|
|
|
|
self.extkey.root_key_id.clone()
|
2017-10-03 03:02:31 +03:00
|
|
|
}
|
|
|
|
|
2017-10-16 20:11:01 +03:00
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-03 03:02:31 +03:00
|
|
|
pub fn from_seed(seed: &[u8]) -> Result<Keychain, Error> {
|
|
|
|
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
|
|
|
|
let extkey = extkey::ExtendedKey::from_seed(&secp, seed)?;
|
|
|
|
let keychain = Keychain {
|
|
|
|
secp: secp,
|
|
|
|
extkey: extkey,
|
2017-10-16 20:11:01 +03:00
|
|
|
key_overrides: HashMap::new(),
|
2017-10-03 03:02:31 +03:00
|
|
|
};
|
|
|
|
Ok(keychain)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// For testing - probably not a good idea to use outside of tests.
|
|
|
|
pub fn from_random_seed() -> Result<Keychain, Error> {
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
pub fn derive_key_id(&self, derivation: u32) -> Result<Identifier, Error> {
|
2017-10-03 03:02:31 +03:00
|
|
|
let extkey = self.extkey.derive(&self.secp, derivation)?;
|
2017-10-13 07:45:07 +03:00
|
|
|
let key_id = extkey.identifier(&self.secp)?;
|
|
|
|
Ok(key_id)
|
2017-10-03 03:02:31 +03:00
|
|
|
}
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
fn derived_key(&self, key_id: &Identifier) -> Result<SecretKey, Error> {
|
2017-10-16 20:11:01 +03:00
|
|
|
if let Some(key) = self.key_overrides.get(key_id) {
|
|
|
|
return Ok(*key);
|
2017-10-12 06:35:40 +03:00
|
|
|
}
|
2017-10-07 20:38:41 +03:00
|
|
|
|
|
|
|
for i in 1..10000 {
|
|
|
|
let extkey = self.extkey.derive(&self.secp, i)?;
|
2017-10-13 07:45:07 +03:00
|
|
|
if extkey.identifier(&self.secp)? == *key_id {
|
2017-10-16 20:11:01 +03:00
|
|
|
return Ok(extkey.key);
|
2017-10-07 20:38:41 +03:00
|
|
|
}
|
|
|
|
}
|
2017-10-16 20:11:01 +03:00
|
|
|
Err(Error::KeyDerivation(
|
|
|
|
format!("cannot find extkey for {:?}", key_id),
|
|
|
|
))
|
2017-10-03 03:02:31 +03:00
|
|
|
}
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
pub fn commit(&self, amount: u64, key_id: &Identifier) -> Result<Commitment, Error> {
|
|
|
|
let skey = self.derived_key(key_id)?;
|
2017-10-03 03:02:31 +03:00
|
|
|
let commit = self.secp.commit(amount, skey)?;
|
|
|
|
Ok(commit)
|
|
|
|
}
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
pub fn switch_commit(&self, key_id: &Identifier) -> Result<Commitment, Error> {
|
|
|
|
let skey = self.derived_key(key_id)?;
|
2017-10-03 03:02:31 +03:00
|
|
|
let commit = self.secp.switch_commit(skey)?;
|
|
|
|
Ok(commit)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn range_proof(
|
|
|
|
&self,
|
|
|
|
amount: u64,
|
2017-10-13 07:45:07 +03:00
|
|
|
key_id: &Identifier,
|
2017-10-03 03:02:31 +03:00
|
|
|
commit: Commitment,
|
2017-10-06 00:40:46 +03:00
|
|
|
msg: ProofMessage,
|
2017-10-03 03:02:31 +03:00
|
|
|
) -> Result<RangeProof, Error> {
|
2017-10-13 07:45:07 +03:00
|
|
|
let skey = self.derived_key(key_id)?;
|
2017-10-06 00:40:46 +03:00
|
|
|
let range_proof = self.secp.range_proof(0, amount, skey, commit, msg);
|
2017-10-03 03:02:31 +03:00
|
|
|
Ok(range_proof)
|
|
|
|
}
|
|
|
|
|
2017-10-06 00:40:46 +03:00
|
|
|
pub fn rewind_range_proof(
|
|
|
|
&self,
|
2017-10-13 07:45:07 +03:00
|
|
|
key_id: &Identifier,
|
2017-10-06 00:40:46 +03:00
|
|
|
commit: Commitment,
|
|
|
|
proof: RangeProof,
|
|
|
|
) -> Result<ProofInfo, Error> {
|
2017-10-13 07:45:07 +03:00
|
|
|
let nonce = self.derived_key(key_id)?;
|
2017-10-06 00:40:46 +03:00
|
|
|
Ok(self.secp.rewind_range_proof(commit, proof, nonce))
|
|
|
|
}
|
|
|
|
|
2017-10-03 03:02:31 +03:00
|
|
|
pub fn blind_sum(&self, blind_sum: &BlindSum) -> Result<BlindingFactor, Error> {
|
|
|
|
let mut pos_keys: Vec<SecretKey> = blind_sum
|
2017-10-13 07:45:07 +03:00
|
|
|
.positive_key_ids
|
2017-10-03 03:02:31 +03:00
|
|
|
.iter()
|
|
|
|
.filter_map(|k| self.derived_key(&k).ok())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut neg_keys: Vec<SecretKey> = blind_sum
|
2017-10-13 07:45:07 +03:00
|
|
|
.negative_key_ids
|
2017-10-03 03:02:31 +03:00
|
|
|
.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::<Vec<SecretKey>>());
|
|
|
|
|
|
|
|
neg_keys.extend(&blind_sum
|
|
|
|
.negative_blinding_factors
|
|
|
|
.iter()
|
|
|
|
.map(|b| b.secret_key())
|
|
|
|
.collect::<Vec<SecretKey>>());
|
|
|
|
|
|
|
|
let blinding = self.secp.blind_sum(pos_keys, neg_keys)?;
|
|
|
|
Ok(BlindingFactor::new(blinding))
|
|
|
|
}
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
pub fn sign(&self, msg: &Message, key_id: &Identifier) -> Result<Signature, Error> {
|
|
|
|
let skey = self.derived_key(key_id)?;
|
2017-10-03 03:02:31 +03:00
|
|
|
let sig = self.secp.sign(msg, &skey)?;
|
|
|
|
Ok(sig)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sign_with_blinding(
|
|
|
|
&self,
|
|
|
|
msg: &Message,
|
|
|
|
blinding: &BlindingFactor,
|
|
|
|
) -> Result<Signature, Error> {
|
|
|
|
let sig = self.secp.sign(msg, &blinding.secret_key())?;
|
|
|
|
Ok(sig)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn secp(&self) -> &Secp256k1 {
|
|
|
|
&self.secp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use keychain::Keychain;
|
2017-11-01 02:20:55 +03:00
|
|
|
use util::secp;
|
|
|
|
use util::secp::pedersen::ProofMessage;
|
2017-10-03 03:02:31 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_key_derivation() {
|
|
|
|
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
|
|
|
|
let keychain = Keychain::from_random_seed().unwrap();
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
// use the keychain to derive a "key_id" based on the underlying seed
|
|
|
|
let key_id = keychain.derive_key_id(1).unwrap();
|
2017-10-03 03:02:31 +03:00
|
|
|
|
|
|
|
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
|
2017-10-13 07:45:07 +03:00
|
|
|
// the key_id
|
|
|
|
let commit = keychain.commit(0, &key_id).unwrap();
|
2017-10-03 03:02:31 +03:00
|
|
|
|
|
|
|
// now check we can use our key to verify a signature from this zero commitment
|
2017-10-13 07:45:07 +03:00
|
|
|
let sig = keychain.sign(&msg, &key_id).unwrap();
|
2017-10-03 03:02:31 +03:00
|
|
|
secp.verify_from_commit(&msg, &sig, &commit).unwrap();
|
|
|
|
}
|
2017-10-06 00:40:46 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rewind_range_proof() {
|
|
|
|
let keychain = Keychain::from_random_seed().unwrap();
|
2017-10-13 07:45:07 +03:00
|
|
|
let key_id = keychain.derive_key_id(1).unwrap();
|
|
|
|
let commit = keychain.commit(5, &key_id).unwrap();
|
2017-10-06 00:40:46 +03:00
|
|
|
let msg = ProofMessage::empty();
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
let proof = keychain.range_proof(5, &key_id, commit, msg).unwrap();
|
|
|
|
let proof_info = keychain.rewind_range_proof(&key_id, commit, proof).unwrap();
|
2017-10-06 00:40:46 +03:00
|
|
|
|
|
|
|
assert_eq!(proof_info.success, true);
|
|
|
|
assert_eq!(proof_info.value, 5);
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
// now check the recovered message is "empty" (but not truncated) i.e. all
|
|
|
|
// zeroes
|
2017-10-06 00:40:46 +03:00
|
|
|
assert_eq!(
|
|
|
|
proof_info.message,
|
|
|
|
secp::pedersen::ProofMessage::from_bytes(&[0; secp::constants::PROOF_MSG_SIZE])
|
|
|
|
);
|
|
|
|
|
2017-10-13 07:45:07 +03:00
|
|
|
let key_id2 = keychain.derive_key_id(2).unwrap();
|
2017-10-06 00:40:46 +03:00
|
|
|
|
|
|
|
// cannot rewind with a different nonce
|
2017-10-16 20:11:01 +03:00
|
|
|
let proof_info = keychain
|
|
|
|
.rewind_range_proof(&key_id2, commit, proof)
|
|
|
|
.unwrap();
|
2017-10-06 00:40:46 +03:00
|
|
|
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
|
2017-10-13 07:45:07 +03:00
|
|
|
let commit2 = keychain.commit(5, &key_id2).unwrap();
|
2017-10-16 20:11:01 +03:00
|
|
|
let proof_info = keychain
|
|
|
|
.rewind_range_proof(&key_id, commit2, proof)
|
|
|
|
.unwrap();
|
2017-10-06 00:40:46 +03:00
|
|
|
assert_eq!(proof_info.success, false);
|
|
|
|
assert_eq!(proof_info.value, 0);
|
|
|
|
|
|
|
|
// cannot rewind with a commitment to a different value
|
2017-10-13 07:45:07 +03:00
|
|
|
let commit3 = keychain.commit(4, &key_id).unwrap();
|
2017-10-16 20:11:01 +03:00
|
|
|
let proof_info = keychain
|
|
|
|
.rewind_range_proof(&key_id, commit3, proof)
|
|
|
|
.unwrap();
|
2017-10-06 00:40:46 +03:00
|
|
|
assert_eq!(proof_info.success, false);
|
|
|
|
assert_eq!(proof_info.value, 0);
|
|
|
|
}
|
2017-10-03 03:02:31 +03:00
|
|
|
}
|