holochain_conductor_services/
dpki_service.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::sync::Arc;

pub use hc_deepkey_sdk::*;
use holochain_types::prelude::*;
use holochain_util::timed;

pub mod derivation_paths;

mod deepkey;
pub use deepkey::*;

use crate::CellRunner;

/// This magic string, when used as the installed app id, denotes that the app
/// is not actually an app, but the DPKI service! This is now a reserved app id,
/// and is used to distinguish the DPKI service from other apps.
pub const DPKI_APP_ID: &str = "DPKI";

pub type DpkiImpl = Arc<DpkiService>;

pub struct DpkiService {
    /// Mirrored from the State.
    /// Note, this is a little weird for DPKI implementations which are not backed by a Holochain DNA.
    /// In that case, the impl still needs an AgentPubKey to sign new key registrations with, and it still
    /// needs a unique identifier to advertise network compatibility, which is covered by the DnaHash.
    /// So such an implementation should just use 32 unique bytes and create a DnaHash from that, to be
    /// used in this CellId.
    pub cell_id: CellId,

    /// State must be accessed through a Mutex
    state: tokio::sync::Mutex<Box<dyn DpkiState>>,
}

/// Interface for the DPKI service
impl DpkiService {
    pub fn new(cell_id: CellId, state: impl DpkiState + 'static) -> Self {
        let state: Box<dyn DpkiState> = Box::new(state);
        let state = tokio::sync::Mutex::new(state);
        Self { cell_id, state }
    }

    /// Whether the passed in DNA hash is Deepkey DNA hash
    pub fn is_deepkey_dna(&self, dna_hash: &DnaHash) -> bool {
        self.cell_id.dna_hash() == dna_hash
    }

    /// Get the UUID of the DPKI service.
    pub fn uuid(&self) -> [u8; 32] {
        self.cell_id.dna_hash().get_raw_32().try_into().unwrap()
    }

    pub fn new_deepkey(installation: DeepkeyInstallation, runner: Arc<impl CellRunner>) -> Self {
        let state: Box<dyn DpkiState> = Box::new(DeepkeyState {
            runner,
            cell_id: installation.cell_id.clone(),
        });
        let cell_id = installation.cell_id;
        let state = tokio::sync::Mutex::new(state);
        Self { cell_id, state }
    }

    pub async fn state(&self) -> tokio::sync::MutexGuard<Box<dyn DpkiState>> {
        timed!([1, 10, 1000], { self.state.lock().await })
    }
}

#[async_trait::async_trait]
#[mockall::automock]
pub trait DpkiState: Send + Sync {
    /// Get derivation details for the next key in a lineage.
    async fn next_derivation_details(
        &self,
        agent_key: AgentPubKey,
    ) -> DpkiServiceResult<DerivationDetails>;

    /// Create a new key for a given app.
    async fn register_key(
        &self,
        input: CreateKeyInput,
    ) -> DpkiServiceResult<(ActionHash, KeyRegistration, KeyMeta)>;

    /// Query meta data for a given key.
    async fn query_key_meta(&self, key: AgentPubKey) -> DpkiServiceResult<KeyMeta>;

    /// Revoke a registered key.
    async fn revoke_key(
        &self,
        input: RevokeKeyInput,
    ) -> DpkiServiceResult<(ActionHash, KeyRegistration)>;

    /// Check if the key is valid (properly created and not revoked) as-at the given Timestamp.
    async fn key_state(
        &self,
        key: AgentPubKey,
        timestamp: Timestamp,
    ) -> DpkiServiceResult<KeyState>;

    /// Get lineage of all keys of an agent, ordered by timestamp.
    async fn get_agent_key_lineage(&self, key: AgentPubKey) -> DpkiServiceResult<Vec<AgentPubKey>>;

    /// Check if the two provided agent keys belong to the same agent.
    async fn is_same_agent(
        &self,
        key_1: AgentPubKey,
        key_2: AgentPubKey,
    ) -> DpkiServiceResult<bool>;
}

/// The errors which can be produced by DPKI
#[derive(thiserror::Error, Debug)]
#[allow(missing_docs)]
pub enum DpkiServiceError {
    #[error("DPKI DNA call failed: {0}")]
    ZomeCallFailed(anyhow::Error),
    #[error(transparent)]
    Serialization(#[from] SerializedBytesError),
    #[error("Error talking to lair keystore: {0}")]
    Lair(anyhow::Error),
    #[error("DPKI service not installed")]
    DpkiNotInstalled,
    #[error("The agent {0} could not be found in DPKI")]
    DpkiAgentMissing(AgentPubKey),
    #[error("The agent {0} was found to be invalid at {1} according to the DPKI service")]
    DpkiAgentInvalid(AgentPubKey, Timestamp),
}
/// Alias
pub type DpkiServiceResult<T> = Result<T, DpkiServiceError>;