Implements Signature Creator & Verifier for openssl
Also adds a little more infrastructure to assist in openssl error handling.

BUG=None
TEST=base_unittests RSA* and Sign*

Review URL: http://codereview.chromium.org/5105003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66622 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/crypto/encryptor_openssl.cc b/base/crypto/encryptor_openssl.cc
index 44ae932d..7e095457 100644
--- a/base/crypto/encryptor_openssl.cc
+++ b/base/crypto/encryptor_openssl.cc
@@ -34,7 +34,7 @@
   }
   ~ScopedCipherCTX() {
     EVP_CIPHER_CTX_cleanup(&ctx_);
-    ClearOpenSSLERRStack();
+    ClearOpenSSLERRStack(FROM_HERE);
   }
   EVP_CIPHER_CTX* get() { return &ctx_; }
 
diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h
index ea5daac..ecec015 100644
--- a/base/crypto/rsa_private_key.h
+++ b/base/crypto/rsa_private_key.h
@@ -204,7 +204,9 @@
 
   ~RSAPrivateKey();
 
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+  EVP_PKEY* key() { return key_; }
+#elif defined(USE_NSS)
   SECKEYPrivateKeyStr* key() { return key_; }
 #elif defined(OS_WIN)
   HCRYPTPROV provider() { return provider_; }
diff --git a/base/crypto/rsa_private_key_openssl.cc b/base/crypto/rsa_private_key_openssl.cc
index e14965f..0776b63 100644
--- a/base/crypto/rsa_private_key_openssl.cc
+++ b/base/crypto/rsa_private_key_openssl.cc
@@ -29,10 +29,10 @@
   if (!key)
     return false;
 
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
   ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new(BIO_s_mem()));
 
   int res = export_fn(bio.get(), key);
-  ClearOpenSSLERRStack();
   if (!res)
     return false;
 
