Seditio Source
Root |
./othercms/xenForo 2.2.8/src/vendor/web-token/jwt-core/Util/RSAKey.php
<?php

declare(strict_types=1);

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2018 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
FG\ASN1\Universal\BitString;
use
FG\ASN1\Universal\Integer;
use
FG\ASN1\Universal\NullObject;
use
FG\ASN1\Universal\ObjectIdentifier;
use
FG\ASN1\Universal\OctetString;
use
FG\ASN1\Universal\Sequence;
use
Jose\Component\Core\JWK;

/**
 * @internal
 */
class RSAKey
{
   
/**
     * @var Sequence
     */
   
private $sequence;

   
/**
     * @var bool
     */
   
private $private = false;

   
/**
     * @var array
     */
   
private $values = [];

   
/**
     * @var BigInteger
     */
   
private $modulus;

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

   
/**
     * @var BigInteger
     */
   
private $public_exponent;

   
/**
     * @var BigInteger|null
     */
   
private $private_exponent = null;

   
/**
     * @var BigInteger[]
     */
   
private $primes = [];

   
/**
     * @var BigInteger[]
     */
   
private $exponents = [];

   
/**
     * @var BigInteger|null
     */
   
private $coefficient = null;

    private function
__construct(JWK $data)
    {
       
$this->loadJWK($data->all());
       
$this->populateBigIntegers();
       
$this->private = \array_key_exists('d', $this->values);
    }

   
/**
     * @return RSAKey
     */
   
public static function createFromJWK(JWK $jwk): self
   
{
        return new
self($jwk);
    }

    public function
getModulus(): BigInteger
   
{
        return
$this->modulus;
    }

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

    public function
getExponent(): BigInteger
   
{
       
$d = $this->getPrivateExponent();
        if (
null !== $d) {
            return
$d;
        }

        return
$this->getPublicExponent();
    }

    public function
getPublicExponent(): BigInteger
   
{
        return
$this->public_exponent;
    }

    public function
getPrivateExponent(): ?BigInteger
   
{
        return
$this->private_exponent;
    }

   
/**
     * @return BigInteger[]
     */
   
public function getPrimes(): array
    {
        return
$this->primes;
    }

   
/**
     * @return BigInteger[]
     */
   
public function getExponents(): array
    {
        return
$this->exponents;
    }

    public function
getCoefficient(): ?BigInteger
   
{
        return
$this->coefficient;
    }

    public function
isPublic(): bool
   
{
        return !\
array_key_exists('d', $this->values);
    }

   
/**
     * @param RSAKey $private
     *
     * @return RSAKey
     */
   
public static function toPublic(self $private): self
   
{
       
$data = $private->toArray();
       
$keys = ['p', 'd', 'q', 'dp', 'dq', 'qi'];
        foreach (
$keys as $key) {
            if (\
array_key_exists($key, $data)) {
                unset(
$data[$key]);
            }
        }

        return new
self(new JWK($data));
    }

    public function
toArray(): array
    {
        return
$this->values;
    }

    private function
loadJWK(array $jwk)
    {
        if (!\
array_key_exists('kty', $jwk)) {
            throw new \
InvalidArgumentException('The key parameter "kty" is missing.');
        }
        if (
'RSA' !== $jwk['kty']) {
            throw new \
InvalidArgumentException('The JWK is not a RSA key.');
        }

       
$this->values = $jwk;
    }

    private function
populateBigIntegers()
    {
       
$this->modulus = $this->convertBase64StringToBigInteger($this->values['n']);
       
$this->modulus_length = \mb_strlen($this->getModulus()->toBytes(), '8bit');
       
$this->public_exponent = $this->convertBase64StringToBigInteger($this->values['e']);

        if (!
$this->isPublic()) {
           
$this->private_exponent = $this->convertBase64StringToBigInteger($this->values['d']);

            if (\
array_key_exists('p', $this->values) && \array_key_exists('q', $this->values)) {
               
$this->primes = [
                   
$this->convertBase64StringToBigInteger($this->values['p']),
                   
$this->convertBase64StringToBigInteger($this->values['q']),
                ];
                if (\
array_key_exists('dp', $this->values) && \array_key_exists('dq', $this->values) && \array_key_exists('qi', $this->values)) {
                   
$this->exponents = [
                       
$this->convertBase64StringToBigInteger($this->values['dp']),
                       
$this->convertBase64StringToBigInteger($this->values['dq']),
                    ];
                   
$this->coefficient = $this->convertBase64StringToBigInteger($this->values['qi']);
                }
            }
        }
    }

