OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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 "chrome/browser/extensions/api/networking_private/networking_private_cr ypto.h" | |
6 | |
7 #include <cert.h> | |
8 #include <cryptohi.h> | |
9 #include <keyhi.h> | |
10 #include <keythi.h> | |
11 #include <pk11pub.h> | |
12 #include <sechash.h> | |
13 #include <secport.h> | |
14 | |
15 #include "base/base64.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "base/strings/string_number_conversions.h" | |
18 #include "base/strings/string_util.h" | |
19 #include "base/strings/stringprintf.h" | |
20 #include "crypto/nss_util.h" | |
21 #include "crypto/rsa_private_key.h" | |
22 #include "net/cert/pem_tokenizer.h" | |
23 #include "net/cert/x509_certificate.h" | |
24 | |
25 const char kTrustedCAPublicKeyDER[] = | |
26 "MIIBCgKCAQEAvCKAvYD2OiEAO652XjV/" | |
27 "PcNkXFWUhjQvBYcozfdpjBezUKe4gvrfx0Mt1n6roG+" | |
28 "3E3KApEcVwSCZUM3sFGIJW6SYzdJBtjZO/" | |
29 "+guMjBKgahCo2ybM27KsvVTZuAnU4YahR6nOT9Kd477VGZm+1hUwF45x/" | |
30 "VQBgvgitTO4WpVH4sXAOZpoyfmCCVpPBKdjQUs1i6iMd60UlDWIEnecaD5rSBAEvHdJevV5rg2" | |
31 "9NaPf8pD3NcQW+Y/UYqFs/P/9gMtyyNPnK0Y55MFjKxSmvdM6Zl6vm5+TQrjxhypk/" | |
32 "o6pZFdHL1m68xg3IZ0ys/4khyYfVf6YUeeq4C35EiAKpLFGwIDAQAB"; | |
Ryan Sleevi
2013/08/28 21:52:29
Please fix this by hand. Clang format is crap here
mef
2013/08/29 14:59:09
Done. Is there actually a better way to define bin
| |
33 | |
34 namespace { | |
35 bool GetDERFromPEM(const std::string& pem_data, | |
36 const std::string& pem_type, | |
37 std::string* der_output) { | |
38 std::vector<std::string> headers; | |
39 headers.push_back(pem_type); | |
40 net::PEMTokenizer pem_tok(pem_data, headers); | |
41 if (!pem_tok.GetNext()) { | |
42 return false; | |
43 } | |
44 | |
45 *der_output = pem_tok.data(); | |
46 return true; | |
47 } | |
48 | |
49 struct NetworkingPrivateCryptoContext { | |
50 NetworkingPrivateCryptoContext(); | |
51 ~NetworkingPrivateCryptoContext(); | |
52 | |
53 SECKEYPublicKey* ca_public_key; | |
54 SECKEYPublicKey* public_key; | |
55 CERTCertificate* cert; | |
Ryan Sleevi
2013/08/28 21:52:29
D'oh. I missed that all of these could be locally
mef
2013/08/29 14:59:09
Done. Nice!
| |
56 }; | |
57 | |
58 NetworkingPrivateCryptoContext::NetworkingPrivateCryptoContext() | |
59 : ca_public_key(NULL), public_key(NULL), cert(NULL) {} | |
60 | |
61 NetworkingPrivateCryptoContext::~NetworkingPrivateCryptoContext() { | |
62 if (ca_public_key) | |
63 SECKEY_DestroyPublicKey(ca_public_key); | |
64 if (public_key) | |
65 SECKEY_DestroyPublicKey(public_key); | |
66 if (cert) | |
67 CERT_DestroyCertificate(cert); | |
68 } | |
69 } // namespace | |
70 | |
71 NetworkingPrivateCrypto::NetworkingPrivateCrypto() {} | |
72 | |
73 NetworkingPrivateCrypto::~NetworkingPrivateCrypto() {} | |
74 | |
75 bool NetworkingPrivateCrypto::VerifyCredentials( | |
76 const std::string& certificate, | |
77 const std::string& signed_data, | |
78 const std::string& unsigned_data, | |
79 const std::string& connected_mac) { | |
80 crypto::EnsureNSSInit(); | |
81 NetworkingPrivateCryptoContext ctx; | |
82 | |
83 std::string cert_data; | |
84 if (!GetDERFromPEM(certificate, "CERTIFICATE", &cert_data)) { | |
85 LOG(ERROR) << "Failed to parse certificate."; | |
86 return false; | |
87 } | |
88 SECItem der_cert = { | |
89 siDERCertBuffer, | |
90 reinterpret_cast<unsigned char*>(const_cast<char*>(cert_data.c_str())), | |
91 cert_data.length()}; | |
92 // Parse into a certificate structure. | |
93 ctx.cert = CERT_NewTempCertificate( | |
94 CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); | |
95 if (!ctx.cert) { | |
96 LOG(ERROR) << "Failed to parse certificate."; | |
97 return false; | |
98 } | |
99 // Check that the certificate is signed by trusted CA. | |
100 std::string ca_key_der; | |
101 base::Base64Decode(kTrustedCAPublicKeyDER, &ca_key_der); | |
102 SECItem trusted_ca_key_der_item = { | |
103 siDERCertBuffer, | |
104 reinterpret_cast<unsigned char*>(const_cast<char*>(ca_key_der.c_str())), | |
105 ca_key_der.size()}; | |
106 ctx.ca_public_key = | |
107 SECKEY_ImportDERPublicKey(&trusted_ca_key_der_item, CKK_RSA); | |
108 SECStatus verified = CERT_VerifySignedDataWithPublicKey( | |
109 &ctx.cert->signatureWrap, ctx.ca_public_key, NULL); | |
110 if (verified != SECSuccess) { | |
111 LOG(ERROR) << "Certificate is not issued by trusted CA."; | |
112 return false; | |
113 } | |
114 | |
115 // Check that the device listed in the certificate is correct. | |
116 // Something like evt_e161 001a11ffacdf | |
117 char* common_name = CERT_GetCommonName(&ctx.cert->subject); | |
Ryan Sleevi
2013/08/28 21:52:29
BUG: This can return NULL.
mef
2013/08/29 14:59:09
Done.
| |
118 std::string subject_name = common_name; | |
Ryan Sleevi
2013/08/28 21:52:29
Use ctor form, not assignment form.
mef
2013/08/29 14:59:09
Done.
| |
119 PORT_Free(common_name); | |
120 std::string translated_mac; | |
121 RemoveChars(connected_mac, ":", &translated_mac); | |
122 if (!EndsWith(subject_name, translated_mac, false)) { | |
123 LOG(ERROR) << "MAC addresses don't match."; | |
124 return false; | |
125 } | |
126 | |
127 // Make sure that the certificate matches the unsigned data presented. | |
128 // Verify that hash(unsigned_data) == public(signed_data) | |
129 ctx.public_key = CERT_ExtractPublicKey(ctx.cert); | |
130 if (!ctx.public_key) { | |
131 LOG(ERROR) << "Unable to extract public key from certificate."; | |
132 return false; | |
133 } | |
134 unsigned char rsa_hash[SHA1_LENGTH]; | |
135 SECStatus hashed = HASH_HashBuf(HASH_AlgSHA1, | |
136 rsa_hash, | |
137 reinterpret_cast<unsigned char*>( | |
138 const_cast<char*>(unsigned_data.c_str())), | |
139 unsigned_data.size()); | |
140 DCHECK(hashed == SECSuccess); | |
141 SECItem digest_item = {siBuffer, rsa_hash, sizeof(rsa_hash)}; | |
142 SECItem signature_item = { | |
143 siBuffer, | |
144 reinterpret_cast<unsigned char*>(const_cast<char*>(signed_data.c_str())), | |
145 static_cast<unsigned int>(signed_data.size())}; | |
146 verified = VFY_VerifyDigestDirect(&digest_item, | |
Ryan Sleevi
2013/08/28 21:52:29
Sorry for the misdirect. VFY_VerifyData will let y
mef
2013/08/29 14:59:09
Done.
| |
147 ctx.public_key, | |
148 &signature_item, | |
149 SEC_OID_PKCS1_RSA_ENCRYPTION, | |
150 SEC_OID_SHA1, | |
151 NULL); | |
152 if (verified != SECSuccess) { | |
153 LOG(ERROR) << "Signed blobs did not match."; | |
154 return false; | |
155 } | |
156 return true; | |
157 } | |
158 | |
159 bool NetworkingPrivateCrypto::EncryptByteString(const std::string& public_key, | |
160 const std::string& data, | |
161 std::string* encrypted_output) { | |
162 crypto::EnsureNSSInit(); | |
163 NetworkingPrivateCryptoContext ctx; | |
164 | |
165 SECItem pub_key_der_item = { | |
166 siDERCertBuffer, | |
167 reinterpret_cast<unsigned char*>(const_cast<char*>(public_key.c_str())), | |
168 public_key.size()}; | |
169 ctx.public_key = SECKEY_ImportDERPublicKey(&pub_key_der_item, CKK_RSA); | |
170 if (!ctx.public_key) { | |
171 LOG(ERROR) << "Failed to parse public key."; | |
172 return false; | |
173 } | |
174 | |
175 size_t encrypted_length = SECKEY_SignatureLen(ctx.public_key); | |
176 scoped_ptr<unsigned char[]> rsa_output(new unsigned char[encrypted_length]); | |
177 SECStatus encrypted = PK11_PubEncryptPKCS1( | |
178 ctx.public_key, | |
179 rsa_output.get(), | |
180 reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())), | |
181 data.length(), | |
182 NULL); | |
183 if (encrypted != SECSuccess) { | |
184 LOG(ERROR) << "Error during encryption."; | |
185 return false; | |
186 } | |
187 encrypted_output->assign(reinterpret_cast<char*>(rsa_output.get()), | |
188 encrypted_length); | |
189 return true; | |
190 } | |
191 | |
192 bool NetworkingPrivateCrypto::DecryptByteString( | |
193 const std::string& private_key_pem, | |
194 const std::string& encrypted_data, | |
195 std::string* decrypted_output) { | |
196 crypto::EnsureNSSInit(); | |
197 | |
198 std::string private_key_der; | |
199 if (!GetDERFromPEM(private_key_pem, "PRIVATE KEY", &private_key_der)) { | |
200 LOG(ERROR) << "Failed to parse private key PEM."; | |
201 return false; | |
202 } | |
203 std::vector<uint8> private_key_data(private_key_der.begin(), | |
204 private_key_der.end()); | |
205 scoped_ptr<crypto::RSAPrivateKey> private_key( | |
206 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(private_key_data)); | |
207 if (!private_key) { | |
Ryan Sleevi
2013/08/28 21:52:29
nit: if (!private_key || !private_key->public_key(
mef
2013/08/29 14:59:09
Done.
| |
208 LOG(ERROR) << "Failed to parse private key DER."; | |
209 return false; | |
210 } | |
211 | |
212 size_t encrypted_length = SECKEY_SignatureLen(private_key->public_key()); | |
213 scoped_ptr<unsigned char[]> rsa_output(new unsigned char[encrypted_length]); | |
214 unsigned int output_length = 0; | |
215 SECStatus decrypted = | |
216 PK11_PrivDecryptPKCS1(private_key->key(), | |
217 rsa_output.get(), | |
218 &output_length, | |
219 encrypted_length, | |
220 reinterpret_cast<unsigned char*>( | |
221 const_cast<char*>(encrypted_data.data())), | |
222 encrypted_data.length()); | |
223 if (decrypted != SECSuccess) { | |
224 LOG(ERROR) << "Error during encryption."; | |
225 return false; | |
226 } | |
227 decrypted_output->assign(reinterpret_cast<char*>(rsa_output.get()), | |
228 output_length); | |
229 return true; | |
230 } | |
OLD | NEW |