microsoft / SealPIR

Example implementation of the SealPIR protocol
MIT License
137 stars 53 forks source link

Hypercube expansion #7

Closed ReverseControl closed 3 years ago

ReverseControl commented 4 years ago

It is unclear to me how the hypercube expansion works exactly. For example, with

uint64_t number_of_items = 1 << 16; 
uint64_t size_per_item = 12*4096/8; // in bytes
uint32_t N = 4096;

// Recommended values: (logt, d) = (12, 2) or (8, 1). 
uint32_t logt = 12; 
uint32_t d = 2;

The loop to expand the query into a hypercube in the generate_reply function:

  for (uint32_t i = 0; i < nvec.size(); i++) {
     vector<Ciphertext> expanded_query; 

        auto time_server_s = high_resolution_clock::now();
        uint64_t n_i = nvec[i];

        for (uint32_t j = 0; j < query[i].size(); j++){
            uint64_t total = N;  

            if (j == query[i].size() - 1){ 
                total = n_i % N;  
            }

            vector<Ciphertext> expanded_query_part = expand_query(query[i][j], total, client_id);
            expanded_query.insert( expanded_query.end(),
                                   std::make_move_iterator(expanded_query_part.begin()), 
                                   std::make_move_iterator(expanded_query_part.end()));
            expanded_query_part.clear(); 
        }

This loop produces 256 polynomials with 256 non-zero cipherthext coefficients each; granted all but one encrypt the plain text value of 0. The loop that does the Homomorphic selection is the following:

 for (uint64_t k = 0; k < product; k++) {

            evaluator_->multiply_plain(expanded_query[0], (*cur)[k], intermediateCtxts[k]);
            for (uint64_t j = 1; j < n_i; j++) {
                evaluator_->multiply_plain(expanded_query[j], (*cur)[k + j * product], temp);
                evaluator_->add_inplace(intermediateCtxts[k], temp); // Adds to first component.
            }
        }

Here is my confusion: it is my understanding that each coefficient of every polynomial should, by themselves, be multiplied by the (k+j product)-th element of the database, and then accumulated using addition into a single value. This is not what happens, instead all 256 polynomials get multiplied 256 times each, where polynomial k multiplies with entry 256n+k for all n up to 0< n < 256. How is this selecting one entry in the database? Where each entry takes up the entire ciphertext space for one polynomial with 4096 coefficients and 12-bits available in each one(total of 6144 bytes).

Also, on the second round of the loop, since "nvec.size = 2" we have n_i = 256 but product = 10? I do not understand this at all. Any comments on the matter would be greatly appreciated.