Subversion Repositories oidplus

Rev

Blame | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /**
  4.  * JSON Web Key (RFC7517 / RFC8037) Formatted EC Handler
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * @author    Jim Wigginton <terrafrost@php.net>
  9.  * @copyright 2015 Jim Wigginton
  10.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  11.  * @link      http://phpseclib.sourceforge.net
  12.  */
  13.  
  14. namespace phpseclib3\Crypt\EC\Formats\Keys;
  15.  
  16. use phpseclib3\Common\Functions\Strings;
  17. use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
  18. use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
  19. use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
  20. use phpseclib3\Crypt\EC\Curves\Ed25519;
  21. use phpseclib3\Crypt\EC\Curves\secp256k1;
  22. use phpseclib3\Crypt\EC\Curves\secp256r1;
  23. use phpseclib3\Crypt\EC\Curves\secp384r1;
  24. use phpseclib3\Crypt\EC\Curves\secp521r1;
  25. use phpseclib3\Exception\UnsupportedCurveException;
  26. use phpseclib3\Math\BigInteger;
  27.  
  28. /**
  29.  * JWK Formatted EC Handler
  30.  *
  31.  * @author  Jim Wigginton <terrafrost@php.net>
  32.  */
  33. abstract class JWK extends Progenitor
  34. {
  35.     use Common;
  36.  
  37.     /**
  38.      * Break a public or private key down into its constituent components
  39.      *
  40.      * @param string $key
  41.      * @param string $password optional
  42.      * @return array
  43.      */
  44.     public static function load($key, $password = '')
  45.     {
  46.         $key = parent::load($key, $password);
  47.  
  48.         switch ($key->kty) {
  49.             case 'EC':
  50.                 switch ($key->crv) {
  51.                     case 'P-256':
  52.                     case 'P-384':
  53.                     case 'P-521':
  54.                     case 'secp256k1':
  55.                         break;
  56.                     default:
  57.                         throw new UnsupportedCurveException('Only P-256, P-384, P-521 and secp256k1 curves are accepted (' . $key->crv . ' provided)');
  58.                 }
  59.                 break;
  60.             case 'OKP':
  61.                 switch ($key->crv) {
  62.                     case 'Ed25519':
  63.                     case 'Ed448':
  64.                         break;
  65.                     default:
  66.                         throw new UnsupportedCurveException('Only Ed25519 and Ed448 curves are accepted (' . $key->crv . ' provided)');
  67.                 }
  68.                 break;
  69.             default:
  70.                 throw new \Exception('Only EC and OKP JWK keys are supported');
  71.         }
  72.  
  73.         $curve = '\phpseclib3\Crypt\EC\Curves\\' . str_replace('P-', 'nistp', $key->crv);
  74.         $curve = new $curve();
  75.  
  76.         if ($curve instanceof TwistedEdwardsCurve) {
  77.             $QA = self::extractPoint(Strings::base64url_decode($key->x), $curve);
  78.             if (!isset($key->d)) {
  79.                 return compact('curve', 'QA');
  80.             }
  81.             $arr = $curve->extractSecret(Strings::base64url_decode($key->d));
  82.             return compact('curve', 'QA') + $arr;
  83.         }
  84.  
  85.         $QA = [
  86.             $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->x), 256)),
  87.             $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->y), 256))
  88.         ];
  89.  
  90.         if (!$curve->verifyPoint($QA)) {
  91.             throw new \RuntimeException('Unable to verify that point exists on curve');
  92.         }
  93.  
  94.         if (!isset($key->d)) {
  95.             return compact('curve', 'QA');
  96.         }
  97.  
  98.         $dA = new BigInteger(Strings::base64url_decode($key->d), 256);
  99.  
  100.         $curve->rangeCheck($dA);
  101.  
  102.         return compact('curve', 'dA', 'QA');
  103.     }
  104.  
  105.     /**
  106.      * Returns the alias that corresponds to a curve
  107.      *
  108.      * @return string
  109.      */
  110.     private static function getAlias(BaseCurve $curve)
  111.     {
  112.         switch (true) {
  113.             case $curve instanceof secp256r1:
  114.                 return 'P-256';
  115.             case $curve instanceof secp384r1:
  116.                 return 'P-384';
  117.             case $curve instanceof secp521r1:
  118.                 return 'P-521';
  119.             case $curve instanceof secp256k1:
  120.                 return 'secp256k1';
  121.         }
  122.  
  123.         $reflect = new \ReflectionClass($curve);
  124.         $curveName = $reflect->isFinal() ?
  125.             $reflect->getParentClass()->getShortName() :
  126.             $reflect->getShortName();
  127.         throw new UnsupportedCurveException("$curveName is not a supported curve");
  128.     }
  129.  
  130.     /**
  131.      * Return the array superstructure for an EC public key
  132.      *
  133.      * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
  134.      * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
  135.      * @return array
  136.      */
  137.     private static function savePublicKeyHelper(BaseCurve $curve, array $publicKey)
  138.     {
  139.         if ($curve instanceof TwistedEdwardsCurve) {
  140.             return [
  141.                 'kty' => 'OKP',
  142.                 'crv' => $curve instanceof Ed25519 ? 'Ed25519' : 'Ed448',
  143.                 'x' => Strings::base64url_encode($curve->encodePoint($publicKey))
  144.             ];
  145.         }
  146.  
  147.         return [
  148.             'kty' => 'EC',
  149.             'crv' => self::getAlias($curve),
  150.             'x' => Strings::base64url_encode($publicKey[0]->toBytes()),
  151.             'y' => Strings::base64url_encode($publicKey[1]->toBytes())
  152.         ];
  153.     }
  154.  
  155.     /**
  156.      * Convert an EC public key to the appropriate format
  157.      *
  158.      * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve
  159.      * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
  160.      * @param array $options optional
  161.      * @return string
  162.      */
  163.     public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
  164.     {
  165.         $key = self::savePublicKeyHelper($curve, $publicKey);
  166.  
  167.         return self::wrapKey($key, $options);
  168.     }
  169.  
  170.     /**
  171.      * Convert a private key to the appropriate format.
  172.      *
  173.      * @param \phpseclib3\Math\BigInteger $privateKey
  174.      * @param \phpseclib3\Crypt\EC\Curves\Ed25519 $curve
  175.      * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
  176.      * @param string $secret optional
  177.      * @param string $password optional
  178.      * @param array $options optional
  179.      * @return string
  180.      */
  181.     public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
  182.     {
  183.         $key = self::savePublicKeyHelper($curve, $publicKey);
  184.         $key['d'] = $curve instanceof TwistedEdwardsCurve ? $secret : $privateKey->toBytes();
  185.         $key['d'] = Strings::base64url_encode($key['d']);
  186.  
  187.         return self::wrapKey($key, $options);
  188.     }
  189. }
  190.