Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
sha256_gadget.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3 
4  Implementation of interfaces for top-level SHA256 gadgets.
5 
6  See sha256_gadget.hpp .
7 
8  *****************************************************************************
9  * @author This file is part of libsnark, developed by SCIPR Lab
10  * and contributors (see AUTHORS).
11  * @copyright MIT license (see LICENSE file)
12  *****************************************************************************/
13 
14 #ifndef SHA256_GADGET_TCC_
15 #define SHA256_GADGET_TCC_
16 
17 namespace libsnark
18 {
19 
20 template<typename FieldT>
21 sha256_compression_function_gadget<FieldT>::sha256_compression_function_gadget(
22  protoboard<FieldT> &pb,
23  const pb_linear_combination_array<FieldT> &prev_output,
24  const pb_variable_array<FieldT> &new_block,
25  const digest_variable<FieldT> &output,
26  const std::string &annotation_prefix)
27  : gadget<FieldT>(pb, annotation_prefix)
28  , prev_output(prev_output)
29  , new_block(new_block)
30  , output(output)
31 {
32  /* message schedule and inputs for it */
33  packed_W.allocate(pb, 64, FMT(this->annotation_prefix, " packed_W"));
34  message_schedule.reset(new sha256_message_schedule_gadget<FieldT>(
35  pb,
36  new_block,
37  packed_W,
38  FMT(this->annotation_prefix, " message_schedule")));
39 
40  /* initalize */
41  round_a.push_back(pb_linear_combination_array<FieldT>(
42  prev_output.rbegin() + 7 * 32, prev_output.rbegin() + 8 * 32));
43  round_b.push_back(pb_linear_combination_array<FieldT>(
44  prev_output.rbegin() + 6 * 32, prev_output.rbegin() + 7 * 32));
45  round_c.push_back(pb_linear_combination_array<FieldT>(
46  prev_output.rbegin() + 5 * 32, prev_output.rbegin() + 6 * 32));
47  round_d.push_back(pb_linear_combination_array<FieldT>(
48  prev_output.rbegin() + 4 * 32, prev_output.rbegin() + 5 * 32));
49  round_e.push_back(pb_linear_combination_array<FieldT>(
50  prev_output.rbegin() + 3 * 32, prev_output.rbegin() + 4 * 32));
51  round_f.push_back(pb_linear_combination_array<FieldT>(
52  prev_output.rbegin() + 2 * 32, prev_output.rbegin() + 3 * 32));
53  round_g.push_back(pb_linear_combination_array<FieldT>(
54  prev_output.rbegin() + 1 * 32, prev_output.rbegin() + 2 * 32));
55  round_h.push_back(pb_linear_combination_array<FieldT>(
56  prev_output.rbegin() + 0 * 32, prev_output.rbegin() + 1 * 32));
57 
58  /* do the rounds */
59  for (size_t i = 0; i < 64; ++i) {
60  round_h.push_back(round_g[i]);
61  round_g.push_back(round_f[i]);
62  round_f.push_back(round_e[i]);
63  round_d.push_back(round_c[i]);
64  round_c.push_back(round_b[i]);
65  round_b.push_back(round_a[i]);
66 
67  pb_variable_array<FieldT> new_round_a_variables;
68  new_round_a_variables.allocate(
69  pb,
70  32,
71  FMT(this->annotation_prefix, " new_round_a_variables_%zu", i + 1));
72  round_a.emplace_back(new_round_a_variables);
73 
74  pb_variable_array<FieldT> new_round_e_variables;
75  new_round_e_variables.allocate(
76  pb,
77  32,
78  FMT(this->annotation_prefix, " new_round_e_variables_%zu", i + 1));
79  round_e.emplace_back(new_round_e_variables);
80 
81  round_functions.push_back(sha256_round_function_gadget<FieldT>(
82  pb,
83  round_a[i],
84  round_b[i],
85  round_c[i],
86  round_d[i],
87  round_e[i],
88  round_f[i],
89  round_g[i],
90  round_h[i],
91  packed_W[i],
92  SHA256_K[i],
93  round_a[i + 1],
94  round_e[i + 1],
95  FMT(this->annotation_prefix, " round_functions_%zu", i)));
96  }
97 
98  /* finalize */
99  unreduced_output.allocate(
100  pb, 8, FMT(this->annotation_prefix, " unreduced_output"));
101  reduced_output.allocate(
102  pb, 8, FMT(this->annotation_prefix, " reduced_output"));
103  for (size_t i = 0; i < 8; ++i) {
104  reduce_output.push_back(lastbits_gadget<FieldT>(
105  pb,
106  unreduced_output[i],
107  32 + 1,
108  reduced_output[i],
109  pb_variable_array<FieldT>(
110  output.bits.rbegin() + (7 - i) * 32,
111  output.bits.rbegin() + (8 - i) * 32),
112  FMT(this->annotation_prefix, " reduce_output_%zu", i)));
113  }
114 }
115 
116 template<typename FieldT>
117 void sha256_compression_function_gadget<FieldT>::generate_r1cs_constraints()
118 {
119  message_schedule->generate_r1cs_constraints();
120  for (size_t i = 0; i < 64; ++i) {
121  round_functions[i].generate_r1cs_constraints();
122  }
123 
124  for (size_t i = 0; i < 4; ++i) {
125  this->pb.add_r1cs_constraint(
126  r1cs_constraint<FieldT>(
127  1,
128  round_functions[3 - i].packed_d +
129  round_functions[63 - i].packed_new_a,
130  unreduced_output[i]),
131  FMT(this->annotation_prefix, " unreduced_output_%zu", i));
132 
133  this->pb.add_r1cs_constraint(
134  r1cs_constraint<FieldT>(
135  1,
136  round_functions[3 - i].packed_h +
137  round_functions[63 - i].packed_new_e,
138  unreduced_output[4 + i]),
139  FMT(this->annotation_prefix, " unreduced_output_%zu", 4 + i));
140  }
141 
142  for (size_t i = 0; i < 8; ++i) {
143  reduce_output[i].generate_r1cs_constraints();
144  }
145 }
146 
147 template<typename FieldT>
148 void sha256_compression_function_gadget<FieldT>::generate_r1cs_witness()
149 {
150  message_schedule->generate_r1cs_witness();
151 
152 #ifdef DEBUG
153  printf("Input:\n");
154  for (size_t j = 0; j < 16; ++j) {
155  printf("%lx ", this->pb.val(packed_W[j]).as_ulong());
156  }
157  printf("\n");
158 #endif
159 
160  for (size_t i = 0; i < 64; ++i) {
161  round_functions[i].generate_r1cs_witness();
162  }
163 
164  for (size_t i = 0; i < 4; ++i) {
165  this->pb.val(unreduced_output[i]) =
166  this->pb.val(round_functions[3 - i].packed_d) +
167  this->pb.val(round_functions[63 - i].packed_new_a);
168  this->pb.val(unreduced_output[4 + i]) =
169  this->pb.val(round_functions[3 - i].packed_h) +
170  this->pb.val(round_functions[63 - i].packed_new_e);
171  }
172 
173  for (size_t i = 0; i < 8; ++i) {
174  reduce_output[i].generate_r1cs_witness();
175  }
176 
177 #ifdef DEBUG
178  printf("Output:\n");
179  for (size_t j = 0; j < 8; ++j) {
180  printf("%lx ", this->pb.val(reduced_output[j]).as_ulong());
181  }
182  printf("\n");
183 #endif
184 }
185 
186 template<typename FieldT>
187 sha256_two_to_one_hash_gadget<FieldT>::sha256_two_to_one_hash_gadget(
188  protoboard<FieldT> &pb,
189  const digest_variable<FieldT> &left,
190  const digest_variable<FieldT> &right,
191  const digest_variable<FieldT> &output,
192  const std::string &annotation_prefix)
193  : gadget<FieldT>(pb, annotation_prefix)
194 {
195  /* concatenate block = left || right */
196  pb_variable_array<FieldT> block;
197  block.insert(block.end(), left.bits.begin(), left.bits.end());
198  block.insert(block.end(), right.bits.begin(), right.bits.end());
199 
200  /* compute the hash itself */
201  f.reset(new sha256_compression_function_gadget<FieldT>(
202  pb,
203  SHA256_default_IV<FieldT>(pb),
204  block,
205  output,
206  FMT(this->annotation_prefix, " f")));
207 }
208 
209 template<typename FieldT>
210 sha256_two_to_one_hash_gadget<FieldT>::sha256_two_to_one_hash_gadget(
211  protoboard<FieldT> &pb,
212  const size_t block_length,
213  const block_variable<FieldT> &input_block,
214  const digest_variable<FieldT> &output,
215  const std::string &annotation_prefix)
216  : gadget<FieldT>(pb, annotation_prefix)
217 {
218 #ifdef NDEBUG
219  libff::UNUSED(block_length);
220 #endif
221  assert(block_length == SHA256_block_size);
222  assert(input_block.bits.size() == block_length);
223  f.reset(new sha256_compression_function_gadget<FieldT>(
224  pb,
225  SHA256_default_IV<FieldT>(pb),
226  input_block.bits,
227  output,
228  FMT(this->annotation_prefix, " f")));
229 }
230 
231 template<typename FieldT>
232 void sha256_two_to_one_hash_gadget<FieldT>::generate_r1cs_constraints(
233  const bool ensure_output_bitness)
234 {
235  libff::UNUSED(ensure_output_bitness);
236  f->generate_r1cs_constraints();
237 }
238 
239 template<typename FieldT>
240 void sha256_two_to_one_hash_gadget<FieldT>::generate_r1cs_witness()
241 {
242  f->generate_r1cs_witness();
243 }
244 
245 template<typename FieldT>
246 size_t sha256_two_to_one_hash_gadget<FieldT>::get_block_len()
247 {
248  return SHA256_block_size;
249 }
250 
251 template<typename FieldT>
252 size_t sha256_two_to_one_hash_gadget<FieldT>::get_digest_len()
253 {
254  return SHA256_digest_size;
255 }
256 
257 template<typename FieldT>
258 libff::bit_vector sha256_two_to_one_hash_gadget<FieldT>::get_hash(
259  const libff::bit_vector &input)
260 {
261  protoboard<FieldT> pb;
262 
263  block_variable<FieldT> input_variable(pb, SHA256_block_size, "input");
264  digest_variable<FieldT> output_variable(pb, SHA256_digest_size, "output");
265  sha256_two_to_one_hash_gadget<FieldT> f(
266  pb, SHA256_block_size, input_variable, output_variable, "f");
267 
268  input_variable.generate_r1cs_witness(input);
269  f.generate_r1cs_witness();
270 
271  return output_variable.get_digest();
272 }
273 
274 template<typename FieldT>
275 size_t sha256_two_to_one_hash_gadget<FieldT>::expected_constraints(
276  const bool ensure_output_bitness)
277 {
278  libff::UNUSED(ensure_output_bitness);
279  return 27280; /* hardcoded for now */
280 }
281 
282 } // namespace libsnark
283 
284 #endif // SHA256_GADGET_TCC_