#include "bee2/core/blob.h"
#include "bee2/core/err.h"
#include "bee2/core/mem.h"
#include "bee2/core/obj.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/zm.h"
#include "bee2/math/zz.h"
#include "bign_lcl.h"
static size_t bignKeyWrap_deep(size_t n, size_t f_deep, size_t ec_d,
size_t ec_deep)
{
return O_OF_W(3 * n) + 32 +
utilMax(2,
ecMulA_deep(n, ec_d, ec_deep, n),
beltKWP_keep());
}
err_t bignKeyWrap(octet token[], const bign_params* params, const octet key[],
size_t len, const octet header[16], const octet pubkey[],
gen_i rng, void* rng_state)
{
err_t code;
size_t no, n;
void* state;
ec_o* ec;
word* k;
word* R;
octet* theta;
octet* stack;
if (!memIsValid(params, sizeof(bign_params)))
return ERR_BAD_INPUT;
if (!bignIsOperable(params))
return ERR_BAD_PARAMS;
if (rng == 0)
return ERR_BAD_RNG;
if (len < 16 ||
!memIsValid(key, len) ||
!memIsNullOrValid(header, 16))
return ERR_BAD_INPUT;
state = blobCreate(bignStart_keep(params->l, bignKeyWrap_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;
if (!memIsValid(pubkey, 2 * no) ||
!memIsValid(token, 16 + no + len))
{
blobClose(state);
return ERR_BAD_INPUT;
}
k = objEnd(ec, word);
R = k + n;
theta = (octet*)(R + 2 * n);
stack = theta + 32;
if (!zzRandNZMod(k, ec->order, n, rng, rng_state))
{
blobClose(state);
return ERR_BAD_RNG;
}
if (!qrFrom(ecX(R), pubkey, ec->f, stack) ||
!qrFrom(ecY(R, n), pubkey + no, ec->f, stack))
{
blobClose(state);
return ERR_BAD_PUBKEY;
}
if (!ecMulA(R, R, ec, k, n, stack))
{
blobClose(state);
return ERR_BAD_PARAMS;
}
qrTo(theta, ecX(R), ec->f, stack);
if (!ecMulA(R, ec->base, ec, k, n, stack))
{
blobClose(state);
return ERR_BAD_PARAMS;
}
qrTo((octet*)R, ecX(R), ec->f, stack);
if (header)
memCopy(R + n, header, 16);
else
memSetZero(R + n, 16);
memMove(token + no, key, len);
memCopy(token + no + len, R + n, 16);
beltKWPStart(stack, theta, 32);
beltKWPStepE(token + no, len + 16, stack);
memCopy(token, R, no);
blobClose(state);
return ERR_OK;
}
static size_t bignKeyUnwrap_deep(size_t n, size_t f_deep, size_t ec_d,
size_t ec_deep)
{
return MAX2(O_OF_W(5 * n), 32 + 16) +
utilMax(3,
beltKWP_keep(),
qrPower_deep(n, n, f_deep),
ecMulA_deep(n, ec_d, ec_deep, n));
}
err_t bignKeyUnwrap(octet key[], const bign_params* params, const octet token[],
size_t len, const octet header[16], const octet privkey[])
{
err_t code;
size_t no, n;
void* state;
ec_o* ec;
word* d;
word* R;
word* t1;
word* t2;
octet* theta;
octet* header2;
void* stack;
if (!memIsValid(params, sizeof(bign_params)))
return ERR_BAD_INPUT;
if (!bignIsOperable(params))
return ERR_BAD_PARAMS;
if (!memIsValid(token, len) ||
!memIsNullOrValid(header, 16))
return ERR_BAD_INPUT;
state = blobCreate(bignStart_keep(params->l, bignKeyUnwrap_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;
if (len < 32 + no)
{
blobClose(state);
return ERR_BAD_KEYTOKEN;
}
if (!memIsValid(privkey, no) ||
!memIsValid(key, len - 16 - no))
{
blobClose(state);
return ERR_BAD_INPUT;
}
d = objEnd(ec, word);
R = d + n;
t1 = R + 2 * n;
t2 = t1 + n;
theta = (octet*)d;
header2 = theta + 32;
if (5 * no >= 48)
stack = t2 + n;
else
stack = header2 + 16;
wwFrom(d, privkey, no);
if (wwIsZero(d, n) || wwCmp(d, ec->order, n) >= 0)
{
blobClose(state);
return ERR_BAD_PRIVKEY;
}
if (!qrFrom(R, token, ec->f, stack))
{
blobClose(state);
return ERR_BAD_KEYTOKEN;
}
qrSqr(t1, R, ec->f, stack);
zmAdd(t1, t1, ec->A, ec->f);
qrMul(t1, t1, R, ec->f, stack);
zmAdd(t1, t1, ec->B, ec->f);
wwCopy(R + n, ec->f->mod, n);
zzAddW2(R + n, n, 1);
wwShLo(R + n, n, 2);
qrPower(R + n, t1, R + n, n, ec->f, stack);
qrSqr(t2, R + n, ec->f, stack);
if (!wwEq(t1, t2, n))
{
blobClose(state);
return ERR_BAD_KEYTOKEN;
}
if (!ecMulA(R, R, ec, d, n, stack))
{
blobClose(state);
return ERR_BAD_PARAMS;
}
qrTo(theta, ecX(R), ec->f, stack);
memCopy(key, token + no, len - no - 16);
memCopy(header2, token + len - 16, 16);
beltKWPStart(stack, theta, 32);
beltKWPStepD2(key, header2, len - no, stack);
if (header && !memEq(header, header2, 16) ||
header == 0 && !memIsZero(header2, 16))
{
memSetZero(key, len - no - 16);
code = ERR_BAD_KEYTOKEN;
}
blobClose(state);
return code;
}