1 // Copyright (c) 2012 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.
5 #include "google_apis/gaia/oauth_request_signer.h"
15 #include "base/base64.h"
16 #include "base/format_macros.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/time/time.h"
22 #include "crypto/hmac.h"
27 const int kHexBase
= 16;
28 char kHexDigits
[] = "0123456789ABCDEF";
29 const size_t kHmacDigestLength
= 20;
30 const int kMaxNonceLength
= 30;
31 const int kMinNonceLength
= 15;
33 const char kOAuthConsumerKeyLabel
[] = "oauth_consumer_key";
34 const char kOAuthNonceCharacters
[] =
35 "abcdefghijklmnopqrstuvwyz"
36 "ABCDEFGHIJKLMNOPQRSTUVWYZ"
38 const char kOAuthNonceLabel
[] = "oauth_nonce";
39 const char kOAuthSignatureLabel
[] = "oauth_signature";
40 const char kOAuthSignatureMethodLabel
[] = "oauth_signature_method";
41 const char kOAuthTimestampLabel
[] = "oauth_timestamp";
42 const char kOAuthTokenLabel
[] = "oauth_token";
43 const char kOAuthVersion
[] = "1.0";
44 const char kOAuthVersionLabel
[] = "oauth_version";
46 enum ParseQueryState
{
52 const std::string
HttpMethodName(OAuthRequestSigner::HttpMethod method
) {
54 case OAuthRequestSigner::GET_METHOD
:
56 case OAuthRequestSigner::POST_METHOD
:
63 const std::string
SignatureMethodName(
64 OAuthRequestSigner::SignatureMethod method
) {
66 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE
:
68 case OAuthRequestSigner::RSA_SHA1_SIGNATURE
:
70 case OAuthRequestSigner::PLAINTEXT_SIGNATURE
:
77 std::string
BuildBaseString(const GURL
& request_base_url
,
78 OAuthRequestSigner::HttpMethod http_method
,
79 const std::string
& base_parameters
) {
80 return base::StringPrintf("%s&%s&%s",
81 HttpMethodName(http_method
).c_str(),
82 OAuthRequestSigner::Encode(
83 request_base_url
.spec()).c_str(),
84 OAuthRequestSigner::Encode(
85 base_parameters
).c_str());
88 std::string
BuildBaseStringParameters(
89 const OAuthRequestSigner::Parameters
& parameters
) {
91 OAuthRequestSigner::Parameters::const_iterator cursor
;
92 OAuthRequestSigner::Parameters::const_iterator limit
;
94 for (cursor
= parameters
.begin(), limit
= parameters
.end();
101 result
+= OAuthRequestSigner::Encode(cursor
->first
);
103 result
+= OAuthRequestSigner::Encode(cursor
->second
);
108 std::string
GenerateNonce() {
109 char result
[kMaxNonceLength
+ 1];
110 int length
= base::RandUint64() % (kMaxNonceLength
- kMinNonceLength
+ 1) +
112 result
[length
] = '\0';
113 for (int index
= 0; index
< length
; ++index
)
114 result
[index
] = kOAuthNonceCharacters
[
115 base::RandUint64() % (sizeof(kOAuthNonceCharacters
) - 1)];
119 std::string
GenerateTimestamp() {
120 return base::StringPrintf(
122 (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds());
125 // Creates a string-to-string, keyword-value map from a parameter/query string
126 // that uses ampersand (&) to seperate paris and equals (=) to seperate
127 // keyword from value.
128 bool ParseQuery(const std::string
& query
,
129 OAuthRequestSigner::Parameters
* parameters_result
) {
130 std::string::const_iterator cursor
;
132 std::string::const_iterator limit
;
133 OAuthRequestSigner::Parameters parameters
;
134 ParseQueryState state
;
138 for (cursor
= query
.begin(), limit
= query
.end();
141 char character
= *cursor
;
146 parameters
[keyword
] = value
;
155 keyword
+= character
;
160 case '&': // Intentionally falling through
164 keyword
+= character
;
165 state
= KEYWORD_STATE
;
173 parameters
[keyword
] = value
;
187 case KEYWORD_STATE
: // Intentionally falling through
189 parameters
[keyword
] = value
;
194 *parameters_result
= parameters
;
198 // Creates the value for the oauth_signature parameter when the
199 // oauth_signature_method is HMAC-SHA1.
200 bool SignHmacSha1(const std::string
& text
,
201 const std::string
& key
,
202 std::string
* signature_return
) {
203 crypto::HMAC
hmac(crypto::HMAC::SHA1
);
204 DCHECK(hmac
.DigestLength() == kHmacDigestLength
);
205 unsigned char digest
[kHmacDigestLength
];
206 bool result
= hmac
.Init(key
) &&
207 hmac
.Sign(text
, digest
, kHmacDigestLength
);
210 std::string(reinterpret_cast<const char*>(digest
), kHmacDigestLength
),
216 // Creates the value for the oauth_signature parameter when the
217 // oauth_signature_method is PLAINTEXT.
219 // Not yet implemented, and might never be.
220 bool SignPlaintext(const std::string
& text
,
221 const std::string
& key
,
222 std::string
* result
) {
227 // Creates the value for the oauth_signature parameter when the
228 // oauth_signature_method is RSA-SHA1.
230 // Not yet implemented, and might never be.
231 bool SignRsaSha1(const std::string
& text
,
232 const std::string
& key
,
233 std::string
* result
) {
238 // Adds parameters that are required by OAuth added as needed to |parameters|.
239 void PrepareParameters(OAuthRequestSigner::Parameters
* parameters
,
240 OAuthRequestSigner::SignatureMethod signature_method
,
241 OAuthRequestSigner::HttpMethod http_method
,
242 const std::string
& consumer_key
,
243 const std::string
& token_key
) {
244 if (parameters
->find(kOAuthNonceLabel
) == parameters
->end())
245 (*parameters
)[kOAuthNonceLabel
] = GenerateNonce();
247 if (parameters
->find(kOAuthTimestampLabel
) == parameters
->end())
248 (*parameters
)[kOAuthTimestampLabel
] = GenerateTimestamp();
250 (*parameters
)[kOAuthConsumerKeyLabel
] = consumer_key
;
251 (*parameters
)[kOAuthSignatureMethodLabel
] =
252 SignatureMethodName(signature_method
);
253 (*parameters
)[kOAuthTokenLabel
] = token_key
;
254 (*parameters
)[kOAuthVersionLabel
] = kOAuthVersion
;
257 // Implements shared signing logic, generating the signature and storing it in
258 // |parameters|. Returns true if the signature has been generated succesfully.
259 bool SignParameters(const GURL
& request_base_url
,
260 OAuthRequestSigner::SignatureMethod signature_method
,
261 OAuthRequestSigner::HttpMethod http_method
,
262 const std::string
& consumer_key
,
263 const std::string
& consumer_secret
,
264 const std::string
& token_key
,
265 const std::string
& token_secret
,
266 OAuthRequestSigner::Parameters
* parameters
) {
267 DCHECK(request_base_url
.is_valid());
268 PrepareParameters(parameters
, signature_method
, http_method
,
269 consumer_key
, token_key
);
270 std::string base_parameters
= BuildBaseStringParameters(*parameters
);
271 std::string base
= BuildBaseString(request_base_url
, http_method
,
273 std::string key
= consumer_secret
+ '&' + token_secret
;
274 bool is_signed
= false;
275 std::string signature
;
276 switch (signature_method
) {
277 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE
:
278 is_signed
= SignHmacSha1(base
, key
, &signature
);
280 case OAuthRequestSigner::RSA_SHA1_SIGNATURE
:
281 is_signed
= SignRsaSha1(base
, key
, &signature
);
283 case OAuthRequestSigner::PLAINTEXT_SIGNATURE
:
284 is_signed
= SignPlaintext(base
, key
, &signature
);
290 (*parameters
)[kOAuthSignatureLabel
] = signature
;
298 bool OAuthRequestSigner::Decode(const std::string
& text
,
299 std::string
* decoded_text
) {
300 std::string accumulator
;
301 std::string::const_iterator cursor
;
302 std::string::const_iterator limit
;
303 for (limit
= text
.end(), cursor
= text
.begin(); cursor
!= limit
; ++cursor
) {
304 char character
= *cursor
;
305 if (character
== '%') {
309 char* first
= strchr(kHexDigits
, *cursor
);
312 int high
= first
- kHexDigits
;
313 DCHECK(high
>= 0 && high
< kHexBase
);
318 char* second
= strchr(kHexDigits
, *cursor
);
321 int low
= second
- kHexDigits
;
322 DCHECK(low
>= 0 || low
< kHexBase
);
324 char decoded
= static_cast<char>(high
* kHexBase
+ low
);
325 DCHECK(!(base::IsAsciiAlpha(decoded
) || base::IsAsciiDigit(decoded
)));
326 DCHECK(!(decoded
&& strchr("-._~", decoded
)));
327 accumulator
+= decoded
;
329 accumulator
+= character
;
332 *decoded_text
= accumulator
;
337 std::string
OAuthRequestSigner::Encode(const std::string
& text
) {
339 std::string::const_iterator cursor
;
340 std::string::const_iterator limit
;
341 for (limit
= text
.end(), cursor
= text
.begin(); cursor
!= limit
; ++cursor
) {
342 char character
= *cursor
;
343 if (base::IsAsciiAlpha(character
) || base::IsAsciiDigit(character
)) {
354 unsigned char byte
= static_cast<unsigned char>(character
);
355 result
= result
+ '%' + kHexDigits
[byte
/ kHexBase
] +
356 kHexDigits
[byte
% kHexBase
];
364 bool OAuthRequestSigner::ParseAndSign(const GURL
& request_url_with_parameters
,
365 SignatureMethod signature_method
,
366 HttpMethod http_method
,
367 const std::string
& consumer_key
,
368 const std::string
& consumer_secret
,
369 const std::string
& token_key
,
370 const std::string
& token_secret
,
371 std::string
* result
) {
372 DCHECK(request_url_with_parameters
.is_valid());
373 Parameters parameters
;
374 if (request_url_with_parameters
.has_query()) {
375 const std::string
& query
= request_url_with_parameters
.query();
376 if (!query
.empty()) {
377 if (!ParseQuery(query
, ¶meters
))
381 std::string spec
= request_url_with_parameters
.spec();
382 std::string url_without_parameters
= spec
;
383 std::string::size_type question
= spec
.find("?");
384 if (question
!= std::string::npos
)
385 url_without_parameters
= spec
.substr(0,question
);
386 return SignURL(GURL(url_without_parameters
), parameters
, signature_method
,
387 http_method
, consumer_key
, consumer_secret
, token_key
,
388 token_secret
, result
);
392 bool OAuthRequestSigner::SignURL(
393 const GURL
& request_base_url
,
394 const Parameters
& request_parameters
,
395 SignatureMethod signature_method
,
396 HttpMethod http_method
,
397 const std::string
& consumer_key
,
398 const std::string
& consumer_secret
,
399 const std::string
& token_key
,
400 const std::string
& token_secret
,
401 std::string
* signed_text_return
) {
402 DCHECK(request_base_url
.is_valid());
403 Parameters
parameters(request_parameters
);
404 bool is_signed
= SignParameters(request_base_url
, signature_method
,
405 http_method
, consumer_key
, consumer_secret
,
406 token_key
, token_secret
, ¶meters
);
408 std::string signed_text
;
409 switch (http_method
) {
411 signed_text
= request_base_url
.spec() + '?';
412 // Intentionally falling through
414 signed_text
+= BuildBaseStringParameters(parameters
);
419 *signed_text_return
= signed_text
;
425 bool OAuthRequestSigner::SignAuthHeader(
426 const GURL
& request_base_url
,
427 const Parameters
& request_parameters
,
428 SignatureMethod signature_method
,
429 HttpMethod http_method
,
430 const std::string
& consumer_key
,
431 const std::string
& consumer_secret
,
432 const std::string
& token_key
,
433 const std::string
& token_secret
,
434 std::string
* signed_text_return
) {
435 DCHECK(request_base_url
.is_valid());
436 Parameters
parameters(request_parameters
);
437 bool is_signed
= SignParameters(request_base_url
, signature_method
,
438 http_method
, consumer_key
, consumer_secret
,
439 token_key
, token_secret
, ¶meters
);
441 std::string signed_text
= "OAuth ";
443 for (Parameters::const_iterator param
= parameters
.begin();
444 param
!= parameters
.end();
453 OAuthRequestSigner::Encode(param
->first
).c_str(),
454 OAuthRequestSigner::Encode(param
->second
).c_str());
456 *signed_text_return
= signed_text
;