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

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: Fixed indentation of switch cases and defaults. 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 <iostream>
11 #include <map>
12 #include <sstream>
13 #include <string>
14
15 #include "base/base64.h"
16 #include "base/logging.h"
17 #include "base/rand_util.h"
18 #include "base/stringprintf.h"
19 #include "crypto/hmac.h"
20 #include "googleurl/src/gurl.h"
21
22 static const int kMinNonceLength = 15;
Nicolas Zea 2011/06/16 18:59:27 Include the static globals in the anonymous namesp
Rick Campbell 2011/06/17 16:57:54 Done.
23 static const int kMaxNonceLength = 30;
24 static const int kMaxTimeLength = 16; // 11 + 1 is sufficient
25
26 static const std::string OAuthConsumerKeyLabel = "oauth_consumer_key";
Mattias Nissler (ping if slow) 2011/06/16 17:20:34 I thought we have a policy to not non-POD statics?
Rick Campbell 2011/06/17 16:57:54 Done.
27 static const std::string OAuthConsumerSecretLabel = "oauth_consumer_secret";
28 static const std::string OAuthNonceLabel = "oauth_nonce";
29 static const std::string OAuthSignatureLabel = "oauth_signature";
30 static const std::string OAuthSignatureMethodLabel = "oauth_signature_method";
31 static const std::string OAuthTimestampLabel = "oauth_timestamp";
32 static const std::string OAuthTokenLabel = "oauth_token";
33 static const std::string OAuthTokenSecretLabel = "oauth_token_secret";
34 static const std::string OAUTH_VERSION = "1.0";
35 static const std::string OAuthVersionLabel = "oauth_version";
36
37 enum ParseQueryState {
38 START_STATE,
39 KEYWORD_STATE,
40 VALUE_STATE,
41 };
42
43 namespace {
44
45 const std::string& HttpMethodName(OAuthRequestSigner::HttpMethod method) {
46 static std::string get_result = "GET";
Nicolas Zea 2011/06/16 18:59:27 Mattias's comment applies here and with the other
Rick Campbell 2011/06/17 16:57:54 Done. Also changed return type to not be a refere
47 static std::string post_result = "POST";
48
49 switch (method) {
50 case OAuthRequestSigner::GET_METHOD:
51 return get_result;
52 case OAuthRequestSigner::POST_METHOD:
53 return post_result;
54 }
55 NOTREACHED();
56 return *(new std::string());
57 }
58
59 const std::string& SignatureMethodName(
60 OAuthRequestSigner::SignatureMethod method) {
61 static std::string hmac_sha1_result = "HMAC-SHA1";
62 static std::string rsa_sha1_result = "RSA-SHA1";
63 static std::string plaintext_result = "PLAINTEXT";
64
65 switch (method) {
66 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
67 return hmac_sha1_result;
68 case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
69 return rsa_sha1_result;
70 case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
71 return plaintext_result;
72 }
73 NOTREACHED();
74 return *(new std::string());
75 }
76
77 // The form of percent encoding used for OAuth request signing is very
78 // specific and strict. See http://oauth.net/core/1.0/#encoding_parameters.
79 //
80 // Any character which is in the "unreserved set" must not be encoded.
81 // All other characters must be encoded.
82 //
83 // The unreserved set is comprised of the alphanumeric characters and these
84 // others:
85 // - minus (-)
86 // - period (.)
87 // - underscore (_)
88 // - tilde (~)
89 void OAuthParameterEncode(std::ostream& output,
Mattias Nissler (ping if slow) 2011/06/16 17:20:34 static
Nicolas Zea 2011/06/16 18:59:27 stream's are only allowed to be used for logging u
Rick Campbell 2011/06/17 16:57:54 This is within an anonymous namespace. Not *exact
Rick Campbell 2011/06/17 16:57:54 Done.
90 const std::string& text) {
91 static std::string kHexDigits = "0123456789ABCDEF";
Rick Campbell 2011/06/17 16:57:54 Changed to const char[] and moved to top of anonym
92 std::string::const_iterator cursor;
93 std::string::const_iterator limit;
94 for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
95 char character = *cursor;
96 if (isalnum(character)) {
97 output << character;
98 } else {
99 switch (character) {
100 case '-':
101 case '.':
102 case '_':
103 case '~':
104 output << character;
105 break;
106 default:
107 unsigned char byte = static_cast<unsigned char>(character);
108 output << '%';
109 output << kHexDigits[byte / 16];
110 output << kHexDigits[byte % 16];
111 }
112 }
113 }
114 }
115
116 void OAuthParameterDecode(std::ostream& output,
Mattias Nissler (ping if slow) 2011/06/16 17:20:34 shouldn't all these helper functions either be sta
Rick Campbell 2011/06/17 16:57:54 Yes, they are within an anonymous namespace. I've
Rick Campbell 2011/06/17 16:57:54 The decode routine is not used (and implemented in
Mattias Nissler (ping if slow) 2011/06/17 17:10:04 No, that's fine, I missed the namespace declaratio
117 const std::string& text) {
118 static std::string kHexDigits = "0123456789ABCDEF";
Rick Campbell 2011/06/17 16:57:54 Changed to const char[]
119 std::string::const_iterator cursor;
120 std::string::const_iterator limit;
121 for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
122 char character = *cursor;
123 if (isalnum(character)) {
124 output << character;
125 } else {
126 switch (character) {
127 case '-':
128 case '.':
129 case '_':
130 case '~':
131 output << character;
132 break;
133 default:
134 unsigned char byte = static_cast<unsigned char>(character);
135 output << '%';
136 output << kHexDigits[byte / 16];
137 output << kHexDigits[byte % 16];
138 }
139 }
140 }
141 }
142
143 std::string EncodedOAuthParameter(const std::string& text) {
144 std::ostringstream output;
145 OAuthParameterEncode(output, text);
146 return output.str();
147 }
148
149 std::string BuildBaseString(const GURL& request_base_url,
150 OAuthRequestSigner::HttpMethod http_method,
151 const std::string base_parameters) {
152 std::ostringstream output;
153 output << HttpMethodName(http_method) << '&';
154 OAuthParameterEncode(output, request_base_url.spec());
155 output << '&';
156 OAuthParameterEncode(output, base_parameters);
157 return output.str();
158 }
159
160 std::string BuildBaseStringParameters(
161 const OAuthRequestSigner::Parameters& parameters) {
162 std::ostringstream output;
163 OAuthRequestSigner::Parameters::const_iterator cursor;
164 OAuthRequestSigner::Parameters::const_iterator limit;
165 bool first = true;
166 for (cursor = parameters.begin(), limit = parameters.end();
167 cursor != limit;
168 ++cursor) {
169 if (first) {
170 first = false;
171 } else {
172 output << '&';
173 }
174 OAuthParameterEncode(output, cursor->first);
175 output << '=';
176 OAuthParameterEncode(output, cursor->second);
177 }
178 return output.str();
179 }
180
181 std::string GenerateNonce() {
182 static const char SELECTION[] =
183 "abcdefghijklmnopqrstuvwyz"
184 "ABCDEFGHIJKLMNOPQRSTUVWYZ"
185 "0123456789_";
186 char result[kMaxNonceLength + 1];
187 int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) +
188 kMinNonceLength;
189 result[length] = '\0';
190 for (int index = 0; index < length; ++index)
191 result[index] = SELECTION[base::RandUint64() % (sizeof(SELECTION) - 1)];
192 return result;
193 }
194
195 std::string GenerateTimestamp() {
196 time_t now;
197 char result [kMaxTimeLength];
198 time(&now);
Mattias Nissler (ping if slow) 2011/06/16 17:20:34 use base/time.h for portability?
Rick Campbell 2011/06/17 16:57:54 I'm going for Time::NowFromSystemTime().ToTimeT()
Mattias Nissler (ping if slow) 2011/06/17 17:10:04 Some time ago, I solved this by (Time::NowFromSyst
Rick Campbell 2011/06/17 18:53:45 Done.
199 base::StringPrintf("%ld", now);
200 return result;
201 }
202
203 // Creates a string-to-string, keyword-value map from a parameter/query string
204 // that uses ampersand (&) to seperate paris and equals (=) to seperate
205 // keyword from value.
206 bool ParseQuery(const std::string& query,
207 OAuthRequestSigner::Parameters* parameters_result) {
208 std::string::const_iterator cursor;
209 std::string keyword;
210 std::string::const_iterator limit;
211 OAuthRequestSigner::Parameters parameters;
212 ParseQueryState state;
213 std::string value;
214
215 state = START_STATE;
216 for (cursor = query.begin(), limit = query.end();
217 cursor != limit;
218 ++cursor) {
219 char character = *cursor;
220 switch (state) {
221 case KEYWORD_STATE:
222 switch (character) {
223 case '&':
224 parameters[keyword] = value;
225 keyword = "";
226 value = "";
227 state = START_STATE;
228 break;
229 case '=':
230 state = VALUE_STATE;
231 break;
232 default:
233 keyword += character;
234 }
235 break;
236 case START_STATE:
237 switch (character) {
238 case '&': // Intentionally falling through
239 case '=':
240 return false;
241 default:
242 keyword += character;
243 state = KEYWORD_STATE;
244 }
245 break;
246 case VALUE_STATE:
247 switch (character) {
248 case '=':
249 return false;
250 case '&':
251 parameters[keyword] = value;
252 keyword = "";
253 value = "";
254 state = START_STATE;
255 break;
256 default:
257 value += character;
258 }
259 break;
260 }
261 }
262 switch (state) {
263 case START_STATE:
264 break;
265 case KEYWORD_STATE: // Intentionally falling through
266 case VALUE_STATE:
267 parameters[keyword] = value;
268 break;
269 default:
270 NOTREACHED();
271 }
272 *parameters_result = parameters;
273 return true;
274 }
275
276 // Creates the value for the oauth_signature parameter when the
277 // oauth_signature_method is HMAC-SHA1.
278 bool SignHmacSha1(const std::string& text,
279 const std::string& key,
280 std::string* signature_return) {
281 crypto::HMAC hmac(crypto::HMAC::SHA1);
282 size_t digest_length = hmac.DigestLength();
283 unsigned char* digest = new unsigned char [digest_length];
284 hmac.Init(key);
285 return hmac.Sign(text, digest, digest_length) &&
286 base::Base64Encode(std::string(reinterpret_cast<const char*>(digest),
287 digest_length),
288 signature_return);
289 }
290
291 // Creates the value for the oauth_signature parameter when the
292 // oauth_signature_method is PLAINTEXT.
293 //
294 // Not yet implemented, and might never be.
295 bool SignPlaintext(const std::string& text,
296 const std::string& key,
297 std::string* result) {
298 NOTIMPLEMENTED();
299 return false;
300 }
301
302 // Creates the value for the oauth_signature parameter when the
303 // oauth_signature_method is RSA-SHA1.
304 //
305 // Not yet implemented, and might never be.
306 bool SignRsaSha1(const std::string& text,
307 const std::string& key,
308 std::string* result) {
309 NOTIMPLEMENTED();
310 return false;
311 }
312
313 }
Nicolas Zea 2011/06/16 18:59:27 "} // namespace"
Rick Campbell 2011/06/17 16:57:54 Done.
314
315 // static
316 bool OAuthRequestSigner::ParseAndSign(const GURL& request_url_with_parameters,
317 SignatureMethod signature_method,
318 HttpMethod http_method,
319 const std::string& consumer_key,
320 const std::string& consumer_secret,
321 const std::string& token_key,
322 const std::string& token_secret,
323 std::string* result) {
324 DCHECK(request_url_with_parameters.is_valid());
325 Parameters parameters;
326 if (request_url_with_parameters.has_query()) {
327 const std::string& query = request_url_with_parameters.query();
328 if (!query.empty()) {
329 if (!ParseQuery(query, &parameters))
330 return false;
331 }
332 }
333 std::string spec = request_url_with_parameters.spec();
334 std::string url_without_parameters = spec;
335 std::string::size_type question = spec.find("?");
336 if (question != std::string::npos) {
337 url_without_parameters = spec.substr(0,question);
338 }
339 return Sign (GURL(url_without_parameters), parameters, signature_method,
340 http_method, consumer_key, consumer_secret, token_key,
341 token_secret, result);
342 }
343
344 // Returns a copy of request_parameters, with parameters that are required by
345 // OAuth added as needed.
346 OAuthRequestSigner::Parameters
347 PrepareParameters(const OAuthRequestSigner::Parameters& request_parameters,
348 OAuthRequestSigner::SignatureMethod signature_method,
349 OAuthRequestSigner::HttpMethod http_method,
350 const std::string& consumer_key,
351 const std::string& token_key) {
352 OAuthRequestSigner::Parameters result(request_parameters);
353
354 if (result.find(OAuthNonceLabel) == result.end())
355 result[OAuthNonceLabel] = GenerateNonce();
356
357 if (result.find(OAuthTimestampLabel) == result.end())
358 result[OAuthTimestampLabel] = GenerateTimestamp();
359
360 result[OAuthConsumerKeyLabel] = consumer_key;
361 result[OAuthSignatureMethodLabel] = SignatureMethodName(signature_method);
362 result[OAuthTokenLabel] = token_key;
363 result[OAuthVersionLabel] = OAUTH_VERSION;
364
365 return result;
366 }
367
368 // static
369 bool OAuthRequestSigner::Sign(
370 const GURL& request_base_url,
371 const Parameters& request_parameters,
372 SignatureMethod signature_method,
373 HttpMethod http_method,
374 const std::string& consumer_key,
375 const std::string& consumer_secret,
376 const std::string& token_key,
377 const std::string& token_secret,
378 std::string* signed_text_return) {
379 DCHECK(request_base_url.is_valid());
380 Parameters parameters = PrepareParameters(request_parameters,
381 signature_method,
382 http_method,
383 consumer_key,
384 token_key);
385 std::string base_parameters = BuildBaseStringParameters(parameters);
386 std::string base = BuildBaseString(request_base_url,
387 http_method,
388 base_parameters);
389 std::string key = consumer_secret + '&' + token_secret;
390 bool is_signed = false;
391 std::string signature;
392 switch (signature_method) {
393 case HMAC_SHA1_SIGNATURE:
394 is_signed = SignHmacSha1(base, key, &signature);
395 break;
396 case RSA_SHA1_SIGNATURE:
397 is_signed = SignRsaSha1(base, key, &signature);
398 break;
399 case PLAINTEXT_SIGNATURE:
400 is_signed = SignPlaintext(base, key, &signature);
401 break;
402 default:
403 NOTREACHED();
404 }
405 if (is_signed) {
406 std::string signed_text;
407 switch (http_method) {
408 case GET_METHOD:
409 signed_text = request_base_url.spec() + '?';
410 // Intentionally falling through
411 case POST_METHOD:
412 signed_text += base_parameters + '&' + OAuthSignatureLabel + '=' +
413 EncodedOAuthParameter(signature);
414 break;
415 default:
416 NOTREACHED();
417 }
418 *signed_text_return = signed_text;
419 }
420 return is_signed;
421 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698