Skip to content

Commit

Permalink
WIP: improve key manager
Browse files Browse the repository at this point in the history
  • Loading branch information
h4sh3d committed Sep 1, 2021
1 parent aaa9eeb commit 7676a3f
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 74 deletions.
5 changes: 5 additions & 0 deletions src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ use crate::consensus::{self, deserialize, serialize, CanonicalBytes, Decodable,
use crate::crypto::{Keys, Signatures};
use crate::transaction::{Buyable, Cancelable, Fundable, Lockable, Punishable, Refundable};

pub enum Blockchain {
Bitcoin,
Monero,
}

/// Defines the type for a blockchain address, this type is used when manipulating transactions.
pub trait Address {
/// Defines the address format for the arbitrating blockchain.
Expand Down
58 changes: 51 additions & 7 deletions src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
//! sign, etc) used to create the generic framework for supporting multiple blockchains under the
//! same interface.
use std::convert::TryInto;
use std::error;
use std::fmt::{self, Debug};
use std::io;

use thiserror::Error;
use tiny_keccak::{Hasher, Keccak};

use crate::consensus::{self, CanonicalBytes, Decodable, Encodable};

Expand Down Expand Up @@ -230,6 +232,55 @@ impl Decodable for SharedKeyId {
}
}

fixed_hash::construct_fixed_hash!(
/// Result of a keccak256 commitment.
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate"),
)]
pub struct KeccakCommitment(32);
);

impl KeccakCommitment {
/// Create a null commitment hash with all zeros.
pub fn null_hash() -> Self {
Self([0u8; 32])
}

/// Hash a stream of bytes with the Keccak-256 hash function.
pub fn new(input: [u8; 32]) -> Self {
Self(input)
}
}

impl CanonicalBytes for KeccakCommitment {
fn as_canonical_bytes(&self) -> Vec<u8> {
(*self).to_fixed_bytes().into()
}

fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, consensus::Error>
where
Self: Sized,
{
Ok(Self::new(bytes.try_into().map_err(consensus::Error::new)?))
}
}

/// Engine to produce and validate hash commitments.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommitmentEngine;

impl Commit<KeccakCommitment> for CommitmentEngine {
fn commit_to<T: AsRef<[u8]>>(&self, value: T) -> KeccakCommitment {
let mut out = [0u8; 32];
let mut keccak = Keccak::v256();
keccak.update(value.as_ref());
keccak.finalize(&mut out);
KeccakCommitment::new(out)
}
}

/// Required for [`Arbitrating`] and [`Accordant`] blockchains to fix the cryptographic secret key
/// and public key types. The public key type is shared across the network and used in
/// transactions, the secret key type is used during signing operation, proofs, etc.
Expand Down Expand Up @@ -260,13 +311,6 @@ pub trait SharedPrivateKeys {
fn shared_keys() -> Vec<SharedKeyId>;
}

/// Required for swaps to fix the commitments type of the keys and parameters that must go through
/// the commit/reveal scheme at the beginning of the protocol.
pub trait Commitment {
/// Commitment type used in the commit/reveal scheme during swap setup.
type Commitment: Clone + PartialEq + Eq + Debug + fmt::Display + CanonicalBytes;
}

/// Trait required for [`Arbitrating`] blockchains to define the cryptographic message format to
/// sign, the signature format and adaptor signature format used in the cryptographic operations
/// such as signing and verifying signatures and adaptor signatures.
Expand Down
149 changes: 85 additions & 64 deletions src/swap/btcxmr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
//! accordant blockchain.
use crate::consensus::{self, CanonicalBytes};
use crate::blockchain::Blockchain;
use crate::crypto::{
self, AccordantKeyId, ArbitratingKeyId, Commit, Commitment, GenerateKey, GenerateSharedKey,
self, AccordantKeyId, ArbitratingKeyId, GenerateKey, GenerateSharedKey, KeccakCommitment,
ProveCrossGroupDleq, SharedKeyId,
};
use crate::monero::{self as xmr};
#[cfg(feature = "experimental")]
use crate::{bitcoin::BitcoinSegwitV0, crypto::Sign, monero::Monero, swap::Swap};

use monero::cryptonote::hash::Hash;
use monero::KeyPair;

#[cfg(feature = "experimental")]
use ecdsa_fun::{
Expand All @@ -34,6 +36,7 @@ use bitcoin::secp256k1::{
};
use bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey};

use std::collections::HashMap;
use std::str::FromStr;

#[cfg(feature = "experimental")]
Expand All @@ -47,24 +50,16 @@ type NonceGen = nonce::Synthetic<Sha256, nonce::GlobalRng<ThreadRng>>;
pub const SHARED_KEY_BITS: usize = 252;

