Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 1... Línea 1...
1
<?php
1
<?php
Línea 2... Línea 2...
2
 
2
 
Línea 3... Línea -...
3
declare(strict_types=1);
-
 
4
 
-
 
5
/*
-
 
6
 * The MIT License (MIT)
-
 
7
 *
-
 
8
 * Copyright (c) 2014-2018 Spomky-Labs
-
 
9
 *
-
 
10
 * This software may be modified and distributed under the terms
-
 
11
 * of the MIT license.  See the LICENSE file for details.
-
 
12
 */
3
declare(strict_types=1);
Línea -... Línea 4...
-
 
4
 
-
 
5
namespace OTPHP;
13
 
6
 
-
 
7
use InvalidArgumentException;
Línea -... Línea 8...
-
 
8
use Psr\Clock\ClockInterface;
-
 
9
use function assert;
-
 
10
use function is_int;
14
namespace OTPHP;
11
 
15
 
12
/**
16
use Assert\Assertion;
-
 
17
 
-
 
18
final class TOTP extends OTP implements TOTPInterface
-
 
19
{
-
 
20
    /**
-
 
21
     * TOTP constructor.
-
 
22
     *
-
 
23
     * @param string|null $secret
13
 * @see \OTPHP\Test\TOTPTest
24
     * @param int         $period
14
 */
25
     * @param string      $digest
15
final class TOTP extends OTP implements TOTPInterface
26
     * @param int         $digits
16
{
27
     * @param int         $epoch
17
    private readonly ClockInterface $clock;
-
 
18
 
28
     */
19
    public function __construct(string $secret, ?ClockInterface $clock = null)
-
 
20
    {
-
 
21
        parent::__construct($secret);
-
 
22
        if ($clock === null) {
-
 
23
            trigger_deprecation(
-
 
24
                'spomky-labs/otphp',
-
 
25
                '11.3.0',
-
 
26
                'The parameter "$clock" will become mandatory in 12.0.0. Please set a valid PSR Clock implementation instead of "null".'
29
    protected function __construct($secret, int $period, string $digest, int $digits, int $epoch = 0)
27
            );
30
    {
28
            $clock = new InternalClock();
Línea 31... Línea -...
31
        parent::__construct($secret, $digest, $digits);
-
 
32
        $this->setPeriod($period);
29
        }
33
        $this->setEpoch($epoch);
30
 
34
    }
31
        $this->clock = $clock;
35
 
32
    }
36
    /**
33
 
37
     * TOTP constructor.
34
    public static function create(
38
     *
35
        null|string $secret = null,
39
     * @param string|null $secret
36
        int $period = self::DEFAULT_PERIOD,
40
     * @param int         $period
37
        string $digest = self::DEFAULT_DIGEST,
-
 
38
        int $digits = self::DEFAULT_DIGITS,
-
 
39
        int $epoch = self::DEFAULT_EPOCH,
41
     * @param string      $digest
40
        ?ClockInterface $clock = null
-
 
41
    ): self {
-
 
42
        $totp = $secret !== null
42
     * @param int         $digits
43
            ? self::createFromSecret($secret, $clock)
-
 
44
            : self::generate($clock)
43
     * @param int         $epoch
45
        ;
44
     *
46
        $totp->setPeriod($period);
45
     * @return self
47
        $totp->setDigest($digest);
Línea 46... Línea -...
46
     */
-
 
47
    public static function create($secret = null, int $period = 30, string $digest = 'sha1', int $digits = 6, int $epoch = 0): self
-
 
48
    {
-
 
49
        return new self($secret, $period, $digest, $digits, $epoch);
48
        $totp->setDigits($digits);
50
    }
49
        $totp->setEpoch($epoch);
-
 
50
 
51
 
51
        return $totp;
-
 
52
    }
-
 
53
 
-
 
54
    public static function createFromSecret(string $secret, ?ClockInterface $clock = null): self
-
 
55
    {
-
 
56
        $totp = new self($secret, $clock);
52
    /**
57
        $totp->setPeriod(self::DEFAULT_PERIOD);
Línea 53... Línea -...
53
     * @param int $period
-
 
54
     */
-
 
55
    protected function setPeriod(int $period)
-
 
56
    {
58
        $totp->setDigest(self::DEFAULT_DIGEST);
57
        $this->setParameter('period', $period);
59
        $totp->setDigits(self::DEFAULT_DIGITS);
58
    }
60
        $totp->setEpoch(self::DEFAULT_EPOCH);
59
 
61
 
Línea 60... Línea -...
60
    /**
-
 
61
     * {@inheritdoc}
-
 
62
     */
-
 
63
    public function getPeriod(): int
62
        return $totp;
64
    {
63
    }
65
        return $this->getParameter('period');
64
 
-
 
65
    public static function generate(?ClockInterface $clock = null): self
-
 
66
    {
-
 
67
        return self::createFromSecret(self::generateSecret(), $clock);
66
    }
68
    }
