DuoSecurity.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. /**
  3. * MageSpecialist
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to info@magespecialist.it so we can send you a copy immediately.
  14. *
  15. * @category MSP
  16. * @package MSP_NoSpam
  17. * @copyright Copyright (c) 2017 Skeeller srl (http://www.magespecialist.it)
  18. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  19. */
  20. namespace MSP\TwoFactorAuth\Model\Provider\Engine;
  21. use Magento\Framework\App\Config\ScopeConfigInterface;
  22. use Magento\Framework\DataObject;
  23. use Magento\User\Api\Data\UserInterface;
  24. use MSP\TwoFactorAuth\Api\EngineInterface;
  25. class DuoSecurity implements EngineInterface
  26. {
  27. const CODE = 'duo_security'; // Must be the same as defined in di.xml
  28. const DUO_PREFIX = "TX";
  29. const APP_PREFIX = "APP";
  30. const AUTH_PREFIX = "AUTH";
  31. const DUO_EXPIRE = 300;
  32. const APP_EXPIRE = 3600;
  33. const XML_PATH_ENABLED = 'msp_securitysuite_twofactorauth/duo/enabled';
  34. const XML_PATH_INTEGRATION_KEY = 'msp_securitysuite_twofactorauth/duo/integration_key';
  35. const XML_PATH_SECRET_KEY = 'msp_securitysuite_twofactorauth/duo/secret_key';
  36. const XML_PATH_API_HOSTNAME = 'msp_securitysuite_twofactorauth/duo/api_hostname';
  37. const XML_PATH_APPLICATION_KEY = 'msp_securitysuite_twofactorauth/duo/application_key';
  38. /**
  39. * @var ScopeConfigInterface
  40. */
  41. private $scopeConfig;
  42. /**
  43. * DuoSecurity constructor.
  44. * @param ScopeConfigInterface $scopeConfig
  45. */
  46. public function __construct(
  47. ScopeConfigInterface $scopeConfig
  48. ) {
  49. $this->scopeConfig = $scopeConfig;
  50. }
  51. /**
  52. * Get API hostname
  53. * @return string
  54. */
  55. public function getApiHostname()
  56. {
  57. return $this->scopeConfig->getValue(static::XML_PATH_API_HOSTNAME);
  58. }
  59. /**
  60. * Get application key
  61. * @return string
  62. */
  63. private function getApplicationKey()
  64. {
  65. return $this->scopeConfig->getValue(static::XML_PATH_APPLICATION_KEY);
  66. }
  67. /**
  68. * Get secret key
  69. * @return string
  70. */
  71. private function getSecretKey()
  72. {
  73. return $this->scopeConfig->getValue(static::XML_PATH_SECRET_KEY);
  74. }
  75. /**
  76. * Get integration key
  77. * @return string
  78. */
  79. private function getIntegrationKey()
  80. {
  81. return $this->scopeConfig->getValue(static::XML_PATH_INTEGRATION_KEY);
  82. }
  83. /**
  84. * Sign values
  85. * @param string $key
  86. * @param string $values
  87. * @param string $prefix
  88. * @param int $expire
  89. * @param int $time
  90. * @return string
  91. */
  92. private function signValues($key, $values, $prefix, $expire, $time)
  93. {
  94. $exp = $time + $expire;
  95. $cookie = $prefix . '|' . base64_encode($values . '|' . $exp);
  96. $sig = hash_hmac("sha1", $cookie, $key);
  97. return $cookie . '|' . $sig;
  98. }
  99. /**
  100. * Parse signed values and return username
  101. * @param string $key
  102. * @param string $val
  103. * @param string $prefix
  104. * @param int $time
  105. * @return string|false
  106. */
  107. private function parseValues($key, $val, $prefix, $time)
  108. {
  109. $integrationKey = $this->getIntegrationKey();
  110. $timestamp = ($time ? $time : time());
  111. $parts = explode('|', $val);
  112. if (count($parts) !== 3) {
  113. return false;
  114. }
  115. list($uPrefix, $uB64, $uSig) = $parts;
  116. $sig = hash_hmac("sha1", $uPrefix . '|' . $uB64, $key);
  117. if (hash_hmac("sha1", $sig, $key) !== hash_hmac("sha1", $uSig, $key)) {
  118. return false;
  119. }
  120. if ($uPrefix !== $prefix) {
  121. return false;
  122. }
  123. // @codingStandardsIgnoreStart
  124. $cookieParts = explode('|', base64_decode($uB64));
  125. // @codingStandardsIgnoreEnd
  126. if (count($cookieParts) !== 3) {
  127. return false;
  128. }
  129. list($user, $uIkey, $exp) = $cookieParts;
  130. if ($uIkey !== $integrationKey) {
  131. return false;
  132. }
  133. if ($timestamp >= (int) $exp) {
  134. return false;
  135. }
  136. return $user;
  137. }
  138. /**
  139. * Get request signature
  140. * @param UserInterface $user
  141. * @return string
  142. */
  143. public function getRequestSignature(UserInterface $user)
  144. {
  145. $time = time();
  146. $values = $user->getUserName() . '|' . $this->getIntegrationKey();
  147. $duoSignature = $this->signValues(
  148. $this->getSecretKey(),
  149. $values,
  150. static::DUO_PREFIX,
  151. static::DUO_EXPIRE,
  152. $time
  153. );
  154. $appSignature = $this->signValues(
  155. $this->getApplicationKey(),
  156. $values,
  157. static::APP_PREFIX,
  158. static::APP_EXPIRE,
  159. $time
  160. );
  161. return $duoSignature . ':' . $appSignature;
  162. }
  163. /**
  164. * Return true on token validation
  165. * @param UserInterface $user
  166. * @param DataObject $request
  167. * @return bool
  168. */
  169. public function verify(UserInterface $user, DataObject $request)
  170. {
  171. $time = time();
  172. list($authSig, $appSig) = explode(':', $request->getData('sig_response'));
  173. $authUser = $this->parseValues($this->getSecretKey(), $authSig, static::AUTH_PREFIX, $time);
  174. $appUser = $this->parseValues($this->getApplicationKey(), $appSig, static::APP_PREFIX, $time);
  175. return (($authUser === $appUser) && ($appUser === $user->getUserName()));
  176. }
  177. /**
  178. * Return true if this provider has been enabled by admin
  179. * @return boolean
  180. */
  181. public function isEnabled()
  182. {
  183. return
  184. !!$this->scopeConfig->getValue(static::XML_PATH_ENABLED) &&
  185. !!$this->getApiHostname() &&
  186. !!$this->getIntegrationKey() &&
  187. !!$this->getApiHostname() &&
  188. !!$this->getSecretKey();
  189. }
  190. /**
  191. * Return true if this provider allows trusted devices
  192. * @return boolean
  193. */
  194. public function isTrustedDevicesAllowed()
  195. {
  196. return false;
  197. }
  198. }