aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/boringssl/src/tool/digest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/boringssl/src/tool/digest.cc')
-rw-r--r--third_party/boringssl/src/tool/digest.cc476
1 files changed, 476 insertions, 0 deletions
diff --git a/third_party/boringssl/src/tool/digest.cc b/third_party/boringssl/src/tool/digest.cc
new file mode 100644
index 0000000000..7cd88278f0
--- /dev/null
+++ b/third_party/boringssl/src/tool/digest.cc
@@ -0,0 +1,476 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/base.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <string.h>
+#include <unistd.h>
+#if !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+#else
+#pragma warning(push, 3)
+#include <windows.h>
+#pragma warning(pop)
+#include <io.h>
+#define PATH_MAX MAX_PATH
+typedef int ssize_t;
+#endif
+
+#include <openssl/digest.h>
+
+
+struct close_delete {
+ void operator()(int *fd) {
+ close(*fd);
+ }
+};
+
+template<typename T, typename R, R (*func) (T*)>
+struct func_delete {
+ void operator()(T* obj) {
+ func(obj);
+ }
+};
+
+// Source is an awkward expression of a union type in C++: Stdin | File filename.
+struct Source {
+ enum Type {
+ STDIN,
+ };
+
+ Source() : is_stdin_(false) {}
+ Source(Type) : is_stdin_(true) {}
+ Source(const std::string &name) : is_stdin_(false), filename_(name) {}
+
+ bool is_stdin() const { return is_stdin_; }
+ const std::string &filename() const { return filename_; }
+
+ private:
+ bool is_stdin_;
+ std::string filename_;
+};
+
+static const char kStdinName[] = "standard input";
+
+// OpenFile opens the regular file named |filename| and sets |*out_fd| to be a
+// file descriptor to it. Returns true on sucess or prints an error to stderr
+// and returns false on error.
+static bool OpenFile(int *out_fd, const std::string &filename) {
+ *out_fd = -1;
+
+ int fd = open(filename.c_str(), O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
+ strerror(errno));
+ return false;
+ }
+ std::unique_ptr<int, close_delete> scoped_fd(&fd);
+
+#if !defined(OPENSSL_WINDOWS)
+ struct stat st;
+ if (fstat(fd, &st)) {
+ fprintf(stderr, "Failed to stat input file '%s': %s\n", filename.c_str(),
+ strerror(errno));
+ return false;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ fprintf(stderr, "%s: not a regular file\n", filename.c_str());
+ return false;
+ }
+#endif
+
+ *out_fd = fd;
+ scoped_fd.release();
+ return true;
+}
+
+// SumFile hashes the contents of |source| with |md| and sets |*out_hex| to the
+// hex-encoded result.
+//
+// It returns true on success or prints an error to stderr and returns false on
+// error.
+static bool SumFile(std::string *out_hex, const EVP_MD *md,
+ const Source &source) {
+ std::unique_ptr<int, close_delete> scoped_fd;
+ int fd;
+
+ if (source.is_stdin()) {
+ fd = 0;
+ } else {
+ if (!OpenFile(&fd, source.filename())) {
+ return false;
+ }
+ scoped_fd.reset(&fd);
+ }
+
+ static const size_t kBufSize = 8192;
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
+
+ EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
+ std::unique_ptr<EVP_MD_CTX, func_delete<EVP_MD_CTX, int, EVP_MD_CTX_cleanup>>
+ scoped_ctx(&ctx);
+
+ if (!EVP_DigestInit_ex(&ctx, md, NULL)) {
+ fprintf(stderr, "Failed to initialize EVP_MD_CTX.\n");
+ return false;
+ }
+
+ for (;;) {
+ ssize_t n;
+
+ do {
+ n = read(fd, buf.get(), kBufSize);
+ } while (n == -1 && errno == EINTR);
+
+ if (n == 0) {
+ break;
+ } else if (n < 0) {
+ fprintf(stderr, "Failed to read from %s: %s\n",
+ source.is_stdin() ? kStdinName : source.filename().c_str(),
+ strerror(errno));
+ return false;
+ }
+
+ if (!EVP_DigestUpdate(&ctx, buf.get(), n)) {
+ fprintf(stderr, "Failed to update hash.\n");
+ return false;
+ }
+ }
+
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ unsigned digest_len;
+ if (!EVP_DigestFinal_ex(&ctx, digest, &digest_len)) {
+ fprintf(stderr, "Failed to finish hash.\n");
+ return false;
+ }
+
+ char hex_digest[EVP_MAX_MD_SIZE * 2];
+ static const char kHextable[] = "0123456789abcdef";
+ for (unsigned i = 0; i < digest_len; i++) {
+ const uint8_t b = digest[i];
+ hex_digest[i * 2] = kHextable[b >> 4];
+ hex_digest[i * 2 + 1] = kHextable[b & 0xf];
+ }
+ *out_hex = std::string(hex_digest, digest_len * 2);
+
+ return true;
+}
+
+// PrintFileSum hashes |source| with |md| and prints a line to stdout in the
+// format of the coreutils *sum utilities. It returns true on success or prints
+// an error to stderr and returns false on error.
+static bool PrintFileSum(const EVP_MD *md, const Source &source) {
+ std::string hex_digest;
+ if (!SumFile(&hex_digest, md, source)) {
+ return false;
+ }
+
+ // TODO: When given "--binary" or "-b", we should print " *" instead of " "
+ // between the digest and the filename.
+ //
+ // MSYS and Cygwin md5sum default to binary mode by default, whereas other
+ // platforms' tools default to text mode by default. We default to text mode
+ // by default and consider text mode equivalent to binary mode (i.e. we
+ // always use Unix semantics, even on Windows), which means that our default
+ // output will differ from the MSYS and Cygwin tools' default output.
+ printf("%s %s\n", hex_digest.c_str(),
+ source.is_stdin() ? "-" : source.filename().c_str());
+ return true;
+}
+
+// CheckModeArguments contains arguments for the check mode. See the
+// sha256sum(1) man page for details.
+struct CheckModeArguments {
+ bool quiet = false;
+ bool status = false;
+ bool warn = false;
+ bool strict = false;
+};
+
+// Check reads lines from |source| where each line is in the format of the
+// coreutils *sum utilities. It attempts to verify each hash by reading the
+// file named in the line.
+//
+// It returns true if all files were verified and, if |args.strict|, no input
+// lines had formatting errors. Otherwise it prints errors to stderr and
+// returns false.
+static bool Check(const CheckModeArguments &args, const EVP_MD *md,
+ const Source &source) {
+ std::unique_ptr<FILE, func_delete<FILE, int, fclose>> scoped_file;
+ FILE *file;
+
+ if (source.is_stdin()) {
+ file = stdin;
+ } else {
+ int fd;
+ if (!OpenFile(&fd, source.filename())) {
+ return false;
+ }
+
+ file = fdopen(fd, "rb");
+ if (!file) {
+ perror("fdopen");
+ close(fd);
+ return false;
+ }
+
+ scoped_file = std::unique_ptr<FILE, func_delete<FILE, int, fclose>>(file);
+ }
+
+ const size_t hex_size = EVP_MD_size(md) * 2;
+ char line[EVP_MAX_MD_SIZE * 2 + 2 /* spaces */ + PATH_MAX + 1 /* newline */ +
+ 1 /* NUL */];
+ unsigned bad_lines = 0;
+ unsigned parsed_lines = 0;
+ unsigned error_lines = 0;
+ unsigned bad_hash_lines = 0;
+ unsigned line_no = 0;
+ bool ok = true;
+ bool draining_overlong_line = false;
+
+ for (;;) {
+ line_no++;
+
+ if (fgets(line, sizeof(line), file) == nullptr) {
+ if (feof(file)) {
+ break;
+ }
+ fprintf(stderr, "Error reading from input.\n");
+ return false;
+ }
+
+ size_t len = strlen(line);
+
+ if (draining_overlong_line) {
+ if (line[len - 1] == '\n') {
+ draining_overlong_line = false;
+ }
+ continue;
+ }
+
+ const bool overlong = line[len - 1] != '\n' && !feof(file);
+
+ if (len < hex_size + 2 /* spaces */ + 1 /* filename */ ||
+ line[hex_size] != ' ' ||
+ line[hex_size + 1] != ' ' ||
+ overlong) {
+ bad_lines++;
+ if (args.warn) {
+ fprintf(stderr, "%s: %u: improperly formatted line\n",
+ source.is_stdin() ? kStdinName : source.filename().c_str(), line_no);
+ }
+ if (args.strict) {
+ ok = false;
+ }
+ if (overlong) {
+ draining_overlong_line = true;
+ }
+ continue;
+ }
+
+ if (line[len - 1] == '\n') {
+ line[len - 1] = 0;
+ len--;
+ }
+
+ parsed_lines++;
+
+ // coreutils does not attempt to restrict relative or absolute paths in the
+ // input so nor does this code.
+ std::string calculated_hex_digest;
+ const std::string target_filename(&line[hex_size + 2]);
+ Source target_source;
+ if (target_filename == "-") {
+ // coreutils reads from stdin if the filename is "-".
+ target_source = Source(Source::STDIN);
+ } else {
+ target_source = Source(target_filename);
+ }
+
+ if (!SumFile(&calculated_hex_digest, md, target_source)) {
+ error_lines++;
+ ok = false;
+ continue;
+ }
+
+ if (calculated_hex_digest != std::string(line, hex_size)) {
+ bad_hash_lines++;
+ if (!args.status) {
+ printf("%s: FAILED\n", target_filename.c_str());
+ }
+ ok = false;
+ continue;
+ }
+
+ if (!args.quiet) {
+ printf("%s: OK\n", target_filename.c_str());
+ }
+ }
+
+ if (!args.status) {
+ if (bad_lines > 0 && parsed_lines > 0) {
+ fprintf(stderr, "WARNING: %u line%s improperly formatted\n", bad_lines,
+ bad_lines == 1 ? " is" : "s are");
+ }
+ if (error_lines > 0) {
+ fprintf(stderr, "WARNING: %u computed checksum(s) did NOT match\n",
+ error_lines);
+ }
+ }
+
+ if (parsed_lines == 0) {
+ fprintf(stderr, "%s: no properly formatted checksum lines found.\n",
+ source.is_stdin() ? kStdinName : source.filename().c_str());
+ ok = false;
+ }
+
+ return ok;
+}
+
+// DigestSum acts like the coreutils *sum utilites, with the given hash
+// function.
+static bool DigestSum(const EVP_MD *md,
+ const std::vector<std::string> &args) {
+ bool check_mode = false;
+ CheckModeArguments check_args;
+ bool check_mode_args_given = false;
+ std::vector<Source> sources;
+
+ auto it = args.begin();
+ while (it != args.end()) {
+ const std::string &arg = *it;
+ if (!arg.empty() && arg[0] != '-') {
+ break;
+ }
+
+ it++;
+
+ if (arg == "--") {
+ break;
+ }
+
+ if (arg == "-") {
+ // "-" ends the argument list and indicates that stdin should be used.
+ sources.push_back(Source(Source::STDIN));
+ break;
+ }
+
+ if (arg.size() >= 2 && arg[0] == '-' && arg[1] != '-') {
+ for (size_t i = 1; i < arg.size(); i++) {
+ switch (arg[i]) {
+ case 'b':
+ case 't':
+ // Binary/text mode – irrelevent, even on Windows.
+ break;
+ case 'c':
+ check_mode = true;
+ break;
+ case 'w':
+ check_mode_args_given = true;
+ check_args.warn = true;
+ break;
+ default:
+ fprintf(stderr, "Unknown option '%c'.\n", arg[i]);
+ return false;
+ }
+ }
+ } else if (arg == "--binary" || arg == "--text") {
+ // Binary/text mode – irrelevent, even on Windows.
+ } else if (arg == "--check") {
+ check_mode = true;
+ } else if (arg == "--quiet") {
+ check_mode_args_given = true;
+ check_args.quiet = true;
+ } else if (arg == "--status") {
+ check_mode_args_given = true;
+ check_args.status = true;
+ } else if (arg == "--warn") {
+ check_mode_args_given = true;
+ check_args.warn = true;
+ } else if (arg == "--strict") {
+ check_mode_args_given = true;
+ check_args.strict = true;
+ } else {
+ fprintf(stderr, "Unknown option '%s'.\n", arg.c_str());
+ return false;
+ }
+ }
+
+ if (check_mode_args_given && !check_mode) {
+ fprintf(
+ stderr,
+ "Check mode arguments are only meaningful when verifying checksums.\n");
+ return false;
+ }
+
+ for (; it != args.end(); it++) {
+ sources.push_back(Source(*it));
+ }
+
+ if (sources.empty()) {
+ sources.push_back(Source(Source::STDIN));
+ }
+
+ bool ok = true;
+
+ if (check_mode) {
+ for (auto &source : sources) {
+ ok &= Check(check_args, md, source);
+ }
+ } else {
+ for (auto &source : sources) {
+ ok &= PrintFileSum(md, source);
+ }
+ }
+
+ return ok;
+}
+
+bool MD5Sum(const std::vector<std::string> &args) {
+ return DigestSum(EVP_md5(), args);
+}
+
+bool SHA1Sum(const std::vector<std::string> &args) {
+ return DigestSum(EVP_sha1(), args);
+}
+
+bool SHA224Sum(const std::vector<std::string> &args) {
+ return DigestSum(EVP_sha224(), args);
+}
+
+bool SHA256Sum(const std::vector<std::string> &args) {
+ return DigestSum(EVP_sha256(), args);
+}
+
+bool SHA384Sum(const std::vector<std::string> &args) {
+ return DigestSum(EVP_sha384(), args);
+}
+
+bool SHA512Sum(const std::vector<std::string> &args) {
+ return DigestSum(EVP_sha512(), args);
+}