Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(602)

Unified Diff: net/quic/crypto/proof_verifier_openssl.cc

Issue 17385010: OpenSSL/NSS implementation of ProofVerfifier. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: added known answer test from wtc Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/quic/crypto/proof_verifier_openssl.cc
diff --git a/net/quic/crypto/proof_verifier_openssl.cc b/net/quic/crypto/proof_verifier_openssl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8ea4f7638daea822833f7ad89ee6bfbb2ffb04a7
--- /dev/null
+++ b/net/quic/crypto/proof_verifier_openssl.cc
@@ -0,0 +1,203 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/proof_verifier_openssl.h"
+
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "base/logging.h"
+#include "crypto/openssl_util.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace {
+
+void EvpMdCtxCleanUp(EVP_MD_CTX *ctx) {
+ (void)EVP_MD_CTX_cleanup(ctx);
+}
+
+// sk_X509_free is a function-style macro, so can't be used as a template
+// param directly.
+void sk_X509_free_fn(STACK_OF(X509)* st) {
+ sk_X509_free(st);
+}
+
+} // namespace anonymous
+
+namespace net {
+
+ProofVerifierOpenSSL::ProofVerifierOpenSSL(const string& root_ca_filename) {
+ store_ = X509_STORE_new();
wtc 2013/06/19 19:44:36 We need to initialize OpenSSL. I suggest we add a
ramant (doing other things) 2013/06/19 19:58:20 Done.
+ X509_LOOKUP* lookup = X509_STORE_add_lookup(store_, X509_LOOKUP_file());
+ if (!X509_LOOKUP_load_file(lookup, root_ca_filename.c_str(),
+ X509_FILETYPE_PEM)) {
+ LOG(WARNING) << "Failed to load root CA file: " << root_ca_filename;
+ }
+}
+
+ProofVerifierOpenSSL::~ProofVerifierOpenSSL() { X509_STORE_free(store_); }
+
+bool ProofVerifierOpenSSL::VerifyProof(const string& hostname,
+ const string& server_config,
+ const vector<string>& certs,
+ const string& signature,
+ string* error_details) const {
+ crypto::ScopedOpenSSL<X509, X509_free> leaf(VerifyChain(certs));
+ if (!leaf.get()) {
+ *error_details = "Failed to verify certificate chain";
+ return false;
+ }
+ if (!VerifySignature(server_config, signature, leaf.get())) {
+ *error_details = "Failed to verify signature of server config";
+ return false;
+ }
+ if (!VerifyLeafForHost(leaf.get(), hostname)) {
+ *error_details = "Leaf certificate doesn't match hostname";
+ return false;
+ }
+
+ return true;
+}
+
+static X509* ParseDERCertificate(const string& der_bytes) {
+ const uint8* data;
+ const uint8* orig_data;
+ orig_data = data = reinterpret_cast<const uint8*>(der_bytes.data());
+ crypto::ScopedOpenSSL<X509, X509_free> cert(
+ d2i_X509(NULL, &data, der_bytes.size()));
+ if (!cert.get()) {
+ LOG(WARNING) << "d2i_X509";
+ return NULL;
+ }
+ if (data - orig_data != static_cast<unsigned int>(der_bytes.size())) {
+ // Trailing garbage.
+ LOG(WARNING) << "Trailing garbage";
+ return NULL;
+ }
+ return cert.release();
+}
+
+X509* ProofVerifierOpenSSL::VerifyChain(const vector<string>& certs) const {
+ if (certs.empty()) {
+ LOG(WARNING) << "certs are empty";
+ return NULL;
+ }
+
+ // TODO(agl): use scoped_ptr_openssl_x509_stack_pop_free from //util/sig once
+ // it has landed.
+ crypto::ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn> intermediates(
+ sk_X509_new_null());
+ if (!intermediates.get()) {
+ LOG(WARNING) << "Out of memory";
+ return NULL;
+ }
+ for (size_t i = 1; i < certs.size(); i++) {
+ X509* cert = ParseDERCertificate(certs[i]);
+ if (!cert) {
+ LOG(WARNING) << "ParseDERCertificate NULL";
+ return NULL;
+ }
+ sk_X509_push(intermediates.get(), cert);
+ }
+
+ crypto::ScopedOpenSSL<X509, X509_free> leaf(ParseDERCertificate(certs[0]));
+ if (!leaf.get()) {
+ LOG(WARNING) << "leaf is NULL";
+ return NULL;
+ }
+
+ crypto::ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx(
+ X509_STORE_CTX_new());
+ if (!X509_STORE_CTX_init(ctx.get(), store_, leaf.get(),
+ intermediates.get())) {
+ LOG(WARNING) << "X509_STORE_CTX_init ";
+ return NULL;
+ }
+ if (X509_verify_cert(ctx.get()) <= 0) {
+ LOG(WARNING) << "X509_verify_cert";
+ const int n = X509_STORE_CTX_get_error(ctx.get());
+ const int depth = X509_STORE_CTX_get_error_depth(ctx.get());
+ LOG(WARNING) << "Certificate verification error at depth "
+ << depth << ": " << X509_verify_cert_error_string(n);
+ // TODO(rtenneti): We are getting X509_V_ERR_CERT_SIGNATURE_FAILURE.
+ // return NULL;
+ }
+ return leaf.release();
+}
+
+// static
+bool ProofVerifierOpenSSL::VerifySignature(const string& signed_data,
+ const string& signature,
+ X509* cert) {
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> pkey(X509_get_pubkey(cert));
+
+ EVP_MD_CTX md_ctx;
+ crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp> scoped_md_ctx(&md_ctx);
+ EVP_MD_CTX_init(&md_ctx);
+ EVP_PKEY_CTX* ctx;
+ if (1 != EVP_DigestVerifyInit(&md_ctx, &ctx, EVP_sha256(),
+ NULL /* no engine */, pkey.get())) {
+ LOG(WARNING) << "EVP_DigestVerifyInit";
+ return false;
+ }
+ if (EVP_PKEY_id(pkey.get()) == EVP_PKEY_RSA) {
+ if (1 != EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING)) {
+ LOG(WARNING) << "EVP_PKEY_CTX_set_rsa_padding";
+ return false;
+ }
+ }
+ if (1 != EVP_DigestVerifyUpdate(&md_ctx, kProofSignatureLabel,
+ sizeof(kProofSignatureLabel))) {
+ LOG(WARNING) << "EVP_DigestVerifyUpdate";
+ return false;
+ }
+ if (1 != EVP_DigestVerifyUpdate(&md_ctx, signed_data.data(),
+ signed_data.size())) {
+ LOG(WARNING) << "EVP_DigestVerifyUpdate";
+ return false;
+ }
+ if (1 != EVP_DigestVerifyFinal(
+ &md_ctx, const_cast<uint8*>(
+ reinterpret_cast<const uint8*>(signature.data())),
+ signature.size())) {
+ LOG(WARNING) << "EVP_DigestVerifyFinal";
+ return false;
+ }
+ return true;
+}
+
+// static
+bool ProofVerifierOpenSSL::VerifyLeafForHost(X509* cert,
+ const string& hostname) {
+ // Note: this is a very simple matching function that just checks for an
+ // exact match in the list of SANs.
+ crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free> gens(
+ reinterpret_cast<GENERAL_NAMES*>(
+ X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL)));
+ if (gens.get() == NULL) {
+ return false;
+ }
+
+ for (int i = 0; i < sk_GENERAL_NAME_num(gens.get()); i++) {
+ const GENERAL_NAME* name = sk_GENERAL_NAME_value(gens.get(), i);
+ if (name->type == GEN_DNS) {
+ StringPiece dnsname(reinterpret_cast<char*>(name->d.dNSName->data),
+ name->d.dNSName->length);
+ if (dnsname == hostname) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698