Skip to content

Commit

Permalink
Save to disk coconut keypair
Browse files Browse the repository at this point in the history
  • Loading branch information
neacsu committed Nov 9, 2022
1 parent f83760e commit 51e407b
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 47 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions common/nymcoconut/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ serde_derive = "1.0"
bs58 = "0.4.0"
sha2 = "0.9"

pemstore = { path = "../pemstore" }

[dependencies.ff]
version = "0.11"
default-features = false
Expand Down
50 changes: 50 additions & 0 deletions common/nymcoconut/src/scheme/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::convert::TryInto;

use bls12_381::{G1Projective, G2Projective, Scalar};
use group::Curve;
use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use serde_derive::{Deserialize, Serialize};

use crate::error::{CoconutError, Result};
Expand All @@ -29,6 +30,22 @@ pub struct SecretKey {
pub(crate) ys: Vec<Scalar>,
}

impl PemStorableKey for SecretKey {
type Error = CoconutError;

fn pem_type() -> &'static str {
"COCONUT SECRET KEY"
}

fn to_bytes(&self) -> Vec<u8> {
self.to_bytes()
}

fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}

impl TryFrom<&[u8]> for SecretKey {
type Error = CoconutError;

Expand Down Expand Up @@ -128,6 +145,22 @@ pub struct VerificationKey {
pub(crate) beta_g2: Vec<G2Projective>,
}

impl PemStorableKey for VerificationKey {
type Error = CoconutError;

fn pem_type() -> &'static str {
"COCONUT VERIFICATION KEY"
}

fn to_bytes(&self) -> Vec<u8> {
self.to_bytes()
}

fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}

