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
1 change: 0 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ parameters:
- '#Parameter \#1 \$value of static method Jose\\Component\\Core\\Util\\BigInteger::createFromGMPResource\(\) expects GMP, resource given\.#'
- '#Return type \(void\) of method Jose\\Bundle\\JoseFramework\\Routing\\JWKSetLoader::getResolver\(\) should be compatible with return type \(Symfony\\Component\\Config\\Loader\\LoaderResolverInterface\) of method Symfony\\Component\\Config\\Loader\\LoaderInterface::getResolver\(\)#'
- '#Instanceof between Jose\\Component\\Core\\JWK and Jose\\Component\\Core\\JWK will always evaluate to true\.#'
- '#Function openssl_pkey_derive not found\.#'
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@
$container->set(Algorithm\HS256_64::class)
->tag('jose.algorithm', ['alias' => 'HS256/64'])
;

$container->set(Algorithm\ES256K::class)
->tag('jose.algorithm', ['alias' => 'ES256K'])
;
};
47 changes: 46 additions & 1 deletion src/Component/Core/Util/ECKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public static function convertPublicKeyToPEM(JWK $jwk): string
case 'P-256':
$der = self::p256PublicKey();

break;
case 'secp256k1':
$der = self::p256KPublicKey();

break;
case 'P-384':
$der = self::p384PublicKey();
Expand All @@ -64,6 +68,10 @@ public static function convertPrivateKeyToPEM(JWK $jwk): string
case 'P-256':
$der = self::p256PrivateKey($jwk);

break;
case 'secp256k1':
$der = self::p256KPrivateKey($jwk);

break;
case 'P-384':
$der = self::p384PrivateKey($jwk);
Expand Down Expand Up @@ -102,6 +110,7 @@ private static function getNistCurveSize(string $curve): int
{
switch ($curve) {
case 'P-256':
case 'secp256k1':
return 256;
case 'P-384':
return 384;
Expand Down Expand Up @@ -146,6 +155,8 @@ private static function getOpensslCurveName(string $curve): string
switch ($curve) {
case 'P-256':
return 'prime256v1';
case 'secp256k1':
return 'secp256k1';
case 'P-384':
return 'secp384r1';
case 'P-521':
Expand All @@ -170,6 +181,21 @@ private static function p256PublicKey(): string
);
}

private static function p256KPublicKey(): string
{
return pack(
'H*',
'3056' // SEQUENCE, length 86
.'3010' // SEQUENCE, length 16
.'0607' // OID, length 7
.'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
.'0605' // OID, length 8
.'2B8104000A' // 1.3.132.0.10 secp256k1
.'0342' // BIT STRING, length 66
.'00' // prepend with NUL - pubkey will follow
);
}

private static function p384PublicKey(): string
{
return pack(
Expand Down Expand Up @@ -212,7 +238,26 @@ private static function p256PrivateKey(JWK $jwk): string
.$d
.'a00a' // TAGGED OBJECT #0, length 10
.'0608' // OID, length 8
.'2a8648ce3d030107' // 1.3.132.0.34 = P-384 Curve
.'2a8648ce3d030107' // 1.3.132.0.34 = P-256 Curve
.'a144' // TAGGED OBJECT #1, length 68
.'0342' // BIT STRING, length 66
.'00' // prepend with NUL - pubkey will follow
);
}

private static function p256KPrivateKey(JWK $jwk): string
{
$d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT))[1];

return pack(
'H*',
'3074' // SEQUENCE, length 84+length($d)=32
.'020101' // INTEGER, 1
.'0420' // OCTET STRING, length($d) = 32
.$d
.'a007' // TAGGED OBJECT #0, length 7
.'0605' // OID, length 5
.'2b8104000a' // 1.3.132.0.10 secp256k1
.'a144' // TAGGED OBJECT #1, length 68
.'0342' // BIT STRING, length 66
.'00' // prepend with NUL - pubkey will follow
Expand Down
32 changes: 32 additions & 0 deletions src/SignatureAlgorithm/Experimental/ES256K.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2019 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

namespace Jose\Component\Signature\Algorithm;

final class ES256K extends ECDSA
{
public function name(): string
{
return 'ES256K';
}

protected function getHashAlgorithm(): string
{
return 'sha256';
}

protected function getSignaturePartLength(): int
{
return 64;
}
}
69 changes: 69 additions & 0 deletions src/SignatureAlgorithm/Experimental/Tests/P256KSignatureTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2019 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

namespace Jose\Component\Signature\Algorithm\Signature;

use Base64Url\Base64Url;
use Jose\Component\Core\JWK;
use Jose\Component\Signature\Algorithm\ES256K;
use PHPUnit\Framework\TestCase;

/**
* @group unit
* @group NewAlgorithm
*
* @covers \Jose\Component\Signature\Algorithm\ES256K
*
* @internal
*/
class P256KSignatureTest extends TestCase
{
/**
* @test
*/
public function es256KVerify()
{
$key = $this->getKey();
$algorithm = new ES256K();
$data = 'Hello';

static::assertTrue($algorithm->verify($key, $data, hex2bin('9c75b9d171d9690a37f2474d4bfab5c234911cb150950ea5cbfc9aedda5ec360725cc47978de95b4efb2a3ed617c7b36b1cd0a26b536662a79d0f3ae873a7924')));
}

/**
* @test
*/
public function es256KSignAndVerify()
{
$key = $this->getKey();
$algorithm = new ES256K();
$data = 'Hello';

static::assertEquals('ES256K', $algorithm->name());

$signature = $algorithm->sign($key, $data);

static::assertTrue($algorithm->verify($key, $data, $signature));
}

private function getKey(): JWK
{
return new JWK([
'kty' => 'EC',
'crv' => 'secp256k1',
'd' => Base64Url::encode(hex2bin('D1592A94BBB9B5D94CDC425FC7DA80B6A47863AE973A9D581FD9D8F29690B659')),
'x' => Base64Url::encode(hex2bin('4B4DF318DE05BB8F3A115BF337F9BCBC55CA14B917B46BCB557D3C9A158D4BE0')),
'y' => Base64Url::encode(hex2bin('627EB75731A8BBEBC7D9A3C57EC4D7DA2CBA6D2A28E7F45134921861FE1CF5D9')),
]);
}
}