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
33namespace net {
34
35namespace {
36
37// A DER encoded SubjectPublicKeyInfo structure containing the server's public
38// key.
39const 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
49const 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.
55class 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
83static 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.
88class 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
206SECKEYPublicKey* 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
221DnsCertProvenanceChecker::Delegate::~Delegate() {
222}
223
224DnsCertProvenanceChecker::~DnsCertProvenanceChecker() {
225}
226
227void 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
238std::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
341namespace net {
342
343DnsCertProvenanceChecker::Delegate::~Delegate() {
344}
345
346DnsCertProvenanceChecker::~DnsCertProvenanceChecker() {
347}
348
349void DnsCertProvenanceChecker::DoAsyncLookup(
350    const std::string& hostname,
351    const std::vector<base::StringPiece>& der_certs,
352    DnsRRResolver* dnsrr_resolver,
353    Delegate* delegate) {
354}
355
356std::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