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 <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 |