digest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /* Copyright (c) 2014, Google Inc.
  2. *
  3. * Permission to use, copy, modify, and/or distribute this software for any
  4. * purpose with or without fee is hereby granted, provided that the above
  5. * copyright notice and this permission notice appear in all copies.
  6. *
  7. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  14. #include <openssl/base.h>
  15. #include <memory>
  16. #include <string>
  17. #include <vector>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <limits.h>
  21. #include <stdio.h>
  22. #include <sys/stat.h>
  23. #include <sys/types.h>
  24. #if !defined(OPENSSL_WINDOWS)
  25. #include <string.h>
  26. #include <unistd.h>
  27. #if !defined(O_BINARY)
  28. #define O_BINARY 0
  29. #endif
  30. #else
  31. OPENSSL_MSVC_PRAGMA(warning(push, 3))
  32. #include <windows.h>
  33. OPENSSL_MSVC_PRAGMA(warning(pop))
  34. #include <io.h>
  35. #define PATH_MAX MAX_PATH
  36. typedef int ssize_t;
  37. #endif
  38. #include <openssl/digest.h>
  39. #include "internal.h"
  40. struct close_delete {
  41. void operator()(int *fd) {
  42. BORINGSSL_CLOSE(*fd);
  43. }
  44. };
  45. template<typename T, typename R, R (*func) (T*)>
  46. struct func_delete {
  47. void operator()(T* obj) {
  48. func(obj);
  49. }
  50. };
  51. // Source is an awkward expression of a union type in C++: Stdin | File filename.
  52. struct Source {
  53. enum Type {
  54. STDIN,
  55. };
  56. Source() : is_stdin_(false) {}
  57. explicit Source(Type) : is_stdin_(true) {}
  58. explicit Source(const std::string &name)
  59. : is_stdin_(false), filename_(name) {}
  60. bool is_stdin() const { return is_stdin_; }
  61. const std::string &filename() const { return filename_; }
  62. private:
  63. bool is_stdin_;
  64. std::string filename_;
  65. };
  66. static const char kStdinName[] = "standard input";
  67. // OpenFile opens the regular file named |filename| and sets |*out_fd| to be a
  68. // file descriptor to it. Returns true on sucess or prints an error to stderr
  69. // and returns false on error.
  70. static bool OpenFile(int *out_fd, const std::string &filename) {
  71. *out_fd = -1;
  72. int fd = BORINGSSL_OPEN(filename.c_str(), O_RDONLY | O_BINARY);
  73. if (fd < 0) {
  74. fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
  75. strerror(errno));
  76. return false;
  77. }
  78. std::unique_ptr<int, close_delete> scoped_fd(&fd);
  79. #if !defined(OPENSSL_WINDOWS)
  80. struct stat st;
  81. if (fstat(fd, &st)) {
  82. fprintf(stderr, "Failed to stat input file '%s': %s\n", filename.c_str(),
  83. strerror(errno));
  84. return false;
  85. }
  86. if (!S_ISREG(st.st_mode)) {
  87. fprintf(stderr, "%s: not a regular file\n", filename.c_str());
  88. return false;
  89. }
  90. #endif
  91. *out_fd = fd;
  92. scoped_fd.release();
  93. return true;
  94. }
  95. // SumFile hashes the contents of |source| with |md| and sets |*out_hex| to the
  96. // hex-encoded result.
  97. //
  98. // It returns true on success or prints an error to stderr and returns false on
  99. // error.
  100. static bool SumFile(std::string *out_hex, const EVP_MD *md,
  101. const Source &source) {
  102. std::unique_ptr<int, close_delete> scoped_fd;
  103. int fd;
  104. if (source.is_stdin()) {
  105. fd = 0;
  106. } else {
  107. if (!OpenFile(&fd, source.filename())) {
  108. return false;
  109. }
  110. scoped_fd.reset(&fd);
  111. }
  112. static const size_t kBufSize = 8192;
  113. std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
  114. bssl::ScopedEVP_MD_CTX ctx;
  115. if (!EVP_DigestInit_ex(ctx.get(), md, NULL)) {
  116. fprintf(stderr, "Failed to initialize EVP_MD_CTX.\n");
  117. return false;
  118. }
  119. for (;;) {
  120. ssize_t n;
  121. do {
  122. n = BORINGSSL_READ(fd, buf.get(), kBufSize);
  123. } while (n == -1 && errno == EINTR);
  124. if (n == 0) {
  125. break;
  126. } else if (n < 0) {
  127. fprintf(stderr, "Failed to read from %s: %s\n",
  128. source.is_stdin() ? kStdinName : source.filename().c_str(),
  129. strerror(errno));
  130. return false;
  131. }
  132. if (!EVP_DigestUpdate(ctx.get(), buf.get(), n)) {
  133. fprintf(stderr, "Failed to update hash.\n");
  134. return false;
  135. }
  136. }
  137. uint8_t digest[EVP_MAX_MD_SIZE];
  138. unsigned digest_len;
  139. if (!EVP_DigestFinal_ex(ctx.get(), digest, &digest_len)) {
  140. fprintf(stderr, "Failed to finish hash.\n");
  141. return false;
  142. }
  143. char hex_digest[EVP_MAX_MD_SIZE * 2];
  144. static const char kHextable[] = "0123456789abcdef";
  145. for (unsigned i = 0; i < digest_len; i++) {
  146. const uint8_t b = digest[i];
  147. hex_digest[i * 2] = kHextable[b >> 4];
  148. hex_digest[i * 2 + 1] = kHextable[b & 0xf];
  149. }
  150. *out_hex = std::string(hex_digest, digest_len * 2);
  151. return true;
  152. }
  153. // PrintFileSum hashes |source| with |md| and prints a line to stdout in the
  154. // format of the coreutils *sum utilities. It returns true on success or prints
  155. // an error to stderr and returns false on error.
  156. static bool PrintFileSum(const EVP_MD *md, const Source &source) {
  157. std::string hex_digest;
  158. if (!SumFile(&hex_digest, md, source)) {
  159. return false;
  160. }
  161. // TODO: When given "--binary" or "-b", we should print " *" instead of " "
  162. // between the digest and the filename.
  163. //
  164. // MSYS and Cygwin md5sum default to binary mode by default, whereas other
  165. // platforms' tools default to text mode by default. We default to text mode
  166. // by default and consider text mode equivalent to binary mode (i.e. we
  167. // always use Unix semantics, even on Windows), which means that our default
  168. // output will differ from the MSYS and Cygwin tools' default output.
  169. printf("%s %s\n", hex_digest.c_str(),
  170. source.is_stdin() ? "-" : source.filename().c_str());
  171. return true;
  172. }
  173. // CheckModeArguments contains arguments for the check mode. See the
  174. // sha256sum(1) man page for details.
  175. struct CheckModeArguments {
  176. bool quiet = false;
  177. bool status = false;
  178. bool warn = false;
  179. bool strict = false;
  180. };
  181. // Check reads lines from |source| where each line is in the format of the
  182. // coreutils *sum utilities. It attempts to verify each hash by reading the
  183. // file named in the line.
  184. //
  185. // It returns true if all files were verified and, if |args.strict|, no input
  186. // lines had formatting errors. Otherwise it prints errors to stderr and
  187. // returns false.
  188. static bool Check(const CheckModeArguments &args, const EVP_MD *md,
  189. const Source &source) {
  190. std::unique_ptr<FILE, func_delete<FILE, int, fclose>> scoped_file;
  191. FILE *file;
  192. if (source.is_stdin()) {
  193. file = stdin;
  194. } else {
  195. int fd;
  196. if (!OpenFile(&fd, source.filename())) {
  197. return false;
  198. }
  199. file = BORINGSSL_FDOPEN(fd, "rb");
  200. if (!file) {
  201. perror("fdopen");
  202. BORINGSSL_CLOSE(fd);
  203. return false;
  204. }
  205. scoped_file = std::unique_ptr<FILE, func_delete<FILE, int, fclose>>(file);
  206. }
  207. const size_t hex_size = EVP_MD_size(md) * 2;
  208. char line[EVP_MAX_MD_SIZE * 2 + 2 /* spaces */ + PATH_MAX + 1 /* newline */ +
  209. 1 /* NUL */];
  210. unsigned bad_lines = 0;
  211. unsigned parsed_lines = 0;
  212. unsigned error_lines = 0;
  213. unsigned bad_hash_lines = 0;
  214. unsigned line_no = 0;
  215. bool ok = true;
  216. bool draining_overlong_line = false;
  217. for (;;) {
  218. line_no++;
  219. if (fgets(line, sizeof(line), file) == nullptr) {
  220. if (feof(file)) {
  221. break;
  222. }
  223. fprintf(stderr, "Error reading from input.\n");
  224. return false;
  225. }
  226. size_t len = strlen(line);
  227. if (draining_overlong_line) {
  228. if (line[len - 1] == '\n') {
  229. draining_overlong_line = false;
  230. }
  231. continue;
  232. }
  233. const bool overlong = line[len - 1] != '\n' && !feof(file);
  234. if (len < hex_size + 2 /* spaces */ + 1 /* filename */ ||
  235. line[hex_size] != ' ' ||
  236. line[hex_size + 1] != ' ' ||
  237. overlong) {
  238. bad_lines++;
  239. if (args.warn) {
  240. fprintf(stderr, "%s: %u: improperly formatted line\n",
  241. source.is_stdin() ? kStdinName : source.filename().c_str(), line_no);
  242. }
  243. if (args.strict) {
  244. ok = false;
  245. }
  246. if (overlong) {
  247. draining_overlong_line = true;
  248. }
  249. continue;
  250. }
  251. if (line[len - 1] == '\n') {
  252. line[len - 1] = 0;
  253. len--;
  254. }
  255. parsed_lines++;
  256. // coreutils does not attempt to restrict relative or absolute paths in the
  257. // input so nor does this code.
  258. std::string calculated_hex_digest;
  259. const std::string target_filename(&line[hex_size + 2]);
  260. Source target_source;
  261. if (target_filename == "-") {
  262. // coreutils reads from stdin if the filename is "-".
  263. target_source = Source(Source::STDIN);
  264. } else {
  265. target_source = Source(target_filename);
  266. }
  267. if (!SumFile(&calculated_hex_digest, md, target_source)) {
  268. error_lines++;
  269. ok = false;
  270. continue;
  271. }
  272. if (calculated_hex_digest != std::string(line, hex_size)) {
  273. bad_hash_lines++;
  274. if (!args.status) {
  275. printf("%s: FAILED\n", target_filename.c_str());
  276. }
  277. ok = false;
  278. continue;
  279. }
  280. if (!args.quiet) {
  281. printf("%s: OK\n", target_filename.c_str());
  282. }
  283. }
  284. if (!args.status) {
  285. if (bad_lines > 0 && parsed_lines > 0) {
  286. fprintf(stderr, "WARNING: %u line%s improperly formatted\n", bad_lines,
  287. bad_lines == 1 ? " is" : "s are");
  288. }
  289. if (error_lines > 0) {
  290. fprintf(stderr, "WARNING: %u computed checksum(s) did NOT match\n",
  291. error_lines);
  292. }
  293. }
  294. if (parsed_lines == 0) {
  295. fprintf(stderr, "%s: no properly formatted checksum lines found.\n",
  296. source.is_stdin() ? kStdinName : source.filename().c_str());
  297. ok = false;
  298. }
  299. return ok;
  300. }
  301. // DigestSum acts like the coreutils *sum utilites, with the given hash
  302. // function.
  303. static bool DigestSum(const EVP_MD *md,
  304. const std::vector<std::string> &args) {
  305. bool check_mode = false;
  306. CheckModeArguments check_args;
  307. bool check_mode_args_given = false;
  308. std::vector<Source> sources;
  309. auto it = args.begin();
  310. while (it != args.end()) {
  311. const std::string &arg = *it;
  312. if (!arg.empty() && arg[0] != '-') {
  313. break;
  314. }
  315. it++;
  316. if (arg == "--") {
  317. break;
  318. }
  319. if (arg == "-") {
  320. // "-" ends the argument list and indicates that stdin should be used.
  321. sources.push_back(Source(Source::STDIN));
  322. break;
  323. }
  324. if (arg.size() >= 2 && arg[0] == '-' && arg[1] != '-') {
  325. for (size_t i = 1; i < arg.size(); i++) {
  326. switch (arg[i]) {
  327. case 'b':
  328. case 't':
  329. // Binary/text mode – irrelevent, even on Windows.
  330. break;
  331. case 'c':
  332. check_mode = true;
  333. break;
  334. case 'w':
  335. check_mode_args_given = true;
  336. check_args.warn = true;
  337. break;
  338. default:
  339. fprintf(stderr, "Unknown option '%c'.\n", arg[i]);
  340. return false;
  341. }
  342. }
  343. } else if (arg == "--binary" || arg == "--text") {
  344. // Binary/text mode – irrelevent, even on Windows.
  345. } else if (arg == "--check") {
  346. check_mode = true;
  347. } else if (arg == "--quiet") {
  348. check_mode_args_given = true;
  349. check_args.quiet = true;
  350. } else if (arg == "--status") {
  351. check_mode_args_given = true;
  352. check_args.status = true;
  353. } else if (arg == "--warn") {
  354. check_mode_args_given = true;
  355. check_args.warn = true;
  356. } else if (arg == "--strict") {
  357. check_mode_args_given = true;
  358. check_args.strict = true;
  359. } else {
  360. fprintf(stderr, "Unknown option '%s'.\n", arg.c_str());
  361. return false;
  362. }
  363. }
  364. if (check_mode_args_given && !check_mode) {
  365. fprintf(
  366. stderr,
  367. "Check mode arguments are only meaningful when verifying checksums.\n");
  368. return false;
  369. }
  370. for (; it != args.end(); it++) {
  371. sources.push_back(Source(*it));
  372. }
  373. if (sources.empty()) {
  374. sources.push_back(Source(Source::STDIN));
  375. }
  376. bool ok = true;
  377. if (check_mode) {
  378. for (auto &source : sources) {
  379. ok &= Check(check_args, md, source);
  380. }
  381. } else {
  382. for (auto &source : sources) {
  383. ok &= PrintFileSum(md, source);
  384. }
  385. }
  386. return ok;
  387. }
  388. bool MD5Sum(const std::vector<std::string> &args) {
  389. return DigestSum(EVP_md5(), args);
  390. }
  391. bool SHA1Sum(const std::vector<std::string> &args) {
  392. return DigestSum(EVP_sha1(), args);
  393. }
  394. bool SHA224Sum(const std::vector<std::string> &args) {
  395. return DigestSum(EVP_sha224(), args);
  396. }
  397. bool SHA256Sum(const std::vector<std::string> &args) {
  398. return DigestSum(EVP_sha256(), args);
  399. }
  400. bool SHA384Sum(const std::vector<std::string> &args) {
  401. return DigestSum(EVP_sha384(), args);
  402. }
  403. bool SHA512Sum(const std::vector<std::string> &args) {
  404. return DigestSum(EVP_sha512(), args);
  405. }