oauth_request_signer.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. #include "google_apis/gaia/oauth_request_signer.h"
  5. #include <stddef.h>
  6. #include <cctype>
  7. #include <cstdlib>
  8. #include <cstring>
  9. #include <ctime>
  10. #include <map>
  11. #include <string>
  12. #include "base/base64.h"
  13. #include "base/check.h"
  14. #include "base/format_macros.h"
  15. #include "base/notreached.h"
  16. #include "base/rand_util.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/strings/stringprintf.h"
  19. #include "base/time/time.h"
  20. #include "crypto/hmac.h"
  21. #include "url/gurl.h"
  22. namespace {
  23. const int kHexBase = 16;
  24. char kHexDigits[] = "0123456789ABCDEF";
  25. const size_t kHmacDigestLength = 20;
  26. const int kMaxNonceLength = 30;
  27. const int kMinNonceLength = 15;
  28. const char kOAuthConsumerKeyLabel[] = "oauth_consumer_key";
  29. const char kOAuthNonceCharacters[] =
  30. "abcdefghijklmnopqrstuvwyz"
  31. "ABCDEFGHIJKLMNOPQRSTUVWYZ"
  32. "0123456789_";
  33. const char kOAuthNonceLabel[] = "oauth_nonce";
  34. const char kOAuthSignatureLabel[] = "oauth_signature";
  35. const char kOAuthSignatureMethodLabel[] = "oauth_signature_method";
  36. const char kOAuthTimestampLabel[] = "oauth_timestamp";
  37. const char kOAuthTokenLabel[] = "oauth_token";
  38. const char kOAuthVersion[] = "1.0";
  39. const char kOAuthVersionLabel[] = "oauth_version";
  40. enum ParseQueryState {
  41. START_STATE,
  42. KEYWORD_STATE,
  43. VALUE_STATE,
  44. };
  45. const std::string HttpMethodName(OAuthRequestSigner::HttpMethod method) {
  46. switch (method) {
  47. case OAuthRequestSigner::GET_METHOD:
  48. return "GET";
  49. case OAuthRequestSigner::POST_METHOD:
  50. return "POST";
  51. }
  52. NOTREACHED();
  53. return std::string();
  54. }
  55. const std::string SignatureMethodName(
  56. OAuthRequestSigner::SignatureMethod method) {
  57. switch (method) {
  58. case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
  59. return "HMAC-SHA1";
  60. case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
  61. return "RSA-SHA1";
  62. case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
  63. return "PLAINTEXT";
  64. }
  65. NOTREACHED();
  66. return std::string();
  67. }
  68. std::string BuildBaseString(const GURL& request_base_url,
  69. OAuthRequestSigner::HttpMethod http_method,
  70. const std::string& base_parameters) {
  71. return base::StringPrintf("%s&%s&%s",
  72. HttpMethodName(http_method).c_str(),
  73. OAuthRequestSigner::Encode(
  74. request_base_url.spec()).c_str(),
  75. OAuthRequestSigner::Encode(
  76. base_parameters).c_str());
  77. }
  78. std::string BuildBaseStringParameters(
  79. const OAuthRequestSigner::Parameters& parameters) {
  80. std::string result;
  81. OAuthRequestSigner::Parameters::const_iterator cursor;
  82. OAuthRequestSigner::Parameters::const_iterator limit;
  83. bool first = true;
  84. for (cursor = parameters.begin(), limit = parameters.end();
  85. cursor != limit;
  86. ++cursor) {
  87. if (first)
  88. first = false;
  89. else
  90. result += '&';
  91. result += OAuthRequestSigner::Encode(cursor->first);
  92. result += '=';
  93. result += OAuthRequestSigner::Encode(cursor->second);
  94. }
  95. return result;
  96. }
  97. std::string GenerateNonce() {
  98. char result[kMaxNonceLength + 1];
  99. int length = base::RandUint64() % (kMaxNonceLength - kMinNonceLength + 1) +
  100. kMinNonceLength;
  101. result[length] = '\0';
  102. for (int index = 0; index < length; ++index)
  103. result[index] = kOAuthNonceCharacters[
  104. base::RandUint64() % (sizeof(kOAuthNonceCharacters) - 1)];
  105. return result;
  106. }
  107. std::string GenerateTimestamp() {
  108. return base::StringPrintf(
  109. "%" PRId64,
  110. (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()).InSeconds());
  111. }
  112. // Creates a string-to-string, keyword-value map from a parameter/query string
  113. // that uses ampersand (&) to seperate paris and equals (=) to seperate
  114. // keyword from value.
  115. bool ParseQuery(const std::string& query,
  116. OAuthRequestSigner::Parameters* parameters_result) {
  117. std::string::const_iterator cursor;
  118. std::string keyword;
  119. std::string::const_iterator limit;
  120. OAuthRequestSigner::Parameters parameters;
  121. ParseQueryState state;
  122. std::string value;
  123. state = START_STATE;
  124. for (cursor = query.begin(), limit = query.end();
  125. cursor != limit;
  126. ++cursor) {
  127. char character = *cursor;
  128. switch (state) {
  129. case KEYWORD_STATE:
  130. switch (character) {
  131. case '&':
  132. parameters[keyword] = value;
  133. keyword = "";
  134. value = "";
  135. state = START_STATE;
  136. break;
  137. case '=':
  138. state = VALUE_STATE;
  139. break;
  140. default:
  141. keyword += character;
  142. }
  143. break;
  144. case START_STATE:
  145. switch (character) {
  146. case '&': // Intentionally falling through
  147. case '=':
  148. return false;
  149. default:
  150. keyword += character;
  151. state = KEYWORD_STATE;
  152. }
  153. break;
  154. case VALUE_STATE:
  155. switch (character) {
  156. case '=':
  157. return false;
  158. case '&':
  159. parameters[keyword] = value;
  160. keyword = "";
  161. value = "";
  162. state = START_STATE;
  163. break;
  164. default:
  165. value += character;
  166. }
  167. break;
  168. }
  169. }
  170. switch (state) {
  171. case START_STATE:
  172. break;
  173. case KEYWORD_STATE: // Intentionally falling through
  174. case VALUE_STATE:
  175. parameters[keyword] = value;
  176. break;
  177. default:
  178. NOTREACHED();
  179. }
  180. *parameters_result = parameters;
  181. return true;
  182. }
  183. // Creates the value for the oauth_signature parameter when the
  184. // oauth_signature_method is HMAC-SHA1.
  185. bool SignHmacSha1(const std::string& text,
  186. const std::string& key,
  187. std::string* signature_return) {
  188. crypto::HMAC hmac(crypto::HMAC::SHA1);
  189. DCHECK(hmac.DigestLength() == kHmacDigestLength);
  190. unsigned char digest[kHmacDigestLength];
  191. bool result = hmac.Init(key) &&
  192. hmac.Sign(text, digest, kHmacDigestLength);
  193. if (result) {
  194. base::Base64Encode(
  195. std::string(reinterpret_cast<const char*>(digest), kHmacDigestLength),
  196. signature_return);
  197. }
  198. return result;
  199. }
  200. // Creates the value for the oauth_signature parameter when the
  201. // oauth_signature_method is PLAINTEXT.
  202. //
  203. // Not yet implemented, and might never be.
  204. bool SignPlaintext(const std::string& text,
  205. const std::string& key,
  206. std::string* result) {
  207. NOTIMPLEMENTED();
  208. return false;
  209. }
  210. // Creates the value for the oauth_signature parameter when the
  211. // oauth_signature_method is RSA-SHA1.
  212. //
  213. // Not yet implemented, and might never be.
  214. bool SignRsaSha1(const std::string& text,
  215. const std::string& key,
  216. std::string* result) {
  217. NOTIMPLEMENTED();
  218. return false;
  219. }
  220. // Adds parameters that are required by OAuth added as needed to |parameters|.
  221. void PrepareParameters(OAuthRequestSigner::Parameters* parameters,
  222. OAuthRequestSigner::SignatureMethod signature_method,
  223. OAuthRequestSigner::HttpMethod http_method,
  224. const std::string& consumer_key,
  225. const std::string& token_key) {
  226. if (parameters->find(kOAuthNonceLabel) == parameters->end())
  227. (*parameters)[kOAuthNonceLabel] = GenerateNonce();
  228. if (parameters->find(kOAuthTimestampLabel) == parameters->end())
  229. (*parameters)[kOAuthTimestampLabel] = GenerateTimestamp();
  230. (*parameters)[kOAuthConsumerKeyLabel] = consumer_key;
  231. (*parameters)[kOAuthSignatureMethodLabel] =
  232. SignatureMethodName(signature_method);
  233. (*parameters)[kOAuthTokenLabel] = token_key;
  234. (*parameters)[kOAuthVersionLabel] = kOAuthVersion;
  235. }
  236. // Implements shared signing logic, generating the signature and storing it in
  237. // |parameters|. Returns true if the signature has been generated succesfully.
  238. bool SignParameters(const GURL& request_base_url,
  239. OAuthRequestSigner::SignatureMethod signature_method,
  240. OAuthRequestSigner::HttpMethod http_method,
  241. const std::string& consumer_key,
  242. const std::string& consumer_secret,
  243. const std::string& token_key,
  244. const std::string& token_secret,
  245. OAuthRequestSigner::Parameters* parameters) {
  246. DCHECK(request_base_url.is_valid());
  247. PrepareParameters(parameters, signature_method, http_method,
  248. consumer_key, token_key);
  249. std::string base_parameters = BuildBaseStringParameters(*parameters);
  250. std::string base = BuildBaseString(request_base_url, http_method,
  251. base_parameters);
  252. std::string key = consumer_secret + '&' + token_secret;
  253. bool is_signed = false;
  254. std::string signature;
  255. switch (signature_method) {
  256. case OAuthRequestSigner::HMAC_SHA1_SIGNATURE:
  257. is_signed = SignHmacSha1(base, key, &signature);
  258. break;
  259. case OAuthRequestSigner::RSA_SHA1_SIGNATURE:
  260. is_signed = SignRsaSha1(base, key, &signature);
  261. break;
  262. case OAuthRequestSigner::PLAINTEXT_SIGNATURE:
  263. is_signed = SignPlaintext(base, key, &signature);
  264. break;
  265. default:
  266. NOTREACHED();
  267. }
  268. if (is_signed)
  269. (*parameters)[kOAuthSignatureLabel] = signature;
  270. return is_signed;
  271. }
  272. } // namespace
  273. // static
  274. bool OAuthRequestSigner::Decode(const std::string& text,
  275. std::string* decoded_text) {
  276. std::string accumulator;
  277. std::string::const_iterator cursor;
  278. std::string::const_iterator limit;
  279. for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
  280. char character = *cursor;
  281. if (character == '%') {
  282. ++cursor;
  283. if (cursor == limit)
  284. return false;
  285. char* first = strchr(kHexDigits, *cursor);
  286. if (!first)
  287. return false;
  288. int high = first - kHexDigits;
  289. DCHECK(high >= 0 && high < kHexBase);
  290. ++cursor;
  291. if (cursor == limit)
  292. return false;
  293. char* second = strchr(kHexDigits, *cursor);
  294. if (!second)
  295. return false;
  296. int low = second - kHexDigits;
  297. DCHECK(low >= 0 || low < kHexBase);
  298. char decoded = static_cast<char>(high * kHexBase + low);
  299. DCHECK(!(base::IsAsciiAlpha(decoded) || base::IsAsciiDigit(decoded)));
  300. DCHECK(!(decoded && strchr("-._~", decoded)));
  301. accumulator += decoded;
  302. } else {
  303. accumulator += character;
  304. }
  305. }
  306. *decoded_text = accumulator;
  307. return true;
  308. }
  309. // static
  310. std::string OAuthRequestSigner::Encode(const std::string& text) {
  311. std::string result;
  312. std::string::const_iterator cursor;
  313. std::string::const_iterator limit;
  314. for (limit = text.end(), cursor = text.begin(); cursor != limit; ++cursor) {
  315. char character = *cursor;
  316. if (base::IsAsciiAlpha(character) || base::IsAsciiDigit(character)) {
  317. result += character;
  318. } else {
  319. switch (character) {
  320. case '-':
  321. case '.':
  322. case '_':
  323. case '~':
  324. result += character;
  325. break;
  326. default:
  327. unsigned char byte = static_cast<unsigned char>(character);
  328. result = result + '%' + kHexDigits[byte / kHexBase] +
  329. kHexDigits[byte % kHexBase];
  330. }
  331. }
  332. }
  333. return result;
  334. }
  335. // static
  336. bool OAuthRequestSigner::ParseAndSign(const GURL& request_url_with_parameters,
  337. SignatureMethod signature_method,
  338. HttpMethod http_method,
  339. const std::string& consumer_key,
  340. const std::string& consumer_secret,
  341. const std::string& token_key,
  342. const std::string& token_secret,
  343. std::string* result) {
  344. DCHECK(request_url_with_parameters.is_valid());
  345. Parameters parameters;
  346. if (request_url_with_parameters.has_query()) {
  347. const std::string& query = request_url_with_parameters.query();
  348. if (!query.empty()) {
  349. if (!ParseQuery(query, &parameters))
  350. return false;
  351. }
  352. }
  353. std::string spec = request_url_with_parameters.spec();
  354. std::string url_without_parameters = spec;
  355. std::string::size_type question = spec.find("?");
  356. if (question != std::string::npos)
  357. url_without_parameters = spec.substr(0,question);
  358. return SignURL(GURL(url_without_parameters), parameters, signature_method,
  359. http_method, consumer_key, consumer_secret, token_key,
  360. token_secret, result);
  361. }
  362. // static
  363. bool OAuthRequestSigner::SignURL(
  364. const GURL& request_base_url,
  365. const Parameters& request_parameters,
  366. SignatureMethod signature_method,
  367. HttpMethod http_method,
  368. const std::string& consumer_key,
  369. const std::string& consumer_secret,
  370. const std::string& token_key,
  371. const std::string& token_secret,
  372. std::string* signed_text_return) {
  373. DCHECK(request_base_url.is_valid());
  374. Parameters parameters(request_parameters);
  375. bool is_signed = SignParameters(request_base_url, signature_method,
  376. http_method, consumer_key, consumer_secret,
  377. token_key, token_secret, &parameters);
  378. if (is_signed) {
  379. std::string signed_text;
  380. switch (http_method) {
  381. case GET_METHOD:
  382. signed_text = request_base_url.spec() + '?';
  383. [[fallthrough]];
  384. case POST_METHOD:
  385. signed_text += BuildBaseStringParameters(parameters);
  386. break;
  387. default:
  388. NOTREACHED();
  389. }
  390. *signed_text_return = signed_text;
  391. }
  392. return is_signed;
  393. }
  394. // static
  395. bool OAuthRequestSigner::SignAuthHeader(
  396. const GURL& request_base_url,
  397. const Parameters& request_parameters,
  398. SignatureMethod signature_method,
  399. HttpMethod http_method,
  400. const std::string& consumer_key,
  401. const std::string& consumer_secret,
  402. const std::string& token_key,
  403. const std::string& token_secret,
  404. std::string* signed_text_return) {
  405. DCHECK(request_base_url.is_valid());
  406. Parameters parameters(request_parameters);
  407. bool is_signed = SignParameters(request_base_url, signature_method,
  408. http_method, consumer_key, consumer_secret,
  409. token_key, token_secret, &parameters);
  410. if (is_signed) {
  411. std::string signed_text = "OAuth ";
  412. bool first = true;
  413. for (Parameters::const_iterator param = parameters.begin();
  414. param != parameters.end();
  415. ++param) {
  416. if (first)
  417. first = false;
  418. else
  419. signed_text += ", ";
  420. signed_text +=
  421. base::StringPrintf(
  422. "%s=\"%s\"",
  423. OAuthRequestSigner::Encode(param->first).c_str(),
  424. OAuthRequestSigner::Encode(param->second).c_str());
  425. }
  426. *signed_text_return = signed_text;
  427. }
  428. return is_signed;
  429. }