123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- // 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 <stddef.h>
- #include <cctype>
- #include <cstdlib>
- #include <cstring>
- #include <ctime>
- #include <map>
- #include <string>
- #include "base/base64.h"
- #include "base/check.h"
- #include "base/format_macros.h"
- #include "base/notreached.h"
- #include "base/rand_util.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/time/time.h"
- #include "crypto/hmac.h"
- #include "url/gurl.h"
- namespace {
- const int kHexBase = 16;
- char kHexDigits[] = "0123456789ABCDEF";
- const size_t kHmacDigestLength = 20;
- const int kMaxNonceLength = 30;
- const int kMinNonceLength = 15;
- const char kOAuthConsumerKeyLabel[] = "oauth_consumer_key";
- const char kOAuthNonceCharacters[] =
- "abcdefghijklmnopqrstuvwyz"
- "ABCDEFGHIJKLMNOPQRSTUVWYZ"
- "0123456789_";
- const char kOAuthNonceLabel[] = "oauth_nonce";
- const char kOAuthSignatureLabel[] = "oauth_signature";
- const char kOAuthSignatureMethodLabel[] = "oauth_signature_method";
- const char kOAuthTimestampLabel[] = "oauth_timestamp";
- const char kOAuthTokenLabel[] = "oauth_token";
- const char kOAuthVersion[] = "1.0";
- 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 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 std::string();
- }
- std::string BuildBaseString(const GURL& request_base_url,
- OAuthRequestSigner::HttpMethod http_method,
- const std::string& base_parameters) {
- return base::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);
- if (result) {
- 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(!(base::IsAsciiAlpha(decoded) || base::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 (base::IsAsciiAlpha(character) || base::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() + '?';
- [[fallthrough]];
- 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 +=
- base::StringPrintf(
- "%s=\"%s\"",
- OAuthRequestSigner::Encode(param->first).c_str(),
- OAuthRequestSigner::Encode(param->second).c_str());
- }
- *signed_text_return = signed_text;
- }
- return is_signed;
- }
|