- add sources.
[platform/framework/web/crosswalk.git] / src / google_apis / gaia / oauth_request_signer.cc
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.
4
5 #include "google_apis/gaia/oauth_request_signer.h"
6
7 #include <cctype>
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cstring>
11 #include <ctime>
12 #include <map>
13 #include <string>
14
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"
24
25 namespace {
26
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;
32
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";
45
46 enum ParseQueryState {
47   START_STATE,
48   KEYWORD_STATE,
49   VALUE_STATE,
50 };
51
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";
58   }
59   NOTREACHED();
60   return std::string();
61 }
62
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";
72   }
73   NOTREACHED();
74   return std::string();
75 }
76
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());
86 }
87
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);
104   }
105   return result;
106 }
107
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;
117 }
118
119 std::string GenerateTimestamp() {
120   return base::StringPrintf(
121       "%" PRId64,
122       (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds());
123 }
124
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;
136
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;
156         }
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;
166         }
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;
180         }
181         break;
182     }
183   }
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();
193   }
194   *parameters_result = parameters;
195   return true;
196 }
197
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       base::Base64Encode(std::string(reinterpret_cast<const char*>(digest),
209                                      kHmacDigestLength),
210                          signature_return);
211   return result;
212 }
213
214 // Creates the value for the oauth_signature parameter when the
215 // oauth_signature_method is PLAINTEXT.
216 //
217 // Not yet implemented, and might never be.
218 bool SignPlaintext(const std::string& text,
219                    const std::string& key,
220                    std::string* result) {
221   NOTIMPLEMENTED();
222   return false;
223 }
224
225 // Creates the value for the oauth_signature parameter when the
226 // oauth_signature_method is RSA-SHA1.
227 //
228 // Not yet implemented, and might never be.
229 bool SignRsaSha1(const std::string& text,
230                  const std::string& key,
231                  std::string* result) {
232   NOTIMPLEMENTED();
233   return false;
234 }
235
236 // Adds parameters that are required by OAuth added as needed to |parameters|.
237 void PrepareParameters(OAuthRequestSigner::Parameters* parameters,
238                        OAuthRequestSigner::SignatureMethod signature_method,
239                        OAuthRequestSigner::HttpMethod http_method,
240                        const std::string& consumer_key,
241                        const std::string& token_key) {
242   if (parameters->find(kOAuthNonceLabel) == parameters->end())
243     (*parameters)[kOAuthNonceLabel] = GenerateNonce();
244
245   if (parameters->find(kOAuthTimestampLabel) == parameters->end())
246     (*parameters)[kOAuthTimestampLabel] = GenerateTimestamp();
247
248   (*parameters)[kOAuthConsumerKeyLabel] = consumer_key;
249   (*parameters)[kOAuthSignatureMethodLabel] =
250       SignatureMethodName(signature_method);
251   (*parameters)[kOAuthTokenLabel] = token_key;
252   (*parameters)[kOAuthVersionLabel] = kOAuthVersion;
253 }
254
255 // Implements shared signing logic, generating the signature and storing it in
256 // |parameters|. Returns true if the signature has been generated succesfully.
257 bool SignParameters(const GURL& request_base_url,
258                     OAuthRequestSigner::SignatureMethod signature_method,
259                     OAuthRequestSigner::HttpMethod http_method,
260                     const std::string& consumer_key,
261                     const std::string& consumer_secret,
262                     const std::string& token_key,
263                     const std::string& token_secret,
264                     OAuthRequestSigner::Parameters* parameters) {
265   DCHECK(request_base_url.is_valid());
266   PrepareParameters(parameters, signature_method, http_method,
267                     consumer_key, token_key);
268   std::string base_parameters = BuildBaseStringParameters(*parameters);
269   std::string base = BuildBaseString(request_base_url, http_method,
270                                      base_parameters);
271   std::string key = consumer_secret + '&' + token_secret;
272   bool is_signed = false;
273   std::string signature;
274   switch (signature_method) {
275     case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
276       is_signed = SignHmacSha1(base, key, &signature);
277       break;
278     case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
279       is_signed = SignRsaSha1(base, key, &signature);
280       break;
281     case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
282       is_signed = SignPlaintext(base, key, &signature);
283       break;
284     default:
285       NOTREACHED();
286   }
287   if (is_signed)
288     (*parameters)[kOAuthSignatureLabel] = signature;
289   return is_signed;
290 }
291
292
293 }  // namespace
294
295 // static
296 bool OAuthRequestSigner::Decode(const std::string& text,
297                                 std::string* decoded_text) {
298   std::string accumulator;
299   std::string::const_iterator cursor;
300   std::string::const_iterator limit;
301   for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
302     char character = *cursor;
303     if (character == '%') {
304       ++cursor;
305       if (cursor == limit)
306         return false;
307       char* first = strchr(kHexDigits, *cursor);
308       if (!first)
309         return false;
310       int high = first - kHexDigits;
311       DCHECK(high >= 0 && high < kHexBase);
312
313       ++cursor;
314       if (cursor == limit)
315         return false;
316       char* second = strchr(kHexDigits, *cursor);
317       if (!second)
318         return false;
319       int low = second - kHexDigits;
320       DCHECK(low >= 0 || low < kHexBase);
321
322       char decoded = static_cast<char>(high * kHexBase + low);
323       DCHECK(!(IsAsciiAlpha(decoded) || IsAsciiDigit(decoded)));
324       DCHECK(!(decoded && strchr("-._~", decoded)));
325       accumulator += decoded;
326     } else {
327       accumulator += character;
328     }
329   }
330   *decoded_text = accumulator;
331   return true;
332 }
333
334 // static
335 std::string OAuthRequestSigner::Encode(const std::string& text) {
336   std::string result;
337   std::string::const_iterator cursor;
338   std::string::const_iterator limit;
339   for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
340     char character = *cursor;
341     if (IsAsciiAlpha(character) || IsAsciiDigit(character)) {
342       result += character;
343     } else {
344       switch (character) {
345         case '-':
346         case '.':
347         case '_':
348         case '~':
349           result += character;
350           break;
351         default:
352           unsigned char byte = static_cast<unsigned char>(character);
353           result = result + '%' + kHexDigits[byte / kHexBase] +
354               kHexDigits[byte % kHexBase];
355       }
356     }
357   }
358   return result;
359 }
360
361 // static
362 bool OAuthRequestSigner::ParseAndSign(const GURL& request_url_with_parameters,
363                                       SignatureMethod signature_method,
364                                       HttpMethod http_method,
365                                       const std::string& consumer_key,
366                                       const std::string& consumer_secret,
367                                       const std::string& token_key,
368                                       const std::string& token_secret,
369                                       std::string* result) {
370   DCHECK(request_url_with_parameters.is_valid());
371   Parameters parameters;
372   if (request_url_with_parameters.has_query()) {
373     const std::string& query = request_url_with_parameters.query();
374     if (!query.empty()) {
375       if (!ParseQuery(query, &parameters))
376         return false;
377     }
378   }
379   std::string spec = request_url_with_parameters.spec();
380   std::string url_without_parameters = spec;
381   std::string::size_type question = spec.find("?");
382   if (question != std::string::npos)
383     url_without_parameters = spec.substr(0,question);
384   return SignURL(GURL(url_without_parameters), parameters, signature_method,
385                  http_method, consumer_key, consumer_secret, token_key,
386                  token_secret, result);
387 }
388
389 // static
390 bool OAuthRequestSigner::SignURL(
391     const GURL& request_base_url,
392     const Parameters& request_parameters,
393     SignatureMethod signature_method,
394     HttpMethod http_method,
395     const std::string& consumer_key,
396     const std::string& consumer_secret,
397     const std::string& token_key,
398     const std::string& token_secret,
399     std::string* signed_text_return) {
400   DCHECK(request_base_url.is_valid());
401   Parameters parameters(request_parameters);
402   bool is_signed = SignParameters(request_base_url, signature_method,
403                                   http_method, consumer_key, consumer_secret,
404                                   token_key, token_secret, &parameters);
405   if (is_signed) {
406     std::string signed_text;
407     switch (http_method) {
408       case GET_METHOD:
409         signed_text = request_base_url.spec() + '?';
410         // Intentionally falling through
411       case POST_METHOD:
412         signed_text += BuildBaseStringParameters(parameters);
413         break;
414       default:
415         NOTREACHED();
416     }
417     *signed_text_return = signed_text;
418   }
419   return is_signed;
420 }
421
422 // static
423 bool OAuthRequestSigner::SignAuthHeader(
424     const GURL& request_base_url,
425     const Parameters& request_parameters,
426     SignatureMethod signature_method,
427     HttpMethod http_method,
428     const std::string& consumer_key,
429     const std::string& consumer_secret,
430     const std::string& token_key,
431     const std::string& token_secret,
432     std::string* signed_text_return) {
433   DCHECK(request_base_url.is_valid());
434   Parameters parameters(request_parameters);
435   bool is_signed = SignParameters(request_base_url, signature_method,
436                                   http_method, consumer_key, consumer_secret,
437                                   token_key, token_secret, &parameters);
438   if (is_signed) {
439     std::string signed_text = "OAuth ";
440     bool first = true;
441     for (Parameters::const_iterator param = parameters.begin();
442          param != parameters.end();
443          ++param) {
444       if (first)
445         first = false;
446       else
447         signed_text += ", ";
448       signed_text +=
449           base::StringPrintf(
450               "%s=\"%s\"",
451               OAuthRequestSigner::Encode(param->first).c_str(),
452               OAuthRequestSigner::Encode(param->second).c_str());
453     }
454     *signed_text_return = signed_text;
455   }
456   return is_signed;
457 }