Skip to content

Commit

Permalink
Porting existing Fog Ledger tests to Router (#3138)
Browse files Browse the repository at this point in the history
* Port existing tests to router server binary, except key image

* Port key image test in router_connection.rs to streaming API

* Fixups from rebase

* remove logging statements

* All Ledger tests now use portpicker to select ports

* Fog router support for the unary API (#3123)

* Cargo fmt

* Ensure unary key image service gets started for router server.

* Improving comment clarity.

* Apply suggestions removing unnecessary comments

Co-authored-by: Nick Santana <nick@mobilecoin.com>

---------

Co-authored-by: Nick Santana <nick@mobilecoin.com>

* Apply suggestions from code review

Co-authored-by: Nick Santana <nick@mobilecoin.com>

* Pull request feedback

---------

Co-authored-by: Emily C <gyrocoder@gmail.com>
Co-authored-by: Nick Santana <nick@mobilecoin.com>
  • Loading branch information
3 people authored Feb 27, 2023
1 parent 2d7d2f7 commit 8318791
Show file tree
Hide file tree
Showing 19 changed files with 1,456 additions and 264 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

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

8 changes: 8 additions & 0 deletions fog/ledger/connection/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ license = "GPL-3.0"
[dependencies]
# mobilecoin
mc-api = { path = "../../../api" }
mc-attest-ake = { path = "../../../attest/ake" }
mc-attest-core = { path = "../../../attest/core" }
mc-attest-verifier = { path = "../../../attest/verifier" }
mc-blockchain-types = { path = "../../../blockchain/types" }
mc-common = { path = "../../../common", features = ["log"] }
mc-crypto-keys = { path = "../../../crypto/keys" }
mc-crypto-noise = { path = "../../../crypto/noise" }
mc-crypto-rand = { path = "../../../crypto/rand" }
mc-transaction-core = { path = "../../../transaction/core" }
mc-util-grpc = { path = "../../../util/grpc" }
mc-util-serial = { path = "../../../util/serial" }
mc-util-uri = { path = "../../../util/uri" }

# fog
Expand All @@ -23,10 +28,13 @@ mc-fog-types = { path = "../../types" }
mc-fog-uri = { path = "../../uri" }

# third-party
aes-gcm = "0.10.1"
displaydoc = { version = "0.2", default-features = false }
futures = "0.3"
grpcio = "0.12.0"
protobuf = "2.27.1"
retry = "2.0"
sha2 = { version = "0.10", default-features = false }

[dev-dependencies]
mc-common = { path = "../../../common", features = ["loggers"] }
3 changes: 3 additions & 0 deletions fog/ledger/connection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ pub use merkle_proof::{FogMerkleProofGrpcClient, OutputError, OutputResultExtens

mod untrusted;
pub use untrusted::FogUntrustedLedgerGrpcClient;

mod router_client;
pub use router_client::LedgerGrpcClient;
259 changes: 259 additions & 0 deletions fog/ledger/connection/src/router_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// Copyright (c) 2023 The MobileCoin Foundation

use aes_gcm::Aes256Gcm;
use futures::{executor::block_on, SinkExt, TryStreamExt};
use grpcio::{ChannelBuilder, ClientDuplexReceiver, ClientDuplexSender, Environment};
use mc_attest_ake::{
AuthResponseInput, ClientInitiate, Error as AttestAkeError, Ready, Start, Transition,
};
use mc_attest_core::VerificationReport;
use mc_attest_verifier::Verifier;
use mc_common::logger::{log, o, Logger};
use mc_crypto_keys::X25519;
use mc_crypto_noise::CipherError;
use mc_crypto_rand::McRng;
use mc_fog_api::{
attest::{AuthMessage, Message},
ledger::{LedgerRequest, LedgerResponse},
ledger_grpc::LedgerApiClient,
};
use mc_fog_types::ledger::{CheckKeyImagesRequest, CheckKeyImagesResponse, KeyImageQuery};
use mc_fog_uri::FogLedgerUri;
use mc_transaction_core::ring_signature::KeyImage;
use mc_util_grpc::ConnectionUriGrpcioChannel;
use mc_util_serial::DecodeError;
use mc_util_uri::{ConnectionUri, UriConversionError};
use sha2::Sha512;
use std::sync::Arc;

/// A high-level object mediating requests to the fog ledger router service
pub struct LedgerGrpcClient {
/// A logger object
logger: Logger,

/// The URI of the router to communicate with
uri: FogLedgerUri,

/// An object which can verify a fog node's provided IAS report
verifier: Verifier,

/// The AKE state machine object, if one is available.
attest_cipher: Option<Ready<Aes256Gcm>>,

/// Sends requests to the fog ledger router
request_sender: ClientDuplexSender<LedgerRequest>,

/// Receives responses from the fog ledger router
response_receiver: ClientDuplexReceiver<LedgerResponse>,

/// Low-lever ledger API client
_client: LedgerApiClient,
}

impl LedgerGrpcClient {
/// Creates a new fog ledger router grpc client and opens a streaming
/// connection to the fog ledger router service.
///
/// Arguments:
/// * uri: The Uri to connect to
/// * verifier: The attestation verifier
/// * env: A grpc environment (thread pool) to use for this connection
/// * logger: For logging
pub fn new(
uri: FogLedgerUri,
verifier: Verifier,
env: Arc<Environment>,
logger: Logger,
) -> Self {
let logger = logger.new(o!("mc.fog.ledger.router.uri" => uri.to_string()));

let ch = ChannelBuilder::default_channel_builder(env).connect_to_uri(&uri, &logger);
let client = LedgerApiClient::new(ch);
let (request_sender, response_receiver) = client
.request()
.expect("Could not retrieve grpc sender and receiver.");

Self {
logger,
attest_cipher: None,
_client: client,
request_sender,
response_receiver,
uri,
verifier,
}
}

fn is_attested(&self) -> bool {
self.attest_cipher.is_some()
}

async fn attest(&mut self) -> Result<VerificationReport, Error> {
// If we have an existing attestation, nuke it.
self.deattest();

let mut csprng = McRng::default();

let initiator = Start::new(self.uri.responder_id()?.to_string());

let init_input = ClientInitiate::<X25519, Aes256Gcm, Sha512>::default();
let (initiator, auth_request_output) = initiator.try_next(&mut csprng, init_input)?;

let attested_message: AuthMessage = auth_request_output.into();
let mut request = LedgerRequest::new();
request.set_auth(attested_message);
self.request_sender
.send((request.clone(), grpcio::WriteFlags::default()))
.await?;

let mut response = self
.response_receiver
.try_next()
.await?
.ok_or(Error::ResponseNotReceived)?;
let auth_response_msg = response.take_auth();

// Process server response, check if key exchange is successful
let auth_response_event =
AuthResponseInput::new(auth_response_msg.into(), self.verifier.clone());
let (initiator, verification_report) =
initiator.try_next(&mut csprng, auth_response_event)?;

self.attest_cipher = Some(initiator);

Ok(verification_report)
}

fn deattest(&mut self) {
if self.is_attested() {
log::trace!(self.logger, "Tearing down existing attested connection.");
self.attest_cipher = None;
}
}

/// Check one or more key images against the ledger router service
pub async fn check_key_images(
&mut self,
key_images: &[KeyImage],
) -> Result<CheckKeyImagesResponse, Error> {
log::trace!(self.logger, "Check key images was called");
if !self.is_attested() {
let verification_report = self.attest().await;
verification_report?;
}

let key_images_queries = key_images
.iter()
.map(|&key_image| KeyImageQuery {
key_image,
start_block: 0,
})
.collect();
let key_images_request = CheckKeyImagesRequest {
queries: key_images_queries,
};

// No authenticated data associated with ledger query
let aad = vec![];

let msg = {
let attest_cipher = self
.attest_cipher
.as_mut()
.expect("no enclave_connection even though attest succeeded");

let mut msg = Message::new();
msg.set_channel_id(Vec::from(attest_cipher.binding()));
msg.set_aad(aad.clone());

let plaintext_bytes = mc_util_serial::encode(&key_images_request);

let request_ciphertext = attest_cipher.encrypt(&aad, &plaintext_bytes)?;
msg.set_data(request_ciphertext);
msg
};
let mut request = LedgerRequest::new();
request.set_check_key_images(msg);

self.request_sender
.send((request.clone(), grpcio::WriteFlags::default()))
.await?;

let message = self
.response_receiver
.try_next()
.await?
.ok_or(Error::ResponseNotReceived)?
.take_check_key_image_response();

{
let attest_cipher = self
.attest_cipher
.as_mut()
.expect("no enclave_connection even though attest succeeded");

let plaintext_bytes = attest_cipher.decrypt(message.get_aad(), message.get_data())?;
let plaintext_response: CheckKeyImagesResponse =
mc_util_serial::decode(&plaintext_bytes)?;
Ok(plaintext_response)
}
}
}

impl Drop for LedgerGrpcClient {
fn drop(&mut self) {
block_on(self.request_sender.close()).expect("Couldn't close the router request sender");
}
}

/// Errors related to the Fog View Router Client.
#[derive(Debug)]
pub enum Error {
/// Decode errors.
Decode(DecodeError),

/// Uri conversion errors.
UriConversion(UriConversionError),

/// Cipher errors.
Cipher(CipherError),

/// Attestation errors.
Attestation(AttestAkeError),

/// Grpc errors.
Grpc(grpcio::Error),

/// Response not received
ResponseNotReceived,
}

impl From<DecodeError> for Error {
fn from(err: DecodeError) -> Self {
Self::Decode(err)
}
}

impl From<CipherError> for Error {
fn from(err: CipherError) -> Self {
Self::Cipher(err)
}
}

impl From<grpcio::Error> for Error {
fn from(err: grpcio::Error) -> Self {
Self::Grpc(err)
}
}

impl From<UriConversionError> for Error {
fn from(err: UriConversionError) -> Self {
Self::UriConversion(err)
}
}

impl From<AttestAkeError> for Error {
fn from(err: AttestAkeError) -> Self {
Self::Attestation(err)
}
}
2 changes: 1 addition & 1 deletion fog/ledger/enclave/impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ where
&self,
auth_request: NonceAuthRequest,
) -> Result<(NonceAuthResponse, NonceSession)> {
self.ake.frontend_accept(auth_request).map_err(|e| e.into())
Ok(self.ake.frontend_accept(auth_request)?)
}
}

Expand Down
4 changes: 4 additions & 0 deletions fog/ledger/server/src/bin/key_image_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use clap::Parser;
use grpcio::{RpcStatus, RpcStatusCode};
use mc_attest_net::{Client, RaClient};
use mc_common::{logger::log, time::SystemTimeProvider};
use mc_fog_ledger_enclave::{LedgerSgxEnclave, ENCLAVE_FILE};
use mc_fog_ledger_server::{KeyImageStoreServer, LedgerStoreConfig, ShardingStrategy};
Expand Down Expand Up @@ -40,10 +41,13 @@ fn main() {
let watcher =
WatcherDB::open_ro(&config.watcher_db, logger.clone()).expect("Could not open watcher DB");

let ias_client = Client::new(&config.ias_api_key).expect("Could not create IAS client");

let mut store_server = match config.sharding_strategy.clone() {
ShardingStrategy::Epoch(sharding_strategy) => KeyImageStoreServer::new_from_config(
config.clone(),
enclave,
ias_client,
db,
watcher,
sharding_strategy,
Expand Down
3 changes: 3 additions & 0 deletions fog/ledger/server/src/bin/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{

use clap::Parser;
use grpcio::ChannelBuilder;
use mc_attest_net::{Client, RaClient};
use mc_common::logger::log;
use mc_fog_api::ledger_grpc::KeyImageStoreApiClient;
use mc_fog_ledger_enclave::{LedgerSgxEnclave, ENCLAVE_FILE};
Expand Down Expand Up @@ -83,9 +84,11 @@ fn main() {
let watcher_db =
WatcherDB::open_ro(&config.watcher_db, logger.clone()).expect("Could not open watcher DB");

let ias_client = Client::new(&config.ias_api_key).expect("Could not create IAS client");
let mut router_server = LedgerRouterServer::new(
config,
enclave,
ias_client,
ledger_store_grpc_clients,
ledger_db,
watcher_db,
Expand Down
8 changes: 8 additions & 0 deletions fog/ledger/server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ pub struct LedgerRouterConfig {
#[clap(long, env = "MC_CLIENT_RESPONDER_ID")]
pub client_responder_id: ResponderId,

/// PEM-formatted keypair to send with an Attestation Request.
#[clap(long, env = "MC_IAS_API_KEY")]
pub ias_api_key: String,

/// The IAS SPID to use when getting a quote
#[clap(long, env = "MC_IAS_SPID")]
pub ias_spid: ProviderId,

/// gRPC listening URI for client requests.
#[clap(long, env = "MC_CLIENT_LISTEN_URI")]
pub client_listen_uri: FogLedgerUri,
Expand Down
Loading

0 comments on commit 8318791

Please sign in to comment.