Skip to content

Commit

Permalink
Merge pull request #832 from Slamdunk/hmac_blake2b
Browse files Browse the repository at this point in the history
Add Blake2b signature algorithm
  • Loading branch information
lcobucci authored Aug 17, 2022
2 parents 9131d04 + 9eb2467 commit df7c37a
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 5 deletions.
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));
}
}

0 comments on commit df7c37a

Please sign in to comment.