Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/test phpseclib 3 #133

Open
wants to merge 4 commits into
base: support/SDK-V3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
"php": ">=5.6.0",
"ext-json": "*",
"guzzlehttp/guzzle": "^6.2.1 || ^7.0.1",
"phpseclib/phpseclib": "^2.0.11",
"gree/jose": "^2.2.1"
"phpseclib/phpseclib": "^3.0.0"
},
"autoload": {
"psr-4": { "Hyperwallet\\": "src/Hyperwallet", "ComposerScript\\" : "src/ComposerScript" }
"psr-4": {
"Hyperwallet\\": "src/Hyperwallet",
"ComposerScript\\": "src/ComposerScript",
"Services\\": "src/Services"
}
},
"autoload-dev" : {
"psr-4": { "Hyperwallet\\Tests\\" : "tests/Hyperwallet/Tests", "ComposerScript\\" : "src/ComposerScript" }
Expand All @@ -36,8 +39,5 @@
"phpunit/phpunit": "^5.7 || ^7.0.0 || ^9.0",
"phake/phake": "^2.3 || ^4.2",
"php-coveralls/php-coveralls": "^2.5"
},
"scripts": {
"post-install-cmd": "ComposerScript\\RsaOaep256AlgorithmInstaller::install"
}
}
140 changes: 73 additions & 67 deletions src/Hyperwallet/Util/HyperwalletEncryption.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
<?php

namespace Hyperwallet\Util;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\UriTemplate\UriTemplate;
use Hyperwallet\Exception\HyperwalletApiException;

use Hyperwallet\Exception\HyperwalletException;
use Hyperwallet\Model\BaseModel;
use Hyperwallet\Response\ErrorResponse;
use Composer\Autoload\ClassLoader;
use phpseclib\Crypt\RSA;
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\Hash;
use JOSE_URLSafeBase64;
use JOSE_JWS;
use JOSE_JWE;
use JOSE_JWK;
use JOSE_JWT;
use phpseclib3\Crypt\RSA;
use phpseclib3\Math\BigInteger;
use Services\Jose\URLSafeBase64;
use Services\Jose\JOSE_JWS;
use Services\Jose\JOSE_JWE;
use Services\Jose\JOSE_JWK;
use Services\Jose\JOSE_JWT;

