dlrho.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * cudadl version 0.9: Compute discrete logs in smooth group orders
  3. * using CUDA
  4. * Copyright (C) 2012 by Ryan Henry and Ian Goldberg
  5. * {rhenry,iang}@cs.uwaterloo.ca
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of version 3 of the GNU General Public License as
  9. * published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <fstream>
  20. #include <unistd.h>
  21. #include <sys/time.h>
  22. #include <NTL/vec_ZZ.h>
  23. #include <NTL/ZZ_p.h>
  24. #include <map>
  25. #include <sstream>
  26. #include <sys/types.h>
  27. #include <sys/wait.h>
  28. #include <sys/socket.h>
  29. #include <cuda_runtime.h>
  30. #include "atomic_iostream.h"
  31. #include "desired_resources.h"
  32. #include "cudadl.h"
  33. NTL_CLIENT
  34. string output_prefix;
  35. #ifdef SAVE_DPS
  36. static ofstream dp_file_stream;
  37. #endif
  38. typedef map<std::string, pair<ZZ,ZZ> > DTable;
  39. struct CBData {
  40. const ZZ_p &base;
  41. const ZZ_p &target;
  42. const ZZ &order;
  43. unsigned long long numdp;
  44. DTable dtable;
  45. bool found_collision;
  46. ZZ expon;
  47. CBData(const ZZ_p &_base, const ZZ_p &_target, const ZZ &_order) :
  48. base(_base), target(_target), order(_order), numdp(0),
  49. found_collision(false) {}
  50. };
  51. // This function is called from inside cuda_dl for each DP it encounters.
  52. // It calls the function named "dpcallback" directly. It would be
  53. // cleaner if this were passed as a function pointer to cuda_dl, but
  54. // that makes nvcc 3.1 segfault. :-p
  55. // dp points to an array of WORDS+7 unsigned ints:
  56. // - 1 word of threadID/blockID
  57. // - WORDS words of the dp value
  58. // - 3 words of a
  59. // - 3 words of b
  60. bool dpcallback(void *cbdata, unsigned int *dpwords)
  61. {
  62. CBData *d = (CBData*)cbdata;
  63. // WARNING: this assumes
  64. // sizeof(unsigned long) == sizeof(unsigned long long) !
  65. ZZ zz_a = to_ZZ(dpwords[WORDS+3]);
  66. zz_a <<= 32;
  67. zz_a += dpwords[WORDS+2];
  68. zz_a <<= 32;
  69. zz_a += dpwords[WORDS+1];
  70. ZZ zz_b = to_ZZ(dpwords[WORDS+6]);
  71. zz_b <<= 32;
  72. zz_b += dpwords[WORDS+5];
  73. zz_b <<= 32;
  74. zz_b += dpwords[WORDS+4];
  75. //ZZ_p dp = power(d->base, zz_a) * power(d->target, zz_b);
  76. // cerr << "DP " << ++(d->numdp) << "\r";
  77. pair<ZZ,ZZ> ab(zz_a,zz_b);
  78. pair<DTable::iterator, bool> res;
  79. string x((const char *)(dpwords+1), WORDS*sizeof(unsigned int));
  80. #ifdef SAVE_DPS
  81. if (!d->found_collision) {
  82. ZZ zz_x;
  83. ZZFromBytes(zz_x, (const unsigned char *)(dpwords+1),
  84. WORDS*sizeof(unsigned int));
  85. dp_file_stream << zz_x << "\n";
  86. }
  87. #endif
  88. res = d->dtable.insert(DTable::value_type(x, ab));
  89. if (!res.second) {
  90. // Collision!
  91. ZZ adiff = to_ZZ(res.first->second.first) - zz_a;
  92. ZZ bdiff = zz_b - to_ZZ(res.first->second.second);
  93. if (bdiff < 0) bdiff += d->order;
  94. ZZ binv;
  95. if (InvModStatus(binv, bdiff, d->order) == 0) {
  96. d->expon = MulMod(binv, adiff, d->order);
  97. d->found_collision = true;
  98. } else {
  99. if (d->order > (1<<20)) {
  100. cerr << "Unhelpful collision\n";
  101. }
  102. }
  103. }
  104. return d->found_collision;
  105. }
  106. // Compute the discrete log of target mod p, to the given base.
  107. // p must be the current ZZ_p modulus.
  108. // Place the result in exp. fvec is a vector of the factors of
  109. // (p-1)/2, which must each be small enough to compute discrete logs
  110. // with some other method (kangaroo, index calculus, GNFS, etc.).
  111. // label is "p" or "q", to be printed to report progress.
  112. // Return 0 on failure, 1 on success.
  113. static int p_dl(const ZZ_p &target, const ZZ_p &base, ZZ &exp,
  114. const vec_ZZ &fvec, const string &label, unsigned int initial_subproblem_id,
  115. unsigned short GB_mem)
  116. {
  117. const int flen = fvec.length();
  118. // Compute phi(p)
  119. ZZ phip;
  120. phip = 2;
  121. for (int i = 0; i < flen; ++i) {
  122. phip *= fvec[i];
  123. }
  124. // Invariant: the desired exp \equiv (curexp mod curmodulus)
  125. ZZ curmodulus, curexp;
  126. curmodulus = 2;
  127. curexp = 0;
  128. for (int i = 0; i < flen; ++i) {
  129. unsigned short desired_dpnodes;
  130. unsigned int max_workers;
  131. unsigned int dpfreq;
  132. unsigned short freq_reduction_threshold = 4;
  133. // this should be okay in most cases and shouldn't cause
  134. // any negative effects if it's too low
  135. desired_resources(fvec[i], 1, 1, GB_mem, freq_reduction_threshold, desired_dpnodes, max_workers, dpfreq);
  136. ZZ quotient;
  137. ZZ_p subgroup_base;
  138. ZZ_p subgroup_target;
  139. {
  140. AtomicWriter atomic_cout(cout);
  141. atomic_cout << label << " submodulus " << i+1 << " of " << flen << "...\n";
  142. atomic_cout << "Settings (memory, dpfreq): " << GB_mem << ", " << dpfreq << "\n";
  143. // Figure out exp mod fvec[i] by taking each side to the power
  144. // of phirho/fvec[i] so that we're working in the
  145. // subgroup of order fvec[i].
  146. quotient = phip / fvec[i];
  147. subgroup_base = power(base, quotient);
  148. subgroup_target = power(target, quotient);
  149. if (subgroup_base == 1) {
  150. // The original base wasn't a generator of the whole group.
  151. if (subgroup_target == 1) {
  152. atomic_cout << "Non-unique solution (mod " << fvec[i] <<")\n";
  153. continue;
  154. } else {
  155. atomic_cout << "Target not in subgroup generated by base\n";
  156. return 0;
  157. }
  158. }
  159. }
  160. #ifdef SAVE_DPS
  161. std::ostringstream oss;
  162. oss << "dplist_" << i+initial_subproblem_id << ".out";
  163. dp_file_stream.open(oss.str().c_str());
  164. #endif
  165. // Now use your favourite method to get the DL of
  166. // subgroup_target with base subgroup_base, knowing that it's in
  167. // the range [0,fvec[i]).
  168. ZZ md = ZZ_p::modulus();
  169. CBData cbdata(subgroup_base, subgroup_target, fvec[i]);
  170. struct timeval st, et;
  171. #ifdef SAVE_DPS
  172. dp_file_stream << "Subproblem " << i+initial_subproblem_id << "\n";
  173. #endif
  174. gettimeofday(&st, NULL);
  175. unsigned int launch_count = 0;
  176. bool filled_dp_buffer = false;
  177. {
  178. #ifdef DERANDOMIZE
  179. RandomStreamPush push_seed;
  180. // the seed will be reset to its original value
  181. // once we exit this scope
  182. SetSeed(rep(subgroup_base)*rep(subgroup_target)*fvec[i]*md);
  183. #endif
  184. cuda_dl(subgroup_base, subgroup_target, fvec[i], md, dpfreq, &cbdata, &launch_count, &filled_dp_buffer);
  185. }
  186. ZZ subgroup_dl = cbdata.expon;
  187. gettimeofday(&et, NULL);
  188. unsigned long us_elapsed = (et.tv_sec-st.tv_sec)*1000000 +
  189. (et.tv_usec-st.tv_usec);
  190. printf("%ld.%06ld seconds elapsed\n", us_elapsed/1000000,
  191. us_elapsed % 1000000);
  192. AtomicWriter(cout) << "Timing (subproblemid, label, launches): "
  193. << i+initial_subproblem_id << ", "
  194. << label << ", "
  195. << launch_count << "\n" << std::flush;
  196. CRT(curexp, curmodulus, subgroup_dl, fvec[i]);
  197. // cout << "CRT\n";
  198. // cout << "curexp = " << curexp << "\n";
  199. // cout << "curmodulus = " << curmodulus << "\n\n";
  200. #ifdef SAVE_DPS
  201. if (filled_dp_buffer) {
  202. AtomicWriter atomic_cout(cout);
  203. atomic_cout << "Warning: The device dp buffer was filled, so some points were not recorded.\n";
  204. atomic_cout << "These points will not be reproducible (subproblem " << i+initial_subproblem_id << ").\n";
  205. }
  206. dp_file_stream.close();
  207. #endif
  208. }
  209. // We'd like a non-negative answer back
  210. if (curexp >= 0) {
  211. exp = curexp;
  212. } else {
  213. exp = curexp + curmodulus;
  214. }
  215. return 1;
  216. }
  217. typedef struct {
  218. pid_t pid;
  219. int rfd;
  220. } PDLHandle;
  221. // Behave like p_dl, but do the work in an asynchronous subprocess
  222. static PDLHandle* p_dl_fork_start(const ZZ &p, const ZZ_p &target,
  223. const ZZ_p &base, const vec_ZZ &fvec, const string &label,
  224. int deviceid, unsigned int initial_subproblem_id,
  225. unsigned short GB_mem_per_subprocess)
  226. {
  227. PDLHandle *handle = new PDLHandle;
  228. int fds[2];
  229. int res = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
  230. if (res < 0) {
  231. perror("socketpair");
  232. delete handle;
  233. return NULL;
  234. }
  235. handle->rfd = fds[0];
  236. cout.flush();
  237. cerr.flush();
  238. // flush the output before forking
  239. pid_t childpid = fork();
  240. if (childpid == -1) {
  241. perror("fork");
  242. delete handle;
  243. return NULL;
  244. }
  245. if (childpid == 0) {
  246. // Child
  247. close(fds[0]);
  248. int wfd = fds[1];
  249. ZZ exp;
  250. ZZ_p::init(p);
  251. ZZ_p target_p, base_p;
  252. conv(target_p, rep(target));
  253. conv(base_p, rep(base));
  254. cudaError_t cudares = cudaSetDevice(deviceid);
  255. if (cudares != cudaSuccess) {
  256. AtomicWriter(cerr) << "Error setting CUDA device: " << cudaGetErrorString(cudares) << "\n";
  257. exit(1);
  258. }
  259. int res = p_dl(target_p, base_p, exp, fvec, label, initial_subproblem_id, GB_mem_per_subprocess);
  260. if (res) {
  261. // Write the result back to the parent using wfd
  262. unsigned short explen = NumBytes(exp);
  263. res = write(wfd, &explen, sizeof(unsigned short));
  264. unsigned char expbuf[explen];
  265. BytesFromZZ(expbuf, exp, explen);
  266. res = write(wfd, expbuf, explen);
  267. close(wfd);
  268. }
  269. exit(0);
  270. } else {
  271. // Parent
  272. close(fds[1]);
  273. }
  274. return handle;
  275. }
  276. // Wait until the subprocess started by p_dl_fork_start completes, and
  277. // return its result. handle is cleaned up.
  278. static int p_dl_fork_join(PDLHandle *handle, ZZ &exp)
  279. {
  280. if (!handle) return 0;
  281. pid_t childpid = handle->pid;
  282. unsigned short explen;
  283. int res = read(handle->rfd, &explen, sizeof(unsigned short));
  284. if (res < (int)sizeof(unsigned short)) {
  285. close(handle->rfd);
  286. delete handle;
  287. waitpid(childpid, NULL, 0);
  288. return 0;
  289. }
  290. unsigned char expbuf[explen];
  291. res = read(handle->rfd, expbuf, explen);
  292. if (res < explen) {
  293. close(handle->rfd);
  294. delete handle;
  295. waitpid(childpid, NULL, 0);
  296. return 0;
  297. }
  298. ZZFromBytes(exp, expbuf, explen);
  299. close(handle->rfd);
  300. delete handle;
  301. waitpid(childpid, NULL, 0);
  302. return 1;
  303. }
  304. int main(int argc, char **argv)
  305. {
  306. #ifdef SAVE_DPS
  307. cerr << "Note: Saving the distinguished points. Do not run huge problems or else it will use up all of your disk space.\n";
  308. #ifndef DERANDOMIZE
  309. cerr << "Saving DPs without derandomization!\n";
  310. #endif
  311. #endif
  312. if (argc != 2) {
  313. cerr << "Usage: " << argv[0] << " total_mem_GB";
  314. exit(1);
  315. }
  316. unsigned short GB_mem_per_node = strtol(argv[1], NULL, 10);
  317. // Initialize the prng with some randomness from the kernel
  318. unsigned char randbuf[1024];
  319. ifstream urand("/dev/urandom");
  320. urand.read((char *)randbuf, sizeof(randbuf));
  321. urand.close();
  322. ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
  323. SetSeed(randzz);
  324. ZZ rho, p, q;
  325. vec_ZZ pfvec, qfvec;
  326. cin >> rho >> p >> pfvec >> q >> qfvec;
  327. // Generate a DLP mod rho (in the large odd-order subgroup)
  328. ZZ_p::init(rho);
  329. ZZ_p base;
  330. ZZ_p target;
  331. {
  332. #ifdef DERANDOMIZE
  333. RandomStreamPush push_seed;
  334. // the seed will be reset to its original value
  335. // once we exit this scope
  336. SetSeed(rho*p*q);
  337. #endif
  338. base = power(random_ZZ_p(), 2);
  339. target = power(random_ZZ_p(), 2);
  340. }
  341. cout << "base = " << base << "\n";
  342. cout << "target = " << target << "\n";
  343. ZZ exp_p, exp_q, exp;
  344. int res_p = 0;
  345. int res_q = 0;
  346. PDLHandle *handle_p, *handle_q;
  347. handle_p = p_dl_fork_start(p, target, base, pfvec, "p", 0, 0, GB_mem_per_node/2);
  348. handle_q = p_dl_fork_start(q, target, base, qfvec, "q", 1, pfvec.length(), GB_mem_per_node/2);
  349. res_p = p_dl_fork_join(handle_p, exp_p);
  350. res_q = p_dl_fork_join(handle_q, exp_q);
  351. if (res_p && res_q) {
  352. ZZ_p::init(rho);
  353. ZZ pm1 = (p - 1)/2;
  354. ZZ qm1 = (q - 1)/2;
  355. if (exp_p < 0) exp_p += pm1;
  356. if (exp_q < 0) exp_q += qm1;
  357. CRT(exp_p, pm1, exp_q, qm1);
  358. exp = exp_p;
  359. // We'd like a non-negative answer back
  360. if (exp < 0) exp += pm1;
  361. cout << "exp = " << exp << "\n";
  362. ZZ_p base_exp;
  363. power(base_exp, base, exp);
  364. if (base_exp == target) {
  365. cout << "CORRECT!\n";
  366. } else {
  367. cout << "INCORRECT:\nbase^exp = " << base_exp << "\n";
  368. cout << "target = " << target << "\n";
  369. }
  370. } else {
  371. cout << "FAIL\n";
  372. }
  373. return 0;
  374. }