oasis_runtime_sdk/crypto/
random.rs

1//! Random number generator based on root VRF key and Merlin transcripts.
2use std::cell::RefCell;
3
4use anyhow::anyhow;
5use merlin::{Transcript, TranscriptRng};
6use rand_core::{CryptoRng, OsRng, RngCore};
7use schnorrkel::keys::{ExpansionMode, Keypair, MiniSecretKey};
8
9use oasis_core_runtime::common::crypto::hash::Hash;
10
11use crate::{
12    context::Context, dispatcher, keymanager::KeyManagerError, modules::core::Error, state::Mode,
13};
14
15/// RNG domain separation context.
16const RNG_CONTEXT: &[u8] = b"oasis-runtime-sdk/crypto: rng v1";
17/// Per-block root VRF key domain separation context.
18const VRF_KEY_CONTEXT: &[u8] = b"oasis-runtime-sdk/crypto: root vrf key v1";
19
20/// A root RNG that can be used to derive domain-separated leaf RNGs.
21pub struct RootRng {
22    inner: RefCell<Inner>,
23    mode: Mode,
24    valid: bool,
25}
26
27struct Inner {
28    /// Merlin transcript for initializing the RNG.
29    transcript: Transcript,
30    /// A transcript-based RNG (when initialized).
31    rng: Option<TranscriptRng>,
32}
33
34impl RootRng {
35    /// Create a new root RNG.
36    pub fn new(mode: Mode) -> Self {
37        Self {
38            inner: RefCell::new(Inner {
39                transcript: Transcript::new(RNG_CONTEXT),
40                rng: None,
41            }),
42            mode,
43            valid: true,
44        }
45    }
46
47    /// Create an invalid root RNG which will fail when any leaf RNGs are requested.
48    pub fn invalid() -> Self {
49        Self {
50            inner: RefCell::new(Inner {
51                transcript: Transcript::new(&[]),
52                rng: None,
53            }),
54            mode: Mode::Simulate, // Use a "safe" mode even though it will never be used.
55            valid: false,
56        }
57    }
58
59    fn derive_root_vrf_key<C: Context + ?Sized>(ctx: &C, mode: Mode) -> Result<Keypair, Error> {
60        let km = ctx
61            .key_manager()
62            .ok_or(Error::Abort(dispatcher::Error::KeyManagerFailure(
63                KeyManagerError::NotInitialized,
64            )))?;
65        let round_header_hash = ctx.runtime_header().encoded_hash();
66        let key_id = crate::keymanager::get_key_pair_id([
67            VRF_KEY_CONTEXT,
68            &[mode as u8],
69            round_header_hash.as_ref(),
70        ]);
71        let km_kp = km
72            .get_or_create_ephemeral_keys(key_id, ctx.epoch())
73            .map_err(|err| Error::Abort(dispatcher::Error::KeyManagerFailure(err)))?
74            .input_keypair;
75        // The KM returns an ed25519 key, but it needs to be in "expanded" form to use with
76        // schnorrkel. Please refer to [`schnorrkel::keys::MiniSecretKey`] for further details.
77        let kp = MiniSecretKey::from_bytes(km_kp.sk.0.as_ref())
78            .map_err(|err| {
79                Error::Abort(dispatcher::Error::KeyManagerFailure(
80                    KeyManagerError::Other(anyhow::anyhow!("{}", err)),
81                ))
82            })?
83            .expand_to_keypair(ExpansionMode::Uniform);
84
85        Ok(kp)
86    }
87
88    /// Append local entropy to the root RNG.
89    ///
90    /// # Non-determinism
91    ///
92    /// Using this method will result in the RNG being non-deterministic.
93    pub fn append_local_entropy(&self) {
94        if !self.valid {
95            return;
96        }
97
98        let mut bytes = [0u8; 32];
99        OsRng.fill_bytes(&mut bytes);
100
101        let mut inner = self.inner.borrow_mut();
102        inner.transcript.append_message(b"local-rng", &bytes);
103    }
104
105    /// Append an observed transaction hash to RNG transcript.
106    pub fn append_tx(&self, tx_hash: Hash) {
107        if !self.valid {
108            return;
109        }
110
111        let mut inner = self.inner.borrow_mut();
112        inner.transcript.append_message(b"tx", tx_hash.as_ref());
113    }
114
115    /// Append an observed subcontext to RNG transcript.
116    pub fn append_subcontext(&self) {
117        if !self.valid {
118            return;
119        }
120
121        let mut inner = self.inner.borrow_mut();
122        inner.transcript.append_message(b"subctx", &[]);
123    }
124
125    /// Create an independent leaf RNG using this RNG as its parent.
126    pub fn fork<C: Context + ?Sized>(&self, ctx: &C, pers: &[u8]) -> Result<LeafRng, Error> {
127        if !self.valid {
128            return Err(Error::InvalidArgument(anyhow!("rng is not available")));
129        }
130
131        let mut inner = self.inner.borrow_mut();
132
133        // Ensure the RNG is initialized and initialize it if not.
134        if inner.rng.is_none() {
135            // Derive the root VRF key for the current block.
136            let root_vrf_key = Self::derive_root_vrf_key(ctx, self.mode)?;
137
138            // Initialize the root RNG.
139            let rng = root_vrf_key
140                .vrf_create_hash(&mut inner.transcript)
141                .make_merlin_rng(&[]);
142            inner.rng = Some(rng);
143        }
144
145        // Generate the leaf RNG.
146        inner.transcript.append_message(b"fork", pers);
147
148        let rng_builder = inner.transcript.build_rng();
149        let parent_rng = inner.rng.as_mut().expect("rng must be initialized");
150        let rng = rng_builder.finalize(parent_rng);
151
152        Ok(LeafRng(rng))
153    }
154}
155
156/// A leaf RNG.
157pub struct LeafRng(TranscriptRng);
158
159impl RngCore for LeafRng {
160    fn next_u32(&mut self) -> u32 {
161        self.0.next_u32()
162    }
163
164    fn next_u64(&mut self) -> u64 {
165        self.0.next_u64()
166    }
167
168    fn fill_bytes(&mut self, dest: &mut [u8]) {
169        self.0.fill_bytes(dest)
170    }
171
172    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
173        self.0.try_fill_bytes(dest)
174    }
175}
176
177impl CryptoRng 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.fill_bytes(&mut bytes1);
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.fill_bytes(&mut bytes1_1);
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.fill_bytes(&mut bytes2);
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.fill_bytes(&mut bytes2_1);
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.fill_bytes(&mut bytes3);
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.fill_bytes(&mut bytes4);
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.fill_bytes(&mut bytes5);
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.fill_bytes(&mut bytes6);
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.fill_bytes(&mut bytes7);
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.fill_bytes(&mut bytes8);
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.fill_bytes(&mut bytes1);
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.fill_bytes(&mut bytes2);
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.fill_bytes(&mut bytes1);
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.fill_bytes(&mut bytes1_1);
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.fill_bytes(&mut bytes2);
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.fill_bytes(&mut bytes2_1);
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}