Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Blake2b signature algorithm #832

Merged
merged 1 commit into from
Aug 17, 2022
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
11 changes: 6 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ $configuration = Configuration::forSymmetricSigner(

Currently supported symmetric algorithms:

| Name | Description | Class | Key length req. |
| --------- | ------------------ | ---------------------------------------- |-----------------|
| `HS256` | HMAC using SHA-256 | `\Lcobucci\JWT\Signer\Hmac\Sha256` | 256 bits |
| `HS384` | HMAC using SHA-384 | `\Lcobucci\JWT\Signer\Hmac\Sha384` | 384 bits |
| `HS512` | HMAC using SHA-512 | `\Lcobucci\JWT\Signer\Hmac\Sha512` | 512 bits |
| Name | Description | Class | Key length req. | Notes |
|-----------|--------------------|-------------------------------------|-----------------|------------------------------------------------------------------------------------------------------------|
| `HS256` | HMAC using SHA-256 | `\Lcobucci\JWT\Signer\Hmac\Sha256` | 256 bits | |
| `HS384` | HMAC using SHA-384 | `\Lcobucci\JWT\Signer\Hmac\Sha384` | 384 bits | |
| `HS512` | HMAC using SHA-512 | `\Lcobucci\JWT\Signer\Hmac\Sha512` | 512 bits | |
| `BLAKE2B` | Blake2b keyed Hash | `\Lcobucci\JWT\Signer\Hmac\Blake2b` | 256 bits | Not a [JWT standard](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) |

Deprecated symmetric algorithms in `v4`:

Expand Down
36 changes: 36 additions & 0 deletions src/Signer/Blake2b.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Signer;

use Lcobucci\JWT\Signer;

use function hash_equals;
use function sodium_crypto_generichash;
use function strlen;

final class Blake2b implements Signer
{
private const MINIMUM_KEY_LENGTH_IN_BITS = 256;

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

public function sign(string $payload, Key $key): string
{
$actualKeyLength = 8 * strlen($key->contents());

if ($actualKeyLength < self::MINIMUM_KEY_LENGTH_IN_BITS) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH_IN_BITS, $actualKeyLength);
}

return sodium_crypto_generichash($payload, $key->contents());
}

public function verify(string $expected, string $payload, Key $key): bool
{
return hash_equals($expected, $this->sign($payload, $key));
}
}
35 changes: 35 additions & 0 deletions test/performance/Blake2bBench.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT;

use Lcobucci\JWT\Signer\Blake2b;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key\InMemory;
use PhpBench\Benchmark\Metadata\Annotations\Groups;

/** @Groups({"Hmac"}) */
final class Blake2bBench extends SignerBench
{
private const ENCODED_KEY = 'b6DNRcX2SFapbICe6lXWYoOZA+JXL/dvkfWiv2hJv3Y=';

protected function signer(): Signer
{
return new Blake2b();
}

protected function signingKey(): Key
{
return $this->createKey();
}

protected function verificationKey(): Key
{
return $this->createKey();
}

private function createKey(): Key
{
return InMemory::base64Encoded(self::ENCODED_KEY);
}
}
98 changes: 98 additions & 0 deletions test/unit/Signer/Blake2bTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Signer;

use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\SodiumBase64Polyfill;
use PHPUnit\Framework\TestCase;

use function hash_equals;

/**
* @coversDefaultClass \Lcobucci\JWT\Signer\Blake2b
*
* @uses \Lcobucci\JWT\Signer\Key\InMemory
* @uses \Lcobucci\JWT\SodiumBase64Polyfill::base642bin()
*/
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 InMemory $keyOne;
private InMemory $keyTwo;
private string $expectedHashWithKeyOne;

/** @before */
public function initializeKey(): void
{
$this->keyOne = InMemory::base64Encoded(self::KEY_ONE);
$this->keyTwo = InMemory::base64Encoded(self::KEY_TWO);
$this->expectedHashWithKeyOne = SodiumBase64Polyfill::base642bin(
self::EXPECTED_HASH_WITH_KEY_ONE,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL
);
}

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

self::assertEquals('BLAKE2B', $signer->algorithmId());
}

/**
* @test
*
* @covers ::sign
* @covers ::verify
*/
public function generatedSignatureMustBeSuccessfullyVerified(): void
{
$signer = new Blake2b();

self::assertTrue(hash_equals($this->expectedHashWithKeyOne, $signer->sign(self::CONTENTS, $this->keyOne)));
self::assertTrue($signer->verify($this->expectedHashWithKeyOne, self::CONTENTS, $this->keyOne));
}

/**
* @test
*
* @covers ::sign
*
* @uses \Lcobucci\JWT\Signer\InvalidKeyProvided
*/
public function signShouldRejectShortKeys(): void
{
$signer = new Blake2b();

$this->expectException(InvalidKeyProvided::class);
$this->expectExceptionMessage('Key provided is shorter than 256 bits, only 128 bits provided');

$signer->sign(self::CONTENTS, InMemory::base64Encoded(self::SHORT_KEY));
}

/**
* @test
*
* @covers ::sign
* @covers ::verify
*/
public function verifyShouldReturnFalseWhenExpectedHashWasNotCreatedWithSameInformation(): void
{
$signer = new Blake2b();

self::assertFalse(hash_equals($this->expectedHashWithKeyOne, $signer->sign(self::CONTENTS, $this->keyTwo)));
self::assertFalse($signer->verify($this->expectedHashWithKeyOne, self::CONTENTS, $this->keyTwo));
}
}