2 *****************************************************************************
4 Implementation of interfaces for the TinyRAM memory masking gadget.
6 See memory_masking_gadget.hpp .
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 *****************************************************************************/
14 #ifndef MEMORY_MASKING_GADGET_TCC_
15 #define MEMORY_MASKING_GADGET_TCC_
20 template<typename FieldT>
21 memory_masking_gadget<FieldT>::memory_masking_gadget(
22 tinyram_protoboard<FieldT> &pb,
23 const doubleword_variable_gadget<FieldT> &dw_contents_prev,
24 const dual_variable_gadget<FieldT> &subaddress,
25 const pb_linear_combination<FieldT> &subcontents,
26 const pb_linear_combination<FieldT> &access_is_word,
27 const pb_linear_combination<FieldT> &access_is_byte,
28 const doubleword_variable_gadget<FieldT> &dw_contents_next,
29 const std::string &annotation_prefix)
30 : tinyram_standard_gadget<FieldT>(pb, annotation_prefix)
31 , dw_contents_prev(dw_contents_prev)
32 , subaddress(subaddress)
33 , subcontents(subcontents)
34 , access_is_word(access_is_word)
35 , access_is_byte(access_is_byte)
36 , dw_contents_next(dw_contents_next)
39 Indicator variables for access being to word_0, word_1, and
42 We use little-endian indexing here (least significant
43 bit/byte/word has the smallest address).
45 is_word0.allocate(pb, FMT(this->annotation_prefix, " is_word0"));
46 is_word1.allocate(pb, FMT(this->annotation_prefix, " is_word1"));
47 is_subaddress.allocate(
49 2 * pb.ap.bytes_in_word(),
50 FMT(this->annotation_prefix, " is_sub_address"));
53 2 * pb.ap.bytes_in_word(),
54 FMT(this->annotation_prefix, " is_byte"));
57 Get value of the dw_contents_prev for which the specified entity
58 is masked out to be zero. E.g. the value of masked_out_bytes[3]
59 will be the same as the value of dw_contents_prev, when 3rd
60 (0-indexed) byte is set to all zeros.
62 masked_out_word0.assign(
64 (FieldT(2) ^ pb.ap.w) *
65 pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
66 dw_contents_prev.bits.begin() + pb.ap.w,
67 dw_contents_prev.bits.begin() + 2 * pb.ap.w)));
68 masked_out_word1.assign(
70 pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
71 dw_contents_prev.bits.begin(),
72 dw_contents_prev.bits.begin() + pb.ap.w)));
73 masked_out_bytes.resize(2 * pb.ap.bytes_in_word());
75 for (size_t i = 0; i < 2 * pb.ap.bytes_in_word(); ++i) {
76 /* just subtract out the byte to be masked */
77 masked_out_bytes[i].assign(
79 (dw_contents_prev.packed -
80 (FieldT(2) ^ (8 * i)) *
81 pb_packing_sum<FieldT>(pb_variable_array<FieldT>(
82 dw_contents_prev.bits.begin() + 8 * i,
83 dw_contents_prev.bits.begin() + 8 * (i + 1)))));
87 Define masked_out_dw_contents_prev to be the correct masked out
88 contents for the current access type.
91 pb_linear_combination_array<FieldT> masked_out_indicators;
92 masked_out_indicators.emplace_back(is_word0);
93 masked_out_indicators.emplace_back(is_word1);
94 masked_out_indicators.insert(
95 masked_out_indicators.end(), is_byte.begin(), is_byte.end());
97 pb_linear_combination_array<FieldT> masked_out_results;
98 masked_out_results.emplace_back(masked_out_word0);
99 masked_out_results.emplace_back(masked_out_word1);
100 masked_out_results.insert(
101 masked_out_results.end(),
102 masked_out_bytes.begin(),
103 masked_out_bytes.end());
105 masked_out_dw_contents_prev.allocate(
106 pb, FMT(this->annotation_prefix, " masked_out_dw_contents_prev"));
107 get_masked_out_dw_contents_prev.reset(new inner_product_gadget<FieldT>(
109 masked_out_indicators,
111 masked_out_dw_contents_prev,
112 FMT(this->annotation_prefix, " get_masked_out_dw_contents_prev")));
115 Define shift so that masked_out_dw_contents_prev + shift * subcontents =
118 linear_combination<FieldT> shift_lc =
119 is_word0 * 1 + is_word1 * (FieldT(2) ^ this->pb.ap.w);
120 for (size_t i = 0; i < 2 * this->pb.ap.bytes_in_word(); ++i) {
121 shift_lc = shift_lc + is_byte[i] * (FieldT(2) ^ (8 * i));
123 shift.assign(pb, shift_lc);
126 template<typename FieldT>
127 void memory_masking_gadget<FieldT>::generate_r1cs_constraints()
129 /* get indicator variables for is_subaddress[i] by adding constraints
130 is_subaddress[i] * (subaddress - i) = 0 and \sum_i is_subaddress[i] = 1
132 for (size_t i = 0; i < 2 * this->pb.ap.bytes_in_word(); ++i) {
133 this->pb.add_r1cs_constraint(
134 r1cs_constraint<FieldT>(is_subaddress[i], subaddress.packed - i, 0),
135 FMT(this->annotation_prefix, " is_subaddress_%zu", i));
137 this->pb.add_r1cs_constraint(
138 r1cs_constraint<FieldT>(1, pb_sum<FieldT>(is_subaddress), 1),
139 FMT(this->annotation_prefix, " is_subaddress"));
141 /* get indicator variables is_byte_X */
142 for (size_t i = 0; i < 2 * this->pb.ap.bytes_in_word(); ++i) {
143 this->pb.add_r1cs_constraint(
144 r1cs_constraint<FieldT>(
145 access_is_byte, is_subaddress[i], is_byte[i]),
146 FMT(this->annotation_prefix, " is_byte_%zu", i));
149 /* get indicator variables is_word_0/is_word_1 */
150 this->pb.add_r1cs_constraint(
151 r1cs_constraint<FieldT>(
153 1 - subaddress.bits[this->pb.ap.subaddr_len() - 1],
155 FMT(this->annotation_prefix, " is_word_0"));
156 this->pb.add_r1cs_constraint(
157 r1cs_constraint<FieldT>(
159 subaddress.bits[this->pb.ap.subaddr_len() - 1],
161 FMT(this->annotation_prefix, " is_word_1"));
163 /* compute masked_out_dw_contents_prev */
164 get_masked_out_dw_contents_prev->generate_r1cs_constraints();
167 masked_out_dw_contents_prev + shift * subcontents = dw_contents_next
169 this->pb.add_r1cs_constraint(
170 r1cs_constraint<FieldT>(
173 dw_contents_next.packed - masked_out_dw_contents_prev),
174 FMT(this->annotation_prefix, " mask_difference"));
177 template<typename FieldT>
178 void memory_masking_gadget<FieldT>::generate_r1cs_witness()
180 /* get indicator variables is_subaddress */
181 for (size_t i = 0; i < 2 * this->pb.ap.bytes_in_word(); ++i) {
182 this->pb.val(is_subaddress[i]) =
183 (this->pb.val(subaddress.packed) == FieldT(i)) ? FieldT::one()
187 /* get indicator variables is_byte_X */
188 for (size_t i = 0; i < 2 * this->pb.ap.bytes_in_word(); ++i) {
189 this->pb.val(is_byte[i]) =
190 this->pb.val(is_subaddress[i]) * this->pb.lc_val(access_is_byte);
193 /* get indicator variables is_word_0/is_word_1 */
194 this->pb.val(is_word0) =
196 this->pb.val(subaddress.bits[this->pb.ap.subaddr_len() - 1])) *
197 this->pb.lc_val(access_is_word);
198 this->pb.val(is_word1) =
199 this->pb.val(subaddress.bits[this->pb.ap.subaddr_len() - 1]) *
200 this->pb.lc_val(access_is_word);
202 /* calculate shift and masked out words/bytes */
203 shift.evaluate(this->pb);
204 masked_out_word0.evaluate(this->pb);
205 masked_out_word1.evaluate(this->pb);
206 masked_out_bytes.evaluate(this->pb);
208 /* get masked_out dw/word0/word1/bytes */
209 get_masked_out_dw_contents_prev->generate_r1cs_witness();
211 /* compute dw_contents_next */
212 this->pb.val(dw_contents_next.packed) =
213 this->pb.val(masked_out_dw_contents_prev) +
214 this->pb.lc_val(shift) * this->pb.lc_val(subcontents);
215 dw_contents_next.generate_r1cs_witness_from_packed();
218 } // namespace libsnark
220 #endif // MEMORY_MASKING_GADGET_TCC_