A src/Binary.php => src/Binary.php +96 -0
@@ 0,0 1,96 @@
+<?php
+
+/*
+ * Copyright (c) 2019-2020 François Kooman <fkooman@tuxed.net>
+ *
+ * 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.
+ */
+
+namespace fkooman\Jwt;
+
+/**
+ * Taken from paragonie/constant_time_encoding under MIT license
+ * @see https://github.com/paragonie/constant_time_encoding/blob/v1.x/src/Binary.php
+ *
+ * Copyright (c) 2016 - 2017 Paragon Initiative Enterprises.
+ * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
+ */
+class Binary
+{
+ /**
+ * Safe string length
+ *
+ * @ref mbstring.func_overload
+ *
+ * @param string $str
+ * @return int
+ */
+ public static function safeStrlen($str)
+ {
+ if (\function_exists('mb_strlen')) {
+ return \mb_strlen($str, '8bit');
+ } else {
+ return \strlen($str);
+ }
+ }
+
+ /**
+ * Safe substring
+ *
+ * @ref mbstring.func_overload
+ *
+ * @staticvar boolean $exists
+ * @param string $str
+ * @param int $start
+ * @param int $length
+ * @throws \TypeError
+ * @return string
+ */
+ public static function safeSubstr(
+ $str,
+ $start = 0,
+ $length = \null
+ ) {
+ if (\function_exists('mb_substr')) {
+ // mb_substr($str, 0, null, '8bit') returns an empty string on PHP
+ // 5.3, so we have to find the length ourselves.
+ if (\is_null($length)) {
+ if ($start >= 0) {
+ $length = self::safeStrlen($str) - $start;
+ } else {
+ $length = -$start;
+ }
+ }
+ // $length calculation above might result in a 0-length string
+ if ($length === 0) {
+ return '';
+ }
+ return \mb_substr($str, $start, $length, '8bit');
+ }
+ if ($length === 0) {
+ return '';
+ }
+ // Unlike mb_substr(), substr() doesn't accept null for length
+ if (!is_null($length)) {
+ return \substr($str, $start, $length);
+ } else {
+ return \substr($str, $start);
+ }
+ }
+}
M src/Keys/EdDSA/PublicKey.php => src/Keys/EdDSA/PublicKey.php +2 -1
@@ 25,6 25,7 @@
namespace fkooman\Jwt\Keys\EdDSA;
use fkooman\Jwt\Base64UrlSafe;
+use fkooman\Jwt\Binary;
use LengthException;
class PublicKey
@@ 37,7 38,7 @@ class PublicKey
*/
public function __construct($publicKey)
{
- if (SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen($publicKey)) {
+ if (SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== Binary::safeStrlen($publicKey)) {
throw new LengthException('invalid public key length');
}
$this->publicKey = $publicKey;
M src/Keys/EdDSA/SecretKey.php => src/Keys/EdDSA/SecretKey.php +4 -3
@@ 25,6 25,7 @@
namespace fkooman\Jwt\Keys\EdDSA;
use fkooman\Jwt\Base64UrlSafe;
+use fkooman\Jwt\Binary;
class SecretKey
{
@@ 36,15 37,15 @@ class SecretKey
*/
public function __construct($secretKey)
{
- switch (strlen($secretKey)) {
+ switch (Binary::safeStrlen($secretKey)) {
case SODIUM_CRYPTO_SIGN_SECRETKEYBYTES:
$this->secretKey = $secretKey;
break;
case SODIUM_CRYPTO_SIGN_SEEDBYTES:
- $this->secretKey = substr(\sodium_crypto_sign_seed_keypair($secretKey), 0, 64);
+ $this->secretKey = Binary::safeSubstr(\sodium_crypto_sign_seed_keypair($secretKey), 0, 64);
break;
case SODIUM_CRYPTO_SIGN_KEYPAIRBYTES:
- $this->secretKey = substr($secretKey, 0, 64);
+ $this->secretKey = Binary::safeSubstr($secretKey, 0, 64);
break;
default:
throw new \LengthException('invalid secret key length');
M src/Keys/HS256/SecretKey.php => src/Keys/HS256/SecretKey.php +2 -2
@@ 25,7 25,7 @@
namespace fkooman\Jwt\Keys\HS256;
use fkooman\Jwt\Base64UrlSafe;
-
+use fkooman\Jwt\Binary;
use fkooman\Jwt\Exception\KeyException;
class SecretKey
@@ 41,7 41,7 @@ class SecretKey
*/
public function __construct($secretKey)
{
- if (32 !== strlen($secretKey)) {
+ if (32 !== Binary::safeStrlen($secretKey)) {
throw new KeyException('invalid key length');
}
$this->secretKey = $secretKey;
M src/Keys/RS256/PrivateKey.php => src/Keys/RS256/PrivateKey.php +2 -1
@@ 24,6 24,7 @@
namespace fkooman\Jwt\Keys\RS256;
+use fkooman\Jwt\Binary;
use fkooman\Jwt\Exception\KeyException;
use RuntimeException;
@@ 51,7 52,7 @@ class PrivateKey
$rsaInfo = $keyInfo['rsa'];
// RSA key MUST be at least 2048 bits
// @see https://tools.ietf.org/html/rfc7518#section-4.2
- if (256 > strlen($rsaInfo['n'])) {
+ if (256 > Binary::safeStrlen($rsaInfo['n'])) {
throw new KeyException('invalid RSA key, must be >= 2048 bits');
}
$this->privateKey = $privateKey;