Index: extensions/browser/api/cast_channel/cast_auth_util_nss.cc |
diff --git a/extensions/browser/api/cast_channel/cast_auth_util_nss.cc b/extensions/browser/api/cast_channel/cast_auth_util_nss.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..97f16d7c8e04fb3f20568d0ec81748b80cd7609c |
--- /dev/null |
+++ b/extensions/browser/api/cast_channel/cast_auth_util_nss.cc |
@@ -0,0 +1,142 @@ |
+// Copyright 2014 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 "extensions/browser/api/cast_channel/cast_auth_util.h" |
+ |
+#include <cert.h> |
+#include <cryptohi.h> |
+#include <pk11pub.h> |
+#include <seccomon.h> |
+#include <string> |
+ |
+#include "base/logging.h" |
+#include "base/strings/string_piece.h" |
+#include "crypto/nss_util.h" |
+#include "crypto/scoped_nss_types.h" |
+#include "extensions/browser/api/cast_channel/cast_auth_ica.h" |
+#include "extensions/browser/api/cast_channel/cast_message_util.h" |
+#include "extensions/common/api/cast_channel/cast_channel.pb.h" |
+#include "net/base/hash_value.h" |
+#include "net/cert/x509_certificate.h" |
+ |
+namespace extensions { |
+namespace core_api { |
+namespace cast_channel { |
+namespace { |
+ |
+typedef scoped_ptr< |
+ CERTCertificate, |
+ crypto::NSSDestroyer<CERTCertificate, CERT_DestroyCertificate> > |
+ ScopedCERTCertificate; |
+ |
+} // namespace |
+ |
+// Authenticates the given credentials: |
+// 1. |signature| verification of |peer_cert| using |certificate|. |
+// 2. |certificate| is signed by a trusted CA. |
+AuthResult VerifyCredentials(const AuthResponse& response, |
+ const std::string& peer_cert) { |
+ const std::string kErrorPrefix("Failed to verify credentials: "); |
+ const std::string& certificate = response.client_auth_certificate(); |
+ const std::string& signature = response.signature(); |
+ |
+ // If the list of intermediates is empty then use kPublicKeyICA1 as |
+ // the trusted CA (legacy case). |
+ // Otherwise, use the first intermediate in the list as long as it |
+ // is in the allowed list of intermediates. |
+ int num_intermediates = response.intermediate_certificate_size(); |
+ |
+ VLOG(1) << "Response has " << num_intermediates << " intermediates"; |
+ |
+ base::StringPiece ica; |
+ if (num_intermediates <= 0) { |
+ ica = GetDefaultTrustedICAPublicKey(); |
+ } else { |
+ ica = GetTrustedICAPublicKey(response.intermediate_certificate(0)); |
+ } |
+ if (ica.empty()) { |
+ return AuthResult::CreateWithParseError( |
+ "Disallowed intermediate cert", |
+ AuthResult::ERROR_FINGERPRINT_NOT_FOUND); |
+ } |
+ |
+ SECItem trusted_ca_key_der; |
+ trusted_ca_key_der.type = SECItemType::siDERCertBuffer; |
+ trusted_ca_key_der.data = |
+ const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(ica.data())); |
+ trusted_ca_key_der.len = ica.size(); |
+ |
+ crypto::EnsureNSSInit(); |
+ SECItem der_cert; |
+ der_cert.type = siDERCertBuffer; |
+ // Make a copy of certificate string so it is safe to type cast. |
+ der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>( |
+ certificate.data())); |
+ der_cert.len = certificate.length(); |
+ |
+ // Parse into a certificate structure. |
+ ScopedCERTCertificate cert(CERT_NewTempCertificate( |
+ CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE)); |
+ if (!cert.get()) { |
+ return AuthResult::CreateWithNSSError( |
+ "Failed to parse certificate.", |
+ AuthResult::ERROR_CERT_PARSING_FAILED, PORT_GetError()); |
+ } |
+ |
+ // Check that the certificate is signed by trusted CA. |
+ // NOTE: We const_cast trusted_ca_key_der since on some platforms |
+ // SECKEY_ImportDERPublicKey API takes in SECItem* and not const |
+ // SECItem*. |
+ crypto::ScopedSECKEYPublicKey ca_public_key( |
+ SECKEY_ImportDERPublicKey(&trusted_ca_key_der, CKK_RSA)); |
+ if (!ca_public_key) { |
+ return AuthResult::CreateWithNSSError( |
+ "Failed to import public key from CA certificate.", |
+ AuthResult::ERROR_CERT_PARSING_FAILED, PORT_GetError()); |
+ } |
+ SECStatus verified = CERT_VerifySignedDataWithPublicKey( |
+ &cert->signatureWrap, ca_public_key.get(), NULL); |
+ if (verified != SECSuccess) { |
+ return AuthResult::CreateWithNSSError( |
+ "Cert not signed by trusted CA", |
+ AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA, PORT_GetError()); |
+ } |
+ |
+ VLOG(1) << "Cert signed by trusted CA"; |
+ |
+ // Verify that the |signature| matches |peer_cert|. |
+ crypto::ScopedSECKEYPublicKey public_key(CERT_ExtractPublicKey(cert.get())); |
+ if (!public_key.get()) { |
+ return AuthResult::CreateWithNSSError( |
+ "Unable to extract public key from certificate", |
+ AuthResult::ERROR_CANNOT_EXTRACT_PUBLIC_KEY, PORT_GetError()); |
+ } |
+ SECItem signature_item; |
+ signature_item.type = siBuffer; |
+ signature_item.data = reinterpret_cast<unsigned char*>( |
+ const_cast<char*>(signature.data())); |
+ signature_item.len = signature.length(); |
+ verified = VFY_VerifyDataDirect( |
+ reinterpret_cast<unsigned char*>(const_cast<char*>(peer_cert.data())), |
+ peer_cert.size(), |
+ public_key.get(), |
+ &signature_item, |
+ SEC_OID_PKCS1_RSA_ENCRYPTION, |
+ SEC_OID_SHA1, NULL, NULL); |
+ |
+ if (verified != SECSuccess) { |
+ return AuthResult::CreateWithNSSError( |
+ "Signed blobs did not match", |
+ AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, |
+ PORT_GetError()); |
+ } |
+ |
+ VLOG(1) << "Signature verification succeeded"; |
+ |
+ return AuthResult(); |
+} |
+ |
+} // namespace cast_channel |
+} // namespace core_api |
+} // namespace extensions |