1 /* Copyright (C) 2020 HElib Project
2 * This program is Licensed under the Apache License, Version 2.0
3 * (the "License"); you may not use this file except in compliance
4 * with the License. You may obtain a copy of the License at
5 * http://www.apache.org/licenses/LICENSE-2.0
6 * Unless required by applicable law or agreed to in writing, software
7 * distributed under the License is distributed on an "AS IS" BASIS,
8 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 * See the License for the specific language governing permissions and
10 * limitations under the License. See accompanying LICENSE file.
11 */
12
13 // This program suggests parameter-sets for CKKS, it takes as input the
14 // target bit-size of the modulus for fresh ciphertexts (which is loosely
15 // related to the bit-capacity of fresh ciphertexts), and potentially also
16 // target security level in the ranage 70-270 (default is 128) and target
17 // accuracy parameter in the range 8-32 (default is 12).
18 // It returns one or more suggestions for the parameters m, r, L, c to
19 // use to get this capacity and accuracy at the given security level.
20 // The parameters r, L will be roughly equal to the target accuracy and
21 // bitsize arguments in the input, but may not be exactly equal to them.
22 #include <cmath>
23 #include <iostream>
24 #include <helib/helib.h>
25 #include <helib/ArgMap.h>
26
27 using namespace std;
28
29
30 struct CKKSparams {
31 int m, L, c;
32 int nCtxtPrimes;
33 double ctxtBits;
34
35 // Look for the largest value of L (modulus bitlength) for the given
36 // m,c, and security, Returns true if snything is found, else false.
tryParametersCKKSparams37 bool tryParameters(int security, int theM, int c2try) {
38
39 // Set the "accruacy parameter" r to 16, it should not have any effect
40 // The "plaintext space" parameter p is set to -1 for CKKS
41 helib::Context theContext(theM, /*p=*/-1, /*r=*/16);
42
43 // Note: teContext has an empty modulus chain, we now try to build many
44 // chains with different values of L and check their security
45
46 // Get largest modulus size (including special primes) for these value
47 // of m and security. The formula from Context.h is
48 // security ~ 3.8*(phi(m)/log2(Q/sigma)) -20,
49 // we overshoot |Q| by ignoring sigma and the -20 and rounding up 3.8 to 4
50
51 int phim = theContext.zMStar.getPhiM();
52 int totalBits = floor(double(4*phim)/security); // an over-estimate
53 while (helib::lweEstimateSecurity(phim, double(totalBits-3), /*hwt=*/0) < security)
54 totalBits--;
55
56 if (totalBits < 50) // no point in doing this, can't suport even 50 bits
57 return false;
58
59 // count down until you find something that actually works
60 this->L = 0;
61 this->ctxtBits = 0;
62 for (int L2try=totalBits; L2try>40; --L2try) {
63 // create a new context and build a chain for it
64 //helib::Context cntxt(theM, /*p=*/-1, /*r=*/16);
65 helib::Context& cntxt = theContext;
66 cntxt.clearModChain();
67 helib::buildModChain(cntxt, L2try, c2try);
68 if (cntxt.securityLevel() >= security) {
69 this->ctxtBits = cntxt.logOfProduct(cntxt.ctxtPrimes)/log(2.0);
70 this->L = L2try;
71 this->nCtxtPrimes = cntxt.ctxtPrimes.card();
72 break;
73 }
74 }
75 if (this->L==0) // nothing was found
76 return false;
77
78 // Keep trying a few more values of L, since it is not clear that
79 // reducing L always reduces the actual number of bits
80 int betterL = 0;
81 for (int delta=1; delta<10; delta++) {
82 //helib::Context cntxt(theM, /*p=*/-1, /*r=*/16);
83 helib::Context& cntxt = theContext;
84 cntxt.clearModChain();
85 helib::buildModChain(cntxt, this->L -delta, c2try);
86 double realBits = cntxt.logOfProduct(cntxt.ctxtPrimes)/log(2.0);
87 if (realBits > this->ctxtBits) {
88 betterL = this->L -delta;
89 this->ctxtBits = realBits;
90 this->nCtxtPrimes = cntxt.ctxtPrimes.card();
91 }
92 }
93
94 // Print a warning if a smaller L was found
95 if (betterL > 0) {
96 std::cerr << "** weird: L="<<betterL<<" gives more real bits than L="<<L
97 << " (m="<<theM<<", c="<<c2try <<')'<< std::endl;
98 L = betterL;
99 }
100
101 // Record the m,c values that were used here
102 this->m = theM;
103 this->c = c2try;
104 return true;
105 }
106 };
107
main(int argc,char * argv[])108 int main(int argc, char* argv[])
109 {
110 // get optional security level from command-line arguments
111 helib::ArgMap amap;
112 int sec = 128;
113 amap.arg("security", sec, "target security level in [70,270]");
114 amap.parse(argc, argv);
115
116 // ensure that security level is in the range [70,270]
117 if (sec<70) sec=70;
118 else if (sec>270) sec=270;
119
120 // print resutls in CSV format
121 std::cout << "sec,m,c,L,bits" << std::endl;
122
123 // Try power-of-two values of m from 2^11 upto 2^18
124 for (int m=2048; m<524288; m*=2) {
125 CKKSparams pprm; // pprm is "previous parameters"
126
127 // Start with a ridicoulusly large value of c
128 if (!pprm.tryParameters(sec, m, /*c=*/100))
129 continue; // even this large c value does not work
130
131 // get a more sensible upper-bound on c
132 int maxC = std::min(pprm.nCtxtPrimes, 100);
133 if (maxC == 1) // FIXME: this should not happen, but it does
134 continue;
135 if (maxC<100 && !pprm.tryParameters(sec, m, maxC))
136 continue; // even this large c value does not work
137
138 // Try successvely smaller values of c, down to c=2. Print the
139 // previous params pprms every time the smaller c values results
140 // is less modulus bits
141 for (int c2try=maxC*0.86; c2try>1; c2try=0.86*c2try) {
142 CKKSparams prm;
143 if (prm.tryParameters(sec, m, c2try)) {
144 if (pprm.L > prm.L) // the smaller c values is worse than before
145 // print the previous (better) set of values
146 std::cout<<sec<<','<<pprm.m<<','<<pprm.c<<','<<pprm.L<<','
147 << round(pprm.ctxtBits)<<std::endl;
148 pprm = prm; // record the current values for next iteration
149 }
150 else { // c value too small, no solution
151 break;
152 }
153 }
154 // print the last value that was recorded
155 std::cout<<sec<<','<<pprm.m<<','<<pprm.c<<','<<pprm.L<<','
156 << round(pprm.ctxtBits)<<std::endl;
157 }
158 return 0;
159 }
160