dlrho.cc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * cudadl version 0.8: 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 "cudadl.h"
  24. #include <sys/types.h>
  25. #include <sys/wait.h>
  26. #include <sys/socket.h>
  27. #include <cuda_runtime.h>
  28. NTL_CLIENT
  29. // Compute the discrete log of target mod p, to the given base.
  30. // p must be the current ZZ_p modulus.
  31. // Place the result in exp. fvec is a vector of the factors of
  32. // (p-1)/2, which must each be small enough to compute discrete logs
  33. // with some other method (kangaroo, index calculus, GNFS, etc.).
  34. // label is "p" or "q", to be printed to report progress.
  35. // Return 0 on failure, 1 on success.
  36. static int p_dl(const ZZ_p &target, const ZZ_p &base, ZZ &exp,
  37. const vec_ZZ &fvec, const string &label)
  38. {
  39. const int flen = fvec.length();
  40. // Compute phi(p)
  41. ZZ phip;
  42. phip = 2;
  43. for (int i = 0; i < flen; ++i) {
  44. phip *= fvec[i];
  45. }
  46. // Invariant: the desired exp \equiv (curexp mod curmodulus)
  47. ZZ curmodulus, curexp;
  48. curmodulus = 2;
  49. curexp = 0;
  50. for (int i = 0; i < flen; ++i) {
  51. cout << label << " submodulus " << i+1 << " of " << flen << "... ";
  52. cout.flush();
  53. // Figure out exp mod fvec[i] by taking each side to the power
  54. // of phirho/fvec[i] so that we're working in the
  55. // subgroup of order fvec[i].
  56. ZZ quotient = phip / fvec[i];
  57. ZZ_p subgroup_base = power(base, quotient);
  58. ZZ_p subgroup_target = power(target, quotient);
  59. if (subgroup_base == 1) {
  60. // The original base wasn't a generator of the whole group.
  61. if (subgroup_target == 1) {
  62. cout << "Non-unique solution (mod " << fvec[i] <<")\n";
  63. continue;
  64. } else {
  65. cout << "Target not in subgroup generated by base\n";
  66. return 0;
  67. }
  68. }
  69. // Now use your favourite method to get the DL of
  70. // subgroup_target with base subgroup_base, knowing that it's in
  71. // the range [0,fvec[i]).
  72. cout << "\n";
  73. ZZ md = ZZ_p::modulus();
  74. struct timeval st, et;
  75. gettimeofday(&st, NULL);
  76. ZZ subgroup_dl = cuda_dl(subgroup_base, subgroup_target, fvec[i],
  77. md);
  78. gettimeofday(&et, NULL);
  79. unsigned long us_elapsed = (et.tv_sec-st.tv_sec)*1000000 +
  80. (et.tv_usec-st.tv_usec);
  81. printf("%ld.%06ld seconds elapsed\n", us_elapsed/1000000,
  82. us_elapsed % 1000000);
  83. CRT(curexp, curmodulus, subgroup_dl, fvec[i]);
  84. // cout << "CRT\n";
  85. // cout << "curexp = " << curexp << "\n";
  86. // cout << "curmodulus = " << curmodulus << "\n\n";
  87. }
  88. // We'd like a non-negative answer back
  89. if (curexp >= 0) {
  90. exp = curexp;
  91. } else {
  92. exp = curexp + curmodulus;
  93. }
  94. return 1;
  95. }
  96. typedef struct {
  97. pid_t pid;
  98. int rfd;
  99. } PDLHandle;
  100. // Behave like p_dl, but do the work in an asynchronous subprocess
  101. static PDLHandle* p_dl_fork_start(const ZZ &p, const ZZ_p &target,
  102. const ZZ_p &base, const vec_ZZ &fvec, const string &label, int deviceid)
  103. {
  104. PDLHandle *handle = new PDLHandle;
  105. int fds[2];
  106. int res = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
  107. if (res < 0) {
  108. perror("socketpair");
  109. delete handle;
  110. return NULL;
  111. }
  112. handle->rfd = fds[0];
  113. pid_t childpid = fork();
  114. if (childpid == -1) {
  115. perror("fork");
  116. delete handle;
  117. return NULL;
  118. }
  119. if (childpid == 0) {
  120. // Child
  121. close(fds[0]);
  122. int wfd = fds[1];
  123. ZZ exp;
  124. ZZ_p::init(p);
  125. ZZ_p target_p, base_p;
  126. conv(target_p, rep(target));
  127. conv(base_p, rep(base));
  128. cudaError_t cudares = cudaSetDevice(deviceid);
  129. if (cudares != cudaSuccess) {
  130. cerr << "Error setting CUDA device\n";
  131. exit(1);
  132. }
  133. int res = p_dl(target_p, base_p, exp, fvec, label);
  134. if (res) {
  135. // Write the result back to the parent using wfd
  136. unsigned short explen = NumBytes(exp);
  137. res = write(wfd, &explen, sizeof(unsigned short));
  138. unsigned char expbuf[explen];
  139. BytesFromZZ(expbuf, exp, explen);
  140. res = write(wfd, expbuf, explen);
  141. close(wfd);
  142. }
  143. exit(0);
  144. } else {
  145. // Parent
  146. close(fds[1]);
  147. }
  148. return handle;
  149. }
  150. // Wait until the subprocess started by p_dl_fork_start completes, and
  151. // return its result. handle is cleaned up.
  152. static int p_dl_fork_join(PDLHandle *handle, ZZ &exp)
  153. {
  154. if (!handle) return 0;
  155. pid_t childpid = handle->pid;
  156. unsigned short explen;
  157. int res = read(handle->rfd, &explen, sizeof(unsigned short));
  158. if (res < (int)sizeof(unsigned short)) {
  159. close(handle->rfd);
  160. delete handle;
  161. waitpid(childpid, NULL, 0);
  162. return 0;
  163. }
  164. unsigned char expbuf[explen];
  165. res = read(handle->rfd, expbuf, explen);
  166. if (res < explen) {
  167. close(handle->rfd);
  168. delete handle;
  169. waitpid(childpid, NULL, 0);
  170. return 0;
  171. }
  172. ZZFromBytes(exp, expbuf, explen);
  173. close(handle->rfd);
  174. delete handle;
  175. waitpid(childpid, NULL, 0);
  176. return 1;
  177. }
  178. int main(int argc, char **argv)
  179. {
  180. // Initialize the prng with some randomness from the kernel
  181. unsigned char randbuf[1024];
  182. ifstream urand("/dev/urandom");
  183. urand.read((char *)randbuf, sizeof(randbuf));
  184. urand.close();
  185. ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
  186. SetSeed(randzz);
  187. ZZ rho, p, q;
  188. vec_ZZ pfvec, qfvec;
  189. cin >> rho >> p >> pfvec >> q >> qfvec;
  190. // Generate a DLP mod rho (in the large odd-order subgroup)
  191. ZZ_p::init(rho);
  192. ZZ_p base = power(random_ZZ_p(), 2);
  193. ZZ_p target = power(random_ZZ_p(), 2);
  194. cout << "base = " << base << "\n";
  195. cout << "target = " << target << "\n";
  196. ZZ exp_p, exp_q, exp;
  197. int res_p = 0;
  198. int res_q = 0;
  199. PDLHandle *handle_p, *handle_q;
  200. handle_p = p_dl_fork_start(p, target, base, pfvec, "p", 0);
  201. handle_q = p_dl_fork_start(q, target, base, qfvec, "q", 1);
  202. res_p = p_dl_fork_join(handle_p, exp_p);
  203. res_q = p_dl_fork_join(handle_q, exp_q);
  204. if (res_p && res_q) {
  205. ZZ_p::init(rho);
  206. ZZ pm1 = (p - 1)/2;
  207. ZZ qm1 = (q - 1)/2;
  208. if (exp_p < 0) exp_p += pm1;
  209. if (exp_q < 0) exp_q += qm1;
  210. CRT(exp_p, pm1, exp_q, qm1);
  211. exp = exp_p;
  212. // We'd like a non-negative answer back
  213. if (exp < 0) exp += pm1;
  214. cout << "exp = " << exp << "\n";
  215. ZZ_p base_exp;
  216. power(base_exp, base, exp);
  217. if (base_exp == target) {
  218. cout << "CORRECT!\n";
  219. } else {
  220. cout << "INCORRECT:\nbase^exp = " << base_exp << "\n";
  221. cout << "target = " << target << "\n";
  222. }
  223. } else {
  224. cout << "FAIL\n";
  225. }
  226. return 0;
  227. }