    private function
convertBase64StringToBigInteger(string $value): BigInteger
   
{
        return
BigInteger::createFromBinaryString(Base64Url::decode($value));
    }

   
/**
     * @throws \Exception
     */
   
public function toPEM(): string
   
{
        if (
null === $this->sequence) {
           
$this->sequence = new Sequence();
            if (\
array_key_exists('d', $this->values)) {
               
$this->initPrivateKey();
            } else {
               
$this->initPublicKey();
            }
        }
       
$result = '-----BEGIN '.($this->private ? 'RSA PRIVATE' : 'PUBLIC').' KEY-----'.PHP_EOL;
       
$result .= \chunk_split(\base64_encode($this->sequence->getBinary()), 64, PHP_EOL);
       
$result .= '-----END '.($this->private ? 'RSA PRIVATE' : 'PUBLIC').' KEY-----'.PHP_EOL;

        return
$result;
    }

   
/**
     * @throws \Exception
     */
   
private function initPublicKey()
    {
       
$oid_sequence = new Sequence();
       
$oid_sequence->addChild(new ObjectIdentifier('1.2.840.113549.1.1.1'));
       
$oid_sequence->addChild(new NullObject());
       
$this->sequence->addChild($oid_sequence);
       
$n = new Integer($this->fromBase64ToInteger($this->values['n']));
       
$e = new Integer($this->fromBase64ToInteger($this->values['e']));
       
$key_sequence = new Sequence();
       
$key_sequence->addChild($n);
       
$key_sequence->addChild($e);
       
$key_bit_string = new BitString(\bin2hex($key_sequence->getBinary()));
       
$this->sequence->addChild($key_bit_string);
    }

    private function
initPrivateKey()
    {
       
$this->sequence->addChild(new Integer(0));
       
$oid_sequence = new Sequence();
       
$oid_sequence->addChild(new ObjectIdentifier('1.2.840.113549.1.1.1'));
       
$oid_sequence->addChild(new NullObject());
       
$this->sequence->addChild($oid_sequence);
       
$v = new Integer(0);
       
$n = new Integer($this->fromBase64ToInteger($this->values['n']));
       
$e = new Integer($this->fromBase64ToInteger($this->values['e']));
       
$d = new Integer($this->fromBase64ToInteger($this->values['d']));
       
$p = new Integer($this->fromBase64ToInteger($this->values['p']));
       
$q = new Integer($this->fromBase64ToInteger($this->values['q']));
       
$dp = \array_key_exists('dp', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['dp'])) : new Integer(0);
       
$dq = \array_key_exists('dq', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['dq'])) : new Integer(0);
       
$qi = \array_key_exists('qi', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['qi'])) : new Integer(0);
       
$key_sequence = new Sequence();
       
$key_sequence->addChild($v);
       
$key_sequence->addChild($n);
       
$key_sequence->addChild($e);
       
$key_sequence->addChild($d);
       
$key_sequence->addChild($p);
       
$key_sequence->addChild($q);
       
$key_sequence->addChild($dp);
       
$key_sequence->addChild($dq);
       
$key_sequence->addChild($qi);
       
$key_octet_string = new OctetString(\bin2hex($key_sequence->getBinary()));
       
$this->sequence->addChild($key_octet_string);
    }

   
/**
     * @param string $value
     *
     * @return string
     */
   
private function fromBase64ToInteger($value)
    {
        return \
gmp_strval(\gmp_init(\current(\unpack('H*', Base64Url::decode($value))), 16), 10);
    }

   
/**
     * Exponentiate with or without Chinese Remainder Theorem.
     * Operation with primes 'p' and 'q' is appox. 2x faster.
     *
     * @param RSAKey $key
     */
   
public static function exponentiate(self $key, BigInteger $c): BigInteger
   
{
        if (
$c->compare(BigInteger::createFromDecimal(0)) < 0 || $c->compare($key->getModulus()) > 0) {
            throw new \
RuntimeException();
        }
        if (
$key->isPublic() || empty($key->getPrimes()) || empty($key->getExponents()) || null === $key->getCoefficient()) {
            return
$c->modPow($key->getExponent(), $key->getModulus());
        }

       
$p = $key->getPrimes()[0];
       
$q = $key->getPrimes()[1];
       
$dP = $key->getExponents()[0];
       
$dQ = $key->getExponents()[1];
       
$qInv = $key->getCoefficient();

       
$m1 = $c->modPow($dP, $p);
       
$m2 = $c->modPow($dQ, $q);
       
$h = $qInv->multiply($m1->subtract($m2)->add($p))->mod($p);
       
$m = $m2->add($h->multiply($q));

        return
$m;
    }
}