Skip to main content

oasis_runtime_sdk/crypto/
random.rs

1//! Random number generator based on root VRF key and Merlin transcripts.
2use std::{cell::RefCell, convert::Infallible};
3
4use anyhow::anyhow;
5use merlin::{Transcript, TranscriptRng};
6use rand::{rngs::SysRng, TryCryptoRng, TryRng};
7use rand_core::RngCore;
8use schnorrkel::keys::{ExpansionMode, Keypair, MiniSecretKey};
9
10use oasis_core_runtime::common::crypto::hash::Hash;
11
12use crate::{
13    context::Context, dispatcher, keymanager::KeyManagerError, modules::core::Error, state::Mode,
14};
15
16/// RNG domain separation context.
17const RNG_CONTEXT: &[u8] = b"oasis-runtime-sdk/crypto: rng v1";
18/// Per-block root VRF key domain separation context.
19const VRF_KEY_CONTEXT: &[u8] = b"oasis-runtime-sdk/crypto: root vrf key v1";
20
21/// A root RNG that can be used to derive domain-separated leaf RNGs.
22pub struct RootRng {
23    inner: RefCell<Inner>,
24    mode: Mode,
25    valid: bool,
26}
27
28struct Inner {
29    /// Merlin transcript for initializing the RNG.
30    transcript: Transcript,
31    /// A transcript-based RNG (when initialized).
32    rng: Option<TranscriptRng>,
33}
34
35impl RootRng {
36    /// Create a new root RNG.
37    pub fn new(mode: Mode) -> Self {
38        Self {
39            inner: RefCell::new(Inner {
40                transcript: Transcript::new(RNG_CONTEXT),
41                rng: None,
42            }),
43            mode,
44            valid: true,
45        }
46    }
47
48    /// Create an invalid root RNG which will fail when any leaf RNGs are requested.
49    pub fn invalid() -> Self {
50        Self {
51            inner: RefCell::new(Inner {
52                transcript: Transcript::new(&[]),
53                rng: None,
54            }),
55            mode: Mode::Simulate, // Use a "safe" mode even though it will never be used.
56            valid: false,
57        }
58    }
59
60    fn derive_root_vrf_key<C: Context + ?Sized>(ctx: &C, mode: Mode) -> Result<Keypair, Error> {
61        let km = ctx
62            .key_manager()
63            .ok_or(Error::Abort(dispatcher::Error::KeyManagerFailure(
64                KeyManagerError::NotInitialized,
65            )))?;
66        let round_header_hash = ctx.runtime_header().encoded_hash();
67        let key_id = crate::keymanager::get_key_pair_id([
68            VRF_KEY_CONTEXT,
69            &[mode as u8],
70            round_header_hash.as_ref(),
71        ]);
72        let km_kp = km
73            .get_or_create_ephemeral_keys(key_id, ctx.epoch())
74            .map_err(|err| Error::Abort(dispatcher::Error::KeyManagerFailure(err)))?
75            .input_keypair;
76        // The KM returns an ed25519 key, but it needs to be in "expanded" form to use with
77        // schnorrkel. Please refer to [`schnorrkel::keys::MiniSecretKey`] for further details.
78        let kp = MiniSecretKey::from_bytes(km_kp.sk.0.as_ref())
79            .map_err(|err| {
80                Error::Abort(dispatcher::Error::KeyManagerFailure(
81                    KeyManagerError::Other(anyhow::anyhow!("{err}")),
82                ))
83            })?
84            .expand_to_keypair(ExpansionMode::Uniform);
85
86        Ok(kp)
87    }
88
89    /// Append local entropy to the root RNG.
90    ///
91    /// # Non-determinism
92    ///
93    /// Using this method will result in the RNG being non-deterministic.
94    pub fn append_local_entropy(&self) {
95        if !self.valid {
96            return;
97        }
98
99        let mut bytes = [0u8; 32];
100        SysRng.try_fill_bytes(&mut bytes).unwrap();
101
102        let mut inner = self.inner.borrow_mut();
103        inner.transcript.append_message(b"local-rng", &bytes);
104    }
105
106    /// Append an observed transaction hash to RNG transcript.
107    pub fn append_tx(&self, tx_hash: Hash) {
108        if !self.valid {
109            return;
110        }
111
112        let mut inner = self.inner.borrow_mut();
113        inner.transcript.append_message(b"tx", tx_hash.as_ref());
114    }
115
116    /// Append an observed subcontext to RNG transcript.
117    pub fn append_subcontext(&self) {
118        if !self.valid {
119            return;
120        }
121
122        let mut inner = self.inner.borrow_mut();
123        inner.transcript.append_message(b"subctx", &[]);
124    }
125
126    /// Create an independent leaf RNG using this RNG as its parent.
127    pub fn fork<C: Context + ?Sized>(&self, ctx: &C, pers: &[u8]) -> Result<LeafRng, Error> {
128        if !self.valid {
129            return Err(Error::InvalidArgument(anyhow!("rng is not available")));
130        }
131
132        let mut inner = self.inner.borrow_mut();
133
134        // Ensure the RNG is initialized and initialize it if not.
135        if inner.rng.is_none() {
136            // Derive the root VRF key for the current block.
137            let root_vrf_key = Self::derive_root_vrf_key(ctx, self.mode)?;
138
139            // Initialize the root RNG.
140            let rng = root_vrf_key
141                .vrf_create_hash(&mut inner.transcript)
142                .make_merlin_rng(&[]);
143            inner.rng = Some(rng);
144        }
145
146        // Generate the leaf RNG.
147        inner.transcript.append_message(b"fork", pers);
148
149        let rng_builder = inner.transcript.build_rng();
150        let parent_rng = inner.rng.as_mut().expect("rng must be initialized");
151        let rng = rng_builder.finalize(parent_rng);
152
153        Ok(LeafRng(rng))
154    }
155}
156
157/// A leaf RNG.
158pub struct LeafRng(TranscriptRng);
159
160impl TryRng for LeafRng {
161    type Error = Infallible;
162
163    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
164        Ok(self.0.next_u32())
165    }
166
167    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
168        Ok(self.0.next_u64())
169    }
170
171    fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
172        self.0.fill_bytes(dst);
173        Ok(())
174    }
175}
176
177impl TryCryptoRng for LeafRng {}
178
179#[cfg(test)]
180mod test {
181    use super::*;
182
183    use crate::{state::Mode, testing::mock};
184
185    #[test]
186    fn test_rng_basic() {
187        let mut mock = mock::Mock::default();
188        let ctx = mock.create_ctx_for_runtime::<mock::EmptyRuntime>(true);
189
190        // Create first root RNG.
191        let root_rng = RootRng::new(Mode::Execute);
192
193        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
194        let mut bytes1 = [0u8; 32];
195        leaf_rng.try_fill_bytes(&mut bytes1).unwrap();
196
197        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
198        let mut bytes1_1 = [0u8; 32];
199        leaf_rng.try_fill_bytes(&mut bytes1_1).unwrap();
200
201        assert_ne!(bytes1, bytes1_1, "rng should apply domain separation");
202
203        // Create second root RNG using the same context so the ephemeral key is shared.
204        let root_rng = RootRng::new(Mode::Execute);
205
206        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
207        let mut bytes2 = [0u8; 32];
208        leaf_rng.try_fill_bytes(&mut bytes2).unwrap();
209
210        assert_eq!(bytes1, bytes2, "rng should be deterministic");
211
212        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
213        let mut bytes2_1 = [0u8; 32];
214        leaf_rng.try_fill_bytes(&mut bytes2_1).unwrap();
215
216        assert_ne!(bytes2, bytes2_1, "rng should apply domain separation");
217        assert_eq!(bytes1_1, bytes2_1, "rng should be deterministic");
218
219        // Create third root RNG using the same context, but with different personalization.
220        let root_rng = RootRng::new(Mode::Execute);
221
222        let mut leaf_rng = root_rng
223            .fork(&ctx, b"domsep")
224            .expect("rng fork should work");
225        let mut bytes3 = [0u8; 32];
226        leaf_rng.try_fill_bytes(&mut bytes3).unwrap();
227
228        assert_ne!(bytes2, bytes3, "rng should apply domain separation");
229
230        // Create another root RNG using the same context, but with different history.
231        let root_rng = RootRng::new(Mode::Execute);
232        root_rng
233            .append_tx("0000000000000000000000000000000000000000000000000000000000000001".into());
234
235        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
236        let mut bytes4 = [0u8; 32];
237        leaf_rng.try_fill_bytes(&mut bytes4).unwrap();
238
239        assert_ne!(bytes2, bytes4, "rng should apply domain separation");
240
241        // Create another root RNG using the same context, but with different history.
242        let root_rng = RootRng::new(Mode::Execute);
243        root_rng
244            .append_tx("0000000000000000000000000000000000000000000000000000000000000002".into());
245
246        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
247        let mut bytes5 = [0u8; 32];
248        leaf_rng.try_fill_bytes(&mut bytes5).unwrap();
249
250        assert_ne!(bytes4, bytes5, "rng should apply domain separation");
251
252        // Create another root RNG using the same context, but with same history as four.
253        let root_rng = RootRng::new(Mode::Execute);
254        root_rng
255            .append_tx("0000000000000000000000000000000000000000000000000000000000000001".into());
256
257        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
258        let mut bytes6 = [0u8; 32];
259        leaf_rng.try_fill_bytes(&mut bytes6).unwrap();
260
261        assert_eq!(bytes4, bytes6, "rng should be deterministic");
262
263        // Create another root RNG using the same context, but with different history.
264        let root_rng = RootRng::new(Mode::Execute);
265        root_rng
266            .append_tx("0000000000000000000000000000000000000000000000000000000000000001".into());
267        root_rng
268            .append_tx("0000000000000000000000000000000000000000000000000000000000000002".into());
269
270        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
271        let mut bytes7 = [0u8; 32];
272        leaf_rng.try_fill_bytes(&mut bytes7).unwrap();
273
274        assert_ne!(bytes4, bytes7, "rng should apply domain separation");
275
276        // Create another root RNG using the same context, but with different init point.
277        let root_rng = RootRng::new(Mode::Execute);
278        root_rng
279            .append_tx("0000000000000000000000000000000000000000000000000000000000000001".into());
280        let _ = root_rng.fork(&ctx, &[]).expect("rng fork should work"); // Force init.
281        root_rng
282            .append_tx("0000000000000000000000000000000000000000000000000000000000000002".into());
283
284        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
285        let mut bytes8 = [0u8; 32];
286        leaf_rng.try_fill_bytes(&mut bytes8).unwrap();
287
288        assert_ne!(bytes7, bytes8, "rng should apply domain separation");
289        assert_ne!(bytes6, bytes8, "rng should apply domain separation");
290    }
291
292    #[test]
293    fn test_rng_fail_nonconfidential() {
294        let mut mock = mock::Mock::default();
295        let ctx = mock.create_ctx_for_runtime::<mock::EmptyRuntime>(false);
296
297        let root_rng = RootRng::new(Mode::Execute);
298        assert!(
299            root_rng.fork(&ctx, &[]).is_err(),
300            "rng fork should fail on non-confidential runtimes"
301        );
302    }
303
304    #[test]
305    fn test_rng_local_entropy() {
306        let mut mock = mock::Mock::default();
307        let ctx = mock.create_ctx_for_runtime::<mock::EmptyRuntime>(true);
308
309        // Create first root RNG.
310        let root_rng = RootRng::new(Mode::Execute);
311
312        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
313        let mut bytes1 = [0u8; 32];
314        leaf_rng.try_fill_bytes(&mut bytes1).unwrap();
315
316        // Create second root RNG using the same context, but mix in local entropy.
317        let root_rng = RootRng::new(Mode::Execute);
318        root_rng.append_local_entropy();
319
320        let mut leaf_rng = root_rng.fork(&ctx, &[]).expect("rng fork should work");
321        let mut bytes2 = [0u8; 32];
322        leaf_rng.try_fill_bytes(&mut bytes2).unwrap();
323
324        assert_ne!(bytes1, bytes2, "rng should apply domain separation");
325    }
326
327    #[test]
328    fn test_rng_parent_fork_propagation() {
329        let mut mock = mock::Mock::default();
330        let ctx = mock.create_ctx_for_runtime::<mock::EmptyRuntime>(true);
331
332        // Create first root RNG.
333        let root_rng = RootRng::new(Mode::Execute);
334
335        let mut leaf_rng = root_rng.fork(&ctx, b"a").expect("rng fork should work");
336        let mut bytes1 = [0u8; 32];
337        leaf_rng.try_fill_bytes(&mut bytes1).unwrap();
338
339        let mut leaf_rng = root_rng.fork(&ctx, b"a").expect("rng fork should work");
340        let mut bytes1_1 = [0u8; 32];
341        leaf_rng.try_fill_bytes(&mut bytes1_1).unwrap();
342
343        // Create second root RNG.
344        let root_rng = RootRng::new(Mode::Execute);
345
346        let mut leaf_rng = root_rng.fork(&ctx, b"b").expect("rng fork should work");
347        let mut bytes2 = [0u8; 32];
348        leaf_rng.try_fill_bytes(&mut bytes2).unwrap();
349
350        let mut leaf_rng = root_rng.fork(&ctx, b"a").expect("rng fork should work");
351        let mut bytes2_1 = [0u8; 32];
352        leaf_rng.try_fill_bytes(&mut bytes2_1).unwrap();
353
354        assert_ne!(
355            bytes1_1, bytes2_1,
356            "forks should propagate domain separator to parent"
357        );
358    }
359
360    #[test]
361    fn test_rng_invalid() {
362        let mut mock = mock::Mock::default();
363        let ctx = mock.create_ctx_for_runtime::<mock::EmptyRuntime>(true);
364
365        let root_rng = RootRng::invalid();
366        assert!(
367            root_rng.fork(&ctx, b"a").is_err(),
368            "rng fork should fail for invalid rng"
369        );
370    }
371}