/**
* The encryption service for Hyperwallet client's requests/responses
*
* @package Hyperwallet\Util
*/
class HyperwalletEncryption {
class HyperwalletEncryption
{

/**
* String that can be a URL or path to file with client JWK set
Expand Down Expand Up @@ -91,16 +86,20 @@ class HyperwalletEncryption {
* @param array $encryptionMethod JWE encryption method, by default value = A256CBC-HS512
* @param array $jwsExpirationMinutes Minutes when JWS signature is valid, by default value = 5
*/
public function __construct($clientPrivateKeySetLocation, $hyperwalletKeySetLocation,
$encryptionAlgorithm = 'RSA-OAEP-256', $signAlgorithm = 'RS256', $encryptionMethod = 'A256CBC-HS512',
$jwsExpirationMinutes = 5) {
public function __construct(
$clientPrivateKeySetLocation,
$hyperwalletKeySetLocation,
$encryptionAlgorithm = 'RSA-OAEP-256',
$signAlgorithm = 'RS256',
$encryptionMethod = 'A256CBC-HS512',
$jwsExpirationMinutes = 5
) {
$this->clientPrivateKeySetLocation = $clientPrivateKeySetLocation;
$this->hyperwalletKeySetLocation = $hyperwalletKeySetLocation;
$this->encryptionAlgorithm = $encryptionAlgorithm;
$this->signAlgorithm = $signAlgorithm;
$this->encryptionMethod = $encryptionMethod;
$this->jwsExpirationMinutes = $jwsExpirationMinutes;
file_put_contents($this->getVendorPath() . "/gree/jose/src/JOSE/JWE.php", file_get_contents(__DIR__ . "/../../JWE"));
}

/**
Expand All @@ -111,7 +110,8 @@ public function __construct($clientPrivateKeySetLocation, $hyperwalletKeySetLoca
*
* @throws HyperwalletException
*/
public function encrypt($body) {
public function encrypt($body)
{
$privateJwsKey = $this->getPrivateJwsKey();
$jws = new JOSE_JWS(new JOSE_JWT($body));
$jws->header['exp'] = $this->getSignatureExpirationTime();
Expand All @@ -133,7 +133,8 @@ public function encrypt($body) {
*
* @throws HyperwalletException
*/
public function decrypt($body) {
public function decrypt($body)
{
$privateJweKey = $this->getPrivateJweKey();
$jwe = JOSE_JWT::decode($body);
$decryptedBody = $jwe->decrypt($privateJweKey);
Expand All @@ -152,7 +153,8 @@ public function decrypt($body) {
*
* @throws HyperwalletException
*/
private function getPrivateJwsKey() {
private function getPrivateJwsKey()
{
$privateKeyData = $this->getJwk($this->clientPrivateKeySetLocation, $this->signAlgorithm);
$this->jwsKid = $privateKeyData['kid'];
return $this->getPrivateKey($privateKeyData);
Expand All @@ -165,7 +167,8 @@ private function getPrivateJwsKey() {
*
* @throws HyperwalletException
*/
private function getPublicJweKey() {
private function getPublicJweKey()
{
$publicKeyData = $this->getJwk($this->hyperwalletKeySetLocation, $this->encryptionAlgorithm);
$this->jweKid = $publicKeyData['kid'];
return $this->getPublicKey($this->convertPrivateKeyToPublic($publicKeyData));
Expand All @@ -178,7 +181,8 @@ private function getPublicJweKey() {
*
* @throws HyperwalletException
*/
private function getPrivateJweKey() {
private function getPrivateJweKey()
{
$privateKeyData = $this->getJwk($this->clientPrivateKeySetLocation, $this->encryptionAlgorithm);
return $this->getPrivateKey($privateKeyData);
}
Expand All @@ -190,7 +194,8 @@ private function getPrivateJweKey() {
*
* @throws HyperwalletException
*/
private function getPublicJwsKey() {
private function getPublicJwsKey()
{
$publicKeyData = $this->getJwk($this->hyperwalletKeySetLocation, $this->signAlgorithm);
return $this->getPublicKey($this->convertPrivateKeyToPublic($publicKeyData));
}
Expand All @@ -201,31 +206,24 @@ private function getPublicJwsKey() {
* @param array $privateKeyData The JWK key data
* @return RSA
*/
private function getPrivateKey($privateKeyData) {
$n = $this->keyParamToBigInteger($privateKeyData['n']);
$e = $this->keyParamToBigInteger($privateKeyData['e']);
$d = $this->keyParamToBigInteger($privateKeyData['d']);
$p = $this->keyParamToBigInteger($privateKeyData['p']);
$q = $this->keyParamToBigInteger($privateKeyData['q']);
$qi = $this->keyParamToBigInteger($privateKeyData['qi']);
$dp = $this->keyParamToBigInteger($privateKeyData['dp']);
$dq = $this->keyParamToBigInteger($privateKeyData['dq']);
$primes = array($p, $q);
$exponents = array($dp, $dq);
$coefficients = array($qi, $qi);
array_unshift($primes, "phoney");
unset($primes[0]);
array_unshift($exponents, "phoney");
unset($exponents[0]);
array_unshift($coefficients, "phoney");
unset($coefficients[0]);
private function getPrivateKey($privateKeyData)
{
$pemData = RSA::load([
'e' => $this->keyParamToBigInteger($privateKeyData['e']),
'n' => $this->keyParamToBigInteger($privateKeyData['n']),
'd' => $this->keyParamToBigInteger($privateKeyData['d']),
'p' => $this->keyParamToBigInteger($privateKeyData['p']),
'q' => $this->keyParamToBigInteger($privateKeyData['q']),
'dp' => $this->keyParamToBigInteger($privateKeyData['dp']),
'dq' => $this->keyParamToBigInteger($privateKeyData['dq']),
'qi' => $this->keyParamToBigInteger($privateKeyData['qi']),
]);

$privateKey = RSA::loadPrivateKey($pemData->toString('PKCS1'));

$pemData = (new RSA())->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
$privateKey = new RSA();
$privateKey->loadKey($pemData);
if ($privateKeyData['alg'] == 'RSA-OAEP-256') {
$privateKey->setHash('sha256');
$privateKey->setMGFHash('sha256');
// $privateKey->setHash('sha256');
// $privateKey->setMGFHash('sha256');
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

phpseclib v3 $privateKey is now a PrivateKey object and not RSA object, it changed completely so i am not sure if i should remove this or not.

}
return $privateKey;
}
Expand All @@ -236,8 +234,9 @@ private function getPrivateKey($privateKeyData) {
* @param string $param base 64 encoded string
* @return BigInteger
*/
private function keyParamToBigInteger($param) {
return new BigInteger('0x' . bin2hex(JOSE_URLSafeBase64::decode($param)), 16);
private function keyParamToBigInteger($param)
{
return new BigInteger('0x' . bin2hex(URLSafeBase64::decode($param)), 16);
}

/**
Expand All @@ -246,12 +245,13 @@ private function keyParamToBigInteger($param) {
* @param array $publicKeyData The JWK key data
* @return RSA
*/
private function getPublicKey($publicKeyData) {
private function getPublicKey($publicKeyData)
{
$publicKeyRaw = new JOSE_JWK($publicKeyData);
$publicKey = $publicKeyRaw->toKey();
if ($publicKeyData['alg'] == 'RSA-OAEP-256') {
$publicKey->setHash('sha256');
$publicKey->setMGFHash('sha256');
// $publicKey->setHash('sha256');
// $publicKey->setMGFHash('sha256');
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}
return $publicKey;
}
Expand All @@ -265,8 +265,9 @@ private function getPublicKey($publicKeyData) {
*
* @throws HyperwalletException
*/
private function getJwk($keySetLocation, $alg) {
if (filter_var($keySetLocation, FILTER_VALIDATE_URL) === FALSE) {
private function getJwk($keySetLocation, $alg)
{
if (filter_var($keySetLocation, FILTER_VALIDATE_URL) === false) {
if (!file_exists($keySetLocation)) {
throw new HyperwalletException("Wrong JWK key set location path = " . $keySetLocation);
}
Expand All @@ -283,8 +284,9 @@ private function getJwk($keySetLocation, $alg) {
*
* @throws HyperwalletException
*/
private function findJwkByAlgorithm($jwkSetArray, $alg) {
foreach($jwkSetArray['keys'] as $jwk) {
private function findJwkByAlgorithm($jwkSetArray, $alg)
{
foreach ($jwkSetArray['keys'] as $jwk) {
if ($alg == $jwk['alg']) {
return $jwk;
}
Expand All @@ -298,7 +300,8 @@ private function findJwkByAlgorithm($jwkSetArray, $alg) {
* @param string $jwk JWK key
* @return array
*/
private function convertPrivateKeyToPublic($jwk) {
private function convertPrivateKeyToPublic($jwk)
{
if (isset($jwk['d'])) {
unset($jwk['d']);
}
Expand All @@ -325,7 +328,8 @@ private function convertPrivateKeyToPublic($jwk) {
*
* @return integer
*/
private function getSignatureExpirationTime() {
private function getSignatureExpirationTime()
{
date_default_timezone_set("UTC");
$secondsInMinute = 60;
return time() + $this->jwsExpirationMinutes * $secondsInMinute;
Expand All @@ -338,15 +342,16 @@ private function getSignatureExpirationTime() {
*
* @throws HyperwalletException
*/
public function checkJwsExpiration($header) {
if(!isset($header['exp'])) {
public function checkJwsExpiration($header)
{
if (!isset($header['exp'])) {
throw new HyperwalletException('While trying to verify JWS signature no [exp] header is found');
}
$exp = $header['exp'];
if(!is_numeric($exp)) {
if (!is_numeric($exp)) {
throw new HyperwalletException('Wrong value in [exp] header of JWS signature, must be integer');
}
if((int)time() > (int)$exp) {
if ((int)time() > (int)$exp) {
throw new HyperwalletException('JWS signature has expired, checked by [exp] JWS header');
}
}
Expand All @@ -358,10 +363,11 @@ public function checkJwsExpiration($header) {
*
* @throws HyperwalletException
*/
public function getVendorPath() {
public function getVendorPath()
{
$reflector = new \ReflectionClass(ClassLoader::class);
$vendorPath = preg_replace('/^(.*)\/composer\/ClassLoader\.php$/', '$1', $reflector->getFileName() );
if($vendorPath && is_dir($vendorPath)) {
$vendorPath = preg_replace('/^(.*)\/composer\/ClassLoader\.php$/', '$1', $reflector->getFileName());
if ($vendorPath && is_dir($vendorPath)) {
return $vendorPath . '/';
}
throw new HyperwalletException('Failed to find a vendor path');
Expand Down
8 changes: 8 additions & 0 deletions src/Services/Jose/Exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Services\Jose;

class Exception extends \Exception
{

}
10 changes: 10 additions & 0 deletions src/Services/Jose/Exception/DecryptionFailed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Services\Jose\Exception;

use Services\Jose\Exception;

class DecryptionFailed extends Exception
{

}
10 changes: 10 additions & 0 deletions src/Services/Jose/Exception/EncryptionFailed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Services\Jose\Exception;

use Services\Jose\Exception;

class EncryptionFailed extends Exception
{

}
10 changes: 10 additions & 0 deletions src/Services/Jose/Exception/InvalidFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Services\Jose\Exception;

use Services\Jose\Exception;

class InvalidFormat extends Exception
{

}
10 changes: 10 additions & 0 deletions src/Services/Jose/Exception/UnexpectedAlgorithm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Services\Jose\Exception;

use Services\Jose\Exception;

class UnexpectedAlgorithm extends Exception
{

}
10 changes: 10 additions & 0 deletions src/Services/Jose/Exception/VerificationFailed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Services\Jose\Exception;

use Services\Jose\Exception;

class VerificationFailed extends Exception
{

}
Loading