1
1
use bincode:: Result ;
2
2
use serde:: Serialize ;
3
+ use sha2:: { Digest , Sha256 } ;
3
4
4
5
/// A high-level representation of a party in a Hash Commitment Scheme.
5
6
///
@@ -20,3 +21,133 @@ pub trait HashCommitmentScheme<T: Serialize> {
20
21
fn commit ( & self ) -> Result < Vec < u8 > > ;
21
22
fn verify ( & self , com : & [ u8 ] , s : & T , r : & [ u8 ] ) -> Result < bool > ;
22
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.
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