Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Porting existing Fog Ledger tests to Router #3138

Merged
merged 8 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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