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_