| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 "net/socket/dns_cert_provenance_checker.h" | |
| 6 | |
| 7 #if !defined(USE_OPENSSL) | |
| 8 | |
| 9 #include <nspr.h> | |
| 10 | |
| 11 #include <hasht.h> | |
| 12 #include <keyhi.h> | |
| 13 #include <pk11pub.h> | |
| 14 #include <sechash.h> | |
| 15 | |
| 16 #include <set> | |
| 17 #include <string> | |
| 18 | |
| 19 #include "base/base64.h" | |
| 20 #include "base/basictypes.h" | |
| 21 #include "base/bind.h" | |
| 22 #include "base/lazy_instance.h" | |
| 23 #include "base/memory/scoped_ptr.h" | |
| 24 #include "base/pickle.h" | |
| 25 #include "base/threading/non_thread_safe.h" | |
| 26 #include "crypto/encryptor.h" | |
| 27 #include "crypto/symmetric_key.h" | |
| 28 #include "net/base/completion_callback.h" | |
| 29 #include "net/base/dns_util.h" | |
| 30 #include "net/base/dnsrr_resolver.h" | |
| 31 #include "net/base/net_errors.h" | |
| 32 #include "net/base/net_log.h" | |
| 33 | |
| 34 namespace net { | |
| 35 | |
| 36 namespace { | |
| 37 | |
| 38 // A DER encoded SubjectPublicKeyInfo structure containing the server's public | |
| 39 // key. | |
| 40 const uint8 kServerPublicKey[] = { | |
| 41 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, | |
| 42 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, | |
| 43 0x04, 0xc7, 0xea, 0x88, 0x60, 0x52, 0xe3, 0xa3, 0x3e, 0x39, 0x92, 0x0f, 0xa4, | |
| 44 0x3d, 0xba, 0xd8, 0x02, 0x2d, 0x06, 0x4d, 0x64, 0x98, 0x66, 0xb4, 0x82, 0xf0, | |
| 45 0x23, 0xa6, 0xd8, 0x37, 0x55, 0x7c, 0x01, 0xbf, 0x18, 0xd8, 0x16, 0x9e, 0x66, | |
| 46 0xdc, 0x49, 0xbf, 0x2e, 0x86, 0xe3, 0x99, 0xbd, 0xb3, 0x75, 0x25, 0x61, 0x04, | |
| 47 0x6c, 0x2e, 0xfb, 0x32, 0x42, 0x27, 0xe4, 0x23, 0xea, 0xcd, 0x81, 0x62, 0xc1, | |
| 48 }; | |
| 49 | |
| 50 const unsigned kMaxUploadsPerSession = 10; | |
| 51 | |
| 52 // DnsCertLimits is a singleton class which keeps track of which hosts we have | |
| 53 // uploaded reports for in this session. Since some users will be behind MITM | |
| 54 // proxies, they would otherwise upload for every host and we don't wish to | |
| 55 // spam the upload server. | |
| 56 class DnsCertLimits { | |
| 57 public: | |
| 58 DnsCertLimits() { } | |
| 59 | |
| 60 // HaveReachedMaxUploads returns true iff we have uploaded the maximum number | |
| 61 // of DNS certificate reports for this session. | |
| 62 bool HaveReachedMaxUploads() { | |
| 63 return uploaded_hostnames_.size() >= kMaxUploadsPerSession; | |
| 64 } | |
| 65 | |
| 66 // HaveReachedMaxUploads returns true iff we have already uploaded a report | |
| 67 // about the given hostname in this session. | |
| 68 bool HaveUploadedForHostname(const std::string& hostname) { | |
| 69 return uploaded_hostnames_.count(hostname) > 0; | |
| 70 } | |
| 71 | |
| 72 void DidUpload(const std::string& hostname) { | |
| 73 uploaded_hostnames_.insert(hostname); | |
| 74 } | |
| 75 | |
| 76 private: | |
| 77 friend struct base::DefaultLazyInstanceTraits<DnsCertLimits>; | |
| 78 | |
| 79 std::set<std::string> uploaded_hostnames_; | |
| 80 | |
| 81 DISALLOW_COPY_AND_ASSIGN(DnsCertLimits); | |
| 82 }; | |
| 83 | |
| 84 static base::LazyInstance<DnsCertLimits> g_dns_cert_limits = | |
| 85 LAZY_INSTANCE_INITIALIZER; | |
| 86 | |
| 87 // DnsCertProvenanceCheck performs the DNS lookup of the certificate. This | |
| 88 // class is self-deleting. | |
| 89 class DnsCertProvenanceCheck : public base::NonThreadSafe { | |
| 90 public: | |
| 91 DnsCertProvenanceCheck( | |
| 92 const std::string& hostname, | |
| 93 DnsRRResolver* dnsrr_resolver, | |
| 94 DnsCertProvenanceChecker::Delegate* delegate, | |
| 95 const std::vector<base::StringPiece>& der_certs) | |
| 96 : hostname_(hostname), | |
| 97 dnsrr_resolver_(dnsrr_resolver), | |
| 98 delegate_(delegate), | |
| 99 der_certs_(der_certs.size()), | |
| 100 handle_(DnsRRResolver::kInvalidHandle) { | |
| 101 for (size_t i = 0; i < der_certs.size(); i++) | |
| 102 der_certs_[i] = der_certs[i].as_string(); | |
| 103 } | |
| 104 | |
| 105 void Start() { | |
| 106 DCHECK(CalledOnValidThread()); | |
| 107 | |
| 108 if (der_certs_.empty()) | |
| 109 return; | |
| 110 | |
| 111 DnsCertLimits* const limits = g_dns_cert_limits.Pointer(); | |
| 112 if (limits->HaveReachedMaxUploads() || | |
| 113 limits->HaveUploadedForHostname(hostname_)) { | |
| 114 return; | |
| 115 } | |
| 116 | |
| 117 uint8 fingerprint[SHA1_LENGTH]; | |
| 118 SECStatus rv = HASH_HashBuf( | |
| 119 HASH_AlgSHA1, fingerprint, (uint8*) der_certs_[0].data(), | |
| 120 der_certs_[0].size()); | |
| 121 DCHECK_EQ(SECSuccess, rv); | |
| 122 char fingerprint_hex[SHA1_LENGTH * 2 + 1]; | |
| 123 for (unsigned i = 0; i < sizeof(fingerprint); i++) { | |
| 124 static const char hextable[] = "0123456789abcdef"; | |
| 125 fingerprint_hex[i*2] = hextable[fingerprint[i] >> 4]; | |
| 126 fingerprint_hex[i*2 + 1] = hextable[fingerprint[i] & 15]; | |
| 127 } | |
| 128 fingerprint_hex[SHA1_LENGTH * 2] = 0; | |
| 129 | |
| 130 static const char kBaseCertName[] = ".certs.googlednstest.com"; | |
| 131 domain_.assign(fingerprint_hex); | |
| 132 domain_.append(kBaseCertName); | |
| 133 | |
| 134 handle_ = dnsrr_resolver_->Resolve( | |
| 135 domain_, kDNS_TXT, 0 /* flags */, | |
| 136 base::Bind(&DnsCertProvenanceCheck::ResolutionComplete, | |
| 137 base::Unretained(this)), | |
| 138 &response_, 0 /* priority */, BoundNetLog()); | |
| 139 if (handle_ == DnsRRResolver::kInvalidHandle) { | |
| 140 LOG(ERROR) << "Failed to resolve " << domain_ << " for " << hostname_; | |
| 141 delete this; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 private: | |
| 146 void ResolutionComplete(int status) { | |
| 147 DCHECK(CalledOnValidThread()); | |
| 148 | |
| 149 if (status == ERR_NAME_NOT_RESOLVED || | |
| 150 (status == OK && response_.rrdatas.empty())) { | |
| 151 LOG(ERROR) << "FAILED" | |
| 152 << " hostname:" << hostname_ | |
| 153 << " domain:" << domain_; | |
| 154 g_dns_cert_limits.Get().DidUpload(hostname_); | |
| 155 LogCertificates(der_certs_); | |
| 156 delegate_->OnDnsCertLookupFailed(hostname_, der_certs_); | |
| 157 } else if (status == OK) { | |
| 158 LOG(ERROR) << "GOOD" | |
| 159 << " hostname:" << hostname_ | |
| 160 << " resp:" << response_.rrdatas[0]; | |
| 161 } else { | |
| 162 LOG(ERROR) << "Unknown error " << status << " for " << domain_; | |
| 163 } | |
| 164 | |
| 165 delete this; | |
| 166 } | |
| 167 | |
| 168 // LogCertificates writes a certificate chain, in PEM format, to LOG(ERROR). | |
| 169 static void LogCertificates( | |
| 170 const std::vector<std::string>& der_certs) { | |
| 171 std::string dump; | |
| 172 bool first = true; | |
| 173 | |
| 174 for (std::vector<std::string>::const_iterator | |
| 175 i = der_certs.begin(); i != der_certs.end(); i++) { | |
| 176 if (!first) | |
| 177 dump += "\n"; | |
| 178 first = false; | |
| 179 | |
| 180 dump += "-----BEGIN CERTIFICATE-----\n"; | |
| 181 std::string b64_encoded; | |
| 182 base::Base64Encode(*i, &b64_encoded); | |
| 183 for (size_t i = 0; i < b64_encoded.size();) { | |
| 184 size_t todo = b64_encoded.size() - i; | |
| 185 if (todo > 64) | |
| 186 todo = 64; | |
| 187 dump += b64_encoded.substr(i, todo); | |
| 188 dump += "\n"; | |
| 189 i += todo; | |
| 190 } | |
| 191 dump += "-----END CERTIFICATE-----"; | |
| 192 } | |
| 193 | |
| 194 LOG(ERROR) << "Offending certificates:\n" << dump; | |
| 195 } | |
| 196 | |
| 197 const std::string hostname_; | |
| 198 std::string domain_; | |
| 199 DnsRRResolver* dnsrr_resolver_; | |
| 200 DnsCertProvenanceChecker::Delegate* const delegate_; | |
| 201 std::vector<std::string> der_certs_; | |
| 202 RRResponse response_; | |
| 203 DnsRRResolver::Handle handle_; | |
| 204 }; | |
| 205 | |
| 206 SECKEYPublicKey* GetServerPubKey() { | |
| 207 SECItem der; | |
| 208 memset(&der, 0, sizeof(der)); | |
| 209 der.data = const_cast<uint8*>(kServerPublicKey); | |
| 210 der.len = sizeof(kServerPublicKey); | |
| 211 | |
| 212 CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); | |
| 213 SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); | |
| 214 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
| 215 | |
| 216 return public_key; | |
| 217 } | |
| 218 | |
| 219 } // namespace | |
| 220 | |
| 221 DnsCertProvenanceChecker::Delegate::~Delegate() { | |
| 222 } | |
| 223 | |
| 224 DnsCertProvenanceChecker::~DnsCertProvenanceChecker() { | |
| 225 } | |
| 226 | |
| 227 void DnsCertProvenanceChecker::DoAsyncLookup( | |
| 228 const std::string& hostname, | |
| 229 const std::vector<base::StringPiece>& der_certs, | |
| 230 DnsRRResolver* dnsrr_resolver, | |
| 231 Delegate* delegate) { | |
| 232 DnsCertProvenanceCheck* check = new DnsCertProvenanceCheck( | |
| 233 hostname, dnsrr_resolver, delegate, der_certs); | |
| 234 check->Start(); | |
| 235 } | |
| 236 | |
| 237 // static | |
| 238 std::string DnsCertProvenanceChecker::BuildEncryptedReport( | |
| 239 const std::string& hostname, | |
| 240 const std::vector<std::string>& der_certs) { | |
| 241 static const int kVersion = 0; | |
| 242 static const unsigned kKeySizeInBytes = 16; // AES-128 | |
| 243 static const unsigned kIVSizeInBytes = 16; // AES's block size | |
| 244 static const unsigned kPadSize = 4096; // we pad up to 4KB, | |
| 245 // This is a DER encoded, ANSI X9.62 CurveParams object which simply | |
| 246 // specifies P256. | |
| 247 static const uint8 kANSIX962CurveParams[] = { | |
| 248 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 | |
| 249 }; | |
| 250 | |
| 251 Pickle p; | |
| 252 p.WriteString(hostname); | |
| 253 p.WriteInt(der_certs.size()); | |
| 254 for (std::vector<std::string>::const_iterator | |
| 255 i = der_certs.begin(); i != der_certs.end(); i++) { | |
| 256 p.WriteString(*i); | |
| 257 } | |
| 258 // We pad to eliminate the possibility that someone could see the size of | |
| 259 // an upload and use that information to reduce the anonymity set of the | |
| 260 // certificate chain. | |
| 261 // The "2*sizeof(uint32)" here covers the padding length which we add next | |
| 262 // and Pickle's internal length which it includes at the beginning of the | |
| 263 // data. | |
| 264 unsigned pad_bytes = kPadSize - ((p.size() + 2*sizeof(uint32)) % kPadSize); | |
| 265 p.WriteUInt32(pad_bytes); | |
| 266 char* padding = new char[pad_bytes]; | |
| 267 memset(padding, 0, pad_bytes); | |
| 268 p.WriteData(padding, pad_bytes); | |
| 269 delete[] padding; | |
| 270 | |
| 271 // We generate a random public value and perform a DH key agreement with | |
| 272 // the server's fixed value. | |
| 273 SECKEYPublicKey* pub_key = NULL; | |
| 274 SECKEYPrivateKey* priv_key = NULL; | |
| 275 SECItem ec_der_params; | |
| 276 memset(&ec_der_params, 0, sizeof(ec_der_params)); | |
| 277 ec_der_params.data = const_cast<uint8*>(kANSIX962CurveParams); | |
| 278 ec_der_params.len = sizeof(kANSIX962CurveParams); | |
| 279 priv_key = SECKEY_CreateECPrivateKey(&ec_der_params, &pub_key, NULL); | |
| 280 SECKEYPublicKey* server_pub_key = GetServerPubKey(); | |
| 281 | |
| 282 // This extracts the big-endian, x value of the shared point. | |
| 283 // The values of the arguments match ssl3_SendECDHClientKeyExchange in NSS | |
| 284 // 3.12.8's lib/ssl/ssl3ecc.c | |
| 285 PK11SymKey* pms = PK11_PubDeriveWithKDF( | |
| 286 priv_key, server_pub_key, PR_FALSE /* is sender */, | |
| 287 NULL /* random a */, NULL /* random b */, CKM_ECDH1_DERIVE, | |
| 288 CKM_TLS_MASTER_KEY_DERIVE_DH, CKA_DERIVE, 0 /* key size */, | |
| 289 CKD_NULL /* KDF */, NULL /* shared data */, NULL /* wincx */); | |
| 290 SECKEY_DestroyPublicKey(server_pub_key); | |
| 291 SECStatus rv = PK11_ExtractKeyValue(pms); | |
| 292 DCHECK_EQ(SECSuccess, rv); | |
| 293 SECItem* x_data = PK11_GetKeyData(pms); | |
| 294 | |
| 295 // The key and IV are 128-bits and generated from a SHA256 hash of the x | |
| 296 // value. | |
| 297 char key_data[SHA256_LENGTH]; | |
| 298 HASH_HashBuf(HASH_AlgSHA256, reinterpret_cast<uint8*>(key_data), | |
| 299 x_data->data, x_data->len); | |
| 300 PK11_FreeSymKey(pms); | |
| 301 | |
| 302 DCHECK_GE(sizeof(key_data), kKeySizeInBytes + kIVSizeInBytes); | |
| 303 std::string raw_key(key_data, kKeySizeInBytes); | |
| 304 | |
| 305 scoped_ptr<crypto::SymmetricKey> symkey( | |
| 306 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key)); | |
| 307 std::string iv(key_data + kKeySizeInBytes, kIVSizeInBytes); | |
| 308 | |
| 309 crypto::Encryptor encryptor; | |
| 310 bool r = encryptor.Init(symkey.get(), crypto::Encryptor::CBC, iv); | |
| 311 CHECK(r); | |
| 312 | |
| 313 std::string plaintext(reinterpret_cast<const char*>(p.data()), p.size()); | |
| 314 std::string ciphertext; | |
| 315 encryptor.Encrypt(plaintext, &ciphertext); | |
| 316 | |
| 317 // We use another Pickle object to serialise the 'outer' wrapping of the | |
| 318 // plaintext. | |
| 319 Pickle outer; | |
| 320 outer.WriteInt(kVersion); | |
| 321 | |
| 322 SECItem* pub_key_serialized = SECKEY_EncodeDERSubjectPublicKeyInfo(pub_key); | |
| 323 outer.WriteString( | |
| 324 std::string(reinterpret_cast<char*>(pub_key_serialized->data), | |
| 325 pub_key_serialized->len)); | |
| 326 SECITEM_FreeItem(pub_key_serialized, PR_TRUE); | |
| 327 | |
| 328 outer.WriteString(ciphertext); | |
| 329 | |
| 330 SECKEY_DestroyPublicKey(pub_key); | |
| 331 SECKEY_DestroyPrivateKey(priv_key); | |
| 332 | |
| 333 return std::string(reinterpret_cast<const char*>(outer.data()), | |
| 334 outer.size()); | |
| 335 } | |
| 336 | |
| 337 } // namespace net | |
| 338 | |
| 339 #else // USE_OPENSSL | |
| 340 | |
| 341 namespace net { | |
| 342 | |
| 343 DnsCertProvenanceChecker::Delegate::~Delegate() { | |
| 344 } | |
| 345 | |
| 346 DnsCertProvenanceChecker::~DnsCertProvenanceChecker() { | |
| 347 } | |
| 348 | |
| 349 void DnsCertProvenanceChecker::DoAsyncLookup( | |
| 350 const std::string& hostname, | |
| 351 const std::vector<base::StringPiece>& der_certs, | |
| 352 DnsRRResolver* dnsrr_resolver, | |
| 353 Delegate* delegate) { | |
| 354 } | |
| 355 | |
| 356 std::string DnsCertProvenanceChecker::BuildEncryptedReport( | |
| 357 const std::string& hostname, | |
| 358 const std::vector<std::string>& der_certs) { | |
| 359 return ""; | |
| 360 } | |
| 361 | |
| 362 } // namespace net | |
| 363 | |
| 364 #endif // USE_OPENSSL | |
| OLD | NEW |