/*
* cudadl version 0.9: Compute discrete logs in smooth group orders
* using CUDA
* Copyright (C) 2012 by Ryan Henry and Ian Goldberg
* {rhenry,iang}@cs.uwaterloo.ca
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of version 3 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "cudadl.h"
#include
#include
#include
#include
NTL_CLIENT
// Compute the discrete log of target mod p, to the given base.
// p must be the current ZZ_p modulus.
// Place the result in exp. fvec is a vector of the factors of
// (p-1)/2, which must each be small enough to compute discrete logs
// with some other method (kangaroo, index calculus, GNFS, etc.).
// label is "p" or "q", to be printed to report progress.
// Return 0 on failure, 1 on success.
static int p_dl(const ZZ_p &target, const ZZ_p &base, ZZ &exp,
const vec_ZZ &fvec, const string &label)
{
const int flen = fvec.length();
// Compute phi(p)
ZZ phip;
phip = 2;
for (int i = 0; i < flen; ++i) {
phip *= fvec[i];
}
// Invariant: the desired exp \equiv (curexp mod curmodulus)
ZZ curmodulus, curexp;
curmodulus = 2;
curexp = 0;
for (int i = 0; i < flen; ++i) {
cout << label << " submodulus " << i+1 << " of " << flen << "... ";
cout.flush();
// Figure out exp mod fvec[i] by taking each side to the power
// of phirho/fvec[i] so that we're working in the
// subgroup of order fvec[i].
ZZ quotient = phip / fvec[i];
ZZ_p subgroup_base = power(base, quotient);
ZZ_p subgroup_target = power(target, quotient);
if (subgroup_base == 1) {
// The original base wasn't a generator of the whole group.
if (subgroup_target == 1) {
cout << "Non-unique solution (mod " << fvec[i] <<")\n";
continue;
} else {
cout << "Target not in subgroup generated by base\n";
return 0;
}
}
// Now use your favourite method to get the DL of
// subgroup_target with base subgroup_base, knowing that it's in
// the range [0,fvec[i]).
cout << "\n";
ZZ md = ZZ_p::modulus();
struct timeval st, et;
gettimeofday(&st, NULL);
ZZ subgroup_dl = cuda_dl(subgroup_base, subgroup_target, fvec[i],
md);
gettimeofday(&et, NULL);
unsigned long us_elapsed = (et.tv_sec-st.tv_sec)*1000000 +
(et.tv_usec-st.tv_usec);
printf("%ld.%06ld seconds elapsed\n", us_elapsed/1000000,
us_elapsed % 1000000);
CRT(curexp, curmodulus, subgroup_dl, fvec[i]);
// cout << "CRT\n";
// cout << "curexp = " << curexp << "\n";
// cout << "curmodulus = " << curmodulus << "\n\n";
}
// We'd like a non-negative answer back
if (curexp >= 0) {
exp = curexp;
} else {
exp = curexp + curmodulus;
}
return 1;
}
typedef struct {
pid_t pid;
int rfd;
} PDLHandle;
// Behave like p_dl, but do the work in an asynchronous subprocess
static PDLHandle* p_dl_fork_start(const ZZ &p, const ZZ_p &target,
const ZZ_p &base, const vec_ZZ &fvec, const string &label, int deviceid)
{
PDLHandle *handle = new PDLHandle;
int fds[2];
int res = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
if (res < 0) {
perror("socketpair");
delete handle;
return NULL;
}
handle->rfd = fds[0];
pid_t childpid = fork();
if (childpid == -1) {
perror("fork");
delete handle;
return NULL;
}
if (childpid == 0) {
// Child
close(fds[0]);
int wfd = fds[1];
ZZ exp;
ZZ_p::init(p);
ZZ_p target_p, base_p;
conv(target_p, rep(target));
conv(base_p, rep(base));
cudaError_t cudares = cudaSetDevice(deviceid);
if (cudares != cudaSuccess) {
cerr << "Error setting CUDA device\n";
exit(1);
}
int res = p_dl(target_p, base_p, exp, fvec, label);
if (res) {
// Write the result back to the parent using wfd
unsigned short explen = NumBytes(exp);
res = write(wfd, &explen, sizeof(unsigned short));
unsigned char expbuf[explen];
BytesFromZZ(expbuf, exp, explen);
res = write(wfd, expbuf, explen);
close(wfd);
}
exit(0);
} else {
// Parent
close(fds[1]);
}
return handle;
}
// Wait until the subprocess started by p_dl_fork_start completes, and
// return its result. handle is cleaned up.
static int p_dl_fork_join(PDLHandle *handle, ZZ &exp)
{
if (!handle) return 0;
pid_t childpid = handle->pid;
unsigned short explen;
int res = read(handle->rfd, &explen, sizeof(unsigned short));
if (res < (int)sizeof(unsigned short)) {
close(handle->rfd);
delete handle;
waitpid(childpid, NULL, 0);
return 0;
}
unsigned char expbuf[explen];
res = read(handle->rfd, expbuf, explen);
if (res < explen) {
close(handle->rfd);
delete handle;
waitpid(childpid, NULL, 0);
return 0;
}
ZZFromBytes(exp, expbuf, explen);
close(handle->rfd);
delete handle;
waitpid(childpid, NULL, 0);
return 1;
}
int main(int argc, char **argv)
{
// Initialize the prng with some randomness from the kernel
unsigned char randbuf[1024];
ifstream urand("/dev/urandom");
urand.read((char *)randbuf, sizeof(randbuf));
urand.close();
ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
SetSeed(randzz);
ZZ rho, p, q;
vec_ZZ pfvec, qfvec;
cin >> rho >> p >> pfvec >> q >> qfvec;
// Generate a DLP mod rho (in the large odd-order subgroup)
ZZ_p::init(rho);
ZZ_p base = power(random_ZZ_p(), 2);
ZZ_p target = power(random_ZZ_p(), 2);
cout << "base = " << base << "\n";
cout << "target = " << target << "\n";
ZZ exp_p, exp_q, exp;
int res_p = 0;
int res_q = 0;
PDLHandle *handle_p, *handle_q;
handle_p = p_dl_fork_start(p, target, base, pfvec, "p", 0);
handle_q = p_dl_fork_start(q, target, base, qfvec, "q", 1);
res_p = p_dl_fork_join(handle_p, exp_p);
res_q = p_dl_fork_join(handle_q, exp_q);
if (res_p && res_q) {
ZZ_p::init(rho);
ZZ pm1 = (p - 1)/2;
ZZ qm1 = (q - 1)/2;
if (exp_p < 0) exp_p += pm1;
if (exp_q < 0) exp_q += qm1;
CRT(exp_p, pm1, exp_q, qm1);
exp = exp_p;
// We'd like a non-negative answer back
if (exp < 0) exp += pm1;
cout << "exp = " << exp << "\n";
ZZ_p base_exp;
power(base_exp, base, exp);
if (base_exp == target) {
cout << "CORRECT!\n";
} else {
cout << "INCORRECT:\nbase^exp = " << base_exp << "\n";
cout << "target = " << target << "\n";
}
} else {
cout << "FAIL\n";
}
return 0;
}