Add a basic settings page with a Polymer paper element which loads at chrome://md...
[chromium-blink-merge.git] / google_apis / gaia / oauth_request_signer.cc
blob115c14d6f01f8c1f8ed43f4956abfa4d8af8daa3
1 // Copyright (c) 2012 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.
5 #include "google_apis/gaia/oauth_request_signer.h"
7 #include <cctype>
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cstring>
11 #include <ctime>
12 #include <map>
13 #include <string>
15 #include "base/base64.h"
16 #include "base/format_macros.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/time/time.h"
22 #include "crypto/hmac.h"
23 #include "url/gurl.h"
25 namespace {
27 const int kHexBase = 16;
28 char kHexDigits[] = "0123456789ABCDEF";
29 const size_t kHmacDigestLength = 20;
30 const int kMaxNonceLength = 30;
31 const int kMinNonceLength = 15;
33 const char kOAuthConsumerKeyLabel[] = "oauth_consumer_key";
34 const char kOAuthNonceCharacters[] =
35 "abcdefghijklmnopqrstuvwyz"
36 "ABCDEFGHIJKLMNOPQRSTUVWYZ"
37 "0123456789_";
38 const char kOAuthNonceLabel[] = "oauth_nonce";
39 const char kOAuthSignatureLabel[] = "oauth_signature";
40 const char kOAuthSignatureMethodLabel[] = "oauth_signature_method";
41 const char kOAuthTimestampLabel[] = "oauth_timestamp";
42 const char kOAuthTokenLabel[] = "oauth_token";
43 const char kOAuthVersion[] = "1.0";
44 const char kOAuthVersionLabel[] = "oauth_version";
46 enum ParseQueryState {
47 START_STATE,
48 KEYWORD_STATE,
49 VALUE_STATE,
52 const std::string HttpMethodName(OAuthRequestSigner::HttpMethod method) {
53 switch (method) {
54 case OAuthRequestSigner::GET_METHOD:
55 return "GET";
56 case OAuthRequestSigner::POST_METHOD:
57 return "POST";
59 NOTREACHED();
60 return std::string();
63 const std::string SignatureMethodName(
64 OAuthRequestSigner::SignatureMethod method) {
65 switch (method) {
66 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
67 return "HMAC-SHA1";
68 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
69 return "RSA-SHA1";
70 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
71 return "PLAINTEXT";
73 NOTREACHED();
74 return std::string();
77 std::string BuildBaseString(const GURL& request_base_url,
78 OAuthRequestSigner::HttpMethod http_method,
79 const std::string& base_parameters) {
80 return base::StringPrintf("%s&%s&%s",
81 HttpMethodName(http_method).c_str(),
82 OAuthRequestSigner::Encode(
83 request_base_url.spec()).c_str(),
84 OAuthRequestSigner::Encode(
85 base_parameters).c_str());
88 std::string BuildBaseStringParameters(
89 const OAuthRequestSigner::Parameters& parameters) {
90 std::string result;
91 OAuthRequestSigner::Parameters::const_iterator cursor;
92 OAuthRequestSigner::Parameters::const_iterator limit;
93 bool first = true;
94 for (cursor = parameters.begin(), limit = parameters.end();
95 cursor != limit;
96 ++cursor) {
97 if (first)
98 first = false;
99 else
100 result += '&';
101 result += OAuthRequestSigner::Encode(cursor->first);
102 result += '=';
103 result += OAuthRequestSigner::Encode(cursor->second);
105 return result;
108 std::string GenerateNonce() {
109 char result[kMaxNonceLength + 1];
110 int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) +
111 kMinNonceLength;
112 result[length] = '\0';
113 for (int index = 0; index < length; ++index)
114 result[index] = kOAuthNonceCharacters[
115 base::RandUint64() % (sizeof(kOAuthNonceCharacters) - 1)];
116 return result;
119 std::string GenerateTimestamp() {
120 return base::StringPrintf(
121 "%" PRId64,
122 (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds());
125 // Creates a string-to-string, keyword-value map from a parameter/query string
126 // that uses ampersand (&) to seperate paris and equals (=) to seperate
127 // keyword from value.
128 bool ParseQuery(const std::string& query,
129 OAuthRequestSigner::Parameters* parameters_result) {
130 std::string::const_iterator cursor;
131 std::string keyword;
132 std::string::const_iterator limit;
133 OAuthRequestSigner::Parameters parameters;
134 ParseQueryState state;
135 std::string value;
137 state = START_STATE;
138 for (cursor = query.begin(), limit = query.end();
139 cursor != limit;
140 ++cursor) {
141 char character = *cursor;
142 switch (state) {
143 case KEYWORD_STATE:
144 switch (character) {
145 case '&':
146 parameters[keyword] = value;
147 keyword = "";
148 value = "";
149 state = START_STATE;
150 break;
151 case '=':
152 state = VALUE_STATE;
153 break;
154 default:
155 keyword += character;
157 break;
158 case START_STATE:
159 switch (character) {
160 case '&': // Intentionally falling through
161 case '=':
162 return false;
163 default:
164 keyword += character;
165 state = KEYWORD_STATE;
167 break;
168 case VALUE_STATE:
169 switch (character) {
170 case '=':
171 return false;
172 case '&':
173 parameters[keyword] = value;
174 keyword = "";
175 value = "";
176 state = START_STATE;
177 break;
178 default:
179 value += character;
181 break;
184 switch (state) {
185 case START_STATE:
186 break;
187 case KEYWORD_STATE: // Intentionally falling through
188 case VALUE_STATE:
189 parameters[keyword] = value;
190 break;
191 default:
192 NOTREACHED();
194 *parameters_result = parameters;
195 return true;
198 // Creates the value for the oauth_signature parameter when the
199 // oauth_signature_method is HMAC-SHA1.
200 bool SignHmacSha1(const std::string& text,
201 const std::string& key,
202 std::string* signature_return) {
203 crypto::HMAC hmac(crypto::HMAC::SHA1);
204 DCHECK(hmac.DigestLength() == kHmacDigestLength);
205 unsigned char digest[kHmacDigestLength];
206 bool result = hmac.Init(key) &&
207 hmac.Sign(text, digest, kHmacDigestLength);
208 if (result) {
209 base::Base64Encode(
210 std::string(reinterpret_cast<const char*>(digest), kHmacDigestLength),
211 signature_return);
213 return result;
216 // Creates the value for the oauth_signature parameter when the
217 // oauth_signature_method is PLAINTEXT.
219 // Not yet implemented, and might never be.
220 bool SignPlaintext(const std::string& text,
221 const std::string& key,
222 std::string* result) {
223 NOTIMPLEMENTED();
224 return false;
227 // Creates the value for the oauth_signature parameter when the
228 // oauth_signature_method is RSA-SHA1.
230 // Not yet implemented, and might never be.
231 bool SignRsaSha1(const std::string& text,
232 const std::string& key,
233 std::string* result) {
234 NOTIMPLEMENTED();
235 return false;
238 // Adds parameters that are required by OAuth added as needed to |parameters|.
239 void PrepareParameters(OAuthRequestSigner::Parameters* parameters,
240 OAuthRequestSigner::SignatureMethod signature_method,
241 OAuthRequestSigner::HttpMethod http_method,
242 const std::string& consumer_key,
243 const std::string& token_key) {
244 if (parameters->find(kOAuthNonceLabel) == parameters->end())
245 (*parameters)[kOAuthNonceLabel] = GenerateNonce();
247 if (parameters->find(kOAuthTimestampLabel) == parameters->end())
248 (*parameters)[kOAuthTimestampLabel] = GenerateTimestamp();
250 (*parameters)[kOAuthConsumerKeyLabel] = consumer_key;
251 (*parameters)[kOAuthSignatureMethodLabel] =
252 SignatureMethodName(signature_method);
253 (*parameters)[kOAuthTokenLabel] = token_key;
254 (*parameters)[kOAuthVersionLabel] = kOAuthVersion;
257 // Implements shared signing logic, generating the signature and storing it in
258 // |parameters|. Returns true if the signature has been generated succesfully.
259 bool SignParameters(const GURL& request_base_url,
260 OAuthRequestSigner::SignatureMethod signature_method,
261 OAuthRequestSigner::HttpMethod http_method,
262 const std::string& consumer_key,
263 const std::string& consumer_secret,
264 const std::string& token_key,
265 const std::string& token_secret,
266 OAuthRequestSigner::Parameters* parameters) {
267 DCHECK(request_base_url.is_valid());
268 PrepareParameters(parameters, signature_method, http_method,
269 consumer_key, token_key);
270 std::string base_parameters = BuildBaseStringParameters(*parameters);
271 std::string base = BuildBaseString(request_base_url, http_method,
272 base_parameters);
273 std::string key = consumer_secret + '&' + token_secret;
274 bool is_signed = false;
275 std::string signature;
276 switch (signature_method) {
277 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
278 is_signed = SignHmacSha1(base, key, &signature);
279 break;
280 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
281 is_signed = SignRsaSha1(base, key, &signature);
282 break;
283 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
284 is_signed = SignPlaintext(base, key, &signature);
285 break;
286 default:
287 NOTREACHED();
289 if (is_signed)
290 (*parameters)[kOAuthSignatureLabel] = signature;
291 return is_signed;
295 } // namespace
297 // static
298 bool OAuthRequestSigner::Decode(const std::string& text,
299 std::string* decoded_text) {
300 std::string accumulator;
301 std::string::const_iterator cursor;
302 std::string::const_iterator limit;
303 for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
304 char character = *cursor;
305 if (character == '%') {
306 ++cursor;
307 if (cursor == limit)
308 return false;
309 char* first = strchr(kHexDigits, *cursor);
310 if (!first)
311 return false;
312 int high = first - kHexDigits;
313 DCHECK(high >= 0 && high < kHexBase);
315 ++cursor;
316 if (cursor == limit)
317 return false;
318 char* second = strchr(kHexDigits, *cursor);
319 if (!second)
320 return false;
321 int low = second - kHexDigits;
322 DCHECK(low >= 0 || low < kHexBase);
324 char decoded = static_cast<char>(high * kHexBase + low);
325 DCHECK(!(IsAsciiAlpha(decoded) || IsAsciiDigit(decoded)));
326 DCHECK(!(decoded && strchr("-._~", decoded)));
327 accumulator += decoded;
328 } else {
329 accumulator += character;
332 *decoded_text = accumulator;
333 return true;
336 // static
337 std::string OAuthRequestSigner::Encode(const std::string& text) {
338 std::string result;
339 std::string::const_iterator cursor;
340 std::string::const_iterator limit;
341 for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
342 char character = *cursor;
343 if (IsAsciiAlpha(character) || IsAsciiDigit(character)) {
344 result += character;
345 } else {
346 switch (character) {
347 case '-':
348 case '.':
349 case '_':
350 case '~':
351 result += character;
352 break;
353 default:
354 unsigned char byte = static_cast<unsigned char>(character);
355 result = result + '%' + kHexDigits[byte / kHexBase] +
356 kHexDigits[byte % kHexBase];
360 return result;
363 // static
364 bool OAuthRequestSigner::ParseAndSign(const GURL& request_url_with_parameters,
365 SignatureMethod signature_method,
366 HttpMethod http_method,
367 const std::string& consumer_key,
368 const std::string& consumer_secret,
369 const std::string& token_key,
370 const std::string& token_secret,
371 std::string* result) {
372 DCHECK(request_url_with_parameters.is_valid());
373 Parameters parameters;
374 if (request_url_with_parameters.has_query()) {
375 const std::string& query = request_url_with_parameters.query();
376 if (!query.empty()) {
377 if (!ParseQuery(query, &parameters))
378 return false;
381 std::string spec = request_url_with_parameters.spec();
382 std::string url_without_parameters = spec;
383 std::string::size_type question = spec.find("?");
384 if (question != std::string::npos)
385 url_without_parameters = spec.substr(0,question);
386 return SignURL(GURL(url_without_parameters), parameters, signature_method,
387 http_method, consumer_key, consumer_secret, token_key,
388 token_secret, result);
391 // static
392 bool OAuthRequestSigner::SignURL(
393 const GURL& request_base_url,
394 const Parameters& request_parameters,
395 SignatureMethod signature_method,
396 HttpMethod http_method,
397 const std::string& consumer_key,
398 const std::string& consumer_secret,
399 const std::string& token_key,
400 const std::string& token_secret,
401 std::string* signed_text_return) {
402 DCHECK(request_base_url.is_valid());
403 Parameters parameters(request_parameters);
404 bool is_signed = SignParameters(request_base_url, signature_method,
405 http_method, consumer_key, consumer_secret,
406 token_key, token_secret, &parameters);
407 if (is_signed) {
408 std::string signed_text;
409 switch (http_method) {
410 case GET_METHOD:
411 signed_text = request_base_url.spec() + '?';
412 // Intentionally falling through
413 case POST_METHOD:
414 signed_text += BuildBaseStringParameters(parameters);
415 break;
416 default:
417 NOTREACHED();
419 *signed_text_return = signed_text;
421 return is_signed;
424 // static
425 bool OAuthRequestSigner::SignAuthHeader(
426 const GURL& request_base_url,
427 const Parameters& request_parameters,
428 SignatureMethod signature_method,
429 HttpMethod http_method,
430 const std::string& consumer_key,
431 const std::string& consumer_secret,
432 const std::string& token_key,
433 const std::string& token_secret,
434 std::string* signed_text_return) {
435 DCHECK(request_base_url.is_valid());
436 Parameters parameters(request_parameters);
437 bool is_signed = SignParameters(request_base_url, signature_method,
438 http_method, consumer_key, consumer_secret,
439 token_key, token_secret, &parameters);
440 if (is_signed) {
441 std::string signed_text = "OAuth ";
442 bool first = true;
443 for (Parameters::const_iterator param = parameters.begin();
444 param != parameters.end();
445 ++param) {
446 if (first)
447 first = false;
448 else
449 signed_text += ", ";
450 signed_text +=
451 base::StringPrintf(
452 "%s=\"%s\"",
453 OAuthRequestSigner::Encode(param->first).c_str(),
454 OAuthRequestSigner::Encode(param->second).c_str());
456 *signed_text_return = signed_text;
458 return is_signed;