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

Side by Side 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: Using PRId64 formatter in preference to static_cast of the value. 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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.
4
5 #include "chrome/common/net/gaia/oauth_request_signer.h"
6
7 #include <cctype>
8 #include <cstdlib>
9 #include <ctime>
10 #include <map>
11 #include <string>
12
13 #include "base/base64.h"
14 #include "base/format_macros.h"
15 #include "base/logging.h"
16 #include "base/rand_util.h"
17 #include "base/stringprintf.h"
18 #include "base/time.h"
19 #include "crypto/hmac.h"
20 #include "googleurl/src/gurl.h"
21
22 namespace {
23
24 static const int kHexBase = 16;
25 static char kHexDigits[] = "0123456789ABCDEF";
26
27 static const int kMaxNonceLength = 30;
28 static const int kMinNonceLength = 15;
29
30 static const char kOAuthConsumerKeyLabel[] = "oauth_consumer_key";
31 static const char kOAuthConsumerSecretLabel[] = "oauth_consumer_secret";
32 static const char kOAuthNonceCharacters[] =
33 "abcdefghijklmnopqrstuvwyz"
34 "ABCDEFGHIJKLMNOPQRSTUVWYZ"
35 "0123456789_";
36 static const char kOAuthNonceLabel[] = "oauth_nonce";
37 static const char kOAuthSignatureLabel[] = "oauth_signature";
38 static const char kOAuthSignatureMethodLabel[] = "oauth_signature_method";
39 static const char kOAuthTimestampLabel[] = "oauth_timestamp";
40 static const char kOAuthTokenLabel[] = "oauth_token";
41 static const char kOAuthTokenSecretLabel[] = "oauth_token_secret";
42 static const char kOAuthVersion[] = "1.0";
43 static const char kOAuthVersionLabel[] = "oauth_version";
44
45 enum ParseQueryState {
46 START_STATE,
47 KEYWORD_STATE,
48 VALUE_STATE,
49 };
50
51 const std::string HttpMethodName(OAuthRequestSigner::HttpMethod method) {
52 switch (method) {
53 case OAuthRequestSigner::GET_METHOD:
54 return "GET";
55 case OAuthRequestSigner::POST_METHOD:
56 return "POST";
57 }
58 NOTREACHED();
59 return *(new std::string());
60 }
61
62 const std::string SignatureMethodName(
63 OAuthRequestSigner::SignatureMethod method) {
64 switch (method) {
65 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
66 return "HMAC-SHA1";
67 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
68 return "RSA-SHA1";
69 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
70 return "PLAINTEXT";
71 }
72 NOTREACHED();
73 return *(new std::string());
74 }
75
76 // The form of percent encoding used for OAuth request signing is very
77 // specific and strict. See http://oauth.net/core/1.0/#encoding_parameters.
78 //
79 // Any character which is in the "unreserved set" must not be encoded.
80 // All other characters must be encoded.
81 //
82 // The unreserved set is comprised of the alphanumeric characters and these
83 // others:
84 // - minus (-)
85 // - period (.)
86 // - underscore (_)
87 // - tilde (~)
88 std::string EncodedOAuthParameter(const std::string& text) {
89 std::string result = "";
90 std::string::const_iterator cursor;
91 std::string::const_iterator limit;
92 for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
93 char character = *cursor;
94 if (isalnum(character)) {
95 result += character;
96 } else {
97 switch (character) {
98 case '-':
99 case '.':
100 case '_':
101 case '~':
102 result += character;
103 break;
104 default:
105 unsigned char byte = static_cast<unsigned char>(character);
106 result = result + '%' + kHexDigits[byte / kHexBase] +
107 kHexDigits[byte % kHexBase];
108 }
109 }
110 }
111 return result;
112 }
113
114 std::string BuildBaseString(const GURL& request_base_url,
115 OAuthRequestSigner::HttpMethod http_method,
116 const std::string base_parameters) {
117 return StringPrintf("%s&%s&%s",
118 HttpMethodName(http_method).c_str(),
119 EncodedOAuthParameter(request_base_url.spec()).c_str(),
120 EncodedOAuthParameter(base_parameters).c_str());
121 }
122
123 std::string BuildBaseStringParameters(
124 const OAuthRequestSigner::Parameters& parameters) {
125 std::string result = "";
126 OAuthRequestSigner::Parameters::const_iterator cursor;
127 OAuthRequestSigner::Parameters::const_iterator limit;
128 bool first = true;
129 for (cursor = parameters.begin(), limit = parameters.end();
130 cursor != limit;
131 ++cursor) {
132 if (first) {
133 first = false;
134 } else {
135 result += '&';
136 }
137 result += EncodedOAuthParameter(cursor->first);
138 result += '=';
139 result += EncodedOAuthParameter(cursor->second);
140 }
141 return result;
142 }
143
144 std::string GenerateNonce() {
145 char result[kMaxNonceLength + 1];
146 int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) +
147 kMinNonceLength;
148 result[length] = '\0';
149 for (int index = 0; index < length; ++index)
150 result[index] = kOAuthNonceCharacters[
151 base::RandUint64() % (sizeof(kOAuthNonceCharacters) - 1)];
152 return result;
153 }
154
155 std::string GenerateTimestamp() {
156 return base::StringPrintf(
157 "%" PRId64,
158 (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds());
159 }
160
161 // Creates a string-to-string, keyword-value map from a parameter/query string
162 // that uses ampersand (&) to seperate paris and equals (=) to seperate
163 // keyword from value.
164 bool ParseQuery(const std::string& query,
165 OAuthRequestSigner::Parameters* parameters_result) {
166 std::string::const_iterator cursor;
167 std::string keyword;
168 std::string::const_iterator limit;
169 OAuthRequestSigner::Parameters parameters;
170 ParseQueryState state;
171 std::string value;
172
173 state = START_STATE;
174 for (cursor = query.begin(), limit = query.end();
175 cursor != limit;
176 ++cursor) {
177 char character = *cursor;
178 switch (state) {
179 case KEYWORD_STATE:
180 switch (character) {
181 case '&':
182 parameters[keyword] = value;
183 keyword = "";
184 value = "";
185 state = START_STATE;
186 break;
187 case '=':
188 state = VALUE_STATE;
189 break;
190 default:
191 keyword += character;
192 }
193 break;
194 case START_STATE:
195 switch (character) {
196 case '&': // Intentionally falling through
197 case '=':
198 return false;
199 default:
200 keyword += character;
201 state = KEYWORD_STATE;
202 }
203 break;
204 case VALUE_STATE:
205 switch (character) {
206 case '=':
207 return false;
208 case '&':
209 parameters[keyword] = value;
210 keyword = "";
211 value = "";
212 state = START_STATE;
213 break;
214 default:
215 value += character;
216 }
217 break;
218 }
219 }
220 switch (state) {
221 case START_STATE:
222 break;
223 case KEYWORD_STATE: // Intentionally falling through
224 case VALUE_STATE:
225 parameters[keyword] = value;
226 break;
227 default:
228 NOTREACHED();
229 }
230 *parameters_result = parameters;
231 return true;
232 }
233
234 // Creates the value for the oauth_signature parameter when the
235 // oauth_signature_method is HMAC-SHA1.
236 bool SignHmacSha1(const std::string& text,
237 const std::string& key,
238 std::string* signature_return) {
239 crypto::HMAC hmac(crypto::HMAC::SHA1);
240 size_t digest_length = hmac.DigestLength();
241 unsigned char* digest = new unsigned char [digest_length];
242 hmac.Init(key);
243 return hmac.Sign(text, digest, digest_length) &&
244 base::Base64Encode(std::string(reinterpret_cast<const char*>(digest),
245 digest_length),
246 signature_return);
247 }
248
249 // Creates the value for the oauth_signature parameter when the
250 // oauth_signature_method is PLAINTEXT.
251 //
252 // Not yet implemented, and might never be.
253 bool SignPlaintext(const std::string& text,
254 const std::string& key,
255 std::string* result) {
256 NOTIMPLEMENTED();
257 return false;
258 }
259
260 // Creates the value for the oauth_signature parameter when the
261 // oauth_signature_method is RSA-SHA1.
262 //
263 // Not yet implemented, and might never be.
264 bool SignRsaSha1(const std::string& text,
265 const std::string& key,
266 std::string* result) {
267 NOTIMPLEMENTED();
268 return false;
269 }
270
271 } // namespace
272
273 // static
274 bool OAuthRequestSigner::ParseAndSign(const GURL& request_url_with_parameters,
275 SignatureMethod signature_method,
276 HttpMethod http_method,
277 const std::string& consumer_key,
278 const std::string& consumer_secret,
279 const std::string& token_key,
280 const std::string& token_secret,
281 std::string* result) {
282 DCHECK(request_url_with_parameters.is_valid());
283 Parameters parameters;
284 if (request_url_with_parameters.has_query()) {
285 const std::string& query = request_url_with_parameters.query();
286 if (!query.empty()) {
287 if (!ParseQuery(query, &parameters))
288 return false;
289 }
290 }
291 std::string spec = request_url_with_parameters.spec();
292 std::string url_without_parameters = spec;
293 std::string::size_type question = spec.find("?");
294 if (question != std::string::npos) {
295 url_without_parameters = spec.substr(0,question);
296 }
297 return Sign (GURL(url_without_parameters), parameters, signature_method,
298 http_method, consumer_key, consumer_secret, token_key,
299 token_secret, result);
300 }
301
302 // Returns a copy of request_parameters, with parameters that are required by
303 // OAuth added as needed.
304 OAuthRequestSigner::Parameters
305 PrepareParameters(const OAuthRequestSigner::Parameters& request_parameters,
306 OAuthRequestSigner::SignatureMethod signature_method,
307 OAuthRequestSigner::HttpMethod http_method,
308 const std::string& consumer_key,
309 const std::string& token_key) {
310 OAuthRequestSigner::Parameters result(request_parameters);
311
312 if (result.find(kOAuthNonceLabel) == result.end())
313 result[kOAuthNonceLabel] = GenerateNonce();
314
315 if (result.find(kOAuthTimestampLabel) == result.end())
316 result[kOAuthTimestampLabel] = GenerateTimestamp();
317
318 result[kOAuthConsumerKeyLabel] = consumer_key;
319 result[kOAuthSignatureMethodLabel] = SignatureMethodName(signature_method);
320 result[kOAuthTokenLabel] = token_key;
321 result[kOAuthVersionLabel] = kOAuthVersion;
322
323 return result;
324 }
325
326 // static
327 bool OAuthRequestSigner::Sign(
328 const GURL& request_base_url,
329 const Parameters& request_parameters,
330 SignatureMethod signature_method,
331 HttpMethod http_method,
332 const std::string& consumer_key,
333 const std::string& consumer_secret,
334 const std::string& token_key,
335 const std::string& token_secret,
336 std::string* signed_text_return) {
337 DCHECK(request_base_url.is_valid());
338 Parameters parameters = PrepareParameters(request_parameters,
339 signature_method,
340 http_method,
341 consumer_key,
342 token_key);
343 std::string base_parameters = BuildBaseStringParameters(parameters);
344 std::string base = BuildBaseString(request_base_url,
345 http_method,
346 base_parameters);
347 std::string key = consumer_secret + '&' + token_secret;
348 bool is_signed = false;
349 std::string signature;
350 switch (signature_method) {
351 case HMAC_SHA1_SIGNATURE:
352 is_signed = SignHmacSha1(base, key, &signature);
353 break;
354 case RSA_SHA1_SIGNATURE:
355 is_signed = SignRsaSha1(base, key, &signature);
356 break;
357 case PLAINTEXT_SIGNATURE:
358 is_signed = SignPlaintext(base, key, &signature);
359 break;
360 default:
361 NOTREACHED();
362 }
363 if (is_signed) {
364 std::string signed_text;
365 switch (http_method) {
366 case GET_METHOD:
367 signed_text = request_base_url.spec() + '?';
368 // Intentionally falling through
369 case POST_METHOD:
370 signed_text += base_parameters + '&' + kOAuthSignatureLabel + '=' +
371 EncodedOAuthParameter(signature);
372 break;
373 default:
374 NOTREACHED();
375 }
376 *signed_text_return = signed_text;
377 }
378 return is_signed;
379 }
OLDNEW
« 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