@@ -49,11 +49,9 @@
 
 // static
 RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) {
-  EnsureOpenSSLInit();
-
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
   ScopedOpenSSL<RSA, RSA_free> rsa_key(RSA_generate_key(num_bits, 65537L,
                                                         NULL, NULL));
-  ClearOpenSSLERRStack();
   if (!rsa_key.get())
     return NULL;
 
@@ -74,7 +72,7 @@
 // static
 RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo(
     const std::vector<uint8>& input) {
-  EnsureOpenSSLInit();
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
 
   // BIO_new_mem_buf is not const aware, but it does not modify the buffer.
   char* data = reinterpret_cast<char*>(const_cast<uint8*>(input.data()));
@@ -87,13 +85,11 @@
   // Info structure returned.
   ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> p8inf(
       d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
-  ClearOpenSSLERRStack();
   if (!p8inf.get())
     return NULL;
 
   scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey);
   result->key_ = EVP_PKCS82PKEY(p8inf.get());
-  ClearOpenSSLERRStack();
   if (!result->key_)
     return NULL;
 
diff --git a/base/crypto/signature_creator.h b/base/crypto/signature_creator.h
index 38e327b..c405560 100644
--- a/base/crypto/signature_creator.h
+++ b/base/crypto/signature_creator.h
@@ -8,7 +8,10 @@
 
 #include "build/build_config.h"
 
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+// Forward declaration for openssl/*.h
+typedef struct env_md_ctx_st EVP_MD_CTX;
+#elif defined(USE_NSS)
 // Forward declaration.
 struct SGNContextStr;
 #elif defined(OS_MACOSX)
@@ -48,7 +51,9 @@
 
   RSAPrivateKey* key_;
 
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+  EVP_MD_CTX* sign_context_;
+#elif defined(USE_NSS)
   SGNContextStr* sign_context_;
 #elif defined(OS_MACOSX)
   CSSM_CC_HANDLE sig_handle_;
diff --git a/base/crypto/signature_creator_openssl.cc b/base/crypto/signature_creator_openssl.cc
index 5d70f01..7eed379 100644
--- a/base/crypto/signature_creator_openssl.cc
+++ b/base/crypto/signature_creator_openssl.cc
@@ -4,29 +4,50 @@
 
 #include "base/crypto/signature_creator.h"
 
+#include <openssl/evp.h>
+
 #include "base/logging.h"
+#include "base/openssl_util.h"
+#include "base/scoped_ptr.h"
 
 namespace base {
 
 // static
 SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) {
-  return NULL;
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  scoped_ptr<SignatureCreator> result(new SignatureCreator);
+  result->key_ = key;
+  if (!EVP_SignInit_ex(result->sign_context_, EVP_sha1(), NULL))
+    return NULL;
+  return result.release();
 }
 
-SignatureCreator::SignatureCreator() {
+SignatureCreator::SignatureCreator()
+    : sign_context_(EVP_MD_CTX_create()) {
 }
 
 SignatureCreator::~SignatureCreator() {
+  EVP_MD_CTX_destroy(sign_context_);
 }
 
 bool SignatureCreator::Update(const uint8* data_part, int data_part_len) {
-  NOTIMPLEMENTED();
-  return false;
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  return EVP_SignUpdate(sign_context_, data_part, data_part_len) == 1;
 }
 
 bool SignatureCreator::Final(std::vector<uint8>* signature) {
-  NOTIMPLEMENTED();
-  return false;
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  EVP_PKEY* key = key_->key();
+  signature->resize(EVP_PKEY_size(key));
+
+  unsigned int len = 0;
+  int rv = EVP_SignFinal(sign_context_, signature->data(), &len, key);
+  if (!rv) {
+    signature->clear();
+    return false;
+  }
+  signature->resize(len);
+  return true;
 }
 
 }  // namespace base
diff --git a/base/crypto/signature_verifier.h b/base/crypto/signature_verifier.h
index 4746edcf..e2b61af 100644
--- a/base/crypto/signature_verifier.h
+++ b/base/crypto/signature_verifier.h
@@ -83,7 +83,10 @@
 
   std::vector<uint8> signature_;
 
-#if defined(USE_NSS)
+#if defined(USE_OPENSSL)
+  struct VerifyContext;
+  VerifyContext* verify_context_;
+#elif defined(USE_NSS)
   VFYContext* vfy_context_;
 #elif defined(OS_MACOSX)
   std::vector<uint8> public_key_info_;
diff --git a/base/crypto/signature_verifier_openssl.cc b/base/crypto/signature_verifier_openssl.cc
index 49b5e073..b4fff78 100644
--- a/base/crypto/signature_verifier_openssl.cc
+++ b/base/crypto/signature_verifier_openssl.cc
@@ -4,14 +4,28 @@
 
 #include "base/crypto/signature_verifier.h"
 
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <vector>
+
 #include "base/logging.h"
+#include "base/openssl_util.h"
+#include "base/scoped_ptr.h"
 
 namespace base {
 
-SignatureVerifier::SignatureVerifier() {
+struct SignatureVerifier::VerifyContext {
+  ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> public_key;
+  ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> ctx;
+};
+
+SignatureVerifier::SignatureVerifier()
+    : verify_context_(NULL) {
 }
 
 SignatureVerifier::~SignatureVerifier() {
+  Reset();
 }
 
 bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm,
@@ -20,22 +34,60 @@
                                    int signature_len,
                                    const uint8* public_key_info,
                                    int public_key_info_len) {
-  NOTIMPLEMENTED();
-  return false;
+  DCHECK(!verify_context_);
+  verify_context_ = new VerifyContext;
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+  ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free> algorithm(
+      d2i_X509_ALGOR(NULL, &signature_algorithm, signature_algorithm_len));
+  if (!algorithm.get())
+    return false;
+
+  const EVP_MD* digest = EVP_get_digestbyobj(algorithm.get()->algorithm);
+  DCHECK(digest);
+
+  signature_.assign(signature, signature + signature_len);
+
+  // BIO_new_mem_buf is not const aware, but it does not modify the buffer.
+  char* data = reinterpret_cast<char*>(const_cast<uint8*>(public_key_info));
+  ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new_mem_buf(data,
+                                                       public_key_info_len));
+  if (!bio.get())
+    return false;
+
+  verify_context_->public_key.reset(d2i_PUBKEY_bio(bio.get(), NULL));
+  if (!verify_context_->public_key.get())
+    return false;
+
+  verify_context_->ctx.reset(EVP_MD_CTX_create());
+  int rv = EVP_VerifyInit_ex(verify_context_->ctx.get(), digest, NULL);
+  return rv == 1;
 }
 
 void SignatureVerifier::VerifyUpdate(const uint8* data_part,
                                      int data_part_len) {
-  NOTIMPLEMENTED();
+  DCHECK(verify_context_);
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  int rv = EVP_VerifyUpdate(verify_context_->ctx.get(),
+                            data_part, data_part_len);
+  DCHECK_EQ(rv, 1);
 }
 
 bool SignatureVerifier::VerifyFinal() {
-  NOTIMPLEMENTED();
-  return false;
+  DCHECK(verify_context_);
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  int rv = EVP_VerifyFinal(verify_context_->ctx.get(),
+                           signature_.data(), signature_.size(),
+                           verify_context_->public_key.get());
+  DCHECK_GE(rv, 0);
+  Reset();
+  return rv == 1;
 }
 
 void SignatureVerifier::Reset() {
-  NOTIMPLEMENTED();
+  delete verify_context_;
+  verify_context_ = NULL;
+  signature_.clear();
 }
 
 }  // namespace base
diff --git a/base/crypto/symmetric_key_openssl.cc b/base/crypto/symmetric_key_openssl.cc
index 9f0ad38..409cce4 100644
--- a/base/crypto/symmetric_key_openssl.cc
+++ b/base/crypto/symmetric_key_openssl.cc
@@ -30,18 +30,13 @@
   if (key_size_in_bits == 0)
     return NULL;
 
-  EnsureOpenSSLInit();
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
   scoped_ptr<SymmetricKey> key(new SymmetricKey);
   uint8* key_data =
       reinterpret_cast<uint8*>(WriteInto(&key->key_, key_size_in_bytes + 1));
 
-  int res = RAND_bytes(key_data, key_size_in_bytes);
-  if (res != 1) {
-    DLOG(ERROR) << "RAND_bytes failed. res = " << res;
-    ClearOpenSSLERRStack();
-    return NULL;
-  }
-  return key.release();
+  int rv = RAND_bytes(key_data, key_size_in_bytes);
+  return rv == 1 ? key.release() : NULL;
 }
 
 // static
