#include <stdarg.h>
#include "bee2/core/mem.h"
#include "bee2/core/util.h"
#include "bee2/core/word.h"
#include "bee2/math/ec.h"
#include "bee2/math/ww.h"
bool_t ecIsOperable2(const ec_o* ec)
{
return objIsOperable2(ec) &&
objKeep(ec) >= sizeof(ec_o) &&
objPCount(ec) == 6 &&
objOCount(ec) == 1 &&
wwIsValid(ec->A, ec->f->n) &&
wwIsValid(ec->B, ec->f->n) &&
ec->d >= 3 &&
ec->froma != 0 &&
ec->toa != 0 &&
ec->neg != 0 &&
ec->add != 0 &&
ec->adda != 0 &&
ec->sub != 0 &&
ec->suba != 0 &&
ec->dbl != 0 &&
ec->dbla != 0;
}
bool_t ecIsOperable(const ec_o* ec)
{
return ecIsOperable2(ec) &&
qrIsOperable(ec->f) &&
ec->deep >= ec->f->deep;
}
bool_t ecCreateGroup(ec_o* ec, const octet xbase[], const octet ybase[],
const octet order[], size_t order_len, u32 cofactor, void* stack)
{
ASSERT(ecIsOperable(ec));
ASSERT(memIsValid(order, order_len));
ASSERT(memIsNullOrValid(xbase, ec->f->no));
ASSERT(memIsNullOrValid(ybase, ec->f->no));
order_len = memNonZeroSize(order, order_len);
if (order_len == 0 ||
W_OF_O(order_len) > ec->f->n + 1 ||
cofactor == 0 ||
(u32)(word)cofactor != cofactor)
return FALSE;
if (xbase == 0)
qrSetZero(ecX(ec->base), ec->f);
else if (!qrFrom(ecX(ec->base), xbase, ec->f, stack))
return FALSE;
if (ybase == 0)
qrSetZero(ecY(ec->base, ec->f->n), ec->f);
else if (!qrFrom(ecY(ec->base, ec->f->n), ybase, ec->f, stack))
return FALSE;
wwFrom(ec->order, order, order_len);
wwSetZero(ec->order + W_OF_O(order_len),
ec->f->n + 1 - W_OF_O(order_len));
ec->cofactor = (word)cofactor;
return TRUE;
}
size_t ecCreateGroup_deep(size_t f_deep)
{
return f_deep;
}
bool_t ecIsOperableGroup(const ec_o* ec)
{
ASSERT(ecIsOperable(ec));
return wwIsValid(ec->base, 2 * ec->f->n) &&
wwIsValid(ec->order, ec->f->n + 1) &&
!wwIsZero(ec->order, ec->f->n + 1) &&
ec->cofactor != 0;
}
static size_t ecNAFWidth(size_t l)
{
if (l >= 336)
return 6;
else if (l >= 120)
return 5;
else if (l >= 40)
return 4;
return 3;
}
bool_t ecMulA(word b[], const word a[], const ec_o* ec, const word d[],
size_t m, void* stack)
{
const size_t n = ec->f->n;
const size_t naf_width = ecNAFWidth(B_OF_W(m));
const size_t naf_count = SIZE_1 << (naf_width - 2);
const word naf_hi = WORD_1 << (naf_width - 1);
register size_t naf_size;
register size_t i;
register word w;
word* naf;
word* t;
word* pre;
ASSERT(ecIsOperable(ec));
naf = (word*)stack;
t = naf + 2 * m + 1;
pre = t + ec->d * n;
stack = pre + naf_count * ec->d * n;
ASSERT(naf_width >= 3);
naf_size = wwNAF(naf, d, m, naf_width);
if (naf_size == 0)
return FALSE;
ecFromA(pre, a, ec, stack);
ASSERT(naf_count > 1);
ecDblA(t, pre, ec, stack);
ecAddA(pre + ec->d * n, t, pre, ec, stack);
for (i = 2; i < naf_count; ++i)
ecAdd(pre + i * ec->d * n, t, pre + (i - 1) * ec->d * n, ec, stack);
w = wwGetBits(naf, 0, naf_width);
ASSERT((w & 1) == 1 && (w & naf_hi) == 0);
wwCopy(t, pre + (w >> 1) * ec->d * n, ec->d * n);
i = naf_width;
while (--naf_size)
{
w = wwGetBits(naf, i, naf_width);
if (w & 1)
{
ecDbl(t, t, ec, stack);
if (w == 1)
ecAddA(t, t, pre, ec, stack);
else if (w == (naf_hi ^ 1))
ecSubA(t, t, pre, ec, stack);
else if (w & naf_hi)
ecSub(t, t, pre + ((w ^ naf_hi) >> 1) * ec->d * n, ec, stack);
else
ecAdd(t, t, pre + (w >> 1) * ec->d * n, ec, stack);
i += naf_width;
}
else
ecDbl(t, t, ec, stack), ++i;
}
CLEAN2(w, i);
return ecToA(b, t, ec, stack);
}
size_t ecMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m)
{
const size_t naf_width = ecNAFWidth(B_OF_W(m));
const size_t naf_count = SIZE_1 << (naf_width - 2);
return O_OF_W(2 * m + 1) +
O_OF_W(ec_d * n) +
O_OF_W(ec_d * n * naf_count) +
ec_deep;
}
bool_t ecHasOrderA(const word a[], const ec_o* ec, const word q[], size_t m,
void* stack)
{
const size_t n = ec->f->n;
word* b = (word*)stack;
stack = b + ec->d * n;
return !ecMulA(b, a, ec, q, m, stack);
}
size_t ecHasOrderA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t m)
{
return O_OF_W(ec_d * n) + ecMulA_deep(n, ec_d, ec_deep, m);
}
bool_t ecAddMulA(word b[], const ec_o* ec, void* stack, size_t k, ...)
{
const size_t n = ec->f->n;
register word w;
size_t i, naf_max_size = 0;
va_list marker;
word* t;
size_t* m;
size_t* naf_width;
size_t* naf_size;
size_t* naf_pos;
word** naf;
word** pre;
ASSERT(ecIsOperable(ec));
ASSERT(k > 0);
t = (word*)stack;
m = (size_t*)(t + ec->d * n);
naf_width = m + k;
naf_size = naf_width + k;
naf_pos = naf_size + k;
naf = (word**)(naf_pos + k);
pre = naf + k;
stack = pre + k;
va_start(marker, k);
for (i = 0; i < k; ++i)
{
const word* a;
const word* d;
size_t naf_count, j;
a = va_arg(marker, const word*);
d = va_arg(marker, const word*);
m[i] = va_arg(marker, size_t);
m[i] = wwWordSize(d, m[i]);
naf_width[i] = ecNAFWidth(B_OF_W(m[i]));
naf_count = SIZE_1 << (naf_width[i] - 2);
naf[i] = (word*)stack;
stack = naf[i] + 2 * m[i] + 1;
naf_size[i] = wwNAF(naf[i], d, m[i], naf_width[i]);
if (naf_size[i] > naf_max_size)
naf_max_size = naf_size[i];
naf_pos[i] = 0;
pre[i] = (word*)stack;
stack = pre[i] + ec->d * n * naf_count;
ecFromA(pre[i], a, ec, stack);
ASSERT(naf_count > 1);
ecDblA(t, pre[i], ec, stack);
ecAddA(pre[i] + ec->d * n, t, pre[i], ec, stack);
for (j = 2; j < naf_count; ++j)
ecAdd(pre[i] + j * ec->d * n, t, pre[i] + (j - 1) * ec->d * n, ec,
stack);
}
va_end(marker);
ecSetO(t, ec);
for (; naf_max_size; --naf_max_size)
{
ecDbl(t, t, ec, stack);
for (i = 0; i < k; ++i)
{
word naf_hi;
if (naf_size[i] < naf_max_size)
continue;
w = wwGetBits(naf[i], naf_pos[i], naf_width[i]);
naf_hi = WORD_1 << (naf_width[i] - 1);
if (w & 1)
{
if (w == 1)
ecAddA(t, t, pre[i], ec, stack);
else if (w == (naf_hi ^ 1))
ecSubA(t, t, pre[i], ec, stack);
else if (w & naf_hi)
w ^= naf_hi,
ecSub(t, t, pre[i] + (w >> 1) * ec->d * n, ec, stack);
else
ecAdd(t, t, pre[i] + (w >> 1) * ec->d * n, ec, stack);
naf_pos[i] += naf_width[i];
}
else
++naf_pos[i];
}
}
CLEAN(w);
return ecToA(b, t, ec, stack);
}
size_t ecAddMulA_deep(size_t n, size_t ec_d, size_t ec_deep, size_t k, ...)
{
size_t i, ret;
va_list marker;
ret = O_OF_W(ec_d * n);
ret += 4 * sizeof(size_t) * k;
ret += 2 * sizeof(word**) * k;
va_start(marker, k);
for (i = 0; i < k; ++i)
{
size_t m = va_arg(marker, size_t);
size_t naf_width = ecNAFWidth(B_OF_W(m));
size_t naf_count = SIZE_1 << (naf_width - 2);
ret += O_OF_W(2 * m + 1);
ret += O_OF_W(ec_d * n * naf_count);
}
va_end(marker);
ret += ec_deep;
return ret;
}