OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "extensions/browser/api/cast_channel/cast_auth_util.h" | |
6 | |
7 #include <cert.h> | |
8 #include <cryptohi.h> | |
9 #include <pk11pub.h> | |
10 #include <seccomon.h> | |
11 #include <string> | |
12 | |
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" | |
22 | |
23 namespace extensions { | |
24 namespace core_api { | |
25 namespace cast_channel { | |
26 namespace { | |
27 | |
28 typedef scoped_ptr< | |
29 CERTCertificate, | |
30 crypto::NSSDestroyer<CERTCertificate, CERT_DestroyCertificate> > | |
31 ScopedCERTCertificate; | |
32 | |
33 } // namespace | |
34 | |
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(); | |
43 | |
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(); | |
49 | |
50 VLOG(1) << "Response has " << num_intermediates << " intermediates"; | |
51 | |
52 base::StringPiece ica; | |
53 if (num_intermediates <= 0) { | |
54 ica = GetDefaultTrustedICAPublicKey(); | |
55 } else { | |
56 ica = GetTrustedICAPublicKey(response.intermediate_certificate(0)); | |
57 } | |
58 if (ica.empty()) { | |
59 return AuthResult::CreateWithParseError( | |
60 "Disallowed intermediate cert", | |
61 AuthResult::ERROR_FINGERPRINT_NOT_FOUND); | |
62 } | |
63 | |
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(); | |
69 | |
70 crypto::EnsureNSSInit(); | |
71 SECItem der_cert; | |
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*>( | |
75 certificate.data())); | |
76 der_cert.len = certificate.length(); | |
77 | |
78 // Parse into a certificate structure. | |
79 ScopedCERTCertificate cert(CERT_NewTempCertificate( | |
80 CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE)); | |
81 if (!cert.get()) { | |
82 return AuthResult::CreateWithNSSError( | |
83 "Failed to parse certificate.", | |
84 AuthResult::ERROR_CERT_PARSING_FAILED, PORT_GetError()); | |
85 } | |
86 | |
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 | |
90 // SECItem*. | |
91 crypto::ScopedSECKEYPublicKey ca_public_key( | |
92 SECKEY_ImportDERPublicKey(&trusted_ca_key_der, CKK_RSA)); | |
93 if (!ca_public_key) { | |
94 return AuthResult::CreateWithNSSError( | |
95 "Failed to import public key from CA certificate.", | |
96 AuthResult::ERROR_CERT_PARSING_FAILED, PORT_GetError()); | |
97 } | |
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()); | |
104 } | |
105 | |
106 VLOG(1) << "Cert signed by trusted CA"; | |
107 | |
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()); | |
114 } | |
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())), | |
122 peer_cert.size(), | |
123 public_key.get(), | |
124 &signature_item, | |
125 SEC_OID_PKCS1_RSA_ENCRYPTION, | |
126 SEC_OID_SHA1, NULL, NULL); | |
127 | |
128 if (verified != SECSuccess) { | |
129 return AuthResult::CreateWithNSSError( | |
130 "Signed blobs did not match", | |
131 AuthResult::ERROR_SIGNED_BLOBS_MISMATCH, | |
132 PORT_GetError()); | |
133 } | |
134 | |
135 VLOG(1) << "Signature verification succeeded"; | |
136 | |
137 return AuthResult(); | |
138 } | |
139 | |
140 } // namespace cast_channel | |
141 } // namespace core_api | |
142 } // namespace extensions | |
OLD | NEW |