#include <stdlib.h>
#include <bee2/core/blob.h>
#include <bee2/core/dec.h>
#include <bee2/core/err.h>
#include <bee2/core/hex.h>
#include <bee2/core/mem.h>
#include <bee2/core/rng.h>
#include <bee2/core/str.h>
#include <bee2/core/util.h>
#include <bee2/crypto/belt.h>
#include <bee2/crypto/bels.h>
#include <bee2/crypto/bpki.h>
#include "bee2/cmd.h"
cmd_pwd_t cmdPwdCreate(size_t size)
{
return (cmd_pwd_t)blobCreate(size + 1);
}
bool_t cmdPwdIsValid(const cmd_pwd_t pwd)
{
return strIsValid(pwd) && blobIsValid(pwd) &&
pwd[blobSize(pwd) - 1] == '\0';
}
void cmdPwdClose(cmd_pwd_t pwd)
{
ASSERT(pwd == 0 || cmdPwdIsValid(pwd));
blobClose(pwd);
}
static err_t cmdPwdGenPass(cmd_pwd_t* pwd, const char* str)
{
return ERR_NOT_IMPLEMENTED;
}
static err_t cmdPwdReadPass(cmd_pwd_t* pwd, const char* str)
{
ASSERT(memIsValid(pwd, sizeof(cmd_pwd_t)));
ASSERT(strIsValid(str));
if (!(*pwd = cmdPwdCreate(strLen(str))))
return ERR_OUTOFMEMORY;
strCopy(*pwd, str);
return ERR_OK;
}
static const char* cmdEnvGet(const char* name)
{
const char* val;
val = getenv(name);
return strIsValid(val) ? val : 0;
}
static err_t cmdPwdGenEnv(cmd_pwd_t* pwd, const char* str)
{
return ERR_NOT_IMPLEMENTED;
}
static err_t cmdPwdReadEnv(cmd_pwd_t* pwd, const char* str)
{
const char* val;
ASSERT(memIsValid(pwd, sizeof(cmd_pwd_t)));
ASSERT(strIsValid(str));
if (!(val = cmdEnvGet(str)))
return ERR_BAD_ENV;
if (!(*pwd = cmdPwdCreate(strLen(val))))
return ERR_OUTOFMEMORY;
strCopy(*pwd, val);
return ERR_OK;
}
static err_t cmdPwdGenShare_internal(cmd_pwd_t* pwd, size_t scount,
size_t threshold, size_t len, bool_t crc, char* shares[],
const cmd_pwd_t spwd)
{
err_t code;
const size_t iter = 10000;
size_t epki_len;
void* stack;
octet* pwd_bin;
octet* state;
octet* share;
octet* salt;
octet* epki;
ASSERT(memIsValid(pwd, sizeof(cmd_pwd_t)));
ASSERT(cmdPwdIsValid(spwd));
ASSERT(2 <= scount && scount <= 16);
ASSERT(2 <= threshold && threshold <= scount);
ASSERT(len % 8 == 0 && len <= 32);
ASSERT(!crc || len != 16);
*pwd = 0;
if (len == 0)
len = 32;
code = bpkiShareWrap(0, &epki_len, 0, len + 1, 0, 0, 0, iter);
ERR_CALL_CHECK(code);
code = cmdRngStart(TRUE);
ERR_CALL_CHECK(code);
code = cmdBlobCreate(stack, len +
utilMax(2,
beltMAC_keep(),
scount * (len + 1) + epki_len + 8));
ERR_CALL_CHECK(code);
pwd_bin = (octet*)stack;
state = share = pwd_bin + len;
salt = share + scount * (len + 1);
epki = salt + 8;
if (crc)
{
rngStepR(pwd_bin, len - 8, 0);
beltMACStart(state, pwd_bin, len - 8);
beltMACStepA(pwd_bin, len - 8, state);
beltMACStepG(pwd_bin + len - 8, state);
}
else
rngStepR(pwd_bin, len, 0);
code = belsShare2(share, scount, threshold, len, pwd_bin, rngStepR, 0);
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
rngRekey();
for (; scount--; share += (len + 1), ++shares)
{
rngStepR(salt, 8, 0);
code = bpkiShareWrap(epki, 0, share, len + 1, (const octet*)spwd,
cmdPwdLen(spwd), salt, iter);
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
code = cmdFileWrite(*shares, epki, epki_len);
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
}
*pwd = cmdPwdCreate(2 * len);
code = *pwd ? ERR_OK : ERR_OUTOFMEMORY;
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
hexFrom(*pwd, pwd_bin, len);
cmdBlobClose(stack);
return code;
}
static err_t cmdPwdReadShare_internal(cmd_pwd_t* pwd, size_t scount,
size_t len, bool_t crc, char* shares[], const cmd_pwd_t spwd)
{
err_t code;
size_t epki_len;
size_t epki_len_min;
size_t epki_len_max;
void* stack;
octet* share;
octet* state;
octet* epki;
octet* pwd_bin;
size_t pos;
ASSERT(memIsValid(pwd, sizeof(cmd_pwd_t)));
ASSERT(cmdPwdIsValid(spwd));
ASSERT(2 <= scount && scount <= 16);
ASSERT(len % 8 == 0 && len <= 32);
ASSERT(!crc || len != 16);
*pwd = 0;
if (len == 0)
{
if ((epki_len = cmdFileSize(shares[0])) == SIZE_MAX)
return ERR_FILE_READ;
for (len = 16; len <= 32; len += 8)
{
code = bpkiShareWrap(0, &epki_len_min, 0, len + 1, 0, 0, 0, 10000);
ERR_CALL_CHECK(code);
code = bpkiShareWrap(0, &epki_len_max, 0, len + 1, 0, 0, 0,
SIZE_MAX);
ERR_CALL_CHECK(code);
if (epki_len_min <= epki_len && epki_len <= epki_len_max)
break;
}
if (len > 32)
return ERR_BAD_FORMAT;
}
else
{
code = bpkiShareWrap(0, &epki_len_min, 0, len + 1, 0, 0, 0, 10000);
ERR_CALL_CHECK(code);
code = bpkiShareWrap(0, &epki_len_max, 0, len + 1, 0, 0, 0, SIZE_MAX);
ERR_CALL_CHECK(code);
}
code = cmdBlobCreate(stack, scount * (len + 1) + epki_len_max + 1 + len);
ERR_CALL_HANDLE(code, cmdPwdClose(*pwd));
share = state = (octet*)stack;
epki = share + scount * (len + 1);
pwd_bin = epki + epki_len_max + 1;
for (pos = 0; pos < scount; ++pos, ++shares)
{
size_t share_len;
code = cmdFileReadAll(0, &epki_len, *shares);
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
code = (epki_len_min <= epki_len && epki_len <= epki_len_max) ?
ERR_OK : ERR_BAD_FORMAT;
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
code = cmdFileReadAll(epki, &epki_len, *shares);
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
code = bpkiShareUnwrap(share + pos * (len + 1), &share_len,
epki, epki_len, (const octet*)spwd, cmdPwdLen(spwd));
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
code = (share_len == len + 1) ? ERR_OK : ERR_BAD_FORMAT;
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
}
code = belsRecover2(pwd_bin, scount, len, share);
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
if (crc)
{
beltMACStart(state, pwd_bin, len - 8);
beltMACStepA(pwd_bin, len - 8, state);
if (!beltMACStepV(pwd_bin + len - 8, state))
code = ERR_BAD_CRC;
}
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
*pwd = cmdPwdCreate(2 * len);
code = *pwd ? ERR_OK : ERR_OUTOFMEMORY;
ERR_CALL_HANDLE(code, cmdBlobClose(stack));
hexFrom(*pwd, pwd_bin, len);
cmdBlobClose(stack);
return code;
}
static err_t cmdPwdGenShare(cmd_pwd_t* pwd, const char* str)
{
err_t code;
int argc;
char** argv = 0;
size_t offset = 0;
size_t threshold = 0;
size_t len = 0;
bool_t crc = FALSE;
cmd_pwd_t spwd = 0;
code = cmdArgCreate(&argc, &argv, str);
ERR_CALL_CHECK(code);
while (argc && strStartsWith(argv[offset], "-"))
{
if (strStartsWith(argv[offset], "-t"))
{
char* dec = argv[offset] + strLen("-t");
if (threshold)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
if (!decIsValid(dec) || decCLZ(dec) || strLen(dec) > 2 ||
(threshold = (size_t)decToU32(dec)) < 2 || threshold > 16)
{
code = ERR_CMD_PARAMS;
goto final;
}
++offset, --argc;
}
else if (strStartsWith(argv[offset], "-l"))
{
char* dec = argv[offset] + strLen("-l");
if (len)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
if (!decIsValid(dec) || decCLZ(dec) || strLen(dec) != 3 ||
(len = (size_t)decToU32(dec)) % 64 || len < 128 || len > 256)
{
code = ERR_CMD_PARAMS;
goto final;
}
len /= 8;
if (len == 16 && crc)
{
code = ERR_CMD_PARAMS;
goto final;
}
++offset, --argc;
}
else if (strStartsWith(argv[offset], "-crc"))
{
if (crc)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
if (len == 16)
{
code = ERR_CMD_PARAMS;
goto final;
}
crc = TRUE, ++offset, --argc;
}
else if (strEq(argv[offset], "-pass"))
{
if (spwd)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
++offset, --argc;
code = cmdPwdRead(&spwd, argv[offset]);
ERR_CALL_HANDLE(code, cmdArgClose(argv));
ASSERT(cmdPwdIsValid(spwd));
++offset, --argc;
}
else
{
code = ERR_CMD_PARAMS;
goto final;
}
}
if (!spwd)
{
code = ERR_CMD_PARAMS;
goto final;
}
if (!threshold)
threshold = 2;
if ((size_t)argc < threshold)
{
code = ERR_CMD_PARAMS;
goto final;
}
if ((code = cmdFileValNotExist(argc, argv + offset)) != ERR_OK)
goto final;
code = cmdPwdGenShare_internal(pwd, (size_t)argc, threshold, len, crc,
argv + offset, spwd);
final:
cmdPwdClose(spwd);
cmdArgClose(argv);
return code;
}
static err_t cmdPwdReadShare(cmd_pwd_t* pwd, const char* str)
{
err_t code;
int argc;
char** argv = 0;
size_t offset = 0;
size_t threshold = 0;
size_t len = 0;
bool_t crc = FALSE;
cmd_pwd_t spwd = 0;
code = cmdArgCreate(&argc, &argv, str);
ERR_CALL_CHECK(code);
while (argc && strStartsWith(argv[offset], "-"))
{
if (strStartsWith(argv[offset], "-t"))
{
char* dec = argv[offset] + strLen("-t");
if (threshold)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
if (!decIsValid(dec) || decCLZ(dec) || strLen(dec) > 2 ||
(threshold = (size_t)decToU32(dec)) < 2 || threshold > 16)
{
code = ERR_CMD_PARAMS;
goto final;
}
++offset, --argc;
}
else if (strStartsWith(argv[offset], "-l"))
{
char* dec = argv[offset] + strLen("-l");
if (len)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
if (!decIsValid(dec) || decCLZ(dec) || strLen(dec) != 3 ||
(len = (size_t)decToU32(dec)) % 64 || len < 128 || len > 256)
{
code = ERR_CMD_PARAMS;
goto final;
}
len /= 8;
if (len == 16 && crc)
{
code = ERR_CMD_PARAMS;
goto final;
}
++offset, --argc;
}
else if (strStartsWith(argv[offset], "-crc"))
{
if (crc)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
if (len == 16)
{
code = ERR_CMD_PARAMS;
goto final;
}
crc = TRUE, ++offset, --argc;
}
else if (strEq(argv[offset], "-pass"))
{
if (spwd)
{
code = ERR_CMD_DUPLICATE;
goto final;
}
++offset, --argc;
code = cmdPwdRead(&spwd, argv[offset]);
ERR_CALL_HANDLE(code, cmdArgClose(argv));
ASSERT(cmdPwdIsValid(spwd));
++offset, --argc;
}
else
{
code = ERR_CMD_PARAMS;
goto final;
}
}
if (!spwd)
{
code = ERR_CMD_PARAMS;
goto final;
}
if (!threshold)
threshold = 2;
if ((size_t)argc < threshold)
{
code = ERR_CMD_PARAMS;
goto final;
}
if ((code = cmdFileValExist(argc, argv + offset)) != ERR_OK)
goto final;
code = cmdPwdReadShare_internal(pwd, (size_t)argc, len, crc,
argv + offset, spwd);
final:
cmdPwdClose(spwd);
cmdArgClose(argv);
return code;
}
err_t cmdPwdGen(cmd_pwd_t* pwd, const char* schema)
{
if (strStartsWith(schema, "pass:"))
return cmdPwdGenPass(pwd, schema + strLen("pass:"));
else if (strStartsWith(schema, "env:"))
return cmdPwdGenEnv(pwd, schema + strLen("env:"));
else if (strStartsWith(schema, "share:"))
return cmdPwdGenShare(pwd, schema + strLen("share:"));
return ERR_CMD_PARAMS;
}
err_t cmdPwdRead(cmd_pwd_t* pwd, const char* schema)
{
if (strStartsWith(schema, "pass:"))
return cmdPwdReadPass(pwd, schema + strLen("pass:"));
else if (strStartsWith(schema, "env:"))
return cmdPwdReadEnv(pwd, schema + strLen("env:"));
else if (strStartsWith(schema, "share:"))
return cmdPwdReadShare(pwd, schema + strLen("share:"));
return ERR_CMD_PARAMS;
}