Skip to content
GitLab
Explore
Projects
Groups
Topics
Snippets
Projects
Groups
Topics
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
fusiondirectory
otphp
Commits
0d3f92d6
Commit
0d3f92d6
authored
2 years ago
by
Filippo Tessarotto
Committed by
Florent Morselli
2 years ago
Browse files
Options
Download
Patches
Plain Diff
Prefer explicit setters over optional parameters
parent
c7051644
11.2.x
11.0.x
11.1.x
dependabot/composer/phpunit/phpunit-tw-9.5.26or-tw-10.0.0
11.1.0
No related merge requests found
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
src/HOTP.php
+23
-20
src/HOTP.php
src/HOTPInterface.php
+6
-16
src/HOTPInterface.php
src/OTP.php
+4
-3
src/OTP.php
src/OTPInterface.php
+28
-0
src/OTPInterface.php
src/ParameterTrait.php
+15
-15
src/ParameterTrait.php
src/TOTP.php
+32
-33
src/TOTP.php
src/TOTPInterface.php
+12
-18
src/TOTPInterface.php
tests/HOTPTest.php
+24
-15
tests/HOTPTest.php
tests/TOTPTest.php
+17
-4
tests/TOTPTest.php
with
161 additions
and
124 deletions
+161
-124
src/HOTP.php
+
23
−
20
View file @
0d3f92d6
...
...
@@ -12,33 +12,36 @@ use function is_int;
*/
final
class
HOTP
extends
OTP
implements
HOTPInterface
{
protected
function
__construct
(
null
|
string
$secret
,
int
$counter
,
string
$digest
,
int
$digits
)
{
parent
::
__construct
(
$secret
,
$digest
,
$digits
);
$this
->
setCounter
(
$counter
);
}
public
static
function
create
(
null
|
string
$secret
=
null
,
int
$counter
=
0
,
string
$digest
=
'sha1'
,
int
$digits
=
6
int
$counter
=
self
::
DEFAULT_COUNTER
,
string
$digest
=
self
::
DEFAULT_DIGEST
,
int
$digits
=
self
::
DEFAULT_DIGITS
):
self
{
return
new
self
(
$secret
,
$counter
,
$digest
,
$digits
);
$htop
=
$secret
!==
null
?
self
::
createFromSecret
(
$secret
)
:
self
::
generate
()
;
$htop
->
setCounter
(
$counter
);
$htop
->
setDigest
(
$digest
);
$htop
->
setDigits
(
$digits
);
return
$htop
;
}
public
static
function
createFromSecret
(
string
$secret
,
int
$counter
=
0
,
string
$digest
=
'sha1'
,
int
$digits
=
6
):
self
{
return
new
self
(
$secret
,
$counter
,
$digest
,
$digits
);
public
static
function
createFromSecret
(
string
$secret
):
self
{
$htop
=
new
self
(
$secret
);
$htop
->
setCounter
(
self
::
DEFAULT_COUNTER
);
$htop
->
setDigest
(
self
::
DEFAULT_DIGEST
);
$htop
->
setDigits
(
self
::
DEFAULT_DIGITS
);
return
$htop
;
}
public
static
function
generate
(
int
$counter
=
0
,
string
$digest
=
'sha1'
,
int
$digits
=
6
):
self
public
static
function
generate
():
self
{
return
new
self
(
self
::
generateSecret
()
,
$counter
,
$digest
,
$digits
);
return
self
::
createFromSecret
(
self
::
generateSecret
());
}
public
function
getCounter
():
int
...
...
@@ -72,7 +75,7 @@ final class HOTP extends OTP implements HOTPInterface
return
$this
->
verifyOtpWithWindow
(
$otp
,
$counter
,
$window
);
}
p
rotected
function
setCounter
(
int
$counter
):
void
p
ublic
function
setCounter
(
int
$counter
):
void
{
$this
->
setParameter
(
'counter'
,
$counter
);
}
...
...
This diff is collapsed.
Click to expand it.
src/HOTPInterface.php
+
6
−
16
View file @
0d3f92d6
...
...
@@ -6,6 +6,8 @@ namespace OTPHP;
interface
HOTPInterface
extends
OTPInterface
{
public
const
DEFAULT_COUNTER
=
0
;
/**
* The initial counter (a positive integer).
*/
...
...
@@ -16,6 +18,9 @@ interface HOTPInterface extends OTPInterface
*
* If the secret is null, a random 64 bytes secret will be generated.
*
* @param null|non-empty-string $secret
* @param non-empty-string $digest
*
* @deprecated Deprecated since v11.1, use ::createFromSecret or ::generate instead
*/
public
static
function
create
(
...
...
@@ -25,20 +30,5 @@ interface HOTPInterface extends OTPInterface
int
$digits
=
6
):
self
;
/**
* Create a TOTP object from an existing secret.
*
* @param non-empty-string $secret
*/
public
static
function
createFromSecret
(
string
$secret
,
int
$counter
=
0
,
string
$digest
=
'sha1'
,
int
$digits
=
6
):
self
;
/**
* Create a new HOTP object. A random 64 bytes secret will be generated.
*/
public
static
function
generate
(
int
$counter
=
0
,
string
$digest
=
'sha1'
,
int
$digits
=
6
):
self
;
public
function
setCounter
(
int
$counter
):
void
;
}
This diff is collapsed.
Click to expand it.
src/OTP.php
+
4
−
3
View file @
0d3f92d6
...
...
@@ -17,11 +17,12 @@ abstract class OTP implements OTPInterface
{
use
ParameterTrait
;
protected
function
__construct
(
null
|
string
$secret
,
string
$digest
,
int
$digits
)
/**
* @param non-empty-string $secret
*/
protected
function
__construct
(
string
$secret
)
{
$this
->
setSecret
(
$secret
);
$this
->
setDigest
(
$digest
);
$this
->
setDigits
(
$digits
);
}
public
function
getQrCodeUri
(
string
$uri
,
string
$placeholder
):
string
...
...
This diff is collapsed.
Click to expand it.
src/OTPInterface.php
+
28
−
0
View file @
0d3f92d6
...
...
@@ -6,6 +6,34 @@ namespace OTPHP;
interface
OTPInterface
{
public
const
DEFAULT_DIGITS
=
6
;
public
const
DEFAULT_DIGEST
=
'sha1'
;
/**
* Create a OTP object from an existing secret.
*
* @param non-empty-string $secret
*/
public
static
function
createFromSecret
(
string
$secret
):
self
;
/**
* Create a new OTP object. A random 64 bytes secret will be generated.
*/
public
static
function
generate
():
self
;
/**
* @param non-empty-string $secret
*/
public
function
setSecret
(
string
$secret
):
void
;
public
function
setDigits
(
int
$digits
):
void
;
/**
* @param non-empty-string $digest
*/
public
function
setDigest
(
string
$digest
):
void
;
/**
* @return string Return the OTP at the specified timestamp
*/
...
...
This diff is collapsed.
Click to expand it.
src/ParameterTrait.php
+
15
−
15
View file @
0d3f92d6
...
...
@@ -121,6 +121,21 @@ trait ParameterTrait
}
}
public
function
setSecret
(
string
$secret
):
void
{
$this
->
setParameter
(
'secret'
,
$secret
);
}
public
function
setDigits
(
int
$digits
):
void
{
$this
->
setParameter
(
'digits'
,
$digits
);
}
public
function
setDigest
(
string
$digest
):
void
{
$this
->
setParameter
(
'algorithm'
,
$digest
);
}
/**
* @return array<string, callable>
*/
...
...
@@ -161,21 +176,6 @@ trait ParameterTrait
];
}
private
function
setSecret
(
null
|
string
$secret
):
void
{
$this
->
setParameter
(
'secret'
,
$secret
);
}
private
function
setDigits
(
int
$digits
):
void
{
$this
->
setParameter
(
'digits'
,
$digits
);
}
private
function
setDigest
(
string
$digest
):
void
{
$this
->
setParameter
(
'algorithm'
,
$digest
);
}
private
function
hasColon
(
string
$value
):
bool
{
$colons
=
[
':'
,
'%3A'
,
'%3a'
];
...
...
This diff is collapsed.
Click to expand it.
src/TOTP.php
+
32
−
33
View file @
0d3f92d6
...
...
@@ -12,40 +12,39 @@ use function is_int;
*/
final
class
TOTP
extends
OTP
implements
TOTPInterface
{
protected
function
__construct
(
null
|
string
$secret
,
int
$period
,
string
$digest
,
int
$digits
,
int
$epoch
=
0
)
{
parent
::
__construct
(
$secret
,
$digest
,
$digits
);
$this
->
setPeriod
(
$period
);
$this
->
setEpoch
(
$epoch
);
}
public
static
function
create
(
null
|
string
$secret
=
null
,
int
$period
=
30
,
string
$digest
=
'sha1'
,
int
$digits
=
6
,
int
$epoch
=
0
int
$period
=
self
::
DEFAULT_PERIOD
,
string
$digest
=
self
::
DEFAULT_DIGEST
,
int
$digits
=
self
::
DEFAULT_DIGITS
,
int
$epoch
=
self
::
DEFAULT_EPOCH
):
self
{
return
new
self
(
$secret
,
$period
,
$digest
,
$digits
,
$epoch
);
$totp
=
$secret
!==
null
?
self
::
createFromSecret
(
$secret
)
:
self
::
generate
()
;
$totp
->
setPeriod
(
$period
);
$totp
->
setDigest
(
$digest
);
$totp
->
setDigits
(
$digits
);
$totp
->
setEpoch
(
$epoch
);
return
$totp
;
}
public
static
function
createFromSecret
(
string
$secret
,
int
$period
=
30
,
string
$digest
=
'sha1'
,
int
$digits
=
6
,
int
$epoch
=
0
):
self
{
return
new
self
(
$secret
,
$period
,
$digest
,
$digits
,
$epoch
);
public
static
function
createFromSecret
(
string
$secret
):
self
{
$totp
=
new
self
(
$secret
);
$totp
->
setPeriod
(
self
::
DEFAULT_PERIOD
);
$totp
->
setDigest
(
self
::
DEFAULT_DIGEST
);
$totp
->
setDigits
(
self
::
DEFAULT_DIGITS
);
$totp
->
setEpoch
(
self
::
DEFAULT_EPOCH
);
return
$totp
;
}
public
static
function
generate
(
int
$period
=
30
,
string
$digest
=
'sha1'
,
int
$digits
=
6
,
int
$epoch
=
0
):
self
{
return
new
self
(
self
::
generateSecret
(),
$period
,
$digest
,
$digits
,
$epoch
);
public
static
function
generate
():
self
{
return
self
::
createFromSecret
(
self
::
generateSecret
());
}
public
function
getPeriod
():
int
...
...
@@ -118,11 +117,16 @@ final class TOTP extends OTP implements TOTPInterface
return
$this
->
generateURI
(
'totp'
,
$params
);
}
p
rotected
function
setPeriod
(
int
$period
):
void
p
ublic
function
setPeriod
(
int
$period
):
void
{
$this
->
setParameter
(
'period'
,
$period
);
}
public
function
setEpoch
(
int
$epoch
):
void
{
$this
->
setParameter
(
'epoch'
,
$epoch
);
}
/**
* @return array<string, callable>
*/
...
...
@@ -161,11 +165,6 @@ final class TOTP extends OTP implements TOTPInterface
ksort
(
$options
);
}
private
function
setEpoch
(
int
$epoch
):
void
{
$this
->
setParameter
(
'epoch'
,
$epoch
);
}
private
function
timecode
(
int
$timestamp
):
int
{
return
(
int
)
floor
((
$timestamp
-
$this
->
getEpoch
())
/
$this
->
getPeriod
());
...
...
This diff is collapsed.
Click to expand it.
src/TOTPInterface.php
+
12
−
18
View file @
0d3f92d6
...
...
@@ -6,36 +6,30 @@ namespace OTPHP;
interface
TOTPInterface
extends
OTPInterface
{
public
const
DEFAULT_PERIOD
=
30
;
public
const
DEFAULT_EPOCH
=
0
;
/**
* Create a new TOTP object.
*
* If the secret is null, a random 64 bytes secret will be generated.
*
* @param null|non-empty-string $secret
* @param non-empty-string $digest
*
* @deprecated Deprecated since v11.1, use ::createFromSecret or ::generate instead
*/
public
static
function
create
(
null
|
string
$secret
=
null
,
int
$period
=
30
,
string
$digest
=
'sha1'
,
int
$digits
=
6
int
$period
=
self
::
DEFAULT_PERIOD
,
string
$digest
=
self
::
DEFAULT_DIGEST
,
int
$digits
=
self
::
DEFAULT_DIGITS
):
self
;
/**
* Create a TOTP object from an existing secret.
*
* @param non-empty-string $secret
*/
public
static
function
createFromSecret
(
string
$secret
,
int
$period
=
30
,
string
$digest
=
'sha1'
,
int
$digits
=
6
):
self
;
public
function
setPeriod
(
int
$period
):
void
;
/**
* Create a new TOTP object. A random 64 bytes secret will be generated.
*/
public
static
function
generate
(
int
$period
=
30
,
string
$digest
=
'sha1'
,
int
$digits
=
6
):
self
;
public
function
setEpoch
(
int
$epoch
):
void
;
/**
* Return the TOTP at the current time.
...
...
This diff is collapsed.
Click to expand it.
tests/HOTPTest.php
+
24
−
15
View file @
0d3f92d6
...
...
@@ -30,10 +30,10 @@ final class HOTPTest extends TestCase
*/
public
function
issuerHasColon
():
void
{
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Issuer must not contain a colon.'
);
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
0
,
'sha512'
,
8
);
$otp
->
setLabel
(
'alice'
);
$otp
->
setIssuer
(
'foo%3Abar'
);
}
...
...
@@ -42,10 +42,10 @@ final class HOTPTest extends TestCase
*/
public
function
issuerHasColon2
():
void
{
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Issuer must not contain a colon.'
);
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
0
,
'sha512'
,
8
);
$otp
->
setLabel
(
'alice'
);
$otp
->
setIssuer
(
'foo%3abar'
);
}
...
...
@@ -54,11 +54,11 @@ final class HOTPTest extends TestCase
*/
public
function
labelHasColon
():
void
{
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Label must not contain a colon.'
);
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
0
,
'sha512'
,
8
);
$otp
->
setLabel
(
'foo%3Abar'
);
$otp
->
getProvisioningUri
();
}
/**
...
...
@@ -66,11 +66,11 @@ final class HOTPTest extends TestCase
*/
public
function
labelHasColon2
():
void
{
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Label must not contain a colon.'
);
$otp
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
0
,
'sha512'
,
8
);
$otp
->
setLabel
(
'foo:bar'
);
$otp
->
getProvisioningUri
();
}
/**
...
...
@@ -78,9 +78,11 @@ final class HOTPTest extends TestCase
*/
public
function
digitsIsNot1OrMore
():
void
{
$htop
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Digits must be at least 1.'
);
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
0
,
'sha512'
,
0
);
$htop
->
setDigits
(
0
);
}
/**
...
...
@@ -88,9 +90,11 @@ final class HOTPTest extends TestCase
*/
public
function
counterIsNot1OrMore
():
void
{
$htop
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Counter must be at least 0.'
);
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
-
500
);
$htop
->
setCounter
(
-
500
);
}
/**
...
...
@@ -98,9 +102,11 @@ final class HOTPTest extends TestCase
*/
public
function
digestIsNotSupported
():
void
{
$htop
=
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'The "foo" digest is not supported.'
);
HOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
0
,
'foo'
);
$htop
->
setDigest
(
'foo'
);
}
/**
...
...
@@ -110,11 +116,10 @@ final class HOTPTest extends TestCase
*/
public
function
secretShouldBeBase32Encoded
():
void
{
$otp
=
HOTP
::
createFromSecret
(
random_bytes
(
32
));
$this
->
expectException
(
RuntimeException
::
class
);
$this
->
expectExceptionMessage
(
'Unable to decode the secret. Is it correctly base32 encoded?'
);
$secret
=
random_bytes
(
32
);
$otp
=
HOTP
::
createFromSecret
(
$secret
);
$otp
->
at
(
0
);
}
...
...
@@ -185,8 +190,12 @@ final class HOTPTest extends TestCase
string
$issuer
=
'My Project'
):
HOTP
{
static
::
assertNotSame
(
''
,
$secret
);
static
::
assertNotSame
(
''
,
$digest
);
$otp
=
HOTP
::
createFromSecret
(
$secret
,
$counter
,
$digest
,
$digits
);
$otp
=
HOTP
::
createFromSecret
(
$secret
);
$otp
->
setCounter
(
$counter
);
$otp
->
setDigest
(
$digest
);
$otp
->
setDigits
(
$digits
);
$otp
->
setLabel
(
$label
);
$otp
->
setIssuer
(
$issuer
);
...
...
This diff is collapsed.
Click to expand it.
tests/TOTPTest.php
+
17
−
4
View file @
0d3f92d6
...
...
@@ -33,7 +33,11 @@ final class TOTPTest extends TestCase
*/
public
function
customParameter
():
void
{
$otp
=
TOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
20
,
'sha512'
,
8
,
100
);
$otp
=
TOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$otp
->
setPeriod
(
20
);
$otp
->
setDigest
(
'sha512'
);
$otp
->
setDigits
(
8
);
$otp
->
setEpoch
(
100
);
$otp
->
setLabel
(
'alice@foo.bar'
);
$otp
->
setIssuer
(
'My Project'
);
$otp
->
setParameter
(
'foo'
,
'bar.baz'
);
...
...
@@ -59,9 +63,11 @@ final class TOTPTest extends TestCase
*/
public
function
periodIsNot1OrMore
():
void
{
$totp
=
TOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Period must be at least 1.'
);
TOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
-
20
,
'sha512'
,
8
);
$totp
->
setPeriod
(
-
20
);
}
/**
...
...
@@ -69,9 +75,11 @@ final class TOTPTest extends TestCase
*/
public
function
epochIsNot0OrMore
():
void
{
$totp
=
TOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
);
$this
->
expectException
(
InvalidArgumentException
::
class
);
$this
->
expectExceptionMessage
(
'Epoch must be greater than or equal to 0.'
);
TOTP
::
createFromSecret
(
'JDDK4U6G3BJLEZ7Y'
,
30
,
'sha512'
,
8
,
-
1
);
$totp
->
setEpoch
(
-
1
);
}
/**
...
...
@@ -403,8 +411,13 @@ final class TOTPTest extends TestCase
int
$epoch
=
0
):
TOTP
{
static
::
assertNotSame
(
''
,
$secret
);
static
::
assertNotSame
(
''
,
$digest
);
$otp
=
TOTP
::
createFromSecret
(
$secret
,
$period
,
$digest
,
$digits
,
$epoch
);
$otp
=
TOTP
::
createFromSecret
(
$secret
);
$otp
->
setPeriod
(
$period
);
$otp
->
setDigest
(
$digest
);
$otp
->
setDigits
(
$digits
);
$otp
->
setEpoch
(
$epoch
);
$otp
->
setLabel
(
$label
);
$otp
->
setIssuer
(
$issuer
);
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Topics
Snippets