cvc.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. /*
  2. *******************************************************************************
  3. \file cvc.c
  4. \brief Manage CV-certificates
  5. \project bee2/cmd
  6. \created 2022.07.12
  7. \version 2024.01.19
  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/err.h>
  15. #include <bee2/core/hex.h>
  16. #include <bee2/core/mem.h>
  17. #include <bee2/core/prng.h>
  18. #include <bee2/core/str.h>
  19. #include <bee2/core/tm.h>
  20. #include <bee2/core/util.h>
  21. #include <bee2/crypto/belt.h>
  22. #include <bee2/crypto/bign.h>
  23. #include <bee2/crypto/btok.h>
  24. #include <stdio.h>
  25. /*
  26. *******************************************************************************
  27. Утилита cvc
  28. Функционал:
  29. - выпуск самоподписанного сертификата;
  30. - создание предсертификата (запроса на выпуск);
  31. - выпуск сертификата;
  32. - проверка цепочки сертификатов;
  33. - проверка соответствия между сертификатом и личным ключом;
  34. - печать полей сертификата.
  35. Пример:
  36. # подготовка ключей
  37. bee2cmd kg gen -l256 -pass pass:root privkey0
  38. bee2cmd kg gen -l192 -pass pass:trent privkey1
  39. bee2cmd kg gen -pass pass:alice privkey2
  40. bee2cmd kg extr -pass pass:alice privkey2 pubkey2
  41. # выпуск сертификатов
  42. bee2cmd cvc root -authority BYCA0000 -from 220707 -until 990707 \
  43. -pass pass:root -eid EEEEEEEEEE -esign 7777 privkey0 cert0
  44. bee2cmd cvc print cert0
  45. bee2cmd cvc print -holder cert0
  46. bee2cmd cvc extr cert0 pubkey0
  47. bee2cmd cvc req -pass pass:trent -authority BYCA0000 -holder BYCA1023 \
  48. -from 220712 -until 221130 -eid DDDDDDDDDD -esign 3333 privkey1 req1
  49. bee2cmd cvc iss -pass pass:root privkey0 cert0 req1 cert1
  50. bee2cmd cvc req -authority BYCA1023 -from 220712 -until 391231 -esign 1111 \
  51. -holder 590082394654 -pass pass:alice -eid 8888888888 privkey2 req2
  52. bee2cmd cvc iss -pass pass:trent privkey1 cert1 req2 cert2
  53. # проверка сертификатов
  54. bee2cmd cvc match -pass pass:alice privkey2 cert2
  55. bee2cmd cvc val cert0 cert0
  56. bee2cmd cvc val -date 220712 cert0 cert1
  57. bee2cmd cvc val -date 000000 cert0 cert1 cert2
  58. # сокращение срока действия
  59. bee2cmd cvc shorten -until 391230 -pass pass:trent privkey1 cert1 cert2
  60. *******************************************************************************
  61. */
  62. static const char _name[] = "cvc";
  63. static const char _descr[] = "manage CV-certificates";
  64. static int cvcUsage()
  65. {
  66. printf(
  67. "bee2cmd/%s: %s\n"
  68. "Usage:\n"
  69. " cvc root [options] <privkeya> <certa>\n"
  70. " issue a self-signed certificate <certa>\n"
  71. " cvc req [options] <privkey> <req>\n"
  72. " generate a pre-certificate <req>\n"
  73. " cvc iss [options] <privkeya> <certa> <req> <cert>\n"
  74. " issue <cert> based on <req> and subordinate to <certa>\n"
  75. " cvc shorten [options] <privkeya> <certa> <cert>\n"
  76. " shorten the lifetime of <cert> subordinate to <certa>\n"
  77. " cvc val [options] <certa> <certb> ... <cert>\n"
  78. " validate <certb> ... <cert> using <certa> as an anchor\n"
  79. " cvc match [options] <privkey> <cert>\n"
  80. " check the match between <privkey> and <cert>\n"
  81. " cvc extr <cert> <pubkey>\n"
  82. " extract <pubkey> from <cert>\n"
  83. " cvc print [field] <cert>\n"
  84. " print <cert> info: all fields or a specific field\n"
  85. " .\n"
  86. " <privkey>, <privkeya>\n"
  87. " containers with private keys\n"
  88. " <pubkey>\n"
  89. " file with a public key\n"
  90. " options:\n"
  91. " -authority <name> -- authority [root] req\n"
  92. " -holder <name> -- holder [root] req [iss]\n"
  93. " -from <YYMMDD> -- starting date root req [iss]\n"
  94. " -until <YYMMDD> -- expiration date root req [iss] cut\n"
  95. " -eid <10*hex> -- eId access mask [root] [req] [iss]\n"
  96. " -esign <4*hex> -- eSign access mask [root] [req] [iss]\n"
  97. " -pass <schema> -- password root req iss shorten match\n"
  98. " -date <YYMMDD> -- validation date [val]\n"
  99. " field:\n"
  100. " {-authority|-holder|-from|-until|-eid|-esign|-pubkey|-sig}\n"
  101. ,
  102. _name, _descr
  103. );
  104. return -1;
  105. }
  106. /*
  107. *******************************************************************************
  108. Самотестирование
  109. *******************************************************************************
  110. */
  111. static err_t cvcSelfTest()
  112. {
  113. octet stack[1024];
  114. bign_params params[1];
  115. octet privkey[32];
  116. octet pubkey[64];
  117. octet hash[32];
  118. const octet oid[] = {
  119. 0x06, 0x09, 0x2A, 0x70, 0x00, 0x02, 0x00, 0x22, 0x65, 0x1F, 0x51,
  120. };
  121. octet sig[48];
  122. // bign-genkeypair
  123. hexTo(privkey,
  124. "1F66B5B84B7339674533F0329C74F218"
  125. "34281FED0732429E0C79235FC273E269");
  126. ASSERT(sizeof(stack) >= prngEcho_keep());
  127. prngEchoStart(stack, privkey, 32);
  128. if (bignParamsStd(params, "1.2.112.0.2.0.34.101.45.3.1") != ERR_OK ||
  129. bignKeypairGen(privkey, pubkey, params, prngEchoStepR,
  130. stack) != ERR_OK ||
  131. !hexEq(pubkey,
  132. "BD1A5650179D79E03FCEE49D4C2BD5DD"
  133. "F54CE46D0CF11E4FF87BF7A890857FD0"
  134. "7AC6A60361E8C8173491686D461B2826"
  135. "190C2EDA5909054A9AB84D2AB9D99A90"))
  136. return ERR_SELFTEST;
  137. // bign-valpubkey
  138. if (bignPubkeyVal(params, pubkey) != ERR_OK)
  139. return ERR_SELFTEST;
  140. // bign-sign
  141. if (beltHash(hash, beltH(), 13) != ERR_OK)
  142. return ERR_SELFTEST;
  143. if (bignSign2(sig, params, oid, sizeof(oid), hash, privkey,
  144. 0, 0) != ERR_OK)
  145. return ERR_SELFTEST;
  146. if (!hexEq(sig,
  147. "19D32B7E01E25BAE4A70EB6BCA42602C"
  148. "CA6A13944451BCC5D4C54CFD8737619C"
  149. "328B8A58FB9C68FD17D569F7D06495FB"))
  150. return ERR_SELFTEST;
  151. if (bignVerify(params, oid, sizeof(oid), hash, sig, pubkey) != ERR_OK)
  152. return ERR_SELFTEST;
  153. sig[0] ^= 1;
  154. if (bignVerify(params, oid, sizeof(oid), hash, sig, pubkey) == ERR_OK)
  155. return ERR_SELFTEST;
  156. // все нормально
  157. return ERR_OK;
  158. }
  159. /*
  160. *******************************************************************************
  161. Разбор опций командной строки
  162. Опции возвращаются по адресам cvc, pwd, date. Любой из адресов может быть
  163. нулевым, и тогда соответствующая опция не возвращается. Более того, ее указание
  164. в командной строке считается ошибкой.
  165. По адресам eid и esign возвращаются признаки наличия в командной строке
  166. одноименных опций. При указании нулевого адреса наличие в командной строке
  167. соответствующей опции считается ошибкой. Передача ненулевого eid (ожидаются
  168. флаги доступа к eId) и нулевого cvc (флаги некуда сохранить) является ошибкой.
  169. Логика распространяется на указатель esign.
  170. Передача ненулевого pwd является запросом на построение пароля по параметрам
  171. командной строки. Запрос должен быть обязательно исполнен.
  172. В случае успеха по адресу readc возвращается число обработанных аргументов.
  173. *******************************************************************************
  174. */
  175. static err_t cvcParseOptions(btok_cvc_t* cvc, bool_t* eid, bool_t* esign,
  176. cmd_pwd_t* pwd, octet date[6], int* readc, int argc, char* argv[])
  177. {
  178. err_t code = ERR_OK;
  179. // pre
  180. ASSERT(memIsNullOrValid(cvc, sizeof(btok_cvc_t)));
  181. ASSERT(memIsNullOrValid(eid, sizeof(bool_t)));
  182. ASSERT(memIsNullOrValid(esign, sizeof(bool_t)));
  183. ASSERT(memIsNullOrValid(pwd, sizeof(cmd_pwd_t)));
  184. ASSERT(memIsNullOrValid(date, 6));
  185. ASSERT(memIsValid(readc, sizeof(int)));
  186. // подготовить выходные данные
  187. cvc ? memSetZero(cvc, sizeof(btok_cvc_t)) : 0;
  188. eid ? *eid = 0 : 0;
  189. esign ? *esign = 0 : 0;
  190. pwd ? *pwd = 0 : 0;
  191. date ? memSetZero(date, 6) : 0;
  192. // обработать опции
  193. *readc = argc;
  194. while (argc && strStartsWith(*argv, "-"))
  195. {
  196. if (argc < 2)
  197. {
  198. code = ERR_CMD_PARAMS;
  199. break;
  200. }
  201. // authority
  202. if (strEq(argv[0], "-authority"))
  203. {
  204. if (!cvc)
  205. {
  206. code = ERR_CMD_PARAMS;
  207. break;
  208. }
  209. if (strLen(cvc->authority))
  210. {
  211. code = ERR_CMD_DUPLICATE;
  212. break;
  213. }
  214. --argc, ++argv;
  215. ASSERT(argc > 0);
  216. if (!strLen(*argv) || strLen(*argv) + 1 > sizeof(cvc->authority))
  217. {
  218. code = ERR_BAD_NAME;
  219. break;
  220. }
  221. strCopy(cvc->authority, *argv);
  222. --argc, ++argv;
  223. }
  224. // holder
  225. else if (strEq(argv[0], "-holder"))
  226. {
  227. if (!cvc)
  228. {
  229. code = ERR_CMD_PARAMS;
  230. break;
  231. }
  232. if (strLen(cvc->holder))
  233. {
  234. code = ERR_CMD_DUPLICATE;
  235. break;
  236. }
  237. --argc, ++argv;
  238. ASSERT(argc > 0);
  239. if (!strLen(*argv) || strLen(*argv) + 1 > sizeof(cvc->holder))
  240. {
  241. code = ERR_BAD_NAME;
  242. break;
  243. }
  244. strCopy(cvc->holder, *argv);
  245. --argc, ++argv;
  246. }
  247. // from
  248. else if (strEq(*argv, "-from"))
  249. {
  250. if (!cvc)
  251. {
  252. code = ERR_CMD_PARAMS;
  253. break;
  254. }
  255. if (!memIsZero(cvc->from, 6))
  256. {
  257. code = ERR_CMD_DUPLICATE;
  258. break;
  259. }
  260. --argc, ++argv;
  261. ASSERT(argc > 0);
  262. code = cmdDateParse(cvc->from, *argv);
  263. if (code != ERR_OK)
  264. break;
  265. --argc, ++argv;
  266. }
  267. // until
  268. else if (strEq(*argv, "-until"))
  269. {
  270. if (!cvc)
  271. {
  272. code = ERR_CMD_PARAMS;
  273. break;
  274. }
  275. if (!memIsZero(cvc->until, 6))
  276. {
  277. code = ERR_CMD_DUPLICATE;
  278. break;
  279. }
  280. --argc, ++argv;
  281. ASSERT(argc > 0);
  282. code = cmdDateParse(cvc->until, *argv);
  283. if (code != ERR_OK)
  284. break;
  285. --argc, ++argv;
  286. }
  287. // eid
  288. else if (strEq(*argv, "-eid"))
  289. {
  290. if (!cvc || !eid)
  291. {
  292. code = ERR_CMD_PARAMS;
  293. break;
  294. }
  295. if (*eid)
  296. {
  297. code = ERR_CMD_DUPLICATE;
  298. break;
  299. }
  300. --argc, ++argv;
  301. ASSERT(argc > 0);
  302. if (strLen(*argv) != 10 || !hexIsValid(*argv))
  303. {
  304. code = ERR_BAD_ACL;
  305. break;
  306. }
  307. hexTo(cvc->hat_eid, *argv);
  308. *eid = TRUE;
  309. --argc, ++argv;
  310. }
  311. // esign
  312. else if (strEq(*argv, "-esign"))
  313. {
  314. if (!cvc || !esign)
  315. {
  316. code = ERR_CMD_PARAMS;
  317. break;
  318. }
  319. if (*esign)
  320. {
  321. code = ERR_CMD_DUPLICATE;
  322. break;
  323. }
  324. --argc, ++argv;
  325. ASSERT(argc > 0);
  326. if (strLen(*argv) != 4 || !hexIsValid(*argv))
  327. {
  328. code = ERR_BAD_ACL;
  329. break;
  330. }
  331. hexTo(cvc->hat_esign, *argv);
  332. *esign = TRUE;
  333. --argc, ++argv;
  334. }
  335. // password
  336. else if (strEq(*argv, "-pass"))
  337. {
  338. if (!pwd)
  339. {
  340. code = ERR_CMD_PARAMS;
  341. break;
  342. }
  343. if (*pwd)
  344. {
  345. code = ERR_CMD_DUPLICATE;
  346. break;
  347. }
  348. --argc, ++argv;
  349. ASSERT(argc > 0);
  350. if ((code = cmdPwdRead(pwd, *argv)) != ERR_OK)
  351. break;
  352. --argc, ++argv;
  353. }
  354. // date
  355. else if (strEq(*argv, "-date"))
  356. {
  357. if (!date)
  358. {
  359. code = ERR_CMD_PARAMS;
  360. break;
  361. }
  362. if (!memIsZero(date, 6))
  363. {
  364. code = ERR_CMD_DUPLICATE;
  365. break;
  366. }
  367. --argc, ++argv;
  368. ASSERT(argc > 0);
  369. code = cmdDateParse(date, *argv);
  370. if (code != ERR_OK)
  371. break;
  372. --argc, ++argv;
  373. }
  374. else
  375. {
  376. code = ERR_CMD_PARAMS;
  377. break;
  378. }
  379. }
  380. // проверить, что запрошенные данные определены
  381. // \remark корректность cvc будет проверена позже
  382. // \remark параметр date не является обязательным
  383. if (code == ERR_OK && pwd && !*pwd)
  384. code = ERR_CMD_PARAMS;
  385. // завершить
  386. if (code != ERR_OK && pwd)
  387. cmdPwdClose(*pwd), *pwd = 0;
  388. else
  389. *readc -= argc;
  390. return code;
  391. }
  392. /*
  393. *******************************************************************************
  394. Выпуск самоподписанного сертификата
  395. cvc root [options] <privkeya> <certa>
  396. \remark Обязательные опции: pass, authority и/или holder, from, until.
  397. Разрешенные: eid, esign.
  398. *******************************************************************************
  399. */
  400. static err_t cvcRoot(int argc, char* argv[])
  401. {
  402. err_t code;
  403. btok_cvc_t cvc[1];
  404. bool_t eid;
  405. bool_t esign;
  406. cmd_pwd_t pwd;
  407. int readc;
  408. size_t privkey_len;
  409. octet* privkey;
  410. size_t cert_len;
  411. octet* cert;
  412. // самотестирование
  413. code = cvcSelfTest();
  414. ERR_CALL_CHECK(code);
  415. // обработать опции
  416. code = cvcParseOptions(cvc, &eid, &esign, &pwd, 0, &readc, argc, argv);
  417. ERR_CALL_CHECK(code);
  418. argc -= readc, argv += readc;
  419. if (argc != 2)
  420. code = ERR_CMD_PARAMS;
  421. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  422. // доопределить cvc и проверить, что authority == holder
  423. if (!strLen(cvc->authority))
  424. strCopy(cvc->authority, cvc->holder);
  425. else if (!strLen(cvc->holder))
  426. strCopy(cvc->holder, cvc->authority);
  427. if (!strEq(cvc->authority, cvc->holder))
  428. code = ERR_BAD_NAME;
  429. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  430. // проверить наличие/отсутствие файлов
  431. code = cmdFileValExist(1, argv);
  432. ERR_CALL_CHECK(code);
  433. code = cmdFileValNotExist(1, argv + 1);
  434. ERR_CALL_CHECK(code);
  435. // прочитать личный ключ
  436. privkey_len = 0;
  437. code = cmdPrivkeyRead(0, &privkey_len, argv[0], pwd);
  438. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  439. code = cmdBlobCreate(privkey, privkey_len);
  440. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  441. code = cmdPrivkeyRead(privkey, 0, argv[0], pwd);
  442. cmdPwdClose(pwd);
  443. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  444. // определить длину сертификата
  445. ASSERT(cvc->pubkey_len == 0);
  446. code = btokCVCWrap(0, &cert_len, cvc, privkey, privkey_len);
  447. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  448. ASSERT(cvc->pubkey_len != 0);
  449. // создать сертификат
  450. code = cmdBlobCreate(cert, cert_len);
  451. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  452. code = btokCVCWrap(cert, 0, cvc, privkey, privkey_len);
  453. cmdBlobClose(privkey);
  454. ERR_CALL_HANDLE(code, cmdBlobClose(cert));
  455. // записать сертификат
  456. code = cmdFileWrite(argv[1], cert, cert_len);
  457. // завершить
  458. cmdBlobClose(cert);
  459. return code;
  460. }
  461. /*
  462. *******************************************************************************
  463. Создание предсертификата (запроса)
  464. cvc req [options] <privkey> <req>
  465. \remark Обязательные опции: pass, authority, holder, from, until.
  466. Разрешенные: eid, esign.
  467. *******************************************************************************
  468. */
  469. static err_t cvcReq(int argc, char* argv[])
  470. {
  471. err_t code;
  472. btok_cvc_t cvc[1];
  473. bool_t eid;
  474. bool_t esign;
  475. cmd_pwd_t pwd;
  476. int readc;
  477. size_t privkey_len;
  478. octet* privkey;
  479. size_t req_len;
  480. octet* req;
  481. // самотестирование
  482. code = cvcSelfTest();
  483. ERR_CALL_CHECK(code);
  484. // обработать опции
  485. code = cvcParseOptions(cvc, &eid, &esign, &pwd, 0, &readc, argc, argv);
  486. ERR_CALL_CHECK(code);
  487. argc -= readc, argv += readc;
  488. if (argc != 2)
  489. code = ERR_CMD_PARAMS;
  490. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  491. // проверить, что authority != holder
  492. if (strEq(cvc->authority, cvc->holder))
  493. code = ERR_BAD_NAME;
  494. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  495. // проверить наличие/отсутствие файлов
  496. code = cmdFileValExist(1, argv);
  497. ERR_CALL_CHECK(code);
  498. code = cmdFileValNotExist(1, argv + 1);
  499. ERR_CALL_CHECK(code);
  500. // прочитать личный ключ
  501. privkey_len = 0;
  502. code = cmdPrivkeyRead(0, &privkey_len, argv[0], pwd);
  503. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  504. code = cmdBlobCreate(privkey, privkey_len);
  505. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  506. code = cmdPrivkeyRead(privkey, 0, argv[0], pwd);
  507. cmdPwdClose(pwd);
  508. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  509. // определить длину предсертификата
  510. ASSERT(cvc->pubkey_len == 0);
  511. code = btokCVCWrap(0, &req_len, cvc, privkey, privkey_len);
  512. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  513. ASSERT(cvc->pubkey_len != 0);
  514. // создать предсертификат
  515. code = cmdBlobCreate(req, req_len);
  516. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  517. code = btokCVCWrap(req, 0, cvc, privkey, privkey_len);
  518. cmdBlobClose(privkey);
  519. ERR_CALL_HANDLE(code, cmdBlobClose(req));
  520. // записать сертификат
  521. code = cmdFileWrite(argv[1], req, req_len);
  522. // завершить
  523. cmdBlobClose(req);
  524. return code;
  525. }
  526. /*
  527. *******************************************************************************
  528. Выпуск сертификата
  529. cvc iss [options] <privkeya> <certa> <req> <cert>
  530. \remark Обязательные опции: pass.
  531. Разрешенные: holder, from, until, eid, esign.
  532. \remark Поле holder в командной строке подавляет одноименное поле в <req>.
  533. Другими словами, эмитент может изменять имя владельца в его сертификате.
  534. Например, имеется последовательность имен, и эмитент выбирает в ней первое
  535. неиспользованное имя.
  536. \remark Поля from и until в командной строке подавляют одноименные поля
  537. в <req>. Другими словами, эмитент может изменять срок действия сертификата.
  538. \remark Поля eid и esign в командной строке накладываются побитово по правилу
  539. AND на одноименные поля в <req>. Другими словами, эмитент может ужесточать
  540. права доступа, например, cледуя определенной политике доступа.
  541. *******************************************************************************
  542. */
  543. static err_t cvcIss(int argc, char* argv[])
  544. {
  545. err_t code;
  546. btok_cvc_t cvc0[1];
  547. bool_t eid;
  548. bool_t esign;
  549. cmd_pwd_t pwd;
  550. int readc;
  551. size_t privkeya_len;
  552. octet* privkeya;
  553. size_t certa_len;
  554. size_t req_len;
  555. size_t cert_len;
  556. void* stack;
  557. octet* certa;
  558. octet* req;
  559. octet* cert;
  560. btok_cvc_t* cvc;
  561. // самотестирование
  562. code = cvcSelfTest();
  563. ERR_CALL_CHECK(code);
  564. // обработать опции
  565. code = cvcParseOptions(cvc0, &eid, &esign, &pwd, 0, &readc, argc, argv);
  566. ERR_CALL_CHECK(code);
  567. // есть запрещенные опции?
  568. if (strLen(cvc0->authority))
  569. code = ERR_CMD_PARAMS;
  570. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  571. // некорректное число аргументов?
  572. argc -= readc, argv += readc;
  573. if (argc != 4)
  574. code = ERR_CMD_PARAMS;
  575. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  576. // проверить наличие/отсутствие файлов
  577. code = cmdFileValExist(3, argv);
  578. ERR_CALL_CHECK(code);
  579. code = cmdFileValNotExist(1, argv + 3);
  580. ERR_CALL_CHECK(code);
  581. // прочитать личный ключ
  582. privkeya_len = 0;
  583. code = cmdPrivkeyRead(0, &privkeya_len, argv[0], pwd);
  584. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  585. code = cmdBlobCreate(privkeya, privkeya_len);
  586. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  587. code = cmdPrivkeyRead(privkeya, 0, argv[0], pwd);
  588. cmdPwdClose(pwd);
  589. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  590. // определить длины входных сертификата и запроса
  591. code = cmdFileReadAll(0, &certa_len, argv[1]);
  592. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  593. code = cmdFileReadAll(0, &req_len, argv[2]);
  594. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  595. // построить оценку сверху для cert_len: req_len + расширение_подписи
  596. cert_len = req_len + (96 - 48);
  597. // выделить память и разметить ее
  598. code = cmdBlobCreate(stack, certa_len + req_len + cert_len +
  599. sizeof(btok_cvc_t));
  600. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  601. certa = (octet*)stack;
  602. req = certa + certa_len;
  603. cert = req + req_len;
  604. cvc = (btok_cvc_t*)(cert + cert_len);
  605. // прочитать сертификат
  606. code = cmdFileReadAll(certa, &certa_len, argv[1]);
  607. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  608. // прочитать запрос
  609. code = cmdFileReadAll(req, &req_len, argv[2]);
  610. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  611. // разобрать запрос
  612. code = btokCVCUnwrap(cvc, req, req_len, cvc->pubkey, 0);
  613. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  614. // перенести в сертификат опции командной строки
  615. if (strLen(cvc0->holder))
  616. strCopy(cvc->holder, cvc0->holder);
  617. if (!memIsZero(cvc0->from, 6))
  618. memCopy(cvc->from, cvc0->from, 6);
  619. if (!memIsZero(cvc0->until, 6))
  620. memCopy(cvc->until, cvc0->until, 6);
  621. if (eid)
  622. {
  623. size_t pos;
  624. for (pos = 0; pos < sizeof(cvc->hat_eid); ++pos)
  625. cvc->hat_eid[pos] &= cvc0->hat_eid[pos];
  626. }
  627. if (esign)
  628. {
  629. size_t pos;
  630. for (pos = 0; pos < sizeof(cvc->hat_esign); ++pos)
  631. cvc->hat_esign[pos] &= cvc0->hat_esign[pos];
  632. }
  633. // выпустить сертификат
  634. code = btokCVCIss(cert, &cert_len, cvc, certa, certa_len, privkeya,
  635. privkeya_len);
  636. cmdBlobClose(privkeya);
  637. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  638. // записать сертификат
  639. code = cmdFileWrite(argv[3], cert, cert_len);
  640. // завершить
  641. cmdBlobClose(stack);
  642. return code;
  643. }
  644. /*
  645. *******************************************************************************
  646. Сокращение срока действия сертификата
  647. cvc shorten [options] <privkeya> <certa> <cert>
  648. \remark Обязательные опции: pass, until.
  649. *******************************************************************************
  650. */
  651. static err_t cvcShorten(int argc, char* argv[])
  652. {
  653. err_t code;
  654. btok_cvc_t cvc0[1];
  655. cmd_pwd_t pwd;
  656. int readc;
  657. size_t privkeya_len;
  658. octet* privkeya;
  659. size_t certa_len;
  660. size_t cert_len;
  661. void* stack;
  662. octet* certa;
  663. octet* cert;
  664. btok_cvc_t* cvc;
  665. // самотестирование
  666. code = cvcSelfTest();
  667. ERR_CALL_CHECK(code);
  668. // обработать опции
  669. code = cvcParseOptions(cvc0, 0, 0, &pwd, 0, &readc, argc, argv);
  670. ERR_CALL_CHECK(code);
  671. // нужные опции установлены и нет запрещенных опций?
  672. if (strLen(cvc0->authority) || strLen(cvc0->holder) ||
  673. !memIsZero(cvc0->from, 6) || memIsZero(cvc0->until, 6))
  674. code = ERR_CMD_PARAMS;
  675. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  676. // некорректное число аргументов?
  677. argc -= readc, argv += readc;
  678. if (argc != 3)
  679. code = ERR_CMD_PARAMS;
  680. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  681. // проверить наличие/отсутствие файлов
  682. code = cmdFileValExist(3, argv);
  683. ERR_CALL_CHECK(code);
  684. // прочитать личный ключ
  685. privkeya_len = 0;
  686. code = cmdPrivkeyRead(0, &privkeya_len, argv[0], pwd);
  687. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  688. code = cmdBlobCreate(privkeya, privkeya_len);
  689. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  690. code = cmdPrivkeyRead(privkeya, 0, argv[0], pwd);
  691. cmdPwdClose(pwd);
  692. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  693. // определить длины сертификатов
  694. code = cmdFileReadAll(0, &certa_len, argv[1]);
  695. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  696. code = cmdFileReadAll(0, &cert_len, argv[2]);
  697. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  698. // выделить память и разметить ее
  699. code = cmdBlobCreate(stack, certa_len + cert_len + sizeof(btok_cvc_t));
  700. ERR_CALL_HANDLE(code, cmdBlobClose(privkeya));
  701. certa = (octet*)stack;
  702. cert = certa + certa_len;
  703. cvc = (btok_cvc_t*)(cert + cert_len);
  704. // прочитать сертификаты
  705. code = cmdFileReadAll(certa, &certa_len, argv[1]);
  706. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  707. code = cmdFileReadAll(cert, &cert_len, argv[2]);
  708. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  709. // проверить сертификат
  710. code = btokCVCVal(cert, cert_len, certa, certa_len, 0);
  711. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  712. // разобрать сертификат
  713. code = btokCVCUnwrap(cvc, cert, cert_len, 0, 0);
  714. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  715. // срок действия действительно сокращается?
  716. if (memCmp(cvc->until, cvc0->until, 6) < 0)
  717. code = ERR_BAD_DATE;
  718. ERR_CALL_HANDLE(code, (cmdBlobClose(privkeya), cmdBlobClose(stack)));
  719. // перенести в сертификат новую дату окончания
  720. memCopy(cvc->until, cvc0->until, 6);
  721. // выпустить сертификат
  722. code = btokCVCIss(cert, 0, cvc, certa, certa_len, privkeya, privkeya_len);
  723. cmdBlobClose(privkeya);
  724. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  725. // записать сертификат
  726. code = cmdFileWrite(argv[2], cert, cert_len);
  727. // завершить
  728. cmdBlobClose(stack);
  729. return code;
  730. }
  731. /*
  732. *******************************************************************************
  733. Проверка цепочки
  734. cvc val [options] <certa> <certb> ... <cert>
  735. \remark Разрешенные опции: date.
  736. \remark Дата проверки, указанная в options, касается только последнего
  737. сертификата цепочки -- дата должна попадать в срок действия сертификата.
  738. *******************************************************************************
  739. */
  740. static err_t cvcVal(int argc, char* argv[])
  741. {
  742. err_t code;
  743. octet date[6];
  744. int readc;
  745. const size_t cert_max_len = 512;
  746. size_t cert_len;
  747. void* stack;
  748. octet* cert;
  749. btok_cvc_t* cvc;
  750. btok_cvc_t* cvc1;
  751. // самотестирование
  752. code = cvcSelfTest();
  753. ERR_CALL_CHECK(code);
  754. // обработать опции
  755. code = cvcParseOptions(0, 0, 0, 0, date, &readc, argc, argv);
  756. ERR_CALL_CHECK(code);
  757. argc -= readc, argv += readc;
  758. if (argc < 2)
  759. code = ERR_CMD_PARAMS;
  760. ERR_CALL_CHECK(code);
  761. // проверить наличие/отсутствие файлов
  762. code = cmdFileValExist(argc, argv);
  763. ERR_CALL_CHECK(code);
  764. // выделить память и разметить ее
  765. code = cmdBlobCreate(stack, cert_max_len + 2 * sizeof(btok_cvc_t));
  766. ERR_CALL_CHECK(code);
  767. cert = (octet*)stack;
  768. cvc = (btok_cvc_t*)(cert + cert_max_len);
  769. cvc1 = cvc + 1;
  770. // прочитать первый сертификат
  771. code = cmdFileReadAll(0, &cert_len, argv[0]);
  772. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  773. code = cert_len <= cert_max_len ? ERR_OK : ERR_BAD_CERT;
  774. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  775. code = cmdFileReadAll(cert, &cert_len, argv[0]);
  776. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  777. // разобрать первый сертификат
  778. code = btokCVCUnwrap(cvc, cert, cert_len, 0, 0);
  779. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  780. // цикл по сертификатам
  781. for (--argc, ++argv; argc--; ++argv)
  782. {
  783. // прочитать очередной сертификат
  784. code = cmdFileReadAll(0, &cert_len, *argv);
  785. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  786. code = cert_len <= cert_max_len ? ERR_OK : ERR_BAD_CERT;
  787. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  788. code = cmdFileReadAll(cert, &cert_len, *argv);
  789. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  790. // проверить очередной сертификат
  791. if (argc == 0 && !memIsZero(date, 6))
  792. code = btokCVCVal2(cvc1, cert, cert_len, cvc, date);
  793. else
  794. code = btokCVCVal2(cvc1, cert, cert_len, cvc, 0);
  795. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  796. // подготовиться к проверке следующего сертификата
  797. memCopy(cvc, cvc1, sizeof(btok_cvc_t));
  798. }
  799. // завершить
  800. cmdBlobClose(stack);
  801. return code;
  802. }
  803. /*
  804. *******************************************************************************
  805. Проверка соответствия между личным ключом и сертификатом
  806. cvc match [options] <privkey> <cert>
  807. \remark Обязательные опции: pass.
  808. *******************************************************************************
  809. */
  810. static err_t cvcMatch(int argc, char* argv[])
  811. {
  812. err_t code;
  813. cmd_pwd_t pwd;
  814. int readc;
  815. size_t privkey_len;
  816. octet* privkey;
  817. size_t cert_len;
  818. octet* cert;
  819. // самотестирование
  820. code = cvcSelfTest();
  821. ERR_CALL_CHECK(code);
  822. // обработать опции
  823. code = cvcParseOptions(0, 0, 0, &pwd, 0, &readc, argc, argv);
  824. ERR_CALL_CHECK(code);
  825. argc -= readc, argv += readc;
  826. if (argc != 2)
  827. code = ERR_CMD_PARAMS;
  828. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  829. // проверить наличие/отсутствие файлов
  830. code = cmdFileValExist(2, argv);
  831. ERR_CALL_CHECK(code);
  832. // прочитать личный ключ
  833. privkey_len = 0;
  834. code = cmdPrivkeyRead(0, &privkey_len, argv[0], pwd);
  835. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  836. code = cmdBlobCreate(privkey, privkey_len);
  837. ERR_CALL_HANDLE(code, cmdPwdClose(pwd));
  838. code = cmdPrivkeyRead(privkey, 0, argv[0], pwd);
  839. cmdPwdClose(pwd);
  840. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  841. // определить длину сертификата
  842. code = cmdFileReadAll(0, &cert_len, argv[1]);
  843. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  844. // прочитать сертификат
  845. code = cmdBlobCreate(cert, cert_len);
  846. ERR_CALL_HANDLE(code, cmdBlobClose(privkey));
  847. code = cmdFileReadAll(cert, &cert_len, argv[1]);
  848. ERR_CALL_HANDLE(code, (cmdBlobClose(privkey), cmdBlobClose(cert)));
  849. // проверить соответствие
  850. code = btokCVCMatch(cert, cert_len, privkey, privkey_len);
  851. // завершить
  852. cmdBlobClose(cert);
  853. cmdBlobClose(privkey);
  854. return code;
  855. }
  856. /*
  857. *******************************************************************************
  858. Извлечение открытого ключа
  859. cvc extr <cert> <pubkey>
  860. *******************************************************************************
  861. */
  862. static err_t cvcExtr(int argc, char* argv[])
  863. {
  864. err_t code;
  865. size_t cert_len;
  866. void* stack;
  867. octet* cert;
  868. btok_cvc_t* cvc;
  869. // обработать опции
  870. if (argc != 2)
  871. return ERR_CMD_PARAMS;
  872. // проверить наличие/отсутствие файлов
  873. code = cmdFileValExist(1, argv);
  874. ERR_CALL_CHECK(code);
  875. code = cmdFileValNotExist(1, argv + 1);
  876. ERR_CALL_CHECK(code);
  877. // определить длину сертификата
  878. code = cmdFileReadAll(0, &cert_len, argv[0]);
  879. ERR_CALL_CHECK(code);
  880. // выделить память и разметить ее
  881. code = cmdBlobCreate(stack, cert_len + sizeof(btok_cvc_t));
  882. ERR_CALL_CHECK(code);
  883. cert = (octet*)stack;
  884. cvc = (btok_cvc_t*)(cert + cert_len);
  885. // прочитать сертификат
  886. code = cmdFileReadAll(cert, &cert_len, argv[0]);
  887. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  888. // разобрать сертификат
  889. code = btokCVCUnwrap(cvc, cert, cert_len, 0, 0);
  890. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  891. // сохранить открытый ключ
  892. code = cmdFileWrite(argv[1], cvc->pubkey, cvc->pubkey_len);
  893. // завершить
  894. cmdBlobClose(stack);
  895. return code;
  896. }
  897. /*
  898. *******************************************************************************
  899. Печать
  900. cvc print [-{authority|holder|from|until|eid|esign|pubkey|sig}] <cert>
  901. *******************************************************************************
  902. */
  903. static err_t cvcPrint(int argc, char* argv[])
  904. {
  905. err_t code;
  906. size_t cert_len;
  907. void* stack;
  908. octet* cert;
  909. btok_cvc_t* cvc;
  910. const char* scope = 0;
  911. // обработать опции
  912. if (argc < 1 || argc > 2)
  913. return ERR_CMD_PARAMS;
  914. if (argc == 2)
  915. {
  916. scope = argv[0];
  917. if (strLen(scope) < 1 || scope[0] != '-')
  918. return ERR_CMD_PARAMS;
  919. ++scope, --argc, ++argv;
  920. }
  921. // проверить наличие/отсутствие файлов
  922. code = cmdFileValExist(1, argv);
  923. ERR_CALL_CHECK(code);
  924. // определить длину сертификата
  925. code = cmdFileReadAll(0, &cert_len, argv[0]);
  926. ERR_CALL_CHECK(code);
  927. // выделить память и разметить ее
  928. code = cmdBlobCreate(stack, cert_len + sizeof(btok_cvc_t));
  929. ERR_CALL_CHECK(code);
  930. cert = (octet*)stack;
  931. cvc = (btok_cvc_t*)(cert + cert_len);
  932. // прочитать сертификат
  933. code = cmdFileReadAll(cert, &cert_len, argv[0]);
  934. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  935. // разобрать сертификат
  936. code = btokCVCUnwrap(cvc, cert, cert_len, 0, 0);
  937. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  938. // печатать содержимое
  939. code = cmdCVCPrint(cvc, scope);
  940. // завершить
  941. cmdBlobClose(stack);
  942. return code;
  943. }
  944. /*
  945. *******************************************************************************
  946. Главная функция
  947. *******************************************************************************
  948. */
  949. int cvcMain(int argc, char* argv[])
  950. {
  951. err_t code;
  952. // справка
  953. if (argc < 2)
  954. return cvcUsage();
  955. // разбор команды
  956. ++argv, --argc;
  957. if (strEq(argv[0], "root"))
  958. code = cvcRoot(argc - 1, argv + 1);
  959. else if (strEq(argv[0], "req"))
  960. code = cvcReq(argc - 1, argv + 1);
  961. else if (strEq(argv[0], "iss"))
  962. code = cvcIss(argc - 1, argv + 1);
  963. else if (strEq(argv[0], "shorten"))
  964. code = cvcShorten(argc - 1, argv + 1);
  965. else if (strEq(argv[0], "val"))
  966. code = cvcVal(argc - 1, argv + 1);
  967. else if (strEq(argv[0], "match"))
  968. code = cvcMatch(argc - 1, argv + 1);
  969. else if (strEq(argv[0], "extr"))
  970. code = cvcExtr(argc - 1, argv + 1);
  971. else if (strEq(argv[0], "print"))
  972. code = cvcPrint(argc - 1, argv + 1);
  973. else
  974. code = ERR_CMD_NOT_FOUND;
  975. // завершить
  976. if (code != ERR_OK || strEq(argv[0], "val") || strEq(argv[0], "match"))
  977. printf("bee2cmd/%s: %s\n", _name, errMsg(code));
  978. return code != ERR_OK ? -1 : 0;
  979. }
  980. /*
  981. *******************************************************************************
  982. Инициализация
  983. *******************************************************************************
  984. */
  985. err_t cvcInit()
  986. {
  987. return cmdReg(_name, _descr, cvcMain);
  988. }