cmd_pwd.c 15 KB

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