#include "bee2/core/mem.h"
#include "bee2/core/util.h"
#include "bee2/math/ec2.h"
#include "bee2/math/gf2.h"
#include "bee2/math/pri.h"
#include "bee2/math/ww.h"
#include "bee2/math/zz.h"
#define ec2SeemsOnA(a, ec)\
(gf2IsIn(ecX(a), (ec)->f) && gf2IsIn(ecY(a, (ec)->f->n), (ec)->f))
#define ec2SeemsOn3(a, ec)\
(ec2SeemsOnA(a, ec) && gf2IsIn(ecZ(a, (ec)->f->n), (ec)->f))
static bool_t ec2FromALD(word b[], const word a[], const ec_o* ec,
void* stack)
{
const size_t n = ec->f->n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOnA(a, ec));
ASSERT(a == b || wwIsDisjoint2(a, 2 * n, b, 3 * n));
qrCopy(ecX(b), ecX(a), ec->f);
qrCopy(ecY(b, n), ecY(a, n), ec->f);
qrSetUnity(ecZ(b, n), ec->f);
return TRUE;
}
static bool_t ec2ToALD(word b[], const word a[], const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
stack = t1 + n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(a == b || wwIsDisjoint2(a, 3 * n, b, 2 * n));
if (qrIsZero(ecZ(a, n), ec->f))
return FALSE;
qrInv(t1, ecZ(a, n), ec->f, stack);
qrMul(ecX(b), ecX(a), t1, ec->f, stack);
qrSqr(t1, t1, ec->f, stack);
qrMul(ecY(b, n), ecY(a, n), t1, ec->f, stack);
return TRUE;
}
static size_t ec2ToALD_deep(size_t n, size_t f_deep)
{
return O_OF_W(n) + f_deep;
}
static void ec2NegLD(word b[], const word a[], const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
stack = t1 + n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(wwIsSameOrDisjoint(a, b, 3 * n));
qrMul(t1, ecX(a), ecZ(a, n), ec->f, stack);
wwCopy(b, a, 3 * n);
gf2Add2(ecY(b, n), t1, ec->f);
}
static size_t ec2NegLD_deep(size_t n, size_t f_deep)
{
return O_OF_W(n) + f_deep;
}
static void ec2DblLD(word b[], const word a[], const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
word* t2 = t1 + n;
stack = t2 + n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(wwIsSameOrDisjoint(a, b, 3 * n));
if (qrIsZero(ecZ(a, n), ec->f) || qrIsZero(ecX(a), ec->f))
{
qrSetZero(ecZ(b, n), ec->f);
return;
}
qrMul(t1, ecX(a), ecZ(a, n), ec->f, stack);
qrSqr(ecZ(b, n), t1, ec->f, stack);
qrSqr(t2, ecX(a), ec->f, stack);
gf2Add(ecX(b), ecY(a, n), t2, ec->f);
qrMul(t1, t1, ecX(b), ec->f, stack);
qrSqr(ecX(b), ecX(b), ec->f, stack);
gf2Add2(ecX(b), t1, ec->f);
gf2Add2(t1, ecZ(b, n), ec->f);
qrSqr(ecY(b, n), t2, ec->f, stack);
qrMul(ecY(b, n), ecY(b, n), ecZ(b, n), ec->f, stack);
if (qrIsUnity(ec->A, ec->f))
gf2Add2(ecX(b), ecZ(b, n), ec->f);
else if (!qrIsZero(ec->A, ec->f))
{
qrMul(t2, ec->A, ecZ(b, n), ec->f, stack);
gf2Add2(ecX(b), t2, ec->f);
}
qrMul(t1, t1, ecX(b), ec->f, stack);
gf2Add2(ecY(b, n), t1, ec->f);
}
static size_t ec2DblLD_deep(size_t n, size_t f_deep)
{
return O_OF_W(2 * n) + f_deep;
}
static void ec2DblALD(word b[], const word a[], const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
stack = t1 + n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOnA(a, ec));
ASSERT(a == b || wwIsDisjoint2(a, 2 * n, b, 3 * n));
if (qrIsZero(ecX(a), ec->f))
{
qrSetZero(ecZ(b, n), ec->f);
return;
}
qrSqr(ecZ(b, n), ecX(a), ec->f, stack);
qrSqr(ecX(b), ecZ(b, n), ec->f, stack);
gf2Add2(ecX(b), ec->B, ec->f);
qrSqr(ecY(b, n), ecY(a, n), ec->f, stack);
gf2Add2(ecY(b, n), ec->B, ec->f);
if (qrIsUnity(ec->A, ec->f))
gf2Add2(ecY(b, n), ecZ(b, n), ec->f);
else if (!qrIsZero(ec->A, ec->f))
{
qrMul(t1, ec->A, ecZ(b, n), ec->f, stack);
gf2Add2(ecY(b, n), t1, ec->f);
}
qrMul(ecY(b, n), ecY(b, n), ecX(b), ec->f, stack);
qrMul(t1, ec->B, ecZ(b, n), ec->f, stack);
gf2Add2(ecY(b, n), t1, ec->f);
}
static size_t ec2DblALD_deep(size_t n, size_t f_deep)
{
return O_OF_W(n) + f_deep;
}
static void ec2AddLD(word c[], const word a[], const word b[],
const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
word* t2 = t1 + n;
word* t3 = t2 + n;
word* t4 = t3 + n;
word* t5 = t4 + n;
word* t6 = t5 + n;
stack = t6 + n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(ec2SeemsOn3(b, ec));
ASSERT(wwIsSameOrDisjoint(a, c, 3 * n));
ASSERT(wwIsSameOrDisjoint(b, c, 3 * n));
if (qrIsZero(ecZ(a, n), ec->f))
{
wwCopy(c, b, 3 * n);
return;
}
if (qrIsZero(ecZ(b, n), ec->f))
{
wwCopy(c, a, 3 * n);
return;
}
qrMul(t1, ecX(a), ecZ(b, n), ec->f, stack);
qrMul(t2, ecX(b), ecZ(a, n), ec->f, stack);
qrSqr(t3, ecZ(b, n), ec->f, stack);
qrMul(t3, t3, ecY(a, n), ec->f, stack);
qrSqr(t4, ecZ(a, n), ec->f, stack);
qrMul(t4, t4, ecY(b, n), ec->f, stack);
if (qrCmp(t1, t2, ec->f) == 0)
{
if (qrCmp(t3, t4, ec->f) == 0)
ec2DblLD(c, a, ec, stack);
else
qrSetZero(ecZ(c, n), ec->f);
return;
}
gf2Add(t5, t1, t2, ec->f);
gf2Add(t6, t3, t4, ec->f);
qrMul(t5, t5, t6, ec->f, stack);
qrSqr(ecX(c), t1, ec->f, stack);
qrSqr(ecY(c, n), t2, ec->f, stack);
gf2Add(t6, ecX(c), ecY(c, n), ec->f);
qrMul(ecZ(c, n), ecZ(a, n), ecZ(b, n), ec->f, stack);
qrMul(ecZ(c, n), t6, ecZ(c, n), ec->f, stack);
gf2Add2(t4, ecY(c, n), ec->f);
qrMul(t4, t1, t4, ec->f, stack);
gf2Add2(ecX(c), t3, ec->f);
qrMul(ecX(c), t2, ecX(c), ec->f, stack);
gf2Add2(ecX(c), t4, ec->f);
qrMul(t1, t1, t5, ec->f, stack);
qrMul(t3, t3, t6, ec->f, stack);
gf2Add2(t1, t3, ec->f);
qrMul(t1, t1, t6, ec->f, stack);
gf2Add(ecY(c, n), t5, ecZ(c, n), ec->f);
qrMul(ecY(c, n), ecY(c, n), ecX(c), ec->f, stack);
gf2Add2(ecY(c, n), t1, ec->f);
}
static size_t ec2AddLD_deep(size_t n, size_t f_deep)
{
return O_OF_W(6 * n) +
utilMax(2,
f_deep,
ec2DblLD_deep(n, f_deep));
}
static void ec2AddALD(word c[], const word a[], const word b[],
const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
word* t2 = t1 + n;
word* t3 = t2 + n;
word* t4 = t3 + n;
stack = t4 + n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(ec2SeemsOnA(b, ec));
ASSERT(wwIsSameOrDisjoint(a, c, 3 * n));
ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n));
if (qrIsZero(ecZ(a, n), ec->f))
{
qrCopy(ecX(c), ecX(b), ec->f);
qrCopy(ecY(c, n), ecY(b, n), ec->f);
qrSetUnity(ecZ(c, n), ec->f);
return;
}
qrSqr(t1, ecZ(a, n), ec->f, stack);
qrMul(t1, ecY(b, n), t1, ec->f, stack);
gf2Add2(t1, ecY(a, n), ec->f);
qrMul(t2, ecX(b), ecZ(a, n), ec->f, stack);
gf2Add2(t2, ecX(a), ec->f);
if (qrIsZero(t2, ec->f))
{
if (qrIsZero(t1, ec->f))
ec2DblALD(c, b, ec, stack);
else
qrSetZero(ecZ(c, n), ec->f);
return;
}
qrMul(t3, t2, ecZ(a, n), ec->f, stack);
qrSqr(ecZ(c, n), t3, ec->f, stack);
qrMul(t4, ecX(b), ecZ(c, n), ec->f, stack);
gf2Add(ecY(c, n), ecX(b), ecY(b, n), ec->f);
qrSqr(ecX(c), t2, ec->f, stack);
gf2Add2(ecX(c), t1, ec->f);
if (qrIsUnity(ec->A, ec->f))
gf2Add2(ecX(c), t3, ec->f);
else if (!qrIsZero(ec->A, ec->f))
{
qrMul(t2, ec->A, t3, ec->f, stack);
gf2Add2(ecX(c), t2, ec->f);
}
qrMul(ecX(c), ecX(c), t3, ec->f, stack);
qrSqr(t2, t1, ec->f, stack);
gf2Add2(ecX(c), t2, ec->f);
qrSqr(t2, ecZ(c, n), ec->f, stack);
qrMul(ecY(c, n), ecY(c, n), t2, ec->f, stack);
gf2Add2(t4, ecX(c), ec->f);
qrMul(t1, t1, t3, ec->f, stack);
gf2Add2(t1, ecZ(c, n), ec->f);
qrMul(t1, t1, t4, ec->f, stack);
gf2Add2(ecY(c, n), t1, ec->f);
}
static size_t ec2AddALD_deep(size_t n, size_t f_deep)
{
return O_OF_W(4 * n) +
utilMax(2,
f_deep,
ec2DblALD_deep(n, f_deep));
}
static void ec2SubLD(word c[], const word a[], const word b[],
const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t = (word*)stack;
stack = t + 3 * n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(ec2SeemsOn3(b, ec));
ASSERT(wwIsSameOrDisjoint(a, c, 3 * n));
ASSERT(wwIsSameOrDisjoint(b, c, 3 * n));
qrMul(ecY(t, n), ecX(b), ecZ(b, n), ec->f, stack);
gf2Add2(ecY(t, n), ecY(b, n), ec->f);
qrCopy(ecX(t), ecX(b), ec->f);
qrCopy(ecZ(t, n), ecZ(b, n), ec->f);
ec2AddLD(c, a, t, ec, stack);
}
static size_t ec2SubLD_deep(size_t n, size_t f_deep)
{
return O_OF_W(3 * n) +
utilMax(2,
f_deep,
ec2AddLD_deep(n, f_deep));
}
static void ec2SubALD(word c[], const word a[], const word b[],
const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t = (word*)stack;
stack = t + 2 * n;
ASSERT(ecIsOperable(ec) && ec->d == 3);
ASSERT(ec2SeemsOn3(a, ec));
ASSERT(ec2SeemsOnA(b, ec));
ASSERT(wwIsSameOrDisjoint(a, c, 3 * n));
ASSERT(b == c || wwIsDisjoint2(b, 2 * n, c, 3 * n));
wwCopy(t, b, 2 * n);
gf2Add2(ecY(t, n), ecX(t), ec->f);
ec2AddALD(c, a, t, ec, stack);
}
static size_t ec2SubALD_deep(size_t n, size_t f_deep)
{
return O_OF_W(2 * n) + ec2AddALD_deep(n, f_deep);
}
bool_t ec2CreateLD(ec_o* ec, const qr_o* f, const octet A[], const octet B[],
void* stack)
{
ASSERT(memIsValid(ec, sizeof(ec_o)));
ASSERT(gf2IsOperable(f));
ASSERT(memIsValid(A, f->no));
ASSERT(memIsValid(B, f->no));
memSetZero(ec, sizeof(ec_o));
ec->d = 3;
ec->f = f;
ec->A = (word*)ec->descr;
ec->B = ec->A + f->n;
if (!qrFrom(ec->A, A, ec->f, stack) || !qrFrom(ec->B, B, ec->f, stack))
return FALSE;
ec->base = ec->B + f->n;
ec->order = ec->base + 2 * f->n;
ec->froma = ec2FromALD;
ec->toa = ec2ToALD;
ec->neg = ec2NegLD;
ec->add = ec2AddLD;
ec->adda = ec2AddALD;
ec->sub = ec2SubLD;
ec->suba = ec2SubALD;
ec->dbl = ec2DblLD;
ec->dbla = ec2DblALD;
ec->deep = utilMax(8,
ec2ToALD_deep(f->n, f->deep),
ec2NegLD_deep(f->n, f->deep),
ec2AddLD_deep(f->n, f->deep),
ec2AddALD_deep(f->n, f->deep),
ec2SubLD_deep(f->n, f->deep),
ec2SubALD_deep(f->n, f->deep),
ec2DblLD_deep(f->n, f->deep),
ec2DblALD_deep(f->n, f->deep));
ec->hdr.keep = sizeof(ec_o) + O_OF_W(5 * f->n + 1);
ec->hdr.p_count = 6;
ec->hdr.o_count = 1;
return TRUE;
}
size_t ec2CreateLD_keep(size_t n)
{
return sizeof(ec_o) + O_OF_W(5 * n + 1);
}
size_t ec2CreateLD_deep(size_t n, size_t f_deep)
{
return utilMax(8,
ec2ToALD_deep(n, f_deep),
ec2NegLD_deep(n, f_deep),
ec2AddLD_deep(n, f_deep),
ec2AddALD_deep(n, f_deep),
ec2SubLD_deep(n, f_deep),
ec2SubALD_deep(n, f_deep),
ec2DblLD_deep(n, f_deep),
ec2DblALD_deep(n, f_deep));
}
bool_t ec2IsValid(const ec_o* ec, void* stack)
{
return ecIsOperable2(ec) &&
gf2IsValid(ec->f, stack) &&
ec->deep >= ec->f->deep &&
gf2IsIn(ec->A, ec->f) &&
gf2IsIn(ec->B, ec->f) &&
!qrIsZero(ec->B, ec->f);
}
size_t ec2IsValid_deep(size_t n)
{
return gf2IsValid_deep(n);
}
bool_t ec2SeemsValidGroup(const ec_o* ec, void* stack)
{
size_t n = ec->f->n;
word* t1 = (word*)stack;
word* t2 = t1 + n + 1;
word* t3 = t2 + n + 2;
stack = t3 + 2 * n;
ASSERT(ecIsOperable(ec));
if (!ecIsOperableGroup(ec) ||
!ec2IsOnA(ec->base, ec, stack))
return FALSE;
wwSetZero(t1, n + 1);
wwFlipBit(t1, gf2Deg(ec->f));
t2[n + 1] = zzMulW(t2, ec->order, n + 1, ec->cofactor);
if (zzSubW2(t2, n + 2, 1))
return FALSE;
if (wwCmp2(t2, n + 2, t1, n + 1) >= 0)
t2[n + 1] -= zzSub2(t2, t1, n + 1);
else
zzSub(t2, t1, t2, n + 1);
n = wwWordSize(t2, n + 2);
if (n > ec->f->n)
return FALSE;
zzSqr(t3, t2, n, stack);
wwFlipBit(t1, gf2Deg(ec->f));
wwFlipBit(t1, gf2Deg(ec->f) + 2);
return wwCmp2(t3, 2 * n, t3, ec->f->n + 1) <= 0;
}
size_t ec2SeemsValidGroup_deep(size_t n, size_t f_deep)
{
return O_OF_W(4 * n + 3) +
utilMax(2,
ec2IsOnA_deep(n, f_deep),
zzSqr_deep(n));
}
bool_t ec2IsSafeGroup(const ec_o* ec, size_t mov_threshold, void* stack)
{
size_t n1 = ec->f->n + 1;
word* t1 = (word*)stack;
word* t2 = t1 + n1;
stack = t2 + n1;
ASSERT(ecIsOperable(ec));
ASSERT(ecIsOperableGroup(ec));
n1 = wwWordSize(ec->order, n1);
if (!priIsPrime(ec->order, n1, stack))
return FALSE;
wwSetZero(t1, ec->f->n + 1);
wwFlipBit(t1, gf2Deg(ec->f));
if (wwCmp2(t1, ec->f->n + 1, ec->order, n1) == 0)
return FALSE;
if (mov_threshold)
{
zzMod(t1, t1, ec->f->n + 1, ec->order, n1, stack);
wwCopy(t2, t1, n1);
if (wwCmpW(t2, n1, 1) == 0)
return FALSE;
while (--mov_threshold)
{
zzMulMod(t2, t2, t1, ec->order, n1, stack);
if (wwCmpW(t2, n1, 1) == 0)
return FALSE;
}
}
return TRUE;
}
size_t ec2IsSafeGroup_deep(size_t n)
{
const size_t n1 = n + 1;
return O_OF_W(2 * n1) +
utilMax(3,
priIsPrime_deep(n1),
zzMod_deep(n + 1, n1),
zzMulMod_deep(n1));
}
bool_t ec2IsOnA(const word a[], const ec_o* ec, void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
word* t2 = t1 + n;
stack = t2 + n;
ASSERT(ecIsOperable(ec));
if (!ec2SeemsOnA(a, ec))
return FALSE;
qrSqr(t1, ecX(a), ec->f, stack);
gf2Add(t2, ecX(a), ec->A, ec->f);
qrMul(t1, t1, t2, ec->f, stack);
gf2Add2(t1, ec->B, ec->f);
gf2Add(t2, ecX(a), ecY(a, n), ec->f);
qrMul(t2, t2, ecY(a, n), ec->f, stack);
return qrCmp(t1, t2, ec->f) == 0;
}
size_t ec2IsOnA_deep(size_t n, size_t f_deep)
{
return O_OF_W(2 * n) + f_deep;
}
void ec2NegA(word b[], const word a[], const ec_o* ec)
{
const size_t n = ec->f->n;
ASSERT(ecIsOperable(ec));
ASSERT(ec2SeemsOnA(a, ec));
ASSERT(wwIsSameOrDisjoint(a, b, 3 * n));
qrCopy(ecX(b), ecX(a), ec->f);
gf2Add(ecY(b, n), ecX(a), ecY(a, n), ec->f);
}
bool_t ec2AddAA(word c[], const word a[], const word b[], const ec_o* ec,
void* stack)
{
const size_t n = ec->f->n;
word* t1 = (word*)stack;
word* t2 = t1 + n;
word* t3 = t2 + n;
stack = t3 + n;
ASSERT(ecIsOperable(ec));
ASSERT(ec2SeemsOnA(a, ec));
ASSERT(ec2SeemsOnA(b, ec));
ASSERT(wwIsDisjoint(a, c, 2 * n));
if (qrCmp(ecX(a), ecX(b), ec->f) == 0)
{
if (qrCmp(ecY(a, n), ecY(b, n), ec->f) != 0)
return FALSE;
if (qrIsZero(ecX(a), ec->f))
return FALSE;
qrDiv(t1, ecY(a, n), ecX(a), ec->f, stack);
gf2Add2(t1, ecX(a), ec->f);
qrCopy(t2, ecX(a), ec->f);
qrSqr(ecX(c), t1, ec->f, stack);
gf2Add2(ecX(c), t1, ec->f);
gf2Add2(ecX(c), ec->A, ec->f);
gf2Add2(t2, ecX(c), ec->f);
qrMul(t2, t1, t2, ec->f, stack);
gf2Add(ecY(c, n), ecY(a, n), t2, ec->f);
gf2Add2(ecY(c, n), ecX(c), ec->f);
return TRUE;
}
qrCopy(t1, ecX(a), ec->f);
gf2Add(ecX(c), ecX(a), ecX(b), ec->f);
gf2Add(t2, ecY(a, n), ecY(b, n), ec->f);
qrDiv(t2, t2, ecX(c), ec->f, stack);
qrSqr(t3, t2, ec->f, stack);
gf2Add2(ecX(c), t2, ec->f);
gf2Add2(ecX(c), t3, ec->f);
gf2Add2(ecX(c), ec->A, ec->f);
gf2Add2(t1, ecX(c), ec->f);
qrMul(t1, t1, t2, ec->f, stack);
gf2Add(ecY(c, n), ecY(a, n), ecX(c), ec->f);
gf2Add2(ecY(c, n), t1, ec->f);
return TRUE;
}
size_t ec2AddAA_deep(size_t n, size_t f_deep)
{
return O_OF_W(3 * n) + f_deep;
}
bool_t ec2SubAA(word c[], const word a[], const word b[], const ec_o* ec,
void* stack)
{
const size_t n = ec->f->n;
word* t = (word*)stack;
stack = t + 2 * n;
ec2NegA(t, b, ec);
return ec2AddAA(c, a, t, ec, stack);
}
size_t ec2SubAA_deep(size_t n, size_t f_deep)
{
return O_OF_W(2 * n) + ec2AddAA_deep(n, f_deep);
}