Skip to content

Commit cbe3710

Browse files
authored
feat: add implementation for the sha256 commitment scheme (#3)
* feat: add sha256 implementation of hashcom Signed-off-by: Luca Georges Francois <luca@quartz.technology> * docs: update cover path in main readme Signed-off-by: Luca Georges Francois <luca@quartz.technology> * docs: add instructions and examples for sha256 scheme Signed-off-by: Luca Georges Francois <luca@quartz.technology> * ci: update package version to 0.2.0 Signed-off-by: Luca Georges Francois <luca@quartz.technology> Signed-off-by: Luca Georges Francois <luca@quartz.technology>
1 parent f1480a9 commit cbe3710

File tree

3 files changed

+170
-4
lines changed

3 files changed

+170
-4
lines changed

Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hashcom-rs"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
authors = ["Luca Georges François <luca@quartz.technology>"]
55
description = "A fast, minimal but yet extensible framework for building and using hash commitment schemes"
66
repository = "https://github.com/quartz-technology/hashcom-rs"
@@ -11,6 +11,11 @@ edition = "2021"
1111

1212
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1313

14+
[dev-dependencies]
15+
base16ct = "0.1.1"
16+
hex-literal = "0.3.4"
17+
1418
[dependencies]
1519
bincode = "1.3.3"
16-
serde = "1.0.150"
20+
serde = "1.0.150"
21+
sha2 = "0.10.6"

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# <h1 align="center"> hashcom-rs </h1>
22

33
<p align="center">
4-
<img src="./.github/assets/COVER.PNG" width="400" alt="A DALL-E representation of a
4+
<img src="https://github.com/quartz-technology/hashcom-rs/blob/main/.github/assets/COVER.PNG" width="400" alt="A DALL-E representation of a
55
photo of a computer circuit in cyberpunk style with a dark theme">
66
</p>
77

@@ -20,4 +20,34 @@ I was inspired by the [go-ibft](https://github.com/0xPolygon/go-ibft) to create
2020
easily integrate and customize a hash commitment scheme in a rust application.
2121

2222
This package exposes both a trait for you to build your scheme given a specific hash function, or
23-
use an existing one.
23+
use an existing one.
24+
25+
## Architecture
26+
27+
The `hashcom-rs` library exposes a [`HashCommitmentScheme`](./src/lib.rs#L20) trait that can be
28+
implemented with you own hash function.
29+
You'll just have to implement the `commit` and `verify` methods.
30+
31+
A [`SHA256`](./src/lib.rs#L34) implementation is already provided. Below is an example of how it can be used
32+
(here, there's only one party who acts as both the prover and the verifier):
33+
```rust
34+
/// Here, one party acts as both the prover and the verifier,
35+
/// assuming that the verifier is not malicious.
36+
fn it_verifies_valid_commitment() {
37+
let s: [u8; 4] = [52, 50, 52, 50]; // 4242 in string format.
38+
let r: [u8; 4] = [50, 52, 50, 52]; // 2424 in string format.
39+
40+
// Commit phase.
41+
let party = SHA256Commitment::new(&s, &r);
42+
let commit = party.commit();
43+
44+
// Verification phase.
45+
let verification = party.verify(&commit.unwrap(), &s, &r);
46+
47+
assert_eq!(verification.is_ok(), true);
48+
assert_eq!(verification.unwrap(), true)
49+
}
50+
```
51+
52+
## Authors
53+
Made with ❤️ by 🤖 [0xpanoramix](https://github.com/0xpanoramix/) 🤖

src/lib.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use bincode::Result;
22
use serde::Serialize;
3+
use sha2::{Digest, Sha256};
34

45
/// A high-level representation of a party in a Hash Commitment Scheme.
56
///
@@ -20,3 +21,133 @@ pub trait HashCommitmentScheme<T: Serialize> {
2021
fn commit(&self) -> Result<Vec<u8>>;
2122
fn verify(&self, com: &[u8], s: &T, r: &[u8]) -> Result<bool>;
2223
}
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.
34+
pub struct SHA256Commitment<'a, T: 'a + Serialize> {
35+
s: &'a T,
36+
r: &'a [u8],
37+
}
38+
39+
impl<'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+
63+
impl<'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)]
80+
mod 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+
}

0 commit comments

Comments
 (0)