#include "bee2/core/blob.h"
#include "bee2/core/err.h"
#include "bee2/core/mem.h"
#include "bee2/core/obj.h"
#include "bee2/core/oid.h"
#include "bee2/core/util.h"
#include "bee2/crypto/belt.h"
#include "bee2/crypto/bign.h"
#include "bee2/math/ec.h"
#include "bee2/math/qr.h"
#include "bee2/math/ww.h"
#include "bee2/math/zz.h"
#include "bign_lcl.h"
static size_t bignSign_deep(size_t n, size_t f_deep, size_t ec_d,
size_t ec_deep)
{
return O_OF_W(4 * n) +
utilMax(4,
beltHash_keep(),
ecMulA_deep(n, ec_d, ec_deep, n),
zzMul_deep(n / 2, n),
zzMod_deep(n + n / 2 + 1, n));
}
err_t bignSign(octet sig[], const bign_params* params, const octet oid_der[],
size_t oid_len, const octet hash[], const octet privkey[], gen_i rng,
void* rng_state)
{
err_t code;
size_t no, n;
void* state;
ec_o* ec;
word* d;
word* k;
word* R;
word* s0;
word* s1;
octet* stack;
if (!memIsValid(params, sizeof(bign_params)))
return ERR_BAD_INPUT;
if (!bignIsOperable(params))
return ERR_BAD_PARAMS;
if (oid_len == SIZE_MAX || oidFromDER(0, oid_der, oid_len) == SIZE_MAX)
return ERR_BAD_OID;
if (rng == 0)
return ERR_BAD_RNG;
state = blobCreate(bignStart_keep(params->l, bignSign_deep));
if (state == 0)
return ERR_OUTOFMEMORY;
code = bignStart(state, params);
ERR_CALL_HANDLE(code, blobClose(state));
ec = (ec_o*)state;
no = ec->f->no;
n = ec->f->n;
ASSERT(n % 2 == 0);
if (!memIsValid(hash, no) ||
!memIsValid(privkey, no) ||
!memIsValid(sig, no + no / 2) ||
!memIsDisjoint2(hash, no, sig, no + no / 2))
{
blobClose(state);
return ERR_BAD_INPUT;
}
d = s1 = objEnd(ec, word);
k = d + n;
R = k + n;
s0 = R + n + n / 2;
stack = (octet*)(R + 2 * n);
wwFrom(d, privkey, no);
if (wwIsZero(d, n) || wwCmp(d, ec->order, n) >= 0)
{
blobClose(state);
return ERR_BAD_PRIVKEY;
}
if (!zzRandNZMod(k, ec->order, n, rng, rng_state))
{
blobClose(state);
return ERR_BAD_RNG;
}
if (!ecMulA(R, ec->base, ec, k, n, stack))
{
blobClose(state);
return ERR_BAD_PARAMS;
}
qrTo((octet*)R, ecX(R), ec->f, stack);
beltHashStart(stack);
beltHashStepH(oid_der, oid_len, stack);
beltHashStepH(R, no, stack);
beltHashStepH(hash, no, stack);
beltHashStepG2(sig, no / 2, stack);
wwFrom(s0, sig, no / 2);
zzMul(R, s0, n / 2, d, n, stack);
R[n + n / 2] = zzAdd(R + n / 2, R + n / 2, d, n);
zzMod(s1, R, n + n / 2 + 1, ec->order, n, stack);
zzSubMod(s1, k, s1, ec->order, n);
wwFrom(k, hash, no);
zzSubMod(s1, s1, k, ec->order, n);
wwTo(sig + no / 2, no, s1);
blobClose(state);
return ERR_OK;
}
static size_t bignSign2_deep(size_t n, size_t f_deep, size_t ec_d,
size_t ec_deep)
{
return O_OF_W(4 * n) + beltHash_keep() +
utilMax(6,
beltHash_keep(),
32,
beltWBL_keep(),
ecMulA_deep(n, ec_d, ec_deep, n),
zzMul_deep(n / 2, n),
zzMod_deep(n + n / 2 + 1, n));
}
err_t bignSign2(octet sig[], const bign_params* params, const octet oid_der[],
size_t oid_len, const octet hash[], const octet privkey[], const void* t,
size_t t_len)
{
err_t code;
size_t no, n;
void* state;
ec_o* ec;
word* d;
word* k;
word* R;
word* s0;
word* s1;
octet* hash_state;
octet* stack;
if (!memIsValid(params, sizeof(bign_params)))
return ERR_BAD_INPUT;
if (!bignIsOperable(params))
return ERR_BAD_PARAMS;
if (oid_len == SIZE_MAX || oidFromDER(0, oid_der, oid_len) == SIZE_MAX)
return ERR_BAD_OID;
if (!memIsNullOrValid(t, t_len))
return ERR_BAD_INPUT;
state = blobCreate(bignStart_keep(params->l, bignSign2_deep));
if (state == 0)
return ERR_OUTOFMEMORY;
code = bignStart(state, params);
ERR_CALL_HANDLE(code, blobClose(state));
ec = (ec_o*)state;
no = ec->f->no;
n = ec->f->n;
ASSERT(n % 2 == 0);
if (!memIsValid(hash, no) ||
!memIsValid(privkey, no) ||
!memIsValid(sig, no + no / 2) ||
!memIsDisjoint2(hash, no, sig, no + no / 2))
{
blobClose(state);
return ERR_BAD_INPUT;
}
d = s1 = objEnd(ec, word);
k = d + n;
R = k + n;
s0 = R + n + n / 2;
hash_state = (octet*)(R + 2 * n);
stack = hash_state + beltHash_keep();
wwFrom(d, privkey, no);
if (wwIsZero(d, n) || wwCmp(d, ec->order, n) >= 0)
{
blobClose(state);
return ERR_BAD_PRIVKEY;
}
beltHashStart(hash_state);
beltHashStepH(oid_der, oid_len, hash_state);
{
memCopy(stack, hash_state, beltHash_keep());
beltHashStepH(privkey, no, stack);
if (t != 0)
beltHashStepH(t, t_len, stack);
beltHashStepG(stack, stack);
beltWBLStart(stack, stack, 32);
memCopy(k, hash, no);
while (1)
{
beltWBLStepE(k, no, stack);
wwFrom(k, k, no);
if (!wwIsZero(k, n) && wwCmp(k, ec->order, n) < 0)
break;
wwTo(k, no, k);
}
}
if (!ecMulA(R, ec->base, ec, k, n, stack))
{
blobClose(state);
return ERR_BAD_PARAMS;
}
qrTo((octet*)R, ecX(R), ec->f, stack);
beltHashStepH(R, no, hash_state);
beltHashStepH(hash, no, hash_state);
beltHashStepG2(sig, no / 2, hash_state);
wwFrom(s0, sig, no / 2);
zzMul(R, s0, n / 2, d, n, stack);
R[n + n / 2] = zzAdd(R + n / 2, R + n / 2, d, n);
zzMod(s1, R, n + n / 2 + 1, ec->order, n, stack);
zzSubMod(s1, k, s1, ec->order, n);
wwFrom(k, hash, no);
zzSubMod(s1, s1, k, ec->order, n);
wwTo(sig + no / 2, no, s1);
blobClose(state);
return ERR_OK;
}
static size_t bignVerify_deep(size_t n, size_t f_deep, size_t ec_d,
size_t ec_deep)
{
return O_OF_W(4 * n) +
utilMax(2,
beltHash_keep(),
ecAddMulA_deep(n, ec_d, ec_deep, 2, n, n / 2 + 1));
}
err_t bignVerify(const bign_params* params, const octet oid_der[],
size_t oid_len, const octet hash[], const octet sig[], const octet pubkey[])
{
err_t code;
size_t no, n;
void* state;
ec_o* ec;
word* Q;
word* R;
word* H;
word* s0;
word* s1;
octet* stack;
if (!memIsValid(params, sizeof(bign_params)))
return ERR_BAD_INPUT;
if (!bignIsOperable(params))
return ERR_BAD_PARAMS;
if (oid_len == SIZE_MAX || oidFromDER(0, oid_der, oid_len) == SIZE_MAX)
return ERR_BAD_OID;
state = blobCreate(bignStart_keep(params->l, bignVerify_deep));
if (state == 0)
return ERR_OUTOFMEMORY;
code = bignStart(state, params);
ERR_CALL_HANDLE(code, blobClose(state));
ec = (ec_o*)state;
no = ec->f->no;
n = ec->f->n;
ASSERT(n % 2 == 0);
if (!memIsValid(hash, no) ||
!memIsValid(sig, no + no / 2) ||
!memIsValid(pubkey, 2 * no))
{
blobClose(state);
return ERR_BAD_INPUT;
}
Q = R = objEnd(ec, word);
H = s0 = Q + 2 * n;
s1 = H + n;
stack = (octet*)(s1 + n);
if (!qrFrom(ecX(Q), pubkey, ec->f, stack) ||
!qrFrom(ecY(Q, n), pubkey + no, ec->f, stack))
{
blobClose(state);
return ERR_BAD_PUBKEY;
}
wwFrom(s1, sig + no / 2, no);
if (wwCmp(s1, ec->order, n) >= 0)
{
blobClose(state);
return ERR_BAD_SIG;
}
wwFrom(H, hash, no);
if (wwCmp(H, ec->order, n) >= 0)
{
zzSub2(H, ec->order, n);
ASSERT(wwCmp(H, ec->order, n) < 0);
}
zzAddMod(s1, s1, H, ec->order, n);
wwFrom(s0, sig, no / 2);
s0[n / 2] = 1;
if (!ecAddMulA(R, ec, stack, 2, ec->base, s1, n, Q, s0, n / 2 + 1))
{
blobClose(state);
return ERR_BAD_SIG;
}
qrTo((octet*)R, ecX(R), ec->f, stack);
beltHashStart(stack);
beltHashStepH(oid_der, oid_len, stack);
beltHashStepH(R, no, stack);
beltHashStepH(hash, no, stack);
code = beltHashStepV2(sig, no / 2, stack) ? ERR_OK : ERR_BAD_SIG;
blobClose(state);
return code;
}