use std::sync::Arc;
use tiny_keccak::{Hasher, TupleHash};
use oasis_core_keymanager::client::{KeyManagerClient as CoreKeyManagerClient, RemoteClient};
pub use oasis_core_keymanager::{
api::KeyManagerError,
crypto::{KeyPair, KeyPairId, SignedPublicKey, StateKey},
policy::TrustedSigners,
};
use oasis_core_runtime::{
common::{crypto::signature::PublicKey, namespace::Namespace},
consensus::{beacon::EpochTime, verifier::Verifier},
future::block_on,
identity::Identity,
protocol::Protocol,
RpcDispatcher,
};
pub(crate) struct KeyManagerClient {
inner: Arc<dyn CoreKeyManagerClient>,
}
impl KeyManagerClient {
pub(crate) fn new(
runtime_id: Namespace,
protocol: Arc<Protocol>,
consensus_verifier: Arc<dyn Verifier>,
identity: Arc<Identity>,
rpc: &mut RpcDispatcher,
key_cache_sizes: usize,
signers: TrustedSigners,
) -> Self {
let remote_client = Arc::new(RemoteClient::new_runtime(
runtime_id,
protocol,
consensus_verifier,
identity,
key_cache_sizes,
signers,
));
let handler_remote_client = remote_client.clone();
rpc.set_keymanager_quote_policy_update_handler(Some(Box::new(move |policy| {
block_on(handler_remote_client.set_quote_policy(policy));
})));
let handler_remote_client = remote_client.clone();
rpc.set_keymanager_status_update_handler(Some(Box::new(move |status| {
block_on(handler_remote_client.set_status(status))
.expect("failed to update km client status");
})));
KeyManagerClient {
inner: remote_client,
}
}
pub(crate) fn with_context(self: &Arc<Self>) -> Box<dyn KeyManager> {
Box::new(KeyManagerClientWithContext::new(self.clone(), false)) as Box<dyn KeyManager>
}
pub(crate) fn with_private_context(self: &Arc<Self>) -> Box<dyn KeyManager> {
Box::new(KeyManagerClientWithContext::new(self.clone(), true)) as Box<dyn KeyManager>
}
pub(crate) fn runtime_id(&self) -> Option<Namespace> {
self.inner.runtime_id()
}
pub(crate) fn runtime_signing_key(&self) -> Option<PublicKey> {
self.inner.runtime_signing_key()
}
pub(crate) fn clear_cache(&self) {
self.inner.clear_cache()
}
pub(crate) async fn get_or_create_keys(
&self,
key_pair_id: KeyPairId,
) -> Result<KeyPair, KeyManagerError> {
retryable(|| self.inner.get_or_create_keys(key_pair_id, 0)).await
}
pub(crate) async fn get_public_key(
&self,
key_pair_id: KeyPairId,
) -> Result<SignedPublicKey, KeyManagerError> {
retryable(|| self.inner.get_public_key(key_pair_id, 0)).await
}
pub(crate) async fn get_or_create_ephemeral_keys(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<KeyPair, KeyManagerError> {
retryable(|| self.inner.get_or_create_ephemeral_keys(key_pair_id, epoch)).await
}
pub(crate) async fn get_public_ephemeral_key(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<SignedPublicKey, KeyManagerError> {
retryable(|| self.inner.get_public_ephemeral_key(key_pair_id, epoch)).await
}
}
async fn retryable<A>(action: A) -> Result<A::Item, A::Error>
where
A: tokio_retry::Action,
{
let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(4)
.max_delay(std::time::Duration::from_millis(250))
.map(tokio_retry::strategy::jitter)
.take(5);
tokio_retry::Retry::spawn(retry_strategy, action).await
}
pub trait KeyManager {
fn runtime_id(&self) -> Option<Namespace>;
fn runtime_signing_key(&self) -> Option<PublicKey>;
fn clear_cache(&self);
fn get_or_create_keys(&self, key_pair_id: KeyPairId) -> Result<KeyPair, KeyManagerError>;
fn get_public_key(&self, key_pair_id: KeyPairId) -> Result<SignedPublicKey, KeyManagerError>;
fn get_or_create_ephemeral_keys(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<KeyPair, KeyManagerError>;
fn get_public_ephemeral_key(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<SignedPublicKey, KeyManagerError>;
fn box_clone(&self) -> Box<dyn KeyManager>;
}
impl Clone for Box<dyn KeyManager> {
fn clone(&self) -> Box<dyn KeyManager> {
self.box_clone()
}
}
#[derive(Clone)]
pub struct KeyManagerClientWithContext {
parent: Arc<KeyManagerClient>,
allow_private: bool,
}
impl KeyManagerClientWithContext {
fn new(parent: Arc<KeyManagerClient>, allow_private: bool) -> KeyManagerClientWithContext {
KeyManagerClientWithContext {
parent,
allow_private,
}
}
async fn get_or_create_keys_async(
&self,
key_pair_id: KeyPairId,
) -> Result<KeyPair, KeyManagerError> {
if !self.allow_private {
return Err(KeyManagerError::Other(anyhow::anyhow!(
"not allowed by local runtime policy"
)));
}
self.parent.get_or_create_keys(key_pair_id).await
}
async fn get_public_key_async(
&self,
key_pair_id: KeyPairId,
) -> Result<SignedPublicKey, KeyManagerError> {
self.parent.get_public_key(key_pair_id).await
}
async fn get_or_create_ephemeral_keys_async(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<KeyPair, KeyManagerError> {
if !self.allow_private {
return Err(KeyManagerError::Other(anyhow::anyhow!(
"not allowed by local runtime policy"
)));
}
self.parent
.get_or_create_ephemeral_keys(key_pair_id, epoch)
.await
}
async fn get_public_ephemeral_key_async(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<SignedPublicKey, KeyManagerError> {
self.parent
.get_public_ephemeral_key(key_pair_id, epoch)
.await
}
}
impl KeyManager for KeyManagerClientWithContext {
fn runtime_id(&self) -> Option<Namespace> {
self.parent.runtime_id()
}
fn runtime_signing_key(&self) -> Option<PublicKey> {
self.parent.runtime_signing_key()
}
fn clear_cache(&self) {
self.parent.clear_cache();
}
fn get_or_create_keys(&self, key_pair_id: KeyPairId) -> Result<KeyPair, KeyManagerError> {
block_on(self.get_or_create_keys_async(key_pair_id))
}
fn get_public_key(&self, key_pair_id: KeyPairId) -> Result<SignedPublicKey, KeyManagerError> {
block_on(self.get_public_key_async(key_pair_id))
}
fn get_or_create_ephemeral_keys(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<KeyPair, KeyManagerError> {
block_on(self.get_or_create_ephemeral_keys_async(key_pair_id, epoch))
}
fn get_public_ephemeral_key(
&self,
key_pair_id: KeyPairId,
epoch: EpochTime,
) -> Result<SignedPublicKey, KeyManagerError> {
block_on(self.get_public_ephemeral_key_async(key_pair_id, epoch))
}
fn box_clone(&self) -> Box<dyn KeyManager> {
Box::new(self.clone())
}
}
pub const KEY_PAIR_ID_CONTEXT: &[u8] = b"oasis-runtime-sdk/keymanager: key pair id";
pub fn get_key_pair_id<'a, C>(context: C) -> KeyPairId
where
C: IntoIterator<Item = &'a [u8]> + 'a,
{
let mut h = TupleHash::v256(KEY_PAIR_ID_CONTEXT);
for item in context.into_iter() {
h.update(item);
}
let mut key_pair_id = [0u8; 32];
h.finalize(&mut key_pair_id);
KeyPairId(key_pair_id)
}