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
62 changes: 54 additions & 8 deletions vicephp/Virtue-JWT/src/Encoding/ASN1.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@

class ASN1
{
private const INTEGER = 2;
private const BIT_STRING = 3;
private const OCTET_STRING = 4;
private const OBJECT_IDENTIFIER = 6;
private const NULL = 5;
private const SEQUENCE = 48;

public const INTEGER = 2;
public const BIT_STRING = 3;
public const OCTET_STRING = 4;
public const OBJECT_IDENTIFIER = 6;
public const NULL = 5;
public const SEQUENCE = 48;

/** @var int */
private $type;

/** @var string */
private $bytes;

private function __construct(int $type, string $bytes)
/** @var string */
private $rest;

private function __construct(int $type, string $bytes, string $rest = "")
{
$this->type = $type;
$this->bytes = $bytes;
$this->rest = $rest;
}

public function encode(): string
Expand Down Expand Up @@ -100,4 +103,47 @@ public static function null(): self
{
return new self(self::NULL, "");
}

public static function decode(string $string): self
{
$offset = 0;
assert(strlen($string) > 1);
$type = ord($string[$offset++]);

$length = ord($string[$offset++]);
if ($length & 0x80) {
$temp = $length & ~0x80;
$result = \unpack("N", str_pad(substr($string, $offset, $temp), 4, "\00", STR_PAD_LEFT));
assert($result !== false);
assert(count($result) == 1);
$length = array_shift($result);
assert(is_int($length));
$offset += $temp;
}

$bytes = substr($string, $offset, $length);
$rest = substr($string, $offset + $length);

return new self($type, $bytes, $rest);
}

public function bytes(): string
{
return $this->bytes;
}

public function type(): int
{
return $this->type;
}

public function rest(): string
{
return $this->rest;
}

public function length(): int
{
return strlen($this->bytes);
}
}
23 changes: 22 additions & 1 deletion vicephp/Virtue-JWT/src/JWT/Algorithms/OpenSSLSign.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Virtue\JWT\Algorithms;

use Virtue\Encoding\ASN1;
use Virtue\JWK\AsymmetricKey;
use Virtue\JWK\Key\OpenSSL\Exportable;
use Virtue\JWT\Algorithm;
Expand Down Expand Up @@ -58,14 +59,34 @@ public function sign(string $msg): string

$signature = '';
$success = \openssl_sign($msg, $signature, $private, $this->supported[$this->name]);

Assert::string($signature);
$ecPadding = [
'ES256' => 32,
'ES384' => 48,
'ES512' => 66,
];
if (array_key_exists($this->private->alg(), $ecPadding)) {
$block = ASN1::decode($signature);
assert($block->type() == ASN1::SEQUENCE);

$block = ASN1::decode($block->bytes());
assert($block->type() == ASN1::INTEGER);
$x = str_pad(ltrim($block->bytes(), "\00"), $ecPadding[$this->private->alg()], "\00", STR_PAD_LEFT);

$block = ASN1::decode($block->rest());
assert($block->type() == ASN1::INTEGER);
$y = str_pad(ltrim($block->bytes(), "\00"), $ecPadding[$this->private->alg()], "\00", STR_PAD_LEFT);
$signature = $x . $y;
}

//TODO remove together with the support of PHP versions < 8.0
if (version_compare(PHP_VERSION, '8.0.0') < 0) {
\openssl_pkey_free($private);
}
if (!$success) {
throw new SignFailed('OpenSSL error: ' . \openssl_error_string());
} else {
Assert::string($signature);
return $signature;
}
}
Expand Down
12 changes: 12 additions & 0 deletions vicephp/Virtue-JWT/src/JWT/Algorithms/OpenSSLVerify.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Virtue\JWT\Algorithms;

use Virtue\Encoding\ASN1;
use Virtue\JWK\AsymmetricKey;
use Virtue\JWK\Key\EdDSA;
use Virtue\JWT\Algorithm;
Expand Down Expand Up @@ -63,6 +64,17 @@ public function verify(Token $token): void
if (!$public = \openssl_pkey_get_public($this->public->asPem())) {
throw new VerificationFailed('Key is invalid.', VerificationFailed::ON_SIGNATURE);
}
$ecPadding = [
'ES256' => 32,
'ES384' => 48,
'ES512' => 66,
];
if (array_key_exists($alg, $ecPadding)) {
$x = substr($sig, 0, $ecPadding[$alg]);
$y = substr($sig, $ecPadding[$alg]);
$sig = ASN1::seq(ASN1::uint(ltrim($x, "\00")), ASN1::uint(ltrim($y, "\00")));
$sig = $sig->encode();
}

// returns 1 on success, 0 on failure, -1 on error.
$success = \openssl_verify($msg, $sig, $public, $this->supported[$alg]);
Expand Down
35 changes: 35 additions & 0 deletions vicephp/Virtue-JWT/tests/Encoding/ASN1Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,39 @@ public function testOctetString(): void
$asn1 = ASN1::octstr("Hello World");
$this->assertEquals("BAtIZWxsbyBXb3JsZA==", $asn1->__toString());
}

public function testDecode(): void
{
$longString = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
$asn1 = ASN1::seq(
ASN1::int(\strrev(\pack('s', 1337))),
ASN1::bitstr($longString),
);

$block = ASN1::decode($asn1->encode());
$this->assertEquals(ASN1::SEQUENCE, $block->type());
$this->assertEquals(521, $block->length());
$this->assertEmpty($block->rest());

$block = ASN1::decode($block->bytes());
$this->assertEquals(ASN1::INTEGER, $block->type());
$this->assertEquals(2, $block->length());
$this->assertEquals(\strrev(\pack('s', 1337)), $block->bytes());
$this->assertNotEmpty($block->rest());

$block = ASN1::decode($block->rest());
$this->assertEquals(ASN1::BIT_STRING, $block->type());
$this->assertEquals(strlen($longString) + 1, $block->length());
$this->assertEquals("\00" . $longString, $block->bytes());
$this->assertEmpty($block->rest());

}

}
1 change: 0 additions & 1 deletion vicephp/Virtue-JWT/tests/JWT/Algorithms/OpenSSLTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public function testSignRSA(string $alg): void
$private = new OpenSSL\PrivateKey($alg, $private);

$details = \openssl_pkey_get_details($key);
/* var_dump($details['key']); */
$this->assertNotFalse($details);
Assert::isMap($details['rsa']);
Assert::string($details['rsa']['n']);
Expand Down