hashcom_rs/lib.rs
1use bincode::Result;
2use serde::Serialize;
3use sha2::{Digest, Sha256};
4
5/// A high-level representation of a party in a Hash Commitment Scheme.
6///
7/// ### Commit Phase
8/// During the commit phase, the prover hides the secret denoted as s using a random number r.
9/// Each implementation of this trait is responsible for the usage of a dedicated hash function.
10/// Once the commitment has been built, the prover sends it to the verifier.
11///
12/// ### Open Phase
13/// During the open phase, the prover reveals his secret s and the random number r he choose to
14/// forge the commitment.
15///
16/// ### Verification Phase
17/// During this last phase, the verifier uses the prover's secret and random number
18/// to forge the expected commitment. If the prover's initial commitment differs from the
19/// expected one, the commitment has not been fulfilled by the prover.
20pub trait HashCommitmentScheme<T: Serialize> {
21 fn commit(&self) -> Result<Vec<u8>>;
22 fn verify(&self, com: &[u8], s: &T, r: &[u8]) -> Result<bool>;
23}
24
25/// An implementation of the Hash Commitment Scheme using the SHA256 hash function.
26///
27/// We store the party's secret and random number as references because we don't want to take
28/// ownership over those variables and avoid useless copies (we only perform read operations
29/// with them).
30///
31/// We use lifetime annotations as we need to store references to existing variables in our
32/// structure, so that an instance of SHA256Commitment can not outlive the references
33/// it holds.
34pub struct SHA256Commitment<'a, T: 'a + Serialize> {
35 s: &'a T,
36 r: &'a [u8],
37}
38
39impl<'a, T: 'a + Serialize> SHA256Commitment<'a, T> {
40 /// Creates a new party for the SHA256 Commitment Scheme using its secret and random
41 /// number.
42 pub fn new(s: &'a T, r: &'a [u8]) -> SHA256Commitment<'a, T> {
43 SHA256Commitment { s, r }
44 }
45
46 /// Forges a commitment given a secret s and a random number r.
47 ///
48 /// We encode the secret to a byte array (which is padded by default), and use it along with
49 /// the random number, given as a byte array, to forge the commitment using the SHA256 hash
50 /// function.
51 fn forge_commitment(&self, s: &T, r: &[u8]) -> Result<Vec<u8>> {
52 let binary_encoded_s = bincode::serialize(s)?;
53
54 let hash = Sha256::new()
55 .chain_update(binary_encoded_s.as_slice())
56 .chain_update(r)
57 .finalize();
58
59 Ok(hash.as_slice().to_vec())
60 }
61}
62
63impl<'a, T: 'a + Serialize> HashCommitmentScheme<T> for SHA256Commitment<'a, T> {
64 /// Creates the commitment used during the commit phase.
65 fn commit(&self) -> Result<Vec<u8>> {
66 self.forge_commitment(self.s, self.r)
67 }
68
69 /// Creates the expected commitment using the prover's secret and random number.
70 /// Then, compares the expected commitment with the prover's one to verify if the commitment
71 /// holds.
72 fn verify(&self, com: &[u8], s: &T, r: &[u8]) -> Result<bool> {
73 let expected_commitment = self.forge_commitment(s, r)?;
74
75 Ok(expected_commitment == com)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::{HashCommitmentScheme, SHA256Commitment};
82 use hex_literal::hex;
83
84 #[test]
85 fn it_commits_correctly() {
86 let s: [u8; 4] = [52, 50, 52, 50]; // 4242 in string format.
87 let r: [u8; 4] = [50, 52, 50, 52]; // 2424 in string format.
88
89 let party = SHA256Commitment::new(&s, &r);
90 let commit = party.commit();
91
92 assert_eq!(commit.is_ok(), true);
93 assert_eq!(
94 commit.unwrap().as_slice(),
95 hex!("f4417d2878a0e2da0393e604b24a98627fd22506089baa83c165f9ac7b336fe9")
96 )
97 }
98
99 /// Here, one party acts as both the prover and the verifier,
100 /// assuming that the verifier is not malicious.
101 #[test]
102 fn it_verifies_valid_commitment() {
103 let s: [u8; 4] = [52, 50, 52, 50]; // 4242 in string format.
104 let r: [u8; 4] = [50, 52, 50, 52]; // 2424 in string format.
105
106 // Commit phase.
107 let party = SHA256Commitment::new(&s, &r);
108 let commit = party.commit();
109
110 // Verification phase.
111 let verification = party.verify(&commit.unwrap(), &s, &r);
112
113 assert_eq!(verification.is_ok(), true);
114 assert_eq!(verification.unwrap(), true)
115 }
116
117 /// Here, during the verification phase, we assume that the prover has given an invalid r.
118 #[test]
119 fn it_fails_to_verify_due_to_invalid_random() {
120 let s: [u8; 4] = [52, 50, 52, 50]; // 4242 in string format.
121 let r: [u8; 4] = [50, 52, 50, 52]; // 2424 in string format.
122
123 // Commit phase.
124 let party = SHA256Commitment::new(&s, &r);
125 let commit = party.commit();
126
127 // Verification phase.
128 let fake_r: [u8; 4] = [66, 68, 66, 68];
129 let verification = party.verify(&commit.unwrap(), &s, &fake_r);
130
131 assert_eq!(verification.is_ok(), true);
132 assert_eq!(verification.unwrap(), false)
133 }
134
135 /// Here, during the verification phase, we assume that the prover has given an invalid secret.
136 /// This happens when the prover decides to break his initial commitment.
137 #[test]
138 fn it_fails_to_verify_due_to_invalid_secret() {
139 let s: [u8; 4] = [52, 50, 52, 50]; // 4242 in string format.
140 let r: [u8; 4] = [50, 52, 50, 52]; // 2424 in string format.
141
142 // Commit phase.
143 let party = SHA256Commitment::new(&s, &r);
144 let commit = party.commit();
145
146 // Verification phase.
147 let fake_s: [u8; 4] = [66, 68, 66, 68];
148 let verification = party.verify(&commit.unwrap(), &fake_s, &r);
149
150 assert_eq!(verification.is_ok(), true);
151 assert_eq!(verification.unwrap(), false)
152 }
153}