mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-20 03:52:17 +02:00
svr3: Implement restore with fallback function
This commit is contained in:
parent
4694d0a1a8
commit
c870e23a5b
@ -7,13 +7,13 @@ use std::convert::TryInto as _;
|
|||||||
use std::num::{NonZeroU16, NonZeroU32};
|
use std::num::{NonZeroU16, NonZeroU32};
|
||||||
|
|
||||||
use base64::prelude::{Engine, BASE64_STANDARD};
|
use base64::prelude::{Engine, BASE64_STANDARD};
|
||||||
use libsignal_bridge_macros::{bridge_fn, bridge_io};
|
|
||||||
use libsignal_bridge_types::net::svr3_connect;
|
|
||||||
use libsignal_net::auth::Auth;
|
|
||||||
use libsignal_net::env::Svr3Env;
|
|
||||||
use libsignal_net::svr3::{self, OpaqueMaskedShareSet, PpssOps as _};
|
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
|
use libsignal_bridge_macros::{bridge_fn, bridge_io};
|
||||||
|
use libsignal_bridge_types::net::Svr3Client;
|
||||||
|
use libsignal_net::auth::Auth;
|
||||||
|
use libsignal_net::svr3::{self, OpaqueMaskedShareSet, Svr3Client as _};
|
||||||
|
|
||||||
pub use libsignal_bridge_types::net::{ConnectionManager, Environment, TokioAsyncContext};
|
pub use libsignal_bridge_types::net::{ConnectionManager, Environment, TokioAsyncContext};
|
||||||
|
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
@ -80,15 +80,10 @@ async fn Svr3Backup(
|
|||||||
.try_into()
|
.try_into()
|
||||||
.expect("can only backup 32 bytes");
|
.expect("can only backup 32 bytes");
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let connections = svr3_connect(connection_manager, username, enclave_password).await?;
|
let client = Svr3Client::new(connection_manager, username, enclave_password);
|
||||||
let share_set = Svr3Env::backup(
|
let share_set = client
|
||||||
connections,
|
.backup(&password, secret, max_tries.into_inner(), &mut rng)
|
||||||
&password,
|
.await?;
|
||||||
secret,
|
|
||||||
max_tries.into_inner(),
|
|
||||||
&mut rng,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(share_set.serialize().expect("can serialize the share set"))
|
Ok(share_set.serialize().expect("can serialize the share set"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +97,8 @@ async fn Svr3Restore(
|
|||||||
) -> Result<Vec<u8>, svr3::Error> {
|
) -> Result<Vec<u8>, svr3::Error> {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let share_set = OpaqueMaskedShareSet::deserialize(&share_set)?;
|
let share_set = OpaqueMaskedShareSet::deserialize(&share_set)?;
|
||||||
let connections = svr3_connect(connection_manager, username, enclave_password).await?;
|
let client = Svr3Client::new(connection_manager, username, enclave_password);
|
||||||
let restored_secret = Svr3Env::restore(connections, &password, share_set, &mut rng).await?;
|
let restored_secret = client.restore(&password, share_set, &mut rng).await?;
|
||||||
Ok(restored_secret.serialize())
|
Ok(restored_secret.serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +108,8 @@ async fn Svr3Remove(
|
|||||||
username: String, // hex-encoded uid
|
username: String, // hex-encoded uid
|
||||||
enclave_password: String, // timestamp:otp(...)
|
enclave_password: String, // timestamp:otp(...)
|
||||||
) -> Result<(), svr3::Error> {
|
) -> Result<(), svr3::Error> {
|
||||||
let connections = svr3_connect(connection_manager, username, enclave_password).await?;
|
let client = Svr3Client::new(connection_manager, username, enclave_password);
|
||||||
Svr3Env::remove(connections).await?;
|
client.remove().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
use std::num::NonZeroU16;
|
use std::num::NonZeroU16;
|
||||||
use std::panic::RefUnwindSafe;
|
use std::panic::RefUnwindSafe;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use http::uri::PathAndQuery;
|
use http::uri::PathAndQuery;
|
||||||
|
|
||||||
use libsignal_net::auth::Auth;
|
use libsignal_net::auth::Auth;
|
||||||
use libsignal_net::enclave::{
|
use libsignal_net::enclave::{
|
||||||
Cdsi, EnclaveEndpoint, EnclaveEndpointConnection, EnclaveKind, Nitro, PpssSetup, Sgx, Tpm2Snp,
|
Cdsi, EnclaveEndpoint, EnclaveEndpointConnection, EnclaveKind, Nitro, PpssSetup, Sgx, Tpm2Snp,
|
||||||
};
|
};
|
||||||
use libsignal_net::env;
|
|
||||||
use libsignal_net::env::{add_user_agent_header, Env, Svr3Env};
|
use libsignal_net::env::{add_user_agent_header, Env, Svr3Env};
|
||||||
use libsignal_net::infra::connection_manager::MultiRouteConnectionManager;
|
use libsignal_net::infra::connection_manager::MultiRouteConnectionManager;
|
||||||
use libsignal_net::infra::dns::DnsResolver;
|
use libsignal_net::infra::dns::DnsResolver;
|
||||||
@ -20,7 +21,8 @@ use libsignal_net::infra::tcp_ssl::{
|
|||||||
TcpSslConnector, TcpSslConnectorStream,
|
TcpSslConnector, TcpSslConnectorStream,
|
||||||
};
|
};
|
||||||
use libsignal_net::infra::{make_ws_config, EndpointConnection};
|
use libsignal_net::infra::{make_ws_config, EndpointConnection};
|
||||||
use libsignal_net::svr::{self, SvrConnection};
|
use libsignal_net::svr::SvrConnection;
|
||||||
|
use libsignal_net::svr3::Svr3Connect;
|
||||||
use libsignal_net::timeouts::ONE_ROUTE_CONNECTION_TIMEOUT;
|
use libsignal_net::timeouts::ONE_ROUTE_CONNECTION_TIMEOUT;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
@ -68,7 +70,8 @@ impl ConnectionManager {
|
|||||||
DnsResolver::new_with_static_fallback(environment.env().static_fallback());
|
DnsResolver::new_with_static_fallback(environment.env().static_fallback());
|
||||||
let transport_connector =
|
let transport_connector =
|
||||||
std::sync::Mutex::new(TcpSslDirectConnector::new(dns_resolver).into());
|
std::sync::Mutex::new(TcpSslDirectConnector::new(dns_resolver).into());
|
||||||
let chat_endpoint = PathAndQuery::from_static(env::constants::WEB_SOCKET_PATH);
|
let chat_endpoint =
|
||||||
|
PathAndQuery::from_static(libsignal_net::env::constants::WEB_SOCKET_PATH);
|
||||||
let chat_connection_params = environment
|
let chat_connection_params = environment
|
||||||
.env()
|
.env()
|
||||||
.chat_domain_config
|
.chat_domain_config
|
||||||
@ -150,23 +153,48 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
bridge_as_handle!(ConnectionManager);
|
bridge_as_handle!(ConnectionManager);
|
||||||
|
|
||||||
pub async fn svr3_connect<'a>(
|
pub struct Svr3Client<'a> {
|
||||||
connection_manager: &ConnectionManager,
|
connection_manager: &'a ConnectionManager,
|
||||||
username: String,
|
auth: Auth,
|
||||||
password: String,
|
}
|
||||||
) -> Result<<Svr3Env<'a> as PpssSetup<TcpSslConnectorStream>>::Connections, svr::Error> {
|
|
||||||
let auth = Auth { username, password };
|
impl<'a> Svr3Client<'a> {
|
||||||
let ConnectionManager {
|
pub fn new(
|
||||||
chat: _chat,
|
connection_manager: &'a ConnectionManager,
|
||||||
cdsi: _cdsi,
|
username: String,
|
||||||
svr3: (sgx, nitro, tpm2snp),
|
password: String,
|
||||||
transport_connector,
|
) -> Self {
|
||||||
} = connection_manager;
|
Svr3Client {
|
||||||
let transport_connector = transport_connector.lock().expect("not poisoned").clone();
|
connection_manager,
|
||||||
let sgx = SvrConnection::connect(auth.clone(), sgx, transport_connector.clone()).await?;
|
auth: Auth { username, password },
|
||||||
let nitro = SvrConnection::connect(auth.clone(), nitro, transport_connector.clone()).await?;
|
}
|
||||||
let tpm2snp = SvrConnection::connect(auth, tpm2snp, transport_connector).await?;
|
}
|
||||||
Ok((sgx, nitro, tpm2snp))
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<'a> Svr3Connect for Svr3Client<'a> {
|
||||||
|
type Stream = TcpSslConnectorStream;
|
||||||
|
type Env = Svr3Env<'static>;
|
||||||
|
|
||||||
|
async fn connect(
|
||||||
|
&self,
|
||||||
|
) -> Result<<Self::Env as PpssSetup<Self::Stream>>::Connections, libsignal_net::enclave::Error>
|
||||||
|
{
|
||||||
|
let ConnectionManager {
|
||||||
|
chat: _chat,
|
||||||
|
cdsi: _cdsi,
|
||||||
|
svr3: (sgx, nitro, tpm2snp),
|
||||||
|
transport_connector,
|
||||||
|
} = &self.connection_manager;
|
||||||
|
let transport_connector = transport_connector.lock().expect("not poisoned").clone();
|
||||||
|
let sgx =
|
||||||
|
SvrConnection::connect(self.auth.clone(), sgx, transport_connector.clone()).await?;
|
||||||
|
let nitro =
|
||||||
|
SvrConnection::connect(self.auth.clone(), nitro, transport_connector.clone()).await?;
|
||||||
|
let tpm2snp =
|
||||||
|
SvrConnection::connect(self.auth.clone(), tpm2snp, transport_connector).await?;
|
||||||
|
Ok((sgx, nitro, tpm2snp))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -8,22 +8,23 @@
|
|||||||
//! as well as the password that will be used to protect the data being stored. Since the
|
//! as well as the password that will be used to protect the data being stored. Since the
|
||||||
//! actual stored secret data needs to be exactly 32 bytes long, it is generated randomly
|
//! actual stored secret data needs to be exactly 32 bytes long, it is generated randomly
|
||||||
//! at each invocation instead of being passed via the command line.
|
//! at each invocation instead of being passed via the command line.
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
|
use async_trait::async_trait;
|
||||||
use base64::prelude::{Engine, BASE64_STANDARD};
|
use base64::prelude::{Engine, BASE64_STANDARD};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize as _;
|
use colored::Colorize as _;
|
||||||
use libsignal_net::infra::dns::DnsResolver;
|
|
||||||
use nonzero_ext::nonzero;
|
use nonzero_ext::nonzero;
|
||||||
use rand_core::{CryptoRngCore, OsRng, RngCore};
|
use rand_core::{CryptoRngCore, OsRng, RngCore};
|
||||||
|
|
||||||
use libsignal_net::auth::Auth;
|
use libsignal_net::auth::Auth;
|
||||||
use libsignal_net::enclave::{EnclaveEndpointConnection, Nitro, Sgx, Tpm2Snp};
|
use libsignal_net::enclave::PpssSetup;
|
||||||
use libsignal_net::env::Svr3Env;
|
use libsignal_net::env::Svr3Env;
|
||||||
use libsignal_net::infra::tcp_ssl::DirectConnector as TcpSslTransportConnector;
|
use libsignal_net::infra::tcp_ssl::DirectConnector;
|
||||||
use libsignal_net::svr::SvrConnection;
|
use libsignal_net::infra::TransportConnector;
|
||||||
use libsignal_net::svr3::{Error, OpaqueMaskedShareSet, PpssOps};
|
use libsignal_net::svr3::{
|
||||||
|
simple_svr3_connect, Error, OpaqueMaskedShareSet, Svr3Client as _, Svr3Connect,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Args {
|
struct Args {
|
||||||
@ -34,6 +35,26 @@ struct Args {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Svr3Client {
|
||||||
|
env: Svr3Env<'static>,
|
||||||
|
auth: Auth,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stream = <DirectConnector as TransportConnector>::Stream;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Svr3Connect for Svr3Client {
|
||||||
|
type Stream = Stream;
|
||||||
|
type Env = Svr3Env<'static>;
|
||||||
|
|
||||||
|
async fn connect(
|
||||||
|
&self,
|
||||||
|
) -> Result<<Svr3Env as PpssSetup<Stream>>::Connections, libsignal_net::enclave::Error> {
|
||||||
|
simple_svr3_connect(&self.env, &self.auth).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
init_logger();
|
init_logger();
|
||||||
@ -49,36 +70,16 @@ async fn main() {
|
|||||||
|
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
let env = libsignal_net::env::STAGING.svr3;
|
|
||||||
|
|
||||||
let uid = {
|
let uid = {
|
||||||
let mut bytes = [0u8; 16];
|
let mut bytes = [0u8; 16];
|
||||||
rng.fill_bytes(&mut bytes[..]);
|
rng.fill_bytes(&mut bytes[..]);
|
||||||
bytes
|
bytes
|
||||||
};
|
};
|
||||||
let auth = Auth::from_uid_and_secret(uid, enclave_secret);
|
|
||||||
|
|
||||||
let connect = || async {
|
let client = {
|
||||||
let connector = TcpSslTransportConnector::new(DnsResolver::default());
|
let env = libsignal_net::env::STAGING.svr3;
|
||||||
let connection_a = EnclaveEndpointConnection::new_multi(
|
let auth = Auth::from_uid_and_secret(uid, enclave_secret);
|
||||||
env.sgx(),
|
Svr3Client { env, auth }
|
||||||
env.sgx().domain_config.connection_params_with_fallback(),
|
|
||||||
Duration::from_secs(10),
|
|
||||||
);
|
|
||||||
let a = SvrConnection::<Sgx, _>::connect(auth.clone(), &connection_a, connector.clone())
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect to SGX");
|
|
||||||
|
|
||||||
let connection_b = EnclaveEndpointConnection::new(env.nitro(), Duration::from_secs(10));
|
|
||||||
let b = SvrConnection::<Nitro, _>::connect(auth.clone(), &connection_b, connector.clone())
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect to Nitro");
|
|
||||||
|
|
||||||
let connection_c = EnclaveEndpointConnection::new(env.tpm2snp(), Duration::from_secs(10));
|
|
||||||
let c = SvrConnection::<Tpm2Snp, _>::connect(auth.clone(), &connection_c, connector)
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect to Tpm2Snp");
|
|
||||||
(a, b, c)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let secret = make_secret(&mut rng);
|
let secret = make_secret(&mut rng);
|
||||||
@ -86,10 +87,10 @@ async fn main() {
|
|||||||
let tries = nonzero!(10u32);
|
let tries = nonzero!(10u32);
|
||||||
|
|
||||||
let share_set_bytes = {
|
let share_set_bytes = {
|
||||||
let opaque_share_set =
|
let opaque_share_set = client
|
||||||
Svr3Env::backup(connect().await, &args.password, secret, tries, &mut rng)
|
.backup(&args.password, secret, tries, &mut rng)
|
||||||
.await
|
.await
|
||||||
.expect("can multi backup");
|
.expect("can multi backup");
|
||||||
opaque_share_set.serialize().expect("can serialize")
|
opaque_share_set.serialize().expect("can serialize")
|
||||||
};
|
};
|
||||||
println!("{}: {}", "Share set".cyan(), hex::encode(&share_set_bytes));
|
println!("{}: {}", "Share set".cyan(), hex::encode(&share_set_bytes));
|
||||||
@ -97,7 +98,8 @@ async fn main() {
|
|||||||
let restored = {
|
let restored = {
|
||||||
let opaque_share_set =
|
let opaque_share_set =
|
||||||
OpaqueMaskedShareSet::deserialize(&share_set_bytes).expect("can deserialize");
|
OpaqueMaskedShareSet::deserialize(&share_set_bytes).expect("can deserialize");
|
||||||
Svr3Env::restore(connect().await, &args.password, opaque_share_set, &mut rng)
|
client
|
||||||
|
.restore(&args.password, opaque_share_set, &mut rng)
|
||||||
.await
|
.await
|
||||||
.expect("can mutli restore")
|
.expect("can mutli restore")
|
||||||
};
|
};
|
||||||
@ -112,17 +114,18 @@ async fn main() {
|
|||||||
println!("{}: {}", "Tries remaining".cyan(), restored.tries_remaining);
|
println!("{}: {}", "Tries remaining".cyan(), restored.tries_remaining);
|
||||||
|
|
||||||
println!("{}...", "Querying...".cyan());
|
println!("{}...", "Querying...".cyan());
|
||||||
let query_result = Svr3Env::query(connect().await).await.expect("can query");
|
let query_result = client.query().await.expect("can query");
|
||||||
println!("{}: {}", "Tries remaining".cyan(), query_result);
|
println!("{}: {}", "Tries remaining".cyan(), query_result);
|
||||||
|
|
||||||
println!("{}...", "Removing the secret".cyan());
|
println!("{}...", "Removing the secret".cyan());
|
||||||
Svr3Env::remove(connect().await).await.expect("can remove");
|
client.remove().await.expect("can remove");
|
||||||
// The next attempt to restore should fail
|
// The next attempt to restore should fail
|
||||||
{
|
{
|
||||||
let opaque_share_set =
|
let opaque_share_set =
|
||||||
OpaqueMaskedShareSet::deserialize(&share_set_bytes).expect("can deserialize");
|
OpaqueMaskedShareSet::deserialize(&share_set_bytes).expect("can deserialize");
|
||||||
let failed_restore_result =
|
let failed_restore_result = client
|
||||||
Svr3Env::restore(connect().await, &args.password, opaque_share_set, &mut rng).await;
|
.restore(&args.password, opaque_share_set, &mut rng)
|
||||||
|
.await;
|
||||||
assert_matches!(failed_restore_result, Err(Error::DataMissing));
|
assert_matches!(failed_restore_result, Err(Error::DataMissing));
|
||||||
}
|
}
|
||||||
println!("{}.", "Done".green());
|
println!("{}.", "Done".green());
|
||||||
|
@ -12,24 +12,26 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use attest::svr2::RaftConfig;
|
||||||
use base64::prelude::{Engine, BASE64_STANDARD};
|
use base64::prelude::{Engine, BASE64_STANDARD};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use libsignal_net::infra::dns::DnsResolver;
|
|
||||||
use nonzero_ext::nonzero;
|
use nonzero_ext::nonzero;
|
||||||
use rand_core::{CryptoRngCore, OsRng, RngCore};
|
use rand_core::{CryptoRngCore, OsRng, RngCore};
|
||||||
|
|
||||||
use attest::svr2::RaftConfig;
|
|
||||||
use libsignal_net::auth::Auth;
|
use libsignal_net::auth::Auth;
|
||||||
use libsignal_net::enclave::{
|
use libsignal_net::enclave::{
|
||||||
EnclaveEndpoint, EnclaveEndpointConnection, EndpointParams, MrEnclave, PpssSetup, Sgx,
|
EnclaveEndpoint, EnclaveEndpointConnection, EndpointParams, Error, MrEnclave, PpssSetup, Sgx,
|
||||||
Svr3Flavor,
|
Svr3Flavor,
|
||||||
};
|
};
|
||||||
use libsignal_net::env::{DomainConfig, PROXY_CONFIG_F_STAGING, PROXY_CONFIG_G};
|
use libsignal_net::env::{DomainConfig, PROXY_CONFIG_F_STAGING, PROXY_CONFIG_G};
|
||||||
use libsignal_net::infra::certs::RootCertificates;
|
use libsignal_net::infra::certs::RootCertificates;
|
||||||
|
use libsignal_net::infra::dns::DnsResolver;
|
||||||
use libsignal_net::infra::tcp_ssl::DirectConnector as TcpSslTransportConnector;
|
use libsignal_net::infra::tcp_ssl::DirectConnector as TcpSslTransportConnector;
|
||||||
|
use libsignal_net::infra::TransportConnector;
|
||||||
use libsignal_net::svr::SvrConnection;
|
use libsignal_net::svr::SvrConnection;
|
||||||
use libsignal_net::svr3::{OpaqueMaskedShareSet, PpssOps};
|
use libsignal_net::svr3::{OpaqueMaskedShareSet, Svr3Client as _, Svr3Connect};
|
||||||
|
|
||||||
const TEST_SERVER_CERT: RootCertificates = RootCertificates::FromDer(Cow::Borrowed(
|
const TEST_SERVER_CERT: RootCertificates = RootCertificates::FromDer(Cow::Borrowed(
|
||||||
include_bytes!("../res/sgx_test_server_cert.cer"),
|
include_bytes!("../res/sgx_test_server_cert.cer"),
|
||||||
@ -69,6 +71,7 @@ where
|
|||||||
B: Svr3Flavor + Send,
|
B: Svr3Flavor + Send,
|
||||||
S: Send,
|
S: Send,
|
||||||
{
|
{
|
||||||
|
type Stream = <TcpSslTransportConnector as TransportConnector>::Stream;
|
||||||
type Connections = (SvrConnection<A, S>, SvrConnection<B, S>);
|
type Connections = (SvrConnection<A, S>, SvrConnection<B, S>);
|
||||||
type ServerIds = [u64; 2];
|
type ServerIds = [u64; 2];
|
||||||
|
|
||||||
@ -87,6 +90,31 @@ struct Args {
|
|||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Client {
|
||||||
|
env: TwoForTwoEnv<'static, Sgx, Sgx>,
|
||||||
|
auth_a: Auth,
|
||||||
|
auth_b: Auth,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Svr3Connect for Client {
|
||||||
|
type Stream = <TcpSslTransportConnector as TransportConnector>::Stream;
|
||||||
|
type Env = TwoForTwoEnv<'static, Sgx, Sgx>;
|
||||||
|
|
||||||
|
async fn connect(&self) -> Result<<Self::Env as PpssSetup<Self::Stream>>::Connections, Error> {
|
||||||
|
let connector = TcpSslTransportConnector::new(DnsResolver::default());
|
||||||
|
let connection_a = EnclaveEndpointConnection::new(&self.env.0, Duration::from_secs(10));
|
||||||
|
|
||||||
|
let a =
|
||||||
|
SvrConnection::connect(self.auth_a.clone(), &connection_a, connector.clone()).await?;
|
||||||
|
|
||||||
|
let connection_b = EnclaveEndpointConnection::new(&self.env.1, Duration::from_secs(10));
|
||||||
|
|
||||||
|
let b = SvrConnection::connect(self.auth_b.clone(), &connection_b, connector).await?;
|
||||||
|
Ok((a, b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
@ -101,14 +129,6 @@ async fn main() {
|
|||||||
|
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
let mut make_uid = || {
|
|
||||||
let mut bytes = [0u8; 16];
|
|
||||||
rng.fill_bytes(&mut bytes[..]);
|
|
||||||
bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
let make_auth = |uid: [u8; 16]| Auth::from_uid_and_secret(uid, auth_secret);
|
|
||||||
|
|
||||||
let two_sgx_env = {
|
let two_sgx_env = {
|
||||||
let endpoint = EnclaveEndpoint::<Sgx> {
|
let endpoint = EnclaveEndpoint::<Sgx> {
|
||||||
domain_config: TEST_SERVER_DOMAIN_CONFIG,
|
domain_config: TEST_SERVER_DOMAIN_CONFIG,
|
||||||
@ -117,37 +137,30 @@ async fn main() {
|
|||||||
TwoForTwoEnv(endpoint.clone(), endpoint)
|
TwoForTwoEnv(endpoint.clone(), endpoint)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (uid_a, uid_b) = (make_uid(), make_uid());
|
let client = {
|
||||||
|
let mut make_uid = || {
|
||||||
|
let mut bytes = [0u8; 16];
|
||||||
|
rng.fill_bytes(&mut bytes[..]);
|
||||||
|
bytes
|
||||||
|
};
|
||||||
|
|
||||||
let connect = || async {
|
let make_auth = |uid: [u8; 16]| Auth::from_uid_and_secret(uid, auth_secret);
|
||||||
let connector = TcpSslTransportConnector::new(DnsResolver::default());
|
|
||||||
let connection_a = EnclaveEndpointConnection::new(&two_sgx_env.0, Duration::from_secs(10));
|
|
||||||
|
|
||||||
let a = SvrConnection::connect(make_auth(uid_a), &connection_a, connector.clone())
|
Client {
|
||||||
.await
|
env: two_sgx_env,
|
||||||
.expect("can attestedly connect");
|
auth_a: make_auth(make_uid()),
|
||||||
|
auth_b: make_auth(make_uid()),
|
||||||
let connection_b = EnclaveEndpointConnection::new(&two_sgx_env.1, Duration::from_secs(10));
|
}
|
||||||
|
|
||||||
let b = SvrConnection::connect(make_auth(uid_b), &connection_b, connector)
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect");
|
|
||||||
(a, b)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let secret = make_secret(&mut rng);
|
let secret = make_secret(&mut rng);
|
||||||
println!("Secret to be stored: {}", hex::encode(secret));
|
println!("Secret to be stored: {}", hex::encode(secret));
|
||||||
|
|
||||||
let share_set_bytes = {
|
let share_set_bytes = {
|
||||||
let opaque_share_set = TwoForTwoEnv::backup(
|
let opaque_share_set = client
|
||||||
connect().await,
|
.backup(&args.password, secret, nonzero!(10u32), &mut rng)
|
||||||
&args.password,
|
.await
|
||||||
secret,
|
.expect("can multi backup");
|
||||||
nonzero!(10u32),
|
|
||||||
&mut rng,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.expect("can multi backup");
|
|
||||||
opaque_share_set.serialize().expect("can serialize")
|
opaque_share_set.serialize().expect("can serialize")
|
||||||
};
|
};
|
||||||
println!("Share set: {}", hex::encode(&share_set_bytes));
|
println!("Share set: {}", hex::encode(&share_set_bytes));
|
||||||
@ -155,7 +168,8 @@ async fn main() {
|
|||||||
let restored = {
|
let restored = {
|
||||||
let opaque_share_set =
|
let opaque_share_set =
|
||||||
OpaqueMaskedShareSet::deserialize(&share_set_bytes).expect("can deserialize");
|
OpaqueMaskedShareSet::deserialize(&share_set_bytes).expect("can deserialize");
|
||||||
TwoForTwoEnv::restore(connect().await, &args.password, opaque_share_set, &mut rng)
|
client
|
||||||
|
.restore(&args.password, opaque_share_set, &mut rng)
|
||||||
.await
|
.await
|
||||||
.expect("can multi restore")
|
.expect("can multi restore")
|
||||||
};
|
};
|
||||||
|
@ -7,20 +7,21 @@ use std::collections::HashMap;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use libsignal_net::infra::dns::DnsResolver;
|
use async_trait::async_trait;
|
||||||
use libsignal_net::infra::ws::DefaultStream;
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use proptest::test_runner::Config;
|
use proptest::test_runner::Config;
|
||||||
use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest};
|
use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest};
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use libsignal_net::auth::Auth;
|
use libsignal_net::auth::Auth;
|
||||||
use libsignal_net::enclave::{EnclaveEndpointConnection, Nitro, PpssSetup, Sgx, Tpm2Snp};
|
use libsignal_net::enclave::{self, PpssSetup};
|
||||||
use libsignal_net::env::Svr3Env;
|
use libsignal_net::env::Svr3Env;
|
||||||
use libsignal_net::infra::tcp_ssl::DirectConnector as TcpSslTransportConnector;
|
use libsignal_net::infra::ws::DefaultStream;
|
||||||
use libsignal_net::svr::SvrConnection;
|
use libsignal_net::svr3::{
|
||||||
use libsignal_net::svr3::{Error, OpaqueMaskedShareSet, PpssOps as _};
|
simple_svr3_connect, Error, OpaqueMaskedShareSet, Svr3Client, Svr3Connect,
|
||||||
|
};
|
||||||
use libsignal_svr3::EvaluationResult;
|
use libsignal_svr3::EvaluationResult;
|
||||||
|
|
||||||
use support::*;
|
use support::*;
|
||||||
|
|
||||||
const MAX_TRIES_LIMIT: u32 = 10;
|
const MAX_TRIES_LIMIT: u32 = 10;
|
||||||
@ -296,6 +297,39 @@ impl StateMachineTest for Svr3Storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Client<'a> {
|
||||||
|
auth: Auth,
|
||||||
|
env: &'a Svr3Env<'static>,
|
||||||
|
config: &'a SUTConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Client<'a> {
|
||||||
|
fn new(uid: Uid, storage: &'a Svr3Storage) -> Self {
|
||||||
|
let auth = Auth::from_uid_and_secret(uid, storage.enclave_secret);
|
||||||
|
Self {
|
||||||
|
auth,
|
||||||
|
env: &storage.env,
|
||||||
|
config: &storage.config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Svr3Connect for Client<'_> {
|
||||||
|
type Stream = DefaultStream;
|
||||||
|
type Env = Svr3Env<'static>;
|
||||||
|
|
||||||
|
async fn connect(
|
||||||
|
&self,
|
||||||
|
) -> Result<<Self::Env as PpssSetup<Self::Stream>>::Connections, enclave::Error> {
|
||||||
|
if let Some(duration) = self.config.sleep {
|
||||||
|
log::info!("💤 to avoid throttling...");
|
||||||
|
tokio::time::sleep(duration).await;
|
||||||
|
}
|
||||||
|
simple_svr3_connect(self.env, &self.auth).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Svr3Storage {
|
impl Svr3Storage {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let enclave_secret = {
|
let enclave_secret = {
|
||||||
@ -319,47 +353,14 @@ impl Svr3Storage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connect(&self, uid: Uid) -> <Svr3Env as PpssSetup<DefaultStream>>::Connections {
|
|
||||||
let connector = TcpSslTransportConnector::new(DnsResolver::default());
|
|
||||||
if let Some(duration) = self.config.sleep {
|
|
||||||
tokio::time::sleep(duration).await;
|
|
||||||
}
|
|
||||||
let auth = Auth::from_uid_and_secret(uid, self.enclave_secret);
|
|
||||||
let sgx_connection =
|
|
||||||
EnclaveEndpointConnection::new(self.env.sgx(), Duration::from_secs(10));
|
|
||||||
let a = SvrConnection::<Sgx, _>::connect(auth.clone(), &sgx_connection, connector.clone())
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect to SGX");
|
|
||||||
|
|
||||||
let nitro_connection =
|
|
||||||
EnclaveEndpointConnection::new(self.env.nitro(), Duration::from_secs(10));
|
|
||||||
let b =
|
|
||||||
SvrConnection::<Nitro, _>::connect(auth.clone(), &nitro_connection, connector.clone())
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect to Nitro");
|
|
||||||
|
|
||||||
let tpm2snp_connection =
|
|
||||||
EnclaveEndpointConnection::new(self.env.tpm2snp(), Duration::from_secs(10));
|
|
||||||
let c = SvrConnection::<Tpm2Snp, _>::connect(auth.clone(), &tpm2snp_connection, connector)
|
|
||||||
.await
|
|
||||||
.expect("can attestedly connect to Nitro");
|
|
||||||
|
|
||||||
(a, b, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backup(&mut self, uid: Uid, what: Secret, max_tries: u32) -> OpaqueMaskedShareSet {
|
fn backup(&mut self, uid: Uid, what: Secret, max_tries: u32) -> OpaqueMaskedShareSet {
|
||||||
self.runtime.block_on(async {
|
self.runtime.block_on(async {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let connections = self.connect(uid).await;
|
let client = Client::new(uid, self);
|
||||||
Svr3Env::backup(
|
client
|
||||||
connections,
|
.backup("password", what, max_tries.try_into().unwrap(), &mut rng)
|
||||||
"password",
|
.await
|
||||||
what,
|
.expect("can backup")
|
||||||
max_tries.try_into().unwrap(),
|
|
||||||
&mut rng,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.expect("can backup")
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,8 +372,8 @@ impl Svr3Storage {
|
|||||||
) -> Result<EvaluationResult, Error> {
|
) -> Result<EvaluationResult, Error> {
|
||||||
self.runtime.block_on(async {
|
self.runtime.block_on(async {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let connections = self.connect(uid).await;
|
let client = Client::new(uid, self);
|
||||||
Svr3Env::restore(connections, password, share_set, &mut rng).await
|
client.restore(password, share_set, &mut rng).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,7 @@ impl<T, const N: usize> ArrayIsh<T> for [T; N] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait PpssSetup<S> {
|
pub trait PpssSetup<S> {
|
||||||
|
type Stream;
|
||||||
type Connections: IntoConnections<Stream = S> + Send;
|
type Connections: IntoConnections<Stream = S> + Send;
|
||||||
type ServerIds: ArrayIsh<u64> + Send;
|
type ServerIds: ArrayIsh<u64> + Send;
|
||||||
const N: usize = Self::ServerIds::N;
|
const N: usize = Self::ServerIds::N;
|
||||||
@ -162,6 +163,7 @@ pub trait PpssSetup<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Send> PpssSetup<S> for Svr3Env<'_> {
|
impl<S: Send> PpssSetup<S> for Svr3Env<'_> {
|
||||||
|
type Stream = S;
|
||||||
type Connections = (
|
type Connections = (
|
||||||
SvrConnection<Sgx, S>,
|
SvrConnection<Sgx, S>,
|
||||||
SvrConnection<Nitro, S>,
|
SvrConnection<Nitro, S>,
|
||||||
|
@ -3,22 +3,28 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
//
|
//
|
||||||
|
|
||||||
use thiserror::Error;
|
use std::num::NonZeroU32;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::enclave::{IntoConnections, PpssSetup};
|
|
||||||
use crate::infra::errors::LogSafeDisplay;
|
|
||||||
use crate::infra::ws::{
|
|
||||||
run_attested_interaction, AttestedConnectionError, NextOrClose, WebSocketConnectError,
|
|
||||||
WebSocketServiceError,
|
|
||||||
};
|
|
||||||
use crate::infra::AsyncDuplexStream;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bincode::Options as _;
|
use bincode::Options as _;
|
||||||
use futures_util::future::try_join_all;
|
|
||||||
use libsignal_svr3::{Backup, EvaluationResult, MaskedShareSet, Query, Restore};
|
|
||||||
use rand_core::CryptoRngCore;
|
use rand_core::CryptoRngCore;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::num::NonZeroU32;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use libsignal_svr3::{EvaluationResult, MaskedShareSet};
|
||||||
|
|
||||||
|
use crate::auth::Auth;
|
||||||
|
use crate::enclave::{self, EnclaveEndpointConnection, Nitro, PpssSetup, Sgx, Tpm2Snp};
|
||||||
|
use crate::env::Svr3Env;
|
||||||
|
use crate::infra::dns::DnsResolver;
|
||||||
|
use crate::infra::errors::LogSafeDisplay;
|
||||||
|
use crate::infra::tcp_ssl::DirectConnector;
|
||||||
|
use crate::infra::ws::{
|
||||||
|
AttestedConnectionError, DefaultStream, WebSocketConnectError, WebSocketServiceError,
|
||||||
|
};
|
||||||
|
use crate::infra::AsyncDuplexStream;
|
||||||
|
use crate::svr::SvrConnection;
|
||||||
|
|
||||||
const MASKED_SHARE_SET_FORMAT: u8 = 0;
|
const MASKED_SHARE_SET_FORMAT: u8 = 0;
|
||||||
|
|
||||||
@ -199,37 +205,30 @@ impl From<AttestedConnectionError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
/// High level data operations on instances of `PpssSetup`
|
||||||
pub trait PpssOps<S>: PpssSetup<S> {
|
///
|
||||||
async fn backup(
|
/// These functions are useful if we ever want to perform multiple operations
|
||||||
connections: Self::Connections,
|
/// on the same set of open connections, as opposed to having to connect for
|
||||||
password: &str,
|
/// each individual operation, as implied by `Svr3Client` trait.
|
||||||
secret: [u8; 32],
|
mod ppss_ops {
|
||||||
max_tries: NonZeroU32,
|
use super::{Error, OpaqueMaskedShareSet};
|
||||||
rng: &mut (impl CryptoRngCore + Send),
|
|
||||||
) -> Result<OpaqueMaskedShareSet, Error>;
|
|
||||||
|
|
||||||
async fn restore(
|
use crate::enclave::{IntoConnections, PpssSetup};
|
||||||
connections: Self::Connections,
|
use crate::infra::ws::{run_attested_interaction, NextOrClose};
|
||||||
password: &str,
|
use crate::infra::AsyncDuplexStream;
|
||||||
share_set: OpaqueMaskedShareSet,
|
use futures_util::future::try_join_all;
|
||||||
rng: &mut (impl CryptoRngCore + Send),
|
use libsignal_svr3::{Backup, EvaluationResult, Query, Restore};
|
||||||
) -> Result<EvaluationResult, Error>;
|
use rand_core::CryptoRngCore;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
async fn remove(connections: Self::Connections) -> Result<(), Error>;
|
pub async fn do_backup<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>>(
|
||||||
async fn query(connections: Self::Connections) -> Result<u32, Error>;
|
connections: Env::Connections,
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>> PpssOps<S> for Env {
|
|
||||||
async fn backup(
|
|
||||||
connections: Self::Connections,
|
|
||||||
password: &str,
|
password: &str,
|
||||||
secret: [u8; 32],
|
secret: [u8; 32],
|
||||||
max_tries: NonZeroU32,
|
max_tries: NonZeroU32,
|
||||||
rng: &mut (impl CryptoRngCore + Send),
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
) -> Result<OpaqueMaskedShareSet, Error> {
|
) -> Result<OpaqueMaskedShareSet, Error> {
|
||||||
let server_ids = Self::server_ids();
|
let server_ids = Env::server_ids();
|
||||||
let backup = Backup::new(server_ids.as_ref(), password, secret, max_tries, rng)?;
|
let backup = Backup::new(server_ids.as_ref(), password, secret, max_tries, rng)?;
|
||||||
let mut connections = connections.into_connections();
|
let mut connections = connections.into_connections();
|
||||||
let futures = connections
|
let futures = connections
|
||||||
@ -244,8 +243,8 @@ impl<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>> PpssOps<S> for Env {
|
|||||||
Ok(OpaqueMaskedShareSet::new(share_set))
|
Ok(OpaqueMaskedShareSet::new(share_set))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn restore(
|
pub async fn do_restore<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>>(
|
||||||
connections: Self::Connections,
|
connections: Env::Connections,
|
||||||
password: &str,
|
password: &str,
|
||||||
share_set: OpaqueMaskedShareSet,
|
share_set: OpaqueMaskedShareSet,
|
||||||
rng: &mut (impl CryptoRngCore + Send),
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
@ -263,7 +262,9 @@ impl<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>> PpssOps<S> for Env {
|
|||||||
Ok(restore.finalize(&responses)?)
|
Ok(restore.finalize(&responses)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove(connections: Self::Connections) -> Result<(), Error> {
|
pub async fn do_remove<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>>(
|
||||||
|
connections: Env::Connections,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let requests = std::iter::repeat(libsignal_svr3::make_remove_request());
|
let requests = std::iter::repeat(libsignal_svr3::make_remove_request());
|
||||||
let mut connections = connections.into_connections();
|
let mut connections = connections.into_connections();
|
||||||
let futures = connections
|
let futures = connections
|
||||||
@ -278,7 +279,9 @@ impl<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>> PpssOps<S> for Env {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query(connections: Self::Connections) -> Result<u32, Error> {
|
pub async fn do_query<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>>(
|
||||||
|
connections: Env::Connections,
|
||||||
|
) -> Result<u32, Error> {
|
||||||
let mut connections = connections.into_connections();
|
let mut connections = connections.into_connections();
|
||||||
let futures = connections
|
let futures = connections
|
||||||
.as_mut()
|
.as_mut()
|
||||||
@ -290,25 +293,156 @@ impl<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>> PpssOps<S> for Env {
|
|||||||
let responses = collect_responses(results, addresses)?;
|
let responses = collect_responses(results, addresses)?;
|
||||||
Ok(Query::finalize(&responses)?)
|
Ok(Query::finalize(&responses)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_responses<'a>(
|
||||||
|
results: impl IntoIterator<Item = NextOrClose<Vec<u8>>>,
|
||||||
|
addresses: impl IntoIterator<Item = &'a url::Host>,
|
||||||
|
) -> Result<Vec<Vec<u8>>, Error> {
|
||||||
|
results
|
||||||
|
.into_iter()
|
||||||
|
.zip(addresses)
|
||||||
|
.map(|(next_or_close, address)| {
|
||||||
|
next_or_close.next_or(Error::Protocol(format!("no response from {}", address)))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_responses<'a>(
|
#[async_trait]
|
||||||
results: impl IntoIterator<Item = NextOrClose<Vec<u8>>>,
|
pub trait Svr3Client {
|
||||||
addresses: impl IntoIterator<Item = &'a url::Host>,
|
async fn backup(
|
||||||
) -> Result<Vec<Vec<u8>>, Error> {
|
&self,
|
||||||
results
|
password: &str,
|
||||||
.into_iter()
|
secret: [u8; 32],
|
||||||
.zip(addresses)
|
max_tries: NonZeroU32,
|
||||||
.map(|(next_or_close, address)| {
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
next_or_close.next_or(Error::Protocol(format!("no response from {}", address)))
|
) -> Result<OpaqueMaskedShareSet, Error>;
|
||||||
})
|
|
||||||
.collect()
|
async fn restore(
|
||||||
|
&self,
|
||||||
|
password: &str,
|
||||||
|
share_set: OpaqueMaskedShareSet,
|
||||||
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
|
) -> Result<EvaluationResult, Error>;
|
||||||
|
|
||||||
|
async fn remove(&self) -> Result<(), Error>;
|
||||||
|
|
||||||
|
async fn query(&self) -> Result<u32, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Svr3Connect {
|
||||||
|
// Stream is needed for the blanket implementation,
|
||||||
|
// otherwise S would be an unconstrained generic parameter.
|
||||||
|
type Stream;
|
||||||
|
type Env: PpssSetup<Self::Stream>;
|
||||||
|
async fn connect(
|
||||||
|
&self,
|
||||||
|
) -> Result<<Self::Env as PpssSetup<Self::Stream>>::Connections, enclave::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T> Svr3Client for T
|
||||||
|
where
|
||||||
|
T: Svr3Connect + Sync,
|
||||||
|
T::Stream: AsyncDuplexStream + 'static,
|
||||||
|
{
|
||||||
|
async fn backup(
|
||||||
|
&self,
|
||||||
|
password: &str,
|
||||||
|
secret: [u8; 32],
|
||||||
|
max_tries: NonZeroU32,
|
||||||
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
|
) -> Result<OpaqueMaskedShareSet, Error> {
|
||||||
|
ppss_ops::do_backup::<T::Stream, T::Env>(
|
||||||
|
self.connect().await?,
|
||||||
|
password,
|
||||||
|
secret,
|
||||||
|
max_tries,
|
||||||
|
rng,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn restore(
|
||||||
|
&self,
|
||||||
|
password: &str,
|
||||||
|
share_set: OpaqueMaskedShareSet,
|
||||||
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
|
) -> Result<EvaluationResult, Error> {
|
||||||
|
ppss_ops::do_restore::<T::Stream, T::Env>(self.connect().await?, password, share_set, rng)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove(&self) -> Result<(), Error> {
|
||||||
|
ppss_ops::do_remove::<T::Stream, T::Env>(self.connect().await?).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn query(&self) -> Result<u32, Error> {
|
||||||
|
ppss_ops::do_query::<T::Stream, T::Env>(self.connect().await?).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt a restore from a pair of SVR3 instances.
|
||||||
|
///
|
||||||
|
/// The function is meant to be used in the registration flow, when the client
|
||||||
|
/// app does not yet know whether it is supposed to be trusting one set of enclaves
|
||||||
|
/// or another. Therefore, it first reads from the primary falling back to the
|
||||||
|
/// secondary enclaves only if the primary returned `DataMissing`, that is, the
|
||||||
|
/// data has not been migrated yet. Any other error terminates the whole operation
|
||||||
|
/// and will need to be retried.
|
||||||
|
///
|
||||||
|
/// The choice of terms "primary" and "fallback" is, perhaps, a little confusing
|
||||||
|
/// when thinking about the enclave migration, where they would be called,
|
||||||
|
/// respectively, "next" and "current", but ordering of parameters and actions in
|
||||||
|
/// the body of the function make "primary" and "fallback" a better fit.
|
||||||
|
pub async fn restore_with_fallback<Primary, Fallback>(
|
||||||
|
clients: (Primary, Fallback),
|
||||||
|
password: &str,
|
||||||
|
share_set: OpaqueMaskedShareSet,
|
||||||
|
rng: &mut (impl CryptoRngCore + Send),
|
||||||
|
) -> Result<EvaluationResult, Error>
|
||||||
|
where
|
||||||
|
Primary: Svr3Client + Sync,
|
||||||
|
Fallback: Svr3Client + Sync,
|
||||||
|
{
|
||||||
|
let (primary_conn, fallback_conn) = clients;
|
||||||
|
|
||||||
|
match primary_conn.restore(password, share_set.clone(), rng).await {
|
||||||
|
Err(Error::DataMissing) => (/* fall through to fallback */),
|
||||||
|
result @ (Err(_) | Ok(_)) => return result,
|
||||||
|
}
|
||||||
|
fallback_conn.restore(password, share_set, rng).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simplest way to connect to an SVR3 Environment in integration tests and examples.
|
||||||
|
pub async fn simple_svr3_connect(
|
||||||
|
env: &Svr3Env<'static>,
|
||||||
|
auth: &Auth,
|
||||||
|
) -> Result<<Svr3Env<'static> as PpssSetup<DefaultStream>>::Connections, enclave::Error> {
|
||||||
|
let connector = DirectConnector::new(DnsResolver::default());
|
||||||
|
let sgx_connection = EnclaveEndpointConnection::new(env.sgx(), Duration::from_secs(10));
|
||||||
|
let a =
|
||||||
|
SvrConnection::<Sgx, _>::connect(auth.clone(), &sgx_connection, connector.clone()).await?;
|
||||||
|
|
||||||
|
let nitro_connection = EnclaveEndpointConnection::new(env.nitro(), Duration::from_secs(10));
|
||||||
|
let b = SvrConnection::<Nitro, _>::connect(auth.clone(), &nitro_connection, connector.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let tpm2snp_connection = EnclaveEndpointConnection::new(env.tpm2snp(), Duration::from_secs(10));
|
||||||
|
let c =
|
||||||
|
SvrConnection::<Tpm2Snp, _>::connect(auth.clone(), &tpm2snp_connection, connector).await?;
|
||||||
|
|
||||||
|
Ok((a, b, c))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use rand_core::OsRng;
|
||||||
|
|
||||||
fn new_empty_share_set() -> OpaqueMaskedShareSet {
|
fn new_empty_share_set() -> OpaqueMaskedShareSet {
|
||||||
OpaqueMaskedShareSet {
|
OpaqueMaskedShareSet {
|
||||||
inner: SerializableMaskedShareSet {
|
inner: SerializableMaskedShareSet {
|
||||||
@ -345,4 +479,112 @@ mod test {
|
|||||||
DeserializeError::BadVersion(_),
|
DeserializeError::BadVersion(_),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TestSvr3Client {
|
||||||
|
restore_fn: fn() -> Result<EvaluationResult, Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Svr3Client for TestSvr3Client {
|
||||||
|
async fn backup(
|
||||||
|
&self,
|
||||||
|
_password: &str,
|
||||||
|
_secret: [u8; 32],
|
||||||
|
_max_tries: NonZeroU32,
|
||||||
|
_rng: &mut (impl CryptoRngCore + Send),
|
||||||
|
) -> Result<OpaqueMaskedShareSet, Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn restore(
|
||||||
|
&self,
|
||||||
|
_password: &str,
|
||||||
|
_share_set: OpaqueMaskedShareSet,
|
||||||
|
_rng: &mut (impl CryptoRngCore + Send),
|
||||||
|
) -> Result<EvaluationResult, Error> {
|
||||||
|
(self.restore_fn)()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove(&self) -> Result<(), Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn query(&self) -> Result<u32, Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_share_set() -> OpaqueMaskedShareSet {
|
||||||
|
OpaqueMaskedShareSet {
|
||||||
|
inner: SerializableMaskedShareSet {
|
||||||
|
server_ids: vec![],
|
||||||
|
masked_shares: vec![],
|
||||||
|
commitment: [0; 32],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn test_evaluation_result() -> EvaluationResult {
|
||||||
|
EvaluationResult {
|
||||||
|
value: [0; 32],
|
||||||
|
tries_remaining: 42,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn restore_with_fallback_primary_success() {
|
||||||
|
let primary = TestSvr3Client {
|
||||||
|
restore_fn: || Ok(test_evaluation_result()),
|
||||||
|
};
|
||||||
|
let fallback = TestSvr3Client {
|
||||||
|
restore_fn: || panic!("Must not be called"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let result =
|
||||||
|
restore_with_fallback((primary, fallback), "", test_share_set(), &mut rng).await;
|
||||||
|
assert_matches!(result, Ok(evaluation_result) => assert_eq!(evaluation_result, test_evaluation_result()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn restore_with_fallback_primary_fatal_error() {
|
||||||
|
let primary = TestSvr3Client {
|
||||||
|
restore_fn: || Err(Error::ConnectionTimedOut),
|
||||||
|
};
|
||||||
|
let fallback = TestSvr3Client {
|
||||||
|
restore_fn: || panic!("Must not be called"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let result =
|
||||||
|
restore_with_fallback((primary, fallback), "", test_share_set(), &mut rng).await;
|
||||||
|
assert_matches!(result, Err(Error::ConnectionTimedOut));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn restore_with_fallback_fallback_error() {
|
||||||
|
let primary = TestSvr3Client {
|
||||||
|
restore_fn: || Err(Error::DataMissing),
|
||||||
|
};
|
||||||
|
let fallback = TestSvr3Client {
|
||||||
|
restore_fn: || Err(Error::RestoreFailed(31415)),
|
||||||
|
};
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let result =
|
||||||
|
restore_with_fallback((primary, fallback), "", test_share_set(), &mut rng).await;
|
||||||
|
assert_matches!(result, Err(Error::RestoreFailed(31415)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn restore_with_fallback_fallback_success() {
|
||||||
|
let primary = TestSvr3Client {
|
||||||
|
restore_fn: || Err(Error::DataMissing),
|
||||||
|
};
|
||||||
|
let fallback = TestSvr3Client {
|
||||||
|
restore_fn: || Ok(test_evaluation_result()),
|
||||||
|
};
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let result =
|
||||||
|
restore_with_fallback((primary, fallback), "", test_share_set(), &mut rng).await;
|
||||||
|
assert_matches!(result, Ok(evaluation_result) => assert_eq!(evaluation_result, test_evaluation_result()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ pub struct Restore<'a> {
|
|||||||
pub requests: Vec<Vec<u8>>,
|
pub requests: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct EvaluationResult {
|
pub struct EvaluationResult {
|
||||||
pub value: [u8; SECRET_BYTES],
|
pub value: [u8; SECRET_BYTES],
|
||||||
pub tries_remaining: u32,
|
pub tries_remaining: u32,
|
||||||
|
Loading…
Reference in New Issue
Block a user