1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/api/cast_channel/cast_auth_util.h"
13 #include "base/logging.h"
14 #include "base/strings/string_piece.h"
15 #include "crypto/nss_util.h"
16 #include "crypto/scoped_nss_types.h"
17 #include "extensions/browser/api/cast_channel/cast_auth_ica.h"
18 #include "extensions/browser/api/cast_channel/cast_message_util.h"
19 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
20 #include "net/base/hash_value.h"
21 #include "net/cert/x509_certificate.h"
23 namespace extensions
{
25 namespace cast_channel
{
30 crypto::NSSDestroyer
<CERTCertificate
, CERT_DestroyCertificate
> >
31 ScopedCERTCertificate
;
35 // Authenticates the given credentials:
36 // 1. |signature| verification of |peer_cert| using |certificate|.
37 // 2. |certificate| is signed by a trusted CA.
38 AuthResult
VerifyCredentials(const AuthResponse
& response
,
39 const std::string
& peer_cert
) {
40 const std::string
kErrorPrefix("Failed to verify credentials: ");
41 const std::string
& certificate
= response
.client_auth_certificate();
42 const std::string
& signature
= response
.signature();
44 // If the list of intermediates is empty then use kPublicKeyICA1 as
45 // the trusted CA (legacy case).
46 // Otherwise, use the first intermediate in the list as long as it
47 // is in the allowed list of intermediates.
48 int num_intermediates
= response
.intermediate_certificate_size();
50 VLOG(1) << "Response has " << num_intermediates
<< " intermediates";
52 base::StringPiece ica
;
53 if (num_intermediates
<= 0) {
54 ica
= GetDefaultTrustedICAPublicKey();
56 ica
= GetTrustedICAPublicKey(response
.intermediate_certificate(0));
59 return AuthResult::CreateWithParseError(
60 "Disallowed intermediate cert",
61 AuthResult::ERROR_FINGERPRINT_NOT_FOUND
);
64 SECItem trusted_ca_key_der
;
65 trusted_ca_key_der
.type
= SECItemType::siDERCertBuffer
;
66 trusted_ca_key_der
.data
=
67 const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(ica
.data()));
68 trusted_ca_key_der
.len
= ica
.size();
70 crypto::EnsureNSSInit();
72 der_cert
.type
= siDERCertBuffer
;
73 // Make a copy of certificate string so it is safe to type cast.
74 der_cert
.data
= reinterpret_cast<unsigned char*>(const_cast<char*>(
76 der_cert
.len
= certificate
.length();
78 // Parse into a certificate structure.
79 ScopedCERTCertificate
cert(CERT_NewTempCertificate(
80 CERT_GetDefaultCertDB(), &der_cert
, NULL
, PR_FALSE
, PR_TRUE
));
82 return AuthResult::CreateWithNSSError(
83 "Failed to parse certificate.",
84 AuthResult::ERROR_CERT_PARSING_FAILED
, PORT_GetError());
87 // Check that the certificate is signed by trusted CA.
88 // NOTE: We const_cast trusted_ca_key_der since on some platforms
89 // SECKEY_ImportDERPublicKey API takes in SECItem* and not const
91 crypto::ScopedSECKEYPublicKey
ca_public_key(
92 SECKEY_ImportDERPublicKey(&trusted_ca_key_der
, CKK_RSA
));
94 return AuthResult::CreateWithNSSError(
95 "Failed to import public key from CA certificate.",
96 AuthResult::ERROR_CERT_PARSING_FAILED
, PORT_GetError());
98 SECStatus verified
= CERT_VerifySignedDataWithPublicKey(
99 &cert
->signatureWrap
, ca_public_key
.get(), NULL
);
100 if (verified
!= SECSuccess
) {
101 return AuthResult::CreateWithNSSError(
102 "Cert not signed by trusted CA",
103 AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA
, PORT_GetError());
106 VLOG(1) << "Cert signed by trusted CA";
108 // Verify that the |signature| matches |peer_cert|.
109 crypto::ScopedSECKEYPublicKey
public_key(CERT_ExtractPublicKey(cert
.get()));
110 if (!public_key
.get()) {
111 return AuthResult::CreateWithNSSError(
112 "Unable to extract public key from certificate",
113 AuthResult::ERROR_CANNOT_EXTRACT_PUBLIC_KEY
, PORT_GetError());
115 SECItem signature_item
;
116 signature_item
.type
= siBuffer
;
117 signature_item
.data
= reinterpret_cast<unsigned char*>(
118 const_cast<char*>(signature
.data()));
119 signature_item
.len
= signature
.length();
120 verified
= VFY_VerifyDataDirect(
121 reinterpret_cast<unsigned char*>(const_cast<char*>(peer_cert
.data())),
125 SEC_OID_PKCS1_RSA_ENCRYPTION
,
126 SEC_OID_SHA1
, NULL
, NULL
);
128 if (verified
!= SECSuccess
) {
129 return AuthResult::CreateWithNSSError(
130 "Signed blobs did not match",
131 AuthResult::ERROR_SIGNED_BLOBS_MISMATCH
,
135 VLOG(1) << "Signature verification succeeded";
140 } // namespace cast_channel
141 } // namespace core_api
142 } // namespace extensions