Initial conversion from Misuzu.
This commit is contained in:
commit
d811e4d6a1
51 changed files with 20301 additions and 0 deletions
.editorconfig.gitattributes.gitignoreLICENCEMakefileREADME.mdVERSIONcomposer.jsoncomposer.lockphpstan-legacy.neonphpstan.neon
dev
php506
php700
php701
php702
php703
php704
php800
php801
php802
php803
php804
src
11
.editorconfig
Normal file
11
.editorconfig
Normal file
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
* text=auto
|
||||
*.sh text eol=lf
|
||||
*.php text eol=lf
|
||||
*.bat text eol=crlf
|
||||
VERSION text eol=lf
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Tt]humbs.db
|
||||
[Dd]esktop.ini
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.vs/
|
||||
.idea/
|
||||
docs/html/
|
||||
.phpdoc*
|
||||
.phpunit*
|
||||
vendor/
|
30
LICENCE
Normal file
30
LICENCE
Normal file
|
@ -0,0 +1,30 @@
|
|||
Copyright (c) 2025, flashwave <me@flash.moe>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted (subject to the limitations in the disclaimer
|
||||
below) provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
||||
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
42
Makefile
Normal file
42
Makefile
Normal file
|
@ -0,0 +1,42 @@
|
|||
ROOT := $(realpath .)
|
||||
export ROOT
|
||||
|
||||
PHPSTAN_CONFIG := $(realpath phpstan.neon)
|
||||
export PHPSTAN_CONFIG
|
||||
|
||||
PHPSTAN_LEGACY_CONFIG := $(realpath phpstan-legacy.neon)
|
||||
export PHPSTAN_LEGACY_CONFIG
|
||||
|
||||
SRC_DIR := $(realpath src)
|
||||
export SRC_DIR
|
||||
|
||||
DEV_TARGETS := install update analyse analyze
|
||||
DEV_DIR := $(realpath dev)
|
||||
DEV_DIRS := $(wildcard ${DEV_DIR}/*)
|
||||
|
||||
# We'll use the lowest common denominator to maintain the root packages
|
||||
PHP_506 := $(shell which php5.6)
|
||||
export PHP_506
|
||||
COMPOSER_LTS := $(shell which composer-lts)
|
||||
export COMPOSER_LTS
|
||||
|
||||
SUDO = $(shell which sudo)
|
||||
|
||||
USER_NAME = $(shell id -un)
|
||||
GROUP_NAME = $(shell id -gn)
|
||||
|
||||
${DEV_TARGETS}: ${DEV_DIRS}
|
||||
|
||||
install:
|
||||
${PHP_506} ${COMPOSER_LTS} install
|
||||
|
||||
update:
|
||||
${PHP_506} ${COMPOSER_LTS} update
|
||||
|
||||
fix-perms: # WSL skill issues
|
||||
${SUDO} chown -R ${USER_NAME}:${GROUP_NAME} .
|
||||
|
||||
${DEV_DIRS}:
|
||||
${MAKE} -C $@ ${MAKECMDGOALS}
|
||||
|
||||
.PHONY: ${DEV_TARGETS} ${DEV_DIRS} fix-perms
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Railgun JWT
|
||||
|
||||
This is a JWT library that exists because I ran into issues with the more commonly used Firebase JWT library.
|
||||
It is very, very loosely based on it in but you'll probably not run into many familiar things.
|
||||
|
||||
I'll write a better readme Someday...
|
||||
|
||||
## Why does this target PHP 5.6?????
|
||||
|
||||
The only core dependency this library has is PHP Sec Lib, which also targets PHP 5.6.
|
||||
Although I'm also doing it as a personal challenge to see if I can support this insane setup.
|
1
VERSION
Normal file
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
0.0.0
|
27
composer.json
Normal file
27
composer.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "railgun/jwt",
|
||||
"description": "A modular JWT library.",
|
||||
"type": "library",
|
||||
"homepage": "https://railgun.sh/libs/jwt",
|
||||
"license": "bsd-3-clause-clear",
|
||||
"require": {
|
||||
"php": ">=5.6",
|
||||
"phpseclib/phpseclib": "~3.0"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "flashwave",
|
||||
"email": "packagist@flash.moe",
|
||||
"homepage": "https://flash.moe",
|
||||
"role": "mom"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Railgun\\Jwt\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/polyfill.php"
|
||||
]
|
||||
}
|
||||
}
|
252
composer.lock
generated
Normal file
252
composer.lock
generated
Normal file
|
@ -0,0 +1,252 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "69fe58fe65d92e48451e103ec91a85a2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/constant_time_encoding.git",
|
||||
"reference": "317718fb438e60151f72b20404f040cb5ae1d494"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/317718fb438e60151f72b20404f040cb5ae1d494",
|
||||
"reference": "317718fb438e60151f72b20404f040cb5ae1d494",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3|^7|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"paragonie/random_compat": "^1.4|^2",
|
||||
"phpunit/phpunit": ">= 4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ParagonIE\\ConstantTime\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Steve 'Sc00bz' Thomas",
|
||||
"email": "steve@tobtu.com",
|
||||
"homepage": "https://www.tobtu.com",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
|
||||
"keywords": [
|
||||
"base16",
|
||||
"base32",
|
||||
"base32_decode",
|
||||
"base32_encode",
|
||||
"base64",
|
||||
"base64_decode",
|
||||
"base64_encode",
|
||||
"bin2hex",
|
||||
"encoding",
|
||||
"hex",
|
||||
"hex2bin",
|
||||
"rfc4648"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
|
||||
"source": "https://github.com/paragonie/constant_time_encoding"
|
||||
},
|
||||
"time": "2022-01-17T05:23:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v2.0.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/96c132c7f2f7bc3230723b66e89f8f150b29d5ae",
|
||||
"reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/random.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"polyfill",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/random_compat/issues",
|
||||
"source": "https://github.com/paragonie/random_compat"
|
||||
},
|
||||
"time": "2022-02-16T17:07:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.43",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
|
||||
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"paragonie/constant_time_encoding": "^1|^2|^3",
|
||||
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
|
||||
"php": ">=5.6.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib3\\": "phpseclib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jim Wigginton",
|
||||
"email": "terrafrost@php.net",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Patrick Monnerat",
|
||||
"email": "pm@datasphere.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andreas Fischer",
|
||||
"email": "bantu@phpbb.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Hans-Jürgen Petrich",
|
||||
"email": "petrich@tronic-media.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
|
||||
"homepage": "http://phpseclib.sourceforge.net",
|
||||
"keywords": [
|
||||
"BigInteger",
|
||||
"aes",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"blowfish",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encryption",
|
||||
"rsa",
|
||||
"security",
|
||||
"sftp",
|
||||
"signature",
|
||||
"signing",
|
||||
"ssh",
|
||||
"twofish",
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/terrafrost",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpseclib",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-14T21:12:59+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
15
dev/php506/Makefile
Normal file
15
dev/php506/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
PHP_506 := $(shell which php5.6)
|
||||
COMPOSER_LTS := $(shell which composer-lts)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
install:
|
||||
${PHP_506} ${COMPOSER_LTS} install
|
||||
|
||||
update:
|
||||
${PHP_506} ${COMPOSER_LTS} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
echo No static analysis available for PHP 5.6.
|
5
dev/php506/composer.json
Normal file
5
dev/php506/composer.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~5.7"
|
||||
}
|
||||
}
|
1550
dev/php506/composer.lock
generated
Normal file
1550
dev/php506/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
15
dev/php700/Makefile
Normal file
15
dev/php700/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
PHP_700 := $(shell which php7.0)
|
||||
COMPOSER_LTS := $(shell which composer-lts)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
install:
|
||||
${PHP_700} ${COMPOSER_LTS} install
|
||||
|
||||
update:
|
||||
${PHP_700} ${COMPOSER_LTS} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
echo No static analysis available for PHP 7.0.
|
5
dev/php700/composer.json
Normal file
5
dev/php700/composer.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~6.5"
|
||||
}
|
||||
}
|
1712
dev/php700/composer.lock
generated
Normal file
1712
dev/php700/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php701/Makefile
Normal file
20
dev/php701/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_701 := $(shell which php7.1)
|
||||
COMPOSER_LTS := $(shell which composer-lts)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_LEGACY_CONFIG),undefined)
|
||||
$(error PHPSTAN_LEGACY_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_701} ${COMPOSER_LTS} install
|
||||
|
||||
update:
|
||||
${PHP_701} ${COMPOSER_LTS} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_701} ${PHPSTAN} analyze -c ${PHPSTAN_LEGACY_CONFIG}
|
6
dev/php701/composer.json
Normal file
6
dev/php701/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpunit/phpunit": "~7.5"
|
||||
}
|
||||
}
|
1763
dev/php701/composer.lock
generated
Normal file
1763
dev/php701/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php702/Makefile
Normal file
20
dev/php702/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_702 := $(shell which php7.2)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_702} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_702} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_702} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php702/composer.json
Normal file
6
dev/php702/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpunit/phpunit": "~8.5"
|
||||
}
|
||||
}
|
1506
dev/php702/composer.lock
generated
Normal file
1506
dev/php702/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php703/Makefile
Normal file
20
dev/php703/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_703 := $(shell which php7.3)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_703} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_703} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_703} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php703/composer.json
Normal file
6
dev/php703/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpunit/phpunit": "~9.6"
|
||||
}
|
||||
}
|
1824
dev/php703/composer.lock
generated
Normal file
1824
dev/php703/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php704/Makefile
Normal file
20
dev/php704/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_704 := $(shell which php7.4)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_704} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_704} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_704} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php704/composer.json
Normal file
6
dev/php704/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~9.6"
|
||||
}
|
||||
}
|
1826
dev/php704/composer.lock
generated
Normal file
1826
dev/php704/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php800/Makefile
Normal file
20
dev/php800/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_800 := $(shell which php8.0)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_800} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_800} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_800} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php800/composer.json
Normal file
6
dev/php800/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~9.6"
|
||||
}
|
||||
}
|
1826
dev/php800/composer.lock
generated
Normal file
1826
dev/php800/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php801/Makefile
Normal file
20
dev/php801/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_801 := $(shell which php8.1)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_801} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_801} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_801} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php801/composer.json
Normal file
6
dev/php801/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~10.5"
|
||||
}
|
||||
}
|
1709
dev/php801/composer.lock
generated
Normal file
1709
dev/php801/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php802/Makefile
Normal file
20
dev/php802/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_802 := $(shell which php8.2)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_802} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_802} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_802} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php802/composer.json
Normal file
6
dev/php802/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~11.5"
|
||||
}
|
||||
}
|
1773
dev/php802/composer.lock
generated
Normal file
1773
dev/php802/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php803/Makefile
Normal file
20
dev/php803/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_803 := $(shell which php8.3)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_803} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_803} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_803} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php803/composer.json
Normal file
6
dev/php803/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~12.1"
|
||||
}
|
||||
}
|
1667
dev/php803/composer.lock
generated
Normal file
1667
dev/php803/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
dev/php804/Makefile
Normal file
20
dev/php804/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
PHP_804 := $(shell which php8.4)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_804} ${COMPOSER} install
|
||||
|
||||
update:
|
||||
${PHP_804} ${COMPOSER} update
|
||||
|
||||
analyze: analyse
|
||||
analyse:
|
||||
${PHP_804} ${PHPSTAN} analyze -c ${PHPSTAN_CONFIG}
|
6
dev/php804/composer.json
Normal file
6
dev/php804/composer.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~12.1"
|
||||
}
|
||||
}
|
1667
dev/php804/composer.lock
generated
Normal file
1667
dev/php804/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
9
phpstan-legacy.neon
Normal file
9
phpstan-legacy.neon
Normal file
|
@ -0,0 +1,9 @@
|
|||
parameters:
|
||||
level: 9
|
||||
checkUninitializedProperties: true
|
||||
treatPhpDocTypesAsCertain: false
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
paths:
|
||||
- src
|
||||
bootstrapFiles:
|
||||
- vendor/autoload.php
|
11
phpstan.neon
Normal file
11
phpstan.neon
Normal file
|
@ -0,0 +1,11 @@
|
|||
parameters:
|
||||
level: 9
|
||||
checkUninitializedProperties: true
|
||||
checkImplicitMixed: true
|
||||
checkBenevolentUnionTypes: true
|
||||
treatPhpDocTypesAsCertain: false
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
paths:
|
||||
- src
|
||||
bootstrapFiles:
|
||||
- vendor/autoload.php
|
72
src/ArrayJwkSet.php
Normal file
72
src/ArrayJwkSet.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use RuntimeException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Represents a JSON Web Key Set backed by an array.
|
||||
*/
|
||||
class ArrayJwkSet implements JwkSet {
|
||||
/**
|
||||
* @var array<string, Jwk>
|
||||
*/
|
||||
private $keys;
|
||||
|
||||
/**
|
||||
* @param array<string, Jwk> $keys
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct($keys) {
|
||||
if(!is_array($keys))
|
||||
throw new InvalidArgumentException('$keys must be an array');
|
||||
if(!array_all($keys, [self::class, 'ensureArrayType']))
|
||||
throw new InvalidArgumentException('$keys does not conform array<string, Jwk>');
|
||||
|
||||
$this->keys = $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
private static function ensureArrayType($value, $key) {
|
||||
return !is_string($key) || !($value instanceof Jwk);
|
||||
}
|
||||
|
||||
public function getKeys() {
|
||||
return $this->keys;
|
||||
}
|
||||
|
||||
public function getKey($keyId = null, $algo = null) {
|
||||
if($keyId !== null && !is_string($keyId))
|
||||
throw new InvalidArgumentException('$keyId must be a string or null');
|
||||
if($algo !== null && !is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string or null');
|
||||
|
||||
if($keyId === null) {
|
||||
if($algo === null)
|
||||
return $this->keys[array_key_first($this->keys)];
|
||||
|
||||
foreach($this->keys as $key) {
|
||||
$keyAlgo = $key->getAlgorithm();
|
||||
if($keyAlgo !== null && hash_equals($keyAlgo, $algo))
|
||||
return $key;
|
||||
}
|
||||
|
||||
throw new RuntimeException('could not find a key that matched the requested algorithm');
|
||||
}
|
||||
|
||||
|
||||
if(!array_key_exists($keyId, $this->keys))
|
||||
throw new RuntimeException('could not find a key with that id');
|
||||
|
||||
$key = $this->keys[$keyId];
|
||||
$keyAlgo = $key->getAlgorithm();
|
||||
if($algo !== null && $keyAlgo !== null && !hash_equals($keyAlgo, $algo))
|
||||
throw new RuntimeException('requested algorithm does not match');
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
124
src/HmacJwk.php
Normal file
124
src/HmacJwk.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use RuntimeException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Represents a HMAC JSON Web Key.
|
||||
*/
|
||||
class HmacJwk implements Jwk {
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private static $algos = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
private $algo;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param ?string $algo
|
||||
* @param ?string $id
|
||||
*/
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
$key,
|
||||
$algo = null,
|
||||
$id = null
|
||||
) {
|
||||
if(!is_string($key))
|
||||
throw new InvalidArgumentException('$key must be a string');
|
||||
if($algo !== null && !is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string or null');
|
||||
if($id !== null && !is_string($id))
|
||||
throw new InvalidArgumentException('$id must be a string or null');
|
||||
|
||||
$this->key = $key;
|
||||
$this->algo = $algo;
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getAlgorithm() {
|
||||
return $this->algo;
|
||||
}
|
||||
|
||||
public function sign($data, $algo = null) {
|
||||
if(!is_string($data))
|
||||
throw new InvalidArgumentException('$data must be a string');
|
||||
if($algo !== null && !is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string or null');
|
||||
|
||||
if($algo === null) {
|
||||
if($this->algo === null)
|
||||
throw new InvalidArgumentException('this key does not specify a default algorithm, you must specify $algo');
|
||||
|
||||
$algo = $this->algo;
|
||||
}
|
||||
|
||||
if(!array_key_exists($algo, self::$algos))
|
||||
throw new InvalidArgumentException('$algo is not a supported algorithm');
|
||||
|
||||
$result = hash_hmac(self::$algos[$algo], $data, $this->key, true);
|
||||
if($result === false) // @phpstan-ignore identical.alwaysFalse
|
||||
throw new RuntimeException('failed to sign $data');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function verify($data, $signature, $algo = null) {
|
||||
if(!is_string($signature))
|
||||
throw new InvalidArgumentException('$signature must be a string');
|
||||
|
||||
return hash_equals($this->sign($data, $algo), $signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registered JWK aliases of native hashing algorithms.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function algorithms() {
|
||||
return array_keys(self::$algos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an algorithm alias.
|
||||
*
|
||||
* @param string $alias JWK algorithm.
|
||||
* @param string $algo Native PHP algorithm.
|
||||
* @return void
|
||||
*/
|
||||
public static function defineAlgorithm($alias, $algo) {
|
||||
if(!is_string($alias))
|
||||
throw new InvalidArgumentException('$alias must be a string');
|
||||
if(!is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string');
|
||||
if(array_key_exists($alias, self::$algos))
|
||||
throw new InvalidArgumentException('$alias has already been defined');
|
||||
if(!in_array($algo, hash_hmac_algos()))
|
||||
throw new InvalidArgumentException('$algo is not a supported HMAC hashing algorithm');
|
||||
|
||||
self::$algos[$alias] = $algo;
|
||||
}
|
||||
}
|
||||
|
||||
HmacJwk::defineAlgorithm('HS256', 'sha256');
|
||||
HmacJwk::defineAlgorithm('HS384', 'sha384');
|
||||
HmacJwk::defineAlgorithm('HS512', 'sha512');
|
38
src/Jwk.php
Normal file
38
src/Jwk.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Represents a JSON Web Key.
|
||||
*/
|
||||
interface Jwk {
|
||||
/**
|
||||
* @return ?string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* @return ?string
|
||||
*/
|
||||
public function getAlgorithm();
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param ?string $algo
|
||||
* @throws RuntimeException
|
||||
* @throws InvalidArgumentException
|
||||
* @return string
|
||||
*/
|
||||
public function sign($data, $algo = null);
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param string $signature
|
||||
* @param ?string $algo
|
||||
* @throws InvalidArgumentException
|
||||
* @return bool
|
||||
*/
|
||||
public function verify($data, $signature, $algo = null);
|
||||
}
|
24
src/JwkSet.php
Normal file
24
src/JwkSet.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Represents a JSON Web Key Set.
|
||||
*/
|
||||
interface JwkSet {
|
||||
/**
|
||||
* @return array<string, Jwk>
|
||||
*/
|
||||
public function getKeys();
|
||||
|
||||
/**
|
||||
* @param ?string $keyId
|
||||
* @param ?string $algo
|
||||
* @throws RuntimeException
|
||||
* @throws InvalidArgumentException
|
||||
* @return Jwk
|
||||
*/
|
||||
public function getKey($keyId = null, $algo = null);
|
||||
}
|
128
src/SecLibJwk.php
Normal file
128
src/SecLibJwk.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Stringable;
|
||||
use UnexpectedValueException;
|
||||
use phpseclib3\Crypt\Common\{AsymmetricKey,PrivateKey,PublicKey};
|
||||
|
||||
/**
|
||||
* Implements a PHP Sec Lib backed JWK.
|
||||
*/
|
||||
class SecLibJwk implements Jwk {
|
||||
/**
|
||||
* @var (AsymmetricKey&PrivateKey)|(AsymmetricKey&PublicKey)
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var ?SecLibJwkAlgorithm
|
||||
*/
|
||||
private $algoInfo;
|
||||
|
||||
/**
|
||||
* @param (AsymmetricKey&PrivateKey)|(AsymmetricKey&PublicKey) $key
|
||||
* @param SecLibJwkAlgorithm|string|null $algo
|
||||
* @param ?string $id
|
||||
*/
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
$key,
|
||||
$algo = null,
|
||||
$id = null
|
||||
) {
|
||||
if(!($key instanceof AsymmetricKey) || (!($key instanceof PublicKey) && !($key instanceof PrivateKey)))
|
||||
throw new InvalidArgumentException('$key is not a supported type combination');
|
||||
if($id !== null && !is_string($id))
|
||||
throw new InvalidArgumentException('$id must be a string or null');
|
||||
if($algo !== null) {
|
||||
if(is_string($algo))
|
||||
$algo = SecLibJwkAlgorithm::create($algo);
|
||||
elseif(!($algo instanceof SecLibJwkAlgorithm))
|
||||
throw new InvalidArgumentException('$algo must be a SecLibJwkAlgorithm, string or null');
|
||||
}
|
||||
|
||||
$this->key = $key;
|
||||
$this->algoInfo = $algo;
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getAlgorithm() {
|
||||
return $this->algoInfo instanceof SecLibJwkAlgorithm
|
||||
? $this->algoInfo->getAlgorithm()
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SecLibJwkAlgorithm|string|null $algo
|
||||
* @throws InvalidArgumentException
|
||||
* @return SecLibJwkAlgorithm
|
||||
*/
|
||||
private function resolveAlgo($algo) {
|
||||
if($algo === null) {
|
||||
if($this->algoInfo === null)
|
||||
throw new InvalidArgumentException('this key does not specify a default algorithm, you must specify $algo');
|
||||
|
||||
$algo = $this->algoInfo;
|
||||
} elseif(is_string($algo))
|
||||
$algo = SecLibJwkAlgorithm::create($algo);
|
||||
|
||||
if($algo instanceof SecLibJwkAlgorithm)
|
||||
return $algo;
|
||||
|
||||
throw new InvalidArgumentException('$algo could not be resolved');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param SecLibJwkAlgorithm|string|null $algo
|
||||
* @throws UnexpectedValueException
|
||||
*/
|
||||
public function sign($data, $algo = null) {
|
||||
if(!is_string($data))
|
||||
throw new InvalidArgumentException('$data must be a string');
|
||||
|
||||
$key = $this->resolveAlgo($algo)->transform($this->key);
|
||||
if(!($key instanceof PrivateKey))
|
||||
throw new RuntimeException('this instance does not have a private key, it can only be used to verify');
|
||||
|
||||
$signature = $key->sign($data);
|
||||
if(is_scalar($signature) || $signature instanceof Stringable)
|
||||
return (string)$signature;
|
||||
|
||||
throw new UnexpectedValueException('unexpected return value for signature');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param SecLibJwkAlgorithm|string|null $algo
|
||||
*/
|
||||
public function verify($data, $signature, $algo = null) {
|
||||
if(!is_string($data))
|
||||
throw new InvalidArgumentException('$data must be a string');
|
||||
if(!is_string($signature))
|
||||
throw new InvalidArgumentException('$signature must be a string');
|
||||
|
||||
$key = $this->resolveAlgo($algo)->transform($this->key);
|
||||
if($key instanceof PrivateKey)
|
||||
$key = $key->getPublicKey();
|
||||
if(!($key instanceof PublicKey))
|
||||
throw new UnexpectedValueException('key result is not of expected type');
|
||||
|
||||
$result = $key->verify($data, $signature);
|
||||
if(is_scalar($result))
|
||||
return (bool)$result;
|
||||
|
||||
throw new UnexpectedValueException('unexpected return value for verification');
|
||||
}
|
||||
}
|
354
src/SecLibJwkAlgorithm.php
Normal file
354
src/SecLibJwkAlgorithm.php
Normal file
|
@ -0,0 +1,354 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
use phpseclib3\Crypt\{EC,RSA};
|
||||
use phpseclib3\Crypt\Common\AsymmetricKey;
|
||||
|
||||
/**
|
||||
* Implements a PHP Sec Lib backed JWK Algorithm.
|
||||
*/
|
||||
class SecLibJwkAlgorithm {
|
||||
/**
|
||||
* @var array<string, callable(AsymmetricKey): AsymmetricKey>
|
||||
*/
|
||||
private static $algos = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $algo;
|
||||
|
||||
/**
|
||||
* @var callable(AsymmetricKey): AsymmetricKey
|
||||
*/
|
||||
public $transformFunc;
|
||||
|
||||
/**
|
||||
* @param string $algo
|
||||
* @param callable(AsymmetricKey): AsymmetricKey $transformFunc
|
||||
*/
|
||||
public function __construct($algo, $transformFunc) {
|
||||
if(!is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string');
|
||||
if(!is_callable($transformFunc))
|
||||
throw new InvalidArgumentException('$transformFunc must be callable');
|
||||
|
||||
$this->algo = $algo;
|
||||
$this->transformFunc = $transformFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAlgorithm() {
|
||||
return $this->algo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
* @return AsymmetricKey
|
||||
*/
|
||||
public function transform($key) {
|
||||
if(!($key instanceof AsymmetricKey))
|
||||
throw new InvalidArgumentException('$key must be an instance of AsymmetricKey');
|
||||
|
||||
$key = ($this->transformFunc)($key);
|
||||
if(!($key instanceof AsymmetricKey))
|
||||
throw new UnexpectedValueException('result of the transformer was not an AsymmetricKey');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $algo
|
||||
* @throws InvalidArgumentException
|
||||
* @return SecLibJwkAlgorithm
|
||||
*/
|
||||
public static function create($algo) {
|
||||
if(!is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string');
|
||||
if(!array_key_exists($algo, self::$algos))
|
||||
throw new InvalidArgumentException(sprintf('$algo is not a supported algorithm: "%s"', $algo));
|
||||
|
||||
return new SecLibJwkAlgorithm($algo, self::$algos[$algo]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function ensureRsaKey($key) {
|
||||
if($key instanceof RSA)
|
||||
return $key;
|
||||
|
||||
throw new InvalidArgumentException('$key must be an RSA key');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RSA $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function applyPkcs1Padding($key) {
|
||||
$key = $key->withPadding(RSA::SIGNATURE_PKCS1);
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RSA $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function applyPssPadding($key) {
|
||||
$key = $key->withPadding(RSA::SIGNATURE_PSS);
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @return EC
|
||||
*/
|
||||
public static function ensureEcKey($key) {
|
||||
if($key instanceof EC)
|
||||
return $key;
|
||||
|
||||
throw new InvalidArgumentException('$key must be an EC key');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EC $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return EC
|
||||
*/
|
||||
public static function applyIeeeSignatureFormat($key) {
|
||||
$key = $key->withSignatureFormat('IEEE');
|
||||
if(!($key instanceof EC))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function algorithms() {
|
||||
return array_keys(self::$algos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function transformRs256($key) {
|
||||
$key = self::applyPkcs1Padding(self::ensureRsaKey($key))->withHash('sha256');
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function transformRs384($key) {
|
||||
$key = self::applyPkcs1Padding(self::ensureRsaKey($key))->withHash('sha384');
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function transformRs512($key) {
|
||||
$key = self::applyPkcs1Padding(self::ensureRsaKey($key))->withHash('sha512');
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function transformPs256($key) {
|
||||
$key = self::applyPssPadding(self::ensureRsaKey($key))->withHash('sha256');
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function transformPs384($key) {
|
||||
$key = self::applyPssPadding(self::ensureRsaKey($key))->withHash('sha384');
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws UnexpectedValueException
|
||||
* @return RSA
|
||||
*/
|
||||
public static function transformPs512($key) {
|
||||
$key = self::applyPssPadding(self::ensureRsaKey($key))->withHash('sha512');
|
||||
if(!($key instanceof RSA))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
* @return EC
|
||||
*/
|
||||
public static function transformEs256($key) {
|
||||
$key = self::ensureEcKey($key);
|
||||
if($key->getCurve() !== 'secp256r1')
|
||||
throw new InvalidArgumentException('curve must be secp256r1');
|
||||
|
||||
$key = self::applyIeeeSignatureFormat($key)->withHash('sha256');
|
||||
if(!($key instanceof EC))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
* @return EC
|
||||
*/
|
||||
public static function transformEs256K($key) {
|
||||
$key = self::ensureEcKey($key);
|
||||
if($key->getCurve() !== 'secp256k1')
|
||||
throw new InvalidArgumentException('curve must be secp256k1');
|
||||
|
||||
$key = self::applyIeeeSignatureFormat($key)->withHash('sha256');
|
||||
if(!($key instanceof EC))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
* @return EC
|
||||
*/
|
||||
public static function transformEs384($key) {
|
||||
$key = self::ensureEcKey($key);
|
||||
if($key->getCurve() !== 'secp384r1')
|
||||
throw new InvalidArgumentException('curve must be secp384r1');
|
||||
|
||||
$key = self::applyIeeeSignatureFormat($key)->withHash('sha384');
|
||||
if(!($key instanceof EC))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
* @return EC
|
||||
*/
|
||||
public static function transformEs512($key) {
|
||||
$key = self::ensureEcKey($key);
|
||||
if($key->getCurve() !== 'secp521r1')
|
||||
throw new InvalidArgumentException('curve must be secp521r1');
|
||||
|
||||
$key = self::applyIeeeSignatureFormat($key)->withHash('sha512');
|
||||
if(!($key instanceof EC))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AsymmetricKey $key
|
||||
* @throws InvalidArgumentException
|
||||
* @throws UnexpectedValueException
|
||||
* @return EC
|
||||
*/
|
||||
public static function transformEdDsa($key) {
|
||||
$key = self::ensureEcKey($key);
|
||||
if($key->getCurve() !== 'Ed25519' && $key->getCurve() !== 'Ed448')
|
||||
throw new InvalidArgumentException('curve must be Ed25519 or Ed448');
|
||||
|
||||
$key = $key;
|
||||
if(!($key instanceof EC))
|
||||
throw new UnexpectedValueException('return type changed unexpectedly');
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $algo
|
||||
* @param callable(AsymmetricKey): AsymmetricKey $transformFunc
|
||||
* @throws InvalidArgumentException
|
||||
* @return void
|
||||
*/
|
||||
public static function defineAlgorithm($algo, $transformFunc) {
|
||||
if(!is_string($algo))
|
||||
throw new InvalidArgumentException('$algo must be a string');
|
||||
if(!is_callable($transformFunc))
|
||||
throw new InvalidArgumentException('$transformFunc must be a callable');
|
||||
if(array_key_exists($algo, self::$algos))
|
||||
throw new InvalidArgumentException('$algo has already been defined');
|
||||
|
||||
self::$algos[$algo] = $transformFunc;
|
||||
}
|
||||
}
|
||||
|
||||
// RSxxx
|
||||
SecLibJwkAlgorithm::defineAlgorithm('RS256', [SecLibJwkAlgorithm::class, 'transformRS256']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('RS384', [SecLibJwkAlgorithm::class, 'transformRS384']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('RS512', [SecLibJwkAlgorithm::class, 'transformRS512']);
|
||||
|
||||
// PSxxx
|
||||
SecLibJwkAlgorithm::defineAlgorithm('PS256', [SecLibJwkAlgorithm::class, 'transformPS256']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('PS384', [SecLibJwkAlgorithm::class, 'transformPS384']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('PS512', [SecLibJwkAlgorithm::class, 'transformPS512']);
|
||||
|
||||
// ESxxxy
|
||||
SecLibJwkAlgorithm::defineAlgorithm('ES256', [SecLibJwkAlgorithm::class, 'transformES256']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('ES256K', [SecLibJwkAlgorithm::class, 'transformES256K']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('ES384', [SecLibJwkAlgorithm::class, 'transformES384']);
|
||||
SecLibJwkAlgorithm::defineAlgorithm('ES512', [SecLibJwkAlgorithm::class, 'transformES512']);
|
||||
|
||||
// EdDSA
|
||||
SecLibJwkAlgorithm::defineAlgorithm('EdDSA', [SecLibJwkAlgorithm::class, 'transformEdDSA']);
|
55
src/polyfill.php
Normal file
55
src/polyfill.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
namespace Railgun\Jwt;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
if(!function_exists('array_key_first')) {
|
||||
/**
|
||||
* Gets the first key of an array.
|
||||
*
|
||||
* @param mixed[] $array An array.
|
||||
* @return int|string|null Returns the first key of array if array is not empty; null otherwise.
|
||||
*/
|
||||
function array_key_first($array) {
|
||||
if(!is_array($array))
|
||||
throw new InvalidArgumentException('$array must be an array');
|
||||
|
||||
foreach($array as $key => $_)
|
||||
return $key;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if(!function_exists('array_all')) {
|
||||
/**
|
||||
* Checks if all array elements satisfy a callback function.
|
||||
*
|
||||
* @param mixed[] $array The array that should be searched.
|
||||
* @param callable(mixed $value, mixed $key): bool $callback The callback function to call to check each element.
|
||||
* @return bool The function returns true, if callback returns true for all elements. Otherwise returns false.
|
||||
*/
|
||||
function array_all($array, $callback) {
|
||||
if(!is_array($array))
|
||||
throw new InvalidArgumentException('$array must be an array');
|
||||
if(!is_callable($callback))
|
||||
throw new InvalidArgumentException('$callback must be a callable');
|
||||
|
||||
foreach($array as $key => $value)
|
||||
if(!$callback($value, $key))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!function_exists('hash_hmac_algos')) {
|
||||
/**
|
||||
* Return a list of registered hashing algorithms suitable for hash_hmac.
|
||||
*
|
||||
* @return string[] Returns a numerically indexed array containing the list of supported hashing algorithms suitable for hash_hmac().
|
||||
*/
|
||||
function hash_hmac_algos(): array {
|
||||
return hash_algos();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue