HasherTest.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. /**
  3. * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. namespace Test\Security;
  9. use OC\Security\Hasher;
  10. use OCP\IConfig;
  11. /**
  12. * Class HasherTest
  13. */
  14. class HasherTest extends \Test\TestCase {
  15. /**
  16. * @return array
  17. */
  18. public function versionHashProvider()
  19. {
  20. return [
  21. ['asf32äà$$a.|3', null],
  22. ['asf32äà$$a.|3|5', null],
  23. ['1|2|3|4', ['version' => 1, 'hash' => '2|3|4']],
  24. ['1|我看|这本书。 我看這本書', ['version' => 1, 'hash' => '我看|这本书。 我看這本書']],
  25. ['2|newhash', ['version' => 2, 'hash' => 'newhash']],
  26. ];
  27. }
  28. /**
  29. * @return array
  30. */
  31. public function hashProviders70_71()
  32. {
  33. return [
  34. // Valid SHA1 strings
  35. ['password', '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', true],
  36. ['owncloud.com', '27a4643e43046c3569e33b68c1a4b15d31306d29', true],
  37. // Invalid SHA1 strings
  38. ['InvalidString', '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', false],
  39. ['AnotherInvalidOne', '27a4643e43046c3569e33b68c1a4b15d31306d29', false],
  40. // Valid legacy password string with password salt "6Wow67q1wZQZpUUeI6G2LsWUu4XKx"
  41. ['password', '$2a$08$emCpDEl.V.QwPWt5gPrqrOhdpH6ailBmkj2Hd2vD5U8qIy20HBe7.', true],
  42. ['password', '$2a$08$yjaLO4ev70SaOsWZ9gRS3eRSEpHVsmSWTdTms1949mylxJ279hzo2', true],
  43. ['password', '$2a$08$.jNRG/oB4r7gHJhAyb.mDupNUAqTnBIW/tWBqFobaYflKXiFeG0A6', true],
  44. ['owncloud.com', '$2a$08$YbEsyASX/hXVNMv8hXQo7ezreN17T8Jl6PjecGZvpX.Ayz2aUyaZ2', true],
  45. ['owncloud.com', '$2a$11$cHdDA2IkUP28oNGBwlL7jO/U3dpr8/0LIjTZmE8dMPA7OCUQsSTqS', true],
  46. ['owncloud.com', '$2a$08$GH.UoIfJ1e.qeZ85KPqzQe6NR8XWRgJXWIUeE1o/j1xndvyTA1x96', true],
  47. // Invalid legacy passwords
  48. ['password', '$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
  49. // Valid passwords "6Wow67q1wZQZpUUeI6G2LsWUu4XKx"
  50. ['password', '1|$2a$05$ezAE0dkwk57jlfo6z5Pql.gcIK3ReXT15W7ITNxVS0ksfhO/4E4Kq', true],
  51. ['password', '1|$2a$05$4OQmloFW4yTVez2MEWGIleDO9Z5G9tWBXxn1vddogmKBQq/Mq93pe', true],
  52. ['password', '1|$2a$11$yj0hlp6qR32G9exGEXktB.yW2rgt2maRBbPgi3EyxcDwKrD14x/WO', true],
  53. ['owncloud.com', '1|$2a$10$Yiss2WVOqGakxuuqySv5UeOKpF8d8KmNjuAPcBMiRJGizJXjA2bKm', true],
  54. ['owncloud.com', '1|$2a$10$v9mh8/.mF/Ut9jZ7pRnpkuac3bdFCnc4W/gSumheQUi02Sr.xMjPi', true],
  55. ['owncloud.com', '1|$2a$05$ST5E.rplNRfDCzRpzq69leRzsTGtY7k88h9Vy2eWj0Ug/iA9w5kGK', true],
  56. // Invalid passwords
  57. ['password', '0|$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
  58. ['password', '1|$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
  59. ['password', '2|$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
  60. ];
  61. }
  62. /**
  63. * @return array
  64. */
  65. public function hashProviders72() {
  66. return [
  67. // Valid ARGON2 hashes
  68. ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$T3JGcEkxVFNOVktNSjZUcg$4/hyLtSejxNgAuzSFFV/HLM3qRQKBwEtKw61qPN4zWA', true],
  69. ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$Zk52V24yNjMzTkhyYjJKOQ$vmqHkCaOD6SiiiFKD1GeKLg/D1ynWpyZbx4XA2yed34', true],
  70. ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$R1pRcUZKamVlNndBc3l5ag$ToRhR8SiZc7fGMpOYfSc5haS5t9+Y00rljPJV7+qLkM', true],
  71. ['nextcloud.com', '2|$argon2i$v=19$m=1024,t=2,p=2$NC9xM0FFaDlzM01QM3kudg$fSfndwtO2mKMZlKdsT8XAtPY51cSS6pLSGS3xMqeJhg', true],
  72. ['nextcloud.com', '2|$argon2i$v=19$m=1024,t=2,p=2$UjkvUjEuL042WWl1cmdHOA$FZivLkBdZnloQsW6qq/jqWK95JSYUHW9rwQC4Ff9GN0', true],
  73. ['nextcloud.com', '2|$argon2i$v=19$m=1024,t=2,p=2$ZnpNdUlzMEpUTW40OVpiMQ$c+yHT9dtSYsjtVGsa7UKOsxxgQAMiUc781d9WsFACqs', true],
  74. //Invalid ARGON2 hashes
  75. ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$UjFDUDg3cjBvM3FkbXVOWQ$7Y5xqFxSERnYn+2+7WChUpWZWMa5BEIhSHWnDgJ71Jk', false],
  76. ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$ZUxSUi5aQklXdkcyMG1uVA$sYjoSvXg/CS/aS6Xnas/o9a/OPVcGKldzzmuiCD1Fxo', false],
  77. ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$ZHQ5V0xMOFNmUC52by44Sg$DzQFk3bJTX0J4PVGwW6rMvtnBJRalBkbtpDIXR+d4A0', false],
  78. ];
  79. }
  80. /** @var Hasher */
  81. protected $hasher;
  82. /** @var IConfig */
  83. protected $config;
  84. protected function setUp() {
  85. parent::setUp();
  86. $this->config = $this->createMock(IConfig::class);
  87. $this->hasher = new Hasher($this->config);
  88. }
  89. public function testHash() {
  90. $hash = $this->hasher->hash('String To Hash');
  91. $this->assertNotNull($hash);
  92. }
  93. /**
  94. * @dataProvider versionHashProvider
  95. */
  96. public function testSplitHash($hash, $expected) {
  97. $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$hash]);
  98. $this->assertSame($expected, $relativePath);
  99. }
  100. /**
  101. * @dataProvider hashProviders70_71
  102. */
  103. public function testVerify($password, $hash, $expected) {
  104. $this->config
  105. ->expects($this->any())
  106. ->method('getSystemValue')
  107. ->with('passwordsalt', null)
  108. ->will($this->returnValue('6Wow67q1wZQZpUUeI6G2LsWUu4XKx'));
  109. $result = $this->hasher->verify($password, $hash);
  110. $this->assertSame($expected, $result);
  111. }
  112. /**
  113. * @dataProvider hashProviders72
  114. */
  115. public function testVerifyArgon2i($password, $hash, $expected) {
  116. if (!\defined('PASSWORD_ARGON2I')) {
  117. $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
  118. }
  119. $result = $this->hasher->verify($password, $hash);
  120. $this->assertSame($expected, $result);
  121. }
  122. public function testUpgradeHashBlowFishToArgon2i() {
  123. if (!\defined('PASSWORD_ARGON2I')) {
  124. $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
  125. }
  126. $message = 'mysecret';
  127. $blowfish = 1 . '|' . password_hash($message, PASSWORD_BCRYPT, []);
  128. $argon2i = 2 . '|' . password_hash($message, PASSWORD_ARGON2I, []);
  129. $this->assertTrue($this->hasher->verify($message, $blowfish,$newHash));
  130. $this->assertTrue($this->hasher->verify($message, $argon2i));
  131. $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$newHash]);
  132. $this->assertFalse(password_needs_rehash($relativePath['hash'], PASSWORD_ARGON2I, []));
  133. }
  134. }