dlrho.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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 <sys/time.h>
  21. #include <NTL/vec_ZZ.h>
  22. #include <NTL/ZZ_p.h>
  23. #include <map>
  24. #include <sys/types.h>
  25. #include <sys/wait.h>
  26. #include <sys/socket.h>
  27. #include <cuda_runtime.h>
  28. #include "cudadl.h"
  29. NTL_CLIENT
  30. typedef map<std::string, pair<ZZ,ZZ> > DTable;
  31. struct CBData {
  32. const ZZ_p &base;
  33. const ZZ_p &target;
  34. const ZZ &order;
  35. unsigned long long numdp;
  36. DTable dtable;
  37. bool found_collision;
  38. ZZ expon;
  39. CBData(const ZZ_p &_base, const ZZ_p &_target, const ZZ &_order) :
  40. base(_base), target(_target), order(_order), numdp(0),
  41. found_collision(false) {}
  42. };
  43. // This function is called from inside cuda_dl for each DP it encounters.
  44. // It calls the function named "dpcallback" directly. It would be
  45. // cleaner if this were passed as a function pointer to cuda_dl, but
  46. // that makes nvcc 3.1 segfault. :-p
  47. // dp points to an array of WORDS+7 unsigned ints:
  48. // - 1 word of threadID/blockID
  49. // - WORDS words of the dp value
  50. // - 3 words of a
  51. // - 3 words of b
  52. bool dpcallback(void *cbdata, unsigned int *dpwords)
  53. {
  54. CBData *d = (CBData*)cbdata;
  55. // WARNING: this assumes
  56. // sizeof(unsigned long) == sizeof(unsigned long long) !
  57. ZZ zz_a = to_ZZ(dpwords[WORDS+3]);
  58. zz_a <<= 32;
  59. zz_a += dpwords[WORDS+2];
  60. zz_a <<= 32;
  61. zz_a += dpwords[WORDS+1];
  62. ZZ zz_b = to_ZZ(dpwords[WORDS+6]);
  63. zz_b <<= 32;
  64. zz_b += dpwords[WORDS+5];
  65. zz_b <<= 32;
  66. zz_b += dpwords[WORDS+4];
  67. //ZZ_p dp = power(d->base, zz_a) * power(d->target, zz_b);
  68. // cerr << "DP " << ++(d->numdp) << "\r";
  69. pair<ZZ,ZZ> ab(zz_a,zz_b);
  70. pair<DTable::iterator, bool> res;
  71. string x((const char *)(dpwords+1), WORDS*sizeof(unsigned int));
  72. res = d->dtable.insert(DTable::value_type(x, ab));
  73. if (!res.second) {
  74. // Collision!
  75. ZZ adiff = to_ZZ(res.first->second.first) - zz_a;
  76. ZZ bdiff = zz_b - to_ZZ(res.first->second.second);
  77. if (bdiff < 0) bdiff += d->order;
  78. ZZ binv;
  79. if (InvModStatus(binv, bdiff, d->order) == 0) {
  80. d->expon = MulMod(binv, adiff, d->order);
  81. d->found_collision = true;
  82. } else {
  83. if (d->order > (1<<20)) {
  84. cerr << "Unhelpful collision\n";
  85. }
  86. }
  87. }
  88. return d->found_collision;
  89. }
  90. // Compute the discrete log of target mod p, to the given base.
  91. // p must be the current ZZ_p modulus.
  92. // Place the result in exp. fvec is a vector of the factors of
  93. // (p-1)/2, which must each be small enough to compute discrete logs
  94. // with some other method (kangaroo, index calculus, GNFS, etc.).
  95. // label is "p" or "q", to be printed to report progress.
  96. // Return 0 on failure, 1 on success.
  97. static int p_dl(const ZZ_p &target, const ZZ_p &base, ZZ &exp,
  98. const vec_ZZ &fvec, const string &label)
  99. {
  100. const int flen = fvec.length();
  101. // Compute phi(p)
  102. ZZ phip;
  103. phip = 2;
  104. for (int i = 0; i < flen; ++i) {
  105. phip *= fvec[i];
  106. }
  107. // Invariant: the desired exp \equiv (curexp mod curmodulus)
  108. ZZ curmodulus, curexp;
  109. curmodulus = 2;
  110. curexp = 0;
  111. for (int i = 0; i < flen; ++i) {
  112. cout << label << " submodulus " << i+1 << " of " << flen << "... ";
  113. cout.flush();
  114. // Figure out exp mod fvec[i] by taking each side to the power
  115. // of phirho/fvec[i] so that we're working in the
  116. // subgroup of order fvec[i].
  117. ZZ quotient = phip / fvec[i];
  118. ZZ_p subgroup_base = power(base, quotient);
  119. ZZ_p subgroup_target = power(target, quotient);
  120. if (subgroup_base == 1) {
  121. // The original base wasn't a generator of the whole group.
  122. if (subgroup_target == 1) {
  123. cout << "Non-unique solution (mod " << fvec[i] <<")\n";
  124. continue;
  125. } else {
  126. cout << "Target not in subgroup generated by base\n";
  127. return 0;
  128. }
  129. }
  130. // Now use your favourite method to get the DL of
  131. // subgroup_target with base subgroup_base, knowing that it's in
  132. // the range [0,fvec[i]).
  133. cout << "\n";
  134. ZZ md = ZZ_p::modulus();
  135. CBData cbdata(subgroup_base, subgroup_target, fvec[i]);
  136. struct timeval st, et;
  137. unsigned int dpfreq = 4294967; // 2^32/1000
  138. if (fvec[i] < 1000000) {
  139. dpfreq = 4294967295; // 2^32-1 : every point is a DP
  140. }
  141. gettimeofday(&st, NULL);
  142. cuda_dl(subgroup_base, subgroup_target, fvec[i], md, dpfreq, &cbdata);
  143. ZZ subgroup_dl = cbdata.expon;
  144. gettimeofday(&et, NULL);
  145. unsigned long us_elapsed = (et.tv_sec-st.tv_sec)*1000000 +
  146. (et.tv_usec-st.tv_usec);
  147. printf("%ld.%06ld seconds elapsed\n", us_elapsed/1000000,
  148. us_elapsed % 1000000);
  149. CRT(curexp, curmodulus, subgroup_dl, fvec[i]);
  150. // cout << "CRT\n";
  151. // cout << "curexp = " << curexp << "\n";
  152. // cout << "curmodulus = " << curmodulus << "\n\n";
  153. }
  154. // We'd like a non-negative answer back
  155. if (curexp >= 0) {
  156. exp = curexp;
  157. } else {
  158. exp = curexp + curmodulus;
  159. }
  160. return 1;
  161. }
  162. typedef struct {
  163. pid_t pid;
  164. int rfd;
  165. } PDLHandle;
  166. // Behave like p_dl, but do the work in an asynchronous subprocess
  167. static PDLHandle* p_dl_fork_start(const ZZ &p, const ZZ_p &target,
  168. const ZZ_p &base, const vec_ZZ &fvec, const string &label, int deviceid)
  169. {
  170. PDLHandle *handle = new PDLHandle;
  171. int fds[2];
  172. int res = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
  173. if (res < 0) {
  174. perror("socketpair");
  175. delete handle;
  176. return NULL;
  177. }
  178. handle->rfd = fds[0];
  179. pid_t childpid = fork();
  180. if (childpid == -1) {
  181. perror("fork");
  182. delete handle;
  183. return NULL;
  184. }
  185. if (childpid == 0) {
  186. // Child
  187. close(fds[0]);
  188. int wfd = fds[1];
  189. ZZ exp;
  190. ZZ_p::init(p);
  191. ZZ_p target_p, base_p;
  192. conv(target_p, rep(target));
  193. conv(base_p, rep(base));
  194. cudaError_t cudares = cudaSetDevice(deviceid);
  195. if (cudares != cudaSuccess) {
  196. cerr << "Error setting CUDA device\n";
  197. exit(1);
  198. }
  199. int res = p_dl(target_p, base_p, exp, fvec, label);
  200. if (res) {
  201. // Write the result back to the parent using wfd
  202. unsigned short explen = NumBytes(exp);
  203. res = write(wfd, &explen, sizeof(unsigned short));
  204. unsigned char expbuf[explen];
  205. BytesFromZZ(expbuf, exp, explen);
  206. res = write(wfd, expbuf, explen);
  207. close(wfd);
  208. }
  209. exit(0);
  210. } else {
  211. // Parent
  212. close(fds[1]);
  213. }
  214. return handle;
  215. }
  216. // Wait until the subprocess started by p_dl_fork_start completes, and
  217. // return its result. handle is cleaned up.
  218. static int p_dl_fork_join(PDLHandle *handle, ZZ &exp)
  219. {
  220. if (!handle) return 0;
  221. pid_t childpid = handle->pid;
  222. unsigned short explen;
  223. int res = read(handle->rfd, &explen, sizeof(unsigned short));
  224. if (res < (int)sizeof(unsigned short)) {
  225. close(handle->rfd);
  226. delete handle;
  227. waitpid(childpid, NULL, 0);
  228. return 0;
  229. }
  230. unsigned char expbuf[explen];
  231. res = read(handle->rfd, expbuf, explen);
  232. if (res < explen) {
  233. close(handle->rfd);
  234. delete handle;
  235. waitpid(childpid, NULL, 0);
  236. return 0;
  237. }
  238. ZZFromBytes(exp, expbuf, explen);
  239. close(handle->rfd);
  240. delete handle;
  241. waitpid(childpid, NULL, 0);
  242. return 1;
  243. }
  244. int main(int argc, char **argv)
  245. {
  246. // Initialize the prng with some randomness from the kernel
  247. unsigned char randbuf[1024];
  248. ifstream urand("/dev/urandom");
  249. urand.read((char *)randbuf, sizeof(randbuf));
  250. urand.close();
  251. ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
  252. SetSeed(randzz);
  253. ZZ rho, p, q;
  254. vec_ZZ pfvec, qfvec;
  255. cin >> rho >> p >> pfvec >> q >> qfvec;
  256. // Generate a DLP mod rho (in the large odd-order subgroup)
  257. ZZ_p::init(rho);
  258. ZZ_p base = power(random_ZZ_p(), 2);
  259. ZZ_p target = power(random_ZZ_p(), 2);
  260. cout << "base = " << base << "\n";
  261. cout << "target = " << target << "\n";
  262. ZZ exp_p, exp_q, exp;
  263. int res_p = 0;
  264. int res_q = 0;
  265. PDLHandle *handle_p, *handle_q;
  266. handle_p = p_dl_fork_start(p, target, base, pfvec, "p", 0);
  267. handle_q = p_dl_fork_start(q, target, base, qfvec, "q", 1);
  268. res_p = p_dl_fork_join(handle_p, exp_p);
  269. res_q = p_dl_fork_join(handle_q, exp_q);
  270. if (res_p && res_q) {
  271. ZZ_p::init(rho);
  272. ZZ pm1 = (p - 1)/2;
  273. ZZ qm1 = (q - 1)/2;
  274. if (exp_p < 0) exp_p += pm1;
  275. if (exp_q < 0) exp_q += qm1;
  276. CRT(exp_p, pm1, exp_q, qm1);
  277. exp = exp_p;
  278. // We'd like a non-negative answer back
  279. if (exp < 0) exp += pm1;
  280. cout << "exp = " << exp << "\n";
  281. ZZ_p base_exp;
  282. power(base_exp, base, exp);
  283. if (base_exp == target) {
  284. cout << "CORRECT!\n";
  285. } else {
  286. cout << "INCORRECT:\nbase^exp = " << base_exp << "\n";
  287. cout << "target = " << target << "\n";
  288. }
  289. } else {
  290. cout << "FAIL\n";
  291. }
  292. return 0;
  293. }