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

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