cmd_pwd.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*
  2. *******************************************************************************
  3. \file cmd_pwd.c
  4. \brief Command-line interface to Bee2: password management
  5. \project bee2/cmd
  6. \created 2022.06.13
  7. \version 2023.06.05
  8. \copyright The Bee2 authors
  9. \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
  10. *******************************************************************************
  11. */
  12. #include "../cmd.h"
  13. #include <bee2/core/blob.h>
  14. #include <bee2/core/dec.h>
  15. #include <bee2/core/err.h>
  16. #include <bee2/core/hex.h>
  17. #include <bee2/core/mem.h>
  18. #include <bee2/core/rng.h>
  19. #include <bee2/core/str.h>
  20. #include <bee2/core/util.h>
  21. #include <bee2/crypto/belt.h>
  22. #include <bee2/crypto/bels.h>
  23. #include <bee2/crypto/bpki.h>
  24. #include <bee2/crypto/brng.h>
  25. #include <stdio.h>
  26. /*
  27. *******************************************************************************
  28. Управление паролями: базовые функции
  29. *******************************************************************************
  30. */
  31. cmd_pwd_t cmdPwdCreate(size_t size)
  32. {
  33. return (cmd_pwd_t)blobCreate(size + 1);
  34. }
  35. bool_t cmdPwdIsValid(const cmd_pwd_t pwd)
  36. {
  37. return pwd != 0 && blobIsValid(pwd) && strIsValid(pwd) &&
  38. pwd[blobSize(pwd) - 1] == '\0';
  39. }
  40. void cmdPwdClose(cmd_pwd_t pwd)
  41. {
  42. ASSERT(pwd == 0 || cmdPwdIsValid(pwd));
  43. blobClose(pwd);
  44. }
  45. /*
  46. *******************************************************************************
  47. Управление паролями: самотестирование
  48. *******************************************************************************
  49. */
  50. err_t pwdSelfTest()
  51. {
  52. const char pwd[] = "B194BAC80A08F53B";
  53. octet stack[1024];
  54. octet buf[5 * (32 + 1)];
  55. octet buf1[32];
  56. // bels-share: разделение и сборка
  57. if (belsShare3(buf, 5, 3, 32, beltH()) != ERR_OK)
  58. return ERR_SELFTEST;
  59. if (belsRecover2(buf1, 1, 32, buf) != ERR_OK ||
  60. memEq(buf1, beltH(), 32))
  61. return ERR_SELFTEST;
  62. if (belsRecover2(buf1, 2, 32, buf) != ERR_OK ||
  63. memEq(buf1, beltH(), 32))
  64. return ERR_SELFTEST;
  65. if (belsRecover2(buf1, 3, 32, buf) != ERR_OK ||
  66. !memEq(buf1, beltH(), 32))
  67. return ERR_SELFTEST;
  68. // brng-ctr: тест Б.2
  69. ASSERT(sizeof(stack) >= brngCTR_keep());
  70. memCopy(buf, beltH(), 96);
  71. brngCTRStart(stack, beltH() + 128, beltH() + 128 + 64);
  72. brngCTRStepR(buf, 96, stack);
  73. if (!hexEq(buf,
  74. "1F66B5B84B7339674533F0329C74F218"
  75. "34281FED0732429E0C79235FC273E269"
  76. "4C0E74B2CD5811AD21F23DE7E0FA742C"
  77. "3ED6EC483C461CE15C33A77AA308B7D2"
  78. "0F51D91347617C20BD4AB07AEF4F26A1"
  79. "AD1362A8F9A3D42FBE1B8E6F1C88AAD5"))
  80. return ERR_SELFTEST;
  81. // pbkdf2 тест E.5
  82. beltPBKDF2(buf, (const octet*)"B194BAC80A08F53B", strLen(pwd), 10000,
  83. beltH() + 128 + 64, 8);
  84. if (!hexEq(buf,
  85. "3D331BBBB1FBBB40E4BF22F6CB9A689E"
  86. "F13A77DC09ECF93291BFE42439A72E7D"))
  87. return FALSE;
  88. // belt-kwp: тест A.21
  89. ASSERT(sizeof(stack) >= beltKWP_keep());
  90. beltKWPStart(stack, beltH() + 128, 32);
  91. memCopy(buf, beltH(), 32);
  92. memCopy(buf + 32, beltH() + 32, 16);
  93. beltKWPStepE(buf, 48, stack);
  94. if (!hexEq(buf,
  95. "49A38EE108D6C742E52B774F00A6EF98"
  96. "B106CBD13EA4FB0680323051BC04DF76"
  97. "E487B055C69BCF541176169F1DC9F6C8"))
  98. return FALSE;
  99. // все нормально
  100. return ERR_OK;
  101. }
  102. /*
  103. *******************************************************************************
  104. Управление паролями: схема pass
  105. *******************************************************************************
  106. */
  107. static err_t cmdPwdGenPass(cmd_pwd_t* pwd, const char* cmdline)
  108. {
  109. ASSERT(memIsValid(pwd, sizeof(blob_t*)));
  110. ASSERT(strIsValid(cmdline));
  111. // создать пароль
  112. if (!(*pwd = blobCreate(strLen(cmdline) + 1)))
  113. return ERR_OUTOFMEMORY;
  114. strCopy(*pwd, cmdline);
  115. return ERR_OK;
  116. }
  117. #define cmdPwdReadPass cmdPwdGenPass
  118. /*
  119. *******************************************************************************
  120. Управление паролями: схема share
  121. *******************************************************************************
  122. */
  123. static err_t cmdPwdGenShare_internal(cmd_pwd_t* pwd, size_t scount,
  124. size_t threshold, size_t len, bool_t crc, char* shares[],
  125. const cmd_pwd_t spwd)
  126. {
  127. err_t code;
  128. const size_t iter = 10000;
  129. size_t epki_len;
  130. void* stack;
  131. octet* pwd_bin;
  132. octet* state;
  133. octet* share;
  134. octet* salt;
  135. octet* epki;
  136. // pre
  137. ASSERT(memIsValid(pwd, sizeof(cmd_pwd_t)));
  138. ASSERT(cmdPwdIsValid(spwd));
  139. ASSERT(2 <= scount && scount <= 16);
  140. ASSERT(2 <= threshold && threshold <= scount);
  141. ASSERT(len % 8 == 0 && len <= 32);
  142. ASSERT(!crc || len != 16);
  143. // пароль пока не создан
  144. *pwd = 0;
  145. // входной контроль
  146. if (!rngIsValid())
  147. return ERR_BAD_RNG;
  148. // определить длину пароля
  149. if (len == 0)
  150. len = 32;
  151. // определить длину контейнера с частичным секретом
  152. code = bpkiShareWrap(0, &epki_len, 0, len + 1, 0, 0, 0, iter);
  153. ERR_CALL_CHECK(code);
  154. // выделить память и разметить ее
  155. code = cmdBlobCreate(stack, len +
  156. utilMax(2,
  157. beltMAC_keep(),
  158. scount * (len + 1) + epki_len + 8));
  159. ERR_CALL_CHECK(code);
  160. pwd_bin = (octet*)stack;
  161. state = share = pwd_bin + len;
  162. salt = share + scount * (len + 1);
  163. epki = salt + 8;
  164. // сгенерировать пароль
  165. if (crc)
  166. {
  167. rngStepR(pwd_bin, len - 8, 0);
  168. beltMACStart(state, pwd_bin, len - 8);
  169. beltMACStepA(pwd_bin, len - 8, state);
  170. beltMACStepG(pwd_bin + len - 8, state);
  171. }
  172. else
  173. rngStepR(pwd_bin, len, 0);
  174. // разделить пароль на частичные секреты
  175. code = belsShare2(share, scount, threshold, len, pwd_bin, rngStepR, 0);
  176. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  177. // обновить ключ ГСЧ
  178. rngRekey();
  179. // защитить частичные секреты
  180. for (; scount--; share += (len + 1), ++shares)
  181. {
  182. FILE* fp;
  183. // установить защиту
  184. rngStepR(salt, 8, 0);
  185. code = bpkiShareWrap(epki, 0, share, len + 1, (const octet*)spwd,
  186. cmdPwdLen(spwd), salt, iter);
  187. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  188. // открыть файл для записи
  189. ASSERT(strIsValid(*shares));
  190. fp = fopen(*shares, "wb");
  191. code = fp ? ERR_OK : ERR_FILE_CREATE;
  192. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  193. // записать
  194. code = fwrite(epki, 1, epki_len, fp) == epki_len ?
  195. ERR_OK : ERR_FILE_WRITE;
  196. fclose(fp);
  197. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  198. }
  199. // создать выходной (текстовый) пароль
  200. *pwd = cmdPwdCreate(2 * len);
  201. code = *pwd ? ERR_OK : ERR_OUTOFMEMORY;
  202. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  203. hexFrom(*pwd, pwd_bin, len);
  204. cmdBlobClose(stack);
  205. return code;
  206. }
  207. static err_t cmdPwdReadShare_internal(cmd_pwd_t* pwd, size_t scount,
  208. size_t len, bool_t crc, char* shares[], const cmd_pwd_t spwd)
  209. {
  210. err_t code;
  211. size_t epki_len;
  212. size_t epki_len_min;
  213. size_t epki_len_max;
  214. void* stack;
  215. octet* share;
  216. octet* state;
  217. octet* epki;
  218. octet* pwd_bin;
  219. size_t pos;
  220. // pre
  221. ASSERT(memIsValid(pwd, sizeof(cmd_pwd_t)));
  222. ASSERT(cmdPwdIsValid(spwd));
  223. ASSERT(2 <= scount && scount <= 16);
  224. ASSERT(len % 8 == 0 && len <= 32);
  225. ASSERT(!crc || len != 16);
  226. // пароль пока не создан
  227. *pwd = 0;
  228. // определить длину частичного секрета
  229. if (len == 0)
  230. {
  231. // определить размер первого файла с частичным секретом
  232. if ((epki_len = cmdFileSize(shares[0])) == SIZE_MAX)
  233. return ERR_FILE_READ;
  234. // найти подходящую длину
  235. for (len = 16; len <= 32; len += 8)
  236. {
  237. code = bpkiShareWrap(0, &epki_len_min, 0, len + 1, 0, 0, 0, 10000);
  238. ERR_CALL_CHECK(code);
  239. code = bpkiShareWrap(0, &epki_len_max, 0, len + 1, 0, 0, 0,
  240. SIZE_MAX);
  241. ERR_CALL_CHECK(code);
  242. if (epki_len_min <= epki_len && epki_len <= epki_len_max)
  243. break;
  244. }
  245. if (len > 32)
  246. return ERR_BAD_FORMAT;
  247. }
  248. else
  249. {
  250. code = bpkiShareWrap(0, &epki_len_min, 0, len + 1, 0, 0, 0, 10000);
  251. ERR_CALL_CHECK(code);
  252. code = bpkiShareWrap(0, &epki_len_max, 0, len + 1, 0, 0, 0, SIZE_MAX);
  253. ERR_CALL_CHECK(code);
  254. }
  255. // выделить память и разметить ее
  256. code = cmdBlobCreate(stack, scount * (len + 1) + epki_len_max + 1 + len);
  257. ERR_CALL_HANDLE(code, cmdPwdClose(*pwd));
  258. share = state = (octet*)stack;
  259. epki = share + scount * (len + 1);
  260. pwd_bin = epki + epki_len_max + 1;
  261. // прочитать частичные секреты
  262. for (pos = 0; pos < scount; ++pos, ++shares)
  263. {
  264. FILE* fp;
  265. size_t share_len;
  266. // открыть файл для чтения
  267. ASSERT(strIsValid(*shares));
  268. code = (fp = fopen(*shares, "rb")) ? ERR_OK : ERR_FILE_OPEN;
  269. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  270. // читать
  271. epki_len = fread(epki, 1, epki_len_max + 1, fp);
  272. fclose(fp);
  273. code = (epki_len_min <= epki_len && epki_len <= epki_len_max) ?
  274. ERR_OK : ERR_BAD_FORMAT;
  275. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  276. // декодировать
  277. code = bpkiShareUnwrap(share + pos * (len + 1), &share_len,
  278. epki, epki_len, (const octet*)spwd, cmdPwdLen(spwd));
  279. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  280. code = (share_len == len + 1) ? ERR_OK : ERR_BAD_FORMAT;
  281. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  282. }
  283. // собрать пароль
  284. code = belsRecover2(pwd_bin, scount, len, share);
  285. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  286. // проверить пароль
  287. if (crc)
  288. {
  289. beltMACStart(state, pwd_bin, len - 8);
  290. beltMACStepA(pwd_bin, len - 8, state);
  291. if (!beltMACStepV(pwd_bin + len - 8, state))
  292. code = ERR_BAD_CRC;
  293. }
  294. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  295. // создать выходной (текстовый) пароль
  296. *pwd = cmdPwdCreate(2 * len);
  297. code = *pwd ? ERR_OK : ERR_OUTOFMEMORY;
  298. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  299. hexFrom(*pwd, pwd_bin, len);
  300. cmdBlobClose(stack);
  301. return code;
  302. }
  303. static err_t cmdPwdGenShare(cmd_pwd_t* pwd, const char* cmdline)
  304. {
  305. err_t code;
  306. int argc;
  307. char** argv = 0;
  308. size_t offset = 0;
  309. size_t threshold = 0;
  310. size_t len = 0;
  311. bool_t crc = FALSE;
  312. cmd_pwd_t spwd = 0;
  313. // составить список аргументов
  314. code = cmdArgCreate(&argc, &argv, cmdline);
  315. ERR_CALL_CHECK(code);
  316. // обработать опции
  317. while (argc && strStartsWith(argv[offset], "-"))
  318. {
  319. // порог
  320. if (strStartsWith(argv[offset], "-t"))
  321. {
  322. char* str = argv[offset] + strLen("-t");
  323. if (threshold)
  324. {
  325. code = ERR_CMD_DUPLICATE;
  326. goto final;
  327. }
  328. if (!decIsValid(str) || decCLZ(str) || strLen(str) > 2 ||
  329. (threshold = (size_t)decToU32(str)) < 2 || threshold > 16)
  330. {
  331. code = ERR_CMD_PARAMS;
  332. goto final;
  333. }
  334. ++offset, --argc;
  335. }
  336. // уровень стойкости
  337. else if (strStartsWith(argv[offset], "-l"))
  338. {
  339. char* str = argv[offset] + strLen("-l");
  340. if (len)
  341. {
  342. code = ERR_CMD_DUPLICATE;
  343. goto final;
  344. }
  345. if (!decIsValid(str) || decCLZ(str) || strLen(str) != 3 ||
  346. (len = (size_t)decToU32(str)) % 64 || len < 128 || len > 256)
  347. {
  348. code = ERR_CMD_PARAMS;
  349. goto final;
  350. }
  351. len /= 8;
  352. if (len == 16 && crc)
  353. {
  354. code = ERR_CMD_PARAMS;
  355. goto final;
  356. }
  357. ++offset, --argc;
  358. }
  359. // контрольная сумма
  360. else if (strStartsWith(argv[offset], "-crc"))
  361. {
  362. if (crc)
  363. {
  364. code = ERR_CMD_DUPLICATE;
  365. goto final;
  366. }
  367. if (len == 16)
  368. {
  369. code = ERR_CMD_PARAMS;
  370. goto final;
  371. }
  372. crc = TRUE, ++offset, --argc;
  373. }
  374. // пароль защиты частичных секретов
  375. else if (strEq(argv[offset], "-pass"))
  376. {
  377. if (spwd)
  378. {
  379. code = ERR_CMD_DUPLICATE;
  380. goto final;
  381. }
  382. ++offset, --argc;
  383. // определить пароль защиты частичных секретов
  384. code = cmdPwdRead(&spwd, argv[offset]);
  385. ERR_CALL_HANDLE(code, cmdArgClose(argv));
  386. ASSERT(cmdPwdIsValid(spwd));
  387. ++offset, --argc;
  388. }
  389. else
  390. {
  391. code = ERR_CMD_PARAMS;
  392. goto final;
  393. }
  394. }
  395. // проверить, что пароль защиты частичных секретов построен
  396. if (!spwd)
  397. {
  398. code = ERR_CMD_PARAMS;
  399. goto final;
  400. }
  401. // настроить порог
  402. if (!threshold)
  403. threshold = 2;
  404. // проверить число файлов с частичными секретами
  405. if ((size_t)argc < threshold)
  406. {
  407. code = ERR_CMD_PARAMS;
  408. goto final;
  409. }
  410. // проверить отсутствие файлов с частичными секретами
  411. if ((code = cmdFileValNotExist(argc, argv + offset)) != ERR_OK)
  412. goto final;
  413. // построить пароль
  414. code = cmdPwdGenShare_internal(pwd, (size_t)argc, threshold, len, crc,
  415. argv + offset, spwd);
  416. final:
  417. cmdPwdClose(spwd);
  418. cmdArgClose(argv);
  419. return code;
  420. }
  421. static err_t cmdPwdReadShare(cmd_pwd_t* pwd, const char* cmdline)
  422. {
  423. err_t code;
  424. int argc;
  425. char** argv = 0;
  426. size_t offset = 0;
  427. size_t threshold = 0;
  428. size_t len = 0;
  429. bool_t crc = FALSE;
  430. cmd_pwd_t spwd = 0;
  431. // составить список аргументов
  432. code = cmdArgCreate(&argc, &argv, cmdline);
  433. ERR_CALL_CHECK(code);
  434. // обработать опции
  435. while (argc && strStartsWith(argv[offset], "-"))
  436. {
  437. // порог
  438. if (strStartsWith(argv[offset], "-t"))
  439. {
  440. char* str = argv[offset] + strLen("-t");
  441. if (threshold)
  442. {
  443. code = ERR_CMD_DUPLICATE;
  444. goto final;
  445. }
  446. if (!decIsValid(str) || decCLZ(str) || strLen(str) > 2 ||
  447. (threshold = (size_t)decToU32(str)) < 2 || threshold > 16)
  448. {
  449. code = ERR_CMD_PARAMS;
  450. goto final;
  451. }
  452. ++offset, --argc;
  453. }
  454. // уровень стойкости
  455. else if (strStartsWith(argv[offset], "-l"))
  456. {
  457. char* str = argv[offset] + strLen("-l");
  458. if (len)
  459. {
  460. code = ERR_CMD_DUPLICATE;
  461. goto final;
  462. }
  463. if (!decIsValid(str) || decCLZ(str) || strLen(str) != 3 ||
  464. (len = (size_t)decToU32(str)) % 64 || len < 128 || len > 256)
  465. {
  466. code = ERR_CMD_PARAMS;
  467. goto final;
  468. }
  469. len /= 8;
  470. if (len == 16 && crc)
  471. {
  472. code = ERR_CMD_PARAMS;
  473. goto final;
  474. }
  475. ++offset, --argc;
  476. }
  477. // контрольная сумма
  478. else if (strStartsWith(argv[offset], "-crc"))
  479. {
  480. if (crc)
  481. {
  482. code = ERR_CMD_DUPLICATE;
  483. goto final;
  484. }
  485. if (len == 16)
  486. {
  487. code = ERR_CMD_PARAMS;
  488. goto final;
  489. }
  490. crc = TRUE, ++offset, --argc;
  491. }
  492. // пароль защиты частичных секретов
  493. else if (strEq(argv[offset], "-pass"))
  494. {
  495. if (spwd)
  496. {
  497. code = ERR_CMD_DUPLICATE;
  498. goto final;
  499. }
  500. ++offset, --argc;
  501. // определить пароль защиты частичных секретов
  502. code = cmdPwdRead(&spwd, argv[offset]);
  503. ERR_CALL_HANDLE(code, cmdArgClose(argv));
  504. ASSERT(cmdPwdIsValid(spwd));
  505. ++offset, --argc;
  506. }
  507. else
  508. {
  509. code = ERR_CMD_PARAMS;
  510. goto final;
  511. }
  512. }
  513. // проверить, что пароль защиты частичных секретов определен
  514. if (!spwd)
  515. {
  516. code = ERR_CMD_PARAMS;
  517. goto final;
  518. }
  519. // настроить порог
  520. if (!threshold)
  521. threshold = 2;
  522. // проверить число файлов с частичными секретами
  523. if ((size_t)argc < threshold)
  524. {
  525. code = ERR_CMD_PARAMS;
  526. goto final;
  527. }
  528. // проверить наличие файлов с частичными секретами
  529. if ((code = cmdFileValExist(argc, argv + offset)) != ERR_OK)
  530. goto final;
  531. // определить пароль
  532. code = cmdPwdReadShare_internal(pwd, (size_t)argc, len, crc,
  533. argv + offset, spwd);
  534. final:
  535. cmdPwdClose(spwd);
  536. cmdArgClose(argv);
  537. return code;
  538. }
  539. /*
  540. *******************************************************************************
  541. Управление паролями: построение / определение
  542. *******************************************************************************
  543. */
  544. err_t cmdPwdGen(cmd_pwd_t* pwd, const char* cmdline)
  545. {
  546. if (strStartsWith(cmdline, "pass:"))
  547. return cmdPwdGenPass(pwd, cmdline + strLen("pass:"));
  548. else if (strStartsWith(cmdline, "share:"))
  549. return cmdPwdGenShare(pwd, cmdline + strLen("share:"));
  550. return ERR_CMD_PARAMS;
  551. }
  552. err_t cmdPwdRead(cmd_pwd_t* pwd, const char* cmdline)
  553. {
  554. if (strStartsWith(cmdline, "pass:"))
  555. return cmdPwdReadPass(pwd, cmdline + strLen("pass:"));
  556. else if (strStartsWith(cmdline, "share:"))
  557. return cmdPwdReadShare(pwd, cmdline + strLen("share:"));
  558. return ERR_CMD_PARAMS;
  559. }