Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3387)

Unified Diff: chrome/common/net/gaia/oauth_request_signer.cc

Issue 7171023: Adds the OAuthRequestSigner class to facilitate OAuth integration (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added a cast to fix build on Mac. Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/common/net/gaia/oauth_request_signer.cc
diff --git a/chrome/common/net/gaia/oauth_request_signer.cc b/chrome/common/net/gaia/oauth_request_signer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2fd78edcd9ec85318ce7ed9227169576a2a8f7cb
--- /dev/null
+++ b/chrome/common/net/gaia/oauth_request_signer.cc
@@ -0,0 +1,380 @@
+// Copyright (c) 2011 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 "chrome/common/net/gaia/oauth_request_signer.h"
+
+#include <cctype>
+#include <cstdlib>
+#include <ctime>
+#include <map>
+#include <string>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/rand_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 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());
+}
+
+// The form of percent encoding used for OAuth request signing is very
+// specific and strict. See http://oauth.net/core/1.0/#encoding_parameters.
+//
+// Any character which is in the "unreserved set" must not be encoded.
+// All other characters must be encoded.
+//
+// The unreserved set is comprised of the alphanumeric characters and these
+// others:
+// - minus (-)
+// - period (.)
+// - underscore (_)
+// - tilde (~)
+std::string EncodedOAuthParameter(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 (isalnum(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;
+}
+
+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(),
+ EncodedOAuthParameter(request_base_url.spec()).c_str(),
+ EncodedOAuthParameter(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 += EncodedOAuthParameter(cursor->first);
+ result += '=';
+ result += EncodedOAuthParameter(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(
+ "%ld",
Nicolas Zea 2011/06/17 22:38:40 See base/format_macros.h. I think you want the "PR
Rick Campbell 2011/06/17 23:04:04 Done.
+ static_cast<long int>(
+ (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);
+ size_t digest_length = hmac.DigestLength();
+ unsigned char* digest = new unsigned char [digest_length];
+ hmac.Init(key);
+ return hmac.Sign(text, digest, digest_length) &&
+ base::Base64Encode(std::string(reinterpret_cast<const char*>(digest),
+ digest_length),
+ signature_return);
+}
+
+// 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;
+}
+
+} // namespace
+
+// 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, &parameters))
+ 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 Sign (GURL(url_without_parameters), parameters, signature_method,
+ http_method, consumer_key, consumer_secret, token_key,
+ token_secret, result);
+}
+
+// Returns a copy of request_parameters, with parameters that are required by
+// OAuth added as needed.
+OAuthRequestSigner::Parameters
+PrepareParameters(const OAuthRequestSigner::Parameters& request_parameters,
+ OAuthRequestSigner::SignatureMethod signature_method,
+ OAuthRequestSigner::HttpMethod http_method,
+ const std::string& consumer_key,
+ const std::string& token_key) {
+ OAuthRequestSigner::Parameters result(request_parameters);
+
+ if (result.find(kOAuthNonceLabel) == result.end())
+ result[kOAuthNonceLabel] = GenerateNonce();
+
+ if (result.find(kOAuthTimestampLabel) == result.end())
+ result[kOAuthTimestampLabel] = GenerateTimestamp();
+
+ result[kOAuthConsumerKeyLabel] = consumer_key;
+ result[kOAuthSignatureMethodLabel] = SignatureMethodName(signature_method);
+ result[kOAuthTokenLabel] = token_key;
+ result[kOAuthVersionLabel] = kOAuthVersion;
+
+ return result;
+}
+
+// static
+bool OAuthRequestSigner::Sign(
+ 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 = PrepareParameters(request_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 HMAC_SHA1_SIGNATURE:
+ is_signed = SignHmacSha1(base, key, &signature);
+ break;
+ case RSA_SHA1_SIGNATURE:
+ is_signed = SignRsaSha1(base, key, &signature);
+ break;
+ case PLAINTEXT_SIGNATURE:
+ is_signed = SignPlaintext(base, key, &signature);
+ break;
+ default:
+ NOTREACHED();
+ }
+ 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 += base_parameters + '&' + kOAuthSignatureLabel + '=' +
+ EncodedOAuthParameter(signature);
+ break;
+ default:
+ NOTREACHED();
+ }
+ *signed_text_return = signed_text;
+ }
+ return is_signed;
+}
« no previous file with comments | « chrome/common/net/gaia/oauth_request_signer.h ('k') | chrome/common/net/gaia/oauth_request_signer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698