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