Add a basic signing tool.

This is mostly to make it easier for me to generate test Ed25519
certificates.

Change-Id: I45e42f556d949d62eb6cdf684194958fa9f909bf
Reviewed-on: https://boringssl-review.googlesource.com/14504
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt
index f0af283..244f7df 100644
--- a/tool/CMakeLists.txt
+++ b/tool/CMakeLists.txt
@@ -8,11 +8,13 @@
   client.cc
   const.cc
   digest.cc
+  file.cc
   generate_ed25519.cc
   genrsa.cc
   pkcs12.cc
   rand.cc
   server.cc
+  sign.cc
   speed.cc
   tool.cc
   transport_common.cc
diff --git a/tool/file.cc b/tool/file.cc
new file mode 100644
index 0000000..9b5ff1b
--- /dev/null
+++ b/tool/file.cc
@@ -0,0 +1,50 @@
+/* Copyright (c) 2017, 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 <stdio.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "internal.h"
+
+
+bool ReadAll(std::vector<uint8_t> *out, FILE *file) {
+  out->clear();
+
+  constexpr size_t kMaxSize = 1024 * 1024;
+  size_t len = 0;
+  out->resize(128);
+
+  for (;;) {
+    len += fread(out->data() + len, 1, out->size() - len, file);
+
+    if (feof(file)) {
+      out->resize(len);
+      return true;
+    }
+    if (ferror(file)) {
+      return false;
+    }
+
+    if (len == out->size()) {
+      if (out->size() == kMaxSize) {
+        fprintf(stderr, "Input too large.\n");
+        return false;
+      }
+      size_t cap = std::min(out->size() * 2, kMaxSize);
+      out->resize(cap);
+    }
+  }
+}
diff --git a/tool/internal.h b/tool/internal.h
index fd66e00..80c9dc9 100644
--- a/tool/internal.h
+++ b/tool/internal.h
@@ -65,6 +65,8 @@
                  unsigned default_value,
                  const std::map<std::string, std::string> &args);
 
+bool ReadAll(std::vector<uint8_t> *out, FILE *in);
+
 bool Ciphers(const std::vector<std::string> &args);
 bool Client(const std::vector<std::string> &args);
 bool DoPKCS12(const std::vector<std::string> &args);
@@ -78,6 +80,7 @@
 bool SHA384Sum(const std::vector<std::string> &args);
 bool SHA512Sum(const std::vector<std::string> &args);
 bool Server(const std::vector<std::string> &args);
+bool Sign(const std::vector<std::string> &args);
 bool Speed(const std::vector<std::string> &args);
 
 // These values are DER encoded, RSA private keys.
diff --git a/tool/server.cc b/tool/server.cc
index 1a97dff..13c7825 100644
--- a/tool/server.cc
+++ b/tool/server.cc
@@ -14,6 +14,8 @@
 
 #include <openssl/base.h>
 
+#include <memory>
+
 #include <openssl/err.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
@@ -61,44 +63,28 @@
     },
 };
 
+struct FileCloser {
+  void operator()(FILE *file) {
+    fclose(file);
+  }
+};
+
+using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
+
 static bool LoadOCSPResponse(SSL_CTX *ctx, const char *filename) {
-  void *data = NULL;
-  bool ret = false;
-  size_t bytes_read;
-  long length;
-
-  FILE *f = fopen(filename, "rb");
-
-  if (f == NULL ||
-      fseek(f, 0, SEEK_END) != 0) {
-    goto out;
+  ScopedFILE f(fopen(filename, "rb"));
+  std::vector<uint8_t> data;
+  if (f == nullptr ||
+      !ReadAll(&data, f.get())) {
+    fprintf(stderr, "Error reading %s.\n", filename);
+    return false;
   }
 
-  length = ftell(f);
-  if (length < 0) {
-    goto out;
+  if (!SSL_CTX_set_ocsp_response(ctx, data.data(), data.size())) {
+    return false;
   }
 
-  data = malloc(length);
-  if (data == NULL) {
-    goto out;
-  }
-  rewind(f);
-
-  bytes_read = fread(data, 1, length, f);
-  if (ferror(f) != 0 ||
-      bytes_read != (size_t)length ||
-      !SSL_CTX_set_ocsp_response(ctx, (uint8_t*)data, bytes_read)) {
-    goto out;
-  }
-
-  ret = true;
-out:
-  if (f != NULL) {
-      fclose(f);
-  }
-  free(data);
-  return ret;
+  return true;
 }
 
 static bssl::UniquePtr<EVP_PKEY> MakeKeyPairForSelfSignedCert() {
diff --git a/tool/sign.cc b/tool/sign.cc
new file mode 100644
index 0000000..74b84f1
--- /dev/null
+++ b/tool/sign.cc
@@ -0,0 +1,88 @@
+/* Copyright (c) 2017, 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 <map>
+#include <vector>
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+
+#include "internal.h"
+
+
+static const struct argument kArguments[] = {
+    {"-key", kRequiredArgument, "The private key, in PEM format, to sign with"},
+    {"-digest", kOptionalArgument, "The digest algorithm to use"},
+    {"", kOptionalArgument, ""},
+};
+
+bool Sign(const std::vector<std::string> &args) {
+  std::map<std::string, std::string> args_map;
+  if (!ParseKeyValueArguments(&args_map, args, kArguments)) {
+    PrintUsage(kArguments);
+    return false;
+  }
+
+  // Load the private key.
+  bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
+  if (!bio || !BIO_read_filename(bio.get(), args_map["-key"].c_str())) {
+    return false;
+  }
+  bssl::UniquePtr<EVP_PKEY> key(
+      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+  if (!key) {
+    return false;
+  }
+
+  // Setup the signing operation.
+  bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key.get(), nullptr));
+  if (!ctx ||
+      !EVP_PKEY_sign_init(ctx.get())) {
+    return false;
+  }
+
+  if (args_map.count("-digest")) {
+    const EVP_MD *md = EVP_get_digestbyname(args_map["-digest"].c_str());
+    if (md == nullptr) {
+      fprintf(stderr, "Unknown digest algorithm: %s\n",
+              args_map["-digest"].c_str());
+      return false;
+    }
+
+    if (!EVP_PKEY_CTX_set_signature_md(ctx.get(), md)) {
+      return false;
+    }
+  }
+
+  std::vector<uint8_t> data;
+  if (!ReadAll(&data, stdin)) {
+    fprintf(stderr, "Error reading input.\n");
+    return false;
+  }
+
+  size_t sig_len = EVP_PKEY_size(key.get());
+  std::unique_ptr<uint8_t[]> sig(new uint8_t[sig_len]);
+  if (!EVP_PKEY_sign_message(ctx.get(), sig.get(), &sig_len, data.data(),
+                             data.size())) {
+    return false;
+  }
+
+  if (fwrite(sig.get(), 1, sig_len, stdout) != sig_len) {
+    fprintf(stderr, "Error writing signature.\n");
+    return false;
+  }
+
+  return true;
+}
diff --git a/tool/tool.cc b/tool/tool.cc
index 34851b4..d698561 100644
--- a/tool/tool.cc
+++ b/tool/tool.cc
@@ -52,6 +52,7 @@
   { "sha256sum", SHA256Sum },
   { "sha384sum", SHA384Sum },
   { "sha512sum", SHA512Sum },
+  { "sign", Sign },
   { "speed", Speed },
   { "", nullptr },
 };
@@ -122,5 +123,10 @@
     args.push_back(argv[i]);
   }
 
-  return !tool(args);
+  if (!tool(args)) {
+    ERR_print_errors_fp(stderr);
+    return 1;
+  }
+
+  return 0;
 }