oasis_core_runtime/consensus/state/
keymanager.rs

1//! Key manager state in the consensus layer.
2use anyhow::anyhow;
3
4use crate::{
5    common::{
6        crypto::{hash::Hash, signature::PublicKey},
7        key_format::{KeyFormat, KeyFormatAtom},
8        namespace::Namespace,
9    },
10    consensus::{
11        beacon::EpochTime,
12        keymanager::{
13            SignedEncryptedEphemeralSecret, SignedEncryptedMasterSecret, SignedPolicySGX,
14        },
15        state::StateError,
16    },
17    key_format,
18    storage::mkvs::ImmutableMKVS,
19};
20
21pub mod churp;
22
23/// Consensus key manager state wrapper.
24pub struct ImmutableState<'a, T: ImmutableMKVS> {
25    mkvs: &'a T,
26}
27
28impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
29    /// Constructs a new ImmutableMKVS.
30    pub fn new(mkvs: &'a T) -> ImmutableState<'a, T> {
31        ImmutableState { mkvs }
32    }
33}
34
35key_format!(StatusKeyFmt, 0x70, Hash);
36key_format!(MasterSecretKeyFmt, 0x72, Hash);
37key_format!(EphemeralSecretKeyFmt, 0x73, Hash);
38
39/// Current key manager status.
40#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Decode, cbor::Encode)]
41pub struct Status {
42    /// Runtime ID of the key manager.
43    pub id: Namespace,
44    /// True iff the key manager is done initializing.
45    pub is_initialized: bool,
46    /// True iff the key manager is secure.
47    pub is_secure: bool,
48    /// Generation of the latest master secret.
49    pub generation: u64,
50    /// Epoch of the last master secret rotation.
51    pub rotation_epoch: EpochTime,
52    /// Key manager master secret verification checksum.
53    pub checksum: Vec<u8>,
54    /// List of currently active key manager node IDs.
55    pub nodes: Vec<PublicKey>,
56    /// Key manager policy.
57    pub policy: Option<SignedPolicySGX>,
58    /// Runtime signing key of the key manager.
59    pub rsk: Option<PublicKey>,
60}
61
62impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
63    /// Looks up a specific key manager status by its namespace identifier.
64    pub fn status(&self, id: Namespace) -> Result<Option<Status>, StateError> {
65        let h = Hash::digest_bytes(id.as_ref());
66        match self.mkvs.get(&StatusKeyFmt(h).encode()) {
67            Ok(Some(b)) => Ok(Some(self.decode_status(&b)?)),
68            Ok(None) => Ok(None),
69            Err(err) => Err(StateError::Unavailable(anyhow!(err))),
70        }
71    }
72
73    /// Returns the list of all key manager statuses.
74    pub fn statuses(&self) -> Result<Vec<Status>, StateError> {
75        let mut it = self.mkvs.iter();
76        it.seek(&StatusKeyFmt::default().encode_partial(0));
77
78        let mut result: Vec<Status> = Vec::new();
79
80        while let Some(value) = it
81            .next()
82            .and_then(|(key, value)| StatusKeyFmt::decode(&key).map(|_| value))
83        {
84            result.push(self.decode_status(&value)?)
85        }
86
87        Ok(result)
88    }
89
90    /// Looks up a specific key manager master secret by its namespace identifier.
91    pub fn master_secret(
92        &self,
93        id: Namespace,
94    ) -> Result<Option<SignedEncryptedMasterSecret>, StateError> {
95        let h = Hash::digest_bytes(id.as_ref());
96        match self.mkvs.get(&MasterSecretKeyFmt(h).encode()) {
97            Ok(Some(b)) => Ok(Some(self.decode_master_secret(&b)?)),
98            Ok(None) => Ok(None),
99            Err(err) => Err(StateError::Unavailable(anyhow!(err))),
100        }
101    }
102
103    /// Looks up a specific key manager ephemeral secret by its namespace identifier and epoch.
104    pub fn ephemeral_secret(
105        &self,
106        id: Namespace,
107    ) -> Result<Option<SignedEncryptedEphemeralSecret>, StateError> {
108        let h = Hash::digest_bytes(id.as_ref());
109        match self.mkvs.get(&EphemeralSecretKeyFmt(h).encode()) {
110            Ok(Some(b)) => Ok(Some(self.decode_ephemeral_secret(&b)?)),
111            Ok(None) => Ok(None),
112            Err(err) => Err(StateError::Unavailable(anyhow!(err))),
113        }
114    }
115
116    fn decode_status(&self, data: &[u8]) -> Result<Status, StateError> {
117        cbor::from_slice(data).map_err(|err| StateError::Unavailable(anyhow!(err)))
118    }
119
120    fn decode_master_secret(&self, data: &[u8]) -> Result<SignedEncryptedMasterSecret, StateError> {
121        cbor::from_slice(data).map_err(|err| StateError::Unavailable(anyhow!(err)))
122    }
123
124    fn decode_ephemeral_secret(
125        &self,
126        data: &[u8],
127    ) -> Result<SignedEncryptedEphemeralSecret, StateError> {
128        cbor::from_slice(data).map_err(|err| StateError::Unavailable(anyhow!(err)))
129    }
130}
131
132#[cfg(test)]
133mod test {
134    use std::collections::HashMap;
135
136    use rustc_hex::FromHex;
137
138    use super::*;
139    use crate::{
140        common::{
141            crypto::{
142                signature::{Signature, SignatureBundle},
143                x25519,
144            },
145            sgx::{EnclaveIdentity, MrEnclave, MrSigner},
146        },
147        consensus::keymanager::{
148            EnclavePolicySGX, EncryptedEphemeralSecret, EncryptedSecret, PolicySGX,
149        },
150        storage::mkvs::{
151            interop::{Fixture, ProtocolServer},
152            Root, RootType, Tree,
153        },
154    };
155
156    #[test]
157    fn test_keymanager_secrets_state_interop() {
158        // Keep in sync with go/consensus/cometbft/apps/keymanager/secrets/state/interop/interop.go.
159        // If mock consensus state changes, update the root hash bellow.
160        // See protocol server stdout for hash.
161        // To make the hash show up during tests, run "cargo test" as
162        // "cargo test -- --nocapture".
163
164        // Setup protocol server with initialized mock consensus state.
165        let server = ProtocolServer::new(Fixture::ConsensusMock.into());
166        let mock_consensus_root = Root {
167            version: 1,
168            root_type: RootType::State,
169            hash: Hash::from("8e39bf193f8a954ab8f8d7cb6388c591fd0785ea060bbd8e3752e266b54499d3"),
170            ..Default::default()
171        };
172        let mkvs = Tree::builder()
173            .with_capacity(100_000, 10_000_000)
174            .with_root(mock_consensus_root)
175            .build(server.read_sync());
176        let keymanager_state = ImmutableState::new(&mkvs);
177
178        // Prepare expected results.
179        let runtime =
180            Namespace::from("8000000000000000000000000000000000000000000000000000000000000000");
181        let keymanager1 =
182            Namespace::from("c000000000000000fffffffffffffffffffffffffffffffffffffffffffffffe");
183        let keymanager2 =
184            Namespace::from("c000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff");
185        let runtime_enclave = EnclaveIdentity {
186            mr_enclave: MrEnclave::from(
187                "18256f783c071521be2da041cd9347b5bdb5a8ef58fb34658571a6e14cf1fcb0",
188            ),
189            mr_signer: MrSigner::from(
190                "e48049d1de0eb333523991671a6c93b97dd65bcf09273d5b6bfe8262dc968ec7",
191            ),
192        };
193        let keymanager_enclave1 = EnclaveIdentity {
194            mr_enclave: MrEnclave::from(
195                "c9a589851b1f35627177fd70378ed778170f737611e4dfbf0b6d25bdff55b474",
196            ),
197            mr_signer: MrSigner::from(
198                "7d310664780931ae103ab30a90171c201af385a72757bb4683578fdebde9adf5",
199            ),
200        };
201        let keymanager_enclave2 = EnclaveIdentity {
202            mr_enclave: MrEnclave::from(
203                "756eaf76f5482c5345808b1eaccdd5c60f864bb2aa2d2b870df00ce435af4e23",
204            ),
205            mr_signer: MrSigner::from(
206                "3597a2ff0743016f28e5d7e129304ee1c43dbdae3dba94e19cee3549038a5a32",
207            ),
208        };
209        let signer1 =
210            PublicKey::from("96533c123a6f4d33c68357109c2eb7c6e6a0f947be3ae1e320d153f561523ff2");
211        let signer2 =
212            PublicKey::from("4b97bfd95e829d5838131492b5c133e66ac6ef0db414c0be6207ec78c12d2b17");
213        let sig1 = Signature::from("eda666cff6e4030200737e0c7707ad4a378aab4cc0455306992c13da2155b97c91b0fde0325a7a6818f2cbf92813cc587723c8c205a7cb5389ca7b21a038b60a");
214        let sig2 = Signature::from("db90d354272e025aa9a5856f32ea4f5d6becb0ff6340f3cb7f9104ac04ef29ed4f9b5c21b7ea82924800b30f94724b40c376414f80780ff8b7b60a34edea9f02");
215        let checksum = "1bff211fae98c88ba82388ae954b88a71d3bbe327e162e9fa711fe7a1b759c3e"
216            .from_hex()
217            .unwrap();
218
219        let expected_statuses = vec![
220            Status {
221                id: keymanager1,
222                is_initialized: false,
223                is_secure: false,
224                generation: 0,
225                rotation_epoch: 0,
226                checksum: vec![],
227                nodes: vec![],
228                policy: None,
229                rsk: None,
230            },
231            Status {
232                id: keymanager2,
233                is_initialized: true,
234                is_secure: true,
235                generation: 0,
236                rotation_epoch: 0,
237                checksum: checksum,
238                nodes: vec![signer1, signer2],
239                policy: Some(SignedPolicySGX {
240                    policy: PolicySGX {
241                        serial: 1,
242                        id: keymanager2,
243                        enclaves: HashMap::from([(
244                            keymanager_enclave1,
245                            EnclavePolicySGX {
246                                may_query: HashMap::from([(runtime, vec![runtime_enclave])]),
247                                may_replicate: vec![keymanager_enclave2],
248                            },
249                        )]),
250                        master_secret_rotation_interval: 0,
251                        max_ephemeral_secret_age: 10,
252                    },
253                    signatures: vec![
254                        SignatureBundle {
255                            public_key: signer1,
256                            signature: sig1,
257                        },
258                        SignatureBundle {
259                            public_key: signer2,
260                            signature: sig2,
261                        },
262                    ],
263                }),
264                rsk: None,
265            },
266        ];
267
268        let rek1 = x25519::PrivateKey::from_test_seed("first rek".to_string());
269        let rek2 = x25519::PrivateKey::from_test_seed("second rek".to_string());
270
271        let expected_secret = SignedEncryptedEphemeralSecret {
272            secret: EncryptedEphemeralSecret {
273                runtime_id: keymanager1,
274                epoch: 1,
275                secret: EncryptedSecret{
276                    checksum: vec![1,2,3,4,5],
277                    pub_key: rek1.public_key(),
278                    ciphertexts: HashMap::from([
279                        (rek1.public_key(), vec![1, 2, 3]),
280                        (rek2.public_key(), vec![4, 5, 6]),
281                    ]),
282                },
283            },
284            signature: Signature::from("4a2d098e02411fdc14d6a36f91bb362fd4f4dbaadb4cbf70e20e038fe1740bc7dde0b20afd25657d6abc916be2b9ed0054d586aedb2b7951c99aab3206b24b02"),
285        };
286
287        // Test statuses.
288        let mut statuses = keymanager_state
289            .statuses()
290            .expect("statuses query should work");
291        statuses.sort_by(|a, b| a.id.partial_cmp(&b.id).unwrap());
292        assert_eq!(statuses, expected_statuses, "invalid statuses");
293
294        // Test status.
295        let status = keymanager_state
296            .status(expected_statuses[1].id)
297            .expect("status query should work")
298            .expect("status query should return a result");
299        assert_eq!(status, expected_statuses[1], "invalid status");
300
301        let id =
302            Namespace::from("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
303        let status = keymanager_state
304            .status(id)
305            .expect("status query should work");
306        assert_eq!(status, None, "invalid status");
307
308        // Test ephemeral secret (happy path, invalid epoch, invalid runtime).
309        let secret = keymanager_state
310            .ephemeral_secret(keymanager1)
311            .expect("ephemeral secret query should work")
312            .expect("ephemeral secret query should return a result");
313        assert_eq!(secret, expected_secret, "invalid ephemeral secret");
314    }
315}