OLD | NEW |
| (Empty) |
1 // Copyright 2014 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/quic/crypto/channel_id_chromium.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/stl_util.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "crypto/ec_private_key.h" | |
12 #include "crypto/ec_signature_creator.h" | |
13 #include "net/base/net_errors.h" | |
14 #include "net/cert/asn1_util.h" | |
15 #include "net/ssl/channel_id_service.h" | |
16 | |
17 namespace net { | |
18 | |
19 ChannelIDKeyChromium::ChannelIDKeyChromium( | |
20 crypto::ECPrivateKey* ec_private_key) | |
21 : ec_private_key_(ec_private_key) {} | |
22 | |
23 ChannelIDKeyChromium::~ChannelIDKeyChromium() {} | |
24 | |
25 bool ChannelIDKeyChromium::Sign(base::StringPiece signed_data, | |
26 std::string* out_signature) const { | |
27 scoped_ptr<crypto::ECSignatureCreator> sig_creator( | |
28 crypto::ECSignatureCreator::Create(ec_private_key_.get())); | |
29 if (!sig_creator) { | |
30 return false; | |
31 } | |
32 const size_t len1 = strlen(ChannelIDVerifier::kContextStr) + 1; | |
33 const size_t len2 = strlen(ChannelIDVerifier::kClientToServerStr) + 1; | |
34 std::vector<uint8> data(len1 + len2 + signed_data.size()); | |
35 memcpy(&data[0], ChannelIDVerifier::kContextStr, len1); | |
36 memcpy(&data[len1], ChannelIDVerifier::kClientToServerStr, len2); | |
37 memcpy(&data[len1 + len2], signed_data.data(), signed_data.size()); | |
38 std::vector<uint8> der_signature; | |
39 if (!sig_creator->Sign(&data[0], data.size(), &der_signature)) { | |
40 return false; | |
41 } | |
42 std::vector<uint8> raw_signature; | |
43 if (!sig_creator->DecodeSignature(der_signature, &raw_signature)) { | |
44 return false; | |
45 } | |
46 memcpy(WriteInto(out_signature, raw_signature.size() + 1), | |
47 &raw_signature[0], raw_signature.size()); | |
48 return true; | |
49 } | |
50 | |
51 std::string ChannelIDKeyChromium::SerializeKey() const { | |
52 std::string out_key; | |
53 if (!ec_private_key_->ExportRawPublicKey(&out_key)) { | |
54 return std::string(); | |
55 } | |
56 return out_key; | |
57 } | |
58 | |
59 // A Job handles the lookup of a single channel ID. It is owned by the | |
60 // ChannelIDSource. If the operation can not complete synchronously, it will | |
61 // notify the ChannelIDSource upon completion. | |
62 class ChannelIDSourceChromium::Job { | |
63 public: | |
64 Job(ChannelIDSourceChromium* channel_id_source, | |
65 ChannelIDService* channel_id_service); | |
66 | |
67 // Starts the channel ID lookup. If |QUIC_PENDING| is returned, then | |
68 // |callback| will be invoked asynchronously when the operation completes. | |
69 QuicAsyncStatus GetChannelIDKey(const std::string& hostname, | |
70 scoped_ptr<ChannelIDKey>* channel_id_key, | |
71 ChannelIDSourceCallback* callback); | |
72 | |
73 private: | |
74 enum State { | |
75 STATE_NONE, | |
76 STATE_GET_CHANNEL_ID_KEY, | |
77 STATE_GET_CHANNEL_ID_KEY_COMPLETE, | |
78 }; | |
79 | |
80 int DoLoop(int last_io_result); | |
81 void OnIOComplete(int result); | |
82 int DoGetChannelIDKey(int result); | |
83 int DoGetChannelIDKeyComplete(int result); | |
84 | |
85 // Channel ID source to notify when this jobs completes. | |
86 ChannelIDSourceChromium* const channel_id_source_; | |
87 | |
88 ChannelIDService* const channel_id_service_; | |
89 | |
90 std::string channel_id_private_key_; | |
91 std::string channel_id_cert_; | |
92 ChannelIDService::RequestHandle channel_id_request_handle_; | |
93 | |
94 // |hostname| specifies the hostname for which we need a channel ID. | |
95 std::string hostname_; | |
96 | |
97 scoped_ptr<ChannelIDSourceCallback> callback_; | |
98 | |
99 scoped_ptr<ChannelIDKey> channel_id_key_; | |
100 | |
101 State next_state_; | |
102 | |
103 DISALLOW_COPY_AND_ASSIGN(Job); | |
104 }; | |
105 | |
106 ChannelIDSourceChromium::Job::Job( | |
107 ChannelIDSourceChromium* channel_id_source, | |
108 ChannelIDService* channel_id_service) | |
109 : channel_id_source_(channel_id_source), | |
110 channel_id_service_(channel_id_service), | |
111 next_state_(STATE_NONE) { | |
112 } | |
113 | |
114 QuicAsyncStatus ChannelIDSourceChromium::Job::GetChannelIDKey( | |
115 const std::string& hostname, | |
116 scoped_ptr<ChannelIDKey>* channel_id_key, | |
117 ChannelIDSourceCallback* callback) { | |
118 DCHECK(channel_id_key); | |
119 DCHECK(callback); | |
120 | |
121 if (STATE_NONE != next_state_) { | |
122 DLOG(DFATAL) << "GetChannelIDKey has begun"; | |
123 return QUIC_FAILURE; | |
124 } | |
125 | |
126 channel_id_key_.reset(); | |
127 | |
128 hostname_ = hostname; | |
129 | |
130 next_state_ = STATE_GET_CHANNEL_ID_KEY; | |
131 switch (DoLoop(OK)) { | |
132 case OK: | |
133 channel_id_key->reset(channel_id_key_.release()); | |
134 return QUIC_SUCCESS; | |
135 case ERR_IO_PENDING: | |
136 callback_.reset(callback); | |
137 return QUIC_PENDING; | |
138 default: | |
139 channel_id_key->reset(); | |
140 return QUIC_FAILURE; | |
141 } | |
142 } | |
143 | |
144 int ChannelIDSourceChromium::Job::DoLoop(int last_result) { | |
145 int rv = last_result; | |
146 do { | |
147 State state = next_state_; | |
148 next_state_ = STATE_NONE; | |
149 switch (state) { | |
150 case STATE_GET_CHANNEL_ID_KEY: | |
151 DCHECK(rv == OK); | |
152 rv = DoGetChannelIDKey(rv); | |
153 break; | |
154 case STATE_GET_CHANNEL_ID_KEY_COMPLETE: | |
155 rv = DoGetChannelIDKeyComplete(rv); | |
156 break; | |
157 case STATE_NONE: | |
158 default: | |
159 rv = ERR_UNEXPECTED; | |
160 LOG(DFATAL) << "unexpected state " << state; | |
161 break; | |
162 } | |
163 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
164 return rv; | |
165 } | |
166 | |
167 void ChannelIDSourceChromium::Job::OnIOComplete(int result) { | |
168 int rv = DoLoop(result); | |
169 if (rv != ERR_IO_PENDING) { | |
170 scoped_ptr<ChannelIDSourceCallback> callback(callback_.release()); | |
171 callback->Run(&channel_id_key_); | |
172 // Will delete |this|. | |
173 channel_id_source_->OnJobComplete(this); | |
174 } | |
175 } | |
176 | |
177 int ChannelIDSourceChromium::Job::DoGetChannelIDKey(int result) { | |
178 next_state_ = STATE_GET_CHANNEL_ID_KEY_COMPLETE; | |
179 | |
180 return channel_id_service_->GetOrCreateChannelID( | |
181 hostname_, | |
182 &channel_id_private_key_, | |
183 &channel_id_cert_, | |
184 base::Bind(&ChannelIDSourceChromium::Job::OnIOComplete, | |
185 base::Unretained(this)), | |
186 &channel_id_request_handle_); | |
187 } | |
188 | |
189 int ChannelIDSourceChromium::Job::DoGetChannelIDKeyComplete(int result) { | |
190 DCHECK_EQ(STATE_NONE, next_state_); | |
191 if (result != OK) { | |
192 DLOG(WARNING) << "Failed to look up channel ID: " << ErrorToString(result); | |
193 return result; | |
194 } | |
195 | |
196 std::vector<uint8> encrypted_private_key_info( | |
197 channel_id_private_key_.size()); | |
198 memcpy(&encrypted_private_key_info[0], channel_id_private_key_.data(), | |
199 channel_id_private_key_.size()); | |
200 | |
201 base::StringPiece spki_piece; | |
202 if (!asn1::ExtractSPKIFromDERCert(channel_id_cert_, &spki_piece)) { | |
203 return ERR_UNEXPECTED; | |
204 } | |
205 std::vector<uint8> subject_public_key_info(spki_piece.size()); | |
206 memcpy(&subject_public_key_info[0], spki_piece.data(), spki_piece.size()); | |
207 | |
208 crypto::ECPrivateKey* ec_private_key = | |
209 crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( | |
210 ChannelIDService::kEPKIPassword, encrypted_private_key_info, | |
211 subject_public_key_info); | |
212 if (!ec_private_key) { | |
213 // TODO(wtc): use the new error code ERR_CHANNEL_ID_IMPORT_FAILED to be | |
214 // added in https://codereview.chromium.org/338093012/. | |
215 return ERR_UNEXPECTED; | |
216 } | |
217 channel_id_key_.reset(new ChannelIDKeyChromium(ec_private_key)); | |
218 | |
219 return result; | |
220 } | |
221 | |
222 ChannelIDSourceChromium::ChannelIDSourceChromium( | |
223 ChannelIDService* channel_id_service) | |
224 : channel_id_service_(channel_id_service) { | |
225 } | |
226 | |
227 ChannelIDSourceChromium::~ChannelIDSourceChromium() { | |
228 STLDeleteElements(&active_jobs_); | |
229 } | |
230 | |
231 QuicAsyncStatus ChannelIDSourceChromium::GetChannelIDKey( | |
232 const std::string& hostname, | |
233 scoped_ptr<ChannelIDKey>* channel_id_key, | |
234 ChannelIDSourceCallback* callback) { | |
235 scoped_ptr<Job> job(new Job(this, channel_id_service_)); | |
236 QuicAsyncStatus status = job->GetChannelIDKey(hostname, channel_id_key, | |
237 callback); | |
238 if (status == QUIC_PENDING) { | |
239 active_jobs_.insert(job.release()); | |
240 } | |
241 return status; | |
242 } | |
243 | |
244 void ChannelIDSourceChromium::OnJobComplete(Job* job) { | |
245 active_jobs_.erase(job); | |
246 delete job; | |
247 } | |
248 | |
249 } // namespace net | |
OLD | NEW |