1 /* Copyright (C) 2012-2019 IBM Corp.
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 /* Test_IO.cpp - Testing the I/O of the important classes of the library
14  * (context, keys, ciphertexts).
15  */
16 #include <cassert>
17 #include <fstream>
18 #include <unistd.h>
19 
20 #include <NTL/ZZX.h>
21 #include <NTL/vector.h>
22 
23 #include <helib/helib.h>
24 #include <helib/ArgMap.h>
25 
26 NTL_CLIENT
27 using namespace helib;
28 
29 #define N_TESTS 3
30 static long ms[N_TESTS][10] = {
31   //nSlots  m   phi(m) ord(2)
32   //  {   2,    7,    6,    3,   0,0,0,0,0,0},
33   {   6,   31,   30,    5,   0,0,0,0,0,0},
34   {   6,  127,  126,    7,  127,  1,  108,  24,   6,-3}, // gens=108(6), 24(!3)
35   {  60, 1023,  600,   10,   11, 93,  838, 584,  10, 6}, // gens=838(10),584(6)
36   //  {  378,  5461,  5292, 14}, // gens=3(126),509(3)
37   //  {  630,  8191,  8190, 13}, // gens=39(630)
38   //  {  600, 13981, 12000, 20}, // gens=10(30),23(10),3(!2)
39   //  {  682, 15709, 15004, 22} // gens=5(682)
40 };
41 
42 void checkCiphertext(const Ctxt& ctxt, const ZZX& ptxt, const SecKey& sk);
43 
44 // Testing the I/O of the important classes of the library
45 // (context, keys, ciphertexts).
main(int argc,char * argv[])46 int main(int argc, char *argv[])
47 {
48   ArgMap amap;
49 
50   long r=1;
51   long p=2;
52   long c = 2;
53   long w = 64;
54   long L = 100;
55   long mm=0;
56   bool noPrint = true;
57   amap.arg("p", p, "plaintext base");
58   amap.arg("r", r,  "lifting");
59   amap.arg("c", c, "number of columns in the key-switching matrices");
60   amap.arg("m", mm, "cyclotomic index","{31,127,1023}");
61   amap.arg("noPrint", noPrint, "suppress printouts");
62   amap.parse(argc, argv);
63 
64   bool useTable = (mm==0 && p==2);
65   long ptxtSpace = power_long(p,r);
66   long numTests = useTable? N_TESTS : 1;
67 
68   std::unique_ptr<Context> contexts[numTests];
69   std::unique_ptr<SecKey> sKeys[numTests];
70   std::unique_ptr<Ctxt> ctxts[numTests];
71   std::unique_ptr<EncryptedArray> eas[numTests];
72   vector<ZZX> ptxts[numTests];
73 
74   // first loop: generate stuff and write it to file
75 
76   // open file for writing
77   {fstream keyFile("iotest.txt", fstream::out|fstream::trunc);
78    assert(keyFile.is_open());
79   for (long i=0; i<numTests; i++) {
80     long m = (mm==0)? ms[i][1] : mm;
81 
82     if (!noPrint)
83       cout << "Testing IO: m="<<m<<", p^r="<<p<<"^"<<r<<endl;
84 
85     Vec<long> mvec(INIT_SIZE,2);
86     mvec[0] = ms[i][4];  mvec[1] = ms[i][5];
87     vector<long> gens(2);
88     gens[0] = ms[i][6];  gens[1] = ms[i][7];
89     vector<long> ords(2);
90     ords[0] = ms[i][8];  ords[1] = ms[i][9];
91 
92     if (useTable && gens[0]>0)
93       contexts[i].reset(new Context(m, p, r, gens, ords));
94     else
95       contexts[i].reset(new Context(m, p, r));
96     if (!noPrint)
97       contexts[i]->zMStar.printout();
98 
99     buildModChain(*contexts[i], L, c);  // Set the modulus chain
100     if (mm==0 && m==1023) contexts[i]->enableBootStrapping(mvec);
101 
102     // Output the Context to file
103     writeContextBase(keyFile, *contexts[i]);
104     if (!noPrint)
105       writeContextBase(cout, *contexts[i]);
106     keyFile << *contexts[i] << endl;
107 
108     sKeys[i].reset(new SecKey(*contexts[i]));
109     sKeys[i]->GenSecKey(); // A +-1/0 secret key
110     addSome1DMatrices(*sKeys[i]);// compute key-switching matrices that we need
111     const PubKey publicKey = *sKeys[i];
112     eas[i].reset(new EncryptedArray(*contexts[i]));
113 
114     long nslots = eas[i]->size();
115 
116     // Output the secret key to file, twice. Below we will have two copies
117     // of most things.
118     keyFile << *sKeys[i] << endl;;
119     keyFile << *sKeys[i] << endl;;
120 
121     vector<ZZX> b;
122     long p2r = eas[i]->getContext().alMod.getPPowR();
123     ZZX poly = RandPoly(0,to_ZZ(p2r)); // choose a random constant polynomial
124     eas[i]->decode(ptxts[i], poly);
125 
126     ctxts[i].reset(new Ctxt(publicKey));
127     eas[i]->encrypt(*ctxts[i], publicKey, ptxts[i]);
128     eas[i]->decrypt(*ctxts[i], *sKeys[i], b);
129     assert(ptxts[i].size() == b.size());
130     for (long j = 0; j < nslots; j++) assert (ptxts[i][j] == b[j]);
131 
132     // output the plaintext
133     keyFile << "[ ";
134     for (long j = 0; j < nslots; j++) keyFile << ptxts[i][j] << " ";
135     keyFile << "]\n";
136 
137     eas[i]->encode(poly,ptxts[i]);
138     keyFile << poly << endl;
139 
140     // Output the ciphertext to file
141     keyFile << *ctxts[i] << endl;
142     keyFile << *ctxts[i] << endl;
143     //    cerr << "okay " << i << endl<< endl;
144   }
145   keyFile.close();}
146   //  cerr << "so far, so good\n\n";
147 
148   // second loop: read from input and repeat the computation
149 
150   // open file for read
151   {fstream keyFile("iotest.txt", fstream::in);
152   for (long i=0; i<numTests; i++) {
153 
154     // Read context from file
155     unsigned long m1, p1, r1;
156     vector<long> gens, ords;
157     readContextBase(keyFile, m1, p1, r1, gens, ords);
158     Context tmpContext(m1, p1, r1, gens, ords);
159     keyFile >> tmpContext;
160     assert (*contexts[i] == tmpContext);
161     //    cerr << i << ": context matches input\n";
162     cout << "GOOD\n";
163 
164     // We define some things below wrt *contexts[i], not tmpContext.
165     // This is because the various operator== methods check equality of
166     // references, not equality of the referenced Context objects.
167     Context& context = *contexts[i];
168     SecKey secretKey(context);
169     SecKey secretKey2(tmpContext);
170     const PubKey& publicKey = secretKey;
171     const PubKey& publicKey2 = secretKey2;
172 
173     keyFile >> secretKey;
174     keyFile >> secretKey2;
175     assert(secretKey == *sKeys[i]);
176     //    cerr << "   secret key matches input\n";
177     cout << "GOOD\n";
178 
179     EncryptedArray ea(context);
180     EncryptedArray ea2(tmpContext);
181 
182     long nslots = ea.size();
183 
184     // Read the plaintext from file
185     vector<ZZX> a;
186     a.resize(nslots);
187     assert(nslots == (long)ptxts[i].size());
188     seekPastChar(keyFile, '['); // defined in NumbTh.cpp
189     for (long j = 0; j < nslots; j++) {
190       keyFile >> a[j];
191       assert(a[j] == ptxts[i][j]);
192     }
193     seekPastChar(keyFile, ']');
194     cout << "GOOD\n";
195     //    cerr << "   ptxt matches input\n";
196 
197     // Read the encoded plaintext from file
198     ZZX poly1, poly2;
199     keyFile >> poly1;
200     eas[i]->encode(poly2,a);
201     assert(poly1 == poly2);
202     cout << "GOOD\n";
203     //    cerr << "   eas[i].encode(a)==poly1 okay\n";
204 
205     ea.encode(poly2,a);
206     assert(poly1 == poly2);
207     cout << "GOOD\n";
208     //    cerr << "   ea.encode(a)==poly1 okay\n";
209 
210     ea2.encode(poly2,a);
211     assert(poly1 == poly2);
212     cout << "GOOD\n";
213     //    cerr << "   ea2.encode(a)==poly1 okay\n";
214 
215     eas[i]->decode(a,poly1);
216     assert(nslots == (long)a.size());
217     for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]);
218     cout << "GOOD\n";
219     //    cerr << "   eas[i].decode(poly1)==ptxts[i] okay\n";
220 
221     ea.decode(a,poly1);
222     assert(nslots == (long)a.size());
223     for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]);
224     cout << "GOOD\n";
225     //    cerr << "   ea.decode(poly1)==ptxts[i] okay\n";
226 
227     ea2.decode(a,poly1);
228     assert(nslots == (long)a.size());
229     for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]);
230     cout << "GOOD\n";
231     //    cerr << "   ea2.decode(poly1)==ptxts[i] okay\n";
232 
233     // Read ciperhtext from file
234     Ctxt ctxt(publicKey);
235     Ctxt ctxt2(publicKey2);
236     keyFile >> ctxt;
237     keyFile >> ctxt2;
238     assert(ctxts[i]->equalsTo(ctxt,/*comparePkeys=*/false));
239     cout << "GOOD\n";
240     //    cerr << "   ctxt matches input\n";
241 
242     sKeys[i]->Decrypt(poly2,*ctxts[i]);
243     assert(poly1 == poly2);
244     cout << "GOOD\n";
245     //    cerr << "   sKeys[i]->decrypt(*ctxts[i]) == poly1 okay\n";
246 
247     secretKey.Decrypt(poly2,*ctxts[i]);
248     assert(poly1 == poly2);
249     cout << "GOOD\n";
250     //    cerr << "   secretKey.decrypt(*ctxts[i]) == poly1 okay\n";
251 
252     secretKey.Decrypt(poly2,ctxt);
253     assert(poly1 == poly2);
254     cout << "GOOD\n";
255     //    cerr << "   secretKey.decrypt(ctxt) == poly1 okay\n";
256 
257     secretKey2.Decrypt(poly2,ctxt2);
258     assert(poly1 == poly2);
259     cout << "GOOD\n";
260     //    cerr << "   secretKey2.decrypt(ctxt2) == poly1 okay\n";
261 
262     eas[i]->decrypt(ctxt, *sKeys[i], a);
263     assert(nslots == (long)a.size());
264     for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]);
265     cout << "GOOD\n";
266     //    cerr << "   eas[i].decrypt(ctxt, *sKeys[i])==ptxts[i] okay\n";
267 
268     ea.decrypt(ctxt, secretKey, a);
269     assert(nslots == (long)a.size());
270     for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]);
271     cout << "GOOD\n";
272     //    cerr << "   ea.decrypt(ctxt, secretKey)==ptxts[i] okay\n";
273 
274     ea2.decrypt(ctxt2, secretKey2, a);
275     assert(nslots == (long)a.size());
276     for (long j = 0; j < nslots; j++) assert(a[j] == ptxts[i][j]);
277     cout << "GOOD\n";
278     //    cerr << "   ea2.decrypt(ctxt2, secretKey2)==ptxts[i] okay\n";
279   }}
280   unlink("iotest.txt"); // clean up before exiting
281 }
282