Hang coconut issuance off the validator-api (#679)

* Hand coconut issuance off the validator-api

* git to cargo

* Move to own module

* Integrate tauri-client, extract common interface

* cargo fmt

* Ergonomics

* Facelift

* Wrap up tauri client

* Set up publish

* Fix fmt

* Install CI dependencies

* Inline deps

* Remove mac deps

* Add dist dir

* Fix beta clippy nag

* Commit some gateway work

* Thread coconut creds through gateway handshake

* Push in progress patch

* Move State from tauri client to coconut interface

* Move get_aggregated_signature from tauri client to coconut interface

* Move prove_credential from tauri client to coconut interface

* Update sphinx version

* Mount coconut routes and manage config file in rocket

* Split default validator endpoint into host and port

* Add init for simple credential initialization

* Fix common gateway client

* Add coconut cred to webassembly client

* Add coconut cred to socks5 client

* Add coconut cred to native client

* Remove direct coconut-rs dependency

* Use only coconut interface in validator api

* Leave validator-api out of workspace and update Cargo.lock

* Fix clippy warnings and update Cargo.lock after rebase

* Switch from attohttpc to reqwest for async gets

This is not only needed for using async requests, but also because attohttpc
causes OpenSSL issues when cross compiling the webassembly client.

* Replace attohttpc with reqwest for puts too

* Make tauri client commands async

* Fix borrow error

* Guard gateway server code from compiling for wasm (client)

* Fix clippy wasm client

* Fix tests

* Fix clippy in tauri client

* Remove commented code

* Update comment of init message

* Remove unnecessary hex dependency

* Replace config argument with key_pair

* Use `trim()` for whitespace removal

* Move verification key query higher up the function calls

* Put KeyPair instead of Config into rocket's managed items

* Re-enable tauri client verify button

* Move verification key up the function calls for prove_credential

* Use consts for verification_key and blind_sign routes of validator-api

* Replace `match` with `map_err`

* Fix typo

* Remove now unnecessary `Clone` derives

... as config is no longer managed by rocket

* Replace `match` with `map_err`

* Make `InternalSignRequest` really internal to validator-api

* Make `with_keypair` live up to its name

* Update Cargo.lock after rebase

* Replace String error with HandshakeError

* Add CoconutInterfaceError to coconut-interface

* Format the new error in tauri client

* Remove  from default, as wasm client doesn't build

* Put public key as init argument...

... for the public attributes of the credential

* Use the hash_to_scalar function to make public key into attribute

Use the function from cli-demo-rs from https://github.com/nymtech/coconut
to make the identity public key into a public attribute.

* Replace vector with array for InitMessage

As we know beforehand the size of the keys, we can use fixed size array
instead of vectors. This eliminates the need for a prefixed length in
the serialized form of the InitMessage structure and enables a easy
deserialization of the remote identity before the actual bincode
deserialization that we do in the handshake process.

Before this, the `extract_remote_identity_from_register_init` function
attempted to deserialize into a public key the length-prefixed public key
received from the client, thus failing sporadically with a `Cannot decompress
Edwards point` error.

* Pass public and private attributes to state `init` instead of PublicKey

* Make tauri call with dummy attributes

* Make clients call with their keypairs

* Revert "Make clients call with their keypairs"

This reverts commit b348f47f7a.

* Put dummy, bandwidth private attribute

Co-authored-by: Bogdan-Ștefan Neacșu <bogdan@nymtech.net>
This commit is contained in:
Drazen Urch
2021-08-13 13:27:40 +02:00
committed by GitHub
parent abb4537551
commit 28e55c6de6
133 changed files with 27169 additions and 153 deletions
+81
View File
@@ -0,0 +1,81 @@
use coconut_interface::{
elgamal::PublicKey, Attribute, BlindSignRequest, BlindSignRequestBody, BlindedSignature,
BlindedSignatureResponse, KeyPair, Parameters, VerificationKeyResponse,
};
use getset::{CopyGetters, Getters};
use rocket::fairing::AdHoc;
use rocket::serde::json::Json;
use rocket::State;
use validator_client::validator_api::VALIDATOR_API_CACHE_VERSION;
#[derive(Getters, CopyGetters, Debug)]
pub(crate) struct InternalSignRequest {
// Total number of parameters to generate for
#[getset(get_copy)]
total_params: u32,
#[getset(get)]
public_attributes: Vec<Attribute>,
#[getset(get)]
public_key: PublicKey,
#[getset(get)]
blind_sign_request: BlindSignRequest,
}
impl InternalSignRequest {
pub fn new(
total_params: u32,
public_attributes: Vec<Attribute>,
public_key: PublicKey,
blind_sign_request: BlindSignRequest,
) -> InternalSignRequest {
InternalSignRequest {
total_params,
public_attributes,
public_key,
blind_sign_request,
}
}
pub fn stage(key_pair: KeyPair) -> AdHoc {
AdHoc::on_ignite("Internal Sign Request Stage", |rocket| async {
rocket.manage(key_pair).mount(
VALIDATOR_API_CACHE_VERSION,
routes![post_blind_sign, get_verification_key],
)
})
}
}
fn blind_sign(request: InternalSignRequest, key_pair: &KeyPair) -> BlindedSignature {
let params = Parameters::new(request.total_params()).unwrap();
coconut_interface::blind_sign(
&params,
&key_pair.secret_key(),
request.public_key(),
request.blind_sign_request(),
request.public_attributes(),
)
.unwrap()
}
#[post("/blind_sign", data = "<blind_sign_request_body>")]
// Until we have serialization and deserialization traits we'll be using a crutch
pub async fn post_blind_sign(
blind_sign_request_body: Json<BlindSignRequestBody>,
key_pair: &State<KeyPair>,
) -> Json<BlindedSignatureResponse> {
debug!("{:?}", blind_sign_request_body);
let internal_request = InternalSignRequest::new(
*blind_sign_request_body.total_params(),
blind_sign_request_body.public_attributes(),
blind_sign_request_body.public_key().clone(),
blind_sign_request_body.blind_sign_request().clone(),
);
let blinded_signature = blind_sign(internal_request, key_pair);
Json(BlindedSignatureResponse::new(blinded_signature))
}
#[get("/verification_key")]
pub async fn get_verification_key(key_pair: &State<KeyPair>) -> Json<VerificationKeyResponse> {
Json(VerificationKeyResponse::new(key_pair.verification_key()))
}
+21 -1
View File
@@ -2,15 +2,23 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::config_template;
use coconut_interface::{Base58, KeyPair};
use config::defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use config::NymConfig;
use const_format::formatcp;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::time::Duration;
mod template;
const DEFAULT_VALIDATOR_REST_ENDPOINTS: &[&str] = &["http://localhost:1317"];
pub const DEFAULT_VALIDATOR_HOST: &str = "localhost";
const DEFAULT_VALIDATOR_PORT: &str = "1317";
const DEFAULT_VALIDATOR_REST_ENDPOINTS: &[&str] = &[formatcp!(
"http://{}:{}",
DEFAULT_VALIDATOR_HOST,
DEFAULT_VALIDATOR_PORT,
)];
const DEFAULT_GATEWAY_SENDING_RATE: usize = 500;
const DEFAULT_MAX_CONCURRENT_GATEWAY_CLIENTS: usize = 50;
@@ -72,6 +80,8 @@ pub struct Base {
/// Address of the validator contract managing the network
mixnet_contract_address: String,
// Avoid breaking derives for now
keypair_bs58: String,
}
impl Default for Base {
@@ -82,6 +92,7 @@ impl Default for Base {
.map(|&endpoint| endpoint.to_string())
.collect(),
mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(),
keypair_bs58: String::default(),
}
}
}
@@ -202,6 +213,10 @@ impl Config {
Config::default()
}
pub fn keypair(&self) -> KeyPair {
KeyPair::try_from_bs58(self.base.keypair_bs58.clone()).unwrap()
}
pub fn enabled_network_monitor(mut self, enabled: bool) -> Self {
self.network_monitor.enabled = enabled;
self
@@ -232,6 +247,11 @@ impl Config {
self
}
pub fn with_keypair(mut self, keypair_bs58: String) -> Self {
self.base.keypair_bs58 = keypair_bs58;
self
}
pub fn get_network_monitor_enabled(&self) -> bool {
self.network_monitor.enabled
}
+16 -2
View File
@@ -13,6 +13,7 @@ use ::config::NymConfig;
use anyhow::Result;
use cache::ValidatorCache;
use clap::{App, Arg, ArgMatches};
use coconut::InternalSignRequest;
use log::info;
use rocket::http::Method;
use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors};
@@ -20,6 +21,7 @@ use std::process;
use validator_client::validator_api::VALIDATOR_API_PORT;
pub(crate) mod cache;
mod coconut;
pub(crate) mod config;
mod network_monitor;
mod node_status_api;
@@ -32,6 +34,7 @@ const VALIDATORS_ARG: &str = "validators";
const DETAILED_REPORT_ARG: &str = "detailed-report";
const MIXNET_CONTRACT_ARG: &str = "mixnet-contract";
const WRITE_CONFIG_ARG: &str = "save-config";
const KEYPAIR_ARG: &str = "keypair";
pub(crate) const PENALISE_OUTDATED: bool = false;
@@ -47,7 +50,6 @@ fn parse_args<'a>() -> ArgMatches<'a> {
Arg::with_name(V4_TOPOLOGY_ARG)
.help("location of .json file containing IPv4 'good' network topology")
.long(V4_TOPOLOGY_ARG)
.takes_value(true)
)
.arg(
Arg::with_name(V6_TOPOLOGY_ARG)
@@ -76,6 +78,10 @@ fn parse_args<'a>() -> ArgMatches<'a> {
.help("specifies whether a config file based on provided arguments should be saved to a file")
.long(WRITE_CONFIG_ARG)
)
.arg(Arg::with_name(KEYPAIR_ARG)
.help("Path to the secret key file")
.takes_value(true)
.long(KEYPAIR_ARG))
.get_matches()
}
@@ -140,6 +146,13 @@ fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
if matches.is_present(DETAILED_REPORT_ARG) {
config = config.detailed_network_monitor_report(true)
}
if let Some(keypair_path) = matches.value_of(KEYPAIR_ARG) {
let keypair_bs58 = std::fs::read_to_string(keypair_path)
.unwrap()
.trim()
.to_string();
config = config.with_keypair(keypair_bs58)
}
if matches.is_present(WRITE_CONFIG_ARG) {
info!("Saving the configuration to a file");
@@ -202,7 +215,8 @@ async fn main() -> Result<()> {
};
let rocket = rocket::custom(rocket_config)
.attach(setup_cors()?)
.attach(ValidatorCache::stage());
.attach(ValidatorCache::stage())
.attach(InternalSignRequest::stage(config.keypair()));
// see if we should start up network monitor and ignite our rocket
let rocket = if config.get_network_monitor_enabled() {
@@ -1,7 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::DEFAULT_VALIDATOR_HOST;
use crate::network_monitor::monitor::receiver::{GatewayClientUpdate, GatewayClientUpdateSender};
use coconut_interface::Credential;
use crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH};
use futures::channel::mpsc;
use futures::stream::{self, FuturesUnordered, StreamExt};
@@ -20,6 +22,7 @@ use std::sync::Arc;
use std::task::Poll;
use std::time::Duration;
use tokio::time::Instant;
use validator_client::validator_api::VALIDATOR_API_PORT;
const TIME_CHUNK_SIZE: Duration = Duration::from_millis(50);
@@ -99,7 +102,7 @@ impl PacketSender {
}
}
fn new_gateway_client(
async fn new_gateway_client(
address: String,
identity: identity::PublicKey,
fresh_gateway_client_data: &FreshGatewayClientData,
@@ -110,6 +113,17 @@ impl PacketSender {
// TODO: future optimization: if we're remaking client for a gateway to which we used to be connected in the past,
// use old shared keys
let (message_sender, message_receiver) = mpsc::unbounded();
let coconut_credential = Credential::init(
vec![format!(
"http://{}:{}",
DEFAULT_VALIDATOR_HOST, VALIDATOR_API_PORT
)],
identity,
)
.await
.expect("Could not initialize coconut credential");
// currently we do not care about acks at all, but we must keep the channel alive
// so that the gateway client would not crash
let (ack_sender, ack_receiver) = mpsc::unbounded();
@@ -122,6 +136,7 @@ impl PacketSender {
message_sender,
ack_sender,
fresh_gateway_client_data.gateway_response_timeout,
coconut_credential,
),
(message_receiver, ack_receiver),
)
@@ -206,7 +221,8 @@ impl PacketSender {
packets.clients_address,
packets.pub_key,
&fresh_gateway_client_data,
);
)
.await;
// Put this in timeout in case the gateway has incorrectly set their ulimit and our connection
// gets stuck in their TCP queue and just hangs on our end but does not terminate