You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

451 lines
12 KiB

  1. #include "px5g-openssl.hpp"
  2. #include <array>
  3. #include <iostream>
  4. #include <string>
  5. #include <string_view>
  6. #include <unistd.h>
  7. class argv_view { // TODO(pst): use std::span when available.
  8. private:
  9. std::basic_string_view<const char *> data;
  10. public:
  11. argv_view(const argv_view &) = delete;
  12. argv_view(argv_view &&) = delete;
  13. auto operator=(const argv_view &) -> argv_view & = delete;
  14. auto operator=(argv_view &&) -> argv_view & = delete;
  15. argv_view(const char ** argv, int argc) :
  16. data{argv, static_cast<size_t>(argc)} {}
  17. inline auto operator[] (size_t pos) const -> std::string_view
  18. { return std::string_view{data[pos]}; }
  19. [[nodiscard]] inline constexpr auto size() const noexcept -> size_t
  20. { return data.size(); }
  21. ~argv_view() = default;
  22. };
  23. static const auto default_validity = 30;
  24. auto checkend(const argv_view & argv) -> int;
  25. void eckey(const argv_view & argv);
  26. void rsakey(const argv_view & argv);
  27. void selfsigned(const argv_view & argv);
  28. inline auto parse_int(const std::string_view & arg) -> int
  29. {
  30. size_t pos;
  31. int ret = stoi(std::string{arg}, &pos);
  32. if (pos < arg.size()) {
  33. throw std::runtime_error("number has trailing char");
  34. }
  35. return ret;
  36. }
  37. inline auto parse_curve(const std::string_view & name) -> int
  38. {
  39. if (name=="P-384") { return NID_secp384r1; }
  40. if (name=="P-521") { return NID_secp521r1; }
  41. if (name=="P-256" || name=="secp256r1") { return NID_X9_62_prime256v1; }
  42. if (name=="secp192r1") { return NID_X9_62_prime192v1; }
  43. return OBJ_sn2nid(name.data());
  44. // not: if (curve == 0) { curve = EC_curve_nist2nid(name.c_str()); }
  45. }
  46. auto checkend(const argv_view & argv) -> int
  47. {
  48. bool use_pem = true;
  49. std::string crtpath{};
  50. time_t seconds = 0;
  51. for (size_t i=2; i<argv.size(); ++i) {
  52. if (argv[i]=="-der") {
  53. use_pem = false;
  54. } else if (argv[i]=="-in") {
  55. ++i;
  56. if (i >= argv.size()) {
  57. throw std::runtime_error("checkend error: -in misses filename");
  58. }
  59. if (!crtpath.empty()) {
  60. if (argv[i] == crtpath) {
  61. std::cerr<<"checkend warning: repeated same -in file\n";
  62. } else {
  63. throw std::runtime_error
  64. ("checkend error: more than one -in file");
  65. }
  66. }
  67. crtpath = argv[i];
  68. }
  69. else if (argv[i][0]=='-') {
  70. std::cerr<<"checkend warning: skipping option "<<argv[i]<<std::endl;
  71. } else { // main option:
  72. intmax_t num = 0;
  73. try {
  74. num = parse_int(argv[i]);
  75. } catch (...) {
  76. auto errmsg = std::string{"checkend error: invalid time "};
  77. errmsg += argv[i];
  78. std::throw_with_nested(std::runtime_error(errmsg));
  79. }
  80. seconds = static_cast<time_t>(num);
  81. if (num!=static_cast<intmax_t>(seconds)) {
  82. auto errmsg = std::string{"checkend error: time too big "};
  83. errmsg += argv[i];
  84. throw std::runtime_error(errmsg);
  85. }
  86. }
  87. }
  88. bool valid = checkend(crtpath, seconds, use_pem);
  89. std::cout<<"Certificate will"<<(valid ? " not " : " ")<<"expire"<<std::endl;
  90. return (valid ? 0 : 1);
  91. }
  92. void eckey(const argv_view & argv)
  93. {
  94. bool has_main_option = false;
  95. bool use_pem = true;
  96. std::string keypath{};
  97. int curve = NID_X9_62_prime256v1;
  98. for (size_t i=2; i < argv.size(); ++i) {
  99. if (argv[i]=="-der") {
  100. use_pem = false;
  101. } else if (argv[i]=="-out") {
  102. ++i;
  103. if (i >= argv.size()) {
  104. throw std::runtime_error("eckey error: -out misses filename");
  105. }
  106. if (!keypath.empty()) {
  107. if (argv[i]==keypath) {
  108. std::cerr<<"eckey warning: repeated same -out file\n";
  109. } else {
  110. throw std::runtime_error
  111. ("eckey error: more than one -out file");
  112. }
  113. }
  114. keypath = argv[i];
  115. }
  116. else if (argv[i][0]=='-') {
  117. std::cerr<<"eckey warning: skipping option "<<argv[i]<<std::endl;
  118. } else { //main option:
  119. if (has_main_option) {
  120. throw std::runtime_error
  121. ("eckey error: more than one main option");
  122. } //else:
  123. has_main_option = true;
  124. curve = parse_curve(argv[i]);
  125. }
  126. }
  127. write_key(gen_eckey(curve), keypath, use_pem);
  128. }
  129. void rsakey(const argv_view & argv)
  130. {
  131. bool has_main_option = false;
  132. bool use_pem = true;
  133. std::string keypath{};
  134. BN_ULONG exponent = RSA_F4;
  135. int keysize = rsa_min_modulus_bits;
  136. for (size_t i=2; i < argv.size(); ++i) {
  137. if (argv[i]=="-der") {
  138. use_pem = false;
  139. } else if (argv[i]=="-3") {
  140. exponent = 3;
  141. } else if (argv[i]=="-out") {
  142. ++i;
  143. if (i >= argv.size()) {
  144. throw std::runtime_error("rsakey error: -out misses filename");
  145. }
  146. if (!keypath.empty()) {
  147. if (argv[i]==keypath) {
  148. std::cerr<<"rsakey warning: repeated -out file"<<std::endl;
  149. } else {
  150. throw std::runtime_error
  151. ("rsakey error: more than one -out file");
  152. }
  153. }
  154. keypath = argv[i];
  155. }
  156. else if (argv[i][0]=='-') {
  157. std::cerr<<"rsakey warning: skipping option "<<argv[i]<<std::endl;
  158. } else { //main option:
  159. if (has_main_option) {
  160. throw std::runtime_error("rsakey error: more than one keysize");
  161. } //else:
  162. has_main_option = true;
  163. try {
  164. keysize = parse_int(argv[i]);
  165. } catch (...) {
  166. std::string errmsg{"rsakey error: invalid keysize "};
  167. errmsg += argv[i];
  168. std::throw_with_nested(std::runtime_error(errmsg));
  169. }
  170. }
  171. }
  172. write_key(gen_rsakey(keysize, exponent), keypath, use_pem);
  173. }
  174. void selfsigned(const argv_view & argv)
  175. {
  176. bool use_pem = true;
  177. int days = default_validity;
  178. std::string keypath{};
  179. std::string crtpath{};
  180. std::string subject{};
  181. bool use_rsa = true;
  182. int keysize = rsa_min_modulus_bits;
  183. BN_ULONG exponent = RSA_F4;
  184. int curve = NID_X9_62_prime256v1;
  185. for (size_t i=2; i < argv.size(); ++i) {
  186. if (argv[i]=="-der") {
  187. use_pem = false;
  188. } else if (argv[i]=="-days") {
  189. ++i;
  190. try {
  191. days = parse_int(argv[i]);
  192. } catch (...) {
  193. std::string errmsg{"selfsigned error: not a number for -days "};
  194. errmsg += argv[i].substr(4);
  195. std::throw_with_nested(std::runtime_error(errmsg));
  196. }
  197. }
  198. else if (argv[i]=="-newkey") {
  199. ++i;
  200. if (i >= argv.size()) {
  201. throw std::runtime_error
  202. ("selfsigned error: -newkey misses algorithm option");
  203. }
  204. static constexpr auto rsa_prefix = std::string_view{"rsa:"};
  205. if (argv[i]=="ec") {
  206. use_rsa = false;
  207. } else if (argv[i].rfind(rsa_prefix, 0) == 0) {
  208. use_rsa = true;
  209. try {
  210. keysize = parse_int(argv[i].substr(rsa_prefix.size()));
  211. } catch (...) {
  212. std::string errmsg{"selfsigned error: invalid keysize "};
  213. errmsg += argv[i].substr(4);
  214. std::throw_with_nested(std::runtime_error(errmsg));
  215. }
  216. } else {
  217. throw std::runtime_error("selfsigned error: invalid algorithm");
  218. }
  219. }
  220. else if (argv[i]=="-pkeyopt") {
  221. ++i;
  222. if (i >= argv.size()) {
  223. throw std::runtime_error
  224. ("selfsigned error: -pkeyopt misses value");
  225. }
  226. static constexpr auto curve_prefix =
  227. std::string_view{"ec_paramgen_curve:"};
  228. if (argv[i].rfind(curve_prefix, 0) != 0) {
  229. throw std::runtime_error("selfsigned error: -pkeyopt invalid");
  230. }
  231. curve = parse_curve(argv[i].substr(curve_prefix.size()));
  232. }
  233. else if (argv[i]=="-keyout") {
  234. ++i;
  235. if (i >= argv.size()) {
  236. throw std::runtime_error
  237. ("selfsigned error: -keyout misses path");
  238. }
  239. if (!keypath.empty()) {
  240. if (argv[i]==keypath) {
  241. std::cerr<<"selfsigned warning: repeated -keyout file\n";
  242. } else {
  243. throw std::runtime_error
  244. ("selfsigned error: more than one -keyout file");
  245. }
  246. }
  247. keypath = argv[i];
  248. }
  249. else if (argv[i]=="-out") {
  250. ++i;
  251. if (i >= argv.size()) {
  252. throw std::runtime_error
  253. ("selfsigned error: -out misses filename");
  254. }
  255. if (!crtpath.empty()) {
  256. if (argv[i]==crtpath) {
  257. std::cerr<<"selfsigned warning: repeated same -out file\n";
  258. } else {
  259. throw std::runtime_error
  260. ("selfsigned error: more than one -out file");
  261. }
  262. }
  263. crtpath = argv[i];
  264. }
  265. else if (argv[i]=="-subj") {
  266. ++i;
  267. if (i >= argv.size()) {
  268. throw std::runtime_error
  269. ("selfsigned error: -subj misses value");
  270. }
  271. if (!subject.empty()) {
  272. if (argv[i]==subject) {
  273. std::cerr<<"selfsigned warning: repeated same -subj\n";
  274. } else {
  275. throw std::runtime_error
  276. ("selfsigned error: more than one -subj value");
  277. }
  278. }
  279. subject = argv[i];
  280. }
  281. else {
  282. std::cerr<<"selfsigned warning: skipping option "<<argv[i]<<std::endl;
  283. }
  284. }
  285. auto pkey = use_rsa ? gen_rsakey(keysize, exponent) : gen_eckey(curve);
  286. selfsigned(pkey, days, subject, crtpath, use_pem);
  287. if (!keypath.empty()) { write_key(pkey, keypath, use_pem); }
  288. }
  289. auto main(int argc, const char ** argv) -> int
  290. {
  291. auto args = argv_view{argv, argc};
  292. auto cmds = std::array{
  293. std::array<std::string, 2>{"checkend",
  294. " [-der] [-in certificate_path] [seconds_remaining]"
  295. },
  296. std::array<std::string, 2>{"eckey",
  297. " [-der] [-out key_path] [curve_name]"
  298. },
  299. std::array<std::string, 2>{"rsakey",
  300. " [-der] [-out key_path] [-3] [key_size]"
  301. },
  302. std::array<std::string, 2>{"selfsigned",
  303. " [-der] [-keyout key_path] [-out certificate_path]"
  304. " [-newkey ec|rsa:key_size] [-pkeyopt ec_paramgen_curve:name]"
  305. " [-days validity] [-subj /C=.../ST=.../L=.../O=.../CN=.../... ]"
  306. },
  307. };
  308. try {
  309. if (argc < 2) { throw std::runtime_error("error: no argument"); }
  310. if (args[1]==cmds[0][0]) {return checkend(args);}
  311. if (args[1]==cmds[1][0]) { eckey(args); }
  312. else if (args[1]==cmds[2][0]) { rsakey(args); }
  313. else if (args[1]==cmds[3][0]) { selfsigned(args); }
  314. else { throw std::runtime_error("error: argument not recognized"); }
  315. }
  316. catch (const std::exception & e) {
  317. auto usage = std::string{"usage: \n"} ;
  318. for (auto cmd : cmds) {
  319. usage += std::string{4, ' '} + *argv +" "+ cmd[0] + cmd[1] +"\n";
  320. }
  321. std::cerr<<usage<<std::flush;
  322. auto print_nested =
  323. [](auto && self, const std::exception & outer, int depth=0) -> void
  324. {
  325. std::cerr<<std::string(depth, '\t')<<outer.what()<<std::endl;
  326. try { std::rethrow_if_nested(outer); }
  327. catch (const std::exception & inner) { self(self, inner, depth+1); }
  328. };
  329. print_nested(print_nested, e);
  330. return 1;
  331. }
  332. catch (...) {
  333. std::cerr<<*argv<<" unknown error."<<std::endl;
  334. return 2;
  335. }
  336. return 0;
  337. }