Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

declare(strict_types=1);

use Jose\Component\Signature\Algorithm\Blake2b;
use Jose\Component\Signature\Algorithm\ES256K;
use Jose\Component\Signature\Algorithm\HS1;
use Jose\Component\Signature\Algorithm\HS256_64;
Expand Down Expand Up @@ -52,4 +53,9 @@
->tag('jose.algorithm', [
'alias' => 'ES256K',
]);

$container->set(Blake2b::class)
->tag('jose.algorithm', [
'alias' => 'BLAKE2B',
]);
};
61 changes: 61 additions & 0 deletions src/SignatureAlgorithm/Experimental/Blake2b.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Jose\Component\Signature\Algorithm;

use function in_array;
use InvalidArgumentException;
use function is_string;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;

/**
* @see \Jose\Tests\Component\Signature\Algorithm\Blake2bTest
*/
final class Blake2b implements MacAlgorithm
{
private const MINIMUM_KEY_LENGTH = 32;

public function allowedKeyTypes(): array
{
return ['oct'];
}

public function name(): string
{
return 'BLAKE2B';
}

public function verify(JWK $key, string $input, string $signature): bool
{
return hash_equals($this->hash($key, $input), $signature);
}

public function hash(JWK $key, string $input): string
{
$k = $this->getKey($key);

return sodium_crypto_generichash($input, $k);
}

private function getKey(JWK $key): string
{
if (! in_array($key->get('kty'), $this->allowedKeyTypes(), true)) {
throw new InvalidArgumentException('Wrong key type.');
}
if (! $key->has('k')) {
throw new InvalidArgumentException('The key parameter "k" is missing.');
}
$k = $key->get('k');
if (! is_string($k)) {
throw new InvalidArgumentException('The key parameter "k" is invalid.');
}
$key = Base64UrlSafe::decode($k);
if (mb_strlen($key, '8bit') < self::MINIMUM_KEY_LENGTH) {
throw new InvalidArgumentException('Key provided is shorter than 256 bits.');
}

return $key;
}
}
100 changes: 100 additions & 0 deletions tests/SignatureAlgorithm/Experimental/Blake2bTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace Jose\Tests\Component\Signature\Algorithm;

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\Blake2b;
use ParagonIE\ConstantTime\Base64UrlSafe;
use PHPUnit\Framework\TestCase;

/**
* @internal
*/
final class Blake2bTest extends TestCase
{
private const KEY_ONE = 'GOu4rLyVCBxmxP-sbniU68ojAja5PkRdvv7vNvBCqDQ';

private const KEY_TWO = 'Pu7gywseH-R5HLIWnMll4rEg1ltjUPq_P9WwEzAsAb8';

private const CONTENTS = 'test';

private const EXPECTED_HASH_WITH_KEY_ONE = '_TG5kmkav_YGl3I9uQiv4cm1VN6Q0zPCom4G7-p74JU';

private const SHORT_KEY = 'PIBQuM5PopdMxtmTWmyvNA';

private JWK $keyOne;

private JWK $keyTwo;

private string $expectedHashWithKeyOne;

/**
* @before
*/
public function initializeKey(): void
{
$this->keyOne = new JWK([
'kty' => 'oct',
'k' => self::KEY_ONE,
]);
$this->keyTwo = new JWK([
'kty' => 'oct',
'k' => self::KEY_TWO,
]);
$this->expectedHashWithKeyOne = Base64UrlSafe::decode(self::EXPECTED_HASH_WITH_KEY_ONE);
}

/**
* @test
*/
public function algorithmIdMustBeCorrect(): void
{
$algorithm = new Blake2b();

static::assertSame('BLAKE2B', $algorithm->name());
}

/**
* @test
*/
public function generatedSignatureMustBeSuccessfullyVerified(): void
{
$algorithm = new Blake2b();

static::assertTrue(hash_equals($this->expectedHashWithKeyOne, $algorithm->hash($this->keyOne, self::CONTENTS)));
static::assertTrue($algorithm->verify($this->keyOne, self::CONTENTS, $this->expectedHashWithKeyOne));
}

/**
* @test
*/
public function signShouldRejectShortKeys(): void
{
$algorithm = new Blake2b();
$key = new JWK([
'kty' => 'oct',
'k' => self::SHORT_KEY,
]);

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key provided is shorter than 256 bits.');

$algorithm->hash($key, self::CONTENTS);
}

/**
* @test
*/
public function verifyShouldReturnFalseWhenExpectedHashWasNotCreatedWithSameInformation(): void
{
$algorithm = new Blake2b();

static::assertFalse(
hash_equals($this->expectedHashWithKeyOne, $algorithm->hash($this->keyTwo, self::CONTENTS))
);
static::assertFalse($algorithm->verify($this->keyTwo, self::CONTENTS, $this->expectedHashWithKeyOne));
}
}