Skip to content

Commit 272f38e

Browse files
authored
Blake2b added as an experimental algorithm (#367)
1 parent 2941d17 commit 272f38e

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed

src/Bundle/JoseFramework/Resources/config/Algorithms/signature_experimental.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
declare(strict_types=1);
44

5+
use Jose\Component\Signature\Algorithm\Blake2b;
56
use Jose\Component\Signature\Algorithm\ES256K;
67
use Jose\Component\Signature\Algorithm\HS1;
78
use Jose\Component\Signature\Algorithm\HS256_64;
@@ -52,4 +53,9 @@
5253
->tag('jose.algorithm', [
5354
'alias' => 'ES256K',
5455
]);
56+
57+
$container->set(Blake2b::class)
58+
->tag('jose.algorithm', [
59+
'alias' => 'BLAKE2B',
60+
]);
5561
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Jose\Component\Signature\Algorithm;
6+
7+
use function in_array;
8+
use InvalidArgumentException;
9+
use function is_string;
10+
use Jose\Component\Core\JWK;
11+
use ParagonIE\ConstantTime\Base64UrlSafe;
12+
13+
/**
14+
* @see \Jose\Tests\Component\Signature\Algorithm\Blake2bTest
15+
*/
16+
final class Blake2b implements MacAlgorithm
17+
{
18+
private const MINIMUM_KEY_LENGTH = 32;
19+
20+
public function allowedKeyTypes(): array
21+
{
22+
return ['oct'];
23+
}
24+
25+
public function name(): string
26+
{
27+
return 'BLAKE2B';
28+
}
29+
30+
public function verify(JWK $key, string $input, string $signature): bool
31+
{
32+
return hash_equals($this->hash($key, $input), $signature);
33+
}
34+
35+
public function hash(JWK $key, string $input): string
36+
{
37+
$k = $this->getKey($key);
38+
39+
return sodium_crypto_generichash($input, $k);
40+
}
41+
42+
private function getKey(JWK $key): string
43+
{
44+
if (! in_array($key->get('kty'), $this->allowedKeyTypes(), true)) {
45+
throw new InvalidArgumentException('Wrong key type.');
46+
}
47+
if (! $key->has('k')) {
48+
throw new InvalidArgumentException('The key parameter "k" is missing.');
49+
}
50+
$k = $key->get('k');
51+
if (! is_string($k)) {
52+
throw new InvalidArgumentException('The key parameter "k" is invalid.');
53+
}
54+
$key = Base64UrlSafe::decode($k);
55+
if (mb_strlen($key, '8bit') < self::MINIMUM_KEY_LENGTH) {
56+
throw new InvalidArgumentException('Key provided is shorter than 256 bits.');
57+
}
58+
59+
return $key;
60+
}
61+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Jose\Tests\Component\Signature\Algorithm;
6+
7+
use InvalidArgumentException;
8+
use Jose\Component\Core\JWK;
9+
use Jose\Component\Signature\Algorithm\Blake2b;
10+
use ParagonIE\ConstantTime\Base64UrlSafe;
11+
use PHPUnit\Framework\TestCase;
12+
13+
/**
14+
* @internal
15+
*/
16+
final class Blake2bTest extends TestCase
17+
{
18+
private const KEY_ONE = 'GOu4rLyVCBxmxP-sbniU68ojAja5PkRdvv7vNvBCqDQ';
19+
20+
private const KEY_TWO = 'Pu7gywseH-R5HLIWnMll4rEg1ltjUPq_P9WwEzAsAb8';
21+
22+
private const CONTENTS = 'test';
23+
24+
private const EXPECTED_HASH_WITH_KEY_ONE = '_TG5kmkav_YGl3I9uQiv4cm1VN6Q0zPCom4G7-p74JU';
25+
26+
private const SHORT_KEY = 'PIBQuM5PopdMxtmTWmyvNA';
27+
28+
private JWK $keyOne;
29+
30+
private JWK $keyTwo;
31+
32+
private string $expectedHashWithKeyOne;
33+
34+
/**
35+
* @before
36+
*/
37+
public function initializeKey(): void
38+
{
39+
$this->keyOne = new JWK([
40+
'kty' => 'oct',
41+
'k' => self::KEY_ONE,
42+
]);
43+
$this->keyTwo = new JWK([
44+
'kty' => 'oct',
45+
'k' => self::KEY_TWO,
46+
]);
47+
$this->expectedHashWithKeyOne = Base64UrlSafe::decode(self::EXPECTED_HASH_WITH_KEY_ONE);
48+
}
49+
50+
/**
51+
* @test
52+
*/
53+
public function algorithmIdMustBeCorrect(): void
54+
{
55+
$algorithm = new Blake2b();
56+
57+
static::assertSame('BLAKE2B', $algorithm->name());
58+
}
59+
60+
/**
61+
* @test
62+
*/
63+
public function generatedSignatureMustBeSuccessfullyVerified(): void
64+
{
65+
$algorithm = new Blake2b();
66+
67+
static::assertTrue(hash_equals($this->expectedHashWithKeyOne, $algorithm->hash($this->keyOne, self::CONTENTS)));
68+
static::assertTrue($algorithm->verify($this->keyOne, self::CONTENTS, $this->expectedHashWithKeyOne));
69+
}
70+
71+
/**
72+
* @test
73+
*/
74+
public function signShouldRejectShortKeys(): void
75+
{
76+
$algorithm = new Blake2b();
77+
$key = new JWK([
78+
'kty' => 'oct',
79+
'k' => self::SHORT_KEY,
80+
]);
81+
82+
$this->expectException(InvalidArgumentException::class);
83+
$this->expectExceptionMessage('Key provided is shorter than 256 bits.');
84+
85+
$algorithm->hash($key, self::CONTENTS);
86+
}
87+
88+
/**
89+
* @test
90+
*/
91+
public function verifyShouldReturnFalseWhenExpectedHashWasNotCreatedWithSameInformation(): void
92+
{
93+
$algorithm = new Blake2b();
94+
95+
static::assertFalse(
96+
hash_equals($this->expectedHashWithKeyOne, $algorithm->hash($this->keyTwo, self::CONTENTS))
97+
);
98+
static::assertFalse($algorithm->verify($this->keyTwo, self::CONTENTS, $this->expectedHashWithKeyOne));
99+
}
100+
}

0 commit comments

Comments
 (0)