Skip to content

Commit 5d154c2

Browse files
authored
feat: use base64 fn from web-token/jwt-library (#438)
Supports native base64 de-/encoding with extension `sodium`. Move dependency `spomky-labs/base64url` to test for cross testing.
1 parent b1216d2 commit 5d154c2

File tree

8 files changed

+35
-31
lines changed

8 files changed

+35
-31
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
php: [ '8.2', '8.3', '8.4', '8.5' ]
2121
ext_base: [ 'none, dom, tokenizer, xml, xmlwriter,' ]
2222
ext_lib: [ 'curl, mbstring, openssl,' ]
23-
ext_optional: [ '', 'bcmath', 'gmp' ]
23+
ext_optional: [ '', 'bcmath', 'gmp', 'sodium' ]
2424

2525
name: PHP ${{ matrix.php }} (${{ matrix.ext_optional }})
2626

.php-cs-fixer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
'concat_space' => ['spacing' => 'none'], // Custom library style.
1010
'declare_strict_types' => true, // Enforce strict code.
1111
'global_namespace_import' => ['import_classes' => false, 'import_constants' => false, 'import_functions' => false],
12+
'ordered_imports' => ['imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha'],
1213
'php_unit_attributes' => true,
1314
'php_unit_construct' => true,
1415
'php_unit_method_casing' => true,

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
"ext-mbstring": "*",
3535
"ext-openssl": "*",
3636
"guzzlehttp/guzzle": "^7.9.2",
37-
"spomky-labs/base64url": "^2.0.4",
3837
"symfony/polyfill-php83": "^1.33",
3938
"web-token/jwt-library": "^3.4.9|^4.0.6"
4039
},
@@ -49,6 +48,7 @@
4948
"phpstan/phpstan-phpunit": "^2.0",
5049
"phpstan/phpstan-strict-rules": "^2.0",
5150
"phpunit/phpunit": "^11.5.46|^12.5.2",
51+
"spomky-labs/base64url": "^2.0.4",
5252
"symfony/polyfill-iconv": "^1.33"
5353
},
5454
"autoload": {

src/Encryption.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
namespace Minishlink\WebPush;
1212

13-
use Base64Url\Base64Url;
1413
use Jose\Component\Core\JWK;
14+
use Jose\Component\Core\Util\Base64UrlSafe;
1515
use Jose\Component\Core\Util\Ecc\PrivateKey;
1616
use Jose\Component\Core\Util\ECKey;
1717

@@ -76,8 +76,8 @@ public static function deterministicEncrypt(
7676
array $localKeyObject,
7777
string $salt
7878
): array {
79-
$userPublicKey = Base64Url::decode($userPublicKey);
80-
$userAuthToken = Base64Url::decode($userAuthToken);
79+
$userPublicKey = Base64UrlSafe::decode($userPublicKey);
80+
$userAuthToken = Base64UrlSafe::decode($userAuthToken);
8181

8282
// get local key pair
8383
if (count($localKeyObject) === 1) {
@@ -91,9 +91,9 @@ public static function deterministicEncrypt(
9191
$localJwk = new JWK([
9292
'kty' => 'EC',
9393
'crv' => 'P-256',
94-
'd' => Base64Url::encode($localPrivateKeyObject->getSecret()->toBytes(false)),
95-
'x' => Base64Url::encode($localPublicKeyObject[0]),
96-
'y' => Base64Url::encode($localPublicKeyObject[1]),
94+
'd' => Base64UrlSafe::encodeUnpadded($localPrivateKeyObject->getSecret()->toBytes(false)),
95+
'x' => Base64UrlSafe::encodeUnpadded($localPublicKeyObject[0]),
96+
'y' => Base64UrlSafe::encodeUnpadded($localPublicKeyObject[1]),
9797
]);
9898
}
9999
if (!$localPublicKey) {
@@ -105,8 +105,8 @@ public static function deterministicEncrypt(
105105
$userJwk = new JWK([
106106
'kty' => 'EC',
107107
'crv' => 'P-256',
108-
'x' => Base64Url::encode($userPublicKeyObjectX),
109-
'y' => Base64Url::encode($userPublicKeyObjectY),
108+
'x' => Base64UrlSafe::encodeUnpadded($userPublicKeyObjectX),
109+
'y' => Base64UrlSafe::encodeUnpadded($userPublicKeyObjectY),
110110
]);
111111

112112
// get shared secret from user public key and local private key
@@ -267,9 +267,9 @@ private static function createLocalKeyObject(): array
267267
new JWK([
268268
'kty' => 'EC',
269269
'crv' => 'P-256',
270-
'x' => Base64Url::encode(self::addNullPadding($details['ec']['x'])),
271-
'y' => Base64Url::encode(self::addNullPadding($details['ec']['y'])),
272-
'd' => Base64Url::encode(self::addNullPadding($details['ec']['d'])),
270+
'x' => Base64UrlSafe::encodeUnpadded(self::addNullPadding($details['ec']['x'])),
271+
'y' => Base64UrlSafe::encodeUnpadded(self::addNullPadding($details['ec']['y'])),
272+
'd' => Base64UrlSafe::encodeUnpadded(self::addNullPadding($details['ec']['d'])),
273273
]),
274274
];
275275
}

src/Utils.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
namespace Minishlink\WebPush;
1212

13-
use Base64Url\Base64Url;
1413
use Jose\Component\Core\JWK;
14+
use Jose\Component\Core\Util\Base64UrlSafe;
1515
use Jose\Component\Core\Util\Ecc\PublicKey;
1616

1717
class Utils
@@ -34,8 +34,8 @@ public static function serializePublicKey(PublicKey $publicKey): string
3434
public static function serializePublicKeyFromJWK(JWK $jwk): string
3535
{
3636
$hexString = '04';
37-
$hexString .= str_pad(bin2hex(Base64Url::decode($jwk->get('x'))), 64, '0', STR_PAD_LEFT);
38-
$hexString .= str_pad(bin2hex(Base64Url::decode($jwk->get('y'))), 64, '0', STR_PAD_LEFT);
37+
$hexString .= str_pad(bin2hex(Base64UrlSafe::decode($jwk->get('x'))), 64, '0', STR_PAD_LEFT);
38+
$hexString .= str_pad(bin2hex(Base64UrlSafe::decode($jwk->get('y'))), 64, '0', STR_PAD_LEFT);
3939

4040
return $hexString;
4141
}

src/VAPID.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
namespace Minishlink\WebPush;
1212

13-
use Base64Url\Base64Url;
1413
use Jose\Component\Core\AlgorithmManager;
1514
use Jose\Component\Core\JWK;
15+
use Jose\Component\Core\Util\Base64UrlSafe;
1616
use Jose\Component\KeyManagement\JWKFactory;
1717
use Jose\Component\Signature\Algorithm\ES256;
1818
use Jose\Component\Signature\JWSBuilder;
@@ -69,14 +69,14 @@ public static function validate(array $vapid): array
6969
throw new \ErrorException('[VAPID] Failed to convert VAPID public key from hexadecimal to binary.');
7070
}
7171
$vapid['publicKey'] = base64_encode($binaryPublicKey);
72-
$vapid['privateKey'] = base64_encode(str_pad(Base64Url::decode($jwk->get('d')), self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
72+
$vapid['privateKey'] = base64_encode(str_pad(Base64UrlSafe::decode($jwk->get('d')), self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
7373
}
7474

7575
if (!isset($vapid['publicKey'])) {
7676
throw new \ErrorException('[VAPID] You must provide a public key.');
7777
}
7878

79-
$publicKey = Base64Url::decode($vapid['publicKey']);
79+
$publicKey = Base64UrlSafe::decode($vapid['publicKey']);
8080

8181
if (Utils::safeStrlen($publicKey) !== self::PUBLIC_KEY_LENGTH) {
8282
throw new \ErrorException('[VAPID] Public key should be 65 bytes long when decoded.');
@@ -86,7 +86,7 @@ public static function validate(array $vapid): array
8686
throw new \ErrorException('[VAPID] You must provide a private key.');
8787
}
8888

89-
$privateKey = Base64Url::decode($vapid['privateKey']);
89+
$privateKey = Base64UrlSafe::decode($vapid['privateKey']);
9090

9191
if (Utils::safeStrlen($privateKey) !== self::PRIVATE_KEY_LENGTH) {
9292
throw new \ErrorException('[VAPID] Private key should be 32 bytes long when decoded.');
@@ -148,9 +148,9 @@ public static function getVapidHeaders(
148148
$jwk = new JWK([
149149
'kty' => 'EC',
150150
'crv' => 'P-256',
151-
'x' => Base64Url::encode($x),
152-
'y' => Base64Url::encode($y),
153-
'd' => Base64Url::encode($privateKey),
151+
'x' => Base64UrlSafe::encodeUnpadded($x),
152+
'y' => Base64UrlSafe::encodeUnpadded($y),
153+
'd' => Base64UrlSafe::encodeUnpadded($privateKey),
154154
]);
155155

156156
$jwsCompactSerializer = new CompactSerializer();
@@ -162,7 +162,7 @@ public static function getVapidHeaders(
162162
->build();
163163

164164
$jwt = $jwsCompactSerializer->serialize($jws, 0);
165-
$encodedPublicKey = Base64Url::encode($publicKey);
165+
$encodedPublicKey = Base64UrlSafe::encodeUnpadded($publicKey);
166166

167167
if ($contentEncoding === ContentEncoding::aesgcm) {
168168
return [
@@ -196,14 +196,14 @@ public static function createVapidKeys(): array
196196
throw new \ErrorException('Failed to convert VAPID public key from hexadecimal to binary.');
197197
}
198198

199-
$binaryPrivateKey = hex2bin(str_pad(bin2hex(Base64Url::decode($jwk->get('d'))), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
199+
$binaryPrivateKey = hex2bin(str_pad(bin2hex(Base64UrlSafe::decode($jwk->get('d'))), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
200200
if (!$binaryPrivateKey) {
201201
throw new \ErrorException('Failed to convert VAPID private key from hexadecimal to binary.');
202202
}
203203

204204
return [
205-
'publicKey' => Base64Url::encode($binaryPublicKey),
206-
'privateKey' => Base64Url::encode($binaryPrivateKey),
205+
'publicKey' => Base64UrlSafe::encode($binaryPublicKey),
206+
'privateKey' => Base64UrlSafe::encode($binaryPrivateKey),
207207
];
208208
}
209209
}

src/WebPush.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
namespace Minishlink\WebPush;
1212

13-
use Base64Url\Base64Url;
1413
use GuzzleHttp\Client;
15-
use GuzzleHttp\Pool;
1614
use GuzzleHttp\Exception\ConnectException;
1715
use GuzzleHttp\Exception\RequestException;
16+
use GuzzleHttp\Pool;
1817
use GuzzleHttp\Psr7\Request;
18+
use Jose\Component\Core\Util\Base64UrlSafe;
1919
use Psr\Http\Message\RequestInterface;
2020
use Psr\Http\Message\ResponseInterface;
2121

@@ -260,8 +260,8 @@ protected function prepare(array $notifications): array
260260
];
261261

262262
if ($contentEncoding === ContentEncoding::aesgcm->value) {
263-
$headers['Encryption'] = 'salt='.Base64Url::encode($salt);
264-
$headers['Crypto-Key'] = 'dh='.Base64Url::encode($localPublicKey);
263+
$headers['Encryption'] = 'salt='.Base64UrlSafe::encode($salt);
264+
$headers['Crypto-Key'] = 'dh='.Base64UrlSafe::encode($localPublicKey);
265265
}
266266

267267
$encryptionContentCodingHeader = Encryption::getContentCodingHeader($salt, $localPublicKey, ContentEncoding::from($contentEncoding));

tests/EncryptionTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ public static function payloadProvider(): array
116116
];
117117
}
118118

119+
/**
120+
* `web-push-php` uses internal base64 functions from `web-token/jwt-library`. For cross testing use `spomky-labs/base64url`.
121+
*/
119122
protected function base64Decode(string $value): string
120123
{
121124
return Base64Url::decode($value);

0 commit comments

Comments
 (0)