mpc_engine/primitives/
commitment.rs1use hacspec_lib::Randomness;
10use hmac::hkdf_extract;
11
12use crate::{Error, STATISTICAL_SECURITY};
13
14pub const COMMITMENT_LENGTH: usize = 32;
17
18#[derive(Debug, Clone)]
20pub struct Commitment {
21 commitment: [u8; COMMITMENT_LENGTH],
22 domain_separator: Vec<u8>,
23}
24
25#[derive(Debug, Clone)]
27pub struct Opening {
28 value: Vec<u8>,
29 opening: [u8; STATISTICAL_SECURITY],
30}
31
32impl Opening {
33 pub fn as_bytes(&self) -> Vec<u8> {
38 let mut result = Vec::new();
39 result.extend_from_slice(&self.opening);
40 result.extend_from_slice(&self.value);
41
42 result
43 }
44
45 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
47 if bytes.len() < STATISTICAL_SECURITY + 1 {
48 return Err(Error::InvalidSerialization);
49 }
50 let opening: [u8; STATISTICAL_SECURITY] = Vec::from(&bytes[0..STATISTICAL_SECURITY])
51 .try_into()
52 .map_err(|_| Error::InvalidSerialization)?;
53 let value = Vec::from(&bytes[STATISTICAL_SECURITY..]);
54 Ok(Self { value, opening })
55 }
56}
57
58impl Commitment {
59 pub fn new(value: &[u8], domain_separator: &[u8], entropy: &mut Randomness) -> (Self, Opening) {
65 let mut opening = [0u8; STATISTICAL_SECURITY];
66 opening.copy_from_slice(
67 entropy
68 .bytes(STATISTICAL_SECURITY)
69 .expect("sufficient randomness should have been provided externally"),
70 );
71
72 let mut ikm = Vec::from(value);
73 ikm.extend_from_slice(&opening);
74
75 let commitment = hkdf_extract(domain_separator, &ikm)
76 .try_into()
77 .expect("should use HKDF with SHA-256 for correct output length");
78 (
79 Commitment {
80 commitment,
81 domain_separator: domain_separator.to_vec(),
82 },
83 Opening {
84 value: value.to_vec(),
85 opening,
86 },
87 )
88 }
89
90 pub fn open(&self, opening: &Opening) -> Result<Vec<u8>, Error> {
92 let mut ikm = vec![0u8; opening.value.len()];
93 ikm.copy_from_slice(&opening.value);
94 ikm.extend_from_slice(&opening.opening);
95 let reconstructed_commitment: [u8; COMMITMENT_LENGTH] =
96 hkdf_extract(&self.domain_separator, &ikm)
97 .try_into()
98 .expect("should use HKDF with SHA-256 for correct output length");
99 if self.commitment != reconstructed_commitment {
100 return Err(Error::BadCommitment(
101 self.commitment,
102 reconstructed_commitment,
103 ));
104 }
105 Ok(opening.value.to_vec())
106 }
107
108 pub fn as_bytes(&self) -> Vec<u8> {
114 let dst_len = self.domain_separator.len();
115 let mut result = Vec::new();
116 result.extend_from_slice(&self.commitment);
117 result.extend_from_slice(&dst_len.to_be_bytes());
118 result.extend_from_slice(&self.domain_separator);
119 result
120 }
121
122 pub fn from_bytes(bytes: &[u8]) -> Result<(Self, Vec<u8>), Error> {
124 if bytes.len() < COMMITMENT_LENGTH + std::mem::size_of::<usize>() {
125 return Err(Error::InvalidSerialization);
126 }
127 let (commitment_bytes, rest) = bytes.split_at(COMMITMENT_LENGTH);
128 let commitment = commitment_bytes.try_into().unwrap();
129
130 let (dst_len_bytes, rest) = rest.split_at(std::mem::size_of::<usize>());
131 let dst_len = usize::from_be_bytes(dst_len_bytes.try_into().unwrap());
132 let (dst_bytes, rest) = rest.split_at(dst_len);
133 let domain_separator = Vec::from(dst_bytes);
134
135 Ok((
136 Self {
137 commitment,
138 domain_separator,
139 },
140 rest.to_vec(),
141 ))
142 }
143}
144
145#[test]
146fn simple() {
147 use rand::{thread_rng, RngCore};
148
149 let mut rng = thread_rng();
150 let mut entropy = [0u8; 32];
151 rng.fill_bytes(&mut entropy);
152 let mut entropy = Randomness::new(entropy.to_vec());
153 let value = b"Hello";
154 let another_value = b"Heya";
155 let dst = b"Test";
156 let (commitment, opening) = Commitment::new(value, dst, &mut entropy);
157 let (_another_commitment, another_opening) = Commitment::new(another_value, dst, &mut entropy);
158 debug_assert!(commitment.open(&opening).is_ok());
159 debug_assert!(commitment.open(&another_opening).is_err());
160}