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/lazy_instance.h" 22 #include "base/memory/scoped_ptr.h" 23 #include "base/pickle.h" 24 #include "base/threading/non_thread_safe.h" 25 #include "crypto/encryptor.h" 26 #include "crypto/symmetric_key.h" 27 #include "net/base/completion_callback.h" 28 #include "net/base/dns_util.h" 29 #include "net/base/dnsrr_resolver.h" 30 #include "net/base/net_errors.h" 31 #include "net/base/net_log.h" 32 33 namespace net { 34 35 namespace { 36 37 // A DER encoded SubjectPublicKeyInfo structure containing the server's public 38 // key. 39 const uint8 kServerPublicKey[] = { 40 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 41 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 42 0x04, 0xc7, 0xea, 0x88, 0x60, 0x52, 0xe3, 0xa3, 0x3e, 0x39, 0x92, 0x0f, 0xa4, 43 0x3d, 0xba, 0xd8, 0x02, 0x2d, 0x06, 0x4d, 0x64, 0x98, 0x66, 0xb4, 0x82, 0xf0, 44 0x23, 0xa6, 0xd8, 0x37, 0x55, 0x7c, 0x01, 0xbf, 0x18, 0xd8, 0x16, 0x9e, 0x66, 45 0xdc, 0x49, 0xbf, 0x2e, 0x86, 0xe3, 0x99, 0xbd, 0xb3, 0x75, 0x25, 0x61, 0x04, 46 0x6c, 0x2e, 0xfb, 0x32, 0x42, 0x27, 0xe4, 0x23, 0xea, 0xcd, 0x81, 0x62, 0xc1, 47 }; 48 49 const unsigned kMaxUploadsPerSession = 10; 50 51 // DnsCertLimits is a singleton class which keeps track of which hosts we have 52 // uploaded reports for in this session. Since some users will be behind MITM 53 // proxies, they would otherwise upload for every host and we don't wish to 54 // spam the upload server. 55 class DnsCertLimits { 56 public: 57 DnsCertLimits() { } 58 59 // HaveReachedMaxUploads returns true iff we have uploaded the maximum number 60 // of DNS certificate reports for this session. 61 bool HaveReachedMaxUploads() { 62 return uploaded_hostnames_.size() >= kMaxUploadsPerSession; 63 } 64 65 // HaveReachedMaxUploads returns true iff we have already uploaded a report 66 // about the given hostname in this session. 67 bool HaveUploadedForHostname(const std::string& hostname) { 68 return uploaded_hostnames_.count(hostname) > 0; 69 } 70 71 void DidUpload(const std::string& hostname) { 72 uploaded_hostnames_.insert(hostname); 73 } 74 75 private: 76 friend struct base::DefaultLazyInstanceTraits<DnsCertLimits>; 77 78 std::set<std::string> uploaded_hostnames_; 79 80 DISALLOW_COPY_AND_ASSIGN(DnsCertLimits); 81 }; 82 83 static base::LazyInstance<DnsCertLimits> g_dns_cert_limits( 84 base::LINKER_INITIALIZED); 85 86 // DnsCertProvenanceCheck performs the DNS lookup of the certificate. This 87 // class is self-deleting. 88 class DnsCertProvenanceCheck : public base::NonThreadSafe { 89 public: 90 DnsCertProvenanceCheck( 91 const std::string& hostname, 92 DnsRRResolver* dnsrr_resolver, 93 DnsCertProvenanceChecker::Delegate* delegate, 94 const std::vector<base::StringPiece>& der_certs) 95 : hostname_(hostname), 96 dnsrr_resolver_(dnsrr_resolver), 97 delegate_(delegate), 98 der_certs_(der_certs.size()), 99 handle_(DnsRRResolver::kInvalidHandle), 100 ALLOW_THIS_IN_INITIALIZER_LIST(callback_( 101 this, &DnsCertProvenanceCheck::ResolutionComplete)) { 102 for (size_t i = 0; i < der_certs.size(); i++) 103 der_certs_[i] = der_certs[i].as_string(); 104 } 105 106 void Start() { 107 DCHECK(CalledOnValidThread()); 108 109 if (der_certs_.empty()) 110 return; 111 112 DnsCertLimits* const limits = g_dns_cert_limits.Pointer(); 113 if (limits->HaveReachedMaxUploads() || 114 limits->HaveUploadedForHostname(hostname_)) { 115 return; 116 } 117 118 uint8 fingerprint[SHA1_LENGTH]; 119 SECStatus rv = HASH_HashBuf( 120 HASH_AlgSHA1, fingerprint, (uint8*) der_certs_[0].data(), 121 der_certs_[0].size()); 122 DCHECK_EQ(SECSuccess, rv); 123 char fingerprint_hex[SHA1_LENGTH * 2 + 1]; 124 for (unsigned i = 0; i < sizeof(fingerprint); i++) { 125 static const char hextable[] = "0123456789abcdef"; 126 fingerprint_hex[i*2] = hextable[fingerprint[i] >> 4]; 127 fingerprint_hex[i*2 + 1] = hextable[fingerprint[i] & 15]; 128 } 129 fingerprint_hex[SHA1_LENGTH * 2] = 0; 130 131 static const char kBaseCertName[] = ".certs.googlednstest.com"; 132 domain_.assign(fingerprint_hex); 133 domain_.append(kBaseCertName); 134 135 handle_ = dnsrr_resolver_->Resolve( 136 domain_, kDNS_TXT, 0 /* flags */, &callback_, &response_, 137 0 /* priority */, BoundNetLog()); 138 if (handle_ == DnsRRResolver::kInvalidHandle) { 139 LOG(ERROR) << "Failed to resolve " << domain_ << " for " << hostname_; 140 delete this; 141 } 142 } 143 144 private: 145 void ResolutionComplete(int status) { 146 DCHECK(CalledOnValidThread()); 147 148 if (status == ERR_NAME_NOT_RESOLVED || 149 (status == OK && response_.rrdatas.empty())) { 150 LOG(ERROR) << "FAILED" 151 << " hostname:" << hostname_ 152 << " domain:" << domain_; 153 g_dns_cert_limits.Get().DidUpload(hostname_); 154 LogCertificates(der_certs_); 155 delegate_->OnDnsCertLookupFailed(hostname_, der_certs_); 156 } else if (status == OK) { 157 LOG(ERROR) << "GOOD" 158 << " hostname:" << hostname_ 159 << " resp:" << response_.rrdatas[0]; 160 } else { 161 LOG(ERROR) << "Unknown error " << status << " for " << domain_; 162 } 163 164 delete this; 165 } 166 167 // LogCertificates writes a certificate chain, in PEM format, to LOG(ERROR). 168 static void LogCertificates( 169 const std::vector<std::string>& der_certs) { 170 std::string dump; 171 bool first = true; 172 173 for (std::vector<std::string>::const_iterator 174 i = der_certs.begin(); i != der_certs.end(); i++) { 175 if (!first) 176 dump += "\n"; 177 first = false; 178 179 dump += "-----BEGIN CERTIFICATE-----\n"; 180 std::string b64_encoded; 181 base::Base64Encode(*i, &b64_encoded); 182 for (size_t i = 0; i < b64_encoded.size();) { 183 size_t todo = b64_encoded.size() - i; 184 if (todo > 64) 185 todo = 64; 186 dump += b64_encoded.substr(i, todo); 187 dump += "\n"; 188 i += todo; 189 } 190 dump += "-----END CERTIFICATE-----"; 191 } 192 193 LOG(ERROR) << "Offending certificates:\n" << dump; 194 } 195 196 const std::string hostname_; 197 std::string domain_; 198 DnsRRResolver* dnsrr_resolver_; 199 DnsCertProvenanceChecker::Delegate* const delegate_; 200 std::vector<std::string> der_certs_; 201 RRResponse response_; 202 DnsRRResolver::Handle handle_; 203 CompletionCallbackImpl<DnsCertProvenanceCheck> callback_; 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 365