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/messaging/message_property_provider.h"
6
7#include "base/json/json_writer.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "base/strings/string_piece.h"
11#include "base/values.h"
12#include "chrome/browser/profiles/profile.h"
13#include "content/public/browser/browser_thread.h"
14#include "extensions/common/api/runtime.h"
15#include "net/base/completion_callback.h"
16#include "net/cert/asn1_util.h"
17#include "net/cert/jwk_serializer.h"
18#include "net/ssl/channel_id_service.h"
19#include "net/url_request/url_request_context.h"
20#include "net/url_request/url_request_context_getter.h"
21#include "url/gurl.h"
22
23namespace extensions {
24
25MessagePropertyProvider::MessagePropertyProvider() {}
26
27void MessagePropertyProvider::GetChannelID(Profile* profile,
28    const GURL& source_url, const ChannelIDCallback& reply) {
29  if (!source_url.is_valid()) {
30    // This isn't a real URL, so there's no sense in looking for a channel ID
31    // for it. Dispatch with an empty tls channel ID.
32    reply.Run(std::string());
33    return;
34  }
35  scoped_refptr<net::URLRequestContextGetter> request_context_getter(
36      profile->GetRequestContext());
37  content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
38      base::Bind(&MessagePropertyProvider::GetChannelIDOnIOThread,
39                 base::MessageLoopProxy::current(),
40                 request_context_getter,
41                 source_url.host(),
42                 reply));
43}
44
45// Helper struct to bind the memory addresses that will be written to by
46// ChannelIDService::GetChannelID to the callback provided to
47// MessagePropertyProvider::GetChannelID.
48struct MessagePropertyProvider::GetChannelIDOutput {
49  std::string domain_bound_private_key;
50  std::string domain_bound_cert;
51  net::ChannelIDService::RequestHandle request_handle;
52};
53
54// static
55void MessagePropertyProvider::GetChannelIDOnIOThread(
56    scoped_refptr<base::TaskRunner> original_task_runner,
57    scoped_refptr<net::URLRequestContextGetter> request_context_getter,
58    const std::string& host,
59    const ChannelIDCallback& reply) {
60  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
61  net::ChannelIDService* channel_id_service =
62      request_context_getter->GetURLRequestContext()->
63          channel_id_service();
64  GetChannelIDOutput* output = new GetChannelIDOutput();
65  net::CompletionCallback net_completion_callback =
66      base::Bind(&MessagePropertyProvider::GotChannelID,
67                 original_task_runner,
68                 base::Owned(output),
69                 reply);
70  int status = channel_id_service->GetChannelID(
71      host,
72      &output->domain_bound_private_key,
73      &output->domain_bound_cert,
74      net_completion_callback,
75      &output->request_handle);
76  if (status == net::ERR_IO_PENDING)
77    return;
78  GotChannelID(original_task_runner, output, reply, status);
79}
80
81// static
82void MessagePropertyProvider::GotChannelID(
83    scoped_refptr<base::TaskRunner> original_task_runner,
84    struct GetChannelIDOutput* output,
85    const ChannelIDCallback& reply,
86    int status) {
87  base::Closure no_tls_channel_id_closure = base::Bind(reply, "");
88  if (status != net::OK) {
89    original_task_runner->PostTask(FROM_HERE, no_tls_channel_id_closure);
90    return;
91  }
92  base::StringPiece spki;
93  if (!net::asn1::ExtractSPKIFromDERCert(output->domain_bound_cert, &spki)) {
94    original_task_runner->PostTask(FROM_HERE, no_tls_channel_id_closure);
95    return;
96  }
97  base::DictionaryValue jwk_value;
98  if (!net::JwkSerializer::ConvertSpkiFromDerToJwk(spki, &jwk_value)) {
99    original_task_runner->PostTask(FROM_HERE, no_tls_channel_id_closure);
100    return;
101  }
102  std::string jwk_str;
103  base::JSONWriter::Write(&jwk_value, &jwk_str);
104  original_task_runner->PostTask(FROM_HERE, base::Bind(reply, jwk_str));
105}
106
107}  // namespace extensions
108