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:
@@ -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(
|
||||
¶ms,
|
||||
&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()))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user