@@ -54,20 +49,15 @@
   int key_size_in_bytes = key_size_in_bits / 8;
   DCHECK_EQ(static_cast<int>(key_size_in_bits), key_size_in_bytes * 8);
 
-  EnsureOpenSSLInit();
+  OpenSSLErrStackTracer err_tracer(FROM_HERE);
   scoped_ptr<SymmetricKey> key(new SymmetricKey);
   uint8* key_data =
       reinterpret_cast<uint8*>(WriteInto(&key->key_, key_size_in_bytes + 1));
-  int res = PKCS5_PBKDF2_HMAC_SHA1(password.data(), password.length(),
-                                   reinterpret_cast<const uint8*>(salt.data()),
-                                   salt.length(), iterations,
-                                   key_size_in_bytes, key_data);
-  if (res != 1) {
-    DLOG(ERROR) << "HMAC SHA1 failed. res = " << res;
-    ClearOpenSSLERRStack();
-    return NULL;
-  }
-  return key.release();
+  int rv = PKCS5_PBKDF2_HMAC_SHA1(password.data(), password.length(),
+                                  reinterpret_cast<const uint8*>(salt.data()),
+                                  salt.length(), iterations,
+                                  key_size_in_bytes, key_data);
+  return rv == 1 ? key.release() : NULL;
 }
 
 // static
diff --git a/base/openssl_util.cc b/base/openssl_util.cc
index 894c710a..1cbc304 100644
--- a/base/openssl_util.cc
+++ b/base/openssl_util.cc
@@ -67,13 +67,15 @@
   (void)Singleton<OpenSSLInitSingleton>::get();
 }
 
-void ClearOpenSSLERRStack() {
+void ClearOpenSSLERRStack(const tracked_objects::Location& location) {
   if (logging::DEBUG_MODE && VLOG_IS_ON(1)) {
     int error_num = ERR_get_error();
     if (error_num == 0)
       return;
 
-    DVLOG(1) << "OpenSSL ERR_get_error stack:";
+    std::string message;
+    location.Write(true, true, &message);
+    DVLOG(1) << "OpenSSL ERR_get_error stack from " << message;
     char buf[140];
     do {
       ERR_error_string_n(error_num, buf, arraysize(buf));
diff --git a/base/openssl_util.h b/base/openssl_util.h
index 1d290ae..9362302 100644
--- a/base/openssl_util.h
+++ b/base/openssl_util.h
@@ -16,10 +16,17 @@
 template <typename T, void (*destructor)(T*)>
 class ScopedOpenSSL {
  public:
-  explicit ScopedOpenSSL(T* ptr_) : ptr_(ptr_) { }
+  ScopedOpenSSL() : ptr_(NULL) { }
+  explicit ScopedOpenSSL(T* ptr) : ptr_(ptr) { }
   ~ScopedOpenSSL() { if (ptr_) (*destructor)(ptr_); }
 
   T* get() const { return ptr_; }
+  void reset(T* ptr) {
+    if (ptr != ptr_) {
+      if (ptr_) (*destructor)(ptr_);
+      ptr_ = ptr;
+    }
+  }
 
  private:
   T* ptr_;
@@ -72,8 +79,30 @@
 void EnsureOpenSSLInit();
 
 // Drains the OpenSSL ERR_get_error stack. On a debug build the error codes
-// are send to VLOG(1), on a release build they are disregarded.
-void ClearOpenSSLERRStack();
+// are send to VLOG(1), on a release build they are disregarded. In most
+// cases you should pass FROM_HERE as the |location|.
+void ClearOpenSSLERRStack(const tracked_objects::Location& location);
+
+// Place an instance of this class on the call stack to automatically clear
+// the OpenSSL error stack on function exit.
+class OpenSSLErrStackTracer {
+ public:
+  // Pass FROM_HERE as |location|, to help track the source of OpenSSL error
+  // messages. Note any diagnostic emitted will be tagged with the location of
+  // the constructor call as it's not possible to trace a destructor's callsite.
+  explicit OpenSSLErrStackTracer(const tracked_objects::Location& location)
+      : location_(location) {
+    EnsureOpenSSLInit();
+  }
+  ~OpenSSLErrStackTracer() {
+    ClearOpenSSLERRStack(location_);
+  }
+
+ private:
+  const tracked_objects::Location location_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(OpenSSLErrStackTracer);
+};
 
 }  // namespace base