/** * Descrypts encrypted text * * @param string $ciphertext Text to decrypt * @return string Decrypted text or DECRYPTION_FAILED in case of failure */ public static function decrypt($ciphertext) { $rsa = new RSA(); $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1); $rsa->loadKey(static::getPrivateKey()); $s = new BigInteger($ciphertext, 16); // prevent library error output appearing in the dashboard set_error_handler(function () { /* ignore errors */ }); $cleartext = $rsa->decrypt($s->toBytes()); restore_error_handler(); return $cleartext; }
/** * Execute the console command. * * @return mixed */ public function handle() { $maxInt = 2147483647; $min = new BigInteger(10000000.0); $max = new BigInteger($maxInt); $prime = $max->randomPrime($min, $max); $a = new BigInteger($prime); $b = new BigInteger($maxInt + 1); if (!($inverse = $a->modInverse($b))) { $this->error("An error accured during calculation. Please re-run 'php artisan rocid:generate'."); return; } $random = hexdec(bin2hex(Random::string(4))) & $maxInt; $this->info("Generated numbers (Paste these in config/rockid.php) :\nprime: {$prime}\ninverse: {$inverse}\nrandom: {$random}"); }
protected function execute(InputInterface $input, OutputInterface $output) { $prime = $input->getArgument('prime'); // Calculate the inverse. $a = new BigInteger($prime); $b = new BigInteger(Optimus::MAX_INT + 1); if (!($inverse = $a->modInverse($b))) { $output->writeln('<error>Invalid prime number</>'); return; } $rand = hexdec(bin2hex(Random::string(4))) & Optimus::MAX_INT; $output->writeln('Prime: ' . $prime); $output->writeln('Inverse: ' . $inverse); $output->writeln('Random: ' . $rand); $output->writeln(''); $output->writeln(' new Optimus(' . $prime . ', ' . $inverse . ', ' . $rand . ');'); }
/** * Get the inverse of the current prime. * * @return int */ public function getInverse() { $x = new BigInteger(Optimus::MAX_INT + 1); if (!($inverse = $this->prime->modInverse($x))) { throw new InvalidPrimeException($this->prime); } return (int) $inverse->toString(); }
/** * Connect to an SSHv1 server * * @return Boolean * @access private */ function _connect() { $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); if (!$this->fsock) { user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error {$errno}. {$errstr}")); return false; } $this->server_identification = $init_line = fgets($this->fsock, 255); if (defined('NET_SSH1_LOGGING')) { $this->_append_log('<-', $this->server_identification); $this->_append_log('->', $this->identifier . "\r\n"); } if (!preg_match('#SSH-([0-9\\.]+)-(.+)#', $init_line, $parts)) { user_error('Can only connect to SSH servers'); return false; } if ($parts[1][0] != 1) { user_error("Cannot connect to SSH {$parts['1']} servers"); return false; } fputs($this->fsock, $this->identifier . "\r\n"); $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { user_error('Expected SSH_SMSG_PUBLIC_KEY'); return false; } $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8); $this->_string_shift($response[self::RESPONSE_DATA], 4); $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->server_key_public_exponent = $server_key_public_exponent; $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->server_key_public_modulus = $server_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->host_key_public_exponent = $host_key_public_exponent; $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->host_key_public_modulus = $host_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); // get a list of the supported ciphers extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); foreach ($this->supported_ciphers as $mask => $name) { if (($supported_ciphers_mask & 1 << $mask) == 0) { unset($this->supported_ciphers[$mask]); } } // get a list of the supported authentications extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); foreach ($this->supported_authentications as $mask => $name) { if (($supported_authentications_mask & 1 << $mask) == 0) { unset($this->supported_authentications[$mask]); } } $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie)); $session_key = Random::string(32); $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0)); if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) { $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($server_key_public_exponent, $server_key_public_modulus)); $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($host_key_public_exponent, $host_key_public_modulus)); } else { $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($host_key_public_exponent, $host_key_public_modulus)); $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($server_key_public_exponent, $server_key_public_modulus)); } $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES; $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_SESSION_KEY'); return false; } switch ($cipher) { //case self::CIPHER_NONE: // $this->crypto = new \phpseclib\Crypt\Null(); // break; case self::CIPHER_DES: $this->crypto = new DES(); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 8)); break; case self::CIPHER_3DES: $this->crypto = new TripleDES(TripleDES::MODE_3CBC); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 24)); break; //case self::CIPHER_RC4: // $this->crypto = new RC4(); // $this->crypto->enableContinuousBuffer(); // $this->crypto->setKey(substr($session_key, 0, 16)); // break; } $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { user_error('Expected SSH_SMSG_SUCCESS'); return false; } $this->bitmap = self::MASK_CONNECTED; return true; }
/** * Convert a private key to the appropriate format. * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @param \phpseclib\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @return string */ static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '') { if (count($primes) != 2) { return false; } $raw = array('modulus' => $n->toBytes(true), 'publicExponent' => $e->toBytes(true), 'privateExponent' => $d->toBytes(true), 'prime1' => $primes[1]->toBytes(true), 'prime2' => $primes[2]->toBytes(true), 'exponent1' => $exponents[1]->toBytes(true), 'exponent2' => $exponents[2]->toBytes(true), 'coefficient' => $coefficients[2]->toBytes(true)); $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; $encryption = !empty($password) || is_string($password) ? 'aes256-cbc' : 'none'; $key .= $encryption; $key .= "\r\nComment: " . self::$comment . "\r\n"; $public = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); $source = pack('Na*Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption, strlen(self::$comment), self::$comment, strlen($public), $public); $public = Base64::encode($public); $key .= "Public-Lines: " . (strlen($public) + 63 >> 6) . "\r\n"; $key .= chunk_split($public, 64); $private = pack('Na*Na*Na*Na*', strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'], strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']); if (empty($password) && !is_string($password)) { $source .= pack('Na*', strlen($private), $private); $hashkey = 'putty-private-key-file-mac-key'; } else { $private .= Random::string(16 - (strlen($private) & 15)); $source .= pack('Na*', strlen($private), $private); $crypto = new AES(); $crypto->setKey(static::generateSymmetricKey($password, 32)); $crypto->setIV(str_repeat("", $crypto->getBlockLength() >> 3)); $crypto->disablePadding(); $private = $crypto->encrypt($private); $hashkey = 'putty-private-key-file-mac-key' . $password; } $private = Base64::encode($private); $key .= 'Private-Lines: ' . (strlen($private) + 63 >> 6) . "\r\n"; $key .= chunk_split($private, 64); $hash = new Hash('sha1'); $hash->setKey(sha1($hashkey, true)); $key .= 'Private-MAC: ' . Hex::encode($hash->hash($source)) . "\r\n"; return $key; }
/** * RSAVP1 * * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. * * @access private * @param BigInteger $s * @return BigInteger */ function _rsavp1($s) { if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { user_error('Signature representative out of range'); return false; } return $this->_exponentiate($s); }
/** * Get the index of a revoked certificate. * * @param array $rclist * @param string $serial * @param bool $create optional * @access private * @return int|false */ function _revokedCertificate(&$rclist, $serial, $create = false) { $serial = new BigInteger($serial); foreach ($rclist as $i => $rc) { if (!($serial->compare($rc['userCertificate']))) { return $i; } } if (!$create) { return false; } $i = count($rclist); $rclist[] = array('userCertificate' => $serial, 'revocationDate' => $this->_timeField(@date('D, d M Y H:i:s O'))); return $i; }
/** * RSAVP1 * * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. * * @access private * @param \phpseclib\Math\BigInteger $s * @return bool|\phpseclib\Math\BigInteger */ function _rsavp1($s) { if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($s); }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $modulus = $n->toBytes(true); $publicExponent = $e->toBytes(true); // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>: // RSAPublicKey ::= SEQUENCE { // modulus INTEGER, -- n // publicExponent INTEGER -- e // } $components = array('modulus' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($publicExponent)), $publicExponent)); $RSAPublicKey = pack('Ca*a*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']); $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . chunk_split(base64_encode($RSAPublicKey), 64) . '-----END RSA PUBLIC KEY-----'; return $RSAPublicKey; }
/** * Decrypt data. * * @param string $text PKCS1-v1_5 encoded text. * * @return string Plaintext. */ public function decrypt($text) { $out = ''; $p_len = strlen($this->_key->key['p']); $text = str_split($text, $p_len); $text[count($text) - 1] = str_pad($text[count($text) - 1], $p_len, chr(0), STR_PAD_LEFT); $p = new BigInteger($this->_key->key['p'], 256); $x = new BigInteger($this->_key->key['x'], 256); for ($i = 0, $j = count($text); $i < $j; $i += 2) { $c1 = new BigInteger($text[$i], 256); $c2 = new BigInteger($text[$i + 1], 256); $s = $c1->modPow($x, $p); $m_prime = $s->modInverse($p)->multiply($c2)->divide($p); $em = str_pad($m_prime[1]->toBytes(), $p_len, chr(0), STR_PAD_LEFT); // EME-PKCS1-v1_5 decoding if (ord($em[0]) !== 0 || ord($em[1]) !== 2) { throw new RuntimeException(); } $out .= substr($em, strpos($em, chr(0), 2) + 1); } return $out; }
/** * Logical Exclusive-Or * * @param \phpseclib\Math\BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat> * @return \phpseclib\Math\BigInteger */ function bitwise_xor($x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_xor($this->value, $x->value); return $this->_normalize($temp); case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->_normalize(new static($left ^ $right, 256)); } $length = max(count($this->value), count($x->value)); $result = $this->copy(); $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { $result->value[$i] ^= $x->value[$i]; } return $this->_normalize($result); }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { return "<RSAKeyValue>\r\n" . ' <Modulus>' . Base64::encode($n->toBytes()) . "</Modulus>\r\n" . ' <Exponent>' . Base64::encode($e->toBytes()) . "</Exponent>\r\n" . '</RSAKeyValue>'; }
/** * Generate a random prime number. * * If there's not a prime within the given range, false will be returned. * If more than $timeout seconds have elapsed, give up and return false. * * @param \phpseclib\Math\BigInteger $min * @param \phpseclib\Math\BigInteger $max * @param int $timeout * @return Math_BigInteger|false * @access public * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. */ static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) { $compare = $max->compare($min); if (!$compare) { return $min->isPrime() ? $min : false; } elseif ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; $min = $temp; } static $one, $two; if (!isset($one)) { $one = new static(1); $two = new static(2); } $start = time(); $x = self::random($min, $max); // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>. if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { $p = new static(); $p->value = gmp_nextprime($x->value); if ($p->compare($max) <= 0) { return $p; } if (!$min->equals($x)) { $x = $x->subtract($one); } return self::randomPrime($min, $x); } if ($x->equals($two)) { return $x; } $x->_make_odd(); if ($x->compare($max) > 0) { // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range if ($min->equals($max)) { return false; } $x = clone $min; $x->_make_odd(); } $initial_x = clone $x; while (true) { if ($timeout !== false && time() - $start > $timeout) { return false; } if ($x->isPrime()) { return $x; } $x = $x->add($two); if ($x->compare($max) > 0) { $x = clone $min; if ($x->equals($two)) { return $x; } $x->_make_odd(); } if ($x->equals($initial_x)) { return false; } } }
/** * DSA verify. * * @param string $message Message. * @param string $hash_alg Hash algorithm. * @param \phpseclib\Math\BigInteger $r r. * @param \phpseclib\Math\BigInteger $s s. * * @return bool True if verified. */ public function verify($message, $hash_alg, $r, $s) { $hash = new Crypt\Hash($hash_alg); $hash_m = new BigInteger($hash->hash($message), 256); $g = new BigInteger($this->_key->key['g'], 256); $p = new BigInteger($this->_key->key['p'], 256); $q = new BigInteger($this->_key->key['q'], 256); $y = new BigInteger($this->_key->key['y'], 256); $w = $s->modInverse($q); $hash_m_mul = $hash_m->multiply($w); $u1_base = $hash_m_mul->divide($q); $u1 = $u1_base[1]; $r_mul = $r->multiply($w); $u2_base = $r_mul->divide($q); $u2 = $u2_base[1]; $g_pow = $g->modPow($u1, $p); $y_pow = $y->modPow($u2, $p); $g_pow_mul = $g_pow->multiply($y_pow); $g_pow_mul_mod_base = $g_pow_mul->divide($p); $g_pow_mul_mod = $g_pow_mul_mod_base[1]; $v_base = $g_pow_mul_mod->divide($q); $v = $v_base[1]; return $v->compare($r) == 0; }
public static function sha1($input) { $number = new BigInteger(sha1($input, true), -256); $zero = new BigInteger(0); return ($zero->compare($number) <= 0 ? "" : "-") . ltrim($number->toHex(), "0"); }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $modulus = $n->toBytes(true); $publicExponent = $e->toBytes(true); // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>: // RSAPublicKey ::= SEQUENCE { // modulus INTEGER, -- n // publicExponent INTEGER -- e // } $components = array('modulus' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, ASN1::encodeLength(strlen($publicExponent)), $publicExponent)); $RSAPublicKey = pack('Ca*a*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']); // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. $rsaOID = "0\r\t*†H†÷\r"; // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(3) . ASN1::encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; $RSAPublicKey = pack('Ca*a*', self::ASN1_SEQUENCE, ASN1::encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($RSAPublicKey), 64) . '-----END PUBLIC KEY-----'; return $RSAPublicKey; }
/** * Pure-PHP implementation of SHA384 and SHA512 * * @access private * @param string $m */ function _sha512($m) { static $init384, $init512, $k; if (!isset($k)) { // Initialize variables $init384 = array('cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'); $init512 = array('6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1', '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'); for ($i = 0; $i < 8; $i++) { $init384[$i] = new BigInteger($init384[$i], 16); $init384[$i]->setPrecision(64); $init512[$i] = new BigInteger($init512[$i], 16); $init512[$i]->setPrecision(64); } // Initialize table of round constants // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) $k = array('428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'); for ($i = 0; $i < 80; $i++) { $k[$i] = new BigInteger($k[$i], 16); } } $hash = $this->l == 48 ? $init384 : $init512; // Pre-processing $length = strlen($m); // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 $m .= str_repeat(chr(0), 128 - ($length + 16 & 0x7f)); $m[$length] = chr(0x80); // we don't support hashing strings 512MB long $m .= pack('N4', 0, 0, 0, $length << 3); // Process the message in successive 1024-bit chunks $chunks = str_split($m, 128); foreach ($chunks as $chunk) { $w = array(); for ($i = 0; $i < 16; $i++) { $temp = new BigInteger($this->_string_shift($chunk, 8), 256); $temp->setPrecision(64); $w[] = $temp; } // Extend the sixteen 32-bit words into eighty 32-bit words for ($i = 16; $i < 80; $i++) { $temp = array($w[$i - 15]->bitwise_rightRotate(1), $w[$i - 15]->bitwise_rightRotate(8), $w[$i - 15]->bitwise_rightShift(7)); $s0 = $temp[0]->bitwise_xor($temp[1]); $s0 = $s0->bitwise_xor($temp[2]); $temp = array($w[$i - 2]->bitwise_rightRotate(19), $w[$i - 2]->bitwise_rightRotate(61), $w[$i - 2]->bitwise_rightShift(6)); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); $w[$i] = $w[$i - 16]->copy(); $w[$i] = $w[$i]->add($s0); $w[$i] = $w[$i]->add($w[$i - 7]); $w[$i] = $w[$i]->add($s1); } // Initialize hash value for this chunk $a = $hash[0]->copy(); $b = $hash[1]->copy(); $c = $hash[2]->copy(); $d = $hash[3]->copy(); $e = $hash[4]->copy(); $f = $hash[5]->copy(); $g = $hash[6]->copy(); $h = $hash[7]->copy(); // Main loop for ($i = 0; $i < 80; $i++) { $temp = array($a->bitwise_rightRotate(28), $a->bitwise_rightRotate(34), $a->bitwise_rightRotate(39)); $s0 = $temp[0]->bitwise_xor($temp[1]); $s0 = $s0->bitwise_xor($temp[2]); $temp = array($a->bitwise_and($b), $a->bitwise_and($c), $b->bitwise_and($c)); $maj = $temp[0]->bitwise_xor($temp[1]); $maj = $maj->bitwise_xor($temp[2]); $t2 = $s0->add($maj); $temp = array($e->bitwise_rightRotate(14), $e->bitwise_rightRotate(18), $e->bitwise_rightRotate(41)); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); $temp = array($e->bitwise_and($f), $g->bitwise_and($e->bitwise_not())); $ch = $temp[0]->bitwise_xor($temp[1]); $t1 = $h->add($s1); $t1 = $t1->add($ch); $t1 = $t1->add($k[$i]); $t1 = $t1->add($w[$i]); $h = $g->copy(); $g = $f->copy(); $f = $e->copy(); $e = $d->add($t1); $d = $c->copy(); $c = $b->copy(); $b = $a->copy(); $a = $t1->add($t2); } // Add this chunk's hash to result so far $hash = array($hash[0]->add($a), $hash[1]->add($b), $hash[2]->add($c), $hash[3]->add($d), $hash[4]->add($e), $hash[5]->add($f), $hash[6]->add($g), $hash[7]->add($h)); } // Produce the final hash value (big-endian) // (\phpseclib\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . $hash[4]->toBytes() . $hash[5]->toBytes(); if ($this->l != 48) { $temp .= $hash[6]->toBytes() . $hash[7]->toBytes(); } return $temp; }
/** * Convert a private key to the appropriate format. * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @param \phpseclib\Math\BigInteger $d * @param array $primes * @param array $exponents * @param array $coefficients * @param string $password optional * @return string */ static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, $primes, $exponents, $coefficients, $password = '') { $n = strrev($n->toBytes()); $e = str_pad(strrev($e->toBytes()), 4, ""); $key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); $key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e); $key .= $n; $key .= strrev($primes[1]->toBytes()); $key .= strrev($primes[2]->toBytes()); $key .= strrev($exponents[1]->toBytes()); $key .= strrev($exponents[2]->toBytes()); $key .= strrev($coefficients[1]->toBytes()); $key .= strrev($d->toBytes()); return base64_encode($key); }
/** * Returns the server public host key. * * Caching this the first time you connect to a server and checking the result on subsequent connections * is recommended. Returns false if the server signature is not signed correctly with the public host key. * * @return Mixed * @access public */ function getServerPublicHostKey() { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { if (!$this->_connect()) { return false; } } $signature = $this->signature; $server_public_host_key = $this->server_public_host_key; extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4))); $this->_string_shift($server_public_host_key, $length); if ($this->signature_validated) { return $this->bitmap ? $this->signature_format . ' ' . base64_encode($this->server_public_host_key) : false; } $this->signature_validated = true; switch ($this->signature_format) { case 'ssh-dss': $zero = new BigInteger(); $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); /* The value for 'dss_signature_blob' is encoded as a string containing r, followed by s (which are 160-bit integers, without lengths or padding, unsigned, and in network byte order). */ $temp = unpack('Nlength', $this->_string_shift($signature, 4)); if ($temp['length'] != 40) { user_error('Invalid signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $r = new BigInteger($this->_string_shift($signature, 20), 256); $s = new BigInteger($this->_string_shift($signature, 20), 256); switch (true) { case $r->equals($zero): case $r->compare($q) >= 0: case $s->equals($zero): case $s->compare($q) >= 0: user_error('Invalid signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $w = $s->modInverse($q); $u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16)); list(, $u1) = $u1->divide($q); $u2 = $w->multiply($r); list(, $u2) = $u2->divide($q); $g = $g->modPow($u1, $p); $y = $y->modPow($u2, $p); $v = $g->multiply($y); list(, $v) = $v->divide($p); list(, $v) = $v->divide($q); if (!$v->equals($r)) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; case 'ssh-rsa': $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $rawN = $this->_string_shift($server_public_host_key, $temp['length']); $n = new BigInteger($rawN, -256); $nLength = strlen(ltrim($rawN, "")); /* $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $signature = $this->_string_shift($signature, $temp['length']); $rsa = new RSA(); $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); if (!$rsa->verify($this->exchange_hash, $signature)) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } */ $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256); // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the // following URL: // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { user_error('Invalid signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $s = $s->modPow($e, $n); $s = $s->toBytes(); $h = pack('N4H*', 0x302130, 0x906052b, 0xe03021a, 0x5000414, sha1($this->exchange_hash)); $h = chr(0x1) . str_repeat(chr(0xff), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; default: user_error('Unsupported signature format'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); }
/** * RSAVP1 * * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. * * @access private * @param \phpseclib\Math\BigInteger $s * @throws \OutOfRangeException if $s < 0 or $s > $this->modulus * @return \phpseclib\Math\BigInteger */ function _rsavp1($s) { if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { throw new \OutOfRangeException('Signature representative out of range'); } return $this->_exponentiate($s); }
/** * ASN.1 Encode (Helper function) * * @param String $source * @param String $mapping * @param Integer $idx * * @return String * @access private */ function _encode_der($source, $mapping, $idx = null, $special = array()) { if ($source instanceof Element) { return $source->element; } // do not encode (implicitly optional) fields with value set to default if (isset($mapping['default']) && $source === $mapping['default']) { return ''; } if (isset($idx)) { if (isset($special[$idx])) { $source = call_user_func($special[$idx], $source); } $this->location[] = $idx; } $tag = $mapping['type']; switch ($tag) { case self::TYPE_SET: // Children order is not important, thus process in sequence. // Children order is not important, thus process in sequence. case self::TYPE_SEQUENCE: $tag |= 0x20; // set the constructed bit $value = ''; // ignore the min and max if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($source as $content) { $temp = $this->_encode_der($content, $child, null, $special); if ($temp === false) { return false; } $value .= $temp; } break; } foreach ($mapping['children'] as $key => $child) { if (!array_key_exists($key, $source)) { if (!isset($child['optional'])) { return false; } continue; } $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } // An empty child encoding means it has been optimized out. // Else we should have at least one tag byte. if ($temp === '') { continue; } // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { /* From X.680-0207.pdf#page=58 (30.6): "The tagging construction specifies explicit tagging if any of the following holds: ... c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)." */ if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']); $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; } else { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | ord($temp[0]) & 0x20 | $child['constant']); $temp = $subtag . substr($temp, 1); } } $value .= $temp; } break; case self::TYPE_CHOICE: $temp = false; foreach ($mapping['children'] as $key => $child) { if (!isset($source[$key])) { continue; } $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } // An empty child encoding means it has been optimized out. // Else we should have at least one tag byte. if ($temp === '') { continue; } $tag = ord($temp[0]); // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | 0x20 | $child['constant']); $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; } else { $subtag = chr(self::CLASS_CONTEXT_SPECIFIC << 6 | ord($temp[0]) & 0x20 | $child['constant']); $temp = $subtag . substr($temp, 1); } } } if (isset($idx)) { array_pop($this->location); } if ($temp && isset($mapping['cast'])) { $temp[0] = chr($mapping['class'] << 6 | $tag & 0x20 | $mapping['cast']); } return $temp; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: if (!isset($mapping['mapping'])) { if (is_numeric($source)) { $source = new BigInteger($source); } $value = $source->toBytes(true); } else { $value = array_search($source, $mapping['mapping']); if ($value === false) { return false; } $value = new BigInteger($value); $value = $value->toBytes(true); } if (!strlen($value)) { $value = chr(0); } break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format .= 'mdHis'; $value = @gmdate($format, strtotime($source)) . 'Z'; break; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $bits = array_fill(0, count($mapping['mapping']), 0); $size = 0; for ($i = 0; $i < count($mapping['mapping']); $i++) { if (in_array($mapping['mapping'][$i], $source)) { $bits[$i] = 1; $size = $i; } } if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { $size = $mapping['min'] - 1; } $offset = 8 - ($size + 1 & 7); $offset = $offset !== 8 ? $offset : 0; $value = chr($offset); for ($i = $size + 1; $i < count($mapping['mapping']); $i++) { unset($bits[$i]); } $bits = implode('', array_pad($bits, $size + $offset + 1, 0)); $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' '))); foreach ($bytes as $byte) { $value .= chr(bindec($byte)); } break; } case self::TYPE_OCTET_STRING: /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ $value = base64_decode($source); break; case self::TYPE_OBJECT_IDENTIFIER: $oid = preg_match('#(?:\\d+\\.)+#', $source) ? $source : array_search($source, $this->oids); if ($oid === false) { user_error('Invalid OID'); return false; } $value = ''; $parts = explode('.', $oid); $value = chr(40 * $parts[0] + $parts[1]); for ($i = 2; $i < count($parts); $i++) { $temp = ''; if (!$parts[$i]) { $temp = ""; } else { while ($parts[$i]) { $temp = chr(0x80 | $parts[$i] & 0x7f) . $temp; $parts[$i] >>= 7; } $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7f); } $value .= $temp; } break; case self::TYPE_ANY: $loc = $this->location; if (isset($idx)) { array_pop($this->location); } switch (true) { case !isset($source): return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special); case is_int($source): case $source instanceof BigInteger: return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special); case is_float($source): return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special); case is_bool($source): return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special); case is_array($source) && count($source) == 1: $typename = implode('', array_keys($source)); $outtype = array_search($typename, $this->ANYmap, true); if ($outtype !== false) { return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special); } } $filters = $this->filters; foreach ($loc as $part) { if (!isset($filters[$part])) { $filters = false; break; } $filters = $filters[$part]; } if ($filters === false) { user_error('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); case self::TYPE_NULL: $value = ''; break; case self::TYPE_NUMERIC_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_UNIVERSAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: case self::TYPE_IA5_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_GENERAL_STRING: $value = $source; break; case self::TYPE_BOOLEAN: $value = $source ? "ÿ" : ""; break; default: user_error('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } if (isset($idx)) { array_pop($this->location); } if (isset($mapping['cast'])) { if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) { $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value; $tag = $mapping['class'] << 6 | 0x20 | $mapping['cast']; } else { $tag = $mapping['class'] << 6 | ord($temp[0]) & 0x20 | $mapping['cast']; } } return chr($tag) . $this->_encodeLength(strlen($value)) . $value; }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $publicExponent = $e->toBytes(true); $modulus = $n->toBytes(true); // from <http://tools.ietf.org/html/rfc4253#page-15>: // string "ssh-rsa" // mpint e // mpint n $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); $RSAPublicKey = 'ssh-rsa ' . Base64::encode($RSAPublicKey) . ' ' . self::$comment; return $RSAPublicKey; }