2014-08-26 13:02:40 -04:00
|
|
|
<?php
|
2021-04-19 09:50:30 -04:00
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2014-08-26 13:02:40 -04:00
|
|
|
/**
|
2024-05-10 09:09:14 -04:00
|
|
|
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
|
|
|
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
2014-08-26 13:02:40 -04:00
|
|
|
*/
|
|
|
|
|
|
2016-05-19 03:02:58 -04:00
|
|
|
namespace Test\Security;
|
|
|
|
|
|
2019-11-22 14:52:10 -05:00
|
|
|
use OC\Security\SecureRandom;
|
2014-09-03 07:51:44 -04:00
|
|
|
|
2014-11-10 17:30:38 -05:00
|
|
|
class SecureRandomTest extends \Test\TestCase {
|
2025-05-13 02:49:30 -04:00
|
|
|
public static function stringGenerationProvider(): array {
|
2020-03-26 04:30:18 -04:00
|
|
|
return [
|
|
|
|
|
[1, 1],
|
2025-11-05 12:10:14 -05:00
|
|
|
[16, 16],
|
|
|
|
|
[31, 31],
|
|
|
|
|
[64, 64],
|
2020-03-26 04:30:18 -04:00
|
|
|
[128, 128],
|
|
|
|
|
[1024, 1024],
|
|
|
|
|
];
|
2014-08-26 13:02:40 -04:00
|
|
|
}
|
|
|
|
|
|
2025-05-13 02:49:30 -04:00
|
|
|
public static function charCombinations(): array {
|
2020-03-26 04:30:18 -04:00
|
|
|
return [
|
|
|
|
|
['CHAR_LOWER', '[a-z]'],
|
|
|
|
|
['CHAR_UPPER', '[A-Z]'],
|
|
|
|
|
['CHAR_DIGITS', '[0-9]'],
|
|
|
|
|
];
|
2014-09-03 08:13:12 -04:00
|
|
|
}
|
|
|
|
|
|
2014-09-03 07:51:44 -04:00
|
|
|
/** @var SecureRandom */
|
|
|
|
|
protected $rng;
|
|
|
|
|
|
2019-11-21 10:40:38 -05:00
|
|
|
protected function setUp(): void {
|
2014-11-10 17:30:38 -05:00
|
|
|
parent::setUp();
|
2025-06-12 12:31:58 -04:00
|
|
|
$this->rng = new SecureRandom();
|
2014-09-03 07:51:44 -04:00
|
|
|
}
|
|
|
|
|
|
2025-06-30 10:56:59 -04:00
|
|
|
#[\PHPUnit\Framework\Attributes\DataProvider('stringGenerationProvider')]
|
2024-09-15 16:32:31 -04:00
|
|
|
public function testGetLowStrengthGeneratorLength($length, $expectedLength): void {
|
2016-01-11 13:59:15 -05:00
|
|
|
$generator = $this->rng;
|
2014-08-26 13:02:40 -04:00
|
|
|
|
|
|
|
|
$this->assertEquals($expectedLength, strlen($generator->generate($length)));
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 10:56:59 -04:00
|
|
|
#[\PHPUnit\Framework\Attributes\DataProvider('stringGenerationProvider')]
|
2024-09-15 16:32:31 -04:00
|
|
|
public function testMediumLowStrengthGeneratorLength($length, $expectedLength): void {
|
2016-01-11 14:05:30 -05:00
|
|
|
$generator = $this->rng;
|
2014-08-26 13:02:40 -04:00
|
|
|
|
|
|
|
|
$this->assertEquals($expectedLength, strlen($generator->generate($length)));
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 10:56:59 -04:00
|
|
|
#[\PHPUnit\Framework\Attributes\DataProvider('stringGenerationProvider')]
|
2024-09-15 16:32:31 -04:00
|
|
|
public function testUninitializedGenerate($length, $expectedLength): void {
|
2015-12-11 00:17:47 -05:00
|
|
|
$this->assertEquals($expectedLength, strlen($this->rng->generate($length)));
|
2014-08-26 13:02:40 -04:00
|
|
|
}
|
2014-09-03 08:13:12 -04:00
|
|
|
|
2025-06-30 10:56:59 -04:00
|
|
|
#[\PHPUnit\Framework\Attributes\DataProvider('charCombinations')]
|
2024-09-15 16:32:31 -04:00
|
|
|
public function testScheme($charName, $chars): void {
|
2016-01-11 14:05:30 -05:00
|
|
|
$generator = $this->rng;
|
2014-09-03 08:13:12 -04:00
|
|
|
$scheme = constant('OCP\Security\ISecureRandom::' . $charName);
|
|
|
|
|
$randomString = $generator->generate(100, $scheme);
|
2024-09-19 05:10:31 -04:00
|
|
|
$matchesRegex = preg_match('/^' . $chars . '+$/', $randomString);
|
2014-09-03 08:13:12 -04:00
|
|
|
$this->assertSame(1, $matchesRegex);
|
|
|
|
|
}
|
2022-05-12 07:58:18 -04:00
|
|
|
|
2025-05-13 02:49:30 -04:00
|
|
|
public static function invalidLengths(): array {
|
2022-05-12 07:58:18 -04:00
|
|
|
return [
|
|
|
|
|
[0],
|
|
|
|
|
[-1],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-30 10:56:59 -04:00
|
|
|
#[\PHPUnit\Framework\Attributes\DataProvider('invalidLengths')]
|
2024-09-15 16:32:31 -04:00
|
|
|
public function testInvalidLengths($length): void {
|
2022-05-12 07:58:18 -04:00
|
|
|
$this->expectException(\LengthException::class);
|
|
|
|
|
$generator = $this->rng;
|
|
|
|
|
$generator->generate($length);
|
|
|
|
|
}
|
2025-11-05 12:10:14 -05:00
|
|
|
|
|
|
|
|
public static function invalidCharProviders(): array {
|
|
|
|
|
return [
|
|
|
|
|
'invalid_too_short' => ['abc'],
|
|
|
|
|
'invalid_duplicates' => ['aabcd'],
|
|
|
|
|
'invalid_non_ascii' => ["abcd\xf0"],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider invalidCharProviders
|
|
|
|
|
*/
|
|
|
|
|
public function testInvalidCharacterSets(string $invalidCharset): void {
|
|
|
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
|
|
|
$this->rng->generate(10, $invalidCharset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDefaultCharsetBase64Characters(): void {
|
|
|
|
|
$randomString = $this->rng->generate(100);
|
|
|
|
|
$this->assertMatchesRegularExpression('/^[A-Za-z0-9\+\/]+$/', $randomString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAllOutputsAreUnique(): void {
|
|
|
|
|
// While collisions are technically possible, extremely unlikely for these sizes
|
|
|
|
|
$first = $this->rng->generate(1000);
|
|
|
|
|
$second = $this->rng->generate(1000);
|
|
|
|
|
$this->assertNotEquals($first, $second, "Random output should not be repeated.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testMinimumValidCharset(): void {
|
|
|
|
|
$charset = 'abcd';
|
|
|
|
|
$randomString = $this->rng->generate(500, $charset);
|
|
|
|
|
$this->assertMatchesRegularExpression('/^[abcd]+$/', $randomString);
|
|
|
|
|
$this->assertEquals(500, strlen($randomString));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLargeCustomCharset(): void {
|
|
|
|
|
$charset = '';
|
|
|
|
|
for ($i = 32; $i <= 126; $i++) { // all printable ASCII
|
|
|
|
|
$charset .= chr($i);
|
|
|
|
|
}
|
|
|
|
|
$randomString = $this->rng->generate(200, $charset);
|
|
|
|
|
foreach (str_split($randomString) as $char) {
|
|
|
|
|
$this->assertStringContainsString($char, $charset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testUserProvidedValidCharset(): void {
|
|
|
|
|
$charset = '@#$!';
|
|
|
|
|
$randomString = $this->rng->generate(64, $charset);
|
|
|
|
|
$this->assertMatchesRegularExpression('/^[@#$!]+$/', $randomString);
|
|
|
|
|
}
|
2014-08-26 13:02:40 -04:00
|
|
|
}
|