oasis_runtime_sdk/
keymanager.rs

1//! Keymanager interface.
2use std::sync::Arc;
3
4use tiny_keccak::{Hasher, TupleHash};
5
6use oasis_core_keymanager::client::{KeyManagerClient as CoreKeyManagerClient, RemoteClient};
7pub use oasis_core_keymanager::{
8    api::KeyManagerError,
9    crypto::{KeyPair, KeyPairId, SignedPublicKey, StateKey},
10    policy::TrustedSigners,
11};
12use oasis_core_runtime::{
13    common::{crypto::signature::PublicKey, namespace::Namespace},
14    consensus::{beacon::EpochTime, verifier::Verifier},
15    future::block_on,
16    identity::Identity,
17    protocol::Protocol,
18    RpcDispatcher,
19};
20
21/// Key manager interface. This is a runtime context-resident convenience
22/// wrapper to the keymanager configured for the runtime.
23pub(crate) struct KeyManagerClient {
24    inner: Arc<dyn CoreKeyManagerClient>,
25}
26
27impl KeyManagerClient {
28    /// Create a new key manager client using the default remote client from oasis-core.
29    pub(crate) fn new(
30        runtime_id: Namespace,
31        protocol: Arc<Protocol>,
32        consensus_verifier: Arc<dyn Verifier>,
33        identity: Arc<Identity>,
34        rpc: &mut RpcDispatcher,
35        key_cache_sizes: usize,
36        signers: TrustedSigners,
37    ) -> Self {
38        let remote_client = Arc::new(RemoteClient::new_runtime(
39            runtime_id,
40            protocol,
41            consensus_verifier,
42            identity,
43            key_cache_sizes,
44            signers,
45        ));
46
47        // Setup the quote policy update handler.
48        let handler_remote_client = remote_client.clone();
49        rpc.set_keymanager_quote_policy_update_handler(Some(Box::new(move |policy| {
50            block_on(handler_remote_client.set_quote_policy(policy));
51        })));
52
53        // Setup the status update handler.
54        let handler_remote_client = remote_client.clone();
55        rpc.set_keymanager_status_update_handler(Some(Box::new(move |status| {
56            block_on(handler_remote_client.set_status(status))
57                .expect("failed to update km client status");
58        })));
59
60        KeyManagerClient {
61            inner: remote_client,
62        }
63    }
64
65    /// Create a client proxy which will forward calls to the inner client using the given context.
66    /// Only public key queries will be allowed.
67    pub(crate) fn with_context(self: &Arc<Self>) -> Box<dyn KeyManager> {
68        Box::new(KeyManagerClientWithContext::new(self.clone(), false)) as Box<dyn KeyManager>
69    }
70
71    /// Create a client proxy which will forward calls to the inner client using the given context.
72    /// Public and private key queries will be allowed.
73    pub(crate) fn with_private_context(self: &Arc<Self>) -> Box<dyn KeyManager> {
74        Box::new(KeyManagerClientWithContext::new(self.clone(), true)) as Box<dyn KeyManager>
75    }
76
77    /// Key manager runtime identifier this client is connected to. It may be `None` in case the
78    /// identifier is not known yet (e.g. the client has not yet been initialized).
79    ///
80    /// See the oasis-core documentation for details.
81    pub(crate) fn runtime_id(&self) -> Option<Namespace> {
82        self.inner.runtime_id()
83    }
84
85    /// Key manager runtime signing key used to sign messages from the key manager.
86    ///
87    /// See the oasis-core documentation for details.
88    pub(crate) fn runtime_signing_key(&self) -> Option<PublicKey> {
89        self.inner.runtime_signing_key()
90    }
91
92    /// Clear local key cache.
93    ///
94    /// See the oasis-core documentation for details.
95    pub(crate) fn clear_cache(&self) {
96        self.inner.clear_cache()
97    }
98
99    /// Get or create named key pair.
100    ///
101    /// See the oasis-core documentation for details.
102    pub(crate) async fn get_or_create_keys(
103        &self,
104        key_pair_id: KeyPairId,
105    ) -> Result<KeyPair, KeyManagerError> {
106        retryable(|| self.inner.get_or_create_keys(key_pair_id, 0)).await
107    }
108
109    /// Get public key for a key pair id.
110    ///
111    /// See the oasis-core documentation for details.
112    pub(crate) async fn get_public_key(
113        &self,
114        key_pair_id: KeyPairId,
115    ) -> Result<SignedPublicKey, KeyManagerError> {
116        retryable(|| self.inner.get_public_key(key_pair_id, 0)).await
117    }
118
119    /// Get or create named ephemeral key pair for given epoch.
120    ///
121    /// See the oasis-core documentation for details.
122    pub(crate) async fn get_or_create_ephemeral_keys(
123        &self,
124        key_pair_id: KeyPairId,
125        epoch: EpochTime,
126    ) -> Result<KeyPair, KeyManagerError> {
127        retryable(|| self.inner.get_or_create_ephemeral_keys(key_pair_id, epoch)).await
128    }
129
130    /// Get ephemeral public key for an epoch and a key pair id.
131    ///
132    /// See the oasis-core documentation for details.
133    pub(crate) async fn get_public_ephemeral_key(
134        &self,
135        key_pair_id: KeyPairId,
136        epoch: EpochTime,
137    ) -> Result<SignedPublicKey, KeyManagerError> {
138        retryable(|| self.inner.get_public_ephemeral_key(key_pair_id, epoch)).await
139    }
140}
141
142/// Decorator for remote method calls that can be safely retried.
143async fn retryable<A>(action: A) -> Result<A::Item, A::Error>
144where
145    A: tokio_retry::Action,
146{
147    let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(4)
148        .max_delay(std::time::Duration::from_millis(250))
149        .map(tokio_retry::strategy::jitter)
150        .take(5);
151
152    tokio_retry::Retry::spawn(retry_strategy, action).await
153}
154
155/// Key manager interface.
156pub trait KeyManager {
157    /// Key manager runtime identifier this client is connected to. It may be `None` in case the
158    /// identifier is not known yet (e.g. the client has not yet been initialized).
159    ///
160    /// See the oasis-core documentation for details.
161    fn runtime_id(&self) -> Option<Namespace>;
162
163    /// Key manager runtime signing key used to sign messages from the key manager.
164    ///
165    /// See the oasis-core documentation for details.
166    fn runtime_signing_key(&self) -> Option<PublicKey>;
167
168    /// Clear local key cache.
169    ///
170    /// See the oasis-core documentation for details.
171    fn clear_cache(&self);
172
173    /// Get or create named key pair.
174    ///
175    /// See the oasis-core documentation for details. This variant of the method
176    /// synchronously blocks for the result.
177    fn get_or_create_keys(&self, key_pair_id: KeyPairId) -> Result<KeyPair, KeyManagerError>;
178
179    /// Get public key for a key pair id.
180    ///
181    /// See the oasis-core documentation for details. This variant of the method
182    /// synchronously blocks for the result.
183    fn get_public_key(&self, key_pair_id: KeyPairId) -> Result<SignedPublicKey, KeyManagerError>;
184
185    /// Get or create named ephemeral key pair for given epoch.
186    ///
187    /// See the oasis-core documentation for details. This variant of the method
188    /// synchronously blocks for the result.
189    fn get_or_create_ephemeral_keys(
190        &self,
191        key_pair_id: KeyPairId,
192        epoch: EpochTime,
193    ) -> Result<KeyPair, KeyManagerError>;
194
195    /// Get ephemeral public key for an epoch and a key pair id.
196    ///
197    /// See the oasis-core documentation for details. This variant of the method
198    /// synchronously blocks for the result.
199    fn get_public_ephemeral_key(
200        &self,
201        key_pair_id: KeyPairId,
202        epoch: EpochTime,
203    ) -> Result<SignedPublicKey, KeyManagerError>;
204
205    fn box_clone(&self) -> Box<dyn KeyManager>;
206}
207
208impl Clone for Box<dyn KeyManager> {
209    fn clone(&self) -> Box<dyn KeyManager> {
210        self.box_clone()
211    }
212}
213
214/// Convenience wrapper around an existing KeyManagerClient instance which uses
215/// a default io context for all calls.
216#[derive(Clone)]
217pub struct KeyManagerClientWithContext {
218    parent: Arc<KeyManagerClient>,
219    allow_private: bool,
220}
221
222impl KeyManagerClientWithContext {
223    fn new(parent: Arc<KeyManagerClient>, allow_private: bool) -> KeyManagerClientWithContext {
224        KeyManagerClientWithContext {
225            parent,
226            allow_private,
227        }
228    }
229
230    /// Get or create named key pair.
231    ///
232    /// See the oasis-core documentation for details.
233    async fn get_or_create_keys_async(
234        &self,
235        key_pair_id: KeyPairId,
236    ) -> Result<KeyPair, KeyManagerError> {
237        if !self.allow_private {
238            return Err(KeyManagerError::Other(anyhow::anyhow!(
239                "not allowed by local runtime policy"
240            )));
241        }
242
243        self.parent.get_or_create_keys(key_pair_id).await
244    }
245
246    /// Get public key for a key pair id.
247    ///
248    /// See the oasis-core documentation for details.
249    async fn get_public_key_async(
250        &self,
251        key_pair_id: KeyPairId,
252    ) -> Result<SignedPublicKey, KeyManagerError> {
253        self.parent.get_public_key(key_pair_id).await
254    }
255
256    /// Get ephemeral public key for an epoch and a key pair id.
257    ///
258    /// See the oasis-core documentation for details.
259    async fn get_or_create_ephemeral_keys_async(
260        &self,
261        key_pair_id: KeyPairId,
262        epoch: EpochTime,
263    ) -> Result<KeyPair, KeyManagerError> {
264        if !self.allow_private {
265            return Err(KeyManagerError::Other(anyhow::anyhow!(
266                "not allowed by local runtime policy"
267            )));
268        }
269
270        self.parent
271            .get_or_create_ephemeral_keys(key_pair_id, epoch)
272            .await
273    }
274
275    /// Get ephemeral public key for an epoch and a key pair id.
276    ///
277    /// See the oasis-core documentation for details.
278    async fn get_public_ephemeral_key_async(
279        &self,
280        key_pair_id: KeyPairId,
281        epoch: EpochTime,
282    ) -> Result<SignedPublicKey, KeyManagerError> {
283        self.parent
284            .get_public_ephemeral_key(key_pair_id, epoch)
285            .await
286    }
287}
288
289impl KeyManager for KeyManagerClientWithContext {
290    fn runtime_id(&self) -> Option<Namespace> {
291        self.parent.runtime_id()
292    }
293
294    fn runtime_signing_key(&self) -> Option<PublicKey> {
295        self.parent.runtime_signing_key()
296    }
297
298    fn clear_cache(&self) {
299        self.parent.clear_cache();
300    }
301
302    fn get_or_create_keys(&self, key_pair_id: KeyPairId) -> Result<KeyPair, KeyManagerError> {
303        block_on(self.get_or_create_keys_async(key_pair_id))
304    }
305
306    fn get_public_key(&self, key_pair_id: KeyPairId) -> Result<SignedPublicKey, KeyManagerError> {
307        block_on(self.get_public_key_async(key_pair_id))
308    }
309
310    fn get_or_create_ephemeral_keys(
311        &self,
312        key_pair_id: KeyPairId,
313        epoch: EpochTime,
314    ) -> Result<KeyPair, KeyManagerError> {
315        block_on(self.get_or_create_ephemeral_keys_async(key_pair_id, epoch))
316    }
317
318    fn get_public_ephemeral_key(
319        &self,
320        key_pair_id: KeyPairId,
321        epoch: EpochTime,
322    ) -> Result<SignedPublicKey, KeyManagerError> {
323        block_on(self.get_public_ephemeral_key_async(key_pair_id, epoch))
324    }
325
326    fn box_clone(&self) -> Box<dyn KeyManager> {
327        Box::new(self.clone())
328    }
329}
330
331/// Key pair ID domain separation context.
332pub const KEY_PAIR_ID_CONTEXT: &[u8] = b"oasis-runtime-sdk/keymanager: key pair id";
333
334/// Derive a `KeyPairId` for use with the key manager functions.
335pub fn get_key_pair_id<'a, C>(context: C) -> KeyPairId
336where
337    C: IntoIterator<Item = &'a [u8]> + 'a,
338{
339    let mut h = TupleHash::v256(KEY_PAIR_ID_CONTEXT);
340    for item in context.into_iter() {
341        h.update(item);
342    }
343    let mut key_pair_id = [0u8; 32];
344    h.finalize(&mut key_pair_id);
345
346    KeyPairId(key_pair_id)
347}