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}