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; |
} |