diff options
author | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-07 04:26:37 +0000 |
---|---|---|
committer | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-07 04:26:37 +0000 |
commit | 6386cf58a85361fa20bba6ecfc23502e922b9a90 (patch) | |
tree | 06f9a9aa7e6bb8db149b53bc7489e69fa87c07fb /google_apis/gaia/oauth_request_signer.cc | |
parent | f2857ecf6c8da488feeca9b05cf2c046ea43e02f (diff) | |
download | chromium_src-6386cf58a85361fa20bba6ecfc23502e922b9a90.zip chromium_src-6386cf58a85361fa20bba6ecfc23502e922b9a90.tar.gz chromium_src-6386cf58a85361fa20bba6ecfc23502e922b9a90.tar.bz2 |
Moving google_apis and GaiaClient to src/google_apis.
TBR=mechanicalowners@chromium.org
BUG=145584
Review URL: https://chromiumcodereview.appspot.com/10928017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155312 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'google_apis/gaia/oauth_request_signer.cc')
-rw-r--r-- | google_apis/gaia/oauth_request_signer.cc | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/google_apis/gaia/oauth_request_signer.cc b/google_apis/gaia/oauth_request_signer.cc new file mode 100644 index 0000000..7ba947b --- /dev/null +++ b/google_apis/gaia/oauth_request_signer.cc @@ -0,0 +1,458 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "google_apis/gaia/oauth_request_signer.h" + +#include <cctype> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <map> +#include <string> + +#include "base/base64.h" +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/string_util.h" +#include "base/stringprintf.h" +#include "base/time.h" +#include "crypto/hmac.h" +#include "googleurl/src/gurl.h" + +namespace { + +static const int kHexBase = 16; +static char kHexDigits[] = "0123456789ABCDEF"; +static const size_t kHmacDigestLength = 20; +static const int kMaxNonceLength = 30; +static const int kMinNonceLength = 15; + +static const char kOAuthConsumerKeyLabel[] = "oauth_consumer_key"; +static const char kOAuthConsumerSecretLabel[] = "oauth_consumer_secret"; +static const char kOAuthNonceCharacters[] = + "abcdefghijklmnopqrstuvwyz" + "ABCDEFGHIJKLMNOPQRSTUVWYZ" + "0123456789_"; +static const char kOAuthNonceLabel[] = "oauth_nonce"; +static const char kOAuthSignatureLabel[] = "oauth_signature"; +static const char kOAuthSignatureMethodLabel[] = "oauth_signature_method"; +static const char kOAuthTimestampLabel[] = "oauth_timestamp"; +static const char kOAuthTokenLabel[] = "oauth_token"; +static const char kOAuthTokenSecretLabel[] = "oauth_token_secret"; +static const char kOAuthVersion[] = "1.0"; +static const char kOAuthVersionLabel[] = "oauth_version"; + +enum ParseQueryState { + START_STATE, + KEYWORD_STATE, + VALUE_STATE, +}; + +const std::string HttpMethodName(OAuthRequestSigner::HttpMethod method) { + switch (method) { + case OAuthRequestSigner::GET_METHOD: + return "GET"; + case OAuthRequestSigner::POST_METHOD: + return "POST"; + } + NOTREACHED(); + return *(new std::string()); +} + +const std::string SignatureMethodName( + OAuthRequestSigner::SignatureMethod method) { + switch (method) { + case OAuthRequestSigner::HMAC_SHA1_SIGNATURE: + return "HMAC-SHA1"; + case OAuthRequestSigner::RSA_SHA1_SIGNATURE: + return "RSA-SHA1"; + case OAuthRequestSigner::PLAINTEXT_SIGNATURE: + return "PLAINTEXT"; + } + NOTREACHED(); + return *(new std::string()); +} + +std::string BuildBaseString(const GURL& request_base_url, + OAuthRequestSigner::HttpMethod http_method, + const std::string& base_parameters) { + return StringPrintf("%s&%s&%s", + HttpMethodName(http_method).c_str(), + OAuthRequestSigner::Encode( + request_base_url.spec()).c_str(), + OAuthRequestSigner::Encode( + base_parameters).c_str()); +} + +std::string BuildBaseStringParameters( + const OAuthRequestSigner::Parameters& parameters) { + std::string result = ""; + OAuthRequestSigner::Parameters::const_iterator cursor; + OAuthRequestSigner::Parameters::const_iterator limit; + bool first = true; + for (cursor = parameters.begin(), limit = parameters.end(); + cursor != limit; + ++cursor) { + if (first) + first = false; + else + result += '&'; + result += OAuthRequestSigner::Encode(cursor->first); + result += '='; + result += OAuthRequestSigner::Encode(cursor->second); + } + return result; +} + +std::string GenerateNonce() { + char result[kMaxNonceLength + 1]; + int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) + + kMinNonceLength; + result[length] = '\0'; + for (int index = 0; index < length; ++index) + result[index] = kOAuthNonceCharacters[ + base::RandUint64() % (sizeof(kOAuthNonceCharacters) - 1)]; + return result; +} + +std::string GenerateTimestamp() { + return base::StringPrintf( + "%" PRId64, + (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds()); +} + +// Creates a string-to-string, keyword-value map from a parameter/query string +// that uses ampersand (&) to seperate paris and equals (=) to seperate +// keyword from value. +bool ParseQuery(const std::string& query, + OAuthRequestSigner::Parameters* parameters_result) { + std::string::const_iterator cursor; + std::string keyword; + std::string::const_iterator limit; + OAuthRequestSigner::Parameters parameters; + ParseQueryState state; + std::string value; + + state = START_STATE; + for (cursor = query.begin(), limit = query.end(); + cursor != limit; + ++cursor) { + char character = *cursor; + switch (state) { + case KEYWORD_STATE: + switch (character) { + case '&': + parameters[keyword] = value; + keyword = ""; + value = ""; + state = START_STATE; + break; + case '=': + state = VALUE_STATE; + break; + default: + keyword += character; + } + break; + case START_STATE: + switch (character) { + case '&': // Intentionally falling through + case '=': + return false; + default: + keyword += character; + state = KEYWORD_STATE; + } + break; + case VALUE_STATE: + switch (character) { + case '=': + return false; + case '&': + parameters[keyword] = value; + keyword = ""; + value = ""; + state = START_STATE; + break; + default: + value += character; + } + break; + } + } + switch (state) { + case START_STATE: + break; + case KEYWORD_STATE: // Intentionally falling through + case VALUE_STATE: + parameters[keyword] = value; + break; + default: + NOTREACHED(); + } + *parameters_result = parameters; + return true; +} + +// Creates the value for the oauth_signature parameter when the +// oauth_signature_method is HMAC-SHA1. +bool SignHmacSha1(const std::string& text, + const std::string& key, + std::string* signature_return) { + crypto::HMAC hmac(crypto::HMAC::SHA1); + DCHECK(hmac.DigestLength() == kHmacDigestLength); + unsigned char digest[kHmacDigestLength]; + bool result = hmac.Init(key) && + hmac.Sign(text, digest, kHmacDigestLength) && + base::Base64Encode(std::string(reinterpret_cast<const char*>(digest), + kHmacDigestLength), + signature_return); + return result; +} + +// Creates the value for the oauth_signature parameter when the +// oauth_signature_method is PLAINTEXT. +// +// Not yet implemented, and might never be. +bool SignPlaintext(const std::string& text, + const std::string& key, + std::string* result) { + NOTIMPLEMENTED(); + return false; +} + +// Creates the value for the oauth_signature parameter when the +// oauth_signature_method is RSA-SHA1. +// +// Not yet implemented, and might never be. +bool SignRsaSha1(const std::string& text, + const std::string& key, + std::string* result) { + NOTIMPLEMENTED(); + return false; +} + +// Adds parameters that are required by OAuth added as needed to |parameters|. +void PrepareParameters(OAuthRequestSigner::Parameters* parameters, + OAuthRequestSigner::SignatureMethod signature_method, + OAuthRequestSigner::HttpMethod http_method, + const std::string& consumer_key, + const std::string& token_key) { + if (parameters->find(kOAuthNonceLabel) == parameters->end()) + (*parameters)[kOAuthNonceLabel] = GenerateNonce(); + + if (parameters->find(kOAuthTimestampLabel) == parameters->end()) + (*parameters)[kOAuthTimestampLabel] = GenerateTimestamp(); + + (*parameters)[kOAuthConsumerKeyLabel] = consumer_key; + (*parameters)[kOAuthSignatureMethodLabel] = + SignatureMethodName(signature_method); + (*parameters)[kOAuthTokenLabel] = token_key; + (*parameters)[kOAuthVersionLabel] = kOAuthVersion; +} + +// Implements shared signing logic, generating the signature and storing it in +// |parameters|. Returns true if the signature has been generated succesfully. +bool SignParameters(const GURL& request_base_url, + OAuthRequestSigner::SignatureMethod signature_method, + OAuthRequestSigner::HttpMethod http_method, + const std::string& consumer_key, + const std::string& consumer_secret, + const std::string& token_key, + const std::string& token_secret, + OAuthRequestSigner::Parameters* parameters) { + DCHECK(request_base_url.is_valid()); + PrepareParameters(parameters, signature_method, http_method, + consumer_key, token_key); + std::string base_parameters = BuildBaseStringParameters(*parameters); + std::string base = BuildBaseString(request_base_url, http_method, + base_parameters); + std::string key = consumer_secret + '&' + token_secret; + bool is_signed = false; + std::string signature; + switch (signature_method) { + case OAuthRequestSigner::HMAC_SHA1_SIGNATURE: + is_signed = SignHmacSha1(base, key, &signature); + break; + case OAuthRequestSigner::RSA_SHA1_SIGNATURE: + is_signed = SignRsaSha1(base, key, &signature); + break; + case OAuthRequestSigner::PLAINTEXT_SIGNATURE: + is_signed = SignPlaintext(base, key, &signature); + break; + default: + NOTREACHED(); + } + if (is_signed) + (*parameters)[kOAuthSignatureLabel] = signature; + return is_signed; +} + + +} // namespace + +// static +bool OAuthRequestSigner::Decode(const std::string& text, + std::string* decoded_text) { + std::string accumulator = ""; + std::string::const_iterator cursor; + std::string::const_iterator limit; + for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) { + char character = *cursor; + if (character == '%') { + ++cursor; + if (cursor == limit) + return false; + char* first = strchr(kHexDigits, *cursor); + if (!first) + return false; + int high = first - kHexDigits; + DCHECK(high >= 0 && high < kHexBase); + + ++cursor; + if (cursor == limit) + return false; + char* second = strchr(kHexDigits, *cursor); + if (!second) + return false; + int low = second - kHexDigits; + DCHECK(low >= 0 || low < kHexBase); + + char decoded = static_cast<char>(high * kHexBase + low); + DCHECK(!(IsAsciiAlpha(decoded) || IsAsciiDigit(decoded))); + DCHECK(!(decoded && strchr("-._~", decoded))); + accumulator += decoded; + } else { + accumulator += character; + } + } + *decoded_text = accumulator; + return true; +} + +// static +std::string OAuthRequestSigner::Encode(const std::string& text) { + std::string result = ""; + std::string::const_iterator cursor; + std::string::const_iterator limit; + for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) { + char character = *cursor; + if (IsAsciiAlpha(character) || IsAsciiDigit(character)) { + result += character; + } else { + switch (character) { + case '-': + case '.': + case '_': + case '~': + result += character; + break; + default: + unsigned char byte = static_cast<unsigned char>(character); + result = result + '%' + kHexDigits[byte / kHexBase] + + kHexDigits[byte % kHexBase]; + } + } + } + return result; +} + +// static +bool OAuthRequestSigner::ParseAndSign(const GURL& request_url_with_parameters, + SignatureMethod signature_method, + HttpMethod http_method, + const std::string& consumer_key, + const std::string& consumer_secret, + const std::string& token_key, + const std::string& token_secret, + std::string* result) { + DCHECK(request_url_with_parameters.is_valid()); + Parameters parameters; + if (request_url_with_parameters.has_query()) { + const std::string& query = request_url_with_parameters.query(); + if (!query.empty()) { + if (!ParseQuery(query, ¶meters)) + return false; + } + } + std::string spec = request_url_with_parameters.spec(); + std::string url_without_parameters = spec; + std::string::size_type question = spec.find("?"); + if (question != std::string::npos) + url_without_parameters = spec.substr(0,question); + return SignURL(GURL(url_without_parameters), parameters, signature_method, + http_method, consumer_key, consumer_secret, token_key, + token_secret, result); +} + +// static +bool OAuthRequestSigner::SignURL( + const GURL& request_base_url, + const Parameters& request_parameters, + SignatureMethod signature_method, + HttpMethod http_method, + const std::string& consumer_key, + const std::string& consumer_secret, + const std::string& token_key, + const std::string& token_secret, + std::string* signed_text_return) { + DCHECK(request_base_url.is_valid()); + Parameters parameters(request_parameters); + bool is_signed = SignParameters(request_base_url, signature_method, + http_method, consumer_key, consumer_secret, + token_key, token_secret, ¶meters); + if (is_signed) { + std::string signed_text; + switch (http_method) { + case GET_METHOD: + signed_text = request_base_url.spec() + '?'; + // Intentionally falling through + case POST_METHOD: + signed_text += BuildBaseStringParameters(parameters); + break; + default: + NOTREACHED(); + } + *signed_text_return = signed_text; + } + return is_signed; +} + +// static +bool OAuthRequestSigner::SignAuthHeader( + const GURL& request_base_url, + const Parameters& request_parameters, + SignatureMethod signature_method, + HttpMethod http_method, + const std::string& consumer_key, + const std::string& consumer_secret, + const std::string& token_key, + const std::string& token_secret, + std::string* signed_text_return) { + DCHECK(request_base_url.is_valid()); + Parameters parameters(request_parameters); + bool is_signed = SignParameters(request_base_url, signature_method, + http_method, consumer_key, consumer_secret, + token_key, token_secret, ¶meters); + if (is_signed) { + std::string signed_text = "OAuth "; + bool first = true; + for (Parameters::const_iterator param = parameters.begin(); + param != parameters.end(); + ++param) { + if (first) + first = false; + else + signed_text += ", "; + signed_text += + StringPrintf("%s=\"%s\"", + OAuthRequestSigner::Encode(param->first).c_str(), + OAuthRequestSigner::Encode(param->second).c_str()); + } + *signed_text_return = signed_text; + } + return is_signed; +} |