Home | History | Annotate | Download | only in socket
      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