Chromium Code Reviews| 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 <keyhi.h> | |
| 9 #include <keythi.h> | |
| 10 #include <pk11pub.h> | |
| 11 #include <sechash.h> | |
| 12 #include <secport.h> | |
| 13 | |
| 14 #include "base/base64.h" | |
| 15 #include "base/memory/scoped_ptr.h" | |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/strings/string_util.h" | |
| 18 #include "base/strings/stringprintf.h" | |
| 19 #include "crypto/nss_util.h" | |
| 20 #include "crypto/rsa_private_key.h" | |
| 21 #include "net/cert/pem_tokenizer.h" | |
| 22 #include "net/cert/x509_certificate.h" | |
| 23 | |
| 24 const char kTrustedCAExponent[] = "00010001"; | |
| 25 | |
| 26 const char kTrustedCAModulus[] = | |
| 27 "BC2280BD80F63A21003BAE765E357F3DC3645C559486342F058728CDF7698C17B350A7B8" | |
| 28 "82FADFC7432DD67EABA06FB7137280A44715C1209950CDEC1462095BA498CDD241B6364E" | |
| 29 "FFE82E32304A81A842A36C9B336ECAB2F55366E02753861A851EA7393F4A778EFB546666" | |
| 30 "FB5854C05E39C7F550060BE08AD4CEE16A551F8B1700E669A327E60825693C129D8D052C" | |
| 31 "D62EA231DEB45250D62049DE71A0F9AD204012F1DD25EBD5E6B836F4D68F7FCA43DCD710" | |
| 32 "5BE63F518A85B3F3FFF6032DCB234F9CAD18E793058CAC529AF74CE9997ABE6E7E4D0AE3" | |
| 33 "C61CA993FA3AA5915D1CBD66EBCC60DC8674CACFF8921C987D57FA61479EAB80B7E44880" | |
| 34 "2A92C51B"; | |
| 35 | |
| 36 namespace { | |
| 37 bool GetDERFromPEM(const std::string& pem_data, | |
| 38 const std::string& pem_type, | |
| 39 std::string* der_output) { | |
| 40 std::vector<std::string> headers; | |
| 41 headers.push_back(pem_type); | |
| 42 net::PEMTokenizer pem_tok(pem_data, headers); | |
| 43 if (!pem_tok.GetNext()) { | |
| 44 return false; | |
| 45 } | |
| 46 | |
| 47 *der_output = pem_tok.data(); | |
| 48 return true; | |
| 49 } | |
| 50 } // namespace | |
| 51 | |
| 52 NetworkingPrivateCrypto::NetworkingPrivateCrypto() | |
| 53 : ca_public_key_(NULL), | |
| 54 cert_public_key_(NULL), | |
| 55 cert_(NULL), | |
| 56 enc_public_key_(NULL) {} | |
| 57 | |
| 58 NetworkingPrivateCrypto::~NetworkingPrivateCrypto() { | |
| 59 if (ca_public_key_) | |
| 60 SECKEY_DestroyPublicKey(ca_public_key_); | |
| 61 if (cert_public_key_) | |
| 62 SECKEY_DestroyPublicKey(cert_public_key_); | |
| 63 if (cert_) | |
| 64 CERT_DestroyCertificate(cert_); | |
| 65 if (enc_public_key_) | |
| 66 SECKEY_DestroyPublicKey(enc_public_key_); | |
| 67 } | |
| 68 | |
| 69 bool NetworkingPrivateCrypto::VerifyCredentials( | |
| 70 const std::string& certificate, | |
| 71 const std::string& signed_data, | |
| 72 const std::string& unsigned_data, | |
| 73 const std::string& connected_mac) { | |
| 74 crypto::EnsureNSSInit(); | |
| 75 | |
| 76 net::CertificateList cl = | |
| 77 net::X509Certificate::CreateCertificateListFromBytes( | |
| 78 const_cast<char*>(certificate.c_str()), | |
| 79 certificate.size(), | |
| 80 net::X509Certificate::FORMAT_AUTO); | |
| 81 if (!cl.size()) { | |
| 82 LOG(ERROR) << "Failed to parse certificate."; | |
| 83 return false; | |
| 84 } | |
| 85 | |
| 86 // Check that the device listed in the certificate is correct. | |
| 87 std::string subject_name = cl[0]->subject().GetDisplayName(); | |
|
mef
2013/08/28 18:05:59
Alternatively I can get subject name directly from
Ryan Sleevi
2013/08/28 18:31:26
The above is really only intended for display purp
mef
2013/08/28 21:28:58
Done.
| |
| 88 // Something like evt_e161 001a11ffacdf | |
| 89 size_t subject_mac_pos = subject_name.rfind(' '); | |
| 90 if (subject_mac_pos == std::string::npos) { | |
| 91 LOG(ERROR) << "Badly formatted subject."; | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 std::string translated_mac; | |
| 96 RemoveChars(connected_mac, ":", &translated_mac); | |
| 97 if (base::strcasecmp(translated_mac.c_str(), | |
| 98 subject_name.substr(subject_mac_pos + 1).c_str())) { | |
|
Ryan Sleevi
2013/08/28 19:02:37
why not just use std::string::compare?
mef
2013/08/28 21:28:58
Done.
| |
| 99 LOG(ERROR) << "MAC addresses don't match."; | |
| 100 return false; | |
| 101 } | |
| 102 | |
| 103 std::string cert_data; | |
| 104 if (!GetDERFromPEM(certificate, "CERTIFICATE", &cert_data)) { | |
|
Ryan Sleevi
2013/08/28 19:02:37
Why are you parsing the certificate twice like thi
mef
2013/08/28 21:28:58
Done.
| |
| 105 LOG(ERROR) << "Failed to parse certificate."; | |
| 106 return false; | |
| 107 } | |
| 108 | |
| 109 SECItem der_cert; | |
| 110 der_cert.data = | |
| 111 reinterpret_cast<unsigned char*>(const_cast<char*>(cert_data.c_str())); | |
| 112 der_cert.len = cert_data.length(); | |
| 113 der_cert.type = siDERCertBuffer; | |
| 114 | |
| 115 // Parse into a certificate structure. | |
| 116 cert_ = CERT_NewTempCertificate( | |
| 117 CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE); | |
| 118 | |
| 119 if (!cert_) { | |
| 120 LOG(ERROR) << "Failed to parse certificate."; | |
| 121 return false; | |
| 122 } | |
| 123 | |
| 124 SECStatus verified = SECSuccess; | |
| 125 | |
| 126 // TODO(mef): Add exports to third_party/nss/nss/exports_win.def and roll DEPS. | |
| 127 #if !defined(OS_WIN) | |
| 128 crypto::PrivateKeyInfoCodec trusted_ca_key_codec(true); | |
|
mef
2013/08/28 18:05:59
crypto::PrivateKeyInfoCodec is causing linking err
Ryan Sleevi
2013/08/28 18:31:26
Correct, it's not exported, because it's not part
mef
2013/08/28 21:28:58
Done.
| |
| 129 base::HexStringToBytes(kTrustedCAModulus, trusted_ca_key_codec.modulus()); | |
| 130 base::HexStringToBytes(kTrustedCAExponent, | |
| 131 trusted_ca_key_codec.public_exponent()); | |
| 132 std::vector<uint8> trusted_ca_key_der; | |
| 133 if (!trusted_ca_key_codec.ExportPublicKey(&trusted_ca_key_der)) { | |
| 134 LOG(ERROR) << "Failed CA ExportPublicKey"; | |
| 135 return false; | |
| 136 } | |
| 137 | |
| 138 SECItem trusted_ca_key_der_item; | |
| 139 trusted_ca_key_der_item.data = | |
| 140 const_cast<unsigned char*>(&trusted_ca_key_der.front()); | |
| 141 trusted_ca_key_der_item.len = trusted_ca_key_der.size(); | |
| 142 | |
| 143 ca_public_key_ = SECKEY_ImportDERPublicKey(&trusted_ca_key_der_item, CKK_RSA); | |
| 144 verified = CERT_VerifySignedDataWithPublicKey( | |
|
mef
2013/08/28 18:05:59
Is it ok to add these to third_party/nss/nss/expor
Ryan Sleevi
2013/08/28 18:31:26
Use http://mxr.mozilla.org/nss/source/lib/nss/nss.
mef
2013/08/28 21:28:58
Done. I did go with CERT_VerifySignedDataWithPubli
| |
| 145 &cert_->signatureWrap, ca_public_key_, NULL); | |
| 146 if (verified != SECSuccess) { | |
| 147 LOG(ERROR) << "Certificate is not issued by trusted CA."; | |
| 148 return false; | |
| 149 } | |
| 150 #endif | |
| 151 | |
| 152 // Excellent, the certificate checks out, now make sure that the certificate | |
| 153 // matches the unsigned data presented. | |
| 154 // We're going to verify that hash(unsigned_data) == public(signed_data) | |
| 155 cert_public_key_ = CERT_ExtractPublicKey(cert_); | |
|
Ryan Sleevi
2013/08/28 18:31:26
Please go through the comments for "we" and look t
mef
2013/08/28 21:28:58
Done.
| |
| 156 | |
| 157 if (!cert_public_key_) { | |
| 158 LOG(ERROR) << "Unable to extract public key from certificate."; | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 struct rsa_hash { | |
| 163 unsigned char asn1[15]; | |
| 164 unsigned char data[SHA1_LENGTH]; | |
| 165 } rsa_hash = {{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, | |
| 166 0x1a, 0x05, 0x00, 0x04, 0x14}, }; | |
|
mef
2013/08/28 18:05:59
Is there a better way to add ASN1 prefix in front
Ryan Sleevi
2013/08/28 18:31:26
Yeah, don't use this API - you're totally using th
mef
2013/08/28 21:28:58
Done.
| |
| 167 | |
| 168 SECStatus hashed = HASH_HashBuf(HASH_AlgSHA1, | |
| 169 rsa_hash.data, | |
| 170 reinterpret_cast<unsigned char*>( | |
| 171 const_cast<char*>(unsigned_data.c_str())), | |
| 172 unsigned_data.size()); | |
| 173 DCHECK(hashed == SECSuccess); | |
| 174 SECItem signature_item = { | |
| 175 siBuffer, | |
| 176 reinterpret_cast<unsigned char*>(const_cast<char*>(signed_data.c_str())), | |
| 177 static_cast<unsigned int>(signed_data.size())}; | |
| 178 SECItem hash_item = {siBuffer, reinterpret_cast<unsigned char*>(&rsa_hash), | |
| 179 sizeof(rsa_hash)}; | |
| 180 | |
| 181 verified = PK11_Verify(cert_public_key_, &signature_item, &hash_item, NULL); | |
| 182 if (verified != SECSuccess) { | |
| 183 LOG(ERROR) << "Signed blobs did not match."; | |
| 184 return false; | |
| 185 } | |
| 186 | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 bool NetworkingPrivateCrypto::EncryptByteString(const std::string& public_key, | |
| 191 const std::string& data, | |
| 192 std::string* encrypted_output) { | |
| 193 crypto::EnsureNSSInit(); | |
| 194 | |
| 195 SECItem pub_key_der_item; | |
| 196 pub_key_der_item.data = | |
| 197 reinterpret_cast<unsigned char*>(const_cast<char*>(public_key.c_str())); | |
| 198 pub_key_der_item.len = public_key.size(); | |
| 199 | |
| 200 enc_public_key_ = SECKEY_ImportDERPublicKey(&pub_key_der_item, CKK_RSA); | |
| 201 if (!enc_public_key_) { | |
| 202 LOG(ERROR) << "Failed to parse public key."; | |
| 203 return false; | |
| 204 } | |
| 205 | |
| 206 size_t encrypted_length = SECKEY_SignatureLen(enc_public_key_); | |
| 207 scoped_ptr<unsigned char[]> rsa_output(new unsigned char[encrypted_length]); | |
| 208 | |
| 209 LOG(INFO) << "Encrypting data with public key."; | |
| 210 SECStatus encrypted = PK11_PubEncryptPKCS1( | |
|
mef
2013/08/28 18:05:59
Is it ok to add this to third_party/nss/nss/export
Ryan Sleevi
2013/08/28 18:31:26
Yes, this is part of nss.def.
| |
| 211 enc_public_key_, | |
| 212 rsa_output.get(), | |
| 213 // The API helpfully tells us that this operation will treat this buffer | |
| 214 // as read only, but fails to mark the parameter const. | |
| 215 reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())), | |
| 216 data.length(), | |
| 217 NULL); | |
| 218 if (encrypted != SECSuccess) { | |
| 219 LOG(ERROR) << "Error during encryption."; | |
| 220 return false; | |
| 221 } | |
| 222 | |
| 223 encrypted_output->assign(reinterpret_cast<char*>(rsa_output.get()), | |
| 224 encrypted_length); | |
| 225 | |
| 226 return true; | |
| 227 } | |
| 228 | |
| 229 bool NetworkingPrivateCrypto::DecryptByteString( | |
| 230 const std::string& private_key_pem, | |
| 231 const std::string& encrypted_data, | |
| 232 std::string* decrypted_output) { | |
| 233 crypto::EnsureNSSInit(); | |
| 234 std::string private_key_der; | |
| 235 if (!GetDERFromPEM(private_key_pem, "RSA PRIVATE KEY", &private_key_der)) { | |
| 236 LOG(ERROR) << "Failed to parse private key PEM."; | |
| 237 return false; | |
| 238 } | |
| 239 std::vector<uint8> private_key_data(private_key_der.begin(), | |
| 240 private_key_der.end()); | |
| 241 | |
| 242 LOG(ERROR) << "Private Key Len:" << private_key_data.size(); | |
| 243 | |
| 244 // TODO(mef): Figure out correct way to import DER private key. | |
| 245 scoped_ptr<crypto::RSAPrivateKey> private_key( | |
| 246 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(private_key_data)); | |
|
mef
2013/08/28 18:05:59
This returns a failure from ASN.1 parser, any sugg
| |
| 247 if (!private_key) { | |
| 248 LOG(ERROR) << "Failed to parse private key DER."; | |
| 249 return false; | |
| 250 } | |
| 251 | |
| 252 size_t encrypted_length = SECKEY_SignatureLen(private_key->public_key()); | |
| 253 scoped_ptr<unsigned char[]> rsa_output(new unsigned char[encrypted_length]); | |
| 254 | |
| 255 unsigned output_length = 0; | |
|
Ryan Sleevi
2013/08/28 19:02:37
Blergh, poor API spec if we're calling it 'unsigne
mef
2013/08/28 21:28:58
Done.
| |
| 256 | |
| 257 SECStatus decrypted = PK11_PrivDecryptPKCS1( | |
| 258 private_key->key(), | |
| 259 rsa_output.get(), | |
| 260 &output_length, | |
| 261 encrypted_length, | |
| 262 | |
| 263 // The API helpfully tells us that this operation will treat this buffer | |
| 264 // as read only, but fails to mark the parameter const. | |
| 265 reinterpret_cast<unsigned char*>( | |
| 266 const_cast<char*>(encrypted_data.data())), | |
| 267 encrypted_data.length()); | |
| 268 | |
| 269 if (decrypted != SECSuccess) { | |
| 270 LOG(ERROR) << "Error during encryption."; | |
| 271 return false; | |
| 272 } | |
| 273 | |
| 274 decrypted_output->assign(reinterpret_cast<char*>(rsa_output.get()), | |
| 275 output_length); | |
| 276 | |
| 277 return true; | |
| 278 } | |
| OLD | NEW |