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

Unified Diff: chrome/common/extensions/api/networking_private/networking_private_crypto_openssl.cc

Issue 396463004: Implement NetworkingPrivateCrypto for OpenSSL. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 5 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: chrome/common/extensions/api/networking_private/networking_private_crypto_openssl.cc
diff --git a/chrome/common/extensions/api/networking_private/networking_private_crypto_openssl.cc b/chrome/common/extensions/api/networking_private/networking_private_crypto_openssl.cc
index 95a6bd6c59e2d06cc8023612cda2453f5451c12e..0e64875d60f1a02560e0ec9572be243483a1d48a 100644
--- a/chrome/common/extensions/api/networking_private/networking_private_crypto_openssl.cc
+++ b/chrome/common/extensions/api/networking_private/networking_private_crypto_openssl.cc
@@ -4,38 +4,213 @@
#include "chrome/common/extensions/api/networking_private/networking_private_crypto.h"
+// TODO(davidben): Remove this #ifdef when we switch to BoringSSL.
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/digest.h>
+#endif
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "crypto/openssl_util.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/scoped_openssl_types.h"
+#include "net/cert/pem_tokenizer.h"
-NetworkingPrivateCrypto::NetworkingPrivateCrypto() {
-}
+namespace {
+
+typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509;
-NetworkingPrivateCrypto::~NetworkingPrivateCrypto() {
+// Parses |pem_data| for a PEM block of |pem_type|.
+// Returns true if a |pem_type| block is found, storing the decoded result in
+// |der_output|.
+bool GetDERFromPEM(const std::string& pem_data,
+ const std::string& pem_type,
+ std::vector<uint8>* der_output) {
+ std::vector<std::string> headers;
+ headers.push_back(pem_type);
+ net::PEMTokenizer pem_tok(pem_data, headers);
+ if (!pem_tok.GetNext()) {
+ return false;
+ }
+
+ der_output->assign(pem_tok.data().begin(), pem_tok.data().end());
+ return true;
}
+} // namespace
+
bool NetworkingPrivateCrypto::VerifyCredentials(
const std::string& certificate,
const std::string& signature,
const std::string& data,
const std::string& connected_mac) {
- // https://crbug.com/393023
- NOTIMPLEMENTED();
- return false;
+ crypto::EnsureOpenSSLInit();
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ std::vector<uint8> cert_data;
+ if (!GetDERFromPEM(certificate, "CERTIFICATE", &cert_data)) {
+ LOG(ERROR) << "Failed to parse certificate.";
+ return false;
+ }
+
+ // Parse into an OpenSSL X509.
+ const uint8* ptr = &cert_data[0];
+ ScopedX509 cert(d2i_X509(NULL, &ptr, cert_data.size()));
+ if (!cert || ptr != (&cert_data[0] + cert_data.size())) {
+ LOG(ERROR) << "Failed to parse certificate.";
+ return false;
+ }
+
+ // Import the trusted public key.
+ ptr = kTrustedCAPublicKeyDER;
+ crypto::ScopedRSA ca_public_key_rsa(
+ d2i_RSAPublicKey(NULL, &ptr, kTrustedCAPublicKeyDERLength));
+ if (!ca_public_key_rsa ||
+ ptr != kTrustedCAPublicKeyDER + kTrustedCAPublicKeyDERLength) {
+ NOTREACHED();
+ LOG(ERROR) << "Failed to import trusted public key.";
+ return false;
+ }
+ crypto::ScopedEVP_PKEY ca_public_key(EVP_PKEY_new());
+ if (!ca_public_key ||
+ !EVP_PKEY_set1_RSA(ca_public_key.get(), ca_public_key_rsa.get())) {
+ LOG(ERROR) << "Failed to initialize EVP_PKEY";
+ return false;
+ }
+
+ // Check that the certificate is signed by the trusted public key.
+ if (X509_verify(cert.get(), ca_public_key.get()) <= 0) {
+ LOG(ERROR) << "Certificate is not issued by the trusted CA.";
+ return false;
+ }
+
+ // Check that the device listed in the certificate is correct.
+ // Something like evt_e161 001a11ffacdf
+ std::string common_name;
+ int common_name_length = X509_NAME_get_text_by_NID(
+ cert->cert_info->subject, NID_commonName, NULL, 0);
+ if (common_name_length < 0) {
+ LOG(ERROR) << "Certificate does not have common name.";
+ return false;
+ }
+ // X509_NAME_get_text_by_NID writes a trailing NUL.
+ common_name.resize(common_name_length + 1);
+ common_name_length =
+ X509_NAME_get_text_by_NID(cert->cert_info->subject,
+ NID_commonName,
+ const_cast<char*>(common_name.data()),
+ common_name_length + 1);
Ryan Sleevi 2014/07/14 20:29:30 Use base::Writeinto if you're going to subvert the
davidben 2014/07/14 21:09:50 Handy. I didn't realize that existed. Although it
+ if (common_name_length < 0) {
+ LOG(ERROR) << "Certificate does not have common name.";
+ return false;
+ }
+ common_name.resize(common_name_length);
+
+ std::string translated_mac;
+ base::RemoveChars(connected_mac, ":", &translated_mac);
+ if (!EndsWith(common_name, translated_mac, false)) {
+ LOG(ERROR) << "MAC addresses don't match.";
+ return false;
+ }
+
+ // Make sure that the certificate matches the unsigned data presented.
+ // Verify that the |signature| matches |data|.
+ crypto::ScopedEVP_PKEY public_key(X509_get_pubkey(cert.get()));
+ if (!public_key) {
+ LOG(ERROR) << "Unable to extract public key from certificate.";
+ return false;
+ }
+
+ EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
+ bool ok =
+ EVP_DigestVerifyInit(&ctx, NULL, EVP_sha1(), NULL, public_key.get()) ==
+ 1 &&
+ EVP_DigestVerifyUpdate(&ctx, data.data(), data.size()) == 1 &&
+ EVP_DigestVerifyFinal(&ctx,
+// TODO(davidben): Remove this #ifdef when we switch to BoringSSL.
+#ifdef OPENSSL_IS_BORINGSSL
+ reinterpret_cast<const uint8*>(signature.data()),
+#else // !OPENSSL_IS_BORINGSSL
+ const_cast<uint8*>(reinterpret_cast<const uint8*>(
+ signature.data())),
+#endif // OPENSSL_IS_BORINGSSL
Ryan Sleevi 2014/07/14 20:29:30 Please extract this into a variable, set up on lin
davidben 2014/07/14 21:09:50 Done.
+ signature.size()) == 1;
+ EVP_MD_CTX_cleanup(&ctx);
+ if (!ok) {
+ LOG(ERROR) << "Signed blobs did not match.";
+ return false;
+ }
+ return true;
}
bool NetworkingPrivateCrypto::EncryptByteString(
const std::vector<uint8>& pub_key_der,
const std::string& data,
std::vector<uint8>* encrypted_output) {
- // https://crbug.com/393023
- NOTIMPLEMENTED();
- return false;
+ crypto::EnsureOpenSSLInit();
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ const uint8* ptr = &pub_key_der[0];
Ryan Sleevi 2014/07/14 20:29:30 CRASH BUG: if pub_key_der is .empty(). The NSS cod
davidben 2014/07/14 21:09:50 Done. (Verified experimentally that OpenSSL is fin
+ crypto::ScopedRSA rsa(d2i_RSAPublicKey(NULL, &ptr, pub_key_der.size()));
+ if (!rsa || ptr != (&pub_key_der[0] + pub_key_der.size())) {
+ LOG(ERROR) << "Failed to parse public key";
+ return false;
+ }
+
+ encrypted_output->resize(RSA_size(rsa.get()));
+ int rsa_len = RSA_public_encrypt(data.size(),
+ reinterpret_cast<const uint8*>(data.data()),
+ &(*encrypted_output)[0],
Ryan Sleevi 2014/07/14 20:29:30 &encrypted_output->front() ?
davidben 2014/07/14 21:09:50 Done.
+ rsa.get(),
+ RSA_PKCS1_PADDING);
+ if (rsa_len < 0) {
+ LOG(ERROR) << "Error during decryption";
+ return false;
+ }
+ encrypted_output->resize(rsa_len);
+ return true;
}
bool NetworkingPrivateCrypto::DecryptByteString(
const std::string& private_key_pem,
const std::vector<uint8>& encrypted_data,
std::string* decrypted_output) {
- // https://crbug.com/393023
- NOTIMPLEMENTED();
- return false;
+ crypto::EnsureOpenSSLInit();
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ std::vector<uint8> private_key_data;
+ if (!GetDERFromPEM(private_key_pem, "PRIVATE KEY", &private_key_data)) {
+ LOG(ERROR) << "Failed to parse private key PEM.";
+ return false;
+ }
+ scoped_ptr<crypto::RSAPrivateKey> private_key(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(private_key_data));
+ if (!private_key || !private_key->key()) {
+ LOG(ERROR) << "Failed to parse private key DER.";
+ return false;
+ }
+
+ crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key->key()));
+ if (!rsa) {
+ LOG(ERROR) << "Failed to get RSA key.";
+ return false;
+ }
+
+ decrypted_output->resize(RSA_size(rsa.get()));
Ryan Sleevi 2014/07/14 20:29:30 base::WriteInto() Please note this is not preserv
davidben 2014/07/14 21:09:50 Well, this is the last operation, so only assuming
+ int rsa_len = RSA_private_decrypt(
+ encrypted_data.size(),
+ &encrypted_data[0],
+ reinterpret_cast<uint8*>(const_cast<char*>(decrypted_output->data())),
+ rsa.get(),
+ RSA_PKCS1_PADDING);
+ if (rsa_len < 0) {
+ LOG(ERROR) << "Error during decryption.";
+ return false;
+ }
+ decrypted_output->resize(rsa_len);
+ return true;
}

Powered by Google App Engine
This is Rietveld 408576698