Skip to main content

fastcrypto_tbls/
random_oracle.rs

1// Copyright (c) 2022, Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use digest::Digest;
5use serde::{Deserialize, Serialize};
6use sha3::Sha3_512;
7use std::fmt::Debug;
8
9/// Random Oracle from SHA3-512.
10/// - prefix should be globally unique.
11/// - evaluate serializes the given input and outputs SHA3-512(prefix_len as big-endian u32 | prefix | input).
12/// - Subprotocols may use a prefix that is extended from the prefix of its parent protocol, by
13///   deriving a new instance using extend, which simply concatenates the strings with the separator
14///   "-". E.g., RandomOracle::new("abc").extend("def") = RandomOracle::new("abc-def").
15///
16/// The caller must make sure to:
17/// - Choose distinct prefix & extension strings, preferably without "-" in them (asserted in debug
18///   mode).
19/// - Ensure that the length of prefix & extension is small enough to fit in u32.
20///   Violating this constraint will cause a panic.
21
22#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
23pub struct RandomOracle {
24    prefix: String,
25}
26
27impl RandomOracle {
28    /// Create a fresh random oracle with a given "session id"/prefix.
29    pub fn new(initial_prefix: &str) -> Self {
30        debug_assert!(!initial_prefix.contains('-'));
31        // Since we shouldn't get such long prefixes, it's safe to assert here.
32        assert!(initial_prefix.len() < u32::MAX as usize);
33        Self {
34            prefix: initial_prefix.into(),
35        }
36    }
37
38    /// Evaluate the random oracle on a given input.
39    pub fn evaluate<T: Serialize>(&self, obj: &T) -> [u8; 64] {
40        let mut hasher = Sha3_512::default();
41        let len: u32 = self
42            .prefix
43            .len()
44            .try_into()
45            .expect("prefix length should be less than u32::MAX, checked when set");
46        hasher.update(len.to_be_bytes());
47        hasher.update(&self.prefix);
48        let serialized = bcs::to_bytes(obj).expect("serialize should never fail");
49        hasher.update(&serialized);
50        hasher.finalize().into()
51    }
52
53    /// Derive a new random oracle from the current one and additional string (can be done multiple times).
54    pub fn extend(&self, extension: &str) -> Self {
55        debug_assert!(!extension.contains('-'));
56        // Since we shouldn't get such long prefixes, it's safe to assert here.
57        assert!(self.prefix.len() + extension.len() + 1 < u32::MAX as usize);
58        Self {
59            prefix: self.prefix.clone() + "-" + extension,
60        }
61    }
62}