33namespace Firebase \JWT ;
44
55use DomainException ;
6+ use Exception ;
67use InvalidArgumentException ;
78use UnexpectedValueException ;
89use DateTime ;
@@ -50,6 +51,7 @@ class JWT
5051 'RS256 ' => array ('openssl ' , 'SHA256 ' ),
5152 'RS384 ' => array ('openssl ' , 'SHA384 ' ),
5253 'RS512 ' => array ('openssl ' , 'SHA512 ' ),
54+ 'EdDSA ' => array ('sodium_crypto ' , 'EdDSA ' ),
5355 );
5456
5557 /**
@@ -198,7 +200,7 @@ public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $he
198200 *
199201 * @return string An encrypted message
200202 *
201- * @throws DomainException Unsupported algorithm was specified
203+ * @throws DomainException Unsupported algorithm or bad key was specified
202204 */
203205 public static function sign ($ msg , $ key , $ alg = 'HS256 ' )
204206 {
@@ -214,14 +216,24 @@ public static function sign($msg, $key, $alg = 'HS256')
214216 $ success = \openssl_sign ($ msg , $ signature , $ key , $ algorithm );
215217 if (!$ success ) {
216218 throw new DomainException ("OpenSSL unable to sign data " );
217- } else {
218- if ($ alg === 'ES256 ' ) {
219- $ signature = self ::signatureFromDER ($ signature , 256 );
220- }
221- if ($ alg === 'ES384 ' ) {
222- $ signature = self ::signatureFromDER ($ signature , 384 );
223- }
224- return $ signature ;
219+ }
220+ if ($ alg === 'ES256 ' ) {
221+ $ signature = self ::signatureFromDER ($ signature , 256 );
222+ } elseif ($ alg === 'ES384 ' ) {
223+ $ signature = self ::signatureFromDER ($ signature , 384 );
224+ }
225+ return $ signature ;
226+ case 'sodium_crypto ' :
227+ if (!function_exists ('sodium_crypto_sign_detached ' )) {
228+ throw new DomainException ('libsodium is not available ' );
229+ }
230+ try {
231+ // The last non-empty line is used as the key.
232+ $ lines = array_filter (explode ("\n" , $ key ));
233+ $ key = base64_decode (end ($ lines ));
234+ return sodium_crypto_sign_detached ($ msg , $ key );
235+ } catch (Exception $ e ) {
236+ throw new DomainException ($ e ->getMessage (), 0 , $ e );
225237 }
226238 }
227239 }
@@ -237,7 +249,7 @@ public static function sign($msg, $key, $alg = 'HS256')
237249 *
238250 * @return bool
239251 *
240- * @throws DomainException Invalid Algorithm or OpenSSL failure
252+ * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
241253 */
242254 private static function verify ($ msg , $ signature , $ key , $ alg )
243255 {
@@ -258,6 +270,18 @@ private static function verify($msg, $signature, $key, $alg)
258270 throw new DomainException (
259271 'OpenSSL error: ' . \openssl_error_string ()
260272 );
273+ case 'sodium_crypto ' :
274+ if (!function_exists ('sodium_crypto_sign_verify_detached ' )) {
275+ throw new DomainException ('libsodium is not available ' );
276+ }
277+ try {
278+ // The last non-empty line is used as the key.
279+ $ lines = array_filter (explode ("\n" , $ key ));
280+ $ key = base64_decode (end ($ lines ));
281+ return sodium_crypto_sign_verify_detached ($ signature , $ msg , $ key );
282+ } catch (Exception $ e ) {
283+ throw new DomainException ($ e ->getMessage (), 0 , $ e );
284+ }
261285 case 'hash_hmac ' :
262286 default :
263287 $ hash = \hash_hmac ($ algorithm , $ msg , $ key , true );
0 commit comments