Chromium Code Reviews| Index: chrome/browser/extensions/api/cast_channel/cast_auth_util_nss.cc |
| diff --git a/chrome/browser/extensions/api/cast_channel/cast_auth_util_nss.cc b/chrome/browser/extensions/api/cast_channel/cast_auth_util_nss.cc |
| index af1e0f362e58c6b20566f067ae2f586be9acaa88..52b6b363db55afe01557cfebd5c3af8c0d98fabd 100644 |
| --- a/chrome/browser/extensions/api/cast_channel/cast_auth_util_nss.cc |
| +++ b/chrome/browser/extensions/api/cast_channel/cast_auth_util_nss.cc |
| @@ -15,6 +15,9 @@ |
| #include "chrome/browser/extensions/api/cast_channel/cast_message_util.h" |
| #include "crypto/nss_util.h" |
| #include "crypto/scoped_nss_types.h" |
| +#include "net/base/hash_value.h" |
| +#include "net/cert/asn1_util.h" |
| +#include "net/cert/x509_certificate.h" |
| namespace { |
| @@ -44,11 +47,30 @@ static const unsigned char kCAPublicKeyDER[] = { |
| 0x61, 0x47, 0x9e, 0xab, 0x80, 0xb7, 0xe4, 0x48, 0x80, 0x2a, 0x92, 0xc5, |
| 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01 }; |
| +static const net::SHA1HashValue kICAFingerprints[] = { |
|
Ryan Sleevi
2014/04/30 01:32:13
Note: We don't use fingerprints for any (granting)
Munjal (Google)
2014/05/05 00:01:46
Let me see if I understand correctly. So you are s
Ryan Sleevi
2014/05/05 19:35:32
I apologize for the confusion. The goal is to have
Munjal (Google)
2014/05/05 21:00:27
So are you suggesting that we hard-code a list of
|
| + { { 0x57, 0x16, 0xe2, 0xad, 0x73, 0x2e, 0xbe, 0xda, 0xeb, 0x18, |
| + 0xe8, 0x47, 0x15, 0xa8, 0xde, 0x90, 0x3b, 0x5e, 0x2a, 0xf4 } }, |
| + { { 0x1b, 0xa2, 0x9e, 0xc9, 0x8e, 0x4e, 0xb3, 0x80, 0xee, 0x55, |
| + 0xb2, 0x97, 0xfd, 0x2e, 0x2b, 0x2c, 0xb6, 0x8e, 0x0b, 0x2f} }, |
| + { { 0x97, 0x05, 0xce, 0xf6, 0x3f, 0xa9, 0x5e, 0x0f, 0xe7, 0x61, |
| + 0xfB, 0x08, 0x44, 0x31, 0xbe, 0xde, 0x01, 0xb8, 0xfB, 0xeb} } |
| +}; |
| + |
| typedef scoped_ptr< |
| CERTCertificate, |
| crypto::NSSDestroyer<CERTCertificate, CERT_DestroyCertificate> > |
| ScopedCERTCertificate; |
| +// Returns whether |fingerprint| matches one of the hard-coded list of |
| +// allowed ICA fingerprints. |
| +static bool IsFingerprintAllowed(const net::SHA1HashValue& fingerprint) { |
| + for (size_t i = 0; i < arraysize(kICAFingerprints); ++i) { |
| + if (fingerprint.Equals(kICAFingerprints[i])) |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| // Parses out DeviceAuthMessage from CastMessage |
| static bool ParseAuthMessage( |
| const extensions::api::cast_channel::CastMessage& challenge_reply, |
| @@ -81,9 +103,32 @@ static bool ParseAuthMessage( |
| // Authenticates the given credentials: |
| // 1. |signature| verification of |data| using |certificate|. |
| // 2. |certificate| is signed by a trusted CA. |
| -bool VerifyCredentials(const std::string& certificate, |
| - const std::string& signature, |
| - const std::string& data) { |
| +bool VerifyCredentials( |
| + const extensions::api::cast_channel::AuthResponse& response, |
| + const std::string& data) { |
| + const std::string& certificate = response.client_auth_certificate(); |
| + const std::string& signature = response.signature(); |
| + |
| + std::string ica_public_key_str; |
| + // If the list of intermediates is not empty, then the fingerprint of |
| + // the first intermediate should be one of the hard-coded list of |
| + // fingerprints. |
| + if (response.intermediate_certificate_size() > 0) { |
|
mark a. foltz
2014/05/05 19:22:22
ISTM having two helper functions that extract the
|
| + const std::string& ica = response.intermediate_certificate(0); |
| + scoped_refptr<net::X509Certificate> ica_cert |
| + = net::X509Certificate::CreateFromBytes(ica.data(), ica.length()); |
| + if (!IsFingerprintAllowed(ica_cert->fingerprint())) { |
| + VLOG(1) << "Disallowed intermdiate cert: " << ica; |
|
mark a. foltz
2014/05/05 19:22:22
ica looks to be a binary blob that is parsed to yi
|
| + return false; |
| + } |
| + base::StringPiece ica_public_key; |
| + if (!net::asn1::ExtractSPKIFromDERCert(ica, &ica_public_key)) { |
| + VLOG(1) << "Could not extract public key from intermediate cert: " << ica; |
|
mark a. foltz
2014/05/05 19:22:22
Ditto.
|
| + return false; |
| + } |
| + ica_public_key_str = ica_public_key.as_string(); |
| + } |
|
Munjal (Google)
2014/05/05 00:01:46
Ryan, does this logic look OK to you?
|
| + |
| crypto::EnsureNSSInit(); |
| SECItem der_cert; |
| der_cert.type = siDERCertBuffer; |
| @@ -103,8 +148,16 @@ bool VerifyCredentials(const std::string& certificate, |
| // Check that the certificate is signed by trusted CA. |
| SECItem trusted_ca_key_der_item; |
| trusted_ca_key_der_item.type = siDERCertBuffer; |
| - trusted_ca_key_der_item.data = const_cast<unsigned char*>(kCAPublicKeyDER); |
| - trusted_ca_key_der_item.len = sizeof(kCAPublicKeyDER); |
| + // Use the hard-coded CA public key if response has no intermediates. |
| + if (ica_public_key_str.empty()) { |
| + trusted_ca_key_der_item.data = const_cast<unsigned char*>(kCAPublicKeyDER); |
| + trusted_ca_key_der_item.len = sizeof(kCAPublicKeyDER); |
| + } else { |
| + trusted_ca_key_der_item.data = reinterpret_cast<unsigned char*>( |
|
mark a. foltz
2014/05/05 19:22:22
You are taking the data pointer out of ica_public_
Ryan Sleevi
2014/05/05 19:35:32
This is fine/safe/blessed. NSS is slowly becoming
|
| + const_cast<char*>(ica_public_key_str.data())); |
| + trusted_ca_key_der_item.len = ica_public_key_str.length(); |
| + } |
| + |
| crypto::ScopedSECKEYPublicKey ca_public_key( |
| SECKEY_ImportDERPublicKey(&trusted_ca_key_der_item, CKK_RSA)); |
| SECStatus verified = CERT_VerifySignedDataWithPublicKey( |
| @@ -157,9 +210,7 @@ bool AuthenticateChallengeReply(const CastMessage& challenge_reply, |
| return false; |
| const AuthResponse& response = auth_message.response(); |
| - return VerifyCredentials(response.client_auth_certificate(), |
| - response.signature(), |
| - peer_cert); |
| + return VerifyCredentials(response, peer_cert); |
| } |
| } // namespace cast_channel |