impl TryFrom<&[u8]> for VerificationKey {
type Error = CoconutError;

Expand Down Expand Up @@ -366,6 +399,23 @@ pub struct KeyPair {
pub index: Option<SignerIndex>,
}

impl PemStorableKeyPair for KeyPair {
type PrivatePemKey = SecretKey;
type PublicPemKey = VerificationKey;

fn private_key(&self) -> &Self::PrivatePemKey {
&self.secret_key
}

fn public_key(&self) -> &Self::PublicPemKey {
&self.verification_key
}

fn from_keys(secret_key: Self::PrivatePemKey, verification_key: Self::PublicPemKey) -> Self {
Self::from_keys(secret_key, verification_key)
}
}

impl KeyPair {
const MARKER_BYTES: &'static [u8] = b"coconutkeypair";

Expand Down
39 changes: 27 additions & 12 deletions validator-api/src/coconut/dkg/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use coconut_dkg_common::types::EpochState;
use dkg::bte::keys::KeyPair as DkgKeyPair;
use rand::rngs::OsRng;
use rand::RngCore;
use std::path::PathBuf;
use std::time::Duration;
use task::ShutdownListener;
use tokio::time::interval;
Expand All @@ -35,13 +36,15 @@ pub(crate) fn init_keypair(config: &Config) -> Result<()> {

pub(crate) struct DkgController<R> {
dkg_client: DkgClient,
secret_key_path: PathBuf,
verification_key_path: PathBuf,
state: State,
rng: R,
polling_rate: Duration,
}

impl<R: RngCore + Clone> DkgController<R> {
pub(crate) fn new(
pub(crate) async fn new(
config: &Config,
nymd_client: nymd_client::Client<SigningNymdClient>,
coconut_keypair: CoconutKeyPair,
Expand All @@ -51,20 +54,28 @@ impl<R: RngCore + Clone> DkgController<R> {
config.decryption_key_path(),
config.public_key_with_proof_path(),
))?;
if let Ok(coconut_keypair_value) = pemstore::load_keypair(&pemstore::KeyPairPath::new(
config.secret_key_path(),
config.verification_key_path(),
)) {
coconut_keypair.set(coconut_keypair_value).await;
}

Ok(DkgController {
dkg_client: DkgClient::new(nymd_client),
secret_key_path: config.secret_key_path(),
verification_key_path: config.verification_key_path(),
state: State::new(dkg_keypair, coconut_keypair),
rng,
polling_rate: config.get_dkg_contract_polling_rate(),
})
}

async fn handle_epoch_state(&mut self) -> bool {
async fn handle_epoch_state(&mut self) {
match self.dkg_client.get_current_epoch_state().await {
Err(e) => warn!("Could not get current epoch state {}", e),
Ok(epoch_state) => {
if let Err(e) = self.state.is_consistent(epoch_state) {
if let Err(e) = self.state.is_consistent(epoch_state).await {
error!(
"Epoch state is corrupted - {}, the process should be terminated",
e
Expand All @@ -78,28 +89,32 @@ impl<R: RngCore + Clone> DkgController<R> {
dealing_exchange(&self.dkg_client, &mut self.state, self.rng.clone()).await
}
EpochState::VerificationKeySubmission => {
verification_key_submission(&self.dkg_client, &mut self.state).await
let keypair_path = pemstore::KeyPairPath::new(
self.secret_key_path.clone(),
self.verification_key_path.clone(),
);
verification_key_submission(
&self.dkg_client,
&mut self.state,
&keypair_path,
)
.await
}
EpochState::InProgress => return true,
// Just wait, in case we need to redo dkg at some point
EpochState::InProgress => Ok(()),
};
if let Err(e) = ret {
warn!("Could not handle this iteration for the epoch state: {}", e);
}
}
}
false
}

pub(crate) async fn run(mut self, mut shutdown: ShutdownListener) {
let mut interval = interval(self.polling_rate);
while !shutdown.is_shutdown() {
tokio::select! {
_ = interval.tick() => {
if self.handle_epoch_state().await {
// If dkg finished, we can finish this task
break;
}
}
_ = interval.tick() => self.handle_epoch_state().await,
_ = shutdown.recv() => {
trace!("DkgController: Received shutdown");
}
Expand Down
3 changes: 1 addition & 2 deletions validator-api/src/coconut/dkg/dealing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub(crate) async fn dealing_exchange(
rng: impl RngCore + Clone,
) -> Result<(), CoconutError> {
if state.receiver_index().is_some() {
info!("Already have index {}", state.receiver_index().unwrap());
return Ok(());
}

Expand Down Expand Up @@ -46,7 +45,7 @@ pub(crate) async fn dealing_exchange(
.await?;
}

info!("Setting index to {}", receiver_index.unwrap());
info!("Finished submitting DKG dealing");
state.set_receiver_index(receiver_index);

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion validator-api/src/coconut/dkg/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub(crate) async fn public_key_submission(
dkg_client.register_dealer(bte_key).await?
};
state.set_node_index(index);
info!("Starting dkg protocol with index {}", index);
info!("Starting DKG protocol with index {}", index);

Ok(())
}
19 changes: 15 additions & 4 deletions validator-api/src/coconut/dkg/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ impl TryFrom<DealerDetails> for DkgParticipant {
}
}

#[async_trait]
pub(crate) trait ConsistentState {
fn node_index_value(&self) -> Result<NodeIndex, CoconutError>;
fn receiver_index_value(&self) -> Result<usize, CoconutError>;
fn threshold(&self) -> Result<Threshold, CoconutError>;
fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
async fn coconut_keypair_is_some(&self) -> Result<(), CoconutError>;
async fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
match epoch_state {
EpochState::PublicKeySubmission => {}
EpochState::DealingExchange => {
Expand All @@ -53,9 +55,7 @@ pub(crate) trait ConsistentState {
self.threshold()?;
}
EpochState::InProgress => {
self.node_index_value()?;
self.receiver_index_value()?;
self.threshold()?;
self.coconut_keypair_is_some().await?;
}
}
Ok(())
Expand All @@ -71,6 +71,7 @@ pub(crate) struct State {
threshold: Option<Threshold>,
}

#[async_trait]
impl ConsistentState for State {
fn node_index_value(&self) -> Result<NodeIndex, CoconutError> {
self.node_index.ok_or(CoconutError::UnrecoverableState {
Expand Down Expand Up @@ -98,6 +99,16 @@ impl ConsistentState for State {
Ok(threshold)
}
}

async fn coconut_keypair_is_some(&self) -> Result<(), CoconutError> {
if self.coconut_keypair_is_some().await {
Ok(())
} else {
Err(CoconutError::UnrecoverableState {
reason: String::from("Coconut keypair should have been set"),
})
}
}
}

impl State {
Expand Down
4 changes: 4 additions & 0 deletions validator-api/src/coconut/dkg/verification_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use credentials::coconut::bandwidth::{PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
use dkg::bte::{decrypt_share, setup};
use dkg::{combine_shares, Dealing};
use nymcoconut::{KeyPair, Parameters, SecretKey};
use pemstore::KeyPairPath;
use std::collections::BTreeMap;

// Filter the dealers based on what dealing they posted (or not) in the contract
Expand Down Expand Up @@ -128,14 +129,17 @@ fn derive_partial_keypair(
pub(crate) async fn verification_key_submission(
dkg_client: &DkgClient,
state: &mut State,
keypair_path: &KeyPairPath,
) -> Result<(), CoconutError> {
if state.coconut_keypair_is_some().await {
return Ok(());
}

let dealings_maps = deterministic_filter_dealers(dkg_client, state).await?;
let coconut_keypair = derive_partial_keypair(state, dealings_maps)?;
pemstore::store_keypair(&coconut_keypair, keypair_path)?;
state.set_coconut_keypair(coconut_keypair).await;
info!("DKG finished, keys are saved to disk");

Ok(())
}
3 changes: 3 additions & 0 deletions validator-api/src/coconut/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub type Result<T> = std::result::Result<T, CoconutError>;

#[derive(Debug, Error)]
pub enum CoconutError {
#[error("{0}")]
IOError(#[from] std::io::Error),

#[error("Could not parse Ed25519 data")]
Ed25519ParseError(#[from] Ed25519RecoveryError),

Expand Down
39 changes: 28 additions & 11 deletions validator-api/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,11 @@ pub struct CoconutSigner {
/// Specifies whether rewarding service is enabled in this process.
enabled: bool,

/// Path to the signing keypair
keypair_path: PathBuf,
/// Path to the coconut verification key.
verification_key_path: PathBuf,

/// Path to the coconut secret key.
secret_key_path: PathBuf,

/// Path to the dkg dealer decryption key.
decryption_key_path: PathBuf,
Expand All @@ -291,6 +294,16 @@ pub struct CoconutSigner {
impl CoconutSigner {
pub const DKG_DECRYPTION_KEY_FILE: &'static str = "dkg_decryption_key.pem";
pub const DKG_PUBLIC_KEY_WITH_PROOF_FILE: &'static str = "dkg_public_key_with_proof.pem";
pub const COCONUT_VERIFICATION_KEY_FILE: &'static str = "coconut_verification_key.pem";
pub const COCONUT_SECRET_KEY_FILE: &'static str = "coconut_secret_key.pem";

fn default_coconut_verification_key_path() -> PathBuf {
Config::default_data_directory(None).join(Self::COCONUT_VERIFICATION_KEY_FILE)
}

fn default_coconut_secret_key_path() -> PathBuf {
Config::default_data_directory(None).join(Self::COCONUT_SECRET_KEY_FILE)
}

fn default_dkg_decryption_key_path() -> PathBuf {
Config::default_data_directory(None).join(Self::DKG_DECRYPTION_KEY_FILE)
Expand All @@ -305,7 +318,8 @@ impl Default for CoconutSigner {
fn default() -> Self {
Self {
enabled: Default::default(),
keypair_path: Default::default(),
verification_key_path: CoconutSigner::default_coconut_verification_key_path(),
secret_key_path: CoconutSigner::default_coconut_secret_key_path(),
decryption_key_path: CoconutSigner::default_dkg_decryption_key_path(),
public_key_with_proof_path: CoconutSigner::default_dkg_public_key_with_proof_path(),
dkg_contract_polling_rate: DEFAULT_DKG_CONTRACT_POLLING_RATE,
Expand All @@ -325,6 +339,10 @@ impl Config {
Config::default_data_directory(Some(id)).join(NodeStatusAPI::DB_FILE);
self.network_monitor.credentials_database_path =
Config::default_data_directory(Some(id)).join(NetworkMonitor::DB_FILE);
self.coconut_signer.verification_key_path = Config::default_data_directory(Some(id))
.join(CoconutSigner::COCONUT_VERIFICATION_KEY_FILE);
self.coconut_signer.secret_key_path =
Config::default_data_directory(Some(id)).join(CoconutSigner::COCONUT_SECRET_KEY_FILE);
self.coconut_signer.decryption_key_path =
Config::default_data_directory(Some(id)).join(CoconutSigner::DKG_DECRYPTION_KEY_FILE);
self.coconut_signer.public_key_with_proof_path = Config::default_data_directory(Some(id))
Expand Down Expand Up @@ -368,12 +386,6 @@ impl Config {
self
}

#[cfg(feature = "coconut")]
pub fn with_keypair_path(mut self, keypair_path: PathBuf) -> Self {
self.coconut_signer.keypair_path = keypair_path;
self
}

#[cfg(feature = "coconut")]
pub fn with_custom_validator_apis(mut self, validator_api_urls: Vec<Url>) -> Self {
self.coconut_signer.all_validator_apis = validator_api_urls;
Expand Down Expand Up @@ -487,8 +499,13 @@ impl Config {
}

#[cfg(feature = "coconut")]
pub fn _keypair_path(&self) -> PathBuf {
self.coconut_signer.keypair_path.clone()
pub fn verification_key_path(&self) -> PathBuf {
self.coconut_signer.verification_key_path.clone()
}

#[cfg(feature = "coconut")]
pub fn secret_key_path(&self) -> PathBuf {
self.coconut_signer.secret_key_path.clone()
}

#[cfg(feature = "coconut")]
Expand Down
Loading

0 comments on commit 51e407b

Please sign in to comment.