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"
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"
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"
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 {
52 const std::string HttpMethodName(OAuthRequestSigner::HttpMethod method) {
54 case OAuthRequestSigner::GET_METHOD:
56 case OAuthRequestSigner::POST_METHOD:
63 const std::string SignatureMethodName(
64 OAuthRequestSigner::SignatureMethod method) {
66 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
68 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
70 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
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) {
91 OAuthRequestSigner::Parameters::const_iterator cursor;
92 OAuthRequestSigner::Parameters::const_iterator limit;
94 for (cursor = parameters.begin(), limit = parameters.end();
101 result += OAuthRequestSigner::Encode(cursor->first);
103 result += OAuthRequestSigner::Encode(cursor->second);
108 std::string GenerateNonce() {
109 char result[kMaxNonceLength + 1];
110 int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) +
112 result[length] = '\0';
113 for (int index = 0; index < length; ++index)
114 result[index] = kOAuthNonceCharacters[
115 base::RandUint64() % (sizeof(kOAuthNonceCharacters) - 1)];
119 std::string GenerateTimestamp() {
120 return base::StringPrintf(
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;
132 std::string::const_iterator limit;
133 OAuthRequestSigner::Parameters parameters;
134 ParseQueryState state;
138 for (cursor = query.begin(), limit = query.end();
141 char character = *cursor;
146 parameters[keyword] = value;
155 keyword += character;
160 case '&': // Intentionally falling through
164 keyword += character;
165 state = KEYWORD_STATE;
173 parameters[keyword] = value;
187 case KEYWORD_STATE: // Intentionally falling through
189 parameters[keyword] = value;
194 *parameters_result = parameters;
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),
214 // Creates the value for the oauth_signature parameter when the
215 // oauth_signature_method is PLAINTEXT.
217 // Not yet implemented, and might never be.
218 bool SignPlaintext(const std::string& text,
219 const std::string& key,
220 std::string* result) {
225 // Creates the value for the oauth_signature parameter when the
226 // oauth_signature_method is RSA-SHA1.
228 // Not yet implemented, and might never be.
229 bool SignRsaSha1(const std::string& text,
230 const std::string& key,
231 std::string* result) {
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();
245 if (parameters->find(kOAuthTimestampLabel) == parameters->end())
246 (*parameters)[kOAuthTimestampLabel] = GenerateTimestamp();
248 (*parameters)[kOAuthConsumerKeyLabel] = consumer_key;
249 (*parameters)[kOAuthSignatureMethodLabel] =
250 SignatureMethodName(signature_method);
251 (*parameters)[kOAuthTokenLabel] = token_key;
252 (*parameters)[kOAuthVersionLabel] = kOAuthVersion;
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,
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);
278 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
279 is_signed = SignRsaSha1(base, key, &signature);
281 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
282 is_signed = SignPlaintext(base, key, &signature);
288 (*parameters)[kOAuthSignatureLabel] = signature;
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 == '%') {
307 char* first = strchr(kHexDigits, *cursor);
310 int high = first - kHexDigits;
311 DCHECK(high >= 0 && high < kHexBase);
316 char* second = strchr(kHexDigits, *cursor);
319 int low = second - kHexDigits;
320 DCHECK(low >= 0 || low < kHexBase);
322 char decoded = static_cast<char>(high * kHexBase + low);
323 DCHECK(!(IsAsciiAlpha(decoded) || IsAsciiDigit(decoded)));
324 DCHECK(!(decoded && strchr("-._~", decoded)));
325 accumulator += decoded;
327 accumulator += character;
330 *decoded_text = accumulator;
335 std::string OAuthRequestSigner::Encode(const std::string& text) {
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)) {
352 unsigned char byte = static_cast<unsigned char>(character);
353 result = result + '%' + kHexDigits[byte / kHexBase] +
354 kHexDigits[byte % kHexBase];
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, ¶meters))
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);
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, ¶meters);
406 std::string signed_text;
407 switch (http_method) {
409 signed_text = request_base_url.spec() + '?';
410 // Intentionally falling through
412 signed_text += BuildBaseStringParameters(parameters);
417 *signed_text_return = signed_text;
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, ¶meters);
439 std::string signed_text = "OAuth ";
441 for (Parameters::const_iterator param = parameters.begin();
442 param != parameters.end();
451 OAuthRequestSigner::Encode(param->first).c_str(),
452 OAuthRequestSigner::Encode(param->second).c_str());
454 *signed_text_return = signed_text;