/// The context for a Bitcoin and Monero swap.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BtcXmr;

#[cfg(feature = "experimental")]
#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
impl Swap for BtcXmr {
/// The arbitrating blockchain
type Ar = BitcoinSegwitV0;

/// The accordant blockchain
type Ac = Monero;

/// The proof system to link both cryptographic groups
type Proof = RingProof;
}

impl Commitment for BtcXmr {
type Commitment = Hash;
type Commitment = KeccakCommitment;
}

impl CanonicalBytes for Hash {
Expand Down Expand Up @@ -97,44 +92,84 @@ impl CanonicalBytes for RingProof {
}

/// Manager responsible for handling key operations (secret and public). Implements traits for
/// handling [`Commit`], [`GenerateKey`], [`GenerateSharedKey`] and [`Sign`].
#[derive(Clone, Debug)]
/// handling [`GenerateKey`], [`GenerateSharedKey`] and [`Sign`].
#[derive(Debug)]
pub struct KeyManager {
seed: Option<[u8; 32]>,
seed: [u8; 32],
//bitcoin_master_key: ExtendedPrivKey,
//monero_master_key: ExtendedPrivKey,
bitcoin_derivations: HashMap<PublicKey, SecretKey>,
monero_derivations: KeyPair,
}

impl KeyManager {
pub fn new(seed: [u8; 32]) -> Self {
Self { seed: Some(seed) }
//pub fn derive_blockchain_seeds(master_seed: [u8; 32], blockchain: Blockchain) -> [u8; 32] {
// // TODO improve by replace this with a proper SLIP10 implementation
// match blockchain {
// Blockchain::Bitcoin =>
// ,
// Blockchain::Monero => ,
// }
//}

pub fn derive_monero_keys(monero_seed: [u8; 32]) -> KeyPair {
let spend = {
let mut bytes = Vec::from(b"farcaster_priv_spend".as_ref());
bytes.extend_from_slice(&monero_seed);

let mut key = Hash::hash(&bytes).to_fixed_bytes();
key[31] &= 0b0000_1111; // Chop off bits that might be greater than the curve modulus

monero::PrivateKey::from_slice(&key).expect("Modulus is lower than curve25519")
};

let view = {
let mut bytes = Vec::from(b"farcaster_priv_view".as_ref());
bytes.extend_from_slice(&monero_seed);
Hash::hash(&bytes).as_scalar()
};

KeyPair { view, spend }
}

pub fn new_keyless() -> Self {
Self { seed: None }
//fn derive_bitcoin_keys() -> HashMap<PublicKey

/// Create a new key manager with the provided master seed.
pub fn new(seed: [u8; 32]) -> Self {
//let master_key = ExtendedPrivKey::new_master(bitcoin::Network::Bitcoin, &seed)
// .map_err(crypto::Error::new)?;

Self {
seed,
bitcoin_derivations: HashMap::new(),
monero_derivations: Self::derive_monero_keys(seed),
}
}

pub fn get_btc_privkey(&self, key_id: ArbitratingKeyId) -> Result<SecretKey, crypto::Error> {
let secp = Secp256k1::new();
if let Some(seed) = self.seed {
let master_key = ExtendedPrivKey::new_master(bitcoin::Network::Bitcoin, &seed)
.map_err(crypto::Error::new)?;
let key =
match key_id {
ArbitratingKeyId::Fund => master_key
.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/1").unwrap()),
ArbitratingKeyId::Buy => master_key
.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/2").unwrap()),
ArbitratingKeyId::Cancel => master_key
.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/3").unwrap()),
ArbitratingKeyId::Refund => master_key
.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/4").unwrap()),
ArbitratingKeyId::Punish => master_key
.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/5").unwrap()),
ArbitratingKeyId::Extra(_) => return Err(crypto::Error::UnsupportedKey),
};
Ok(key.map_err(crypto::Error::new)?.private_key.key)
} else {
Err(crypto::Error::UnsupportedKey)
}
let seed = self.seed;
let master_key = ExtendedPrivKey::new_master(bitcoin::Network::Bitcoin, &seed)
.map_err(crypto::Error::new)?;
let key = match key_id {
ArbitratingKeyId::Fund => {
master_key.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/1").unwrap())
}
ArbitratingKeyId::Buy => {
master_key.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/2").unwrap())
}
ArbitratingKeyId::Cancel => {
master_key.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/3").unwrap())
}
ArbitratingKeyId::Refund => {
master_key.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/4").unwrap())
}
ArbitratingKeyId::Punish => {
master_key.derive_priv(&secp, &DerivationPath::from_str("m/0/1'/5").unwrap())
}
ArbitratingKeyId::Extra(_) => return Err(crypto::Error::UnsupportedKey),
};
Ok(key.map_err(crypto::Error::new)?.private_key.key)
}

pub fn get_btc_privkey_by_pub(&self, pubkey: &PublicKey) -> Result<SecretKey, crypto::Error> {
Expand Down Expand Up @@ -166,17 +201,13 @@ impl KeyManager {
}

pub fn private_spend_from_seed(&self) -> Result<monero::PrivateKey, crypto::Error> {
if let Some(seed) = self.seed {
let mut bytes = Vec::from(b"farcaster_priv_spend".as_ref());
bytes.extend_from_slice(&seed);
let mut bytes = Vec::from(b"farcaster_priv_spend".as_ref());
bytes.extend_from_slice(&self.seed);

let mut key = Hash::hash(&bytes).to_fixed_bytes();
key[31] &= 0b0000_1111; // Chop off bits that might be greater than the curve modulus
let mut key = Hash::hash(&bytes).to_fixed_bytes();
key[31] &= 0b0000_1111; // Chop off bits that might be greater than the curve modulus

monero::PrivateKey::from_slice(&key).map_err(crypto::Error::new)
} else {
Err(crypto::Error::UnsupportedKey)
}
monero::PrivateKey::from_slice(&key).map_err(crypto::Error::new)
}
}

Expand All @@ -193,17 +224,13 @@ impl GenerateKey<monero::PublicKey, AccordantKeyId> for KeyManager {

impl GenerateSharedKey<monero::PrivateKey> for KeyManager {
fn get_shared_key(&self, key_id: SharedKeyId) -> Result<monero::PrivateKey, crypto::Error> {
if let Some(seed) = self.seed {
match key_id.id() {
xmr::SHARED_VIEW_KEY_ID => {
let mut bytes = Vec::from(b"farcaster_priv_view".as_ref());
bytes.extend_from_slice(&seed);
Ok(Hash::hash(&bytes).as_scalar())
}
_ => Err(crypto::Error::UnsupportedKey),
match key_id.id() {
xmr::SHARED_VIEW_KEY_ID => {
let mut bytes = Vec::from(b"farcaster_priv_view".as_ref());
bytes.extend_from_slice(&self.seed);
Ok(Hash::hash(&bytes).as_scalar())
}
} else {
Err(crypto::Error::UnsupportedKey)
_ => Err(crypto::Error::UnsupportedKey),
}
}
}
Expand Down Expand Up @@ -325,12 +352,6 @@ impl Sign<PublicKey, SecretKey, Sha256dHash, Signature, EncryptedSignature> for
}
}

impl Commit<Hash> for KeyManager {
fn commit_to<T: AsRef<[u8]>>(&self, value: T) -> Hash {
Hash::hash(value.as_ref())
}
}

impl ProveCrossGroupDleq<PublicKey, monero::PublicKey, RingProof> for KeyManager {
/// Generate the proof and the two public keys: the arbitrating public key, also called the
/// adaptor public key, and the accordant public spend key.
Expand Down
8 changes: 5 additions & 3 deletions src/swap/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
//! Defines the high level of a swap between a Arbitrating blockchain and a Accordant blockchain
//! and its concrete instances of swaps.
use std::fmt::Debug;
use std::fmt::{self, Debug};
use std::io;

use crate::consensus::{self, CanonicalBytes, Decodable, Encodable};
use crate::crypto::Commitment;
use crate::role::{Accordant, Arbitrating};

use lightning_encoding::strategies::AsStrict;
Expand Down Expand Up @@ -43,7 +42,7 @@ impl lightning_encoding::Strategy for SwapId {

/// Specifie the context of a swap, fixing the arbitrating blockchain, the accordant blockchain and
/// the link between them.
pub trait Swap: Debug + Clone + Commitment {
pub trait Swap: Debug + Clone {
/// The arbitrating blockchain concrete implementation used for the swap.
type Ar: Arbitrating;

Expand All @@ -52,4 +51,7 @@ pub trait Swap: Debug + Clone + Commitment {

///// The concrete type to link both blockchain cryptographic groups used in by the signatures.
type Proof: Clone + Debug + CanonicalBytes;

/// Commitment type used in the commit/reveal scheme during swap setup.
type Commitment: Clone + PartialEq + Eq + Debug + fmt::Display + CanonicalBytes;
}

0 comments on commit 7676a3f

Please sign in to comment.