PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/sdk-1.5.5/authentication/signature_v4query.class.php

https://bitbucket.org/ardydedase/web-build-optimizer
PHP | 345 lines | 172 code | 51 blank | 122 comment | 10 complexity | 6436a6335e3afcc9a9f4064c55bfc119 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. <?php
  2. /*
  3. * Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License").
  6. * You may not use this file except in compliance with the License.
  7. * A copy of the License is located at
  8. *
  9. * http://aws.amazon.com/apache2.0
  10. *
  11. * or in the "license" file accompanying this file. This file is distributed
  12. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  13. * express or implied. See the License for the specific language governing
  14. * permissions and limitations under the License.
  15. */
  16. /*%******************************************************************************************%*/
  17. // CLASS
  18. /**
  19. * Implements support for Signature v4 (Query).
  20. *
  21. * @version 2011.01.03
  22. * @license See the included NOTICE.md file for more information.
  23. * @copyright See the included NOTICE.md file for more information.
  24. * @link http://aws.amazon.com/php/ PHP Developer Center
  25. */
  26. class AuthV4Query extends Signer implements Signable
  27. {
  28. /**
  29. * Constructs a new instance of the <AuthV4Query> class.
  30. *
  31. * @param string $endpoint (Required) The endpoint to direct the request to.
  32. * @param string $operation (Required) The operation to execute as a result of this request.
  33. * @param array $payload (Required) The options to use as part of the payload in the request.
  34. * @param CFCredential $credentials (Required) The credentials to use for signing and making requests.
  35. * @return void
  36. */
  37. public function __construct($endpoint, $operation, $payload, CFCredential $credentials)
  38. {
  39. parent::__construct($endpoint, $operation, $payload, $credentials);
  40. }
  41. /**
  42. * Generates a cURL handle with all of the required authentication bits set.
  43. *
  44. * @return resource A cURL handle ready for executing.
  45. */
  46. public function authenticate()
  47. {
  48. // Determine signing values
  49. $current_time = time();
  50. $timestamp = gmdate(CFUtilities::DATE_FORMAT_SIGV4, $current_time);
  51. // Initialize
  52. $x_amz_target = null;
  53. $this->headers = array();
  54. $this->signed_headers = array();
  55. $this->canonical_headers = array();
  56. $this->query = array('body' => is_array($this->payload) ? $this->payload : array());
  57. // Do we have an authentication token?
  58. if ($this->auth_token)
  59. {
  60. $this->headers['X-Amz-Security-Token'] = $this->auth_token;
  61. $this->query['body']['SecurityToken'] = $this->auth_token;
  62. }
  63. // Manage the key-value pairs that are used in the query.
  64. if (stripos($this->operation, 'x-amz-target') !== false)
  65. {
  66. $x_amz_target = trim(str_ireplace('x-amz-target:', '', $this->operation));
  67. }
  68. else
  69. {
  70. $this->query['body']['Action'] = $this->operation;
  71. }
  72. // Only add it if it exists.
  73. if ($this->api_version)
  74. {
  75. $this->query['body']['Version'] = $this->api_version;
  76. }
  77. // Do a case-sensitive, natural order sort on the array keys.
  78. uksort($this->query['body'], 'strcmp');
  79. // Remove the default scheme from the domain.
  80. $domain = str_replace(array('http://', 'https://'), '', $this->endpoint);
  81. // Parse our request.
  82. $parsed_url = parse_url('http://' . $domain);
  83. // Set the proper host header.
  84. if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443)
  85. {
  86. $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port'];
  87. }
  88. else
  89. {
  90. $host_header = strtolower($parsed_url['host']);
  91. }
  92. // Generate the querystring from $this->query
  93. $this->querystring = $this->util->to_query_string($this->query);
  94. // Gather information to pass along to other classes.
  95. $helpers = array(
  96. 'utilities' => $this->utilities_class,
  97. 'request' => $this->request_class,
  98. 'response' => $this->response_class,
  99. );
  100. // Compose the request.
  101. $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain;
  102. $request_url .= !isset($parsed_url['path']) ? '/' : '';
  103. // Instantiate the request class
  104. $request = new $this->request_class($request_url, $this->proxy, $helpers, $this->credentials);
  105. $request->set_method('POST');
  106. $request->set_body($this->canonical_querystring());
  107. $this->querystring = $this->canonical_querystring();
  108. $this->headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  109. $this->headers['X-Amz-Target'] = $x_amz_target;
  110. // Pass along registered stream callbacks
  111. if ($this->registered_streaming_read_callback)
  112. {
  113. $request->register_streaming_read_callback($this->registered_streaming_read_callback);
  114. }
  115. if ($this->registered_streaming_write_callback)
  116. {
  117. $request->register_streaming_write_callback($this->registered_streaming_write_callback);
  118. }
  119. // Add authentication headers
  120. $this->headers['X-Amz-Date'] = $timestamp;
  121. $this->headers['Content-Length'] = strlen($this->querystring);
  122. $this->headers['Content-MD5'] = $this->util->hex_to_base64(md5($this->querystring));
  123. $this->headers['Host'] = $host_header;
  124. // Sort headers
  125. uksort($this->headers, 'strnatcasecmp');
  126. // Add headers to request and compute the string to sign
  127. foreach ($this->headers as $header_key => $header_value)
  128. {
  129. // Strip linebreaks from header values as they're illegal and can allow for security issues
  130. $header_value = str_replace(array("\r", "\n"), '', $header_value);
  131. $request->add_header($header_key, $header_value);
  132. $this->canonical_headers[] = strtolower($header_key) . ':' . $header_value;
  133. $this->signed_headers[] = strtolower($header_key);
  134. }
  135. $this->headers['Authorization'] = $this->authorization($timestamp);
  136. $request->add_header('Authorization', $this->headers['Authorization']);
  137. $request->request_headers = $this->headers;
  138. return $request;
  139. }
  140. /**
  141. * Generates the authorization string to use for the request.
  142. *
  143. * @param string $datetime (Required) The current timestamp.
  144. * @return string The authorization string.
  145. */
  146. protected function authorization($datetime)
  147. {
  148. $access_key_id = $this->key;
  149. $parts = array();
  150. $parts[] = "AWS4-HMAC-SHA256 Credential=${access_key_id}/" . $this->credential_string($datetime);
  151. $parts[] = 'SignedHeaders=' . implode(';', $this->signed_headers);
  152. $parts[] = 'Signature=' . $this->hex16($this->signature($datetime));
  153. return implode(',', $parts);
  154. }
  155. /**
  156. * Calculate the signature.
  157. *
  158. * @param string $datetime (Required) The current timestamp.
  159. * @return string The signature.
  160. */
  161. protected function signature($datetime)
  162. {
  163. $k_date = $this->hmac('AWS4' . $this->secret_key, substr($datetime, 0, 8));
  164. $k_region = $this->hmac($k_date, $this->region());
  165. $k_service = $this->hmac($k_region, $this->service());
  166. $k_credentials = $this->hmac($k_service, 'aws4_request');
  167. $signature = $this->hmac($k_credentials, $this->string_to_sign($datetime));
  168. return $signature;
  169. }
  170. /**
  171. * Calculate the string to sign.
  172. *
  173. * @param string $datetime (Required) The current timestamp.
  174. * @return string The string to sign.
  175. */
  176. protected function string_to_sign($datetime)
  177. {
  178. $parts = array();
  179. $parts[] = 'AWS4-HMAC-SHA256';
  180. $parts[] = $datetime;
  181. $parts[] = $this->credential_string($datetime);
  182. $parts[] = $this->hex16($this->hash($this->canonical_request()));
  183. $this->string_to_sign = implode("\n", $parts);
  184. return $this->string_to_sign;
  185. }
  186. /**
  187. * Generates the credential string to use for signing.
  188. *
  189. * @param string $datetime (Required) The current timestamp.
  190. * @return string The credential string.
  191. */
  192. protected function credential_string($datetime)
  193. {
  194. $parts = array();
  195. $parts[] = substr($datetime, 0, 8);
  196. $parts[] = $this->region();
  197. $parts[] = $this->service();
  198. $parts[] = 'aws4_request';
  199. return implode('/', $parts);
  200. }
  201. /**
  202. * Calculate the canonical request.
  203. *
  204. * @return string The canonical request.
  205. */
  206. protected function canonical_request()
  207. {
  208. $parts = array();
  209. $parts[] = 'POST';
  210. $parts[] = $this->canonical_uri();
  211. $parts[] = ''; // $parts[] = $this->canonical_querystring();
  212. $parts[] = implode("\n", $this->canonical_headers) . "\n";
  213. $parts[] = implode(';', $this->signed_headers);
  214. $parts[] = $this->hex16($this->hash($this->canonical_querystring()));
  215. $this->canonical_request = implode("\n", $parts);
  216. return $this->canonical_request;
  217. }
  218. /**
  219. * The region ID to use in the signature.
  220. *
  221. * @return return The region ID.
  222. */
  223. protected function region()
  224. {
  225. $pieces = explode('.', $this->endpoint);
  226. // Handle cases with single/no region (i.e. service.region.amazonaws.com vs. service.amazonaws.com)
  227. if (count($pieces < 4))
  228. {
  229. return 'us-east-1';
  230. }
  231. return $pieces[1];
  232. }
  233. /**
  234. * The service ID to use in the signature.
  235. *
  236. * @return return The service ID.
  237. */
  238. protected function service()
  239. {
  240. $pieces = explode('.', $this->endpoint);
  241. return $pieces[0];
  242. }
  243. /**
  244. * The request URI path.
  245. *
  246. * @return string The request URI path.
  247. */
  248. protected function canonical_uri()
  249. {
  250. return '/';
  251. }
  252. /**
  253. * The canonical query string.
  254. *
  255. * @return string The canonical query string.
  256. */
  257. protected function canonical_querystring()
  258. {
  259. if (!isset($this->canonical_querystring))
  260. {
  261. $this->canonical_querystring = $this->util->to_signable_string($this->query['body']);
  262. }
  263. return $this->canonical_querystring;
  264. }
  265. /**
  266. * Hex16-pack the data.
  267. *
  268. * @param string $value (Required) The data to hex16 pack.
  269. * @return string The hex16-packed data.
  270. */
  271. protected function hex16($value)
  272. {
  273. $result = unpack('H*', $value);
  274. return reset($result);
  275. }
  276. /**
  277. * Applies HMAC SHA-256 encryption to the string, salted by the key.
  278. *
  279. * @return string Raw HMAC SHA-256 hashed string.
  280. */
  281. protected function hmac($key, $string)
  282. {
  283. return hash_hmac('sha256', $string, $key, true);
  284. }
  285. /**
  286. * SHA-256 hashes the string.
  287. *
  288. * @return string Raw SHA-256 hashed string.
  289. */
  290. protected function hash($string)
  291. {
  292. return hash('sha256', $string, true);
  293. }
  294. }