| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "chrome/common/net/gaia/oauth_request_signer.h" | |
| 6 | |
| 7 #include <cctype> | |
| 8 #include <cstddef> | |
| 9 #include <cstdlib> | |
| 10 #include <cstring> | |
| 11 #include <ctime> | |
| 12 #include <map> | |
| 13 #include <string> | |
| 14 | |
| 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/string_util.h" | |
| 20 #include "base/stringprintf.h" | |
| 21 #include "base/time.h" | |
| 22 #include "crypto/hmac.h" | |
| 23 #include "googleurl/src/gurl.h" | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 static const int kHexBase = 16; | |
| 28 static char kHexDigits[] = "0123456789ABCDEF"; | |
| 29 static const size_t kHmacDigestLength = 20; | |
| 30 static const int kMaxNonceLength = 30; | |
| 31 static const int kMinNonceLength = 15; | |
| 32 | |
| 33 static const char kOAuthConsumerKeyLabel[] = "oauth_consumer_key"; | |
| 34 static const char kOAuthConsumerSecretLabel[] = "oauth_consumer_secret"; | |
| 35 static const char kOAuthNonceCharacters[] = | |
| 36 "abcdefghijklmnopqrstuvwyz" | |
| 37 "ABCDEFGHIJKLMNOPQRSTUVWYZ" | |
| 38 "0123456789_"; | |
| 39 static const char kOAuthNonceLabel[] = "oauth_nonce"; | |
| 40 static const char kOAuthSignatureLabel[] = "oauth_signature"; | |
| 41 static const char kOAuthSignatureMethodLabel[] = "oauth_signature_method"; | |
| 42 static const char kOAuthTimestampLabel[] = "oauth_timestamp"; | |
| 43 static const char kOAuthTokenLabel[] = "oauth_token"; | |
| 44 static const char kOAuthTokenSecretLabel[] = "oauth_token_secret"; | |
| 45 static const char kOAuthVersion[] = "1.0"; | |
| 46 static const char kOAuthVersionLabel[] = "oauth_version"; | |
| 47 | |
| 48 enum ParseQueryState { | |
| 49 START_STATE, | |
| 50 KEYWORD_STATE, | |
| 51 VALUE_STATE, | |
| 52 }; | |
| 53 | |
| 54 const std::string HttpMethodName(OAuthRequestSigner::HttpMethod method) { | |
| 55 switch (method) { | |
| 56 case OAuthRequestSigner::GET_METHOD: | |
| 57 return "GET"; | |
| 58 case OAuthRequestSigner::POST_METHOD: | |
| 59 return "POST"; | |
| 60 } | |
| 61 NOTREACHED(); | |
| 62 return *(new std::string()); | |
| 63 } | |
| 64 | |
| 65 const std::string SignatureMethodName( | |
| 66 OAuthRequestSigner::SignatureMethod method) { | |
| 67 switch (method) { | |
| 68 case OAuthRequestSigner::HMAC_SHA1_SIGNATURE: | |
| 69 return "HMAC-SHA1"; | |
| 70 case OAuthRequestSigner::RSA_SHA1_SIGNATURE: | |
| 71 return "RSA-SHA1"; | |
| 72 case OAuthRequestSigner::PLAINTEXT_SIGNATURE: | |
| 73 return "PLAINTEXT"; | |
| 74 } | |
| 75 NOTREACHED(); | |
| 76 return *(new std::string()); | |
| 77 } | |
| 78 | |
| 79 std::string BuildBaseString(const GURL& request_base_url, | |
| 80 OAuthRequestSigner::HttpMethod http_method, | |
| 81 const std::string& base_parameters) { | |
| 82 return StringPrintf("%s&%s&%s", | |
| 83 HttpMethodName(http_method).c_str(), | |
| 84 OAuthRequestSigner::Encode( | |
| 85 request_base_url.spec()).c_str(), | |
| 86 OAuthRequestSigner::Encode( | |
| 87 base_parameters).c_str()); | |
| 88 } | |
| 89 | |
| 90 std::string BuildBaseStringParameters( | |
| 91 const OAuthRequestSigner::Parameters& parameters) { | |
| 92 std::string result = ""; | |
| 93 OAuthRequestSigner::Parameters::const_iterator cursor; | |
| 94 OAuthRequestSigner::Parameters::const_iterator limit; | |
| 95 bool first = true; | |
| 96 for (cursor = parameters.begin(), limit = parameters.end(); | |
| 97 cursor != limit; | |
| 98 ++cursor) { | |
| 99 if (first) | |
| 100 first = false; | |
| 101 else | |
| 102 result += '&'; | |
| 103 result += OAuthRequestSigner::Encode(cursor->first); | |
| 104 result += '='; | |
| 105 result += OAuthRequestSigner::Encode(cursor->second); | |
| 106 } | |
| 107 return result; | |
| 108 } | |
| 109 | |
| 110 std::string GenerateNonce() { | |
| 111 char result[kMaxNonceLength + 1]; | |
| 112 int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) + | |
| 113 kMinNonceLength; | |
| 114 result[length] = '\0'; | |
| 115 for (int index = 0; index < length; ++index) | |
| 116 result[index] = kOAuthNonceCharacters[ | |
| 117 base::RandUint64() % (sizeof(kOAuthNonceCharacters) - 1)]; | |
| 118 return result; | |
| 119 } | |
| 120 | |
| 121 std::string GenerateTimestamp() { | |
| 122 return base::StringPrintf( | |
| 123 "%" PRId64, | |
| 124 (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds()); | |
| 125 } | |
| 126 | |
| 127 // Creates a string-to-string, keyword-value map from a parameter/query string | |
| 128 // that uses ampersand (&) to seperate paris and equals (=) to seperate | |
| 129 // keyword from value. | |
| 130 bool ParseQuery(const std::string& query, | |
| 131 OAuthRequestSigner::Parameters* parameters_result) { | |
| 132 std::string::const_iterator cursor; | |
| 133 std::string keyword; | |
| 134 std::string::const_iterator limit; | |
| 135 OAuthRequestSigner::Parameters parameters; | |
| 136 ParseQueryState state; | |
| 137 std::string value; | |
| 138 | |
| 139 state = START_STATE; | |
| 140 for (cursor = query.begin(), limit = query.end(); | |
| 141 cursor != limit; | |
| 142 ++cursor) { | |
| 143 char character = *cursor; | |
| 144 switch (state) { | |
| 145 case KEYWORD_STATE: | |
| 146 switch (character) { | |
| 147 case '&': | |
| 148 parameters[keyword] = value; | |
| 149 keyword = ""; | |
| 150 value = ""; | |
| 151 state = START_STATE; | |
| 152 break; | |
| 153 case '=': | |
| 154 state = VALUE_STATE; | |
| 155 break; | |
| 156 default: | |
| 157 keyword += character; | |
| 158 } | |
| 159 break; | |
| 160 case START_STATE: | |
| 161 switch (character) { | |
| 162 case '&': // Intentionally falling through | |
| 163 case '=': | |
| 164 return false; | |
| 165 default: | |
| 166 keyword += character; | |
| 167 state = KEYWORD_STATE; | |
| 168 } | |
| 169 break; | |
| 170 case VALUE_STATE: | |
| 171 switch (character) { | |
| 172 case '=': | |
| 173 return false; | |
| 174 case '&': | |
| 175 parameters[keyword] = value; | |
| 176 keyword = ""; | |
| 177 value = ""; | |
| 178 state = START_STATE; | |
| 179 break; | |
| 180 default: | |
| 181 value += character; | |
| 182 } | |
| 183 break; | |
| 184 } | |
| 185 } | |
| 186 switch (state) { | |
| 187 case START_STATE: | |
| 188 break; | |
| 189 case KEYWORD_STATE: // Intentionally falling through | |
| 190 case VALUE_STATE: | |
| 191 parameters[keyword] = value; | |
| 192 break; | |
| 193 default: | |
| 194 NOTREACHED(); | |
| 195 } | |
| 196 *parameters_result = parameters; | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 // Creates the value for the oauth_signature parameter when the | |
| 201 // oauth_signature_method is HMAC-SHA1. | |
| 202 bool SignHmacSha1(const std::string& text, | |
| 203 const std::string& key, | |
| 204 std::string* signature_return) { | |
| 205 crypto::HMAC hmac(crypto::HMAC::SHA1); | |
| 206 DCHECK(hmac.DigestLength() == kHmacDigestLength); | |
| 207 unsigned char digest[kHmacDigestLength]; | |
| 208 bool result = hmac.Init(key) && | |
| 209 hmac.Sign(text, digest, kHmacDigestLength) && | |
| 210 base::Base64Encode(std::string(reinterpret_cast<const char*>(digest), | |
| 211 kHmacDigestLength), | |
| 212 signature_return); | |
| 213 return result; | |
| 214 } | |
| 215 | |
| 216 // Creates the value for the oauth_signature parameter when the | |
| 217 // oauth_signature_method is PLAINTEXT. | |
| 218 // | |
| 219 // Not yet implemented, and might never be. | |
| 220 bool SignPlaintext(const std::string& text, | |
| 221 const std::string& key, | |
| 222 std::string* result) { | |
| 223 NOTIMPLEMENTED(); | |
| 224 return false; | |
| 225 } | |
| 226 | |
| 227 // Creates the value for the oauth_signature parameter when the | |
| 228 // oauth_signature_method is RSA-SHA1. | |
| 229 // | |
| 230 // Not yet implemented, and might never be. | |
| 231 bool SignRsaSha1(const std::string& text, | |
| 232 const std::string& key, | |
| 233 std::string* result) { | |
| 234 NOTIMPLEMENTED(); | |
| 235 return false; | |
| 236 } | |
| 237 | |
| 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(); | |
| 246 | |
| 247 if (parameters->find(kOAuthTimestampLabel) == parameters->end()) | |
| 248 (*parameters)[kOAuthTimestampLabel] = GenerateTimestamp(); | |
| 249 | |
| 250 (*parameters)[kOAuthConsumerKeyLabel] = consumer_key; | |
| 251 (*parameters)[kOAuthSignatureMethodLabel] = | |
| 252 SignatureMethodName(signature_method); | |
| 253 (*parameters)[kOAuthTokenLabel] = token_key; | |
| 254 (*parameters)[kOAuthVersionLabel] = kOAuthVersion; | |
| 255 } | |
| 256 | |
| 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, | |
| 272 base_parameters); | |
| 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); | |
| 279 break; | |
| 280 case OAuthRequestSigner::RSA_SHA1_SIGNATURE: | |
| 281 is_signed = SignRsaSha1(base, key, &signature); | |
| 282 break; | |
| 283 case OAuthRequestSigner::PLAINTEXT_SIGNATURE: | |
| 284 is_signed = SignPlaintext(base, key, &signature); | |
| 285 break; | |
| 286 default: | |
| 287 NOTREACHED(); | |
| 288 } | |
| 289 if (is_signed) | |
| 290 (*parameters)[kOAuthSignatureLabel] = signature; | |
| 291 return is_signed; | |
| 292 } | |
| 293 | |
| 294 | |
| 295 } // namespace | |
| 296 | |
| 297 // static | |
| 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 == '%') { | |
| 306 ++cursor; | |
| 307 if (cursor == limit) | |
| 308 return false; | |
| 309 char* first = strchr(kHexDigits, *cursor); | |
| 310 if (!first) | |
| 311 return false; | |
| 312 int high = first - kHexDigits; | |
| 313 DCHECK(high >= 0 && high < kHexBase); | |
| 314 | |
| 315 ++cursor; | |
| 316 if (cursor == limit) | |
| 317 return false; | |
| 318 char* second = strchr(kHexDigits, *cursor); | |
| 319 if (!second) | |
| 320 return false; | |
| 321 int low = second - kHexDigits; | |
| 322 DCHECK(low >= 0 || low < kHexBase); | |
| 323 | |
| 324 char decoded = static_cast<char>(high * kHexBase + low); | |
| 325 DCHECK(!(IsAsciiAlpha(decoded) || IsAsciiDigit(decoded))); | |
| 326 DCHECK(!(decoded && strchr("-._~", decoded))); | |
| 327 accumulator += decoded; | |
| 328 } else { | |
| 329 accumulator += character; | |
| 330 } | |
| 331 } | |
| 332 *decoded_text = accumulator; | |
| 333 return true; | |
| 334 } | |
| 335 | |
| 336 // static | |
| 337 std::string OAuthRequestSigner::Encode(const std::string& text) { | |
| 338 std::string result = ""; | |
| 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 (IsAsciiAlpha(character) || IsAsciiDigit(character)) { | |
| 344 result += character; | |
| 345 } else { | |
| 346 switch (character) { | |
| 347 case '-': | |
| 348 case '.': | |
| 349 case '_': | |
| 350 case '~': | |
| 351 result += character; | |
| 352 break; | |
| 353 default: | |
| 354 unsigned char byte = static_cast<unsigned char>(character); | |
| 355 result = result + '%' + kHexDigits[byte / kHexBase] + | |
| 356 kHexDigits[byte % kHexBase]; | |
| 357 } | |
| 358 } | |
| 359 } | |
| 360 return result; | |
| 361 } | |
| 362 | |
| 363 // static | |
| 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)) | |
| 378 return false; | |
| 379 } | |
| 380 } | |
| 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); | |
| 389 } | |
| 390 | |
| 391 // static | |
| 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); | |
| 407 if (is_signed) { | |
| 408 std::string signed_text; | |
| 409 switch (http_method) { | |
| 410 case GET_METHOD: | |
| 411 signed_text = request_base_url.spec() + '?'; | |
| 412 // Intentionally falling through | |
| 413 case POST_METHOD: | |
| 414 signed_text += BuildBaseStringParameters(parameters); | |
| 415 break; | |
| 416 default: | |
| 417 NOTREACHED(); | |
| 418 } | |
| 419 *signed_text_return = signed_text; | |
| 420 } | |
| 421 return is_signed; | |
| 422 } | |
| 423 | |
| 424 // static | |
| 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); | |
| 440 if (is_signed) { | |
| 441 std::string signed_text = "OAuth "; | |
| 442 bool first = true; | |
| 443 for (Parameters::const_iterator param = parameters.begin(); | |
| 444 param != parameters.end(); | |
| 445 ++param) { | |
| 446 if (first) | |
| 447 first = false; | |
| 448 else | |
| 449 signed_text += ", "; | |
| 450 signed_text += | |
| 451 StringPrintf("%s=\"%s\"", | |
| 452 OAuthRequestSigner::Encode(param->first).c_str(), | |
| 453 OAuthRequestSigner::Encode(param->second).c_str()); | |
| 454 } | |
| 455 *signed_text_return = signed_text; | |
| 456 } | |
| 457 return is_signed; | |
| 458 } | |
| OLD | NEW |