‪TYPO3CMS  ‪main
TotpTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Base32\Base32;
21 use PHPUnit\Framework\Attributes\DataProvider;
22 use PHPUnit\Framework\Attributes\Test;
27 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
28 
29 final class ‪TotpTest extends UnitTestCase
30 {
31  protected string ‪$secret;
32  protected int ‪$timestamp = 1613652061;
33  protected bool ‪$resetSingletonInstances = true;
34 
35  protected function ‪setUp(): void
36  {
37  parent::setUp();
38  // We generate the secret here to ensure TOTP works with a secret encoded by the 3rd party package
39  $this->secret = Base32::encode('TYPO3IsAwesome!'); // KRMVATZTJFZUC53FONXW2ZJB
40  }
41 
42  #[Test]
44  {
45  $this->expectException(\InvalidArgumentException::class);
46  $this->expectExceptionCode(1611748791);
47  GeneralUtility::makeInstance(Totp::class, 'some-secret', 'md5');
48  }
49 
50  #[Test]
52  {
53  $this->expectException(\InvalidArgumentException::class);
54  $this->expectExceptionCode(1611748792);
55  GeneralUtility::makeInstance(Totp::class, 'some-secret', 'sha1', 4);
56  }
57 
58  #[DataProvider('totpDataProvider')]
59  #[Test]
60  public function ‪generateTotpTest(string $expectedTotp, array $arguments): void
61  {
62  $counter = (int)floor(($this->timestamp - 0) / 30); // see Totp::getTimeCounter()
63 
64  self::assertEquals(
65  $expectedTotp,
66  GeneralUtility::makeInstance(Totp::class, $this->secret, ...$arguments)->generateTotp($counter)
67  );
68  }
69 
70  #[DataProvider('totpDataProvider')]
71  #[Test]
72  public function ‪verifyTotpTest(string $totp, array $arguments): void
73  {
74  GeneralUtility::makeInstance(Context::class)
75  ->setAspect('date', new ‪DateTimeAspect(new \DateTimeImmutable('@' . $this->timestamp)));
76 
77  self::assertTrue(
78  GeneralUtility::makeInstance(Totp::class, $this->secret, ...$arguments)->verifyTotp($totp)
79  );
80  }
81 
82  public static function ‪totpDataProvider(): \Generator
83  {
84  yield 'Default' => ['337475', []];
85  yield 'sha256 algo' => ['874487', ['sha256']];
86  yield 'sha512 algo' => ['497852', ['sha512']];
87  yield '7 digit code' => ['8337475', ['sha1', 7]];
88  yield '8 digit code' => ['48337475', ['sha1', 8]];
89  }
90 
91  #[Test]
92  public function ‪verifyTotpWithGracePeriodTest(): void
93  {
94  GeneralUtility::makeInstance(Context::class)
95  ->setAspect('date', new ‪DateTimeAspect(new \DateTimeImmutable('@' . $this->timestamp)));
96 
97  $totpInstance = GeneralUtility::makeInstance(Totp::class, $this->secret);
98 
99  $totpFuture = $totpInstance->generateTotp((int)floor((($this->timestamp + 90) - 0) / 30));
100  self::assertFalse($totpInstance->verifyTotp($totpFuture, 3));
101 
102  $totpFuture = $totpInstance->generateTotp((int)floor((($this->timestamp + 60) - 0) / 30));
103  self::assertTrue($totpInstance->verifyTotp($totpFuture, 3));
104 
105  $totpFuture = $totpInstance->generateTotp((int)floor((($this->timestamp + 30) - 0) / 30));
106  self::assertTrue($totpInstance->verifyTotp($totpFuture, 3));
107 
108  $totpPast = $totpInstance->generateTotp((int)floor((($this->timestamp - 30) - 0) / 30));
109  self::assertTrue($totpInstance->verifyTotp($totpPast, 3));
110 
111  $totpPast = $totpInstance->generateTotp((int)floor((($this->timestamp - 60) - 0) / 30));
112  self::assertTrue($totpInstance->verifyTotp($totpPast, 3));
113 
114  $totpPast = $totpInstance->generateTotp((int)floor((($this->timestamp - 90) - 0) / 30));
115  self::assertFalse($totpInstance->verifyTotp($totpPast, 3));
116  }
117 
118  #[DataProvider('getTotpAuthUrlTestDataProvider')]
119  #[Test]
120  public function ‪getTotpAuthUrlTest(array $constructorArguments, array $methodArguments, string $expected): void
121  {
122  $totp = GeneralUtility::makeInstance(Totp::class, ...$constructorArguments);
123 
124  self::assertEquals($expected, $totp->getTotpAuthUrl(...$methodArguments));
125  }
126 
127  public static function ‪getTotpAuthUrlTestDataProvider(): \Generator
128  {
129  yield 'Default Totp with account and additional params' => [
130  [
131  'N5WGS4ZNOR4XA3ZTFVZWS5DF',
132  ],
133  [
134  'Oli`s awesome site`',
135  'user@typo3.org',
136  [
137  'foo' => 'bar',
138  'bar' => [
139  'baz' => 123,
140  ],
141  ],
142  ],
143  'otpauth://totp/Oli%60s%20awesome%20site%60%3Auser%40typo3.org?secret=N5WGS4ZNOR4XA3ZTFVZWS5DF&issuer=Oli%60s%20awesome%20site%60&foo=bar&bar%5Bbaz%5D=123',
144  ];
145  yield 'Custom Totp settings with account without additional params' => [
146  [
147  'N5WGS4ZNOR4XA3ZTFVZWS5DF',
148  'sha256',
149  8,
150  20,
151  12345,
152  ],
153  [
154  'Some other site',
155  'user@typo3.org',
156  ],
157  'otpauth://totp/Some%20other%20site%3Auser%40typo3.org?secret=N5WGS4ZNOR4XA3ZTFVZWS5DF&issuer=Some%20other%20site&algorithm=sha256&period=20&digits=8&epoch=12345',
158  ];
159  }
160 
161  #[Test]
162  public function ‪generateEncodedSecretTest(): void
163  {
164  // Check 100 times WITHOUT additional auth factors
165  for ($i = 0; $i < 100; $i++) {
166  // Assert correct length and secret only contains allowed alphabet
167  self::assertMatchesRegularExpression('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]{32}$/', ‪Totp::generateEncodedSecret());
168  }
169 
170  // Check 100 times WITH additional auth factors
171  for ($i = 0; $i < 100; $i++) {
172  $authFactors = ['uid' => 5, 'username' => 'non.admin'];
173  // Assert correct length and secret only contains allowed alphabet
174  self::assertMatchesRegularExpression('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]{32}$/', ‪Totp::generateEncodedSecret($authFactors));
175  }
176  }
177 }
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: TotpTest.php:33
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\throwsExceptionOnDisallowedAlogTest
‪throwsExceptionOnDisallowedAlogTest()
Definition: TotpTest.php:43
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\throwsExceptionOnInvalidTotpLengthTest
‪throwsExceptionOnInvalidTotpLengthTest()
Definition: TotpTest.php:51
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\generateTotpTest
‪generateTotpTest(string $expectedTotp, array $arguments)
Definition: TotpTest.php:60
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\totpDataProvider
‪static totpDataProvider()
Definition: TotpTest.php:82
‪TYPO3\CMS\Core\Authentication\Mfa\Provider\Totp
Definition: Totp.php:30
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\verifyTotpWithGracePeriodTest
‪verifyTotpWithGracePeriodTest()
Definition: TotpTest.php:92
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\generateEncodedSecretTest
‪generateEncodedSecretTest()
Definition: TotpTest.php:162
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\$secret
‪string $secret
Definition: TotpTest.php:31
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\getTotpAuthUrlTest
‪getTotpAuthUrlTest(array $constructorArguments, array $methodArguments, string $expected)
Definition: TotpTest.php:120
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\verifyTotpTest
‪verifyTotpTest(string $totp, array $arguments)
Definition: TotpTest.php:72
‪TYPO3\CMS\Core\Authentication\Mfa\Provider\Totp\generateEncodedSecret
‪static generateEncodedSecret(array $additionalAuthFactors=[])
Definition: Totp.php:177
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\getTotpAuthUrlTestDataProvider
‪static getTotpAuthUrlTestDataProvider()
Definition: TotpTest.php:127
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\$timestamp
‪int $timestamp
Definition: TotpTest.php:32
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest
Definition: TotpTest.php:30
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\TotpTest\setUp
‪setUp()
Definition: TotpTest.php:35
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Context\DateTimeAspect
Definition: DateTimeAspect.php:35