Version 4.6.0
This commit is contained in:
1 parent
f79dcf067a
commit
517a5e1f70
2036 files changed
+110041
-26162
No files matched your search
@@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
@@ -0,0 +1 @@
|
||||
patreon: FlorentMorselli
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
|
||||
You should submit it here: https://github.com/web-token/jwt-framework/pulls
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* This class is a header parameter checker.
|
||||
* When the "alg" header parameter is present, it will check if the value is within the allowed ones.
|
||||
*/
|
||||
final class AlgorithmChecker implements HeaderChecker
|
||||
{
|
||||
private const HEADER_NAME = 'alg';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeader = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $supportedAlgorithms;
|
||||
|
||||
/**
|
||||
* @param string[] $supportedAlgorithms
|
||||
*/
|
||||
public function __construct(array $supportedAlgorithms, bool $protectedHeader = false)
|
||||
{
|
||||
$this->supportedAlgorithms = $supportedAlgorithms;
|
||||
$this->protectedHeader = $protectedHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidHeaderException('"alg" must be a string.', self::HEADER_NAME, $value);
|
||||
}
|
||||
if (!in_array($value, $this->supportedAlgorithms, true)) {
|
||||
throw new InvalidHeaderException('Unsupported algorithm.', self::HEADER_NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::HEADER_NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeader;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* This class is a header parameter and claim checker.
|
||||
* When the "aud" header parameter or claim is present, it will check if the value is within the allowed ones.
|
||||
*/
|
||||
final class AudienceChecker implements ClaimChecker, HeaderChecker
|
||||
{
|
||||
private const CLAIM_NAME = 'aud';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeader = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $audience;
|
||||
|
||||
public function __construct(string $audience, bool $protectedHeader = false)
|
||||
{
|
||||
$this->audience = $audience;
|
||||
$this->protectedHeader = $protectedHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkClaim($value): void
|
||||
{
|
||||
$this->checkValue($value, InvalidClaimException::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
$this->checkValue($value, InvalidHeaderException::class);
|
||||
}
|
||||
|
||||
public function supportedClaim(): string
|
||||
{
|
||||
return self::CLAIM_NAME;
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::CLAIM_NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidClaimException if the claim is invalid
|
||||
* @throws InvalidHeaderException if the header is invalid
|
||||
*/
|
||||
private function checkValue($value, string $class): void
|
||||
{
|
||||
if (is_string($value) && $value !== $this->audience) {
|
||||
throw new $class('Bad audience.', self::CLAIM_NAME, $value);
|
||||
}
|
||||
if (is_array($value) && !in_array($this->audience, $value, true)) {
|
||||
throw new $class('Bad audience.', self::CLAIM_NAME, $value);
|
||||
}
|
||||
if (!is_array($value) && !is_string($value)) {
|
||||
throw new $class('Bad audience.', self::CLAIM_NAME, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
interface ClaimChecker
|
||||
{
|
||||
/**
|
||||
* When the token has the applicable claim, the value is checked.
|
||||
* If for some reason the value is not valid, an InvalidClaimException must be thrown.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidClaimException if the claim is invalid
|
||||
*/
|
||||
public function checkClaim($value): void;
|
||||
|
||||
/**
|
||||
* The method returns the claim to be checked.
|
||||
*/
|
||||
public function supportedClaim(): string;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* This manager handles as many claim checkers as needed.
|
||||
*/
|
||||
class ClaimCheckerManager
|
||||
{
|
||||
/**
|
||||
* @var ClaimChecker[]
|
||||
*/
|
||||
private $checkers = [];
|
||||
|
||||
/**
|
||||
* @param ClaimChecker[] $checkers
|
||||
*/
|
||||
public function __construct(array $checkers)
|
||||
{
|
||||
foreach ($checkers as $checker) {
|
||||
$this->add($checker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns all checkers handled by this manager.
|
||||
*
|
||||
* @return ClaimChecker[]
|
||||
*/
|
||||
public function getCheckers(): array
|
||||
{
|
||||
return $this->checkers;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks all the claims passed as argument.
|
||||
* All claims are checked against the claim checkers.
|
||||
* If one fails, the InvalidClaimException is thrown.
|
||||
*
|
||||
* This method returns an array with all checked claims.
|
||||
* It is up to the implementor to decide use the claims that have not been checked.
|
||||
*
|
||||
* @param string[] $mandatoryClaims
|
||||
*
|
||||
* @throws InvalidClaimException
|
||||
* @throws MissingMandatoryClaimException
|
||||
*/
|
||||
public function check(array $claims, array $mandatoryClaims = []): array
|
||||
{
|
||||
$this->checkMandatoryClaims($mandatoryClaims, $claims);
|
||||
$checkedClaims = [];
|
||||
foreach ($this->checkers as $claim => $checker) {
|
||||
if (array_key_exists($claim, $claims)) {
|
||||
$checker->checkClaim($claims[$claim]);
|
||||
$checkedClaims[$claim] = $claims[$claim];
|
||||
}
|
||||
}
|
||||
|
||||
return $checkedClaims;
|
||||
}
|
||||
|
||||
private function add(ClaimChecker $checker): void
|
||||
{
|
||||
$claim = $checker->supportedClaim();
|
||||
$this->checkers[$claim] = $checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $mandatoryClaims
|
||||
*
|
||||
* @throws MissingMandatoryClaimException
|
||||
*/
|
||||
private function checkMandatoryClaims(array $mandatoryClaims, array $claims): void
|
||||
{
|
||||
if (0 === count($mandatoryClaims)) {
|
||||
return;
|
||||
}
|
||||
$diff = array_keys(array_diff_key(array_flip($mandatoryClaims), $claims));
|
||||
if (0 !== count($diff)) {
|
||||
throw new MissingMandatoryClaimException(sprintf('The following claims are mandatory: %s.', implode(', ', $diff)), $diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class ClaimCheckerManagerFactory
|
||||
{
|
||||
/**
|
||||
* @var ClaimChecker[]
|
||||
*/
|
||||
private $checkers = [];
|
||||
|
||||
/**
|
||||
* This method creates a Claim Checker Manager and populate it with the claim checkers found based on the alias.
|
||||
* If the alias is not supported, an InvalidArgumentException is thrown.
|
||||
*
|
||||
* @param string[] $aliases
|
||||
*/
|
||||
public function create(array $aliases): ClaimCheckerManager
|
||||
{
|
||||
$checkers = [];
|
||||
foreach ($aliases as $alias) {
|
||||
if (!isset($this->checkers[$alias])) {
|
||||
throw new InvalidArgumentException(sprintf('The claim checker with the alias "%s" is not supported.', $alias));
|
||||
}
|
||||
$checkers[] = $this->checkers[$alias];
|
||||
}
|
||||
|
||||
return new ClaimCheckerManager($checkers);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a claim checker to this factory.
|
||||
*/
|
||||
public function add(string $alias, ClaimChecker $checker): void
|
||||
{
|
||||
$this->checkers[$alias] = $checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all claim checker aliases supported by this factory.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function aliases(): array
|
||||
{
|
||||
return array_keys($this->checkers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all claim checkers supported by this factory.
|
||||
*
|
||||
* @return ClaimChecker[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->checkers;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exceptions thrown by this component.
|
||||
*/
|
||||
interface ClaimExceptionInterface extends Throwable
|
||||
{
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
|
||||
/**
|
||||
* This class is a claim checker.
|
||||
* When the "exp" is present, it will compare the value with the current timestamp.
|
||||
*/
|
||||
final class ExpirationTimeChecker implements ClaimChecker, HeaderChecker
|
||||
{
|
||||
private const NAME = 'exp';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $allowedTimeDrift;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeaderOnly;
|
||||
|
||||
public function __construct(int $allowedTimeDrift = 0, bool $protectedHeaderOnly = false)
|
||||
{
|
||||
$this->allowedTimeDrift = $allowedTimeDrift;
|
||||
$this->protectedHeaderOnly = $protectedHeaderOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidClaimException if the claim "exp" is not valid
|
||||
*/
|
||||
public function checkClaim($value): void
|
||||
{
|
||||
if (!is_float($value) && !is_int($value)) {
|
||||
throw new InvalidClaimException('"exp" must be an integer.', self::NAME, $value);
|
||||
}
|
||||
if (time() > $value + $this->allowedTimeDrift) {
|
||||
throw new InvalidClaimException('The token expired.', self::NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedClaim(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidHeaderException if the claim "exp" is not valid
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
if (!is_float($value) && !is_int($value)) {
|
||||
throw new InvalidHeaderException('"exp" must be an integer.', self::NAME, $value);
|
||||
}
|
||||
if (time() > $value + $this->allowedTimeDrift) {
|
||||
throw new InvalidHeaderException('The token expired.', self::NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeaderOnly;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
interface HeaderChecker
|
||||
{
|
||||
/**
|
||||
* This method is called when the header parameter is present.
|
||||
* If for some reason the value is not valid, an InvalidHeaderException must be thrown.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidHeaderException if the header parameter is invalid
|
||||
*/
|
||||
public function checkHeader($value): void;
|
||||
|
||||
/**
|
||||
* The method returns the header parameter to be checked.
|
||||
*/
|
||||
public function supportedHeader(): string;
|
||||
|
||||
/**
|
||||
* When true, the header parameter to be checked MUST be set in the protected header of the token.
|
||||
*/
|
||||
public function protectedHeaderOnly(): bool;
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\Core\JWT;
|
||||
|
||||
class HeaderCheckerManager
|
||||
{
|
||||
/**
|
||||
* @var HeaderChecker[]
|
||||
*/
|
||||
private $checkers = [];
|
||||
|
||||
/**
|
||||
* @var TokenTypeSupport[]
|
||||
*/
|
||||
private $tokenTypes = [];
|
||||
|
||||
/**
|
||||
* HeaderCheckerManager constructor.
|
||||
*
|
||||
* @param HeaderChecker[] $checkers
|
||||
* @param TokenTypeSupport[] $tokenTypes
|
||||
*/
|
||||
public function __construct(array $checkers, array $tokenTypes)
|
||||
{
|
||||
foreach ($checkers as $checker) {
|
||||
$this->add($checker);
|
||||
}
|
||||
foreach ($tokenTypes as $tokenType) {
|
||||
$this->addTokenTypeSupport($tokenType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns all checkers handled by this manager.
|
||||
*
|
||||
* @return HeaderChecker[]
|
||||
*/
|
||||
public function getCheckers(): array
|
||||
{
|
||||
return $this->checkers;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks all the header parameters passed as argument.
|
||||
* All header parameters are checked against the header parameter checkers.
|
||||
* If one fails, the InvalidHeaderException is thrown.
|
||||
*
|
||||
* @param string[] $mandatoryHeaderParameters
|
||||
*
|
||||
* @throws InvalidArgumentException if the token format is not valid
|
||||
*/
|
||||
public function check(JWT $jwt, int $index, array $mandatoryHeaderParameters = []): void
|
||||
{
|
||||
foreach ($this->tokenTypes as $tokenType) {
|
||||
if ($tokenType->supports($jwt)) {
|
||||
$protected = [];
|
||||
$unprotected = [];
|
||||
$tokenType->retrieveTokenHeaders($jwt, $index, $protected, $unprotected);
|
||||
$this->checkDuplicatedHeaderParameters($protected, $unprotected);
|
||||
$this->checkMandatoryHeaderParameters($mandatoryHeaderParameters, $protected, $unprotected);
|
||||
$this->checkHeaders($protected, $unprotected);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Unsupported token type.');
|
||||
}
|
||||
|
||||
private function addTokenTypeSupport(TokenTypeSupport $tokenType): void
|
||||
{
|
||||
$this->tokenTypes[] = $tokenType;
|
||||
}
|
||||
|
||||
private function add(HeaderChecker $checker): void
|
||||
{
|
||||
$header = $checker->supportedHeader();
|
||||
$this->checkers[$header] = $checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the header contains duplicated entries
|
||||
*/
|
||||
private function checkDuplicatedHeaderParameters(array $header1, array $header2): void
|
||||
{
|
||||
$inter = array_intersect_key($header1, $header2);
|
||||
if (0 !== count($inter)) {
|
||||
throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $mandatoryHeaderParameters
|
||||
*
|
||||
* @throws MissingMandatoryHeaderParameterException if a mandatory header parameter is missing
|
||||
*/
|
||||
private function checkMandatoryHeaderParameters(array $mandatoryHeaderParameters, array $protected, array $unprotected): void
|
||||
{
|
||||
if (0 === count($mandatoryHeaderParameters)) {
|
||||
return;
|
||||
}
|
||||
$diff = array_keys(array_diff_key(array_flip($mandatoryHeaderParameters), array_merge($protected, $unprotected)));
|
||||
if (0 !== count($diff)) {
|
||||
throw new MissingMandatoryHeaderParameterException(sprintf('The following header parameters are mandatory: %s.', implode(', ', $diff)), $diff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidHeaderException if a protected header parameter is not in the protected header
|
||||
*/
|
||||
private function checkHeaders(array $protected, array $header): void
|
||||
{
|
||||
$checkedHeaderParameters = [];
|
||||
foreach ($this->checkers as $headerParameter => $checker) {
|
||||
if ($checker->protectedHeaderOnly()) {
|
||||
if (array_key_exists($headerParameter, $protected)) {
|
||||
$checker->checkHeader($protected[$headerParameter]);
|
||||
$checkedHeaderParameters[] = $headerParameter;
|
||||
} elseif (array_key_exists($headerParameter, $header)) {
|
||||
throw new InvalidHeaderException(sprintf('The header parameter "%s" must be protected.', $headerParameter), $headerParameter, $header[$headerParameter]);
|
||||
}
|
||||
} else {
|
||||
if (array_key_exists($headerParameter, $protected)) {
|
||||
$checker->checkHeader($protected[$headerParameter]);
|
||||
$checkedHeaderParameters[] = $headerParameter;
|
||||
} elseif (array_key_exists($headerParameter, $header)) {
|
||||
$checker->checkHeader($header[$headerParameter]);
|
||||
$checkedHeaderParameters[] = $headerParameter;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->checkCriticalHeader($protected, $header, $checkedHeaderParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidHeaderException if the "crit" parameter is not valid or if a critical header parameter cannot be verified
|
||||
*/
|
||||
private function checkCriticalHeader(array $protected, array $header, array $checkedHeaderParameters): void
|
||||
{
|
||||
if (array_key_exists('crit', $protected)) {
|
||||
if (!is_array($protected['crit'])) {
|
||||
throw new InvalidHeaderException('The header "crit" must be a list of header parameters.', 'crit', $protected['crit']);
|
||||
}
|
||||
$diff = array_diff($protected['crit'], $checkedHeaderParameters);
|
||||
if (0 !== count($diff)) {
|
||||
throw new InvalidHeaderException(sprintf('One or more header parameters are marked as critical, but they are missing or have not been checked: %s.', implode(', ', array_values($diff))), 'crit', $protected['crit']);
|
||||
}
|
||||
} elseif (array_key_exists('crit', $header)) {
|
||||
throw new InvalidHeaderException('The header parameter "crit" must be protected.', 'crit', $header['crit']);
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class HeaderCheckerManagerFactory
|
||||
{
|
||||
/**
|
||||
* @var HeaderChecker[]
|
||||
*/
|
||||
private $checkers = [];
|
||||
|
||||
/**
|
||||
* @var TokenTypeSupport[]
|
||||
*/
|
||||
private $tokenTypes = [];
|
||||
|
||||
/**
|
||||
* This method creates a Header Checker Manager and populate it with the header parameter checkers found based on the alias.
|
||||
* If the alias is not supported, an InvalidArgumentException is thrown.
|
||||
*
|
||||
* @param string[] $aliases
|
||||
*
|
||||
* @throws InvalidArgumentException if an alias is not supported
|
||||
*/
|
||||
public function create(array $aliases): HeaderCheckerManager
|
||||
{
|
||||
$checkers = [];
|
||||
foreach ($aliases as $alias) {
|
||||
if (!isset($this->checkers[$alias])) {
|
||||
throw new InvalidArgumentException(sprintf('The header checker with the alias "%s" is not supported.', $alias));
|
||||
}
|
||||
$checkers[] = $this->checkers[$alias];
|
||||
}
|
||||
|
||||
return new HeaderCheckerManager($checkers, $this->tokenTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a header parameter checker to this factory.
|
||||
* The checker is uniquely identified by an alias. This allows the same header parameter checker to be added twice (or more)
|
||||
* using several configuration options.
|
||||
*/
|
||||
public function add(string $alias, HeaderChecker $checker): void
|
||||
{
|
||||
$this->checkers[$alias] = $checker;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds a token type support to this factory.
|
||||
*/
|
||||
public function addTokenTypeSupport(TokenTypeSupport $tokenType): void
|
||||
{
|
||||
$this->tokenTypes[] = $tokenType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all header parameter checker aliases supported by this factory.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function aliases(): array
|
||||
{
|
||||
return array_keys($this->checkers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all header parameter checkers supported by this factory.
|
||||
*
|
||||
* @return HeaderChecker[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->checkers;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* This exception is thrown by claim checkers when a claim check failed.
|
||||
*/
|
||||
class InvalidClaimException extends Exception implements ClaimExceptionInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $claim;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct(string $message, string $claim, $value)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->claim = $claim;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the claim that caused the exception.
|
||||
*/
|
||||
public function getClaim(): string
|
||||
{
|
||||
return $this->claim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the claim value that caused the exception.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* This exception is thrown by header parameter checkers when a header parameter check failed.
|
||||
*/
|
||||
class InvalidHeaderException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $header;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct(string $message, string $header, $value)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->header = $header;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header parameter that caused the exception.
|
||||
*/
|
||||
public function getHeader(): string
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header parameter value that caused the exception.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
|
||||
/**
|
||||
* This class is a claim checker.
|
||||
* When the "iat" is present, it will compare the value with the current timestamp.
|
||||
*/
|
||||
final class IssuedAtChecker implements ClaimChecker, HeaderChecker
|
||||
{
|
||||
private const NAME = 'iat';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $allowedTimeDrift;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeaderOnly;
|
||||
|
||||
public function __construct(int $allowedTimeDrift = 0, bool $protectedHeaderOnly = false)
|
||||
{
|
||||
$this->allowedTimeDrift = $allowedTimeDrift;
|
||||
$this->protectedHeaderOnly = $protectedHeaderOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidClaimException if the claim is invalid
|
||||
*/
|
||||
public function checkClaim($value): void
|
||||
{
|
||||
if (!is_float($value) && !is_int($value)) {
|
||||
throw new InvalidClaimException('"iat" must be an integer.', self::NAME, $value);
|
||||
}
|
||||
if (time() < $value - $this->allowedTimeDrift) {
|
||||
throw new InvalidClaimException('The JWT is issued in the future.', self::NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedClaim(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidHeaderException if the header parameter is invalid
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
if (!is_float($value) && !is_int($value)) {
|
||||
throw new InvalidHeaderException('The header "iat" must be an integer.', self::NAME, $value);
|
||||
}
|
||||
if (time() < $value - $this->allowedTimeDrift) {
|
||||
throw new InvalidHeaderException('The JWT is issued in the future.', self::NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeaderOnly;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* This class is a header parameter and claim checker.
|
||||
* When the "iss" header parameter or claim is present, it will check if the value is within the allowed ones.
|
||||
*/
|
||||
final class IssuerChecker implements ClaimChecker, HeaderChecker
|
||||
{
|
||||
private const CLAIM_NAME = 'iss';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeader = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $issuers;
|
||||
|
||||
public function __construct(array $issuer, bool $protectedHeader = false)
|
||||
{
|
||||
$this->issuers = $issuer;
|
||||
$this->protectedHeader = $protectedHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidClaimException if the claim is invalid
|
||||
*/
|
||||
public function checkClaim($value): void
|
||||
{
|
||||
$this->checkValue($value, InvalidClaimException::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidHeaderException if the header parameter is invalid
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
$this->checkValue($value, InvalidHeaderException::class);
|
||||
}
|
||||
|
||||
public function supportedClaim(): string
|
||||
{
|
||||
return self::CLAIM_NAME;
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::CLAIM_NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidClaimException if the claim is invalid
|
||||
* @throws InvalidHeaderException if the header parameter is invalid
|
||||
*/
|
||||
private function checkValue($value, string $class): void
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new $class('Invalid value.', self::CLAIM_NAME, $value);
|
||||
}
|
||||
if (!in_array($value, $this->issuers, true)) {
|
||||
throw new $class('Unknown issuer.', self::CLAIM_NAME, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 Spomky-Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use Exception;
|
||||
|
||||
class MissingMandatoryClaimException extends Exception implements ClaimExceptionInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $claims;
|
||||
|
||||
/**
|
||||
* MissingMandatoryClaimException constructor.
|
||||
*
|
||||
* @param string[] $claims
|
||||
*/
|
||||
public function __construct(string $message, array $claims)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->claims = $claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClaims(): array
|
||||
{
|
||||
return $this->claims;
|
||||
}
|
||||
}
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use Exception;
|
||||
|
||||
class MissingMandatoryHeaderParameterException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $parameters;
|
||||
|
||||
/**
|
||||
* MissingMandatoryHeaderParameterException constructor.
|
||||
*
|
||||
* @param string[] $parameters
|
||||
*/
|
||||
public function __construct(string $message, array $parameters)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getParameters(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
|
||||
/**
|
||||
* This class is a claim checker.
|
||||
* When the "nbf" is present, it will compare the value with the current timestamp.
|
||||
*/
|
||||
final class NotBeforeChecker implements ClaimChecker, HeaderChecker
|
||||
{
|
||||
private const NAME = 'nbf';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $allowedTimeDrift;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeaderOnly;
|
||||
|
||||
public function __construct(int $allowedTimeDrift = 0, bool $protectedHeaderOnly = false)
|
||||
{
|
||||
$this->allowedTimeDrift = $allowedTimeDrift;
|
||||
$this->protectedHeaderOnly = $protectedHeaderOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidClaimException if the claim "nbf" is not an integer
|
||||
* @throws InvalidClaimException if the claim "nbf" restrict the use of the token
|
||||
*/
|
||||
public function checkClaim($value): void
|
||||
{
|
||||
if (!is_float($value) && !is_int($value)) {
|
||||
throw new InvalidClaimException('"nbf" must be an integer.', self::NAME, $value);
|
||||
}
|
||||
if (time() < $value - $this->allowedTimeDrift) {
|
||||
throw new InvalidClaimException('The JWT can not be used yet.', self::NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedClaim(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidHeaderException if the claim "nbf" is not an integer
|
||||
* @throws InvalidHeaderException if the claim "nbf" restrict the use of the token
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
if (!is_float($value) && !is_int($value)) {
|
||||
throw new InvalidHeaderException('"nbf" must be an integer.', self::NAME, $value);
|
||||
}
|
||||
if (time() < $value - $this->allowedTimeDrift) {
|
||||
throw new InvalidHeaderException('The JWT can not be used yet.', self::NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeaderOnly;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
PHP JWT Checker Component
|
||||
=========================
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
|
||||
**Please do not submit any Pull Request here.**
|
||||
You should go to [the main repository](https://github.com/web-token/jwt-framework) instead.
|
||||
|
||||
# Documentation
|
||||
|
||||
The official documentation is available as https://web-token.spomky-labs.com/
|
||||
|
||||
# Licence
|
||||
|
||||
This software is release under [MIT licence](LICENSE).
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use Jose\Component\Core\JWT;
|
||||
|
||||
interface TokenTypeSupport
|
||||
{
|
||||
/**
|
||||
* This method will retrieve the protect and unprotected headers of the token for the given index.
|
||||
* The index is useful when the token is serialized using the Json General Serialization mode.
|
||||
* For example the JWE Json General Serialization Mode allows several recipients to be set.
|
||||
* The unprotected headers correspond to the share unprotected header and the selected recipient header.
|
||||
*/
|
||||
public function retrieveTokenHeaders(JWT $jwt, int $index, array &$protectedHeader, array &$unprotectedHeader): void;
|
||||
|
||||
/**
|
||||
* This method returns true if the token in argument is supported, otherwise false.
|
||||
*/
|
||||
public function supports(JWT $jwt): bool;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Checker;
|
||||
|
||||
use function is_bool;
|
||||
|
||||
/**
|
||||
* This class is a header parameter checker.
|
||||
* When the "b64" is present, it will check if the value is a boolean or not.
|
||||
*
|
||||
* The use of this checker will allow the use of token with unencoded payload.
|
||||
*/
|
||||
final class UnencodedPayloadChecker implements HeaderChecker
|
||||
{
|
||||
private const HEADER_NAME = 'b64';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidHeaderException if the header parameter "b64" is not a boolean
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
if (!is_bool($value)) {
|
||||
throw new InvalidHeaderException('"b64" must be a boolean.', self::HEADER_NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::HEADER_NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "web-token/jwt-checker",
|
||||
"description": "Checker component of the JWT Framework.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"],
|
||||
"homepage": "https://github.com/web-token",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky"
|
||||
},{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/web-token/jwt-checker/contributors"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jose\\Component\\Checker\\": ""
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"web-token/jwt-core": "^2.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
@@ -0,0 +1 @@
|
||||
patreon: FlorentMorselli
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
|
||||
You should submit it here: https://github.com/web-token/jwt-framework/pulls
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class AddKeyIntoKeysetCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:add:key')
|
||||
->setDescription('Add a key into a key set.')
|
||||
->setHelp('This command adds a key at the end of a key set.')
|
||||
->addArgument('jwkset', InputArgument::REQUIRED, 'The JWKSet object')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The new JWK object')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwkset = $this->getKeyset($input);
|
||||
$jwk = $this->getKey($input);
|
||||
$jwkset = $jwkset->with($jwk);
|
||||
$this->prepareJsonOutput($input, $output, $jwkset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key set is invalid
|
||||
*/
|
||||
private function getKeyset(InputInterface $input): JWKSet
|
||||
{
|
||||
$jwkset = $input->getArgument('jwkset');
|
||||
if (!is_string($jwkset)) {
|
||||
throw new InvalidArgumentException('The argument must be a valid JWKSet.');
|
||||
}
|
||||
$json = JsonConverter::decode($jwkset);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('The argument must be a valid JWKSet.');
|
||||
}
|
||||
|
||||
return JWKSet::createFromKeyData($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key is invalid
|
||||
*/
|
||||
private function getKey(InputInterface $input): JWK
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('The argument must be a valid JWK.');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('The argument must be a valid JWK.');
|
||||
}
|
||||
|
||||
return new JWK($json);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class EcKeyGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:generate:ec')
|
||||
->setDescription('Generate an EC key (JWK format)')
|
||||
->addArgument('curve', InputArgument::REQUIRED, 'Curve of the key.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$curve = $input->getArgument('curve');
|
||||
if (!is_string($curve)) {
|
||||
throw new InvalidArgumentException('Invalid curve');
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
|
||||
$jwk = JWKFactory::createECKey($curve, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class EcKeysetGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:generate:ec')
|
||||
->setDescription('Generate an EC key set (JWKSet format)')
|
||||
->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of keys in the key set.')
|
||||
->addArgument('curve', InputArgument::REQUIRED, 'Curve of the keys.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the quantity of keys is invalid
|
||||
* @throws InvalidArgumentException if the curve is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$quantity = $input->getArgument('quantity');
|
||||
if (null === $quantity) {
|
||||
$quantity = 1;
|
||||
} elseif (is_array($quantity)) {
|
||||
$quantity = 1;
|
||||
} else {
|
||||
$quantity = (int) $quantity;
|
||||
}
|
||||
if ($quantity < 1) {
|
||||
throw new InvalidArgumentException('Invalid quantity');
|
||||
}
|
||||
$curve = $input->getArgument('curve');
|
||||
if (!is_string($curve)) {
|
||||
throw new InvalidArgumentException('Invalid curve');
|
||||
}
|
||||
|
||||
$keyset = new JWKSet([]);
|
||||
for ($i = 0; $i < $quantity; ++$i) {
|
||||
$args = $this->getOptions($input);
|
||||
$keyset = $keyset->with(JWKFactory::createECKey($curve, $args));
|
||||
}
|
||||
$this->prepareJsonOutput($input, $output, $keyset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use Base64Url\Base64Url;
|
||||
use InvalidArgumentException;
|
||||
use function is_bool;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
abstract class GeneratorCommand extends ObjectOutputCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return class_exists(JWKFactory::class);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->addOption('use', 'u', InputOption::VALUE_OPTIONAL, 'Usage of the key. Must be either "sig" or "enc".')
|
||||
->addOption('alg', 'a', InputOption::VALUE_OPTIONAL, 'Algorithm for the key.')
|
||||
->addOption('random_id', null, InputOption::VALUE_NONE, 'If this option is set, a random key ID (kid) will be generated.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the option "random_id" is not a valid
|
||||
*/
|
||||
protected function getOptions(InputInterface $input): array
|
||||
{
|
||||
$args = [];
|
||||
$useRandomId = $input->getOption('random_id');
|
||||
if (!is_bool($useRandomId)) {
|
||||
throw new InvalidArgumentException('Invalid value for option "random_id"');
|
||||
}
|
||||
if ($useRandomId) {
|
||||
$args['kid'] = $this->generateKeyID();
|
||||
}
|
||||
foreach (['use', 'alg'] as $key) {
|
||||
$value = $input->getOption($key);
|
||||
if (null !== $value) {
|
||||
$args[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function generateKeyID(): string
|
||||
{
|
||||
return Base64Url::encode(random_bytes(32));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class GetThumbprintCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:thumbprint')
|
||||
->setDescription('Get the thumbprint of a JWK key.')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The JWK key.')
|
||||
->addOption('hash', null, InputOption::VALUE_OPTIONAL, 'The hashing algorithm.', 'sha256')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the JWK or the hashing function are invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$hash = $input->getOption('hash');
|
||||
if (!is_string($hash)) {
|
||||
throw new InvalidArgumentException('Invalid hash algorithm');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid input.');
|
||||
}
|
||||
$key = new JWK($json);
|
||||
$output->write($key->thumbprint($hash));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JKUFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class JKULoaderCommand extends ObjectOutputCommand
|
||||
{
|
||||
/**
|
||||
* @var JKUFactory
|
||||
*/
|
||||
private $jkuFactory;
|
||||
|
||||
public function __construct(JKUFactory $jkuFactory, ?string $name = null)
|
||||
{
|
||||
$this->jkuFactory = $jkuFactory;
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:load:jku')
|
||||
->setDescription('Loads a key set from an url.')
|
||||
->setHelp('This command will try to get a key set from an URL. The distant key set is a JWKSet.')
|
||||
->addArgument('url', InputArgument::REQUIRED, 'The URL')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the URL is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$url = $input->getArgument('url');
|
||||
if (!is_string($url)) {
|
||||
throw new InvalidArgumentException('Invalid URL');
|
||||
}
|
||||
$result = $this->jkuFactory->loadFromUrl($url);
|
||||
$this->prepareJsonOutput($input, $output, $result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\KeyManagement\Analyzer\KeyAnalyzerManager;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class KeyAnalyzerCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var KeyAnalyzerManager
|
||||
*/
|
||||
private $analyzerManager;
|
||||
|
||||
public function __construct(KeyAnalyzerManager $keysetAnalyzerManager, string $name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->analyzerManager = $keysetAnalyzerManager;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:analyze')
|
||||
->setDescription('JWK quality analyzer.')
|
||||
->setHelp('This command will analyze a JWK object and find security issues.')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The JWK object')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->getFormatter()->setStyle('success', new OutputFormatterStyle('white', 'green'));
|
||||
$output->getFormatter()->setStyle('high', new OutputFormatterStyle('white', 'red', ['bold']));
|
||||
$output->getFormatter()->setStyle('medium', new OutputFormatterStyle('yellow'));
|
||||
$output->getFormatter()->setStyle('low', new OutputFormatterStyle('blue'));
|
||||
$jwk = $this->getKey($input);
|
||||
|
||||
$result = $this->analyzerManager->analyze($jwk);
|
||||
if (0 === $result->count()) {
|
||||
$output->writeln('<success>All good! No issue found.</success>');
|
||||
} else {
|
||||
foreach ($result->all() as $message) {
|
||||
$output->writeln('<'.$message->getSeverity().'>* '.$message->getMessage().'</'.$message->getSeverity().'>');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key is invalid
|
||||
*/
|
||||
private function getKey(InputInterface $input): JWK
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWK.');
|
||||
}
|
||||
|
||||
return new JWK($json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class KeyFileLoaderCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:load:key')
|
||||
->setDescription('Loads a key from a key file (JWK format)')
|
||||
->addArgument('file', InputArgument::REQUIRED, 'Filename of the key.')
|
||||
->addOption('secret', 's', InputOption::VALUE_OPTIONAL, 'Secret if the key is encrypted.', null)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the file is invalid
|
||||
* @throws InvalidArgumentException if the secret is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$file = $input->getArgument('file');
|
||||
$password = $input->getOption('secret');
|
||||
if (!is_string($file)) {
|
||||
throw new InvalidArgumentException('Invalid file');
|
||||
}
|
||||
if (null !== $password && !is_string($password)) {
|
||||
throw new InvalidArgumentException('Invalid secret');
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
|
||||
$jwk = JWKFactory::createFromKeyFile($file, $password, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\KeyManagement\Analyzer\KeyAnalyzerManager;
|
||||
use Jose\Component\KeyManagement\Analyzer\KeysetAnalyzerManager;
|
||||
use Jose\Component\KeyManagement\Analyzer\MessageBag;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class KeysetAnalyzerCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var KeysetAnalyzerManager
|
||||
*/
|
||||
private $keysetAnalyzerManager;
|
||||
|
||||
/**
|
||||
* @var KeyAnalyzerManager
|
||||
*/
|
||||
private $keyAnalyzerManager;
|
||||
|
||||
public function __construct(KeysetAnalyzerManager $keysetAnalyzerManager, KeyAnalyzerManager $keyAnalyzerManager, string $name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->keysetAnalyzerManager = $keysetAnalyzerManager;
|
||||
$this->keyAnalyzerManager = $keyAnalyzerManager;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:analyze')
|
||||
->setDescription('JWKSet quality analyzer.')
|
||||
->setHelp('This command will analyze a JWKSet object and find security issues.')
|
||||
->addArgument('jwkset', InputArgument::REQUIRED, 'The JWKSet object')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->getFormatter()->setStyle('success', new OutputFormatterStyle('white', 'green'));
|
||||
$output->getFormatter()->setStyle('high', new OutputFormatterStyle('white', 'red', ['bold']));
|
||||
$output->getFormatter()->setStyle('medium', new OutputFormatterStyle('yellow'));
|
||||
$output->getFormatter()->setStyle('low', new OutputFormatterStyle('blue'));
|
||||
|
||||
$jwkset = $this->getKeyset($input);
|
||||
|
||||
$messages = $this->keysetAnalyzerManager->analyze($jwkset);
|
||||
$this->showMessages($messages, $output);
|
||||
foreach ($jwkset as $kid => $jwk) {
|
||||
$output->writeln(sprintf('Analysing key with index/kid "%s"', $kid));
|
||||
$messages = $this->keyAnalyzerManager->analyze($jwk);
|
||||
$this->showMessages($messages, $output);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function showMessages(MessageBag $messages, OutputInterface $output): void
|
||||
{
|
||||
if (0 === $messages->count()) {
|
||||
$output->writeln(' <success>All good! No issue found.</success>');
|
||||
} else {
|
||||
foreach ($messages->all() as $message) {
|
||||
$output->writeln(' <'.$message->getSeverity().'>* '.$message->getMessage().'</'.$message->getSeverity().'>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the JWKSet is invalid
|
||||
*/
|
||||
private function getKeyset(InputInterface $input): JWKSet
|
||||
{
|
||||
$jwkset = $input->getArgument('jwkset');
|
||||
if (!is_string($jwkset)) {
|
||||
throw new InvalidArgumentException('Invalid JWKSet');
|
||||
}
|
||||
$json = JsonConverter::decode($jwkset);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWKSet');
|
||||
}
|
||||
|
||||
return JWKSet::createFromKeyData($json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 Spomky-Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class MergeKeysetCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:merge')
|
||||
->setDescription('Merge several key sets into one.')
|
||||
->setHelp('This command merges several key sets into one. It is very useful when you generate e.g. RSA, EC and OKP keys and you want only one key set to rule them all.')
|
||||
->addArgument('jwksets', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'The JWKSet objects')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the JWKSet is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
/** @var string[] $keySets */
|
||||
$keySets = $input->getArgument('jwksets');
|
||||
$newJwkset = new JWKSet([]);
|
||||
foreach ($keySets as $keySet) {
|
||||
$json = JsonConverter::decode($keySet);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('The argument must be a valid JWKSet.');
|
||||
}
|
||||
$jwkset = JWKSet::createFromKeyData($json);
|
||||
foreach ($jwkset->all() as $jwk) {
|
||||
$newJwkset = $newJwkset->with($jwk);
|
||||
}
|
||||
}
|
||||
$this->prepareJsonOutput($input, $output, $newJwkset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class NoneKeyGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:generate:none')
|
||||
->setDescription('Generate a none key (JWK format). This key type is only supposed to be used with the "none" algorithm.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$args = $this->getOptions($input);
|
||||
|
||||
$jwk = JWKFactory::createNoneKey($args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use JsonSerializable;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
abstract class ObjectOutputCommand extends Command
|
||||
{
|
||||
protected function prepareJsonOutput(InputInterface $input, OutputInterface $output, JsonSerializable $json): void
|
||||
{
|
||||
$data = JsonConverter::encode($json);
|
||||
$output->write($data);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class OctKeyGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:generate:oct')
|
||||
->setDescription('Generate an octet key (JWK format)')
|
||||
->addArgument('size', InputArgument::REQUIRED, 'Key size.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key size is not valid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$size = $input->getArgument('size');
|
||||
if (null === $size) {
|
||||
$size = 1;
|
||||
} elseif (is_array($size)) {
|
||||
$size = 1;
|
||||
} else {
|
||||
$size = (int) $size;
|
||||
}
|
||||
if ($size < 1) {
|
||||
throw new InvalidArgumentException('Invalid size');
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
|
||||
$jwk = JWKFactory::createOctKey($size, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class OctKeysetGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:generate:oct')
|
||||
->setDescription('Generate a key set with octet keys (JWK format)')
|
||||
->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of keys in the key set.')
|
||||
->addArgument('size', InputArgument::REQUIRED, 'Key size.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the quantity is not valid
|
||||
* @throws InvalidArgumentException if the key size is not valid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$quantity = $input->getArgument('quantity');
|
||||
if (null === $quantity) {
|
||||
$quantity = 1;
|
||||
} elseif (is_array($quantity)) {
|
||||
$quantity = 1;
|
||||
} else {
|
||||
$quantity = (int) $quantity;
|
||||
}
|
||||
|
||||
$size = $input->getArgument('size');
|
||||
if (null === $size) {
|
||||
$size = 1;
|
||||
} elseif (is_array($size)) {
|
||||
$size = 1;
|
||||
} else {
|
||||
$size = (int) $size;
|
||||
}
|
||||
if ($quantity < 1) {
|
||||
throw new InvalidArgumentException('Invalid quantity');
|
||||
}
|
||||
if ($size < 1) {
|
||||
throw new InvalidArgumentException('Invalid size');
|
||||
}
|
||||
|
||||
$keyset = new JWKSet([]);
|
||||
for ($i = 0; $i < $quantity; ++$i) {
|
||||
$args = $this->getOptions($input);
|
||||
$keyset = $keyset->with(JWKFactory::createOctKey($size, $args));
|
||||
}
|
||||
$this->prepareJsonOutput($input, $output, $keyset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class OkpKeyGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:generate:okp')
|
||||
->setDescription('Generate an Octet Key Pair key (JWK format)')
|
||||
->addArgument('curve', InputArgument::REQUIRED, 'Curve of the key.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is not valid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$curve = $input->getArgument('curve');
|
||||
if (!is_string($curve)) {
|
||||
throw new InvalidArgumentException('Invalid curve');
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
|
||||
$jwk = JWKFactory::createOKPKey($curve, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class OkpKeysetGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:generate:okp')
|
||||
->setDescription('Generate a key set with Octet Key Pairs keys (JWKSet format)')
|
||||
->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of keys in the key set.')
|
||||
->addArgument('curve', InputArgument::REQUIRED, 'Curve of the keys.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is not valid
|
||||
* @throws InvalidArgumentException if the quantity is not valid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$quantity = $input->getArgument('quantity');
|
||||
if (null === $quantity) {
|
||||
$quantity = 1;
|
||||
} elseif (is_array($quantity)) {
|
||||
$quantity = 1;
|
||||
} else {
|
||||
$quantity = (int) $quantity;
|
||||
}
|
||||
$curve = $input->getArgument('curve');
|
||||
if ($quantity < 1) {
|
||||
throw new InvalidArgumentException('Invalid quantity');
|
||||
}
|
||||
if (!is_string($curve)) {
|
||||
throw new InvalidArgumentException('Invalid curve');
|
||||
}
|
||||
|
||||
$keyset = new JWKSet([]);
|
||||
for ($i = 0; $i < $quantity; ++$i) {
|
||||
$args = $this->getOptions($input);
|
||||
$keyset = $keyset->with(JWKFactory::createOKPKey($curve, $args));
|
||||
}
|
||||
$this->prepareJsonOutput($input, $output, $keyset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\KeyManagement\KeyConverter\RSAKey;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class OptimizeRsaKeyCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:optimize')
|
||||
->setDescription('Optimize a RSA key by calculating additional primes (CRT).')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The RSA key.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key is not valid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$key = RSAKey::createFromJWK(new JWK($json));
|
||||
$key->optimize();
|
||||
$this->prepareJsonOutput($input, $output, $key->toJwk());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class P12CertificateLoaderCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:load:p12')
|
||||
->setDescription('Load a key from a P12 certificate file.')
|
||||
->addArgument('file', InputArgument::REQUIRED, 'Filename of the P12 certificate.')
|
||||
->addOption('secret', 's', InputOption::VALUE_OPTIONAL, 'Secret if the key is encrypted.', null)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the file is not valid
|
||||
* @throws InvalidArgumentException if the secret is not valid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$file = $input->getArgument('file');
|
||||
$password = $input->getOption('secret');
|
||||
if (!is_string($file)) {
|
||||
throw new InvalidArgumentException('Invalid file');
|
||||
}
|
||||
if (!is_string($password)) {
|
||||
throw new InvalidArgumentException('Invalid secret');
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
$jwk = JWKFactory::createFromPKCS12CertificateFile($file, $password, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\ECKey;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\Core\Util\RSAKey;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class PemConverterCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:convert:pkcs1')
|
||||
->setDescription('Converts a RSA or EC key into PKCS#1 key.')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The key')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key is invalid
|
||||
* @throws InvalidArgumentException if the key type is not RSA or EC
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWK.');
|
||||
}
|
||||
$key = new JWK($json);
|
||||
|
||||
switch ($key->get('kty')) {
|
||||
case 'RSA':
|
||||
$pem = RSAKey::createFromJWK($key)->toPEM();
|
||||
|
||||
break;
|
||||
|
||||
case 'EC':
|
||||
$pem = ECKey::convertToPEM($key);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Not a RSA or EC key.');
|
||||
}
|
||||
$output->write($pem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class PublicKeyCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:convert:public')
|
||||
->setDescription('Convert a private key into public key. Symmetric keys (shared keys) are not changed.')
|
||||
->setHelp('This command converts a private key into a public key.')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The JWK object')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwk = $this->getKey($input);
|
||||
$jwk = $jwk->toPublic();
|
||||
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key is invalid
|
||||
*/
|
||||
private function getKey(InputInterface $input): JWK
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
|
||||
return new JWK($json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class PublicKeysetCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:convert:public')
|
||||
->setDescription('Convert private keys in a key set into public keys. Symmetric keys (shared keys) are not changed.')
|
||||
->setHelp('This command converts private keys in a key set into public keys.')
|
||||
->addArgument('jwkset', InputArgument::REQUIRED, 'The JWKSet object')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwkset = $this->getKeyset($input);
|
||||
$newJwkset = new JWKSet([]);
|
||||
|
||||
foreach ($jwkset->all() as $jwk) {
|
||||
$newJwkset = $newJwkset->with($jwk->toPublic());
|
||||
}
|
||||
$this->prepareJsonOutput($input, $output, $newJwkset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the keyset is invalid
|
||||
*/
|
||||
private function getKeyset(InputInterface $input): JWKSet
|
||||
{
|
||||
$jwkset = $input->getArgument('jwkset');
|
||||
if (!is_string($jwkset)) {
|
||||
throw new InvalidArgumentException('Invalid JWKSet');
|
||||
}
|
||||
$json = JsonConverter::decode($jwkset);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWKSet');
|
||||
}
|
||||
|
||||
return JWKSet::createFromKeyData($json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
PHP JWT Console Component
|
||||
================================
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
|
||||
**Please do not submit any Pull Request here.**
|
||||
You should go to [the main repository](https://github.com/web-token/jwt-framework) instead.
|
||||
|
||||
# Documentation
|
||||
|
||||
The official documentation is available as https://web-token.spomky-labs.com/
|
||||
|
||||
# Licence
|
||||
|
||||
This software is release under [MIT licence](LICENSE).
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use function count;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class RotateKeysetCommand extends ObjectOutputCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:rotate')
|
||||
->setDescription('Rotate a key set.')
|
||||
->setHelp('This command removes the last key in a key set a place a new one at the beginning.')
|
||||
->addArgument('jwkset', InputArgument::REQUIRED, 'The JWKSet object')
|
||||
->addArgument('jwk', InputArgument::REQUIRED, 'The new JWK object')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jwkset = $this->getKeyset($input)->all();
|
||||
$jwk = $this->getKey($input);
|
||||
|
||||
if (0 !== count($jwkset)) {
|
||||
array_pop($jwkset);
|
||||
}
|
||||
array_unshift($jwkset, $jwk);
|
||||
|
||||
$this->prepareJsonOutput($input, $output, new JWKSet($jwkset));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the keyset is invalid
|
||||
*/
|
||||
private function getKeyset(InputInterface $input): JWKSet
|
||||
{
|
||||
$jwkset = $input->getArgument('jwkset');
|
||||
if (!is_string($jwkset)) {
|
||||
throw new InvalidArgumentException('Invalid JWKSet');
|
||||
}
|
||||
$json = JsonConverter::decode($jwkset);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWKSet');
|
||||
}
|
||||
|
||||
return JWKSet::createFromKeyData($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key is invalid
|
||||
*/
|
||||
private function getKey(InputInterface $input): JWK
|
||||
{
|
||||
$jwk = $input->getArgument('jwk');
|
||||
if (!is_string($jwk)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
$json = JsonConverter::decode($jwk);
|
||||
if (!is_array($json)) {
|
||||
throw new InvalidArgumentException('Invalid JWK');
|
||||
}
|
||||
|
||||
return new JWK($json);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class RsaKeyGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:generate:rsa')
|
||||
->setDescription('Generate a RSA key (JWK format)')
|
||||
->addArgument('size', InputArgument::REQUIRED, 'Key size.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key size is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$size = $input->getArgument('size');
|
||||
if (null === $size) {
|
||||
$size = 1;
|
||||
} elseif (is_array($size)) {
|
||||
$size = 1;
|
||||
} else {
|
||||
$size = (int) $size;
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
if ($size < 1) {
|
||||
throw new InvalidArgumentException('Invalid size');
|
||||
}
|
||||
|
||||
$jwk = JWKFactory::createRSAKey($size, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class RsaKeysetGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:generate:rsa')
|
||||
->setDescription('Generate a key set with RSA keys (JWK format)')
|
||||
->addArgument('quantity', InputArgument::REQUIRED, 'Quantity of keys in the key set.')
|
||||
->addArgument('size', InputArgument::REQUIRED, 'Key size.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the quantity is invalid
|
||||
* @throws InvalidArgumentException if the key size is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$quantity = $input->getArgument('quantity');
|
||||
if (null === $quantity) {
|
||||
$quantity = 1;
|
||||
} elseif (is_array($quantity)) {
|
||||
$quantity = 1;
|
||||
} else {
|
||||
$quantity = (int) $quantity;
|
||||
}
|
||||
$size = $input->getArgument('size');
|
||||
if (null === $size) {
|
||||
$size = 1;
|
||||
} elseif (is_array($size)) {
|
||||
$size = 1;
|
||||
} else {
|
||||
$size = (int) $size;
|
||||
}
|
||||
if ($quantity < 1) {
|
||||
throw new InvalidArgumentException('Invalid quantity');
|
||||
}
|
||||
if ($size < 1) {
|
||||
throw new InvalidArgumentException('Invalid size');
|
||||
}
|
||||
|
||||
$keyset = new JWKSet([]);
|
||||
for ($i = 0; $i < $quantity; ++$i) {
|
||||
$args = $this->getOptions($input);
|
||||
$keyset = $keyset->with(JWKFactory::createRSAKey($size, $args));
|
||||
}
|
||||
$this->prepareJsonOutput($input, $output, $keyset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_bool;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class SecretKeyGeneratorCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:generate:from_secret')
|
||||
->setDescription('Generate an octet key (JWK format) using an existing secret')
|
||||
->addArgument('secret', InputArgument::REQUIRED, 'The secret')
|
||||
->addOption('is_b64', 'b', InputOption::VALUE_NONE, 'Indicates if the secret is Base64 encoded (useful for binary secrets)')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the secret is invalid
|
||||
* @throws InvalidArgumentException if the option "is_b4" is not a boolean
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$secret = $input->getArgument('secret');
|
||||
if (!is_string($secret)) {
|
||||
throw new InvalidArgumentException('Invalid secret');
|
||||
}
|
||||
$isBsae64Encoded = $input->getOption('is_b64');
|
||||
if (!is_bool($isBsae64Encoded)) {
|
||||
throw new InvalidArgumentException('Invalid option value for "is_b64"');
|
||||
}
|
||||
if ($isBsae64Encoded) {
|
||||
$secret = base64_decode($secret, true);
|
||||
}
|
||||
$args = $this->getOptions($input);
|
||||
|
||||
$jwk = JWKFactory::createFromSecret($secret, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\JWKFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class X509CertificateLoaderCommand extends GeneratorCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('key:load:x509')
|
||||
->setDescription('Load a key from a X.509 certificate file.')
|
||||
->addArgument('file', InputArgument::REQUIRED, 'Filename of the X.509 certificate.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the file is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$file = $input->getArgument('file');
|
||||
if (!is_string($file)) {
|
||||
throw new InvalidArgumentException('Invalid file');
|
||||
}
|
||||
$args = [];
|
||||
foreach (['use', 'alg'] as $key) {
|
||||
$value = $input->getOption($key);
|
||||
if (null !== $value) {
|
||||
$args[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$jwk = JWKFactory::createFromCertificateFile($file, $args);
|
||||
$this->prepareJsonOutput($input, $output, $jwk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Console;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\KeyManagement\X5UFactory;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
final class X5ULoaderCommand extends ObjectOutputCommand
|
||||
{
|
||||
/**
|
||||
* @var X5UFactory
|
||||
*/
|
||||
private $x5uFactory;
|
||||
|
||||
public function __construct(X5UFactory $x5uFactory, ?string $name = null)
|
||||
{
|
||||
$this->x5uFactory = $x5uFactory;
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this
|
||||
->setName('keyset:load:x5u')
|
||||
->setDescription('Loads a key set from an url.')
|
||||
->setHelp('This command will try to get a key set from an URL. The distant key set is list of X.509 certificates.')
|
||||
->addArgument('url', InputArgument::REQUIRED, 'The URL')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the URL is invalid
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$url = $input->getArgument('url');
|
||||
if (!is_string($url)) {
|
||||
throw new InvalidArgumentException('Invalid URL');
|
||||
}
|
||||
$result = $this->x5uFactory->loadFromUrl($url);
|
||||
$this->prepareJsonOutput($input, $output, $result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "web-token/jwt-console",
|
||||
"description": "Console component of the JWT Framework.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"],
|
||||
"homepage": "https://github.com/web-token",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky"
|
||||
},{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/web-token/jwt-console/contributors"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jose\\Component\\Console\\": ""
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"symfony/console": "^4.2|^5.0",
|
||||
"web-token/jwt-key-mgmt": "^2.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
@@ -0,0 +1 @@
|
||||
patreon: FlorentMorselli
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
|
||||
You should submit it here: https://github.com/web-token/jwt-framework/pulls
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core;
|
||||
|
||||
interface Algorithm
|
||||
{
|
||||
/**
|
||||
* Returns the name of the algorithm.
|
||||
*/
|
||||
public function name(): string;
|
||||
|
||||
/**
|
||||
* Returns the key types suitable for this algorithm (e.g. "oct", "RSA"...).
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function allowedKeyTypes(): array;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core;
|
||||
|
||||
use function array_key_exists;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class AlgorithmManager
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $algorithms = [];
|
||||
|
||||
/**
|
||||
* @param Algorithm[] $algorithms
|
||||
*/
|
||||
public function __construct(array $algorithms)
|
||||
{
|
||||
foreach ($algorithms as $algorithm) {
|
||||
$this->add($algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the algorithm is supported.
|
||||
*
|
||||
* @param string $algorithm The algorithm
|
||||
*/
|
||||
public function has(string $algorithm): bool
|
||||
{
|
||||
return array_key_exists($algorithm, $this->algorithms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of names of supported algorithms.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function list(): array
|
||||
{
|
||||
return array_keys($this->algorithms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the algorithm if supported, otherwise throw an exception.
|
||||
*
|
||||
* @param string $algorithm The algorithm
|
||||
*
|
||||
* @throws InvalidArgumentException if the algorithm is not supported
|
||||
*/
|
||||
public function get(string $algorithm): Algorithm
|
||||
{
|
||||
if (!$this->has($algorithm)) {
|
||||
throw new InvalidArgumentException(sprintf('The algorithm "%s" is not supported.', $algorithm));
|
||||
}
|
||||
|
||||
return $this->algorithms[$algorithm];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an algorithm to the manager.
|
||||
*/
|
||||
public function add(Algorithm $algorithm): void
|
||||
{
|
||||
$name = $algorithm->name();
|
||||
$this->algorithms[$name] = $algorithm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
|
||||
class AlgorithmManagerFactory
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $algorithms = [];
|
||||
|
||||
/**
|
||||
* Adds an algorithm.
|
||||
*
|
||||
* Each algorithm is identified by an alias hence it is allowed to have the same algorithm twice (or more).
|
||||
* This can be helpful when an algorithm have several configuration options.
|
||||
*/
|
||||
public function add(string $alias, Algorithm $algorithm): void
|
||||
{
|
||||
$this->algorithms[$alias] = $algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of aliases.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function aliases(): array
|
||||
{
|
||||
return array_keys($this->algorithms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all algorithms supported by this factory.
|
||||
* This is an associative array. Keys are the aliases of the algorithms.
|
||||
*
|
||||
* @return Algorithm[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->algorithms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an algorithm manager using the given aliases.
|
||||
*
|
||||
* @param string[] $aliases
|
||||
*
|
||||
* @throws InvalidArgumentException if the alias is invalid or is not supported
|
||||
*/
|
||||
public function create(array $aliases): AlgorithmManager
|
||||
{
|
||||
$algorithms = [];
|
||||
foreach ($aliases as $alias) {
|
||||
if (!is_string($alias)) {
|
||||
throw new InvalidArgumentException('Invalid alias');
|
||||
}
|
||||
if (!isset($this->algorithms[$alias])) {
|
||||
throw new InvalidArgumentException(sprintf('The algorithm with the alias "%s" is not supported.', $alias));
|
||||
}
|
||||
$algorithms[] = $this->algorithms[$alias];
|
||||
}
|
||||
|
||||
return new AlgorithmManager($algorithms);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core;
|
||||
|
||||
use function array_key_exists;
|
||||
use Base64Url\Base64Url;
|
||||
use function in_array;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use JsonSerializable;
|
||||
|
||||
class JWK implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $values = [];
|
||||
|
||||
/**
|
||||
* Creates a JWK object using the given values.
|
||||
* The member "kty" is mandatory. Other members are NOT checked.
|
||||
*
|
||||
* @throws InvalidArgumentException if the key parameter "kty" is missing
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if (!isset($values['kty'])) {
|
||||
throw new InvalidArgumentException('The parameter "kty" is mandatory.');
|
||||
}
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JWK object using the given Json string.
|
||||
*
|
||||
* @throws InvalidArgumentException if the data is not valid
|
||||
*
|
||||
* @return JWK
|
||||
*/
|
||||
public static function createFromJson(string $json): self
|
||||
{
|
||||
$data = json_decode($json, true);
|
||||
if (!is_array($data)) {
|
||||
throw new InvalidArgumentException('Invalid argument.');
|
||||
}
|
||||
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values to be serialized.
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value with a specific key.
|
||||
*
|
||||
* @param string $key The key
|
||||
*
|
||||
* @throws InvalidArgumentException if the key does not exist
|
||||
*
|
||||
* @return null|mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
if (!$this->has($key)) {
|
||||
throw new InvalidArgumentException(sprintf('The value identified by "%s" does not exist.', $key));
|
||||
}
|
||||
|
||||
return $this->values[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the JWK has the value identified by.
|
||||
*
|
||||
* @param string $key The key
|
||||
*/
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all values stored in the JWK object.
|
||||
*
|
||||
* @return array Values of the JWK object
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thumbprint of the key.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7638
|
||||
*
|
||||
* @throws InvalidArgumentException if the hashing function is not supported
|
||||
*/
|
||||
public function thumbprint(string $hash_algorithm): string
|
||||
{
|
||||
if (!in_array($hash_algorithm, hash_algos(), true)) {
|
||||
throw new InvalidArgumentException(sprintf('The hash algorithm "%s" is not supported.', $hash_algorithm));
|
||||
}
|
||||
$values = array_intersect_key($this->values, array_flip(['kty', 'n', 'e', 'crv', 'x', 'y', 'k']));
|
||||
ksort($values);
|
||||
$input = json_encode($values, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
return Base64Url::encode(hash($hash_algorithm, $input, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the associated public key.
|
||||
* This method has no effect for:
|
||||
* - public keys
|
||||
* - shared keys
|
||||
* - unknown keys.
|
||||
*
|
||||
* Known keys are "oct", "RSA", "EC" and "OKP".
|
||||
*
|
||||
* @return JWK
|
||||
*/
|
||||
public function toPublic(): self
|
||||
{
|
||||
$values = array_diff_key($this->values, array_flip(['p', 'd', 'q', 'dp', 'dq', 'qi']));
|
||||
|
||||
return new self($values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core;
|
||||
|
||||
use function array_key_exists;
|
||||
use ArrayIterator;
|
||||
use function count;
|
||||
use Countable;
|
||||
use function in_array;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use Traversable;
|
||||
|
||||
class JWKSet implements Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $keys = [];
|
||||
|
||||
/**
|
||||
* @param JWK[] $keys
|
||||
*
|
||||
* @throws InvalidArgumentException if the list is invalid
|
||||
*/
|
||||
public function __construct(array $keys)
|
||||
{
|
||||
foreach ($keys as $k => $key) {
|
||||
if (!$key instanceof JWK) {
|
||||
throw new InvalidArgumentException('Invalid list. Should only contains JWK objects');
|
||||
}
|
||||
|
||||
if ($key->has('kid')) {
|
||||
unset($keys[$k]);
|
||||
$this->keys[$key->get('kid')] = $key;
|
||||
} else {
|
||||
$this->keys[] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JWKSet object using the given values.
|
||||
*
|
||||
* @throws InvalidArgumentException if the keyset is not valid
|
||||
*
|
||||
* @return JWKSet
|
||||
*/
|
||||
public static function createFromKeyData(array $data): self
|
||||
{
|
||||
if (!isset($data['keys'])) {
|
||||
throw new InvalidArgumentException('Invalid data.');
|
||||
}
|
||||
if (!is_array($data['keys'])) {
|
||||
throw new InvalidArgumentException('Invalid data.');
|
||||
}
|
||||
|
||||
$jwkset = new self([]);
|
||||
foreach ($data['keys'] as $key) {
|
||||
$jwk = new JWK($key);
|
||||
if ($jwk->has('kid')) {
|
||||
$jwkset->keys[$jwk->get('kid')] = $jwk;
|
||||
} else {
|
||||
$jwkset->keys[] = $jwk;
|
||||
}
|
||||
}
|
||||
|
||||
return $jwkset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JWKSet object using the given Json string.
|
||||
*
|
||||
* @throws InvalidArgumentException if the data is not valid
|
||||
*
|
||||
* @return JWKSet
|
||||
*/
|
||||
public static function createFromJson(string $json): self
|
||||
{
|
||||
$data = json_decode($json, true);
|
||||
if (!is_array($data)) {
|
||||
throw new InvalidArgumentException('Invalid argument.');
|
||||
}
|
||||
|
||||
return self::createFromKeyData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of keys stored in the key set.
|
||||
*
|
||||
* @return JWK[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add key to store in the key set.
|
||||
* This method is immutable and will return a new object.
|
||||
*
|
||||
* @return JWKSet
|
||||
*/
|
||||
public function with(JWK $jwk): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
if ($jwk->has('kid')) {
|
||||
$clone->keys[$jwk->get('kid')] = $jwk;
|
||||
} else {
|
||||
$clone->keys[] = $jwk;
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove key from the key set.
|
||||
* This method is immutable and will return a new object.
|
||||
*
|
||||
* @param int|string $key Key to remove from the key set
|
||||
*
|
||||
* @return JWKSet
|
||||
*/
|
||||
public function without($key): self
|
||||
{
|
||||
if (!$this->has($key)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
unset($clone->keys[$key]);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the key set contains a key with the given index.
|
||||
*
|
||||
* @param int|string $index
|
||||
*/
|
||||
public function has($index): bool
|
||||
{
|
||||
return array_key_exists($index, $this->keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key with the given index. Throws an exception if the index is not present in the key store.
|
||||
*
|
||||
* @param int|string $index
|
||||
*
|
||||
* @throws InvalidArgumentException if the index is not defined
|
||||
*/
|
||||
public function get($index): JWK
|
||||
{
|
||||
if (!$this->has($index)) {
|
||||
throw new InvalidArgumentException('Undefined index.');
|
||||
}
|
||||
|
||||
return $this->keys[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values to be serialized.
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return ['keys' => array_values($this->keys)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of keys in the key set.
|
||||
*
|
||||
* @param int $mode
|
||||
*/
|
||||
public function count($mode = COUNT_NORMAL): int
|
||||
{
|
||||
return count($this->keys, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a key that fits on the selected requirements.
|
||||
* Returns null if not found.
|
||||
*
|
||||
* @param string $type Must be 'sig' (signature) or 'enc' (encryption)
|
||||
* @param null|Algorithm $algorithm Specifies the algorithm to be used
|
||||
* @param array $restrictions More restrictions such as 'kid' or 'kty'
|
||||
*
|
||||
* @throws InvalidArgumentException if the key type is not valid (must be "sig" or "enc")
|
||||
*/
|
||||
public function selectKey(string $type, ?Algorithm $algorithm = null, array $restrictions = []): ?JWK
|
||||
{
|
||||
if (!in_array($type, ['enc', 'sig'], true)) {
|
||||
throw new InvalidArgumentException('Allowed key types are "sig" or "enc".');
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->keys as $key) {
|
||||
$ind = 0;
|
||||
|
||||
$can_use = $this->canKeyBeUsedFor($type, $key);
|
||||
if (false === $can_use) {
|
||||
continue;
|
||||
}
|
||||
$ind += $can_use;
|
||||
|
||||
$alg = $this->canKeyBeUsedWithAlgorithm($algorithm, $key);
|
||||
if (false === $alg) {
|
||||
continue;
|
||||
}
|
||||
$ind += $alg;
|
||||
|
||||
if (false === $this->doesKeySatisfyRestrictions($restrictions, $key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = ['key' => $key, 'ind' => $ind];
|
||||
}
|
||||
|
||||
if (0 === count($result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
usort($result, [$this, 'sortKeys']);
|
||||
|
||||
return $result[0]['key'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method only. Should not be used.
|
||||
*
|
||||
* @internal
|
||||
* @internal
|
||||
*/
|
||||
public static function sortKeys(array $a, array $b): int
|
||||
{
|
||||
if ($a['ind'] === $b['ind']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a['ind'] > $b['ind']) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method only. Should not be used.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key does not fulfill with the "key_ops" constraint
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
private function canKeyBeUsedFor(string $type, JWK $key)
|
||||
{
|
||||
if ($key->has('use')) {
|
||||
return $type === $key->get('use') ? 1 : false;
|
||||
}
|
||||
if ($key->has('key_ops')) {
|
||||
$key_ops = $key->get('key_ops');
|
||||
if (!is_array($key_ops)) {
|
||||
throw new InvalidArgumentException('Invalid key parameter "key_ops". Should be a list of key operations');
|
||||
}
|
||||
|
||||
return $type === self::convertKeyOpsToKeyUse($key_ops) ? 1 : false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|int
|
||||
*/
|
||||
private function canKeyBeUsedWithAlgorithm(?Algorithm $algorithm, JWK $key)
|
||||
{
|
||||
if (null === $algorithm) {
|
||||
return 0;
|
||||
}
|
||||
if (!in_array($key->get('kty'), $algorithm->allowedKeyTypes(), true)) {
|
||||
return false;
|
||||
}
|
||||
if ($key->has('alg')) {
|
||||
return $algorithm->name() === $key->get('alg') ? 2 : false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function doesKeySatisfyRestrictions(array $restrictions, JWK $key): bool
|
||||
{
|
||||
foreach ($restrictions as $k => $v) {
|
||||
if (!$key->has($k) || $v !== $key->get($k)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the key operation is not supported
|
||||
*/
|
||||
private static function convertKeyOpsToKeyUse(array $key_ops): string
|
||||
{
|
||||
switch (true) {
|
||||
case in_array('verify', $key_ops, true):
|
||||
case in_array('sign', $key_ops, true):
|
||||
return 'sig';
|
||||
|
||||
case in_array('encrypt', $key_ops, true):
|
||||
case in_array('decrypt', $key_ops, true):
|
||||
case in_array('wrapKey', $key_ops, true):
|
||||
case in_array('unwrapKey', $key_ops, true):
|
||||
case in_array('deriveKey', $key_ops, true):
|
||||
case in_array('deriveBits', $key_ops, true):
|
||||
return 'enc';
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Unsupported key operation value "%s"', $key_ops));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core;
|
||||
|
||||
interface JWT
|
||||
{
|
||||
/**
|
||||
* Returns the payload of the JWT.
|
||||
* null is a valid payload (e.g. JWS with detached payload).
|
||||
*/
|
||||
public function getPayload(): ?string;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 Spomky-Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,15 @@
|
||||
PHP JWT Core Component
|
||||
======================
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
|
||||
**Please do not submit any Pull Request here.**
|
||||
You should go to [the main repository](https://github.com/web-token/jwt-framework) instead.
|
||||
|
||||
# Documentation
|
||||
|
||||
The official documentation is available as https://web-token.spomky-labs.com/
|
||||
|
||||
# Licence
|
||||
|
||||
This software is release under [MIT licence](LICENSE).
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util;
|
||||
|
||||
use Brick\Math\BigInteger as BrickBigInteger;
|
||||
use function chr;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class BigInteger
|
||||
{
|
||||
/**
|
||||
* Holds the BigInteger's value.
|
||||
*
|
||||
* @var BrickBigInteger
|
||||
*/
|
||||
private $value;
|
||||
|
||||
private function __construct(BrickBigInteger $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BigInteger
|
||||
*/
|
||||
public static function createFromBinaryString(string $value): self
|
||||
{
|
||||
$data = current(unpack('H*', $value));
|
||||
|
||||
return new self(BrickBigInteger::fromBase($data, 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BigInteger
|
||||
*/
|
||||
public static function createFromDecimal(int $value): self
|
||||
{
|
||||
return new self(BrickBigInteger::of($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BigInteger
|
||||
*/
|
||||
public static function createFromBigInteger(BrickBigInteger $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BigInteger to a binary string.
|
||||
*/
|
||||
public function toBytes(): string
|
||||
{
|
||||
if ($this->value->isEqualTo(BrickBigInteger::zero())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$temp = $this->value->toBase(16);
|
||||
$temp = 0 !== (mb_strlen($temp, '8bit') & 1) ? '0'.$temp : $temp;
|
||||
$temp = hex2bin($temp);
|
||||
|
||||
return ltrim($temp, chr(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two BigIntegers.
|
||||
*
|
||||
* @param BigInteger $y
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function add(self $y): self
|
||||
{
|
||||
$value = $this->value->plus($y->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts two BigIntegers.
|
||||
*
|
||||
* @param BigInteger $y
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function subtract(self $y): self
|
||||
{
|
||||
$value = $this->value->minus($y->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two BigIntegers.
|
||||
*
|
||||
* @param BigInteger $x
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function multiply(self $x): self
|
||||
{
|
||||
$value = $this->value->multipliedBy($x->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides two BigIntegers.
|
||||
*
|
||||
* @param BigInteger $x
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function divide(self $x): self
|
||||
{
|
||||
$value = $this->value->dividedBy($x->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs modular exponentiation.
|
||||
*
|
||||
* @param BigInteger $e
|
||||
* @param BigInteger $n
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function modPow(self $e, self $n): self
|
||||
{
|
||||
$value = $this->value->modPow($e->value, $n->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs modular exponentiation.
|
||||
*
|
||||
* @param BigInteger $d
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function mod(self $d): self
|
||||
{
|
||||
$value = $this->value->mod($d->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public function modInverse(BigInteger $m): BigInteger
|
||||
{
|
||||
return new self($this->value->modInverse($m->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two numbers.
|
||||
*
|
||||
* @param BigInteger $y
|
||||
*/
|
||||
public function compare(self $y): int
|
||||
{
|
||||
return $this->value->compareTo($y->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BigInteger $y
|
||||
*/
|
||||
public function equals(self $y): bool
|
||||
{
|
||||
return $this->value->isEqualTo($y->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BigInteger $y
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public static function random(self $y): self
|
||||
{
|
||||
return new self(BrickBigInteger::randomRange(0, $y->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BigInteger $y
|
||||
*
|
||||
* @return BigInteger
|
||||
*/
|
||||
public function gcd(self $y): self
|
||||
{
|
||||
return new self($this->value->gcd($y->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BigInteger $y
|
||||
*/
|
||||
public function lowerThan(self $y): bool
|
||||
{
|
||||
return $this->value->isLessThan($y->value);
|
||||
}
|
||||
|
||||
public function isEven(): bool
|
||||
{
|
||||
return $this->value->isEven();
|
||||
}
|
||||
|
||||
public function get(): BrickBigInteger
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util;
|
||||
|
||||
use Base64Url\Base64Url;
|
||||
use function extension_loaded;
|
||||
use InvalidArgumentException;
|
||||
use Jose\Component\Core\JWK;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ECKey
|
||||
{
|
||||
public static function convertToPEM(JWK $jwk): string
|
||||
{
|
||||
if ($jwk->has('d')) {
|
||||
return self::convertPrivateKeyToPEM($jwk);
|
||||
}
|
||||
|
||||
return self::convertPublicKeyToPEM($jwk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is not supported
|
||||
*/
|
||||
public static function convertPublicKeyToPEM(JWK $jwk): string
|
||||
{
|
||||
switch ($jwk->get('crv')) {
|
||||
case 'P-256':
|
||||
$der = self::p256PublicKey();
|
||||
|
||||
break;
|
||||
|
||||
case 'secp256k1':
|
||||
$der = self::p256KPublicKey();
|
||||
|
||||
break;
|
||||
|
||||
case 'P-384':
|
||||
$der = self::p384PublicKey();
|
||||
|
||||
break;
|
||||
|
||||
case 'P-521':
|
||||
$der = self::p521PublicKey();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Unsupported curve.');
|
||||
}
|
||||
$der .= self::getKey($jwk);
|
||||
$pem = '-----BEGIN PUBLIC KEY-----'.PHP_EOL;
|
||||
$pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
|
||||
$pem .= '-----END PUBLIC KEY-----'.PHP_EOL;
|
||||
|
||||
return $pem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is not supported
|
||||
*/
|
||||
public static function convertPrivateKeyToPEM(JWK $jwk): string
|
||||
{
|
||||
switch ($jwk->get('crv')) {
|
||||
case 'P-256':
|
||||
$der = self::p256PrivateKey($jwk);
|
||||
|
||||
break;
|
||||
|
||||
case 'secp256k1':
|
||||
$der = self::p256KPrivateKey($jwk);
|
||||
|
||||
break;
|
||||
|
||||
case 'P-384':
|
||||
$der = self::p384PrivateKey($jwk);
|
||||
|
||||
break;
|
||||
|
||||
case 'P-521':
|
||||
$der = self::p521PrivateKey($jwk);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Unsupported curve.');
|
||||
}
|
||||
$der .= self::getKey($jwk);
|
||||
$pem = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL;
|
||||
$pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
|
||||
$pem .= '-----END EC PRIVATE KEY-----'.PHP_EOL;
|
||||
|
||||
return $pem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a EC key with the given curve and additional values.
|
||||
*
|
||||
* @param string $curve The curve
|
||||
* @param array $values values to configure the key
|
||||
*/
|
||||
public static function createECKey(string $curve, array $values = []): JWK
|
||||
{
|
||||
$jwk = self::createECKeyUsingOpenSSL($curve);
|
||||
$values = array_merge($values, $jwk);
|
||||
|
||||
return new JWK($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is not supported
|
||||
*/
|
||||
private static function getNistCurveSize(string $curve): int
|
||||
{
|
||||
switch ($curve) {
|
||||
case 'P-256':
|
||||
case 'secp256k1':
|
||||
return 256;
|
||||
|
||||
case 'P-384':
|
||||
return 384;
|
||||
|
||||
case 'P-521':
|
||||
return 521;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the extension OpenSSL is not available
|
||||
* @throws RuntimeException if the key cannot be created
|
||||
*/
|
||||
private static function createECKeyUsingOpenSSL(string $curve): array
|
||||
{
|
||||
if (!extension_loaded('openssl')) {
|
||||
throw new RuntimeException('Please install the OpenSSL extension');
|
||||
}
|
||||
$key = openssl_pkey_new([
|
||||
'curve_name' => self::getOpensslCurveName($curve),
|
||||
'private_key_type' => OPENSSL_KEYTYPE_EC,
|
||||
]);
|
||||
if (false === $key) {
|
||||
throw new RuntimeException('Unable to create the key');
|
||||
}
|
||||
$result = openssl_pkey_export($key, $out);
|
||||
if (false === $result) {
|
||||
throw new RuntimeException('Unable to create the key');
|
||||
}
|
||||
$res = openssl_pkey_get_private($out);
|
||||
if (false === $res) {
|
||||
throw new RuntimeException('Unable to create the key');
|
||||
}
|
||||
$details = openssl_pkey_get_details($res);
|
||||
$nistCurveSize = self::getNistCurveSize($curve);
|
||||
|
||||
return [
|
||||
'kty' => 'EC',
|
||||
'crv' => $curve,
|
||||
'd' => Base64Url::encode(str_pad($details['ec']['d'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)),
|
||||
'x' => Base64Url::encode(str_pad($details['ec']['x'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)),
|
||||
'y' => Base64Url::encode(str_pad($details['ec']['y'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the curve is not supported
|
||||
*/
|
||||
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':
|
||||
return 'secp521r1';
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
|
||||
}
|
||||
}
|
||||
|
||||
private static function p256PublicKey(): string
|
||||
{
|
||||
return pack(
|
||||
'H*',
|
||||
'3059' // SEQUENCE, length 89
|
||||
.'3013' // SEQUENCE, length 19
|
||||
.'0607' // OID, length 7
|
||||
.'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
.'0608' // OID, length 8
|
||||
.'2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve
|
||||
.'0342' // BIT STRING, length 66
|
||||
.'00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
'H*',
|
||||
'3076' // SEQUENCE, length 118
|
||||
.'3010' // SEQUENCE, length 16
|
||||
.'0607' // OID, length 7
|
||||
.'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
.'0605' // OID, length 5
|
||||
.'2b81040022' // 1.3.132.0.34 = P-384 Curve
|
||||
.'0362' // BIT STRING, length 98
|
||||
.'00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p521PublicKey(): string
|
||||
{
|
||||
return pack(
|
||||
'H*',
|
||||
'30819b' // SEQUENCE, length 154
|
||||
.'3010' // SEQUENCE, length 16
|
||||
.'0607' // OID, length 7
|
||||
.'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
.'0605' // OID, length 5
|
||||
.'2b81040023' // 1.3.132.0.35 = P-521 Curve
|
||||
.'038186' // BIT STRING, length 134
|
||||
.'00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p256PrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT))[1];
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3077' // SEQUENCE, length 87+length($d)=32
|
||||
.'020101' // INTEGER, 1
|
||||
.'0420' // OCTET STRING, length($d) = 32
|
||||
.$d
|
||||
.'a00a' // TAGGED OBJECT #0, length 10
|
||||
.'0608' // OID, length 8
|
||||
.'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
|
||||
);
|
||||
}
|
||||
|
||||
private static function p384PrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 48, "\0", STR_PAD_LEFT))[1];
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3081a4' // SEQUENCE, length 116 + length($d)=48
|
||||
.'020101' // INTEGER, 1
|
||||
.'0430' // OCTET STRING, length($d) = 30
|
||||
.$d
|
||||
.'a007' // TAGGED OBJECT #0, length 7
|
||||
.'0605' // OID, length 5
|
||||
.'2b81040022' // 1.3.132.0.34 = P-384 Curve
|
||||
.'a164' // TAGGED OBJECT #1, length 100
|
||||
.'0362' // BIT STRING, length 98
|
||||
.'00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p521PrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 66, "\0", STR_PAD_LEFT))[1];
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3081dc' // SEQUENCE, length 154 + length($d)=66
|
||||
.'020101' // INTEGER, 1
|
||||
.'0442' // OCTET STRING, length(d) = 66
|
||||
.$d
|
||||
.'a007' // TAGGED OBJECT #0, length 7
|
||||
.'0605' // OID, length 5
|
||||
.'2b81040023' // 1.3.132.0.35 = P-521 Curve
|
||||
.'a18189' // TAGGED OBJECT #1, length 137
|
||||
.'038186' // BIT STRING, length 134
|
||||
.'00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function getKey(JWK $jwk): string
|
||||
{
|
||||
$nistCurveSize = self::getNistCurveSize($jwk->get('crv'));
|
||||
$length = (int) ceil($nistCurveSize / 8);
|
||||
|
||||
return
|
||||
"\04"
|
||||
.str_pad(Base64Url::decode($jwk->get('x')), $length, "\0", STR_PAD_LEFT)
|
||||
.str_pad(Base64Url::decode($jwk->get('y')), $length, "\0", STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ECSignature
|
||||
{
|
||||
private const ASN1_SEQUENCE = '30';
|
||||
private const ASN1_INTEGER = '02';
|
||||
private const ASN1_MAX_SINGLE_BYTE = 128;
|
||||
private const ASN1_LENGTH_2BYTES = '81';
|
||||
private const ASN1_BIG_INTEGER_LIMIT = '7f';
|
||||
private const ASN1_NEGATIVE_INTEGER = '00';
|
||||
private const BYTE_SIZE = 2;
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the length of the signature is invalid
|
||||
*/
|
||||
public static function toAsn1(string $signature, int $length): string
|
||||
{
|
||||
$signature = bin2hex($signature);
|
||||
|
||||
if (self::octetLength($signature) !== $length) {
|
||||
throw new InvalidArgumentException('Invalid signature length.');
|
||||
}
|
||||
|
||||
$pointR = self::preparePositiveInteger(mb_substr($signature, 0, $length, '8bit'));
|
||||
$pointS = self::preparePositiveInteger(mb_substr($signature, $length, null, '8bit'));
|
||||
|
||||
$lengthR = self::octetLength($pointR);
|
||||
$lengthS = self::octetLength($pointS);
|
||||
|
||||
$totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
|
||||
$lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
|
||||
|
||||
return hex2bin(
|
||||
self::ASN1_SEQUENCE
|
||||
.$lengthPrefix.dechex($totalLength)
|
||||
.self::ASN1_INTEGER.dechex($lengthR).$pointR
|
||||
.self::ASN1_INTEGER.dechex($lengthS).$pointS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the signature is not an ASN.1 sequence
|
||||
*/
|
||||
public static function fromAsn1(string $signature, int $length): string
|
||||
{
|
||||
$message = bin2hex($signature);
|
||||
$position = 0;
|
||||
|
||||
if (self::ASN1_SEQUENCE !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
|
||||
throw new InvalidArgumentException('Invalid data. Should start with a sequence.');
|
||||
}
|
||||
|
||||
if (self::ASN1_LENGTH_2BYTES === self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
|
||||
$position += self::BYTE_SIZE;
|
||||
}
|
||||
|
||||
$pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
|
||||
$pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
|
||||
|
||||
return hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT).str_pad($pointS, $length, '0', STR_PAD_LEFT));
|
||||
}
|
||||
|
||||
private static function octetLength(string $data): int
|
||||
{
|
||||
return (int) (mb_strlen($data, '8bit') / self::BYTE_SIZE);
|
||||
}
|
||||
|
||||
private static function preparePositiveInteger(string $data): string
|
||||
{
|
||||
if (mb_substr($data, 0, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
|
||||
return self::ASN1_NEGATIVE_INTEGER.$data;
|
||||
}
|
||||
|
||||
while (0 === mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit')
|
||||
&& mb_substr($data, 2, self::BYTE_SIZE, '8bit') <= self::ASN1_BIG_INTEGER_LIMIT) {
|
||||
$data = mb_substr($data, 2, null, '8bit');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function readAsn1Content(string $message, int &$position, int $length): string
|
||||
{
|
||||
$content = mb_substr($message, $position, $length, '8bit');
|
||||
$position += $length;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the data is not an integer
|
||||
*/
|
||||
private static function readAsn1Integer(string $message, int &$position): string
|
||||
{
|
||||
if (self::ASN1_INTEGER !== self::readAsn1Content($message, $position, self::BYTE_SIZE)) {
|
||||
throw new InvalidArgumentException('Invalid data. Should contain an integer.');
|
||||
}
|
||||
|
||||
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
|
||||
|
||||
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
|
||||
}
|
||||
|
||||
private static function retrievePositiveInteger(string $data): string
|
||||
{
|
||||
while (0 === mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit')
|
||||
&& mb_substr($data, 2, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
|
||||
$data = mb_substr($data, 2, null, '8bit');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
+1
@@ -0,0 +1 @@
|
||||
patreon: FlorentMorselli
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
|
||||
You should submit it here: https://github.com/web-token/jwt-framework/pulls
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\Algorithm as JoseAlgorithm;
|
||||
use Jose\Component\Signature\Algorithm;
|
||||
|
||||
abstract class AbstractBuilder
|
||||
{
|
||||
/**
|
||||
* @var JWT
|
||||
*/
|
||||
protected $jwt;
|
||||
|
||||
/**
|
||||
* @var JoseAlgorithm[]
|
||||
*/
|
||||
protected $algorithms = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->jwt = new JWT();
|
||||
$this->algorithms = (new AlgorithmProvider($this->getAlgorithmMap()))
|
||||
->getAvailableAlgorithms()
|
||||
;
|
||||
}
|
||||
|
||||
public function payload(array $payload): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->jwt->claims->replace($payload);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function iss(string $iss, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('iss', $iss, $inHeader);
|
||||
}
|
||||
|
||||
public function sub(string $sub, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('sub', $sub, $inHeader);
|
||||
}
|
||||
|
||||
public function aud(string $aud, bool $inHeader = false): self
|
||||
{
|
||||
$audience = $this->jwt->claims->has('aud') ? $this->jwt->claims->get('aud') : [];
|
||||
$audience[] = $aud;
|
||||
|
||||
return $this->claim('aud', $audience, $inHeader);
|
||||
}
|
||||
|
||||
public function jti(string $jti, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('jti', $jti, $inHeader);
|
||||
}
|
||||
|
||||
public function exp(int $exp, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('exp', $exp, $inHeader);
|
||||
}
|
||||
|
||||
public function iat(?int $iat = null, bool $inHeader = false): self
|
||||
{
|
||||
$iat = $iat ?? time();
|
||||
|
||||
return $this->claim('iat', $iat, $inHeader);
|
||||
}
|
||||
|
||||
public function nbf(?int $nbf = null, bool $inHeader = false): self
|
||||
{
|
||||
$nbf = $nbf ?? time();
|
||||
|
||||
return $this->claim('nbf', $nbf, $inHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Algorithm\SignatureAlgorithm|string $alg
|
||||
*
|
||||
* @throws InvalidArgumentException if the algorithm is not a string or an instance of Jose\Component\Core\Algorithm
|
||||
*/
|
||||
public function alg($alg): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
switch (true) {
|
||||
case $alg instanceof JoseAlgorithm:
|
||||
$clone->algorithms[] = $alg;
|
||||
$clone->jwt->header->set('alg', $alg->name());
|
||||
|
||||
break;
|
||||
|
||||
case is_string($alg):
|
||||
$clone->jwt->header->set('alg', $alg);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid parameter "alg". Shall be a string or an algorithm instance.');
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function cty(string $cty): self
|
||||
{
|
||||
return $this->header('cty', $cty);
|
||||
}
|
||||
|
||||
public function typ(string $typ): self
|
||||
{
|
||||
return $this->header('typ', $typ);
|
||||
}
|
||||
|
||||
public function crit(array $crit): self
|
||||
{
|
||||
return $this->header('crit', $crit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function claim(string $key, $value, bool $inHeader = false): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->jwt->claims->set($key, $value);
|
||||
if ($inHeader) {
|
||||
$clone->jwt->header->set($key, $value);
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function header(string $key, $value): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->jwt->header->set($key, $value);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
abstract protected function getAlgorithmMap(): array;
|
||||
}
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use function in_array;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
use Jose\Component\Checker;
|
||||
use Jose\Component\Core\Algorithm;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\JWKSet;
|
||||
|
||||
abstract class AbstractLoader
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* @var JWKSet
|
||||
*/
|
||||
protected $jwkset;
|
||||
|
||||
/**
|
||||
* @var Checker\HeaderChecker[]
|
||||
*/
|
||||
protected $headerCheckers = [];
|
||||
|
||||
/**
|
||||
* @var Checker\ClaimChecker[]
|
||||
*/
|
||||
protected $claimCheckers = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allowedAlgorithms = [];
|
||||
|
||||
/**
|
||||
* @var Algorithm[]
|
||||
*/
|
||||
protected $algorithms = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $mandatoryClaims = [];
|
||||
|
||||
protected function __construct(string $token)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->jwkset = new JWKSet([]);
|
||||
$this->claimCheckers = [];
|
||||
|
||||
$this->algorithms = (new AlgorithmProvider($this->getAlgorithmMap()))
|
||||
->getAvailableAlgorithms()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $mandatoryClaims
|
||||
*/
|
||||
public function mandatory(array $mandatoryClaims): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->mandatoryClaims = $mandatoryClaims;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function aud(string $aud, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('aud', new Checker\AudienceChecker($aud, true), $inHeader);
|
||||
}
|
||||
|
||||
public function iss(string $iss, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('iss', new Checker\IssuerChecker([$iss], true), $inHeader);
|
||||
}
|
||||
|
||||
public function jti(string $jti, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('jti', $jti, $inHeader);
|
||||
}
|
||||
|
||||
public function sub(string $sub, bool $inHeader = false): self
|
||||
{
|
||||
return $this->claim('sub', $sub, $inHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|array|callable|Checker\ClaimChecker $checker
|
||||
*/
|
||||
public function claim(string $key, $checker, bool $inHeader = false): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
if (false === $checker) {
|
||||
unset($clone->claimCheckers[$key]);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $checker instanceof Checker\ClaimChecker:
|
||||
break;
|
||||
|
||||
case is_callable($checker):
|
||||
$checker = new CallableChecker($key, $checker);
|
||||
|
||||
break;
|
||||
|
||||
case is_array($checker):
|
||||
$checker = new CallableChecker($key, static function ($value) use ($checker): bool {return in_array($value, $checker, true); });
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$checker = new CallableChecker($key, static function ($value) use ($checker): bool {return $value === $checker; });
|
||||
}
|
||||
|
||||
$clone->claimCheckers[$key] = $checker;
|
||||
if ($inHeader) {
|
||||
return $clone->header($key, $checker);
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param false|int $leeway
|
||||
*
|
||||
* @throws InvalidArgumentException if the leeway is negative, not an integer or not false
|
||||
*/
|
||||
public function exp($leeway = 0, bool $inHeader = false): self
|
||||
{
|
||||
if (false === $leeway) {
|
||||
$clone = clone $this;
|
||||
unset($clone->claimCheckers['exp']);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
if (!is_int($leeway) or $leeway < 0) {
|
||||
throw new InvalidArgumentException('First parameter for "exp" claim is invalid. Set false to disable or a positive integer.');
|
||||
}
|
||||
|
||||
return $this->claim('exp', new Checker\ExpirationTimeChecker($leeway), $inHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param false|int $leeway
|
||||
*
|
||||
* @throws InvalidArgumentException if the leeway is negative, not an integer or not false
|
||||
*/
|
||||
public function nbf($leeway = 0, bool $inHeader = false): self
|
||||
{
|
||||
if (false === $leeway) {
|
||||
$clone = clone $this;
|
||||
unset($clone->claimCheckers['nbf']);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
if (!is_int($leeway) or $leeway < 0) {
|
||||
throw new InvalidArgumentException('First parameter for "nbf" claim is invalid. Set false to disable or a positive integer.');
|
||||
}
|
||||
|
||||
return $this->claim('nbf', new Checker\NotBeforeChecker($leeway, true), $inHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param false|int $leeway
|
||||
*
|
||||
* @throws InvalidArgumentException if the leeway is negative, not an integer or not false
|
||||
*/
|
||||
public function iat($leeway = 0, bool $inHeader = false): self
|
||||
{
|
||||
if (false === $leeway) {
|
||||
$clone = clone $this;
|
||||
unset($clone->claimCheckers['iat']);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
if (!is_int($leeway) or $leeway < 0) {
|
||||
throw new InvalidArgumentException('First parameter for "iat" claim is invalid. Set false to disable or a positive integer.');
|
||||
}
|
||||
|
||||
return $this->claim('iat', new Checker\IssuedAtChecker($leeway, true), $inHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Algorithm|string $alg
|
||||
*
|
||||
* @throws InvalidArgumentException if the algorithm is not a string or an instance of Jose\Component\Core\Algorithm
|
||||
*/
|
||||
public function alg($alg): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
switch (true) {
|
||||
case is_string($alg):
|
||||
$clone->allowedAlgorithms[] = $alg;
|
||||
|
||||
return $clone;
|
||||
|
||||
case $alg instanceof Algorithm:
|
||||
$clone->algorithms[$alg->name()] = $alg;
|
||||
$clone->allowedAlgorithms[] = $alg->name();
|
||||
|
||||
return $clone;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid parameter "alg". Shall be a string or an algorithm instance.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Algorithm[]|string[] $algs
|
||||
*/
|
||||
public function algs($algs): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
foreach ($algs as $alg) {
|
||||
$clone = $clone->alg($alg);
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|callable|Checker\HeaderChecker|false|mixed $checker
|
||||
*/
|
||||
public function header(string $key, $checker): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
if (false === $checker) {
|
||||
unset($clone->headerCheckers[$key]);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $checker instanceof Checker\HeaderChecker:
|
||||
break;
|
||||
|
||||
case is_callable($checker):
|
||||
$checker = new CallableChecker($key, $checker);
|
||||
|
||||
break;
|
||||
|
||||
case is_array($checker):
|
||||
$checker = new CallableChecker($key, static function ($value) use ($checker): bool {return in_array($value, $checker, true); });
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$checker = new CallableChecker($key, static function ($value) use ($checker): bool {return $value === $checker; });
|
||||
}
|
||||
|
||||
$clone->headerCheckers[$key] = $checker;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function key(JWK $jwk): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$jwkset = $this->jwkset->with($jwk);
|
||||
$clone->jwkset = $jwkset;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function keyset(JWKSet $jwkset): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->jwkset = $jwkset;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
abstract protected function getAlgorithmMap(): array;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use Jose\Component\Core\Algorithm;
|
||||
use Throwable;
|
||||
|
||||
final class AlgorithmProvider
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $algorithmClasses;
|
||||
|
||||
/**
|
||||
* @var Algorithm[]
|
||||
*/
|
||||
private $algorithms = [];
|
||||
|
||||
public function __construct(array $algorithmClasses)
|
||||
{
|
||||
$this->algorithmClasses = $algorithmClasses;
|
||||
foreach ($algorithmClasses as $algorithmClass) {
|
||||
$this->addClass($algorithmClass);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAlgorithmClasses(): array
|
||||
{
|
||||
return $this->algorithmClasses;
|
||||
}
|
||||
|
||||
public function getAvailableAlgorithms(): array
|
||||
{
|
||||
return $this->algorithms;
|
||||
}
|
||||
|
||||
private function addClass(string $algorithmClass): void
|
||||
{
|
||||
if (class_exists($algorithmClass)) {
|
||||
try {
|
||||
$this->algorithms[] = new $algorithmClass();
|
||||
} catch (Throwable $throwable) {
|
||||
//does nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
class Build
|
||||
{
|
||||
public static function jws(): JWSBuilder
|
||||
{
|
||||
return new JWSBuilder();
|
||||
}
|
||||
|
||||
public static function jwe(): JWEBuilder
|
||||
{
|
||||
return new JWEBuilder();
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use Jose\Component\Checker\ClaimChecker;
|
||||
use Jose\Component\Checker\HeaderChecker;
|
||||
use Jose\Component\Checker\InvalidClaimException;
|
||||
use Jose\Component\Checker\InvalidHeaderException;
|
||||
|
||||
final class CallableChecker implements ClaimChecker, HeaderChecker
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $callable;
|
||||
|
||||
public function __construct(string $key, callable $callable)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->callable = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidClaimException if the claim is invalid
|
||||
*/
|
||||
public function checkClaim($value): void
|
||||
{
|
||||
$callable = $this->callable;
|
||||
$isValid = $callable($value);
|
||||
if (!$isValid) {
|
||||
throw new InvalidClaimException(sprintf('Invalid claim "%s"', $this->key), $this->key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedClaim(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
$callable = $this->callable;
|
||||
$isValid = $callable($value);
|
||||
if (!$isValid) {
|
||||
throw new InvalidHeaderException(sprintf('Invalid header "%s"', $this->key), $this->key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Checker\HeaderChecker;
|
||||
use Jose\Component\Checker\InvalidHeaderException;
|
||||
|
||||
/**
|
||||
* This class is a header parameter checker.
|
||||
* When the "enc" header parameter is present, it will check if the value is within the allowed ones.
|
||||
*/
|
||||
final class ContentEncryptionAlgorithmChecker implements HeaderChecker
|
||||
{
|
||||
private const HEADER_NAME = 'enc';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $protectedHeader = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $supportedAlgorithms;
|
||||
|
||||
/**
|
||||
* @param string[] $supportedAlgorithms
|
||||
*/
|
||||
public function __construct(array $supportedAlgorithms, bool $protectedHeader = false)
|
||||
{
|
||||
$this->supportedAlgorithms = $supportedAlgorithms;
|
||||
$this->protectedHeader = $protectedHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws InvalidHeaderException if the header is invalid
|
||||
*/
|
||||
public function checkHeader($value): void
|
||||
{
|
||||
if (!is_string($value)) {
|
||||
throw new InvalidHeaderException('"enc" must be a string.', self::HEADER_NAME, $value);
|
||||
}
|
||||
if (!in_array($value, $this->supportedAlgorithms, true)) {
|
||||
throw new InvalidHeaderException('Unsupported algorithm.', self::HEADER_NAME, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function supportedHeader(): string
|
||||
{
|
||||
return self::HEADER_NAME;
|
||||
}
|
||||
|
||||
public function protectedHeaderOnly(): bool
|
||||
{
|
||||
return $this->protectedHeader;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use function count;
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\Checker;
|
||||
use Jose\Component\Core\Algorithm;
|
||||
use Jose\Component\Core\AlgorithmManager;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\Encryption\Algorithm\ContentEncryption;
|
||||
use Jose\Component\Encryption\Algorithm\KeyEncryption;
|
||||
use Jose\Component\Encryption\Compression\CompressionMethod;
|
||||
use Jose\Component\Encryption\Compression\CompressionMethodManager;
|
||||
use Jose\Component\Encryption\Compression\Deflate;
|
||||
use Jose\Component\Encryption\JWEDecrypter;
|
||||
use Jose\Component\Encryption\JWETokenSupport;
|
||||
use Jose\Component\Encryption\Serializer\CompactSerializer;
|
||||
|
||||
class Decrypt extends AbstractLoader
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allowedContentEncryptionAlgorithms = [];
|
||||
/**
|
||||
* @var CompressionMethod[]
|
||||
*/
|
||||
private $compressionMethods;
|
||||
|
||||
private function __construct(string $token)
|
||||
{
|
||||
parent::__construct($token);
|
||||
$this->compressionMethods = [
|
||||
new Deflate(),
|
||||
];
|
||||
}
|
||||
|
||||
public static function token(string $token): self
|
||||
{
|
||||
return new self($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Algorithm|string $enc
|
||||
*
|
||||
* @throws InvalidArgumentException if the encryption algorithm is invalid
|
||||
*/
|
||||
public function enc($enc): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
switch (true) {
|
||||
case is_string($enc):
|
||||
$clone->allowedContentEncryptionAlgorithms[] = $enc;
|
||||
|
||||
return $clone;
|
||||
|
||||
case $enc instanceof Algorithm:
|
||||
$clone->algorithms[$enc->name()] = $enc;
|
||||
$clone->allowedContentEncryptionAlgorithms[] = $enc->name();
|
||||
|
||||
return $clone;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid parameter "enc". Shall be a string or an algorithm instance.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Algorithm[]|string[] $encs
|
||||
*/
|
||||
public function encs($encs): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
foreach ($encs as $enc) {
|
||||
$clone = $clone->enc($enc);
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function run(): JWT
|
||||
{
|
||||
if (0 !== count($this->allowedAlgorithms)) {
|
||||
$this->headerCheckers[] = new Checker\AlgorithmChecker($this->allowedAlgorithms, true);
|
||||
}
|
||||
if (0 !== count($this->allowedContentEncryptionAlgorithms)) {
|
||||
$this->headerCheckers[] = new ContentEncryptionAlgorithmChecker($this->allowedContentEncryptionAlgorithms, true);
|
||||
}
|
||||
$jwe = (new CompactSerializer())->unserialize($this->token);
|
||||
$headerChecker = new Checker\HeaderCheckerManager($this->headerCheckers, [new JWETokenSupport()]);
|
||||
$headerChecker->check($jwe, 0);
|
||||
|
||||
$verifier = new JWEDecrypter(
|
||||
new AlgorithmManager($this->algorithms),
|
||||
new AlgorithmManager($this->algorithms),
|
||||
new CompressionMethodManager($this->compressionMethods)
|
||||
);
|
||||
$verifier->decryptUsingKeySet($jwe, $this->jwkset, 0);
|
||||
|
||||
$jwt = new JWT();
|
||||
$jwt->header->replace($jwe->getSharedProtectedHeader());
|
||||
$jwt->claims->replace(JsonConverter::decode($jwe->getPayload()));
|
||||
|
||||
$claimChecker = new Checker\ClaimCheckerManager($this->claimCheckers);
|
||||
$claimChecker->check($jwt->claims->all(), $this->mandatoryClaims);
|
||||
|
||||
return $jwt;
|
||||
}
|
||||
|
||||
protected function getAlgorithmMap(): array
|
||||
{
|
||||
return [
|
||||
KeyEncryption\A128GCMKW::class,
|
||||
KeyEncryption\A192GCMKW::class,
|
||||
KeyEncryption\A256GCMKW::class,
|
||||
KeyEncryption\A128KW::class,
|
||||
KeyEncryption\A192KW::class,
|
||||
KeyEncryption\A256KW::class,
|
||||
KeyEncryption\Dir::class,
|
||||
KeyEncryption\ECDHES::class,
|
||||
KeyEncryption\ECDHESA128KW::class,
|
||||
KeyEncryption\ECDHESA192KW::class,
|
||||
KeyEncryption\ECDHESA256KW::class,
|
||||
KeyEncryption\PBES2HS256A128KW::class,
|
||||
KeyEncryption\PBES2HS384A192KW::class,
|
||||
KeyEncryption\PBES2HS512A256KW::class,
|
||||
KeyEncryption\RSA15::class,
|
||||
KeyEncryption\RSAOAEP::class,
|
||||
KeyEncryption\RSAOAEP256::class,
|
||||
ContentEncryption\A128GCM::class,
|
||||
ContentEncryption\A192GCM::class,
|
||||
ContentEncryption\A256GCM::class,
|
||||
ContentEncryption\A128CBCHS256::class,
|
||||
ContentEncryption\A192CBCHS384::class,
|
||||
ContentEncryption\A256CBCHS512::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\Algorithm;
|
||||
use Jose\Component\Core\AlgorithmManager;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\Encryption\Algorithm\ContentEncryption;
|
||||
use Jose\Component\Encryption\Algorithm\KeyEncryption;
|
||||
use Jose\Component\Encryption\Compression\CompressionMethod;
|
||||
use Jose\Component\Encryption\Compression\CompressionMethodManager;
|
||||
use Jose\Component\Encryption\Compression\Deflate;
|
||||
use Jose\Component\Encryption\JWEBuilder as JoseBuilder;
|
||||
use Jose\Component\Encryption\Serializer\CompactSerializer;
|
||||
|
||||
class JWEBuilder extends AbstractBuilder
|
||||
{
|
||||
/**
|
||||
* @var CompressionMethod[]
|
||||
*/
|
||||
private $compressionMethods;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->compressionMethods = [
|
||||
new Deflate(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Algorithm|string $enc
|
||||
*
|
||||
* @throws InvalidArgumentException if the header parameter "enc" is invalid
|
||||
*/
|
||||
public function enc($enc): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
switch (true) {
|
||||
case $enc instanceof Algorithm:
|
||||
$clone->algorithms[] = $enc;
|
||||
$clone->jwt->header->set('enc', $enc->name());
|
||||
|
||||
break;
|
||||
|
||||
case is_string($enc):
|
||||
$clone->jwt->header->set('enc', $enc);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid algorithm');
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CompressionMethod|string $zip
|
||||
*
|
||||
* @throws InvalidArgumentException if the header parameter "zip" is invalid
|
||||
*/
|
||||
public function zip($zip): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
switch (true) {
|
||||
case $zip instanceof CompressionMethod:
|
||||
$clone->compressionMethods[] = $zip;
|
||||
$clone->jwt->header->set('zip', $zip->name());
|
||||
|
||||
break;
|
||||
|
||||
case is_string($zip):
|
||||
$clone->jwt->header->set('zip', $zip);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid compression method');
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function encrypt(JWK $jwk): string
|
||||
{
|
||||
$builder = new JoseBuilder(
|
||||
new AlgorithmManager($this->algorithms),
|
||||
new AlgorithmManager($this->algorithms),
|
||||
new CompressionMethodManager($this->compressionMethods)
|
||||
);
|
||||
$jwe = $builder
|
||||
->create()
|
||||
->withPayload(JsonConverter::encode($this->jwt->claims->all()))
|
||||
->withSharedProtectedHeader($this->jwt->header->all())
|
||||
->addRecipient($jwk)
|
||||
->build()
|
||||
;
|
||||
|
||||
return (new CompactSerializer())->serialize($jwe);
|
||||
}
|
||||
|
||||
protected function getAlgorithmMap(): array
|
||||
{
|
||||
return [
|
||||
KeyEncryption\A128GCMKW::class,
|
||||
KeyEncryption\A192GCMKW::class,
|
||||
KeyEncryption\A256GCMKW::class,
|
||||
KeyEncryption\A128KW::class,
|
||||
KeyEncryption\A192KW::class,
|
||||
KeyEncryption\A256KW::class,
|
||||
KeyEncryption\Dir::class,
|
||||
KeyEncryption\ECDHES::class,
|
||||
KeyEncryption\ECDHESA128KW::class,
|
||||
KeyEncryption\ECDHESA192KW::class,
|
||||
KeyEncryption\ECDHESA256KW::class,
|
||||
KeyEncryption\PBES2HS256A128KW::class,
|
||||
KeyEncryption\PBES2HS384A192KW::class,
|
||||
KeyEncryption\PBES2HS512A256KW::class,
|
||||
KeyEncryption\RSA15::class,
|
||||
KeyEncryption\RSAOAEP::class,
|
||||
KeyEncryption\RSAOAEP256::class,
|
||||
ContentEncryption\A128GCM::class,
|
||||
ContentEncryption\A192GCM::class,
|
||||
ContentEncryption\A256GCM::class,
|
||||
ContentEncryption\A128CBCHS256::class,
|
||||
ContentEncryption\A192CBCHS384::class,
|
||||
ContentEncryption\A256CBCHS512::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use Jose\Component\Core\AlgorithmManager;
|
||||
use Jose\Component\Core\JWK;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\Signature\Algorithm;
|
||||
use Jose\Component\Signature\JWSBuilder as JoseBuilder;
|
||||
use Jose\Component\Signature\Serializer\CompactSerializer;
|
||||
|
||||
class JWSBuilder extends AbstractBuilder
|
||||
{
|
||||
public function sign(JWK $jwk): string
|
||||
{
|
||||
$builder = new JoseBuilder(new AlgorithmManager($this->algorithms));
|
||||
$jws = $builder
|
||||
->create()
|
||||
->withPayload(JsonConverter::encode($this->jwt->claims->all()))
|
||||
->addSignature($jwk, $this->jwt->header->all())
|
||||
->build()
|
||||
;
|
||||
|
||||
return (new CompactSerializer())->serialize($jws);
|
||||
}
|
||||
|
||||
protected function getAlgorithmMap(): array
|
||||
{
|
||||
return [
|
||||
Algorithm\HS256::class,
|
||||
Algorithm\HS384::class,
|
||||
Algorithm\HS512::class,
|
||||
Algorithm\RS256::class,
|
||||
Algorithm\RS384::class,
|
||||
Algorithm\RS512::class,
|
||||
Algorithm\PS256::class,
|
||||
Algorithm\PS384::class,
|
||||
Algorithm\PS512::class,
|
||||
Algorithm\ES256::class,
|
||||
Algorithm\ES384::class,
|
||||
Algorithm\ES512::class,
|
||||
Algorithm\EdDSA::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
final class JWT
|
||||
{
|
||||
/**
|
||||
* @var ParameterBag
|
||||
*/
|
||||
public $claims;
|
||||
|
||||
/**
|
||||
* @var ParameterBag
|
||||
*/
|
||||
public $header;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->claims = new ParameterBag();
|
||||
$this->header = new ParameterBag();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 Spomky-Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
class Load
|
||||
{
|
||||
public static function jws(string $jws): Validate
|
||||
{
|
||||
return Validate::token($jws);
|
||||
}
|
||||
|
||||
public static function jwe(string $jwe): Decrypt
|
||||
{
|
||||
return Decrypt::token($jwe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use function array_key_exists;
|
||||
use ArrayIterator;
|
||||
use function call_user_func_array;
|
||||
use function count;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
|
||||
class ParameterBag implements IteratorAggregate, Countable
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $parameters = [];
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
if (method_exists($this, $name)) {
|
||||
return call_user_func_array([$this, $name], $arguments);
|
||||
}
|
||||
|
||||
if (0 === count($arguments)) {
|
||||
return $this->get($name);
|
||||
}
|
||||
array_unshift($arguments, $name);
|
||||
|
||||
return call_user_func_array([$this, 'set'], $arguments);
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function keys(): array
|
||||
{
|
||||
return array_keys($this->parameters);
|
||||
}
|
||||
|
||||
public function replace(array $parameters): void
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the parameters are invalid
|
||||
*/
|
||||
public function add(array $parameters): void
|
||||
{
|
||||
$replaced = array_replace($this->parameters, $parameters);
|
||||
if (null === $replaced) {
|
||||
throw new InvalidArgumentException('Invalid parameters');
|
||||
}
|
||||
$this->parameters = $replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if the selected parameter is missing
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key)
|
||||
{
|
||||
if (!array_key_exists($key, $this->parameters)) {
|
||||
throw new InvalidArgumentException(sprintf('Parameter "%s" is missing', $key));
|
||||
}
|
||||
|
||||
return $this->parameters[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value The value
|
||||
*/
|
||||
public function set(string $key, $value): void
|
||||
{
|
||||
$this->parameters[$key] = $value;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->parameters);
|
||||
}
|
||||
|
||||
public function remove(string $key): void
|
||||
{
|
||||
unset($this->parameters[$key]);
|
||||
}
|
||||
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->parameters);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Easy Toolset For JWT-Framework
|
||||
===============================
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
|
||||
**Please do not submit any Pull Request here.**
|
||||
You should go to [the main repository](https://github.com/web-token/jwt-framework) instead.
|
||||
|
||||
# Documentation
|
||||
|
||||
The official documentation is available as https://web-token.spomky-labs.com/
|
||||
|
||||
# Licence
|
||||
|
||||
This software is release under [MIT licence](LICENSE).
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Jose\Easy;
|
||||
|
||||
use function count;
|
||||
use Exception;
|
||||
use Jose\Component\Checker;
|
||||
use Jose\Component\Core\AlgorithmManager;
|
||||
use Jose\Component\Core\Util\JsonConverter;
|
||||
use Jose\Component\Signature\Algorithm;
|
||||
use Jose\Component\Signature\JWSTokenSupport;
|
||||
use Jose\Component\Signature\JWSVerifier;
|
||||
use Jose\Component\Signature\Serializer\CompactSerializer;
|
||||
|
||||
class Validate extends AbstractLoader
|
||||
{
|
||||
public static function token(string $token): self
|
||||
{
|
||||
return new self($token);
|
||||
}
|
||||
|
||||
public function run(): JWT
|
||||
{
|
||||
if (0 !== count($this->allowedAlgorithms)) {
|
||||
$this->headerCheckers[] = new Checker\AlgorithmChecker($this->allowedAlgorithms, true);
|
||||
}
|
||||
$jws = (new CompactSerializer())->unserialize($this->token);
|
||||
$headerChecker = new Checker\HeaderCheckerManager($this->headerCheckers, [new JWSTokenSupport()]);
|
||||
$headerChecker->check($jws, 0);
|
||||
|
||||
$verifier = new JWSVerifier(new AlgorithmManager($this->algorithms));
|
||||
if (!$verifier->verifyWithKeySet($jws, $this->jwkset, 0)) {
|
||||
throw new Exception('Invalid signature');
|
||||
}
|
||||
|
||||
$jwt = new JWT();
|
||||
$jwt->header->replace($jws->getSignature(0)->getProtectedHeader());
|
||||
$jwt->claims->replace(JsonConverter::decode($jws->getPayload()));
|
||||
|
||||
$claimChecker = new Checker\ClaimCheckerManager($this->claimCheckers);
|
||||
$claimChecker->check($jwt->claims->all(), $this->mandatoryClaims);
|
||||
|
||||
return $jwt;
|
||||
}
|
||||
|
||||
protected function getAlgorithmMap(): array
|
||||
{
|
||||
return [
|
||||
Algorithm\HS256::class,
|
||||
Algorithm\HS384::class,
|
||||
Algorithm\HS512::class,
|
||||
Algorithm\RS256::class,
|
||||
Algorithm\RS384::class,
|
||||
Algorithm\RS512::class,
|
||||
Algorithm\PS256::class,
|
||||
Algorithm\PS384::class,
|
||||
Algorithm\PS512::class,
|
||||
Algorithm\ES256::class,
|
||||
Algorithm\ES384::class,
|
||||
Algorithm\ES512::class,
|
||||
Algorithm\EdDSA::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "web-token/jwt-easy",
|
||||
"description": "Easy toolset to use the JWT Framework.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"],
|
||||
"homepage": "https://github.com/web-token",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Florent Morselli",
|
||||
"homepage": "https://github.com/Spomky"
|
||||
},{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/web-token/jwt-framework/contributors"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jose\\Easy\\": ""
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"web-token/jwt-encryption": "^2.1",
|
||||
"web-token/jwt-signature": "^2.1",
|
||||
"web-token/jwt-checker": "^2.1"
|
||||
},
|
||||
"suggest": {
|
||||
"web-token/jwt-encryption-algorithm-aescbc": "Adds AES-CBC based encryption algorithms",
|
||||
"web-token/jwt-encryption-algorithm-aesgcm": "Adds AES-GCM based encryption algorithms",
|
||||
"web-token/jwt-encryption-algorithm-aesgcmkw": "Adds AES-GCM Key Wrapping based encryption algorithms",
|
||||
"web-token/jwt-encryption-algorithm-aeskw": "Adds AES Key Wrapping based encryption algorithms",
|
||||
"web-token/jwt-encryption-algorithm-dir": "Adds Direct encryption algorithm",
|
||||
"web-token/jwt-encryption-algorithm-ecdh-es": "Adds ECDH-ES based encryption algorithms",
|
||||
"web-token/jwt-encryption-algorithm-pbes2": "Adds PBES2 based encryption algorithms",
|
||||
"web-token/jwt-encryption-algorithm-rsa": "Adds RSA based encryption algorithms",
|
||||
"web-token/jwt-signature-algorithm-ecdsa": "Adds ECDSA based signature algorithms",
|
||||
"web-token/jwt-signature-algorithm-eddsa": "Adds EdDSA based signature algorithms",
|
||||
"web-token/jwt-signature-algorithm-none": "Adds none signature algorithms",
|
||||
"web-token/jwt-signature-algorithm-hmac": "Adds HMAC based signature algorithms",
|
||||
"web-token/jwt-signature-algorithm-rsa": "Adds RSA based signature algorithms"
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
@@ -0,0 +1 @@
|
||||
patreon: FlorentMorselli
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Please do not submit any Pull Requests here. It will be automatically closed.
|
||||
|
||||
You should submit it here: https://github.com/web-token/jwt-framework/pulls
|
||||
@@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util\Ecc;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function is_null;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Curve
|
||||
{
|
||||
/**
|
||||
* Elliptic curve over the field of integers modulo a prime.
|
||||
*
|
||||
* @var BigInteger
|
||||
*/
|
||||
private $a;
|
||||
|
||||
/**
|
||||
* @var BigInteger
|
||||
*/
|
||||
private $b;
|
||||
|
||||
/**
|
||||
* @var BigInteger
|
||||
*/
|
||||
private $prime;
|
||||
|
||||
/**
|
||||
* Binary length of keys associated with these curve parameters.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* @var Point
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
public function __construct(int $size, BigInteger $prime, BigInteger $a, BigInteger $b, Point $generator)
|
||||
{
|
||||
$this->size = $size;
|
||||
$this->prime = $prime;
|
||||
$this->a = $a;
|
||||
$this->b = $b;
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'curve('.Math::toString($this->getA()).', '.Math::toString($this->getB()).', '.Math::toString($this->getPrime()).')';
|
||||
}
|
||||
|
||||
public function getA(): BigInteger
|
||||
{
|
||||
return $this->a;
|
||||
}
|
||||
|
||||
public function getB(): BigInteger
|
||||
{
|
||||
return $this->b;
|
||||
}
|
||||
|
||||
public function getPrime(): BigInteger
|
||||
{
|
||||
return $this->prime;
|
||||
}
|
||||
|
||||
public function getSize(): int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the curve does not contain the point
|
||||
*/
|
||||
public function getPoint(BigInteger $x, BigInteger $y, ?BigInteger $order = null): Point
|
||||
{
|
||||
if (!$this->contains($x, $y)) {
|
||||
throw new RuntimeException('Curve '.$this->__toString().' does not contain point ('.Math::toString($x).', '.Math::toString($y).')');
|
||||
}
|
||||
$point = Point::create($x, $y, $order);
|
||||
if (!is_null($order)) {
|
||||
$mul = $this->mul($point, $order);
|
||||
if (!$mul->isInfinity()) {
|
||||
throw new RuntimeException('SELF * ORDER MUST EQUAL INFINITY.');
|
||||
}
|
||||
}
|
||||
|
||||
return $point;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the coordinates are out of range
|
||||
*/
|
||||
public function getPublicKeyFrom(BigInteger $x, BigInteger $y): PublicKey
|
||||
{
|
||||
$zero = BigInteger::zero();
|
||||
if ($x->compareTo($zero) < 0 || $y->compareTo($zero) < 0 || $this->generator->getOrder()->compareTo($x) <= 0 || $this->generator->getOrder()->compareTo($y) <= 0) {
|
||||
throw new RuntimeException('Generator point has x and y out of range.');
|
||||
}
|
||||
$point = $this->getPoint($x, $y);
|
||||
|
||||
return new PublicKey($point);
|
||||
}
|
||||
|
||||
public function contains(BigInteger $x, BigInteger $y): bool
|
||||
{
|
||||
return Math::equals(
|
||||
ModularArithmetic::sub(
|
||||
$y->power(2),
|
||||
Math::add(
|
||||
Math::add(
|
||||
$x->power(3),
|
||||
$this->getA()->multipliedBy($x)
|
||||
),
|
||||
$this->getB()
|
||||
),
|
||||
$this->getPrime()
|
||||
),
|
||||
BigInteger::zero()
|
||||
);
|
||||
}
|
||||
|
||||
public function add(Point $one, Point $two): Point
|
||||
{
|
||||
if ($two->isInfinity()) {
|
||||
return clone $one;
|
||||
}
|
||||
|
||||
if ($one->isInfinity()) {
|
||||
return clone $two;
|
||||
}
|
||||
|
||||
if ($two->getX()->isEqualTo($one->getX())) {
|
||||
if ($two->getY()->isEqualTo($one->getY())) {
|
||||
return $this->getDouble($one);
|
||||
}
|
||||
|
||||
return Point::infinity();
|
||||
}
|
||||
|
||||
$slope = ModularArithmetic::div(
|
||||
$two->getY()->minus($one->getY()),
|
||||
$two->getX()->minus($one->getX()),
|
||||
$this->getPrime()
|
||||
);
|
||||
|
||||
$xR = ModularArithmetic::sub(
|
||||
$slope->power(2)->minus($one->getX()),
|
||||
$two->getX(),
|
||||
$this->getPrime()
|
||||
);
|
||||
|
||||
$yR = ModularArithmetic::sub(
|
||||
$slope->multipliedBy($one->getX()->minus($xR)),
|
||||
$one->getY(),
|
||||
$this->getPrime()
|
||||
);
|
||||
|
||||
return $this->getPoint($xR, $yR, $one->getOrder());
|
||||
}
|
||||
|
||||
public function mul(Point $one, BigInteger $n): Point
|
||||
{
|
||||
if ($one->isInfinity()) {
|
||||
return Point::infinity();
|
||||
}
|
||||
|
||||
/** @var BigInteger $zero */
|
||||
$zero = BigInteger::zero();
|
||||
if ($one->getOrder()->compareTo($zero) > 0) {
|
||||
$n = $n->mod($one->getOrder());
|
||||
}
|
||||
|
||||
if ($n->isEqualTo($zero)) {
|
||||
return Point::infinity();
|
||||
}
|
||||
|
||||
/** @var Point[] $r */
|
||||
$r = [
|
||||
Point::infinity(),
|
||||
clone $one,
|
||||
];
|
||||
|
||||
$k = $this->getSize();
|
||||
$n = str_pad(Math::baseConvert(Math::toString($n), 10, 2), $k, '0', STR_PAD_LEFT);
|
||||
|
||||
for ($i = 0; $i < $k; ++$i) {
|
||||
$j = $n[$i];
|
||||
Point::cswap($r[0], $r[1], $j ^ 1);
|
||||
$r[0] = $this->add($r[0], $r[1]);
|
||||
$r[1] = $this->getDouble($r[1]);
|
||||
Point::cswap($r[0], $r[1], $j ^ 1);
|
||||
}
|
||||
|
||||
$this->validate($r[0]);
|
||||
|
||||
return $r[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Curve $other
|
||||
*/
|
||||
public function cmp(self $other): int
|
||||
{
|
||||
$equal = $this->getA()->isEqualTo($other->getA())
|
||||
&& $this->getB()->isEqualTo($other->getB())
|
||||
&& $this->getPrime()->isEqualTo($other->getPrime());
|
||||
|
||||
return $equal ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Curve $other
|
||||
*/
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
return 0 === $this->cmp($other);
|
||||
}
|
||||
|
||||
public function getDouble(Point $point): Point
|
||||
{
|
||||
if ($point->isInfinity()) {
|
||||
return Point::infinity();
|
||||
}
|
||||
|
||||
$a = $this->getA();
|
||||
$threeX2 = BigInteger::of(3)->multipliedBy($point->getX()->power(2));
|
||||
|
||||
$tangent = ModularArithmetic::div(
|
||||
$threeX2->plus($a),
|
||||
BigInteger::of(2)->multipliedBy($point->getY()),
|
||||
$this->getPrime()
|
||||
);
|
||||
|
||||
$x3 = ModularArithmetic::sub(
|
||||
$tangent->power(2),
|
||||
BigInteger::of(2)->multipliedBy($point->getX()),
|
||||
$this->getPrime()
|
||||
);
|
||||
|
||||
$y3 = ModularArithmetic::sub(
|
||||
$tangent->multipliedBy($point->getX()->minus($x3)),
|
||||
$point->getY(),
|
||||
$this->getPrime()
|
||||
);
|
||||
|
||||
return $this->getPoint($x3, $y3, $point->getOrder());
|
||||
}
|
||||
|
||||
public function createPrivateKey(): PrivateKey
|
||||
{
|
||||
return PrivateKey::create($this->generate());
|
||||
}
|
||||
|
||||
public function createPublicKey(PrivateKey $privateKey): PublicKey
|
||||
{
|
||||
$point = $this->mul($this->generator, $privateKey->getSecret());
|
||||
|
||||
return new PublicKey($point);
|
||||
}
|
||||
|
||||
public function getGenerator(): Point
|
||||
{
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException if the point is invalid
|
||||
*/
|
||||
private function validate(Point $point): void
|
||||
{
|
||||
if (!$point->isInfinity() && !$this->contains($point->getX(), $point->getY())) {
|
||||
throw new RuntimeException('Invalid point');
|
||||
}
|
||||
}
|
||||
|
||||
private function generate(): BigInteger
|
||||
{
|
||||
$max = $this->generator->getOrder();
|
||||
$numBits = $this->bnNumBits($max);
|
||||
$numBytes = (int) ceil($numBits / 8);
|
||||
// Generate an integer of size >= $numBits
|
||||
$bytes = BigInteger::randomBits($numBytes);
|
||||
$mask = BigInteger::of(2)->power($numBits)->minus(1);
|
||||
|
||||
return $bytes->and($mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bits used to store this number. Non-significant upper bits are not counted.
|
||||
*
|
||||
* @see https://www.openssl.org/docs/crypto/BN_num_bytes.html
|
||||
*/
|
||||
private function bnNumBits(BigInteger $x): int
|
||||
{
|
||||
$zero = BigInteger::of(0);
|
||||
if ($x->isEqualTo($zero)) {
|
||||
return 0;
|
||||
}
|
||||
$log2 = 0;
|
||||
while (!$x->isEqualTo($zero)) {
|
||||
$x = $x->shiftedRight(1);
|
||||
++$log2;
|
||||
}
|
||||
|
||||
return $log2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util\Ecc;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
|
||||
/*
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class EcDH
|
||||
{
|
||||
public static function computeSharedKey(Curve $curve, PublicKey $publicKey, PrivateKey $privateKey): BigInteger
|
||||
{
|
||||
return $curve->mul($publicKey->getPoint(), $privateKey->getSecret())->getX();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 Spomky-Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util\Ecc;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use Jose\Component\Core\Util\BigInteger as CoreBigInteger;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Math
|
||||
{
|
||||
public static function equals(BigInteger $first, BigInteger $other): bool
|
||||
{
|
||||
return $first->isEqualTo($other);
|
||||
}
|
||||
|
||||
public static function add(BigInteger $augend, BigInteger $addend): BigInteger
|
||||
{
|
||||
return $augend->plus($addend);
|
||||
}
|
||||
|
||||
public static function toString(BigInteger $value): string
|
||||
{
|
||||
return $value->toBase(10);
|
||||
}
|
||||
|
||||
public static function inverseMod(BigInteger $a, BigInteger $m): BigInteger
|
||||
{
|
||||
return CoreBigInteger::createFromBigInteger($a)->modInverse(CoreBigInteger::createFromBigInteger($m))->get();
|
||||
}
|
||||
|
||||
public static function baseConvert(string $number, int $from, int $to): string
|
||||
{
|
||||
return BigInteger::fromBase($number, $from)->toBase($to);
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util\Ecc;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ModularArithmetic
|
||||
{
|
||||
public static function sub(BigInteger $minuend, BigInteger $subtrahend, BigInteger $modulus): BigInteger
|
||||
{
|
||||
return $minuend->minus($subtrahend)->mod($modulus);
|
||||
}
|
||||
|
||||
public static function mul(BigInteger $multiplier, BigInteger $muliplicand, BigInteger $modulus): BigInteger
|
||||
{
|
||||
return $multiplier->multipliedBy($muliplicand)->mod($modulus);
|
||||
}
|
||||
|
||||
public static function div(BigInteger $dividend, BigInteger $divisor, BigInteger $modulus): BigInteger
|
||||
{
|
||||
return self::mul($dividend, Math::inverseMod($divisor, $modulus), $modulus);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2020 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\Core\Util\Ecc;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class NistCurve
|
||||
{
|
||||
/**
|
||||
* Returns an NIST P-256 curve.
|
||||
*/
|
||||
public static function curve256(): Curve
|
||||
{
|
||||
$p = BigInteger::fromBase('ffffffff00000001000000000000000000000000ffffffffffffffffffffffff', 16);
|
||||
$a = BigInteger::fromBase('ffffffff00000001000000000000000000000000fffffffffffffffffffffffc', 16);
|
||||
$b = BigInteger::fromBase('5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16);
|
||||
$x = BigInteger::fromBase('6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296', 16);
|
||||
$y = BigInteger::fromBase('4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5', 16);
|
||||
$n = BigInteger::fromBase('ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551', 16);
|
||||
$generator = Point::create($x, $y, $n);
|
||||
|
||||
return new Curve(256, $p, $a, $b, $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-384 curve.
|
||||
*/
|
||||
public static function curve384(): Curve
|
||||
{
|
||||
$p = BigInteger::fromBase('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff', 16);
|
||||
$a = BigInteger::fromBase('fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc', 16);
|
||||
$b = BigInteger::fromBase('b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16);
|
||||
$x = BigInteger::fromBase('aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7', 16);
|
||||
$y = BigInteger::fromBase('3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f', 16);
|
||||
$n = BigInteger::fromBase('ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973', 16);
|
||||
$generator = Point::create($x, $y, $n);
|
||||
|
||||
return new Curve(384, $p, $a, $b, $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-521 curve.
|
||||
*/
|
||||
public static function curve521(): Curve
|
||||
{
|
||||
$p = BigInteger::fromBase('000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16);
|
||||
$a = BigInteger::fromBase('000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc', 16);
|
||||
$b = BigInteger::fromBase('00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', 16);
|
||||
$x = BigInteger::fromBase('000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66', 16);
|
||||
$y = BigInteger::fromBase('0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650', 16);
|
||||
$n = BigInteger::fromBase('000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409', 16);
|
||||
$generator = Point::create($x, $y, $n);
|
||||
|
||||
return new Curve(521, $p, $a, $b, $generator);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 2036 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user