OtpTest.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. namespace Otp\Tests;
  3. use Otp\Otp;
  4. use PHPUnit\Framework\TestCase;
  5. /**
  6. * Otp test case.
  7. */
  8. class OtpTest extends TestCase
  9. {
  10. /**
  11. *
  12. * @var Otp
  13. */
  14. private $Otp;
  15. private $secret = "12345678901234567890";
  16. /**
  17. * Prepares the environment before running a test.
  18. */
  19. protected function setUp()
  20. {
  21. parent::setUp();
  22. $this->Otp = new Otp();
  23. }
  24. /**
  25. * Cleans up the environment after running a test.
  26. */
  27. protected function tearDown()
  28. {
  29. $this->Otp = null;
  30. parent::tearDown();
  31. }
  32. /**
  33. * Invalid counter values for tests
  34. */
  35. public function hotpTestValues()
  36. {
  37. return [
  38. ['755224', 0], ['287082', 1], ['359152', 2],
  39. ['969429', 3], ['338314', 4], ['254676', 5],
  40. ['287922', 6], ['162583', 7], ['399871', 8],
  41. ['520489', 9]
  42. ];
  43. }
  44. /**
  45. * Invalid counter values for tests
  46. */
  47. public function totpTestValues()
  48. {
  49. // https://www.rfc-editor.org/errata_search.php?rfc=6238
  50. $secretSha1 = '12345678901234567890';
  51. $secretSha256 = '12345678901234567890123456789012';
  52. $secretSha512 = '1234567890123456789012345678901234567890123456789012345678901234';
  53. return [
  54. ['sha1', $secretSha1, '94287082', 59],
  55. ['sha1', $secretSha1, '07081804', 1111111109],
  56. ['sha1', $secretSha1, '14050471', 1111111111],
  57. ['sha1', $secretSha1, '89005924', 1234567890],
  58. ['sha1', $secretSha1, '69279037', 2000000000],
  59. ['sha1', $secretSha1, '65353130', 20000000000],
  60. ['sha256', $secretSha256, '46119246', 59],
  61. ['sha256', $secretSha256, '68084774', 1111111109],
  62. ['sha256', $secretSha256, '67062674', 1111111111],
  63. ['sha256', $secretSha256, '91819424', 1234567890],
  64. ['sha256', $secretSha256, '90698825', 2000000000],
  65. ['sha256', $secretSha256, '77737706', 20000000000],
  66. ['sha512', $secretSha512, '90693936', 59],
  67. ['sha512', $secretSha512, '25091201', 1111111109],
  68. ['sha512', $secretSha512, '99943326', 1111111111],
  69. ['sha512', $secretSha512, '93441116', 1234567890],
  70. ['sha512', $secretSha512, '38618901', 2000000000],
  71. ['sha512', $secretSha512, '47863826', 20000000000],
  72. ];
  73. }
  74. /**
  75. * Invalid counter values for tests
  76. */
  77. public function invalidCounterValues()
  78. {
  79. return [
  80. ['a'], [-1]
  81. ];
  82. }
  83. /**
  84. * Invalid counter values for tests
  85. */
  86. public function hotpResyncDefaultTestValues()
  87. {
  88. return [
  89. ['755224', 0], ['287082', 1], ['359152', 2]
  90. ];
  91. }
  92. /**
  93. * Invalid counter values for tests
  94. */
  95. public function hotpResyncWindowTestValues()
  96. {
  97. return [
  98. ['969429', 0, 3], ['338314', 0, 4],
  99. ['287922', 3, 3], ['162583', 3, 4]
  100. ];
  101. }
  102. /**
  103. * Invalid counter values for tests
  104. */
  105. public function hotpResyncFailureTestValues()
  106. {
  107. return [
  108. ['287922', 7], ['162583', 8], ['399871', 9]
  109. ];
  110. }
  111. /**
  112. * Tests Otp->hotp()
  113. *
  114. * Using test vectors from RFC
  115. * https://tools.ietf.org/html/rfc4226
  116. *
  117. * @dataProvider hotpTestValues
  118. */
  119. public function testHotpRfc($key, $counter)
  120. {
  121. $secret = $this->secret;
  122. $this->assertEquals($key, $this->Otp->hotp($secret, $counter));
  123. }
  124. /**
  125. * Tests TOTP general construction
  126. *
  127. * Still uses the hotp function, but since totp is a bit more special, has
  128. * its own tests
  129. * Using test vectors from RFC
  130. * https://tools.ietf.org/html/rfc6238
  131. *
  132. * @dataProvider totpTestValues
  133. */
  134. public function testTotpRfc($algo, $secret, $key, $time)
  135. {
  136. // Test vectors are in 8 digits
  137. $this->Otp->setDigits(8);
  138. // The time presented in the test vector has to be first divided through 30
  139. // to count as the key
  140. $this->Otp->setAlgorithm($algo);
  141. $this->assertEquals($key, $this->Otp->hotp($secret, floor($time/30)), "$algo with $time");
  142. }
  143. /**
  144. * @dataProvider invalidCounterValues
  145. * @expectedException InvalidArgumentException
  146. * @expectedExceptionMessage Invalid counter supplied
  147. */
  148. public function testHotpInvalidCounter($counter)
  149. {
  150. $this->Otp->hotp($this->secret, $counter);
  151. }
  152. /**
  153. * Tests Otp->checkHotpResync() with default counter window
  154. *
  155. * @dataProvider hotpResyncDefaultTestValues
  156. */
  157. public function testHotpResyncDefault($key, $counter)
  158. {
  159. $secret = $this->secret;
  160. // test with default counter window
  161. $this->assertSame($counter, $this->Otp->checkHotpResync($secret, $counter, $key));
  162. }
  163. /**
  164. * Tests Otp->checkHotpResync() with a provided counter window
  165. *
  166. * @dataProvider hotpResyncWindowTestValues
  167. */
  168. public function testHotpResyncWindow($key, $counter, $counterwindow)
  169. {
  170. $secret = $this->secret;
  171. // test with provided counter window
  172. $this->assertSame(($counter + $counterwindow), $this->Otp->checkHotpResync($secret, $counter, $key, $counterwindow));
  173. }
  174. /**
  175. * Tests Otp->checkHotpResync() with mismatching key and counter
  176. *
  177. * @dataProvider hotpResyncFailureTestValues
  178. */
  179. public function testHotpResyncFailures($key, $counter)
  180. {
  181. $secret = $this->secret;
  182. // test failures
  183. $this->assertFalse($this->Otp->checkHotpResync($secret, $counter, $key));
  184. }
  185. /**
  186. * @dataProvider invalidCounterValues
  187. * @expectedException InvalidArgumentException
  188. * @expectedExceptionMessage Invalid counter supplied
  189. */
  190. public function testHotpResyncInvalidCounter($counter)
  191. {
  192. $this->Otp->checkHotpResync($this->secret, $counter, '755224');
  193. }
  194. /**
  195. * @dataProvider invalidCounterValues
  196. * @expectedException InvalidArgumentException
  197. * @expectedExceptionMessage Invalid counterwindow supplied
  198. */
  199. public function testHotpResyncInvalidCounterWindow($counterwindow)
  200. {
  201. $this->Otp->checkHotpResync($this->secret, 0, '755224', $counterwindow);
  202. }
  203. }