1<?php 2 3/** 4 * PuTTY Formatted EC Key Handler 5 * 6 * PHP version 5 7 * 8 * @category Crypt 9 * @package EC 10 * @author Jim Wigginton <terrafrost@php.net> 11 * @copyright 2015 Jim Wigginton 12 * @license http://www.opensource.org/licenses/mit-license.html MIT License 13 * @link http://phpseclib.sourceforge.net 14 */ 15 16namespace phpseclib3\Crypt\EC\Formats\Keys; 17 18use ParagonIE\ConstantTime\Base64; 19use phpseclib3\Common\Functions\Strings; 20use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor; 21use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve; 22use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve; 23use phpseclib3\Math\BigInteger; 24 25/** 26 * PuTTY Formatted EC Key Handler 27 * 28 * @package EC 29 * @author Jim Wigginton <terrafrost@php.net> 30 * @access public 31 */ 32abstract class PuTTY extends Progenitor 33{ 34 use Common; 35 36 /** 37 * Public Handler 38 * 39 * @var string 40 * @access private 41 */ 42 const PUBLIC_HANDLER = 'phpseclib3\Crypt\EC\Formats\Keys\OpenSSH'; 43 44 /** 45 * Supported Key Types 46 * 47 * @var array 48 * @access private 49 */ 50 protected static $types = [ 51 'ecdsa-sha2-nistp256', 52 'ecdsa-sha2-nistp384', 53 'ecdsa-sha2-nistp521', 54 'ssh-ed25519' 55 ]; 56 57 /** 58 * Break a public or private key down into its constituent components 59 * 60 * @access public 61 * @param string $key 62 * @param string $password optional 63 * @return array 64 */ 65 public static function load($key, $password = '') 66 { 67 $components = parent::load($key, $password); 68 if (!isset($components['private'])) { 69 return $components; 70 } 71 72 $private = $components['private']; 73 74 $temp = Base64::encode(Strings::packSSH2('s', $components['type']) . $components['public']); 75 $components = OpenSSH::load($components['type'] . ' ' . $temp . ' ' . $components['comment']); 76 77 if ($components['curve'] instanceof TwistedEdwardsCurve) { 78 if (Strings::shift($private, 4) != "\0\0\0\x20") { 79 throw new \RuntimeException('Length of ssh-ed25519 key should be 32'); 80 } 81 $components['dA'] = $components['curve']->extractSecret($private); 82 } else { 83 list($components['dA']) = Strings::unpackSSH2('i', $private); 84 $components['curve']->rangeCheck($components['dA']); 85 } 86 87 return $components; 88 } 89 90 /** 91 * Convert a private key to the appropriate format. 92 * 93 * @access public 94 * @param \phpseclib3\Math\BigInteger $privateKey 95 * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve 96 * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey 97 * @param string $password optional 98 * @param array $options optional 99 * @return string 100 */ 101 public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $password = false, array $options = []) 102 { 103 self::initialize_static_variables(); 104 105 $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey)); 106 $name = $public[0]; 107 $public = Base64::decode($public[1]); 108 list(, $length) = unpack('N', Strings::shift($public, 4)); 109 Strings::shift($public, $length); 110 111 // PuTTY pads private keys with a null byte per the following: 112 // https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926 113 if (!$curve instanceof TwistedEdwardsCurve) { 114 $private = $privateKey->toBytes(); 115 if (!(strlen($privateKey->toBits()) & 7)) { 116 $private = "\0$private"; 117 } 118 } 119 120 $private = $curve instanceof TwistedEdwardsCurve ? 121 Strings::packSSH2('s', $privateKey->secret) : 122 Strings::packSSH2('s', $private); 123 124 return self::wrapPrivateKey($public, $private, $name, $password, $options); 125 } 126 127 /** 128 * Convert an EC public key to the appropriate format 129 * 130 * @access public 131 * @param \phpseclib3\Crypt\EC\BaseCurves\Base $curve 132 * @param \phpseclib3\Math\Common\FiniteField[] $publicKey 133 * @return string 134 */ 135 public static function savePublicKey(BaseCurve $curve, array $publicKey) 136 { 137 $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey)); 138 $type = $public[0]; 139 $public = Base64::decode($public[1]); 140 list(, $length) = unpack('N', Strings::shift($public, 4)); 141 Strings::shift($public, $length); 142 143 return self::wrapPublicKey($public, $type); 144 } 145} 146