Línea 67... Línea -...
67
 
-
 
68
    /**
-
 
69
     * @param int $epoch
-
 
70
     */
69
 
71
    private function setEpoch(int $epoch)
70
    public function getPeriod(): int
72
    {
71
    {
-
 
72
        $value = $this->getParameter('period');
-
 
73
        (is_int($value) && $value > 0) || throw new InvalidArgumentException('Invalid "period" parameter.');
-
 
74
 
73
        $this->setParameter('epoch', $epoch);
75
        return $value;
Línea 74... Línea -...
74
    }
-
 
75
 
-
 
76
    /**
-
 
77
     * {@inheritdoc}
76
    }
78
     */
77
 
-
 
78
    public function getEpoch(): int
-
 
79
    {
79
    public function getEpoch(): int
80
        $value = $this->getParameter('epoch');
80
    {
81
        (is_int($value) && $value >= 0) || throw new InvalidArgumentException('Invalid "epoch" parameter.');
Línea 81... Línea 82...
81
        return $this->getParameter('epoch');
82
 
-
 
83
        return $value;
-
 
84
    }
82
    }
85
 
83
 
86
    public function expiresIn(): int
84
    /**
87
    {
85
     * {@inheritdoc}
88
        $period = $this->getPeriod();
86
     */
89
 
87
    public function at(int $timestamp): string
90
        return $period - ($this->clock->now()->getTimestamp() % $this->getPeriod());
Línea 88... Línea -...
88
    {
-
 
89
        return $this->generateOTP($this->timecode($timestamp));
-
 
90
    }
91
    }
91
 
-
 
92
    /**
-
 
93
     * {@inheritdoc}
92
 
94
     */
93
    /**
95
    public function now(): string
-
 
96
    {
94
     * The OTP at the specified input.
97
        return $this->at(time());
95
     *
98
    }
-
 
Línea 99... Línea 96...
99
 
96
     * @param 0|positive-int $input
100
    /**
97
     */
Línea 101... Línea 98...
101
     * If no timestamp is provided, the OTP is verified at the actual timestamp
98
    public function at(int $input): string
102
     * {@inheritdoc}
-
 
103
     */
99
    {
104
    public function verify(string $otp, $timestamp = null, $window = null): bool
100
        return $this->generateOTP($this->timecode($input));
105
    {
101
    }
106
        $timestamp = $this->getTimestamp($timestamp);
102
 
-
 
103
    public function now(): string
107
 
104
    {
108
        if (null === $window) {
105
        $timestamp = $this->clock->now()
109
            return $this->compareOTP($this->at($timestamp), $otp);
106
            ->getTimestamp();
-
 
107
        assert($timestamp >= 0, 'The timestamp must return a positive integer.');
110
        }
108
 
-
 
109
        return $this->at($timestamp);
Línea 111... Línea 110...
111
 
110
    }
112
        return $this->verifyOtpWithWindow($otp, $timestamp, $window);
-
 
113
    }
-
 
114
 
-
 
115
    /**
111
 
116
     * @param string $otp
-
 
117
     * @param int    $timestamp
-
 
118
     * @param int    $window
-
 
119
     *
-
 
120
     * @return bool
112
    /**
Línea 121... Línea 113...
121
     */
113
     * If no timestamp is provided, the OTP is verified at the actual timestamp. When used, the leeway parameter will
122
    private function verifyOtpWithWindow(string $otp, int $timestamp, int $window): bool
-
 
