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);
210 std::string(reinterpret_cast<const char*>(digest), kHmacDigestLength),
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) {
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) {
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,
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);
280 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
281 is_signed = SignRsaSha1(base, key, &signature);
283 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
284 is_signed = SignPlaintext(base, key, &signature);
290 (*parameters)[kOAuthSignatureLabel] = signature;
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 == '%') {
309 char* first = strchr(kHexDigits, *cursor);
312 int high = first - kHexDigits;
313 DCHECK(high >= 0 && high < kHexBase);
318 char* second = strchr(kHexDigits, *cursor);
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;
329 accumulator += character;
332 *decoded_text = accumulator;
337 std::string OAuthRequestSigner::Encode(const std::string& text) {
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)) {
354 unsigned char byte = static_cast<unsigned char>(character);
355 result = result + '%' + kHexDigits[byte / kHexBase] +
356 kHexDigits[byte % kHexBase];
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, ¶meters))
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);
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, ¶meters);
408 std::string signed_text;
409 switch (http_method) {
411 signed_text = request_base_url.spec() + '?';
412 // Intentionally falling through
414 signed_text += BuildBaseStringParameters(parameters);
419 *signed_text_return = signed_text;
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, ¶meters);
441 std::string signed_text = "OAuth ";
443 for (Parameters::const_iterator param = parameters.begin();
444 param != parameters.end();
453 OAuthRequestSigner::Encode(param->first).c_str(),
454 OAuthRequestSigner::Encode(param->second).c_str());
456 *signed_text_return = signed_text;