123
    {
-
 
124
        $window = abs($window);
-
 
125
 
114
     * allow time drift. The passed value is in seconds.
126
        for ($i = 0; $i <= $window; $i++) {
-
 
127
            $next = (int) $i * $this->getPeriod() + $timestamp;
115
     *
128
            $previous = (int) -$i * $this->getPeriod() + $timestamp;
116
     * @param 0|positive-int $timestamp
129
            $valid = $this->compareOTP($this->at($next), $otp) ||
117
     * @param null|0|positive-int $leeway
130
                $this->compareOTP($this->at($previous), $otp);
-
 
131
 
118
     */
132
            if ($valid) {
119
    public function verify(string $otp, null|int $timestamp = null, null|int $leeway = null): bool
-
 
120
    {
Línea -... Línea 121...
-
 
121
        $timestamp ??= $this->clock->now()
133
                return true;
122
            ->getTimestamp();
-
 
123
        $timestamp >= 0 || throw new InvalidArgumentException('Timestamp must be at least 0.');
134
            }
124
 
Línea 135... Línea -...
135
        }
-
 
136
 
-
 
137
        return false;
-
 
138
    }
125
        if ($leeway === null) {
139
 
126
            return $this->compareOTP($this->at($timestamp), $otp);
140
    /**
127
        }
141
     * @param int|null $timestamp
128
 
142
     *
129
        $leeway = abs($leeway);
143
     * @return int
130
        $leeway < $this->getPeriod() || throw new InvalidArgumentException(
Línea 144... Línea 131...
144
     */
131
            'The leeway must be lower than the TOTP period'
145
    private function getTimestamp($timestamp): int
132
        );
146
    {
133
        $timestampMinusLeeway = $timestamp - $leeway;
Línea 147... Línea 134...
147
        $timestamp = $timestamp ?? time();
134
        $timestampMinusLeeway >= 0 || throw new InvalidArgumentException(
148
        Assertion::greaterOrEqualThan($timestamp, 0, 'Timestamp must be at least 0.');
135
            'The timestamp must be greater than or equal to the leeway.'
Línea 149... Línea -...
149
 
-
 
150
        return (int) $timestamp;
-
 
151
    }
-
 
152
 
-
 
153
    /**
-
 
154
     * {@inheritdoc}
136
        );
155
     */
137
 
156
    public function getProvisioningUri(): string
138
        return $this->compareOTP($this->at($timestampMinusLeeway), $otp)
-
 
139
            || $this->compareOTP($this->at($timestamp), $otp)
-
 
140
            || $this->compareOTP($this->at($timestamp + $leeway), $otp);
-
 
141
    }
-
 
142
 
-
 
143
    public function getProvisioningUri(): string
157
    {
144
    {
Línea 158... Línea 145...
158
        $params = [];
145
        $params = [];
159
        if (30 !== $this->getPeriod()) {
146
        if ($this->getPeriod() !== 30) {
160
            $params['period'] = $this->getPeriod();
147
            $params['period'] = $this->getPeriod();
161
        }
148
        }
162
 
149
 
163
        if (0 !== $this->getEpoch()) {
150
        if ($this->getEpoch() !== 0) {
164
            $params['epoch'] = $this->getEpoch();
151
            $params['epoch'] = $this->getEpoch();
165
        }
-
 
166
 
152
        }
167
        return $this->generateURI('totp', $params);
153
 
168
    }
154
        return $this->generateURI('totp', $params);
169
 
155
    }
170
    /**
156
 
171
     * @param int $timestamp
157
    public function setPeriod(int $period): void
172
     *
158
    {
173
     * @return int
-
 
174
     */
159
        $this->setParameter('period', $period);
175
    private function timecode(int $timestamp): int
160
    }
176
    {
-
 
177
        return (int) floor(($timestamp - $this->getEpoch()) / $this->getPeriod());
-
 
Línea -... Línea 161...
-
 
161
 
178
    }
162
    public function setEpoch(int $epoch): void
-
 
163
    {
179
 
164
        $this->setParameter('epoch', $epoch);
Línea 180... Línea 165...
180
    /**
165
    }
181
     * {@inheritdoc}
166
 
182
     */
167
    /**
183
    protected function getParameterMap(): array
168
     * @return array<non-empty-string, callable>
184
    {
169
     */
185
        $v = array_merge(
170
    protected function getParameterMap(): array
Línea 186... Línea 171...
186
            parent::getParameterMap(),
171
    {
187
            [
172
        return [
188
                'period' => function ($value) {
173
            ...parent::getParameterMap(),
Línea 189... Línea 174...
189
                    Assertion::greaterThan((int) $value, 0, 'Period must be at least 1.');
174
            'period' => static function ($value): int {
190
 
175
                (int) $value > 0 || throw new InvalidArgumentException('Period must be at least 1.');
-
 
176
 
-
 
177
                return (int) $value;
-
 
178
            },
-
 
179
            'epoch' => static function ($value): int {
-
 
180
                (int) $value >= 0 || throw new InvalidArgumentException(
-
 
181
                    'Epoch must be greater than or equal to 0.'
-
 
182
                );
-
 
183
 
-
 
184
                return (int) $value;
-
 
185
            },
-
 
186
        ];
-
 
187
    }
-
 
188
 
191
                    return (int) $value;
189
    /**