From b1b1c048bd5428405ee0c442321a25a5fccb1b5d Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Mon, 27 Jun 2016 14:10:54 -0400 Subject: [PATCH 01/21] updated to work on the lastest version of PocketMine and Genisys (MCPE 0.15.x) --- src/ParagonIE/.gitignore | 2 + src/ParagonIE/.travis.yml | 15 + src/ParagonIE/ConstantTime/Base32.php | 396 ++ src/ParagonIE/ConstantTime/Base32Hex.php | 111 + src/ParagonIE/ConstantTime/Base64.php | 229 + src/ParagonIE/ConstantTime/Base64DotSlash.php | 88 + .../ConstantTime/Base64DotSlashOrdered.php | 82 + src/ParagonIE/ConstantTime/Base64UrlSafe.php | 95 + src/ParagonIE/ConstantTime/Binary.php | 98 + .../ConstantTime/EncoderInterface.php | 52 + src/ParagonIE/ConstantTime/Encoding.php | 245 + src/ParagonIE/ConstantTime/Hex.php | 132 + src/ParagonIE/ConstantTime/RFC4648.php | 166 + src/phpseclib/Crypt/AES.php | 169 +- src/phpseclib/Crypt/Base.php | 1608 +++++-- src/phpseclib/Crypt/Blowfish.php | 588 +++ src/phpseclib/Crypt/DES.php | 383 +- src/phpseclib/Crypt/Hash.php | 687 +-- src/phpseclib/Crypt/RC2.php | 704 +++ src/phpseclib/Crypt/RC4.php | 253 +- src/phpseclib/Crypt/RSA.php | 2177 ++++----- src/phpseclib/Crypt/RSA/MSBLOB.php | 224 + src/phpseclib/Crypt/RSA/OpenSSH.php | 141 + src/phpseclib/Crypt/RSA/PKCS.php | 487 ++ src/phpseclib/Crypt/RSA/PKCS1.php | 174 + src/phpseclib/Crypt/RSA/PKCS8.php | 209 + src/phpseclib/Crypt/RSA/PuTTY.php | 313 ++ src/phpseclib/Crypt/RSA/Raw.php | 103 + src/phpseclib/Crypt/RSA/XML.php | 147 + src/phpseclib/Crypt/Random.php | 366 +- src/phpseclib/Crypt/Rijndael.php | 1182 ++--- src/phpseclib/Crypt/TripleDES.php | 314 +- src/phpseclib/Crypt/Twofish.php | 846 ++++ .../Exception/BadConfigurationException.php | 26 + .../Exception/FileNotFoundException.php | 26 + .../NoSupportedAlgorithmsException.php | 26 + .../UnsupportedAlgorithmException.php | 26 + src/phpseclib/File/ANSI.php | 574 +++ src/phpseclib/File/ASN1.php | 985 ++-- src/phpseclib/File/ASN1/Element.php | 47 + src/phpseclib/File/X509.php | 1840 ++++--- src/phpseclib/LICENSE | 21 - src/phpseclib/Math/BigInteger.php | 2111 ++++---- src/phpseclib/Net/SCP.php | 338 ++ src/phpseclib/Net/SFTP.php | 2947 ++++++++++++ src/phpseclib/Net/SFTP/Stream.php | 795 ++++ src/phpseclib/Net/SSH1.php | 1607 +++++++ src/phpseclib/Net/SSH2.php | 4224 +++++++++++++++++ src/phpseclib/System/SSH/Agent.php | 313 ++ src/phpseclib/System/SSH/Agent/Identity.php | 170 + src/phpseclib/bootstrap.php | 20 + src/phpseclib/openssl.cnf | 6 + src/shoghicp/BigBrother/BigBrother.php | 10 +- src/shoghicp/BigBrother/DesktopPlayer.php | 4 +- src/shoghicp/BigBrother/network/Info.php | 0 src/shoghicp/BigBrother/network/Packet.php | 0 .../BigBrother/network/ProtocolInterface.php | 0 .../BigBrother/network/ServerManager.php | 0 .../BigBrother/network/ServerThread.php | 4 +- src/shoghicp/BigBrother/network/Session.php | 0 .../network/protocol/BlockChangePacket.php | 0 .../network/protocol/CTSChatPacket.php | 0 .../network/protocol/CTSCloseWindowPacket.php | 0 .../protocol/ChangeGameStatePacket.php | 0 .../network/protocol/ChunkDataPacket.php | 0 .../network/protocol/ClientStatusPacket.php | 0 .../protocol/DestroyEntitiesPacket.php | 0 .../protocol/EncryptionRequestPacket.php | 0 .../protocol/EncryptionResponsePacket.php | 0 .../network/protocol/EntityHeadLookPacket.php | 0 .../network/protocol/EntityMetadataPacket.php | 0 .../network/protocol/EntityTeleportPacket.php | 0 .../network/protocol/EntityVelocityPacket.php | 0 .../network/protocol/JoinGamePacket.php | 0 .../network/protocol/KeepAlivePacket.php | 0 .../protocol/LoginDisconnectPacket.php | 0 .../network/protocol/LoginStartPacket.php | 0 .../network/protocol/LoginSuccessPacket.php | 0 .../network/protocol/OpenWindowPacket.php | 0 .../network/protocol/PingPacket.php | 0 .../network/protocol/PlayDisconnectPacket.php | 0 .../protocol/PlayerAbilitiesPacket.php | 0 .../protocol/PlayerBlockPlacementPacket.php | 0 .../network/protocol/PlayerDiggingPacket.php | 0 .../network/protocol/PlayerLookPacket.php | 0 .../protocol/PlayerPositionAndLookPacket.php | 0 .../network/protocol/PlayerPositionPacket.php | 0 .../protocol/PositionAndLookPacket.php | 0 .../network/protocol/RespawnPacket.php | 0 .../network/protocol/STCChatPacket.php | 0 .../network/protocol/STCCloseWindowPacket.php | 0 .../network/protocol/SetSlotPacket.php | 0 .../network/protocol/SpawnMobPacket.php | 0 .../network/protocol/SpawnObjectPacket.php | 0 .../network/protocol/SpawnPlayerPacket.php | 0 .../network/protocol/SpawnPositionPacket.php | 0 .../network/protocol/TimeUpdatePacket.php | 0 .../network/protocol/UpdateHealthPacket.php | 0 .../network/protocol/UseEntityPacket.php | 0 .../network/protocol/WindowItemsPacket.php | 0 .../network/translation/Translator.php | 0 .../{Translator_20.php => Translator_81.php} | 8 +- .../BigBrother/tasks/AuthenticateOnline.php | 0 .../BigBrother/tasks/GeneratePrivateKey.php | 0 .../BigBrother/tasks/LevelDBToAnvil.php | 0 .../BigBrother/tasks/McRegionToAnvil.php | 0 .../BigBrother/tasks/OnlineProfile.php | 0 src/shoghicp/BigBrother/utils/AES.php | 0 src/shoghicp/BigBrother/utils/Binary.php | 0 109 files changed, 22623 insertions(+), 6291 deletions(-) create mode 100755 src/ParagonIE/.gitignore create mode 100755 src/ParagonIE/.travis.yml create mode 100755 src/ParagonIE/ConstantTime/Base32.php create mode 100755 src/ParagonIE/ConstantTime/Base32Hex.php create mode 100755 src/ParagonIE/ConstantTime/Base64.php create mode 100755 src/ParagonIE/ConstantTime/Base64DotSlash.php create mode 100755 src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php create mode 100755 src/ParagonIE/ConstantTime/Base64UrlSafe.php create mode 100755 src/ParagonIE/ConstantTime/Binary.php create mode 100755 src/ParagonIE/ConstantTime/EncoderInterface.php create mode 100755 src/ParagonIE/ConstantTime/Encoding.php create mode 100755 src/ParagonIE/ConstantTime/Hex.php create mode 100755 src/ParagonIE/ConstantTime/RFC4648.php mode change 100644 => 100755 src/phpseclib/Crypt/AES.php mode change 100644 => 100755 src/phpseclib/Crypt/Base.php create mode 100755 src/phpseclib/Crypt/Blowfish.php mode change 100644 => 100755 src/phpseclib/Crypt/DES.php mode change 100644 => 100755 src/phpseclib/Crypt/Hash.php create mode 100755 src/phpseclib/Crypt/RC2.php mode change 100644 => 100755 src/phpseclib/Crypt/RC4.php mode change 100644 => 100755 src/phpseclib/Crypt/RSA.php create mode 100755 src/phpseclib/Crypt/RSA/MSBLOB.php create mode 100755 src/phpseclib/Crypt/RSA/OpenSSH.php create mode 100755 src/phpseclib/Crypt/RSA/PKCS.php create mode 100755 src/phpseclib/Crypt/RSA/PKCS1.php create mode 100755 src/phpseclib/Crypt/RSA/PKCS8.php create mode 100755 src/phpseclib/Crypt/RSA/PuTTY.php create mode 100755 src/phpseclib/Crypt/RSA/Raw.php create mode 100755 src/phpseclib/Crypt/RSA/XML.php mode change 100644 => 100755 src/phpseclib/Crypt/Random.php mode change 100644 => 100755 src/phpseclib/Crypt/Rijndael.php mode change 100644 => 100755 src/phpseclib/Crypt/TripleDES.php create mode 100755 src/phpseclib/Crypt/Twofish.php create mode 100755 src/phpseclib/Exception/BadConfigurationException.php create mode 100755 src/phpseclib/Exception/FileNotFoundException.php create mode 100755 src/phpseclib/Exception/NoSupportedAlgorithmsException.php create mode 100755 src/phpseclib/Exception/UnsupportedAlgorithmException.php create mode 100755 src/phpseclib/File/ANSI.php mode change 100644 => 100755 src/phpseclib/File/ASN1.php create mode 100755 src/phpseclib/File/ASN1/Element.php mode change 100644 => 100755 src/phpseclib/File/X509.php delete mode 100644 src/phpseclib/LICENSE mode change 100644 => 100755 src/phpseclib/Math/BigInteger.php create mode 100755 src/phpseclib/Net/SCP.php create mode 100755 src/phpseclib/Net/SFTP.php create mode 100755 src/phpseclib/Net/SFTP/Stream.php create mode 100755 src/phpseclib/Net/SSH1.php create mode 100755 src/phpseclib/Net/SSH2.php create mode 100755 src/phpseclib/System/SSH/Agent.php create mode 100755 src/phpseclib/System/SSH/Agent/Identity.php create mode 100755 src/phpseclib/bootstrap.php create mode 100755 src/phpseclib/openssl.cnf mode change 100644 => 100755 src/shoghicp/BigBrother/BigBrother.php mode change 100644 => 100755 src/shoghicp/BigBrother/DesktopPlayer.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/Info.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/Packet.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/ProtocolInterface.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/ServerManager.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/ServerThread.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/Session.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PingPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/RespawnPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/STCChatPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php mode change 100644 => 100755 src/shoghicp/BigBrother/network/translation/Translator.php rename src/shoghicp/BigBrother/network/translation/{Translator_20.php => Translator_81.php} (98%) mode change 100644 => 100755 mode change 100644 => 100755 src/shoghicp/BigBrother/tasks/AuthenticateOnline.php mode change 100644 => 100755 src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php mode change 100644 => 100755 src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php mode change 100644 => 100755 src/shoghicp/BigBrother/tasks/McRegionToAnvil.php mode change 100644 => 100755 src/shoghicp/BigBrother/tasks/OnlineProfile.php mode change 100644 => 100755 src/shoghicp/BigBrother/utils/AES.php mode change 100644 => 100755 src/shoghicp/BigBrother/utils/Binary.php diff --git a/src/ParagonIE/.gitignore b/src/ParagonIE/.gitignore new file mode 100755 index 00000000..e0caea8f --- /dev/null +++ b/src/ParagonIE/.gitignore @@ -0,0 +1,2 @@ +.idea/ +vendor/ \ No newline at end of file diff --git a/src/ParagonIE/.travis.yml b/src/ParagonIE/.travis.yml new file mode 100755 index 00000000..a78dfe92 --- /dev/null +++ b/src/ParagonIE/.travis.yml @@ -0,0 +1,15 @@ +language: php +sudo: false + +php: + - "7.0" + +matrix: + fast_finish: true + +install: + - composer self-update + - composer update + +script: + - vendor/bin/phpunit diff --git a/src/ParagonIE/ConstantTime/Base32.php b/src/ParagonIE/ConstantTime/Base32.php new file mode 100755 index 00000000..a65c1c73 --- /dev/null +++ b/src/ParagonIE/ConstantTime/Base32.php @@ -0,0 +1,396 @@ + 96 && $src < 123) $ret += $src - 97 + 1; // -64 + $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96); + + // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 + $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 5-bit integers + * into 8-bit integers. + * + * Uppercase variant. + * + * @param int $src + * @return int + */ + protected static function decode5BitsUpper(int $src): int + { + $ret = -1; + + // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64 + $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); + + // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 + $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 5-bit integers. + * + * @param $src + * @return string + */ + protected static function encode5Bits(int $src): string + { + $diff = 0x61; + + // if ($src > 25) $ret -= 72; + $diff -= ((25 - $src) >> 8) & 73; + + return \pack('C', $src + $diff); + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 5-bit integers. + * + * Uppercase variant. + * + * @param $src + * @return string + */ + protected static function encode5BitsUpper(int $src): string + { + $diff = 0x41; + + // if ($src > 25) $ret -= 40; + $diff -= ((25 - $src) >> 8) & 41; + + return \pack('C', $src + $diff); + } + + + /** + * Base32 decoding + * + * @param string $src + * @param bool $upper + * @param bool $strictPadding + * @return string + */ + protected static function doDecode(string $src, bool $upper = false, bool $strictPadding = false): string + { + // We do this to reduce code duplication: + $method = $upper + ? 'decode5BitsUpper' + : 'decode5Bits'; + + // Remove padding + $srcLen = Binary::safeStrlen($src); + if ($srcLen === 0) { + return ''; + } + if ($strictPadding) { + if (($srcLen & 7) === 0) { + for ($j = 0; $j < 7; ++$j) { + if ($src[$srcLen - 1] === '=') { + $srcLen--; + } else { + break; + } + } + } + if (($srcLen & 7) === 1) { + throw new \RangeException( + 'Incorrect padding' + ); + } + } else { + $src = \rtrim($src, '='); + $srcLen = Binary::safeStrlen($src); + } + + $err = 0; + $dest = ''; + // Main loop (no padding): + for ($i = 0; $i + 8 <= $srcLen; $i += 8) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8)); + $c0 = static::$method($chunk[1]); + $c1 = static::$method($chunk[2]); + $c2 = static::$method($chunk[3]); + $c3 = static::$method($chunk[4]); + $c4 = static::$method($chunk[5]); + $c5 = static::$method($chunk[6]); + $c6 = static::$method($chunk[7]); + $c7 = static::$method($chunk[8]); + + $dest .= \pack( + 'CCCCC', + (($c0 << 3) | ($c1 >> 2) ) & 0xff, + (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, + (($c3 << 4) | ($c4 >> 1) ) & 0xff, + (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff, + (($c6 << 5) | ($c7 ) ) & 0xff + ); + $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8; + } + // The last chunk, which may have padding: + if ($i < $srcLen) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); + $c0 = static::$method($chunk[1]); + + if ($i + 6 < $srcLen) { + $c1 = static::$method($chunk[2]); + $c2 = static::$method($chunk[3]); + $c3 = static::$method($chunk[4]); + $c4 = static::$method($chunk[5]); + $c5 = static::$method($chunk[6]); + $c6 = static::$method($chunk[7]); + + $dest .= \pack( + 'CCCC', + (($c0 << 3) | ($c1 >> 2) ) & 0xff, + (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, + (($c3 << 4) | ($c4 >> 1) ) & 0xff, + (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff + ); + $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; + } elseif ($i + 5 < $srcLen) { + $c1 = static::$method($chunk[2]); + $c2 = static::$method($chunk[3]); + $c3 = static::$method($chunk[4]); + $c4 = static::$method($chunk[5]); + $c5 = static::$method($chunk[6]); + + $dest .= \pack( + 'CCCC', + (($c0 << 3) | ($c1 >> 2) ) & 0xff, + (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, + (($c3 << 4) | ($c4 >> 1) ) & 0xff, + (($c4 << 7) | ($c5 << 2) ) & 0xff + ); + $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8; + } elseif ($i + 4 < $srcLen) { + $c1 = static::$method($chunk[2]); + $c2 = static::$method($chunk[3]); + $c3 = static::$method($chunk[4]); + $c4 = static::$method($chunk[5]); + + $dest .= \pack( + 'CCC', + (($c0 << 3) | ($c1 >> 2) ) & 0xff, + (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, + (($c3 << 4) | ($c4 >> 1) ) & 0xff + ); + $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; + } elseif ($i + 3 < $srcLen) { + $c1 = static::$method($chunk[2]); + $c2 = static::$method($chunk[3]); + $c3 = static::$method($chunk[4]); + + $dest .= \pack( + 'CC', + (($c0 << 3) | ($c1 >> 2) ) & 0xff, + (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff + ); + $err |= ($c0 | $c1 | $c2 | $c3) >> 8; + } elseif ($i + 2 < $srcLen) { + $c1 = static::$method($chunk[2]); + $c2 = static::$method($chunk[3]); + + $dest .= \pack( + 'CC', + (($c0 << 3) | ($c1 >> 2) ) & 0xff, + (($c1 << 6) | ($c2 << 1) ) & 0xff + ); + $err |= ($c0 | $c1 | $c2) >> 8; + } elseif ($i + 1 < $srcLen) { + $c1 = static::$method($chunk[2]); + + $dest .= \pack( + 'C', + (($c0 << 3) | ($c1 >> 2) ) & 0xff + ); + $err |= ($c0 | $c1) >> 8; + } else { + $dest .= \pack( + 'C', + (($c0 << 3) ) & 0xff + ); + $err |= ($c0) >> 8; + } + } + if ($err !== 0) { + throw new \RangeException( + 'Base32::doDecode() only expects characters in the correct base32 alphabet' + ); + } + return $dest; + } + + /** + * Base32 Decoding + * + * @param string $src + * @param bool $upper + * @return string + */ + protected static function doEncode(string $src, bool $upper = false): string + { + // We do this to reduce code duplication: + $method = $upper + ? 'encode5BitsUpper' + : 'encode5Bits'; + + $dest = ''; + $srcLen = Binary::safeStrlen($src); + + // Main loop (no padding): + for ($i = 0; $i + 5 <= $srcLen; $i += 5) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5)); + $b0 = $chunk[1]; + $b1 = $chunk[2]; + $b2 = $chunk[3]; + $b3 = $chunk[4]; + $b4 = $chunk[5]; + $dest .= + static::$method( ($b0 >> 3) & 31) . + static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . + static::$method((($b1 >> 1) ) & 31) . + static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . + static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . + static::$method((($b3 >> 2) ) & 31) . + static::$method((($b3 << 3) | ($b4 >> 5)) & 31) . + static::$method( $b4 & 31); + } + // The last chunk, which may have padding: + if ($i < $srcLen) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); + $b0 = $chunk[1]; + if ($i + 3 < $srcLen) { + $b1 = $chunk[2]; + $b2 = $chunk[3]; + $b3 = $chunk[4]; + $dest .= + static::$method( ($b0 >> 3) & 31) . + static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . + static::$method((($b1 >> 1) ) & 31) . + static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . + static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . + static::$method((($b3 >> 2) ) & 31) . + static::$method((($b3 << 3) ) & 31) . + '='; + } elseif ($i + 2 < $srcLen) { + $b1 = $chunk[2]; + $b2 = $chunk[3]; + $dest .= + static::$method( ($b0 >> 3) & 31) . + static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . + static::$method((($b1 >> 1) ) & 31) . + static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . + static::$method((($b2 << 1) ) & 31) . + '==='; + } elseif ($i + 1 < $srcLen) { + $b1 = $chunk[2]; + $dest .= + static::$method( ($b0 >> 3) & 31) . + static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . + static::$method((($b1 >> 1) ) & 31) . + static::$method((($b1 << 4) ) & 31) . + '===='; + } else { + $dest .= + static::$method( ($b0 >> 3) & 31) . + static::$method( ($b0 << 2) & 31) . + '======'; + } + } + return $dest; + } +} diff --git a/src/ParagonIE/ConstantTime/Base32Hex.php b/src/ParagonIE/ConstantTime/Base32Hex.php new file mode 100755 index 00000000..5aff0a6b --- /dev/null +++ b/src/ParagonIE/ConstantTime/Base32Hex.php @@ -0,0 +1,111 @@ + 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47 + $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47); + + // if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86 + $ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86); + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 5-bit integers + * into 8-bit integers. + * + * @param int $src + * @return int + */ + protected static function decode5BitsUpper(int $src): int + { + $ret = -1; + + // if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47 + $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47); + + // if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54 + $ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54); + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 5-bit integers. + * + * @param int $src + * @return string + */ + protected static function encode5Bits(int $src): string + { + $src += 0x30; + + // if ($src > 0x39) $src += 0x61 - 0x3a; // 39 + $src += ((0x39 - $src) >> 8) & 39; + + return \pack('C', $src); + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 5-bit integers. + * + * Uppercase variant. + * + * @param int $src + * @return string + */ + protected static function encode5BitsUpper(int $src): string + { + $src += 0x30; + + // if ($src > 0x39) $src += 0x41 - 0x3a; // 7 + $src += ((0x39 - $src) >> 8) & 7; + + return \pack('C', $src); + } +} \ No newline at end of file diff --git a/src/ParagonIE/ConstantTime/Base64.php b/src/ParagonIE/ConstantTime/Base64.php new file mode 100755 index 00000000..df801cc8 --- /dev/null +++ b/src/ParagonIE/ConstantTime/Base64.php @@ -0,0 +1,229 @@ +> 2 ) . + static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . + static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) . + static::encode6Bits( $b2 & 63); + } + // The last chunk, which may have padding: + if ($i < $srcLen) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); + $b0 = $chunk[1]; + if ($i + 1 < $srcLen) { + $b1 = $chunk[2]; + $dest .= + static::encode6Bits( $b0 >> 2 ) . + static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . + static::encode6Bits( ($b1 << 2) & 63) . '='; + } else { + $dest .= + static::encode6Bits( $b0 >> 2) . + static::encode6Bits(($b0 << 4) & 63) . '=='; + } + } + return $dest; + } + + /** + * decode from base64 into binary + * + * Base64 character set "./[A-Z][a-z][0-9]" + * + * @param string $src + * @param bool $strictPadding + * @return string|bool + * @throws \RangeException + */ + public static function decode(string $src, bool $strictPadding = false): string + { + // Remove padding + $srcLen = Binary::safeStrlen($src); + if ($srcLen === 0) { + return ''; + } + + if ($strictPadding) { + if (($srcLen & 3) === 0) { + if ($src[$srcLen - 1] === '=') { + $srcLen--; + if ($src[$srcLen - 1] === '=') { + $srcLen--; + } + } + } + if (($srcLen & 3) === 1) { + throw new \RangeException( + 'Incorrect padding' + ); + } + if ($src[$srcLen - 1] === '=') { + throw new \RangeException( + 'Incorrect padding' + ); + } + } else { + $src = \rtrim($src, '='); + $srcLen = Binary::safeStrlen($src); + } + + $err = 0; + $dest = ''; + // Main loop (no padding): + for ($i = 0; $i + 4 <= $srcLen; $i += 4) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 4)); + $c0 = static::decode6Bits($chunk[1]); + $c1 = static::decode6Bits($chunk[2]); + $c2 = static::decode6Bits($chunk[3]); + $c3 = static::decode6Bits($chunk[4]); + + $dest .= \pack( + 'CCC', + ((($c0 << 2) | ($c1 >> 4)) & 0xff), + ((($c1 << 4) | ($c2 >> 2)) & 0xff), + ((($c2 << 6) | $c3 ) & 0xff) + ); + $err |= ($c0 | $c1 | $c2 | $c3) >> 8; + } + // The last chunk, which may have padding: + if ($i < $srcLen) { + $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); + $c0 = static::decode6Bits($chunk[1]); + + if ($i + 2 < $srcLen) { + $c1 = static::decode6Bits($chunk[2]); + $c2 = static::decode6Bits($chunk[3]); + $dest .= \pack( + 'CC', + ((($c0 << 2) | ($c1 >> 4)) & 0xff), + ((($c1 << 4) | ($c2 >> 2)) & 0xff) + ); + $err |= ($c0 | $c1 | $c2) >> 8; + } elseif ($i + 1 < $srcLen) { + $c1 = static::decode6Bits($chunk[2]); + $dest .= \pack( + 'C', + ((($c0 << 2) | ($c1 >> 4)) & 0xff) + ); + $err |= ($c0 | $c1) >> 8; + } elseif ($i < $srcLen && $strictPadding) { + $err |= 1; + } + } + if ($err !== 0) { + return false; + } + return $dest; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 6-bit integers + * into 8-bit integers. + * + * Base64 character set: + * [A-Z] [a-z] [0-9] + / + * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f + * + * @param int $src + * @return int + */ + protected static function decode6Bits(int $src): int + { + $ret = -1; + + // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 + $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); + + // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 + $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); + + // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 + $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); + + // if ($src == 0x2b) $ret += 62 + 1; + $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63; + + // if ($src == 0x2f) ret += 63 + 1; + $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64; + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 6-bit integers. + * + * @param int $src + * @return string + */ + protected static function encode6Bits(int $src): string + { + $diff = 0x41; + + // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 + $diff += ((25 - $src) >> 8) & 6; + + // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 + $diff -= ((51 - $src) >> 8) & 75; + + // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15 + $diff -= ((61 - $src) >> 8) & 15; + + // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3 + $diff += ((62 - $src) >> 8) & 3; + + return \pack('C', $src + $diff); + } +} diff --git a/src/ParagonIE/ConstantTime/Base64DotSlash.php b/src/ParagonIE/ConstantTime/Base64DotSlash.php new file mode 100755 index 00000000..7f975139 --- /dev/null +++ b/src/ParagonIE/ConstantTime/Base64DotSlash.php @@ -0,0 +1,88 @@ + 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45 + $ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45); + + // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62 + $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62); + + // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68 + $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68); + + // if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7 + $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7); + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 6-bit integers. + * + * @param int $src + * @return string + */ + protected static function encode6Bits(int $src): string + { + $src += 0x2e; + + // if ($src > 0x2f) $src += 0x41 - 0x30; // 17 + $src += ((0x2f - $src) >> 8) & 17; + + // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6 + $src += ((0x5a - $src) >> 8) & 6; + + // if ($src > 0x7a) $src += 0x30 - 0x7b; // -75 + $src -= ((0x7a - $src) >> 8) & 75; + + return \pack('C', $src); + } +} diff --git a/src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php b/src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php new file mode 100755 index 00000000..3fb4209e --- /dev/null +++ b/src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php @@ -0,0 +1,82 @@ + 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45 + $ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45); + + // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52 + $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52); + + // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58 + $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58); + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 6-bit integers. + * + * @param int $src + * @return string + */ + protected static function encode6Bits(int $src): string + { + $src += 0x2e; + + // if ($src > 0x39) $src += 0x41 - 0x3a; // 7 + $src += ((0x39 - $src) >> 8) & 7; + + // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6 + $src += ((0x5a - $src) >> 8) & 6; + + return \pack('C', $src); + } +} diff --git a/src/ParagonIE/ConstantTime/Base64UrlSafe.php b/src/ParagonIE/ConstantTime/Base64UrlSafe.php new file mode 100755 index 00000000..f250095d --- /dev/null +++ b/src/ParagonIE/ConstantTime/Base64UrlSafe.php @@ -0,0 +1,95 @@ + 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 + $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); + + // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 + $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); + + // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 + $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); + + // if ($src == 0x2c) $ret += 62 + 1; + $ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63; + + // if ($src == 0x5f) ret += 63 + 1; + $ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64; + + return $ret; + } + + /** + * Uses bitwise operators instead of table-lookups to turn 8-bit integers + * into 6-bit integers. + * + * @param int $src + * @return string + */ + protected static function encode6Bits(int $src): string + { + $diff = 0x41; + + // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 + $diff += ((25 - $src) >> 8) & 6; + + // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 + $diff -= ((51 - $src) >> 8) & 75; + + // if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13 + $diff -= ((61 - $src) >> 8) & 13; + + // if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3 + $diff += ((62 - $src) >> 8) & 49; + + return \pack('C', $src + $diff); + } +} diff --git a/src/ParagonIE/ConstantTime/Binary.php b/src/ParagonIE/ConstantTime/Binary.php new file mode 100755 index 00000000..1028e66e --- /dev/null +++ b/src/ParagonIE/ConstantTime/Binary.php @@ -0,0 +1,98 @@ += 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 ($length !== null) { + return \substr($str, $start, $length); + } else { + return \substr($str, $start); + } + } +} \ No newline at end of file diff --git a/src/ParagonIE/ConstantTime/EncoderInterface.php b/src/ParagonIE/ConstantTime/EncoderInterface.php new file mode 100755 index 00000000..ca699168 --- /dev/null +++ b/src/ParagonIE/ConstantTime/EncoderInterface.php @@ -0,0 +1,52 @@ +> 4; + $hex .= pack( + 'CC', + (87 + $b + ((($b - 10) >> 8) & ~38)), + (87 + $c + ((($c - 10) >> 8) & ~38)) + ); + } + return $hex; + } + + /** + * Convert a binary string into a hexadecimal string without cache-timing + * leaks, returning uppercase letters (as per RFC 4648) + * + * @param string $bin_string (raw binary) + * @return string + */ + public static function encodeUpper(string $bin_string): string + { + $hex = ''; + $len = Binary::safeStrlen($bin_string); + for ($i = 0; $i < $len; ++$i) { + $chunk = \unpack('C', Binary::safeSubstr($bin_string, $i, 2)); + $c = $chunk[1] & 0xf; + $b = $chunk[1] >> 4; + $hex .= pack( + 'CC', + (55 + $b + ((($b - 10) >> 8) & ~6)), + (55 + $c + ((($c - 10) >> 8) & ~6)) + ); + } + return $hex; + } + + /** + * Convert a hexadecimal string into a binary string without cache-timing + * leaks + * + * @param string $hex_string + * @param bool $strictPadding + * @return string (raw binary) + * @throws \RangeException + */ + public static function decode(string $hexString, bool $strictPadding = false): string + { + $hex_pos = 0; + $bin = ''; + $c_acc = 0; + $hex_len = Binary::safeStrlen($hexString); + $state = 0; + if (($hex_len & 1) !== 0) { + if ($strictPadding) { + throw new \RangeException( + 'Expected an even number of hexadecimal characters' + ); + } else { + $hexString = '0' . $hexString; + ++$hex_len; + } + } + + $chunk = \unpack('C*', $hexString); + while ($hex_pos < $hex_len) { + ++$hex_pos; + $c = $chunk[$hex_pos]; + $c_num = $c ^ 48; + $c_num0 = ($c_num - 10) >> 8; + $c_alpha = ($c & ~32) - 55; + $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; + if (($c_num0 | $c_alpha0) === 0) { + throw new \RangeException( + 'hexEncode() only expects hexadecimal characters' + ); + } + $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); + if ($state === 0) { + $c_acc = $c_val * 16; + } else { + $bin .= \pack('C', $c_acc | $c_val); + } + $state ^= 1; + } + return $bin; + } +} diff --git a/src/ParagonIE/ConstantTime/RFC4648.php b/src/ParagonIE/ConstantTime/RFC4648.php new file mode 100755 index 00000000..63b2ee69 --- /dev/null +++ b/src/ParagonIE/ConstantTime/RFC4648.php @@ -0,0 +1,166 @@ + "Zm9v" + * + * @param string $str + * @return string + */ + public function base64Encode(string $str): string + { + return Base64::encode($str); + } + + /** + * RFC 4648 Base64 decoding + * + * "Zm9v" -> "foo" + * + * @param string $str + * @return string + */ + public function base64Decode(string $str): string + { + return Base64::decode($str, true); + } + + /** + * RFC 4648 Base64 (URL Safe) encoding + * + * "foo" -> "Zm9v" + * + * @param string $str + * @return string + */ + public function base64UrlSafeEncode(string $str): string + { + return Base64UrlSafe::encode($str); + } + + /** + * RFC 4648 Base64 (URL Safe) decoding + * + * "Zm9v" -> "foo" + * + * @param string $str + * @return string + */ + public function base64UrlSafeDecode(string $str): string + { + return Base64UrlSafe::decode($str, true); + } + + /** + * RFC 4648 Base32 encoding + * + * "foo" -> "MZXW6===" + * + * @param string $str + * @return string + */ + public function base32Encode(string $str): string + { + return Base32::encodeUpper($str); + } + + /** + * RFC 4648 Base32 encoding + * + * "MZXW6===" -> "foo" + * + * @param string $str + * @return string + */ + public function base32Decode(string $str): string + { + return Base32::decodeUpper($str, true); + } + + /** + * RFC 4648 Base32-Hex encoding + * + * "foo" -> "CPNMU===" + * + * @param string $str + * @return string + */ + public function base32HexEncode(string $str): string + { + return Base32::encodeUpper($str); + } + + /** + * RFC 4648 Base32-Hex decoding + * + * "CPNMU===" -> "foo" + * + * @param string $str + * @return string + */ + public function base32HexDecode(string $str): string + { + return Base32::decodeUpper($str, true); + } + + /** + * RFC 4648 Base16 decoding + * + * "foo" -> "666F6F" + * + * @param string $str + * @return string + */ + public function base16Encode(string $str): string + { + return Hex::encodeUpper($str); + } + + /** + * RFC 4648 Base16 decoding + * + * "666F6F" -> "foo" + * + * @param string $str + * @return string + */ + public function base16Decode(string $str): string + { + return Hex::decode($str, true); + } +} \ No newline at end of file diff --git a/src/phpseclib/Crypt/AES.php b/src/phpseclib/Crypt/AES.php old mode 100644 new mode 100755 index aa650b8e..8521eb5e --- a/src/phpseclib/Crypt/AES.php +++ b/src/phpseclib/Crypt/AES.php @@ -5,23 +5,27 @@ * * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * - * PHP versions 4 and 5 + * PHP version 5 * - * If {@link AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from - * {@link AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits - * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link AES::setKey() setKey()} + * NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually + * just a wrapper to Rijndael.php you may consider using Rijndael.php instead of + * to save one include_once(). + * + * If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from + * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits + * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()} * is called, again, at which point, it'll be recalculated. * - * Since AES extends Rijndael, some functions are available to be called that, in the context of AES, don't - * make a whole lot of sense. {@link AES::setBlockLength() setBlockLength()}, for instance. Calling that function, + * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't + * make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function, * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one). * * Here's a short example of how to use this library: * * setKey('abcdefghijklmnop'); * @@ -35,146 +39,85 @@ * ?> * * - * LICENSE: 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. - * * @category Crypt * @package AES * @author Jim Wigginton - * @copyright MMVIII Jim Wigginton + * @copyright 2008 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access public - * @see AES::encrypt() - * @see AES::decrypt() - */ -/** - * Encrypt / decrypt using the Counter mode. - * - * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 - */ -@define('CRYPT_AES_MODE_CTR', CRYPT_MODE_CTR); -/** - * Encrypt / decrypt using the Electronic Code Book mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 - */ -@define('CRYPT_AES_MODE_ECB', CRYPT_MODE_ECB); -/** - * Encrypt / decrypt using the Code Book Chaining mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 - */ -@define('CRYPT_AES_MODE_CBC', CRYPT_MODE_CBC); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 - */ -@define('CRYPT_AES_MODE_CFB', CRYPT_MODE_CFB); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 - */ -@define('CRYPT_AES_MODE_OFB', CRYPT_MODE_OFB); -/**#@-*/ - -/**#@+ - * @access private - * @see AES::AES() - */ -/** - * Toggles the internal implementation - */ -@define('CRYPT_AES_MODE_INTERNAL', CRYPT_MODE_INTERNAL); -/** - * Toggles the mcrypt implementation - */ -@define('CRYPT_AES_MODE_MCRYPT', CRYPT_MODE_MCRYPT); -/**#@-*/ - /** * Pure-PHP implementation of AES. * * @package AES * @author Jim Wigginton - * @version 0.1.0 * @access public */ class AES extends Rijndael { /** - * The namespace used by the cipher for its constants. + * Dummy function + * + * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything. * - * @see Base::const_namespace - * @var String - * @access private + * @see \phpseclib\Crypt\Rijndael::setBlockLength() + * @access public + * @param int $length + * @throws \BadMethodCallException anytime it's called */ - var $const_namespace = 'AES'; + function setBlockLength($length) + { + throw new \BadMethodCallException('The block length cannot be set for AES.'); + } /** - * Default Constructor. - * - * Determines whether or not the mcrypt extension should be used. - * - * $mode could be: - * - * - CRYPT_AES_MODE_ECB - * - * - CRYPT_AES_MODE_CBC - * - * - CRYPT_AES_MODE_CTR + * Sets the key length * - * - CRYPT_AES_MODE_CFB + * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length * - * - CRYPT_AES_MODE_OFB - * - * If not explictly set, CRYPT_AES_MODE_CBC will be used. - * - * @see Rijndael::Rijndael() - * @see Base::Base() - * @param optional Integer $mode + * @see \phpseclib\Crypt\Rijndael:setKeyLength() * @access public + * @param int $length + * @throws \LengthException if the key length isn't supported */ - function __construct($mode = CRYPT_AES_MODE_CBC) + function setKeyLength($length) { - parent::__construct($mode); + switch ($length) { + case 128: + case 192: + case 256: + break; + default: + throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported'); + } + parent::setKeyLength($length); } /** - * Dummy function + * Sets the key. * - * Since AES extends Rijndael, this function is, technically, available, but it doesn't do anything. + * Rijndael supports five different key lengths, AES only supports three. * - * @see Rijndael::setBlockLength() + * @see \phpseclib\Crypt\Rijndael:setKey() + * @see setKeyLength() * @access public - * @param Integer $length + * @param string $key + * @throws \LengthException if the key length isn't supported */ - function setBlockLength($length) + function setKey($key) { - return; + switch (strlen($key)) { + case 16: + case 24: + case 32: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + + parent::setKey($key); } } diff --git a/src/phpseclib/Crypt/Base.php b/src/phpseclib/Crypt/Base.php old mode 100644 new mode 100755 index 34cd67a7..e3cc7b87 --- a/src/phpseclib/Crypt/Base.php +++ b/src/phpseclib/Crypt/Base.php @@ -1,14 +1,14 @@ * @author Hans-Juergen Petrich - * @copyright MMVII Jim Wigginton + * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version 1.0.1 * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access public - * @see Base::encrypt() - * @see Base::decrypt() - */ -/** - * Encrypt / decrypt using the Counter mode. - * - * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 - */ -@define('CRYPT_MODE_CTR', -1); -/** - * Encrypt / decrypt using the Electronic Code Book mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 - */ -@define('CRYPT_MODE_ECB', 1); -/** - * Encrypt / decrypt using the Code Book Chaining mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 - */ -@define('CRYPT_MODE_CBC', 2); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 - */ -@define('CRYPT_MODE_CFB', 3); -/** - * Encrypt / decrypt using the Output Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 - */ -@define('CRYPT_MODE_OFB', 4); -/** - * Encrypt / decrypt using streaming mode. - * - */ -@define('CRYPT_MODE_STREAM', 5); -/**#@-*/ - -/**#@+ - * @access private - * @see Base::Base() - */ -/** - * Base value for the internal implementation $engine switch - */ -@define('CRYPT_MODE_INTERNAL', 1); -/** - * Base value for the mcrypt implementation $engine switch - */ -@define('CRYPT_MODE_MCRYPT', 2); -/**#@-*/ - /** - * Base Class for all * cipher classes + * Base Class for all \phpseclib\Crypt\* cipher classes * * @package Base * @author Jim Wigginton * @author Hans-Juergen Petrich - * @version 1.0.0 - * @access public */ -class Base +abstract class Base { + /**#@+ + * @access public + * @see \phpseclib\Crypt\Base::encrypt() + * @see \phpseclib\Crypt\Base::decrypt() + */ + /** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ + const MODE_CTR = -1; + /** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ + const MODE_ECB = 1; + /** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ + const MODE_CBC = 2; + /** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ + const MODE_CFB = 3; + /** + * Encrypt / decrypt using the Output Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ + const MODE_OFB = 4; + /** + * Encrypt / decrypt using streaming mode. + */ + const MODE_STREAM = 5; + /**#@-*/ + + /** + * Whirlpool available flag + * + * @see \phpseclib\Crypt\Base::_hashInlineCryptFunction() + * @var bool + * @access private + */ + static $WHIRLPOOL_AVAILABLE; + + /**#@+ + * @access private + * @see \phpseclib\Crypt\Base::__construct() + */ + /** + * Base value for the internal implementation $engine switch + */ + const ENGINE_INTERNAL = 1; + /** + * Base value for the mcrypt implementation $engine switch + */ + const ENGINE_MCRYPT = 2; + /** + * Base value for the mcrypt implementation $engine switch + */ + const ENGINE_OPENSSL = 3; + /**#@-*/ + /** * The Encryption Mode * - * @see Base::Base() - * @var Integer + * @see self::__construct() + * @var int * @access private */ var $mode; @@ -136,7 +127,7 @@ class Base /** * The Block Length of the block cipher * - * @var Integer + * @var int * @access private */ var $block_size = 16; @@ -144,27 +135,27 @@ class Base /** * The Key * - * @see Base::setKey() - * @var String + * @see self::setKey() + * @var string * @access private */ - var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + var $key = false; /** * The Initialization Vector * - * @see Base::setIV() - * @var String + * @see self::setIV() + * @var string * @access private */ - var $iv; + var $iv = false; /** * A "sliding" Initialization Vector * - * @see Base::enableContinuousBuffer() - * @see Base::_clearBuffers() - * @var String + * @see self::enableContinuousBuffer() + * @see self::_clearBuffers() + * @var string * @access private */ var $encryptIV; @@ -172,9 +163,9 @@ class Base /** * A "sliding" Initialization Vector * - * @see Base::enableContinuousBuffer() - * @see Base::_clearBuffers() - * @var String + * @see self::enableContinuousBuffer() + * @see self::_clearBuffers() + * @var string * @access private */ var $decryptIV; @@ -182,8 +173,8 @@ class Base /** * Continuous Buffer status * - * @see Base::enableContinuousBuffer() - * @var Boolean + * @see self::enableContinuousBuffer() + * @var bool * @access private */ var $continuousBuffer = false; @@ -191,9 +182,9 @@ class Base /** * Encryption buffer for CTR, OFB and CFB modes * - * @see Base::encrypt() - * @see Base::_clearBuffers() - * @var Array + * @see self::encrypt() + * @see self::_clearBuffers() + * @var array * @access private */ var $enbuffer; @@ -201,9 +192,9 @@ class Base /** * Decryption buffer for CTR, OFB and CFB modes * - * @see Base::decrypt() - * @see Base::_clearBuffers() - * @var Array + * @see self::decrypt() + * @see self::_clearBuffers() + * @var array * @access private */ var $debuffer; @@ -214,8 +205,8 @@ class Base * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. * - * @see Base::encrypt() - * @var Resource + * @see self::encrypt() + * @var resource * @access private */ var $enmcrypt; @@ -226,8 +217,8 @@ class Base * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. * - * @see Base::decrypt() - * @var Resource + * @see self::decrypt() + * @var resource * @access private */ var $demcrypt; @@ -235,9 +226,9 @@ class Base /** * Does the enmcrypt resource need to be (re)initialized? * - * @see Twofish::setKey() - * @see Twofish::setIV() - * @var Boolean + * @see \phpseclib\Crypt\Twofish::setKey() + * @see \phpseclib\Crypt\Twofish::setIV() + * @var bool * @access private */ var $enchanged = true; @@ -245,9 +236,9 @@ class Base /** * Does the demcrypt resource need to be (re)initialized? * - * @see Twofish::setKey() - * @see Twofish::setIV() - * @var Boolean + * @see \phpseclib\Crypt\Twofish::setKey() + * @see \phpseclib\Crypt\Twofish::setIV() + * @var bool * @access private */ var $dechanged = true; @@ -263,10 +254,10 @@ class Base * use a separate ECB-mode mcrypt resource. * * @link http://phpseclib.sourceforge.net/cfb-demo.phps - * @see Base::encrypt() - * @see Base::decrypt() - * @see Base::_setupMcrypt() - * @var Resource + * @see self::encrypt() + * @see self::decrypt() + * @see self::_setupMcrypt() + * @var resource * @access private */ var $ecb; @@ -275,20 +266,20 @@ class Base * Optimizing value while CFB-encrypting * * Only relevant if $continuousBuffer enabled - * and $engine == CRYPT_MODE_MCRYPT + * and $engine == self::ENGINE_MCRYPT * * It's faster to re-init $enmcrypt if * $buffer bytes > $cfb_init_len than * using the $ecb resource furthermore. * - * This value depends of the choosen cipher + * This value depends of the chosen cipher * and the time it would be needed for it's * initialization [by mcrypt_generic_init()] * which, typically, depends on the complexity * on its internaly Key-expanding algorithm. * - * @see Base::encrypt() - * @var Integer + * @see self::encrypt() + * @var int * @access private */ var $cfb_init_len = 600; @@ -296,10 +287,10 @@ class Base /** * Does internal cipher state need to be (re)initialized? * - * @see setKey() - * @see setIV() - * @see disableContinuousBuffer() - * @var Boolean + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() + * @var bool * @access private */ var $changed = true; @@ -307,8 +298,8 @@ class Base /** * Padding status * - * @see Base::enablePadding() - * @var Boolean + * @see self::enablePadding() + * @var bool * @access private */ var $padding = true; @@ -316,8 +307,8 @@ class Base /** * Is the mode one that is paddable? * - * @see Base::Base() - * @var Boolean + * @see self::__construct() + * @var bool * @access private */ var $paddable = false; @@ -327,86 +318,83 @@ class Base * which will be determined automatically on __construct() * * Currently available $engines are: - * - CRYPT_MODE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) - * - CRYPT_MODE_INTERNAL (slower, pure php-engine, no php-extension required) - * - * In the pipeline... maybe. But currently not available: - * - CRYPT_MODE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) - * - * If possible, CRYPT_MODE_MCRYPT will be used for each cipher. - * Otherwise CRYPT_MODE_INTERNAL - * - * @see Base::encrypt() - * @see Base::decrypt() - * @var Integer + * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) + * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) + * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required) + * + * @see self::_setEngine() + * @see self::encrypt() + * @see self::decrypt() + * @var int * @access private */ var $engine; + /** + * Holds the preferred crypt engine + * + * @see self::_setEngine() + * @see self::setPreferredEngine() + * @var int + * @access private + */ + var $preferredEngine; + /** * The mcrypt specific name of the cipher * - * Only used if $engine == CRYPT_MODE_MCRYPT + * Only used if $engine == self::ENGINE_MCRYPT * * @link http://www.php.net/mcrypt_module_open * @link http://www.php.net/mcrypt_list_algorithms - * @see Base::_setupMcrypt() - * @var String + * @see self::_setupMcrypt() + * @var string * @access private */ var $cipher_name_mcrypt; /** - * The default password key_size used by setPassword() + * The openssl specific name of the cipher + * + * Only used if $engine == self::ENGINE_OPENSSL * - * @see Base::setPassword() - * @var Integer + * @link http://www.php.net/openssl-get-cipher-methods + * @var string * @access private */ - var $password_key_size = 32; + var $cipher_name_openssl; /** - * The default salt used by setPassword() + * The openssl specific name of the cipher in ECB mode + * + * If OpenSSL does not support the mode we're trying to use (CTR) + * it can still be emulated with ECB mode. * - * @see Base::setPassword() - * @var String + * @link http://www.php.net/openssl-get-cipher-methods + * @var string * @access private */ - var $password_default_salt = 'phpseclib/salt'; + var $cipher_name_openssl_ecb; /** - * The namespace used by the cipher for its constants. - * - * ie: AES.php is using CRYPT_AES_MODE_* for its constants - * so $const_namespace is AES - * - * DES.php is using CRYPT_DES_MODE_* for its constants - * so $const_namespace is DES... and so on - * - * All CRYPT_<$const_namespace>_MODE_* are aliases of - * the generic CRYPT_MODE_* constants, so both could be used - * for each cipher. - * - * Example: - * $aes = new AES(CRYPT_AES_MODE_CFB); // $aes will operate in cfb mode - * $aes = new AES(CRYPT_MODE_CFB); // identical + * The default salt used by setPassword() * - * @see Base::Base() - * @var String + * @see self::setPassword() + * @var string * @access private */ - var $const_namespace; + var $password_default_salt = 'phpseclib/salt'; /** * The name of the performance-optimized callback function * * Used by encrypt() / decrypt() - * only if $engine == CRYPT_MODE_INTERNAL + * only if $engine == self::ENGINE_INTERNAL * - * @see Base::encrypt() - * @see Base::decrypt() - * @see Base::_setupInlineCrypt() - * @see Base::$use_inline_crypt + * @see self::encrypt() + * @see self::decrypt() + * @see self::_setupInlineCrypt() + * @see self::$use_inline_crypt * @var Callback * @access private */ @@ -415,90 +403,89 @@ class Base /** * Holds whether performance-optimized $inline_crypt() can/should be used. * - * @see Base::encrypt() - * @see Base::decrypt() - * @see Base::inline_crypt + * @see self::encrypt() + * @see self::decrypt() + * @see self::inline_crypt * @var mixed * @access private */ var $use_inline_crypt; /** - * Default Constructor. + * If OpenSSL can be used in ECB but not in CTR we can emulate CTR + * + * @see self::_openssl_ctr_process() + * @var bool + * @access private + */ + var $openssl_emulate_ctr = false; + + /** + * Determines what options are passed to openssl_encrypt/decrypt * - * Determines whether or not the mcrypt extension should be used. + * @see self::isValidEngine() + * @var mixed + * @access private + */ + var $openssl_options; + + /** + * Don't truncate / null pad key * - * $mode could be: + * @see self::_clearBuffers() + * @var bool + * @access private + */ + var $skip_key_adjustment = false; + + /** + * Has the key length explicitly been set or should it be derived from the key, itself? * - * - CRYPT_MODE_ECB + * @see self::setKeyLength() + * @var bool + * @access private + */ + var $explicit_key_length = false; + + /** + * Default Constructor. * - * - CRYPT_MODE_CBC + * $mode could be: * - * - CRYPT_MODE_CTR + * - self::MODE_ECB * - * - CRYPT_MODE_CFB + * - self::MODE_CBC * - * - CRYPT_MODE_OFB + * - self::MODE_CTR * - * (or the alias constants of the choosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...) + * - self::MODE_CFB * - * If not explictly set, CRYPT_MODE_CBC will be used. + * - self::MODE_OFB * - * @param optional Integer $mode + * @param int $mode * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ - - public function Base($mode = CRYPT_MODE_CBC){ - $this->__construct($mode); - } - - function __construct($mode = CRYPT_MODE_CBC) + function __construct($mode) { - $const_crypt_mode = 'CRYPT_' . $this->const_namespace . '_MODE'; - - // Determining the availibility of mcrypt support for the cipher - - if (!defined($const_crypt_mode)) { - switch (true) { - case extension_loaded('mcrypt') && in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()): - @define($const_crypt_mode, CRYPT_MODE_MCRYPT); - break; - default: - @define($const_crypt_mode, CRYPT_MODE_INTERNAL); - } - } - - // Determining which internal $engine should be used. - // The fastes possible first. - switch (true) { - case empty($this->cipher_name_mcrypt): // The cipher module has no mcrypt-engine support at all so we force CRYPT_MODE_INTERNAL - $this->engine = CRYPT_MODE_INTERNAL; - break; - case constant($const_crypt_mode) == CRYPT_MODE_MCRYPT: - $this->engine = CRYPT_MODE_MCRYPT; - break; - default: - $this->engine = CRYPT_MODE_INTERNAL; - } - // $mode dependent settings switch ($mode) { - case CRYPT_MODE_ECB: + case self::MODE_ECB: + case self::MODE_CBC: $this->paddable = true; - $this->mode = $mode; break; - case CRYPT_MODE_CTR: - case CRYPT_MODE_CFB: - case CRYPT_MODE_OFB: - case CRYPT_MODE_STREAM: - $this->mode = $mode; + case self::MODE_CTR: + case self::MODE_CFB: + case self::MODE_OFB: + case self::MODE_STREAM: + $this->paddable = false; break; - case CRYPT_MODE_CBC: default: - $this->paddable = true; - $this->mode = CRYPT_MODE_CBC; + throw new \InvalidArgumentException('No valid mode has been specified'); } + $this->mode = $mode; + // Determining whether inline crypting can be used by the cipher if ($this->use_inline_crypt !== false && function_exists('create_function')) { $this->use_inline_crypt = true; @@ -506,26 +493,85 @@ function __construct($mode = CRYPT_MODE_CBC) } /** - * Sets the initialization vector. (optional) - * - * SetIV is not required when CRYPT_MODE_ECB (or ie for AES: CRYPT_AES_MODE_ECB) is being used. If not explictly set, it'll be assumed - * to be all zero's. + * Sets the initialization vector. * - * Note: Could, but not must, extend by the child * class + * setIV() is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. * * @access public - * @param String $iv + * @param string $iv + * @throws \LengthException if the IV length isn't equal to the block size + * @throws \InvalidArgumentException if an IV is provided when one shouldn't be + * @internal Can be overwritten by a sub class, but does not have to be */ function setIV($iv) { - if ($this->mode == CRYPT_MODE_ECB) { - return; + if ($this->mode == self::MODE_ECB) { + throw new \InvalidArgumentException('This mode does not require an IV.'); + } + + if ($this->mode == self::MODE_STREAM && $this->usesIV()) { + throw new \InvalidArgumentException('This algorithm does not use an IV.'); + } + + if (strlen($iv) != $this->block_size) { + throw new \LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required'); } $this->iv = $iv; $this->changed = true; } + /** + * Returns whether or not the algorithm uses an IV + * + * @access public + * @return bool + */ + function usesIV() + { + return true; + } + + /** + * Returns the current key length in bits + * + * @access public + * @return int + */ + function getKeyLength() + { + return $this->key_length << 3; + } + + /** + * Returns the current block length in bits + * + * @access public + * @return int + */ + function getBlockLength() + { + return $this->block_size << 3; + } + + /** + * Sets the key length. + * + * Keys with explicitly set lengths need to be treated accordingly + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + $this->explicit_key_length = $length >> 3; + + if (is_string($this->key) && strlen($this->key) != $this->explicit_key_length) { + $this->key = false; + throw new \LengthException('Key has already been set and is not ' .$this->explicit_key_length . ' bytes long'); + } + } + /** * Sets the key. * @@ -536,39 +582,45 @@ function setIV($iv) * * If the key is not explicitly set, it'll be assumed to be all null bytes. * - * Note: Could, but not must, extend by the child * class - * * @access public - * @param String $key + * @param string $key + * @internal Could, but not must, extend by the child Crypt_* class */ function setKey($key) { + if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { + throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); + } + $this->key = $key; + $this->key_length = strlen($key); $this->changed = true; + $this->_setEngine(); } /** * Sets the password. * * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: - * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1: * $hash, $salt, $count, $dkLen * * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php * - * Note: Could, but not must, extend by the child * class - * * @see Crypt/Hash.php - * @param String $password - * @param optional String $method + * @param string $password + * @param string $method + * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length + * @return bool * @access public + * @internal Could, but not must, extend by the child Crypt_* class */ function setPassword($password, $method = 'pbkdf2') { $key = ''; switch ($method) { - default: // 'pbkdf2' + default: // 'pbkdf2' or 'pbkdf1' $func_args = func_get_args(); // Hash function @@ -582,10 +634,31 @@ function setPassword($password, $method = 'pbkdf2') $count = isset($func_args[4]) ? $func_args[4] : 1000; // Keylength - $dkLen = isset($func_args[5]) ? $func_args[5] : $this->password_key_size; + if (isset($func_args[5])) { + $dkLen = $func_args[5]; + } else { + $key_length = $this->explicit_key_length !== false ? $this->explicit_key_length : $this->key_length; + $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length; + } - // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable switch (true) { + case $method == 'pbkdf1': + $hashObj = new Hash(); + $hashObj->setHash($hash); + if ($dkLen > $hashObj->getLength()) { + throw new \LengthException('Derived key length cannot be longer than the hash length'); + } + $t = $password . $salt; + for ($i = 0; $i < $count; ++$i) { + $t = $hashObj->hash($t); + } + $key = substr($t, 0, $dkLen); + + $this->setKey(substr($key, 0, $dkLen >> 1)); + $this->setIV(substr($key, $dkLen >> 1)); + + return true; + // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable case !function_exists('hash_pbkdf2'): case !function_exists('hash_algos'): case !in_array($hash, hash_algos()): @@ -594,9 +667,9 @@ function setPassword($password, $method = 'pbkdf2') $hmac = new Hash(); $hmac->setHash($hash); $hmac->setKey($password); - $f = $u = $hmac->_hash($salt . pack('N', $i++)); + $f = $u = $hmac->hash($salt . pack('N', $i++)); for ($j = 2; $j <= $count; ++$j) { - $u = $hmac->_hash($u); + $u = $hmac->hash($u); $f^= $u; } $key.= $f; @@ -609,6 +682,8 @@ function setPassword($password, $method = 'pbkdf2') } $this->setKey($key); + + return true; } /** @@ -625,30 +700,107 @@ function setPassword($password, $method = 'pbkdf2') * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that * length. * - * Note: Could, but not must, extend by the child * class - * - * @see Base::decrypt() + * @see self::decrypt() * @access public - * @param String $plaintext - * @return String $cipertext + * @param string $plaintext + * @return string $ciphertext + * @internal Could, but not must, extend by the child Crypt_* class */ function encrypt($plaintext) { - if ($this->engine == CRYPT_MODE_MCRYPT) { + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + + if ($this->engine === self::ENGINE_OPENSSL) { + if ($this->changed) { + $this->_clearBuffers(); + $this->changed = false; + } + switch ($this->mode) { + case self::MODE_STREAM: + return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + case self::MODE_ECB: + $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; + case self::MODE_CBC: + $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); + if (!defined('OPENSSL_RAW_DATA')) { + $result = substr($result, 0, -$this->block_size); + } + if ($this->continuousBuffer) { + $this->encryptIV = substr($result, -$this->block_size); + } + return $result; + case self::MODE_CTR: + return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer); + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + $ciphertext = ''; + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $this->block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $plaintext = substr($plaintext, $i); + } + + $overflow = $len % $this->block_size; + + if ($overflow) { + $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $iv = $this->_string_pop($ciphertext, $this->block_size); + + $size = $len - $overflow; + $block = $iv ^ substr($plaintext, -$overflow); + $iv = substr_replace($iv, $block, 0, $overflow); + $ciphertext.= $block; + $pos = $overflow; + } elseif ($len) { + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $iv = substr($ciphertext, -$this->block_size); + } + + return $ciphertext; + case self::MODE_OFB: + return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); + } + } + + if ($this->engine === self::ENGINE_MCRYPT) { if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->enchanged) { - - mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->_getIV($this->encryptIV)); $this->enchanged = false; } // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's // rewritten CFB implementation the above outputs the same thing twice. - if (false && $this->continuousBuffer) { + if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { $block_size = $this->block_size; $iv = &$this->encryptIV; $pos = &$this->enbuffer['pos']; @@ -701,14 +853,10 @@ function encrypt($plaintext) return $ciphertext; } - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } - $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->_getIV($this->encryptIV)); } return $ciphertext; @@ -722,20 +870,17 @@ function encrypt($plaintext) $inline = $this->inline_crypt; return $inline('encrypt', $this, $plaintext); } - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } $buffer = &$this->enbuffer; $block_size = $this->block_size; $ciphertext = ''; switch ($this->mode) { - case CRYPT_MODE_ECB: + case self::MODE_ECB: for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); } break; - case CRYPT_MODE_CBC: + case self::MODE_CBC: $xor = $this->encryptIV; for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); @@ -747,75 +892,76 @@ function encrypt($plaintext) $this->encryptIV = $xor; } break; - case CRYPT_MODE_CTR: + case self::MODE_CTR: $xor = $this->encryptIV; - if (strlen($buffer['encrypted'])) { + if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - if (strlen($block) > strlen($buffer['encrypted'])) { - $buffer['encrypted'].= $this->_encryptBlock($this->_generateXor($xor, $block_size)); + if (strlen($block) > strlen($buffer['ciphertext'])) { + $buffer['ciphertext'].= $this->_encryptBlock($xor); } - $key = $this->_stringShift($buffer['encrypted'], $block_size); + $this->_increment_str($xor); + $key = $this->_string_shift($buffer['ciphertext'], $block_size); $ciphertext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - $key = $this->_encryptBlock($this->_generateXor($xor, $block_size)); + $key = $this->_encryptBlock($xor); + $this->_increment_str($xor); $ciphertext.= $block ^ $key; } } if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { - $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } break; - - case CRYPT_MODE_CFB: - // cfb loosely routines inspired by openssl's: - // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} - if ($this->continuousBuffer) { - $iv = &$this->encryptIV; - $pos = &$buffer['pos']; - } else { - $iv = $this->encryptIV; - $pos = 0; - } - $len = strlen($plaintext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = $block_size - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - } - while ($len >= $block_size) { - $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); - $ciphertext.= $iv; - $len-= $block_size; - $i+= $block_size; - } - if ($len) { - $iv = $this->_encryptBlock($iv); - $block = $iv ^ substr($plaintext, $i); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } break; - case CRYPT_MODE_OFB: + case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { @@ -824,7 +970,7 @@ function encrypt($plaintext) $xor = $this->_encryptBlock($xor); $buffer['xor'].= $xor; } - $key = $this->_stringShift($buffer['xor'], $block_size); + $key = $this->_string_shift($buffer['xor'], $block_size); $ciphertext.= $block ^ $key; } } else { @@ -837,11 +983,11 @@ function encrypt($plaintext) if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; + $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; - case CRYPT_MODE_STREAM: + case self::MODE_STREAM: $ciphertext = $this->_encryptBlock($plaintext); break; } @@ -855,27 +1001,114 @@ function encrypt($plaintext) * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until * it is. * - * Note: Could, but not must, extend by the child * class - * - * @see Base::encrypt() + * @see self::encrypt() * @access public - * @param String $ciphertext - * @return String $plaintext + * @param string $ciphertext + * @return string $plaintext + * @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size + * @internal Could, but not must, extend by the child Crypt_* class */ function decrypt($ciphertext) { - if ($this->engine == CRYPT_MODE_MCRYPT) { + if ($this->paddable && strlen($ciphertext) % $this->block_size) { + throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')'); + } + + if ($this->engine === self::ENGINE_OPENSSL) { + if ($this->changed) { + $this->_clearBuffers(); + $this->changed = false; + } + switch ($this->mode) { + case self::MODE_STREAM: + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + break; + case self::MODE_ECB: + if (!defined('OPENSSL_RAW_DATA')) { + $ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); + } + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + break; + case self::MODE_CBC: + if (!defined('OPENSSL_RAW_DATA')) { + $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); + $ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); + $offset = 2 * $this->block_size; + } else { + $offset = $this->block_size; + } + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); + if ($this->continuousBuffer) { + $this->decryptIV = substr($ciphertext, -$offset, $this->block_size); + } + break; + case self::MODE_CTR: + $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer); + break; + case self::MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + $plaintext = ''; + if ($this->continuousBuffer) { + $iv = &$this->decryptIV; + $pos = &$this->buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; + } + $len = strlen($ciphertext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $this->block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + $ciphertext = substr($ciphertext, $i); + } + $overflow = $len % $this->block_size; + if ($overflow) { + $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + if ($len - $overflow) { + $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow); + } + $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $plaintext.= $iv ^ substr($ciphertext, -$overflow); + $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow); + $pos = $overflow; + } elseif ($len) { + $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); + $iv = substr($ciphertext, -$this->block_size); + } + break; + case self::MODE_OFB: + $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); + } + + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; + } + + if ($this->engine === self::ENGINE_MCRYPT) { $block_size = $this->block_size; if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->dechanged) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->_getIV($this->decryptIV)); $this->dechanged = false; } - if (false && $this->continuousBuffer) { + if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$this->debuffer['pos']; $len = strlen($ciphertext); @@ -913,16 +1146,10 @@ function decrypt($ciphertext) return $plaintext; } - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($block_size - strlen($ciphertext) % $block_size) % $block_size, chr(0)); - } - $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->_getIV($this->decryptIV)); } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; @@ -938,20 +1165,16 @@ function decrypt($ciphertext) } $block_size = $this->block_size; - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does [...] - $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($block_size - strlen($ciphertext) % $block_size) % $block_size, chr(0)); - } $buffer = &$this->debuffer; $plaintext = ''; switch ($this->mode) { - case CRYPT_MODE_ECB: + case self::MODE_ECB: for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); } break; - case CRYPT_MODE_CBC: + case self::MODE_CBC: $xor = $this->decryptIV; for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); @@ -962,21 +1185,23 @@ function decrypt($ciphertext) $this->decryptIV = $xor; } break; - case CRYPT_MODE_CTR: + case self::MODE_CTR: $xor = $this->decryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { - $buffer['ciphertext'].= $this->_encryptBlock($this->_generateXor($xor, $block_size)); + $buffer['ciphertext'].= $this->_encryptBlock($xor); + $this->_increment_str($xor); } - $key = $this->_stringShift($buffer['ciphertext'], $block_size); + $key = $this->_string_shift($buffer['ciphertext'], $block_size); $plaintext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); - $key = $this->_encryptBlock($this->_generateXor($xor, $block_size)); + $key = $this->_encryptBlock($xor); + $this->_increment_str($xor); $plaintext.= $block ^ $key; } } @@ -987,7 +1212,7 @@ function decrypt($ciphertext) } } break; - case CRYPT_MODE_CFB: + case self::MODE_CFB: if ($this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$buffer['pos']; @@ -1028,7 +1253,7 @@ function decrypt($ciphertext) $pos = $len; } break; - case CRYPT_MODE_OFB: + case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { @@ -1037,7 +1262,7 @@ function decrypt($ciphertext) $xor = $this->_encryptBlock($xor); $buffer['xor'].= $xor; } - $key = $this->_stringShift($buffer['xor'], $block_size); + $key = $this->_string_shift($buffer['xor'], $block_size); $plaintext.= $block ^ $key; } } else { @@ -1050,17 +1275,205 @@ function decrypt($ciphertext) if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % $block_size) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; + $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; - case CRYPT_MODE_STREAM: + case self::MODE_STREAM: $plaintext = $this->_decryptBlock($ciphertext); break; } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } + /** + * Get the IV + * + * mcrypt requires an IV even if ECB is used + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $iv + * @return string + * @access private + */ + function _getIV($iv) + { + return $this->mode == self::MODE_ECB ? str_repeat("\0", $this->block_size) : $iv; + } + + /** + * OpenSSL CTR Processor + * + * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream + * for CTR is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() + * and Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this + * function will emulate CTR with ECB when necesary. + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $plaintext + * @param string $encryptIV + * @param array $buffer + * @return string + * @access private + */ + function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer) + { + $ciphertext = ''; + + $block_size = $this->block_size; + $key = $this->key; + + if ($this->openssl_emulate_ctr) { + $xor = $encryptIV; + if (strlen($buffer['ciphertext'])) { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + if (strlen($block) > strlen($buffer['ciphertext'])) { + $result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; + $buffer['ciphertext'].= $result; + } + $this->_increment_str($xor); + $otp = $this->_string_shift($buffer['ciphertext'], $block_size); + $ciphertext.= $block ^ $otp; + } + } else { + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; + $this->_increment_str($xor); + $ciphertext.= $block ^ $otp; + } + } + if ($this->continuousBuffer) { + $encryptIV = $xor; + if ($start = strlen($plaintext) % $block_size) { + $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + } + } + + return $ciphertext; + } + + if (strlen($buffer['ciphertext'])) { + $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext)); + $plaintext = substr($plaintext, strlen($ciphertext)); + + if (!strlen($plaintext)) { + return $ciphertext; + } + } + + $overflow = strlen($plaintext) % $block_size; + if ($overflow) { + $plaintext2 = $this->_string_pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2 + $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + $temp = $this->_string_pop($encrypted, $block_size); + $ciphertext.= $encrypted . ($plaintext2 ^ $temp); + if ($this->continuousBuffer) { + $buffer['ciphertext'] = substr($temp, $overflow); + $encryptIV = $temp; + } + } elseif (!strlen($buffer['ciphertext'])) { + $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + $temp = $this->_string_pop($ciphertext, $block_size); + if ($this->continuousBuffer) { + $encryptIV = $temp; + } + } + if ($this->continuousBuffer) { + if (!defined('OPENSSL_RAW_DATA')) { + $encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + } + $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + if ($overflow) { + $this->_increment_str($encryptIV); + } + } + + return $ciphertext; + } + + /** + * OpenSSL OFB Processor + * + * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream + * for OFB is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() + * and Base::decrypt(). + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $plaintext + * @param string $encryptIV + * @param array $buffer + * @return string + * @access private + */ + function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer) + { + if (strlen($buffer['xor'])) { + $ciphertext = $plaintext ^ $buffer['xor']; + $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); + $plaintext = substr($plaintext, strlen($ciphertext)); + } else { + $ciphertext = ''; + } + + $block_size = $this->block_size; + + $len = strlen($plaintext); + $key = $this->key; + $overflow = $len % $block_size; + + if (strlen($plaintext)) { + if ($overflow) { + $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + $xor = $this->_string_pop($ciphertext, $block_size); + if ($this->continuousBuffer) { + $encryptIV = $xor; + } + $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow); + if ($this->continuousBuffer) { + $buffer['xor'] = $xor; + } + } else { + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); + if ($this->continuousBuffer) { + $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size); + } + } + } + + return $ciphertext; + } + + /** + * phpseclib <-> OpenSSL Mode Mapper + * + * May need to be overwritten by classes extending this one in some cases + * + * @return int + * @access private + */ + function _openssl_translate_mode() + { + switch ($this->mode) { + case self::MODE_ECB: + return 'ecb'; + case self::MODE_CBC: + return 'cbc'; + case self::MODE_CTR: + return 'ctr'; + case self::MODE_CFB: + return 'cfb'; + case self::MODE_OFB: + return 'ofb'; + } + } + /** * Pad "packets". * @@ -1073,7 +1486,7 @@ function decrypt($ciphertext) * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is * transmitted separately) * - * @see Base::disablePadding() + * @see self::disablePadding() * @access public */ function enablePadding() @@ -1084,7 +1497,7 @@ function enablePadding() /** * Do not pad packets. * - * @see Base::enablePadding() + * @see self::enablePadding() * @access public */ function disablePadding() @@ -1121,23 +1534,24 @@ function disablePadding() * outputs. The reason is due to the fact that the initialization vector's change after every encryption / * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. * - * Put another way, when the continuous buffer is enabled, the state of the *() object changes after each + * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\*() object changes after each * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * - * Note: Could, but not must, extend by the child * class - * - * @see Base::disableContinuousBuffer() + * @see self::disableContinuousBuffer() * @access public + * @internal Could, but not must, extend by the child Crypt_* class */ function enableContinuousBuffer() { - if ($this->mode == CRYPT_MODE_ECB) { + if ($this->mode == self::MODE_ECB) { return; } $this->continuousBuffer = true; + + $this->_setEngine(); } /** @@ -1145,14 +1559,13 @@ function enableContinuousBuffer() * * The default behavior. * - * Note: Could, but not must, extend by the child * class - * - * @see Base::enableContinuousBuffer() + * @see self::enableContinuousBuffer() * @access public + * @internal Could, but not must, extend by the child Crypt_* class */ function disableContinuousBuffer() { - if ($this->mode == CRYPT_MODE_ECB) { + if ($this->mode == self::MODE_ECB) { return; } if (!$this->continuousBuffer) { @@ -1161,56 +1574,191 @@ function disableContinuousBuffer() $this->continuousBuffer = false; $this->changed = true; + + $this->_setEngine(); } /** - * Encrypts a block + * Test for engine validity * - * Note: Must extend by the child * class + * @see self::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) { + return false; + } + $this->openssl_emulate_ctr = false; + $result = $this->cipher_name_openssl && + extension_loaded('openssl') && + // PHP 5.3.0 - 5.3.2 did not let you set IV's + version_compare(PHP_VERSION, '5.3.3', '>='); + if (!$result) { + return false; + } + + // prior to PHP 5.4.0 OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer + // $options openssl_encrypt expected a boolean $raw_data. + if (!defined('OPENSSL_RAW_DATA')) { + $this->openssl_options = true; + } else { + $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; + } + + $methods = openssl_get_cipher_methods(); + if (in_array($this->cipher_name_openssl, $methods)) { + return true; + } + // not all of openssl's symmetric cipher's support ctr. for those + // that don't we'll emulate it + switch ($this->mode) { + case self::MODE_CTR: + if (in_array($this->cipher_name_openssl_ecb, $methods)) { + $this->openssl_emulate_ctr = true; + return true; + } + } + return false; + case self::ENGINE_MCRYPT: + return $this->cipher_name_mcrypt && + extension_loaded('mcrypt') && + in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); + case self::ENGINE_INTERNAL: + return true; + } + + return false; + } + + /** + * Sets the preferred crypt engine * - * @access private - * @param String $in - * @return String + * Currently, $engine could be: + * + * - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast] + * + * - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast] + * + * - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow] + * + * If the preferred crypt engine is not available the fastest available one will be used + * + * @see self::__construct() + * @param int $engine + * @access public */ - function _encryptBlock($in) + function setPreferredEngine($engine) { - user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + switch ($engine) { + //case self::ENGINE_OPENSSL; + case self::ENGINE_MCRYPT: + case self::ENGINE_INTERNAL: + $this->preferredEngine = $engine; + break; + default: + $this->preferredEngine = self::ENGINE_OPENSSL; + } + + $this->_setEngine(); } /** - * Decrypts a block + * Returns the engine currently being utilized * - * Note: Must extend by the child * class + * @see self::_setEngine() + * @access public + */ + function getEngine() + { + return $this->engine; + } + + /** + * Sets the engine as appropriate * + * @see self::__construct() * @access private - * @param String $in - * @return String */ - function _decryptBlock($in) + function _setEngine() { - user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + $this->engine = null; + + $candidateEngines = array( + $this->preferredEngine, + self::ENGINE_OPENSSL, + self::ENGINE_MCRYPT + ); + foreach ($candidateEngines as $engine) { + if ($this->isValidEngine($engine)) { + $this->engine = $engine; + break; + } + } + if (!$this->engine) { + $this->engine = self::ENGINE_INTERNAL; + } + + if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { + // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, + // (re)open them with the module named in $this->cipher_name_mcrypt + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); + $this->enmcrypt = null; + $this->demcrypt = null; + + if ($this->ecb) { + mcrypt_module_close($this->ecb); + $this->ecb = null; + } + } + + $this->changed = true; } + /** + * Encrypts a block + * + * Note: Must be extended by the child \phpseclib\Crypt\* class + * + * @access private + * @param string $in + * @return string + */ + abstract function _encryptBlock($in); + + /** + * Decrypts a block + * + * Note: Must be extended by the child \phpseclib\Crypt\* class + * + * @access private + * @param string $in + * @return string + */ + abstract function _decryptBlock($in); + /** * Setup the key (expansion) * - * Only used if $engine == CRYPT_MODE_INTERNAL + * Only used if $engine == self::ENGINE_INTERNAL * - * Note: Must extend by the child * class + * Note: Must extend by the child \phpseclib\Crypt\* class * - * @see Base::_setup() + * @see self::_setup() * @access private */ - function _setupKey() - { - user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); - } + abstract function _setupKey(); /** - * Setup the CRYPT_MODE_INTERNAL $engine + * Setup the self::ENGINE_INTERNAL $engine * * (re)init, if necessary, the internal cipher $engine and flush all $buffers - * Used (only) if $engine == CRYPT_MODE_INTERNAL + * Used (only) if $engine == self::ENGINE_INTERNAL * * _setup() will be called each time if $changed === true * typically this happens when using one or more of following public methods: @@ -1223,14 +1771,12 @@ function _setupKey() * * - First run of encrypt() / decrypt() with no init-settings * - * Internally: _setup() is called always before(!) en/decryption. - * - * Note: Could, but not must, extend by the child * class - * - * @see setKey() - * @see setIV() - * @see disableContinuousBuffer() + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() * @access private + * @internal _setup() is always called before en/decryption. + * @internal Could, but not must, extend by the child Crypt_* class */ function _setup() { @@ -1243,10 +1789,10 @@ function _setup() } /** - * Setup the CRYPT_MODE_MCRYPT $engine + * Setup the self::ENGINE_MCRYPT $engine * * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers - * Used (only) if $engine = CRYPT_MODE_MCRYPT + * Used (only) if $engine = self::ENGINE_MCRYPT * * _setupMcrypt() will be called each time if $changed === true * typically this happens when using one or more of following public methods: @@ -1259,13 +1805,11 @@ function _setup() * * - First run of encrypt() / decrypt() * - * - * Note: Could, but not must, extend by the child * class - * - * @see setKey() - * @see setIV() - * @see disableContinuousBuffer() + * @see self::setKey() + * @see self::setIV() + * @see self::disableContinuousBuffer() * @access private + * @internal Could, but not must, extend by the child Crypt_* class */ function _setupMcrypt() { @@ -1274,12 +1818,12 @@ function _setupMcrypt() if (!isset($this->enmcrypt)) { static $mcrypt_modes = array( - CRYPT_MODE_CTR => 'ctr', - CRYPT_MODE_ECB => MCRYPT_MODE_ECB, - CRYPT_MODE_CBC => MCRYPT_MODE_CBC, - CRYPT_MODE_CFB => 'cfb', - CRYPT_MODE_OFB => MCRYPT_MODE_NOFB, - CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM, + self::MODE_CTR => 'ctr', + self::MODE_ECB => MCRYPT_MODE_ECB, + self::MODE_CBC => MCRYPT_MODE_CBC, + self::MODE_CFB => 'ncfb', + self::MODE_OFB => MCRYPT_MODE_NOFB, + self::MODE_STREAM => MCRYPT_MODE_STREAM, ); $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); @@ -1288,13 +1832,12 @@ function _setupMcrypt() // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} - if (false) { + if ($this->mode == self::MODE_CFB) { $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } - } // else should mcrypt_generic_deinit be called? - if (false) { + if ($this->mode == self::MODE_CFB) { mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } @@ -1309,10 +1852,11 @@ function _setupMcrypt() * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless * and padding will, hence forth, be enabled. * - * @see Base::_unpad() - * @param String $text + * @see self::_unpad() + * @param string $text + * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size * @access private - * @return String + * @return string */ function _pad($text) { @@ -1322,8 +1866,7 @@ function _pad($text) if ($length % $this->block_size == 0) { return $text; } else { - user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); - $this->padding = true; + throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); } } @@ -1338,10 +1881,11 @@ function _pad($text) * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong * and false will be returned. * - * @see Base::_pad() - * @param String $text + * @see self::_pad() + * @param string $text + * @throws \LengthException if the ciphertext's length is not a multiple of the block size * @access private - * @return String + * @return string */ function _unpad($text) { @@ -1352,7 +1896,7 @@ function _unpad($text) $length = ord($text[strlen($text) - 1]); if (!$length || $length > $this->block_size) { - return false; + throw new \LengthException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})"); } return substr($text, 0, -$length); @@ -1365,18 +1909,19 @@ function _unpad($text) * after disableContinuousBuffer() or on cipher $engine (re)init * ie after setKey() or setIV() * - * Note: Could, but not must, extend by the child * class - * - * @access public + * @access private + * @internal Could, but not must, extend by the child Crypt_* class + * @throws \UnexpectedValueException when an IV is required but not defined */ function _clearBuffers() { - $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); - $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + + if ($this->iv === false && !in_array($this->mode, array(self::MODE_STREAM, self::MODE_ECB))) { + throw new \UnexpectedValueException('No IV has been defined'); + } - // mcrypt's handling of invalid's $iv: - // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size); - $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0"); + $this->encryptIV = $this->decryptIV = $this->iv; } /** @@ -1384,12 +1929,12 @@ function _clearBuffers() * * Inspired by array_shift * - * @param String $string - * @param optional Integer $index + * @param string $string + * @param int $index * @access private - * @return String + * @return string */ - function _stringShift(&$string, $index = 1) + function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); @@ -1397,43 +1942,57 @@ function _stringShift(&$string, $index = 1) } /** - * Generate CTR XOR encryption key + * String Pop * - * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the - * plaintext / ciphertext in CTR mode. + * Inspired by array_pop * - * @see Base::decrypt() - * @see Base::encrypt() - * @param String $iv - * @param Integer $length + * @param string $string + * @param int $index * @access private - * @return String $xor + * @return string */ - function _generateXor(&$iv, $length) + function _string_pop(&$string, $index = 1) { - $xor = ''; - $block_size = $this->block_size; - $num_blocks = floor(($length + ($block_size - 1)) / $block_size); - for ($i = 0; $i < $num_blocks; $i++) { - $xor.= $iv; - for ($j = 4; $j <= $block_size; $j+= 4) { - $temp = substr($iv, -$j, 4); - switch ($temp) { - case "\xFF\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); - break; - case "\x7F\xFF\xFF\xFF": - $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); - break 2; - default: - extract(unpack('Ncount', $temp)); - $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); - break 2; - } + $substr = substr($string, -$index); + $string = substr($string, 0, -$index); + return $substr; + } + + /** + * Increment the current string + * + * @see self::decrypt() + * @see self::encrypt() + * @param string $var + * @access private + */ + function _increment_str(&$var) + { + for ($i = 4; $i <= strlen($var); $i+= 4) { + $temp = substr($var, -$i, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4); + break; + case "\x7F\xFF\xFF\xFF": + $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4); + return; + default: + $temp = unpack('Nnum', $temp); + $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4); + return; } } - return $xor; + $remainder = strlen($var) % 4; + + if ($remainder == 0) { + return; + } + + $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT)); + $temp = substr(pack('N', $temp['num'] + 1), -$remainder); + $var = substr_replace($var, $temp, 0, $remainder); } /** @@ -1446,14 +2005,14 @@ function _generateXor(&$iv, $length) * * _setupInlineCrypt() would be called only if: * - * - $engine == CRYPT_MODE_INTERNAL and + * - $engine == self::ENGINE_INTERNAL and * * - $use_inline_crypt === true * * - each time on _setup(), after(!) _setupKey() * * - * This ensures that _setupInlineCrypt() has allways a + * This ensures that _setupInlineCrypt() has always a * full ready2go initializated internal cipher $engine state * where, for example, the keys allready expanded, * keys/block_size calculated and such. @@ -1481,7 +2040,7 @@ function _generateXor(&$iv, $length) * - short (as good as possible) * * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code. - * - In case of using inline crypting, _setupInlineCrypt() must extend by the child * class. + * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib\Crypt\* class. * - The following variable names are reserved: * - $_* (all variable names prefixed with an underscore) * - $self (object reference to it self. Do not use $this, but $self instead) @@ -1489,19 +2048,18 @@ function _generateXor(&$iv, $length) * - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only * * - * @see Base::_setup() - * @see Base::_createInlineCryptFunction() - * @see Base::encrypt() - * @see Base::decrypt() + * @see self::_setup() + * @see self::_createInlineCryptFunction() + * @see self::encrypt() + * @see self::decrypt() * @access private + * @internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt() */ function _setupInlineCrypt() { - // If a * class providing inline crypting it must extend _setupInlineCrypt() - - // If, for any reason, an extending Base() * class + // If, for any reason, an extending \phpseclib\Crypt\Base() \phpseclib\Crypt\* class // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false - // ie in the class var declaration of $use_inline_crypt in general for the * class, + // ie in the class var declaration of $use_inline_crypt in general for the \phpseclib\Crypt\* class, // in the constructor at object instance-time // or, if it's runtime-specific, at runtime @@ -1598,7 +2156,7 @@ function _setupInlineCrypt() * +----------------------------------------------------------------------------------------------+ * * - * See also the *::_setupInlineCrypt()'s for + * See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for * productive inline $cipher_code's how they works. * * Structure of: @@ -1612,12 +2170,12 @@ function _setupInlineCrypt() * ); * * - * @see Base::_setupInlineCrypt() - * @see Base::encrypt() - * @see Base::decrypt() - * @param Array $cipher_code + * @see self::_setupInlineCrypt() + * @see self::encrypt() + * @see self::decrypt() + * @param array $cipher_code * @access private - * @return String (the name of the created callback function) + * @return string (the name of the created callback function) */ function _createInlineCryptFunction($cipher_code) { @@ -1635,10 +2193,9 @@ function _createInlineCryptFunction($cipher_code) // merged with the $cipher_code algorithm // for encrypt- and decryption. switch ($this->mode) { - case CRYPT_MODE_ECB: + case self::MODE_ECB: $encrypt = $init_encrypt . ' $_ciphertext = ""; - $_text = $self->_pad($_text); $_plaintext_len = strlen($_text); for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { @@ -1664,29 +2221,30 @@ function _createInlineCryptFunction($cipher_code) return $self->_unpad($_plaintext); '; break; - case CRYPT_MODE_CTR: + case self::MODE_CTR: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $_xor = $self->encryptIV; $_buffer = &$self->enbuffer; - - if (strlen($_buffer["encrypted"])) { + if (strlen($_buffer["ciphertext"])) { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); - if (strlen($_block) > strlen($_buffer["encrypted"])) { - $in = $self->_generateXor($_xor, '.$block_size.'); + if (strlen($_block) > strlen($_buffer["ciphertext"])) { + $in = $_xor; '.$encrypt_block.' - $_buffer["encrypted"].= $in; + $self->_increment_str($_xor); + $_buffer["ciphertext"].= $in; } - $_key = $self->_stringShift($_buffer["encrypted"], '.$block_size.'); + $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); $_ciphertext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); - $in = $self->_generateXor($_xor, '.$block_size.'); + $in = $_xor; '.$encrypt_block.' + $self->_increment_str($_xor); $_key = $in; $_ciphertext.= $_block ^ $_key; } @@ -1694,7 +2252,7 @@ function _createInlineCryptFunction($cipher_code) if ($self->continuousBuffer) { $self->encryptIV = $_xor; if ($_start = $_plaintext_len % '.$block_size.') { - $_buffer["encrypted"] = substr($_key, $_start) . $_buffer["encrypted"]; + $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; } } @@ -1711,18 +2269,20 @@ function _createInlineCryptFunction($cipher_code) for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); if (strlen($_block) > strlen($_buffer["ciphertext"])) { - $in = $self->_generateXor($_xor, '.$block_size.'); + $in = $_xor; '.$encrypt_block.' + $self->_increment_str($_xor); $_buffer["ciphertext"].= $in; } - $_key = $self->_stringShift($_buffer["ciphertext"], '.$block_size.'); + $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); $_plaintext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); - $in = $self->_generateXor($_xor, '.$block_size.'); + $in = $_xor; '.$encrypt_block.' + $self->_increment_str($_xor); $_key = $in; $_plaintext.= $_block ^ $_key; } @@ -1737,7 +2297,7 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - case CRYPT_MODE_CFB: + case self::MODE_CFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_buffer = &$self->enbuffer; @@ -1836,7 +2396,7 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - case CRYPT_MODE_OFB: + case self::MODE_OFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); @@ -1852,7 +2412,7 @@ function _createInlineCryptFunction($cipher_code) $_xor = $in; $_buffer["xor"].= $_xor; } - $_key = $self->_stringShift($_buffer["xor"], '.$block_size.'); + $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); $_ciphertext.= $_block ^ $_key; } } else { @@ -1888,7 +2448,7 @@ function _createInlineCryptFunction($cipher_code) $_xor = $in; $_buffer["xor"].= $_xor; } - $_key = $self->_stringShift($_buffer["xor"], '.$block_size.'); + $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); $_plaintext.= $_block ^ $_key; } } else { @@ -1909,7 +2469,7 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - case CRYPT_MODE_STREAM: + case self::MODE_STREAM: $encrypt = $init_encrypt . ' $_ciphertext = ""; '.$encrypt_block.' @@ -1921,11 +2481,10 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - // case CRYPT_MODE_CBC: + // case self::MODE_CBC: default: $encrypt = $init_encrypt . ' $_ciphertext = ""; - $_text = $self->_pad($_text); $_plaintext_len = strlen($_text); $in = $self->encryptIV; @@ -1983,11 +2542,46 @@ function _createInlineCryptFunction($cipher_code) * for which $mode the lambda function was created. * * @access private - * @return &Array + * @return array &$functions */ function &_getLambdaFunctions() { static $functions = array(); return $functions; } + + /** + * Generates a digest from $bytes + * + * @see self::_setupInlineCrypt() + * @access private + * @param $bytes + * @return string + */ + function _hashInlineCryptFunction($bytes) + { + if (!isset(self::$WHIRLPOOL_AVAILABLE)) { + self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos()); + } + + $result = ''; + $hash = $bytes; + + switch (true) { + case self::$WHIRLPOOL_AVAILABLE: + foreach (str_split($bytes, 64) as $t) { + $hash = hash('whirlpool', $hash, true); + $result .= $t ^ $hash; + } + return $result . hash('whirlpool', $hash, true); + default: + $len = strlen($bytes); + for ($i = 0; $i < $len; $i+=20) { + $t = substr($bytes, $i, 20); + $hash = sha1($hash, true); + $result .= $t ^ $hash; + } + return $result . sha1($hash, true); + } + } } diff --git a/src/phpseclib/Crypt/Blowfish.php b/src/phpseclib/Crypt/Blowfish.php new file mode 100755 index 00000000..3500df59 --- /dev/null +++ b/src/phpseclib/Crypt/Blowfish.php @@ -0,0 +1,588 @@ + + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $blowfish->decrypt($blowfish->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package Blowfish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of Blowfish. + * + * @package Blowfish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @access public + */ +class Blowfish extends Base +{ + /** + * Block Length of the cipher + * + * @see \phpseclib\Crypt\Base::block_size + * @var int + * @access private + */ + var $block_size = 8; + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'blowfish'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 500; + + /** + * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each + * + * S-Box 0 + * + * @access private + * @var array + */ + var $sbox0 = array( + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + ); + + /** + * S-Box 1 + * + * @access private + * @var array + */ + var $sbox1 = array( + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + ); + + /** + * S-Box 2 + * + * @access private + * @var array + */ + var $sbox2 = array( + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + ); + + /** + * S-Box 3 + * + * @access private + * @var array + */ + var $sbox3 = array( + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + ); + + /** + * P-Array consists of 18 32-bit subkeys + * + * @var array + * @access private + */ + var $parray = array( + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b + ); + + /** + * The BCTX-working Array + * + * Holds the expanded key [p] and the key-depended s-boxes [sb] + * + * @var array + * @access private + */ + var $bctx; + + /** + * Holds the last used key + * + * @var array + * @access private + */ + var $kl; + + /** + * The Key Length (in bytes) + * + * @see \phpseclib\Crypt\Base::setKeyLength() + * @var int + * @access private + * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk + * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could + * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * of that, we'll just precompute it once. + */ + var $key_length = 16; + + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + + /** + * Sets the key length. + * + * Key lengths can be between 32 and 448 bits. + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + if ($length < 32 || $length > 448) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported'); + } + + $this->key_length = $length >> 3; + + parent::setKeyLength($length); + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::isValidEngine() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + if ($this->key_length != 16) { + return false; + } + $this->cipher_name_openssl_ecb = 'bf-ecb'; + $this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode(); + } + + return parent::isValidEngine($engine); + } + + /** + * Setup the key (expansion) + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key); + + /* key-expanding p[] and S-Box building sb[] */ + $this->bctx = array( + 'p' => array(), + 'sb' => array( + $this->sbox0, + $this->sbox1, + $this->sbox2, + $this->sbox3 + ) + ); + + // unpack binary string in unsigned chars + $key = array_values(unpack('C*', $this->key)); + $keyl = count($key); + for ($j = 0, $i = 0; $i < 18; ++$i) { + // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ... + for ($data = 0, $k = 0; $k < 4; ++$k) { + $data = ($data << 8) | $key[$j]; + if (++$j >= $keyl) { + $j = 0; + } + } + $this->bctx['p'][] = $this->parray[$i] ^ $data; + } + + // encrypt the zero-string, replace P1 and P2 with the encrypted data, + // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys + $data = "\0\0\0\0\0\0\0\0"; + for ($i = 0; $i < 18; $i += 2) { + list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); + $this->bctx['p'][$i ] = $l; + $this->bctx['p'][$i + 1] = $r; + } + for ($i = 0; $i < 4; ++$i) { + for ($j = 0; $j < 256; $j += 2) { + list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); + $this->bctx['sb'][$i][$j ] = $l; + $this->bctx['sb'][$i][$j + 1] = $r; + } + } + } + + /** + * Encrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + $p = $this->bctx["p"]; + // extract($this->bctx["sb"], EXTR_PREFIX_ALL, "sb"); // slower + $sb_0 = $this->bctx["sb"][0]; + $sb_1 = $this->bctx["sb"][1]; + $sb_2 = $this->bctx["sb"][2]; + $sb_3 = $this->bctx["sb"][3]; + + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + + for ($i = 0; $i < 16; $i+= 2) { + $l^= $p[$i]; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= $p[$i + 1]; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + } + return pack("N*", $r ^ $p[17], $l ^ $p[16]); + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + $p = $this->bctx["p"]; + $sb_0 = $this->bctx["sb"][0]; + $sb_1 = $this->bctx["sb"][1]; + $sb_2 = $this->bctx["sb"][2]; + $sb_3 = $this->bctx["sb"][3]; + + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + + for ($i = 17; $i > 2; $i-= 2) { + $l^= $p[$i]; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= $p[$i - 1]; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + } + return pack("N*", $r ^ $p[0], $l ^ $p[1]); + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // (Currently, for Blowfish, one generated $lambda_function cost on php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit) + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a unique hash for our generated code + $code_hash = "Crypt_Blowfish, {$this->mode}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + $p = $this->bctx['p']; + $init_crypt = ' + static $sb_0, $sb_1, $sb_2, $sb_3; + if (!$sb_0) { + $sb_0 = $self->bctx["sb"][0]; + $sb_1 = $self->bctx["sb"][1]; + $sb_2 = $self->bctx["sb"][2]; + $sb_3 = $self->bctx["sb"][3]; + } + '; + break; + default: + $p = array(); + for ($i = 0; $i < 18; ++$i) { + $p[] = '$p_' . $i; + } + $init_crypt = ' + list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"]; + list(' . implode(',', $p) . ') = $self->bctx["p"]; + + '; + } + + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + for ($i = 0; $i < 16; $i+= 2) { + $encrypt_block.= ' + $l^= ' . $p[$i] . '; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= ' . $p[$i + 1] . '; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + '; + } + $encrypt_block.= ' + $in = pack("N*", + $r ^ ' . $p[17] . ', + $l ^ ' . $p[16] . ' + ); + '; + + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("N*", $in); + $l = $in[1]; + $r = $in[2]; + '; + + for ($i = 17; $i > 2; $i-= 2) { + $decrypt_block.= ' + $l^= ' . $p[$i] . '; + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ + $sb_2[$l >> 8 & 0xff]) + + $sb_3[$l & 0xff]; + + $r^= ' . $p[$i - 1] . '; + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ + $sb_2[$r >> 8 & 0xff]) + + $sb_3[$r & 0xff]; + '; + } + + $decrypt_block.= ' + $in = pack("N*", + $r ^ ' . $p[0] . ', + $l ^ ' . $p[1] . ' + ); + '; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => '', + 'init_decrypt' => '', + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/src/phpseclib/Crypt/DES.php b/src/phpseclib/Crypt/DES.php old mode 100644 new mode 100755 index 934632dc..14273d28 --- a/src/phpseclib/Crypt/DES.php +++ b/src/phpseclib/Crypt/DES.php @@ -5,7 +5,7 @@ * * Uses mcrypt, if available, and an internal implementation, otherwise. * - * PHP versions 4 and 5 + * PHP version 5 * * Useful resources are as follows: * @@ -16,9 +16,9 @@ * Here's a short example of how to use this library: * * setKey('abcdefgh'); * @@ -32,164 +32,87 @@ * ?> * * - * LICENSE: 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. - * * @category Crypt * @package DES * @author Jim Wigginton - * @copyright MMVII Jim Wigginton + * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access private - * @see DES::_setupKey() - * @see DES::_processBlock() - */ -/** - * Contains $keys[CRYPT_DES_ENCRYPT] - */ -@define('CRYPT_DES_ENCRYPT', 0); -/** - * Contains $keys[CRYPT_DES_DECRYPT] - */ -@define('CRYPT_DES_DECRYPT', 1); -/**#@-*/ - -/**#@+ - * @access public - * @see DES::encrypt() - * @see DES::decrypt() - */ -/** - * Encrypt / decrypt using the Counter mode. - * - * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 - */ -@define('CRYPT_DES_MODE_CTR', CRYPT_MODE_CTR); -/** - * Encrypt / decrypt using the Electronic Code Book mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 - */ -@define('CRYPT_DES_MODE_ECB', CRYPT_MODE_ECB); -/** - * Encrypt / decrypt using the Code Book Chaining mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 - */ -@define('CRYPT_DES_MODE_CBC', CRYPT_MODE_CBC); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 - */ -@define('CRYPT_DES_MODE_CFB', CRYPT_MODE_CFB); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 - */ -@define('CRYPT_DES_MODE_OFB', CRYPT_MODE_OFB); -/**#@-*/ - -/**#@+ - * @access private - * @see DES::DES() - */ -/** - * Toggles the internal implementation - */ -@define('CRYPT_DES_MODE_INTERNAL', CRYPT_MODE_INTERNAL); -/** - * Toggles the mcrypt implementation - */ -@define('CRYPT_DES_MODE_MCRYPT', CRYPT_MODE_MCRYPT); -/**#@-*/ - /** * Pure-PHP implementation of DES. * * @package DES * @author Jim Wigginton - * @version 0.1.0 * @access public */ class DES extends Base { - /** - * Block Length of the cipher - * - * @see Base::block_size - * @var Integer + /**#@+ * @access private + * @see \phpseclib\Crypt\DES::_setupKey() + * @see \phpseclib\Crypt\DES::_processBlock() */ - var $block_size = 8; + /** + * Contains $keys[self::ENCRYPT] + */ + const ENCRYPT = 0; + /** + * Contains $keys[self::DECRYPT] + */ + const DECRYPT = 1; + /**#@-*/ /** - * The Key + * Block Length of the cipher * - * @see Base::key - * @see setKey() - * @var String + * @see \phpseclib\Crypt\Base::block_size + * @var int * @access private */ - var $key = "\0\0\0\0\0\0\0\0"; + var $block_size = 8; /** - * The default password key_size used by setPassword() + * Key Length (in bytes) * - * @see Base::password_key_size - * @see Base::setPassword() - * @var Integer + * @see \phpseclib\Crypt\Base::setKeyLength() + * @var int * @access private */ - var $password_key_size = 8; + var $key_length = 8; /** - * The namespace used by the cipher for its constants. + * The mcrypt specific name of the cipher * - * @see Base::const_namespace - * @var String + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string * @access private */ - var $const_namespace = 'DES'; + var $cipher_name_mcrypt = 'des'; /** - * The mcrypt specific name of the cipher + * The OpenSSL names of the cipher / modes * - * @see Base::cipher_name_mcrypt - * @var String + * @see \phpseclib\Crypt\Base::openssl_mode_names + * @var array * @access private */ - var $cipher_name_mcrypt = 'des'; + var $openssl_mode_names = array( + self::MODE_ECB => 'des-ecb', + self::MODE_CBC => 'des-cbc', + self::MODE_CFB => 'des-cfb', + self::MODE_OFB => 'des-ofb' + // self::MODE_CTR is undefined for DES + ); /** * Optimizing value while CFB-encrypting * - * @see Base::cfb_init_len - * @var Integer + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int * @access private */ var $cfb_init_len = 500; @@ -197,11 +120,11 @@ class DES extends Base /** * Switch for DES/3DES encryption * - * Used only if $engine == CRYPT_DES_MODE_INTERNAL + * Used only if $engine == self::ENGINE_INTERNAL * - * @see DES::_setupKey() - * @see DES::_processBlock() - * @var Integer + * @see self::_setupKey() + * @see self::_processBlock() + * @var int * @access private */ var $des_rounds = 1; @@ -209,17 +132,17 @@ class DES extends Base /** * max possible size of $key * - * @see DES::setKey() - * @var String + * @see self::setKey() + * @var string * @access private */ - var $key_size_max = 8; + var $key_length_max = 8; /** * The Key Schedule * - * @see DES::_setupKey() - * @var Array + * @see self::_setupKey() + * @var array * @access private */ var $keys; @@ -231,9 +154,9 @@ class DES extends Base * with each byte containing all bits in the same state as the * corresponding bit in the index value. * - * @see DES::_processBlock() - * @see DES::_setupKey() - * @var Array + * @see self::_processBlock() + * @see self::_setupKey() + * @var array * @access private */ var $shuffle = array( @@ -372,7 +295,7 @@ class DES extends Base * * Indexing this table with each source byte performs the initial bit permutation. * - * @var Array + * @var array * @access private */ var $ipmap = array( @@ -414,7 +337,7 @@ class DES extends Base * Inverse IP mapping helper table. * Indexing this table with a byte value reverses the bit order. * - * @var Array + * @var array * @access private */ var $invipmap = array( @@ -458,7 +381,7 @@ class DES extends Base * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the * P table: concatenation can then be replaced by exclusive ORs. * - * @var Array + * @var array * @access private */ var $sbox1 = array( @@ -483,7 +406,7 @@ class DES extends Base /** * Pre-permuted S-box2 * - * @var Array + * @var array * @access private */ var $sbox2 = array( @@ -508,7 +431,7 @@ class DES extends Base /** * Pre-permuted S-box3 * - * @var Array + * @var array * @access private */ var $sbox3 = array( @@ -533,7 +456,7 @@ class DES extends Base /** * Pre-permuted S-box4 * - * @var Array + * @var array * @access private */ var $sbox4 = array( @@ -558,7 +481,7 @@ class DES extends Base /** * Pre-permuted S-box5 * - * @var Array + * @var array * @access private */ var $sbox5 = array( @@ -583,7 +506,7 @@ class DES extends Base /** * Pre-permuted S-box6 * - * @var Array + * @var array * @access private */ var $sbox6 = array( @@ -608,7 +531,7 @@ class DES extends Base /** * Pre-permuted S-box7 * - * @var Array + * @var array * @access private */ var $sbox7 = array( @@ -633,7 +556,7 @@ class DES extends Base /** * Pre-permuted S-box8 * - * @var Array + * @var array * @access private */ var $sbox8 = array( @@ -658,52 +581,56 @@ class DES extends Base /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. - * - * $mode could be: - * - * - CRYPT_DES_MODE_ECB - * - * - CRYPT_DES_MODE_CBC - * - * - CRYPT_DES_MODE_CTR - * - * - CRYPT_DES_MODE_CFB - * - * - CRYPT_DES_MODE_OFB + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + + /** + * Test for engine validity * - * If not explictly set, CRYPT_DES_MODE_CBC will be used. + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() * - * @see Base::Base() - * @param optional Integer $mode + * @see \phpseclib\Crypt\Base::isValidEngine() + * @param int $engine * @access public + * @return bool */ - function DES($mode = CRYPT_DES_MODE_CBC) + function isValidEngine($engine) { - parent::Base($mode); + if ($this->key_length_max == 8) { + if ($engine == self::ENGINE_OPENSSL) { + $this->cipher_name_openssl_ecb = 'des-ecb'; + $this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode(); + } + } + + return parent::isValidEngine($engine); } /** * Sets the key. * - * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we - * only use the first eight, if $key has more then eight characters in it, and pad $key with the - * null byte if it is less then eight characters long. + * Keys must be 64-bits long or 8 bytes long. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * - * If the key is not explicitly set, it'll be assumed to be all zero's. - * - * @see Base::setKey() + * @see \phpseclib\Crypt\Base::setKey() * @access public - * @param String $key + * @param string $key */ function setKey($key) { - // We check/cut here only up to max length of the key. - // Key padding to the proper length will be done in _setupKey() - if (strlen($key) > $this->key_size_max) { - $key = substr($key, 0, $this->key_size_max); + if (!($this instanceof TripleDES) && strlen($key) != 8) { + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported'); } // Sets the key @@ -713,46 +640,46 @@ function setKey($key) /** * Encrypts a block * - * @see Base::_encryptBlock() - * @see Base::encrypt() - * @see DES::encrypt() + * @see \phpseclib\Crypt\Base::_encryptBlock() + * @see \phpseclib\Crypt\Base::encrypt() + * @see self::encrypt() * @access private - * @param String $in - * @return String + * @param string $in + * @return string */ function _encryptBlock($in) { - return $this->_processBlock($in, CRYPT_DES_ENCRYPT); + return $this->_processBlock($in, self::ENCRYPT); } /** * Decrypts a block * - * @see Base::_decryptBlock() - * @see Base::decrypt() - * @see DES::decrypt() + * @see \phpseclib\Crypt\Base::_decryptBlock() + * @see \phpseclib\Crypt\Base::decrypt() + * @see self::decrypt() * @access private - * @param String $in - * @return String + * @param string $in + * @return string */ function _decryptBlock($in) { - return $this->_processBlock($in, CRYPT_DES_DECRYPT); + return $this->_processBlock($in, self::DECRYPT); } /** * Encrypts or decrypts a 64-bit block * - * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See + * $mode should be either self::ENCRYPT or self::DECRYPT. See * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general * idea of what this function does. * - * @see DES::_encryptBlock() - * @see DES::_decryptBlock() + * @see self::_encryptBlock() + * @see self::_decryptBlock() * @access private - * @param String $block - * @param Integer $mode - * @return String + * @param string $block + * @param int $mode + * @return string */ function _processBlock($block, $mode) { @@ -832,7 +759,7 @@ function _processBlock($block, $mode) /** * Creates the key schedule * - * @see Base::_setupKey() + * @see \phpseclib\Crypt\Base::_setupKey() * @access private */ function _setupKey() @@ -1313,8 +1240,8 @@ function _setupKey() $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F); $keys[$des_round] = array( - CRYPT_DES_ENCRYPT => array(), - CRYPT_DES_DECRYPT => array_fill(0, 32, 0) + self::ENCRYPT => array(), + self::DECRYPT => array_fill(0, 32, 0) ); for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) { $c <<= $shifts[$i]; @@ -1333,33 +1260,33 @@ function _setupKey() (($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF); $val2 = (($cp << 8) & 0xFF000000) | (($cp << 16) & 0x00FF0000) | (($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF); - $keys[$des_round][CRYPT_DES_ENCRYPT][ ] = $val1; - $keys[$des_round][CRYPT_DES_DECRYPT][$ki - 1] = $val1; - $keys[$des_round][CRYPT_DES_ENCRYPT][ ] = $val2; - $keys[$des_round][CRYPT_DES_DECRYPT][$ki ] = $val2; + $keys[$des_round][self::ENCRYPT][ ] = $val1; + $keys[$des_round][self::DECRYPT][$ki - 1] = $val1; + $keys[$des_round][self::ENCRYPT][ ] = $val2; + $keys[$des_round][self::DECRYPT][$ki ] = $val2; } } switch ($this->des_rounds) { case 3: // 3DES keys $this->keys = array( - CRYPT_DES_ENCRYPT => array_merge( - $keys[0][CRYPT_DES_ENCRYPT], - $keys[1][CRYPT_DES_DECRYPT], - $keys[2][CRYPT_DES_ENCRYPT] + self::ENCRYPT => array_merge( + $keys[0][self::ENCRYPT], + $keys[1][self::DECRYPT], + $keys[2][self::ENCRYPT] ), - CRYPT_DES_DECRYPT => array_merge( - $keys[2][CRYPT_DES_DECRYPT], - $keys[1][CRYPT_DES_ENCRYPT], - $keys[0][CRYPT_DES_DECRYPT] + self::DECRYPT => array_merge( + $keys[2][self::DECRYPT], + $keys[1][self::ENCRYPT], + $keys[0][self::DECRYPT] ) ); break; // case 1: // DES keys default: $this->keys = array( - CRYPT_DES_ENCRYPT => $keys[0][CRYPT_DES_ENCRYPT], - CRYPT_DES_DECRYPT => $keys[0][CRYPT_DES_DECRYPT] + self::ENCRYPT => $keys[0][self::ENCRYPT], + self::DECRYPT => $keys[0][self::DECRYPT] ); } } @@ -1367,12 +1294,12 @@ function _setupKey() /** * Setup the performance-optimized function for de/encrypt() * - * @see Base::_setupInlineCrypt() + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() * @access private */ function _setupInlineCrypt() { - $lambda_functions =& DES::_getLambdaFunctions(); + $lambda_functions =& self::_getLambdaFunctions(); // Engine configuration for: // - DES ($des_rounds == 1) or @@ -1380,21 +1307,20 @@ function _setupInlineCrypt() $des_rounds = $this->des_rounds; // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // (Currently, for DES, one generated $lambda_function cost on php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit) + // (Currently, for TripleDES, one generated $lambda_function cost on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit) // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); // Generation of a uniqe hash for our generated code - switch (true) { - case $gen_hi_opt_code: - // For hi-optimized code, we create for each combination of - // $mode, $des_rounds and $this->key its own encrypt/decrypt function. - $code_hash = md5(str_pad("DES, $des_rounds, {$this->mode}, ", 32, "\0") . $this->key); - break; - default: - // After max 10 hi-optimized functions, we create generic - // (still very fast.. but not ultra) functions for each $mode/$des_rounds - // Currently 2 * 5 generic functions will be then max. possible. - $code_hash = "DES, $des_rounds, {$this->mode}"; + $code_hash = "Crypt_DES, $des_rounds, {$this->mode}"; + if ($gen_hi_opt_code) { + // For hi-optimized code, we create for each combination of + // $mode, $des_rounds and $this->key its own encrypt/decrypt function. + // After max 10 hi-optimized functions, we create generic + // (still very fast.. but not ultra) functions for each $mode/$des_rounds + // Currently 2 * 5 generic functions will be then max. possible. + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } // Is there a re-usable $lambda_functions in there? If not, we have to create it. @@ -1424,8 +1350,8 @@ function _setupInlineCrypt() // No futher initialisation of the $keys schedule is necessary. // That is the extra performance boost. $k = array( - CRYPT_DES_ENCRYPT => $this->keys[CRYPT_DES_ENCRYPT], - CRYPT_DES_DECRYPT => $this->keys[CRYPT_DES_DECRYPT] + self::ENCRYPT => $this->keys[self::ENCRYPT], + self::DECRYPT => $this->keys[self::DECRYPT] ); $init_encrypt = ''; $init_decrypt = ''; @@ -1434,22 +1360,21 @@ function _setupInlineCrypt() // In generic optimized code mode, we have to use, as the best compromise [currently], // our key schedule as $ke/$kd arrays. (with hardcoded indexes...) $k = array( - CRYPT_DES_ENCRYPT => array(), - CRYPT_DES_DECRYPT => array() + self::ENCRYPT => array(), + self::DECRYPT => array() ); - for ($i = 0, $c = count($this->keys[CRYPT_DES_ENCRYPT]); $i < $c; ++$i) { - $k[CRYPT_DES_ENCRYPT][$i] = '$ke[' . $i . ']'; - $k[CRYPT_DES_DECRYPT][$i] = '$kd[' . $i . ']'; + for ($i = 0, $c = count($this->keys[self::ENCRYPT]); $i < $c; ++$i) { + $k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; + $k[self::DECRYPT][$i] = '$kd[' . $i . ']'; } - $init_encrypt = '$ke = $self->keys[CRYPT_DES_ENCRYPT];'; - $init_decrypt = '$kd = $self->keys[CRYPT_DES_DECRYPT];'; + $init_encrypt = '$ke = $self->keys[self::ENCRYPT];'; + $init_decrypt = '$kd = $self->keys[self::DECRYPT];'; break; } // Creating code for en- and decryption. $crypt_block = array(); - foreach (array(CRYPT_DES_ENCRYPT, CRYPT_DES_DECRYPT) as $c) { - + foreach (array(self::ENCRYPT, self::DECRYPT) as $c) { /* Do the initial IP permutation. */ $crypt_block[$c] = ' $in = unpack("N*", $in); @@ -1516,8 +1441,8 @@ function _setupInlineCrypt() 'init_crypt' => $init_crypt, 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, - 'encrypt_block' => $crypt_block[CRYPT_DES_ENCRYPT], - 'decrypt_block' => $crypt_block[CRYPT_DES_DECRYPT] + 'encrypt_block' => $crypt_block[self::ENCRYPT], + 'decrypt_block' => $crypt_block[self::DECRYPT] ) ); } diff --git a/src/phpseclib/Crypt/Hash.php b/src/phpseclib/Crypt/Hash.php old mode 100644 new mode 100755 index c8879212..be48a14a --- a/src/phpseclib/Crypt/Hash.php +++ b/src/phpseclib/Crypt/Hash.php @@ -1,87 +1,45 @@ * setKey('abcdefg'); * - * echo base64_encode($hash->_hash('abcdefg')); + * echo base64_encode($hash->hash('abcdefg')); * ?> * * - * LICENSE: 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. - * * @category Crypt * @package Hash * @author Jim Wigginton - * @copyright MMVII Jim Wigginton + * @copyright 2015 Jim Wigginton + * @author Andreas Fischer + * @copyright 2015 Andreas Fischer * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access private - * @see Hash::Hash() - */ -/** - * Toggles the internal implementation - */ use phpseclib\Math\BigInteger; +use phpseclib\Exception\UnsupportedAlgorithmException; -@define('CRYPT_HASH_MODE_INTERNAL', 1); -/** - * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+. - */ -@define('CRYPT_HASH_MODE_MHASH', 2); /** - * Toggles the hash() implementation, which works on PHP 5.1.2+. - */ -@define('CRYPT_HASH_MODE_HASH', 3); -/**#@-*/ - -/** - * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. - * * @package Hash * @author Jim Wigginton - * @version 0.1.0 + * @author Andreas Fischer * @access public */ class Hash @@ -89,35 +47,26 @@ class Hash /** * Hash Parameter * - * @see Hash::setHash() - * @var Integer + * @see self::setHash() + * @var int * @access private */ var $hashParam; - /** - * Byte-length of compression blocks / key (Internal HMAC) - * - * @see Hash::setAlgorithm() - * @var Integer - * @access private - */ - var $b; - /** * Byte-length of hash output (Internal HMAC) * - * @see Hash::setHash() - * @var Integer + * @see self::setHash() + * @var int * @access private */ - var $l = false; + var $length; /** * Hash Algorithm * - * @see Hash::setHash() - * @var String + * @see self::setHash() + * @var string * @access private */ var $hash; @@ -125,17 +74,30 @@ class Hash /** * Key * - * @see Hash::setKey() - * @var String + * @see self::setKey() + * @var string * @access private */ var $key = false; + /** + * Initial Hash + * + * Used only for sha512/* + * + * @see self::_sha512() + * @var array + * @access private + */ + var $initial = false; + /** * Outer XOR (Internal HMAC) * - * @see Hash::setKey() - * @var String + * Used only for sha512/* + * + * @see self::hash() + * @var string * @access private */ var $opad; @@ -143,8 +105,10 @@ class Hash /** * Inner XOR (Internal HMAC) * - * @see Hash::setKey() - * @var String + * Used only for sha512/* + * + * @see self::hash() + * @var string * @access private */ var $ipad; @@ -152,26 +116,15 @@ class Hash /** * Default Constructor. * - * @param optional String $hash - * @return Hash + * @param string $hash * @access public */ - function Hash($hash = 'sha1') + function __construct($hash = 'sha256') { - if ( !defined('CRYPT_HASH_MODE') ) { - switch (true) { - case extension_loaded('hash'): - @define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH); - break; - case extension_loaded('mhash'): - @define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH); - break; - default: - @define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL); - } - } - $this->setHash($hash); + + $this->ipad = str_repeat(chr(0x36), 128); + $this->opad = str_repeat(chr(0x5C), 128); } /** @@ -180,7 +133,7 @@ function Hash($hash = 'sha1') * Keys can be of any length. * * @access public - * @param optional String $key + * @param string $key */ function setKey($key = false) { @@ -193,7 +146,7 @@ function setKey($key = false) * As set by the constructor or by the setHash() method. * * @access public - * @return String + * @return string */ function getHash() { @@ -204,407 +157,133 @@ function getHash() * Sets the hash function. * * @access public - * @param String $hash + * @param string $hash */ function setHash($hash) { $this->hashParam = $hash = strtolower($hash); switch ($hash) { + case 'md2-96': case 'md5-96': case 'sha1-96': - $this->l = 12; // 96 / 8 = 12 + case 'sha224-96': + case 'sha256-96': + case 'sha384-96': + case 'sha512-96': + case 'sha512/224-96': + case 'sha512/256-96': + $hash = substr($hash, 0, -3); + $this->length = 12; // 96 / 8 = 12 break; case 'md2': case 'md5': - $this->l = 16; + $this->length = 16; break; case 'sha1': - $this->l = 20; + $this->length = 20; + break; + case 'sha224': + case 'sha512/224': + $this->length = 28; break; case 'sha256': - $this->l = 32; + case 'sha512/256': + $this->length = 32; break; case 'sha384': - $this->l = 48; + $this->length = 48; break; case 'sha512': - $this->l = 64; - } - - switch ($hash) { - case 'md2': - $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ? - CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL; - break; - case 'sha384': - case 'sha512': - $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; + $this->length = 64; break; default: - $mode = CRYPT_HASH_MODE; - } - - switch ( $mode ) { - case CRYPT_HASH_MODE_MHASH: - switch ($hash) { - case 'md5': - case 'md5-96': - $this->hash = MHASH_MD5; - break; - case 'sha256': - $this->hash = MHASH_SHA256; - break; - case 'sha1': - case 'sha1-96': - default: - $this->hash = MHASH_SHA1; - } - return; - case CRYPT_HASH_MODE_HASH: - switch ($hash) { - case 'md5': - case 'md5-96': - $this->hash = 'md5'; - return; - case 'md2': - case 'sha256': - case 'sha384': - case 'sha512': - $this->hash = $hash; - return; - case 'sha1': - case 'sha1-96': - default: - $this->hash = 'sha1'; - } - return; - } - - switch ($hash) { - case 'md2': - $this->b = 16; - $this->hash = array($this, '_md2'); - break; - case 'md5': - case 'md5-96': - $this->b = 64; - $this->hash = array($this, '_md5'); - break; - case 'sha256': - $this->b = 64; - $this->hash = array($this, '_sha256'); - break; - case 'sha384': - case 'sha512': - $this->b = 128; - $this->hash = array($this, '_sha512'); - break; - case 'sha1': - case 'sha1-96': - default: - $this->b = 64; - $this->hash = array($this, '_sha1'); + throw new UnsupportedAlgorithmException( + "$hash is not a supported algorithm" + ); } - $this->ipad = str_repeat(chr(0x36), $this->b); - $this->opad = str_repeat(chr(0x5C), $this->b); - } - - /** - * Compute the HMAC. - * - * @access public - * @param String $text - * @return String - */ - function _hash($text) - { - $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; - - if (!empty($this->key) || is_string($this->key)) { - switch ( $mode ) { - case CRYPT_HASH_MODE_MHASH: - $output = mhash($this->hash, $text, $this->key); - break; - case CRYPT_HASH_MODE_HASH: - $output = hash_hmac($this->hash, $text, $this->key, true); - break; - case CRYPT_HASH_MODE_INTERNAL: - /* "Applications that use keys longer than B bytes will first hash the key using H and then use the - resultant L byte string as the actual key to HMAC." - - -- http://tools.ietf.org/html/rfc2104#section-2 */ - $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key; - - $key = str_pad($key, $this->b, chr(0)); // step 1 - $temp = $this->ipad ^ $key; // step 2 - $temp .= $text; // step 3 - $temp = call_user_func($this->hash, $temp); // step 4 - $output = $this->opad ^ $key; // step 5 - $output.= $temp; // step 6 - $output = call_user_func($this->hash, $output); // step 7 - } - } else { - switch ( $mode ) { - case CRYPT_HASH_MODE_MHASH: - $output = mhash($this->hash, $text); - break; - case CRYPT_HASH_MODE_HASH: - $output = hash($this->hash, $text, true); - break; - case CRYPT_HASH_MODE_INTERNAL: - $output = call_user_func($this->hash, $text); + if ($hash == 'sha512/224' || $hash == 'sha512/256') { + // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24 + $this->initial = $hash == 'sha512/256' ? + array( + '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD', + '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2' + ) : + array( + '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF', + '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1' + ); + for ($i = 0; $i < 8; $i++) { + $this->initial[$i] = new BigInteger($this->initial[$i], 16); + $this->initial[$i]->setPrecision(64); } } - return substr($output, 0, $this->l); + $this->hash = $hash; } /** - * Returns the hash length (in bytes) + * Compute the HMAC. * * @access public - * @return Integer - */ - function getLength() - { - return $this->l; - } - - /** - * Wrapper for MD5 - * - * @access private - * @param String $m - */ - function _md5($m) - { - return pack('H*', md5($m)); - } - - /** - * Wrapper for SHA1 - * - * @access private - * @param String $m - */ - function _sha1($m) - { - return pack('H*', sha1($m)); - } - - /** - * Pure-PHP implementation of MD2 - * - * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. - * - * @access private - * @param String $m + * @param string $text + * @return string */ - function _md2($m) + function hash($text) { - static $s = array( - 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, - 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, - 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, - 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, - 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, - 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, - 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, - 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, - 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, - 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, - 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, - 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, - 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, - 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, - 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, - 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, - 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, - 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 - ); - - // Step 1. Append Padding Bytes - $pad = 16 - (strlen($m) & 0xF); - $m.= str_repeat(chr($pad), $pad); - - $length = strlen($m); - - // Step 2. Append Checksum - $c = str_repeat(chr(0), 16); - $l = chr(0); - for ($i = 0; $i < $length; $i+= 16) { - for ($j = 0; $j < 16; $j++) { - // RFC1319 incorrectly states that C[j] should be set to S[c xor L] - //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); - // per , however, C[j] should be set to S[c xor L] xor C[j] - $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j])); - $l = $c[$j]; - } - } - $m.= $c; + switch ($this->hash) { + case 'sha512/224': + case 'sha512/256': + if (empty($this->key) || !is_string($this->key)) { + return substr(self::_sha512($text, $this->initial), 0, $this->length); + } + /* "Applications that use keys longer than B bytes will first hash the key using H and then use the + resultant L byte string as the actual key to HMAC." - $length+= 16; + -- http://tools.ietf.org/html/rfc2104#section-2 */ + $key = strlen($this->key) > $this->b ? self::_sha512($this->key, $this->initial) : $this->key; - // Step 3. Initialize MD Buffer - $x = str_repeat(chr(0), 48); + $key = str_pad($this->key, 128, chr(0)); // step 1 + $temp = $this->ipad ^ $this->key; // step 2 + $temp .= $text; // step 3 + $temp = self::_sha512($temp, $this->initial); // step 4 + $output = $this->opad ^ $this->key; // step 5 + $output.= $temp; // step 6 + $output = self::_sha512($output, $this->initial); // step 7 - // Step 4. Process Message in 16-Byte Blocks - for ($i = 0; $i < $length; $i+= 16) { - for ($j = 0; $j < 16; $j++) { - $x[$j + 16] = $m[$i + $j]; - $x[$j + 32] = $x[$j + 16] ^ $x[$j]; - } - $t = chr(0); - for ($j = 0; $j < 18; $j++) { - for ($k = 0; $k < 48; $k++) { - $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); - //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]); - } - $t = chr(ord($t) + $j); - } + return substr($output, 0, $this->length); } + $output = !empty($this->key) || is_string($this->key) ? + hash_hmac($this->hash, $text, $this->key, true) : + hash($this->hash, $text, true); - // Step 5. Output - return substr($x, 0, 16); + return strlen($output) > $this->length + ? substr($output, 0, $this->length) + : $output; } /** - * Pure-PHP implementation of SHA256 - * - * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. + * Returns the hash length (in bytes) * - * @access private - * @param String $m + * @access public + * @return int */ - function _sha256($m) + function getLength() { - if (extension_loaded('suhosin')) { - return pack('H*', sha256($m)); - } - - // Initialize variables - $hash = array( - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 - ); - // Initialize table of round constants - // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) - static $k = array( - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - ); - - // Pre-processing - $length = strlen($m); - // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64 - $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); - $m[$length] = chr(0x80); - // we don't support hashing strings 512MB long - $m.= pack('N2', 0, $length << 3); - - // Process the message in successive 512-bit chunks - $chunks = str_split($m, 64); - foreach ($chunks as $chunk) { - $w = array(); - for ($i = 0; $i < 16; $i++) { - extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); - $w[] = $temp; - } - - // Extend the sixteen 32-bit words into sixty-four 32-bit words - for ($i = 16; $i < 64; $i++) { - $s0 = $this->_rightRotate($w[$i - 15], 7) ^ - $this->_rightRotate($w[$i - 15], 18) ^ - $this->_rightShift( $w[$i - 15], 3); - $s1 = $this->_rightRotate($w[$i - 2], 17) ^ - $this->_rightRotate($w[$i - 2], 19) ^ - $this->_rightShift( $w[$i - 2], 10); - $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); - - } - - // Initialize hash value for this chunk - list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; - - // Main loop - for ($i = 0; $i < 64; $i++) { - $s0 = $this->_rightRotate($a, 2) ^ - $this->_rightRotate($a, 13) ^ - $this->_rightRotate($a, 22); - $maj = ($a & $b) ^ - ($a & $c) ^ - ($b & $c); - $t2 = $this->_add($s0, $maj); - - $s1 = $this->_rightRotate($e, 6) ^ - $this->_rightRotate($e, 11) ^ - $this->_rightRotate($e, 25); - $ch = ($e & $f) ^ - ($this->_not($e) & $g); - $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); - - $h = $g; - $g = $f; - $f = $e; - $e = $this->_add($d, $t1); - $d = $c; - $c = $b; - $b = $a; - $a = $this->_add($t1, $t2); - } - - // Add this chunk's hash to result so far - $hash = array( - $this->_add($hash[0], $a), - $this->_add($hash[1], $b), - $this->_add($hash[2], $c), - $this->_add($hash[3], $d), - $this->_add($hash[4], $e), - $this->_add($hash[5], $f), - $this->_add($hash[6], $g), - $this->_add($hash[7], $h) - ); - } - - // Produce the final hash value (big-endian) - return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); + return $this->length; } /** - * Pure-PHP implementation of SHA384 and SHA512 + * Pure-PHP implementation of SHA512 * * @access private - * @param String $m + * @param string $m */ - function _sha512($m) + static function _sha512($m, $hash) { - - static $init384, $init512, $k; + static $k; if (!isset($k)) { - // Initialize variables - $init384 = array( // initial values for SHA384 - 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', - '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' - ); - $init512 = array( // initial values for SHA512 - '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( @@ -635,8 +314,6 @@ function _sha512($m) } } - $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 @@ -650,7 +327,7 @@ function _sha512($m) foreach ($chunks as $chunk) { $w = array(); for ($i = 0; $i < 16; $i++) { - $temp = new BigInteger($this->_string_shift($chunk, 8), 256); + $temp = new BigInteger(self::_string_shift($chunk, 8), 256); $temp->setPrecision(64); $w[] = $temp; } @@ -671,21 +348,21 @@ function _sha512($m) ); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); - $w[$i] = $w[$i - 16]->copy(); + $w[$i] = clone $w[$i - 16]; $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(); + $a = clone $hash[0]; + $b = clone $hash[1]; + $c = clone $hash[2]; + $d = clone $hash[3]; + $e = clone $hash[4]; + $f = clone $hash[5]; + $g = clone $hash[6]; + $h = clone $hash[7]; // Main loop for ($i = 0; $i < 80; $i++) { @@ -722,13 +399,13 @@ function _sha512($m) $t1 = $t1->add($k[$i]); $t1 = $t1->add($w[$i]); - $h = $g->copy(); - $g = $f->copy(); - $f = $e->copy(); + $h = clone $g; + $g = clone $f; + $f = clone $e; $e = $d->add($t1); - $d = $c->copy(); - $c = $b->copy(); - $b = $a->copy(); + $d = clone $c; + $c = clone $b; + $b = clone $a; $a = $t1->add($t2); } @@ -746,98 +423,24 @@ function _sha512($m) } // Produce the final hash value (big-endian) - // (Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) + // (\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(); - } + $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes(); return $temp; } - /** - * Right Rotate - * - * @access private - * @param Integer $int - * @param Integer $amt - * @see _sha256() - * @return Integer - */ - function _rightRotate($int, $amt) - { - $invamt = 32 - $amt; - $mask = (1 << $invamt) - 1; - return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); - } - - /** - * Right Shift - * - * @access private - * @param Integer $int - * @param Integer $amt - * @see _sha256() - * @return Integer - */ - function _rightShift($int, $amt) - { - $mask = (1 << (32 - $amt)) - 1; - return ($int >> $amt) & $mask; - } - - /** - * Not - * - * @access private - * @param Integer $int - * @see _sha256() - * @return Integer - */ - function _not($int) - { - return ~$int & 0xFFFFFFFF; - } - - /** - * Add - * - * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the - * possibility of overflow exists, care has to be taken. BigInteger() could be used but this should be faster. - * - * @param Integer $... - * @return Integer - * @see _sha256() - * @access private - */ - function _add() - { - static $mod; - if (!isset($mod)) { - $mod = pow(2, 32); - } - - $result = 0; - $arguments = func_get_args(); - foreach ($arguments as $argument) { - $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; - } - - return fmod($result, $mod); - } - /** * String Shift * * Inspired by array_shift * - * @param String $string - * @param optional Integer $index - * @return String + * @param string $string + * @param int $index + * @return string * @access private */ - function _string_shift(&$string, $index = 1) + static function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); diff --git a/src/phpseclib/Crypt/RC2.php b/src/phpseclib/Crypt/RC2.php new file mode 100755 index 00000000..648cf96a --- /dev/null +++ b/src/phpseclib/Crypt/RC2.php @@ -0,0 +1,704 @@ + + * setKey('abcdefgh'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $rc2->decrypt($rc2->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package RC2 + * @author Patrick Monnerat + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of RC2. + * + * @package RC2 + * @access public + */ +class RC2 extends Base +{ + /** + * Block Length of the cipher + * + * @see \phpseclib\Crypt\Base::block_size + * @var int + * @access private + */ + var $block_size = 8; + + /** + * The Key + * + * @see \phpseclib\Crypt\Base::key + * @see self::setKey() + * @var string + * @access private + */ + var $key; + + /** + * The Original (unpadded) Key + * + * @see \phpseclib\Crypt\Base::key + * @see self::setKey() + * @see self::encrypt() + * @see self::decrypt() + * @var string + * @access private + */ + var $orig_key; + + /** + * Don't truncate / null pad key + * + * @see \phpseclib\Crypt\Base::_clearBuffers() + * @var bool + * @access private + */ + var $skip_key_adjustment = true; + + /** + * Key Length (in bytes) + * + * @see \phpseclib\Crypt\RC2::setKeyLength() + * @var int + * @access private + */ + var $key_length = 16; // = 128 bits + + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'rc2'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 500; + + /** + * The key length in bits. + * + * @see self::setKeyLength() + * @see self::setKey() + * @var int + * @access private + * @internal Should be in range [1..1024]. + * @internal Changing this value after setting the key has no effect. + */ + var $default_key_length = 1024; + + /** + * The key length in bits. + * + * @see self::isValidEnine() + * @see self::setKey() + * @var int + * @access private + * @internal Should be in range [1..1024]. + */ + var $current_key_length; + + /** + * The Key Schedule + * + * @see self::_setupKey() + * @var array + * @access private + */ + var $keys; + + /** + * Key expansion randomization table. + * Twice the same 256-value sequence to save a modulus in key expansion. + * + * @see self::setKey() + * @var array + * @access private + */ + var $pitable = array( + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD, + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD + ); + + /** + * Inverse key expansion randomization table. + * + * @see self::setKey() + * @var array + * @access private + */ + var $invpitable = array( + 0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66, + 0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4, + 0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20, + 0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53, + 0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68, + 0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B, + 0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12, + 0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB, + 0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3, + 0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26, + 0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67, + 0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB, + 0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC, + 0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60, + 0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7, + 0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD, + 0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24, + 0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31, + 0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE, + 0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99, + 0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C, + 0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA, + 0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35, + 0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61, + 0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72, + 0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3, + 0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F, + 0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9, + 0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77, + 0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75, + 0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87, + 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 + ); + + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + switch ($engine) { + case self::ENGINE_OPENSSL: + if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) { + return false; + } + $this->cipher_name_openssl_ecb = 'rc2-ecb'; + $this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode(); + } + + return parent::isValidEngine($engine); + } + + /** + * Sets the key length. + * + * Valid key lengths are 8 to 1024. + * Calling this function after setting the key has no effect until the next + * \phpseclib\Crypt\RC2::setKey() call. + * + * @access public + * @param int $length in bits + * @throws \LengthException if the key length isn't supported + */ + function setKeyLength($length) + { + if ($length < 8 || $length > 1024) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); + } + + $this->default_key_length = $this->current_key_length = $length; + } + + /** + * Returns the current key length + * + * @access public + * @return int + */ + function getKeyLength() + { + return $this->current_key_length; + } + + /** + * Sets the key. + * + * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg. + * strlen($key) <= 128), however, we only use the first 128 bytes if $key + * has more then 128 bytes in it, and set $key to a single null byte if + * it is empty. + * + * If the key is not explicitly set, it'll be assumed to be a single + * null byte. + * + * @see \phpseclib\Crypt\Base::setKey() + * @access public + * @param string $key + * @param int $t1 optional Effective key length in bits. + * @throws \LengthException if the key length isn't supported + */ + function setKey($key, $t1 = false) + { + $this->orig_key = $key; + + if ($t1 === false) { + $t1 = $this->default_key_length; + } + + if ($t1 < 1 || $t1 > 1024) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); + } + + $this->current_key_length = $t1; + // Key byte count should be 1..128. + $key = strlen($key) ? substr($key, 0, 128) : "\x00"; + $t = strlen($key); + + // The mcrypt RC2 implementation only supports effective key length + // of 1024 bits. It is however possible to handle effective key + // lengths in range 1..1024 by expanding the key and applying + // inverse pitable mapping to the first byte before submitting it + // to mcrypt. + + // Key expansion. + $l = array_values(unpack('C*', $key)); + $t8 = ($t1 + 7) >> 3; + $tm = 0xFF >> (8 * $t8 - $t1); + + // Expand key. + $pitable = $this->pitable; + for ($i = $t; $i < 128; $i++) { + $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; + } + $i = 128 - $t8; + $l[$i] = $pitable[$l[$i] & $tm]; + while ($i--) { + $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; + } + + // Prepare the key for mcrypt. + $l[0] = $this->invpitable[$l[0]]; + array_unshift($l, 'C*'); + + parent::setKey(call_user_func_array('pack', $l)); + } + + /** + * Encrypts a message. + * + * Mostly a wrapper for \phpseclib\Crypt\Base::encrypt, with some additional OpenSSL handling code + * + * @see self::decrypt() + * @access public + * @param string $plaintext + * @return string $ciphertext + */ + function encrypt($plaintext) + { + if ($this->engine == self::ENGINE_OPENSSL) { + $temp = $this->key; + $this->key = $this->orig_key; + $result = parent::encrypt($plaintext); + $this->key = $temp; + return $result; + } + + return parent::encrypt($plaintext); + } + + /** + * Decrypts a message. + * + * Mostly a wrapper for \phpseclib\Crypt\Base::decrypt, with some additional OpenSSL handling code + * + * @see self::encrypt() + * @access public + * @param string $ciphertext + * @return string $plaintext + */ + function decrypt($ciphertext) + { + if ($this->engine == self::ENGINE_OPENSSL) { + $temp = $this->key; + $this->key = $this->orig_key; + $result = parent::decrypt($ciphertext); + $this->key = $temp; + return $result; + } + + return parent::decrypt($ciphertext); + } + + /** + * Encrypts a block + * + * @see \phpseclib\Crypt\Base::_encryptBlock() + * @see \phpseclib\Crypt\Base::encrypt() + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); + $keys = $this->keys; + $limit = 20; + $actions = array($limit => 44, 44 => 64); + $j = 0; + + for (;;) { + // Mixing round. + $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16; + + if ($j === $limit) { + if ($limit === 64) { + break; + } + + // Mashing round. + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F]; + $limit = $actions[$limit]; + } + } + + return pack('vvvv', $r0, $r1, $r2, $r3); + } + + /** + * Decrypts a block + * + * @see \phpseclib\Crypt\Base::_decryptBlock() + * @see \phpseclib\Crypt\Base::decrypt() + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); + $keys = $this->keys; + $limit = 44; + $actions = array($limit => 20, 20 => 0); + $j = 64; + + for (;;) { + // R-mixing round. + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF; + + if ($j === $limit) { + if ($limit === 0) { + break; + } + + // R-mashing round. + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF; + $limit = $actions[$limit]; + } + } + + return pack('vvvv', $r0, $r1, $r2, $r3); + } + + /** + * Setup the \phpseclib\Crypt\Base::ENGINE_MCRYPT $engine + * + * @see \phpseclib\Crypt\Base::_setupMcrypt() + * @access private + */ + function _setupMcrypt() + { + if (!isset($this->key)) { + $this->setKey(''); + } + + parent::_setupMcrypt(); + } + + /** + * Creates the key schedule + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (!isset($this->key)) { + $this->setKey(''); + } + + // Key has already been expanded in \phpseclib\Crypt\RC2::setKey(): + // Only the first value must be altered. + $l = unpack('Ca/Cb/v*', $this->key); + array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8)); + unset($l['a']); + unset($l['b']); + $this->keys = $l; + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // The first 10 generated $lambda_functions will use the $keys hardcoded as integers + // for the mixing rounds, for better inline crypt performance [~20% faster]. + // But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10. + // (Currently, for Crypt_RC2, one generated $lambda_function cost on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit) + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a uniqe hash for our generated code + $code_hash = "Crypt_RC2, {$this->mode}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + // Is there a re-usable $lambda_functions in there? + // If not, we have to create it. + if (!isset($lambda_functions[$code_hash])) { + // Init code for both, encrypt and decrypt. + $init_crypt = '$keys = $self->keys;'; + + switch (true) { + case $gen_hi_opt_code: + $keys = $this->keys; + default: + $keys = array(); + foreach ($this->keys as $k => $v) { + $keys[$k] = '$keys[' . $k . ']'; + } + } + + // $in is the current 8 bytes block which has to be en/decrypt + $encrypt_block = $decrypt_block = ' + $in = unpack("v4", $in); + $r0 = $in[1]; + $r1 = $in[2]; + $r2 = $in[3]; + $r3 = $in[4]; + '; + + // Create code for encryption. + $limit = 20; + $actions = array($limit => 44, 44 => 64); + $j = 0; + + for (;;) { + // Mixing round. + $encrypt_block .= ' + $r0 = (($r0 + ' . $keys[$j++] . ' + + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; + $r0 |= $r0 >> 16; + $r1 = (($r1 + ' . $keys[$j++] . ' + + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; + $r1 |= $r1 >> 16; + $r2 = (($r2 + ' . $keys[$j++] . ' + + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; + $r2 |= $r2 >> 16; + $r3 = (($r3 + ' . $keys[$j++] . ' + + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; + $r3 |= $r3 >> 16;'; + + if ($j === $limit) { + if ($limit === 64) { + break; + } + + // Mashing round. + $encrypt_block .= ' + $r0 += $keys[$r3 & 0x3F]; + $r1 += $keys[$r0 & 0x3F]; + $r2 += $keys[$r1 & 0x3F]; + $r3 += $keys[$r2 & 0x3F];'; + $limit = $actions[$limit]; + } + } + + $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + + // Create code for decryption. + $limit = 44; + $actions = array($limit => 20, 20 => 0); + $j = 64; + + for (;;) { + // R-mixing round. + $decrypt_block .= ' + $r3 = ($r3 | ($r3 << 16)) >> 5; + $r3 = ($r3 - ' . $keys[--$j] . ' - + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; + $r2 = ($r2 | ($r2 << 16)) >> 3; + $r2 = ($r2 - ' . $keys[--$j] . ' - + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; + $r1 = ($r1 | ($r1 << 16)) >> 2; + $r1 = ($r1 - ' . $keys[--$j] . ' - + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; + $r0 = ($r0 | ($r0 << 16)) >> 1; + $r0 = ($r0 - ' . $keys[--$j] . ' - + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; + + if ($j === $limit) { + if ($limit === 0) { + break; + } + + // R-mashing round. + $decrypt_block .= ' + $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; + $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; + $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; + $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; + $limit = $actions[$limit]; + } + } + + $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; + + // Creates the inline-crypt function + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + + // Set the inline-crypt function as callback in: $this->inline_crypt + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/src/phpseclib/Crypt/RC4.php b/src/phpseclib/Crypt/RC4.php old mode 100644 new mode 100755 index dadb2d48..3da70b6e --- a/src/phpseclib/Crypt/RC4.php +++ b/src/phpseclib/Crypt/RC4.php @@ -5,7 +5,7 @@ * * Uses mcrypt, if available, and an internal implementation, otherwise. * - * PHP versions 4 and 5 + * PHP version 5 * * Useful resources are as follows: * @@ -18,9 +18,9 @@ * Here's a short example of how to use this library: * * setKey('abcdefgh'); * @@ -34,102 +34,59 @@ * ?> * * - * LICENSE: 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. - * * @category Crypt * @package RC4 * @author Jim Wigginton - * @copyright MMVII Jim Wigginton + * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access private - * @see RC4::RC4() - */ -/** - * Toggles the internal implementation - */ -@define('CRYPT_RC4_MODE_INTERNAL', CRYPT_MODE_INTERNAL); -/** - * Toggles the mcrypt implementation - */ -@define('CRYPT_RC4_MODE_MCRYPT', CRYPT_MODE_MCRYPT); -/**#@-*/ - -/**#@+ - * @access private - * @see RC4::_crypt() - */ -@define('CRYPT_RC4_ENCRYPT', 0); -@define('CRYPT_RC4_DECRYPT', 1); -/**#@-*/ - /** * Pure-PHP implementation of RC4. * * @package RC4 * @author Jim Wigginton - * @version 0.1.0 * @access public */ class RC4 extends Base { + /**#@+ + * @access private + * @see \phpseclib\Crypt\RC4::_crypt() + */ + const ENCRYPT = 0; + const DECRYPT = 1; + /**#@-*/ + /** * Block Length of the cipher * * RC4 is a stream cipher * so we the block_size to 0 * - * @see Base::block_size - * @var Integer + * @see \phpseclib\Crypt\Base::block_size + * @var int * @access private */ var $block_size = 0; /** - * The default password key_size used by setPassword() + * Key Length (in bytes) * - * @see Base::password_key_size - * @see Base::setPassword() - * @var Integer + * @see \phpseclib\Crypt\RC4::setKeyLength() + * @var int * @access private */ - var $password_key_size = 128; // = 1024 bits - - /** - * The namespace used by the cipher for its constants. - * - * @see Base::const_namespace - * @var String - * @access private - */ - var $const_namespace = 'RC4'; + var $key_length = 128; // = 1024 bits /** * The mcrypt specific name of the cipher * - * @see Base::cipher_name_mcrypt - * @var String + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string * @access private */ var $cipher_name_mcrypt = 'arcfour'; @@ -137,7 +94,7 @@ class RC4 extends Base /** * Holds whether performance-optimized $inline_crypt() can/should be used. * - * @see Base::inline_crypt + * @see \phpseclib\Crypt\Base::inline_crypt * @var mixed * @access private */ @@ -146,8 +103,8 @@ class RC4 extends Base /** * The Key * - * @see RC4::setKey() - * @var String + * @see self::setKey() + * @var string * @access private */ var $key = "\0"; @@ -155,8 +112,8 @@ class RC4 extends Base /** * The Key Stream for decryption and encryption * - * @see RC4::setKey() - * @var Array + * @see self::setKey() + * @var array * @access private */ var $stream; @@ -164,107 +121,167 @@ class RC4 extends Base /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. - * - * @see Base::Base() - * @return RC4 + * @see \phpseclib\Crypt\Base::__construct() + * @return \phpseclib\Crypt\RC4 * @access public */ - function RC4() + function __construct() { - parent::Base(CRYPT_MODE_STREAM); + parent::__construct(Base::MODE_STREAM); } /** - * Dummy function. + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() * - * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1]. - * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before - * calling setKey(). + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + switch ($engine) { + case Base::ENGINE_OPENSSL: + switch (strlen($this->key)) { + case 5: + $this->cipher_name_openssl = 'rc4-40'; + break; + case 8: + $this->cipher_name_openssl = 'rc4-64'; + break; + case 16: + $this->cipher_name_openssl = 'rc4'; + break; + default: + return false; + } + } + + return parent::isValidEngine($engine); + } + + /** + * RC4 does not use an IV * - * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol, - * the IV's are relatively easy to predict, an attack described by - * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir} - * can be used to quickly guess at the rest of the key. The following links elaborate: + * @access public + * @return bool + */ + function usesIV() + { + return false; + } + + /** + * Sets the key length * - * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009} - * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack} + * Keys can be between 1 and 256 bytes long. * - * @param String $iv - * @see RC4::setKey() * @access public + * @param int $length + * @throws \LengthException if the key length is invalid */ - function setIV($iv) + function setKeyLength($length) { + if ($length < 8 || $length > 2048) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); + } + + $this->key_length = $length >> 3; + + parent::setKeyLength($length); } /** - * Sets the key. + * Sets the key length * - * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will - * be used. If no key is explicitly set, it'll be assumed to be a single null byte. + * Keys can be between 1 and 256 bytes long. * * @access public - * @see Base::setKey() - * @param String $key + * @param int $length + * @throws \LengthException if the key length is invalid */ function setKey($key) { - parent::setKey(substr($key, 0, 256)); + $length = strlen($key); + if ($length < 1 || $length > 256) { + throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); + } + + parent::setKey($key); } /** * Encrypts a message. * - * @see Base::decrypt() - * @see RC4::_crypt() + * @see \phpseclib\Crypt\Base::decrypt() + * @see self::_crypt() * @access public - * @param String $plaintext - * @return String $ciphertext + * @param string $plaintext + * @return string $ciphertext */ function encrypt($plaintext) { - if ($this->engine == CRYPT_MODE_MCRYPT) { + if ($this->engine != Base::ENGINE_INTERNAL) { return parent::encrypt($plaintext); } - return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT); + return $this->_crypt($plaintext, self::ENCRYPT); } /** * Decrypts a message. * * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). - * Atleast if the continuous buffer is disabled. + * At least if the continuous buffer is disabled. * - * @see Base::encrypt() - * @see RC4::_crypt() + * @see \phpseclib\Crypt\Base::encrypt() + * @see self::_crypt() * @access public - * @param String $ciphertext - * @return String $plaintext + * @param string $ciphertext + * @return string $plaintext */ function decrypt($ciphertext) { - if ($this->engine == CRYPT_MODE_MCRYPT) { + if ($this->engine != Base::ENGINE_INTERNAL) { return parent::decrypt($ciphertext); } - return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT); + return $this->_crypt($ciphertext, self::DECRYPT); } + /** + * Encrypts a block + * + * @access private + * @param string $in + */ + function _encryptBlock($in) + { + // RC4 does not utilize this method + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + */ + function _decryptBlock($in) + { + // RC4 does not utilize this method + } /** * Setup the key (expansion) * - * @see Base::_setupKey() + * @see \phpseclib\Crypt\Base::_setupKey() * @access private */ function _setupKey() { $key = $this->key; $keyLength = strlen($key); - $keyStream = array(); - for ($i = 0; $i < 256; $i++) { - $keyStream[$i] = $i; - } + $keyStream = range(0, 255); $j = 0; for ($i = 0; $i < 256; $i++) { $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; @@ -274,7 +291,7 @@ function _setupKey() } $this->stream = array(); - $this->stream[CRYPT_RC4_DECRYPT] = $this->stream[CRYPT_RC4_ENCRYPT] = array( + $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array( 0, // index $i 0, // index $j $keyStream @@ -284,12 +301,12 @@ function _setupKey() /** * Encrypts or decrypts a message. * - * @see RC4::encrypt() - * @see RC4::decrypt() + * @see self::encrypt() + * @see self::decrypt() * @access private - * @param String $text - * @param Integer $mode - * @return String $text + * @param string $text + * @param int $mode + * @return string $text */ function _crypt($text, $mode) { @@ -318,7 +335,7 @@ function _crypt($text, $mode) $keyStream[$i] = $ksj; $keyStream[$j] = $ksi; - $text[$k] = chr(ord($text[$k]) ^ $keyStream[($ksj + $ksi) & 255]); + $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); } return $text; diff --git a/src/phpseclib/Crypt/RSA.php b/src/phpseclib/Crypt/RSA.php old mode 100644 new mode 100755 index d99b248b..d34b6bae --- a/src/phpseclib/Crypt/RSA.php +++ b/src/phpseclib/Crypt/RSA.php @@ -3,271 +3,198 @@ /** * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA. * - * PHP versions 4 and 5 + * PHP version 5 * * Here's an example of how to encrypt and decrypt text with this library: * * createKey()); + * extract(\phpseclib\Crypt\RSA::createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $rsa->loadKey($privatekey); - * $ciphertext = $rsa->encrypt($plaintext); + * $ciphertext = $publickey->encrypt($plaintext); * - * $rsa->loadKey($publickey); - * echo $rsa->decrypt($ciphertext); + * echo $privatekey->decrypt($ciphertext); * ?> * * * Here's an example of how to create signatures and verify signatures with this library: * * createKey()); + * extract(\phpseclib\Crypt\RSA::createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $rsa->loadKey($privatekey); - * $signature = $rsa->sign($plaintext); + * $signature = $privatekey->sign($plaintext); * - * $rsa->loadKey($publickey); - * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * * - * LICENSE: 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. - * * @category Crypt * @package RSA * @author Jim Wigginton - * @copyright MMIX Jim Wigginton + * @copyright 2009 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access public - * @see RSA::encrypt() - * @see RSA::decrypt() - */ -/** - * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} - * (OAEP) for encryption / decryption. - * - * Uses sha1 by default. - * - * @see RSA::setHash() - * @see RSA::setMGFHash() - */ +use ParagonIE\ConstantTime\Base64; +use phpseclib\File\ASN1; use phpseclib\Math\BigInteger; -@define('CRYPT_RSA_ENCRYPTION_OAEP', 1); -/** - * Use PKCS#1 padding. - * - * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards - * compatability with protocols (like SSH-1) written before OAEP's introduction. - */ -@define('CRYPT_RSA_ENCRYPTION_PKCS1', 2); -/**#@-*/ - -/**#@+ - * @access public - * @see RSA::sign() - * @see RSA::verify() - * @see RSA::setHash() - */ -/** - * Use the Probabilistic Signature Scheme for signing - * - * Uses sha1 by default. - * - * @see RSA::setSaltLength() - * @see RSA::setMGFHash() - */ -@define('CRYPT_RSA_SIGNATURE_PSS', 1); -/** - * Use the PKCS#1 scheme by default. - * - * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards - * compatability with protocols (like SSH-2) written before PSS's introduction. - */ -@define('CRYPT_RSA_SIGNATURE_PKCS1', 2); -/**#@-*/ - -/**#@+ - * @access private - * @see RSA::createKey() - */ -/** - * ASN1 Integer - */ -@define('CRYPT_RSA_ASN1_INTEGER', 2); -/** - * ASN1 Bit String - */ -@define('CRYPT_RSA_ASN1_BITSTRING', 3); -/** - * ASN1 Sequence (with the constucted bit set) - */ -@define('CRYPT_RSA_ASN1_SEQUENCE', 48); -/**#@-*/ - -/**#@+ - * @access private - * @see RSA::RSA() - */ -/** - * To use the pure-PHP implementation - */ -@define('CRYPT_RSA_MODE_INTERNAL', 1); -/** - * To use the OpenSSL library - * - * (if enabled; otherwise, the internal implementation will be used) - */ -@define('CRYPT_RSA_MODE_OPENSSL', 2); -/**#@-*/ - -/** - * Default openSSL configuration file. - */ -@define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf'); - -/**#@+ - * @access public - * @see RSA::createKey() - * @see RSA::setPrivateKeyFormat() - */ -/** - * PKCS#1 formatted private key - * - * Used by OpenSSH - */ -@define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0); -/** - * PuTTY formatted private key - */ -@define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1); -/** - * XML formatted private key - */ -@define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2); -/**#@-*/ - -/**#@+ - * @access public - * @see RSA::createKey() - * @see RSA::setPublicKeyFormat() - */ -/** - * Raw public key - * - * An array containing two BigInteger objects. - * - * The exponent can be indexed with any of the following: - * - * 0, e, exponent, publicExponent - * - * The modulus can be indexed with any of the following: - * - * 1, n, modulo, modulus - */ -@define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3); -/** - * PKCS#1 formatted public key (raw) - * - * Used by File/X509.php - */ -@define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4); -/** - * XML formatted public key - */ -@define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5); -/** - * OpenSSH formatted public key - * - * Place in $HOME/.ssh/authorized_keys - */ -@define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6); -/** - * PKCS#1 formatted public key (encapsulated) - * - * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) - */ -@define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 7); -/**#@-*/ - /** * Pure-PHP PKCS#1 compliant implementation of RSA. * * @package RSA * @author Jim Wigginton - * @version 0.1.0 * @access public */ class RSA { + /**#@+ + * @access public + * @see self::encrypt() + * @see self::decrypt() + */ + /** + * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} + * (OAEP) for encryption / decryption. + * + * Uses sha256 by default + * + * @see self::setHash() + * @see self::setMGFHash() + */ + const PADDING_OAEP = 1; + /** + * Use PKCS#1 padding. + * + * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards + * compatibility with protocols (like SSH-1) written before OAEP's introduction. + */ + const PADDING_PKCS1 = 2; + /** + * Do not use any padding + * + * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy + * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. + */ + const PADDING_NONE = 3; + /** + * Use PKCS#1 padding with PKCS1 v1.5 compatability + * + * A PKCS1 v2.1 encrypted message may not successfully decrypt with a PKCS1 v1.5 implementation (such as OpenSSL). + */ + const PADDING_PKCS15_COMPAT = 6; + /**#@-*/ + + /**#@+ + * @access public + * @see self::sign() + * @see self::verify() + * @see self::setHash() + */ + /** + * Use the Probabilistic Signature Scheme for signing + * + * Uses sha256 and 0 as the salt length + * + * @see self::setSaltLength() + * @see self::setMGFHash() + * @see self::setHash() + */ + const PADDING_PSS = 4; + /** + * Use a relaxed version of PKCS#1 padding for signature verification + */ + const PADDING_RELAXED_PKCS1 = 5; + /**#@-*/ + + /**#@+ + * @access private + * @see self::createKey() + */ + /** + * ASN1 Integer + */ + const ASN1_INTEGER = 2; + /** + * ASN1 Bit String + */ + const ASN1_BITSTRING = 3; + /** + * ASN1 Octet String + */ + const ASN1_OCTETSTRING = 4; + /** + * ASN1 Object Identifier + */ + const ASN1_OBJECT = 6; + /** + * ASN1 Sequence (with the constucted bit set) + */ + const ASN1_SEQUENCE = 48; + /**#@-*/ + + /**#@+ + * @access private + * @see self::__construct() + */ + /** + * To use the pure-PHP implementation + */ + const MODE_INTERNAL = 1; + /** + * To use the OpenSSL library + * + * (if enabled; otherwise, the internal implementation will be used) + */ + const MODE_OPENSSL = 2; + /**#@-*/ + /** * Precomputed Zero * - * @var Array + * @var array * @access private */ - var $zero; + static $zero; /** * Precomputed One * - * @var Array + * @var array * @access private */ - var $one; + static $one; /** * Private Key Format * - * @var Integer + * @var string * @access private */ - var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1; + var $privateKeyFormat = 'PKCS1'; /** * Public Key Format * - * @var Integer - * @access public + * @var string + * @access private */ - var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1; + var $publicKeyFormat = 'PKCS8'; /** * Modulus (ie. n) * - * @var BigInteger + * @var \phpseclib\Math\BigInteger * @access private */ var $modulus; @@ -275,7 +202,7 @@ class RSA /** * Modulus length * - * @var BigInteger + * @var \phpseclib\Math\BigInteger * @access private */ var $k; @@ -283,7 +210,7 @@ class RSA /** * Exponent (ie. e or d) * - * @var BigInteger + * @var \phpseclib\Math\BigInteger * @access private */ var $exponent; @@ -291,7 +218,7 @@ class RSA /** * Primes for Chinese Remainder Theorem (ie. p and q) * - * @var Array + * @var array * @access private */ var $primes; @@ -299,7 +226,7 @@ class RSA /** * Exponents for Chinese Remainder Theorem (ie. dP and dQ) * - * @var Array + * @var array * @access private */ var $exponents; @@ -307,7 +234,7 @@ class RSA /** * Coefficients for Chinese Remainder Theorem (ie. qInv) * - * @var Array + * @var array * @access private */ var $coefficients; @@ -315,7 +242,7 @@ class RSA /** * Hash name * - * @var String + * @var string * @access private */ var $hashName; @@ -323,7 +250,7 @@ class RSA /** * Hash function * - * @var Hash + * @var \phpseclib\Crypt\Hash * @access private */ var $hash; @@ -331,7 +258,7 @@ class RSA /** * Length of hash function output * - * @var Integer + * @var int * @access private */ var $hLen; @@ -339,7 +266,7 @@ class RSA /** * Length of salt * - * @var Integer + * @var int * @access private */ var $sLen; @@ -347,7 +274,7 @@ class RSA /** * Hash function for the Mask Generation Function * - * @var Hash + * @var \phpseclib\Crypt\Hash * @access private */ var $mgfHash; @@ -355,149 +282,110 @@ class RSA /** * Length of MGF hash function output * - * @var Integer + * @var int * @access private */ var $mgfHLen; /** - * Encryption mode + * Public Exponent * - * @var Integer + * @var mixed * @access private */ - var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP; + var $publicExponent = false; /** - * Signature mode + * Password * - * @var Integer + * @var string * @access private */ - var $signatureMode = CRYPT_RSA_SIGNATURE_PSS; + var $password = false; /** - * Public Exponent + * Loaded File Format * - * @var Mixed + * @var string * @access private */ - var $publicExponent = false; + var $format = false; /** - * Password + * OpenSSL configuration file name. * - * @var String - * @access private + * Set to null to use system configuration file. + * + * @see self::createKey() + * @var mixed + * @access public */ - var $password = false; + static $configFile; /** - * Components - * - * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - - * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. + * Supported file formats (lower case) * - * @see RSA::_start_element_handler() - * @var Array + * @see self::_initialize_static_variables() + * @var array * @access private */ - var $components = array(); + static $fileFormats = false; /** - * Current String + * Supported file formats (original case) * - * For use with parsing XML formatted keys. - * - * @see RSA::_character_handler() - * @see RSA::_stop_element_handler() - * @var Mixed + * @see self::_initialize_static_variables() + * @var array * @access private */ - var $current; + static $origFileFormats = false; - /** - * OpenSSL configuration file name. - * - * Set to null to use system configuration file. - * @see RSA::createKey() - * @var Mixed - * @Access public - */ - var $configFile; /** - * Public key comment field. + * Initialize static variables * - * @var String * @access private */ - var $comment = 'phpseclib-generated-key'; + static function _initialize_static_variables() + { + if (!isset(self::$zero)) { + self::$zero= new BigInteger(0); + self::$one = new BigInteger(1); + self::$configFile = __DIR__ . '/../openssl.cnf'; + + if (self::$fileFormats === false) { + self::$fileFormats = array(); + foreach (glob(__DIR__ . '/RSA/*.php') as $file) { + $name = pathinfo($file, PATHINFO_FILENAME); + $type = 'phpseclib\Crypt\RSA\\' . $name; + $meta = new \ReflectionClass($type); + if (!$meta->isAbstract()) { + self::$fileFormats[strtolower($name)] = $type; + self::$origFileFormats[] = $name; + } + } + } + } + } /** * The constructor * * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason - * RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires + * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late. * - * @return RSA + * @return \phpseclib\Crypt\RSA * @access public */ function __construct() { + self::_initialize_static_variables(); - $this->configFile = CRYPT_RSA_OPENSSL_CONFIG; - - if ( !defined('CRYPT_RSA_MODE') ) { - // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, - // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger - // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. - if ( defined('MATH_BIGINTEGER_OPENSSL_DISABLE') ) { - @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); - } - - switch ( !defined('CRYPT_RSA_MODE') ) { // ie. only run this if the above didn't set CRYPT_RSA_MODE already - case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): - // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - ob_start(); - phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - - $versions = array(); - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - } - } - - // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ - switch (true) { - case !isset($versions['Header']): - case !isset($versions['Library']): - case $versions['Header'] == $versions['Library']: - @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL); - break; - default: - @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); - @define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); - } - break; - case true: - @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); - } - } - - $this->zero = new BigInteger(); - $this->one = new BigInteger(1); - - $this->hash = new Hash('sha1'); + $this->hash = new Hash('sha256'); $this->hLen = $this->hash->getLength(); - $this->hashName = 'sha1'; - $this->mgfHash = new Hash('sha1'); + $this->hashName = 'sha256'; + $this->mgfHash = new Hash('sha256'); $this->mgfHLen = $this->mgfHash->getLength(); } @@ -508,45 +396,65 @@ function __construct() * - 'privatekey': The private key. * - 'publickey': The public key. * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). - * Will need to be passed back to RSA::createKey() as the third parameter for further processing. + * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing. * * @access public - * @param optional Integer $bits - * @param optional Integer $timeout - * @param optional BigInteger $p + * @param int $bits + * @param int $timeout + * @param array $p */ - function createKey($bits = 1024, $timeout = false, $partial = array()) + static function createKey($bits = 2048, $timeout = false, $partial = array()) { + self::_initialize_static_variables(); + + if (!defined('CRYPT_RSA_MODE')) { + switch (true) { + // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, + // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger + // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. + case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + break; + case extension_loaded('openssl') && file_exists(self::$configFile): + define('CRYPT_RSA_MODE', self::MODE_OPENSSL); + break; + default: + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + } + } + if (!defined('CRYPT_RSA_EXPONENT')) { // http://en.wikipedia.org/wiki/65537_%28number%29 - @define('CRYPT_RSA_EXPONENT', '65537'); + define('CRYPT_RSA_EXPONENT', '65537'); } // per , this number ought not result in primes smaller // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if - // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then + // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key // generation when there's a chance neither gmp nor OpenSSL are installed) if (!defined('CRYPT_RSA_SMALLEST_PRIME')) { - @define('CRYPT_RSA_SMALLEST_PRIME', 4096); + define('CRYPT_RSA_SMALLEST_PRIME', 4096); } // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum - if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { + if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { $config = array(); - if (isset($this->configFile)) { - $config['config'] = $this->configFile; + if (isset(self::$configFile)) { + $config['config'] = self::$configFile; } $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); - openssl_pkey_export($rsa, $privatekey, null, $config); - $publickey = openssl_pkey_get_details($rsa); - $publickey = $publickey['key']; + openssl_pkey_export($rsa, $privatekeystr, null, $config); + $privatekey = new RSA(); + $privatekey->load($privatekeystr); - $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1))); - $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1))); + $publickeyarr = openssl_pkey_get_details($rsa); + $publickey = new RSA(); + $publickey->load($publickeyarr['key']); // clear the buffer of error strings stemming from a minimalistic openssl.cnf - while (openssl_error_string() !== false); + while (openssl_error_string() !== false) { + } return array( 'privatekey' => $privatekey, @@ -560,7 +468,7 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) $e = new BigInteger(CRYPT_RSA_EXPONENT); } - extract($this->_generateMinMax($bits)); + extract(self::_generateMinMax($bits)); $absoluteMin = $min; $temp = $bits >> 1; // divide by two to see how many bits P and Q would be if ($temp > CRYPT_RSA_SMALLEST_PRIME) { @@ -569,19 +477,17 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) } else { $num_primes = 2; } - extract($this->_generateMinMax($temp + $bits % $temp)); + extract(self::_generateMinMax($temp + $bits % $temp)); $finalMax = $max; - extract($this->_generateMinMax($temp)); + extract(self::_generateMinMax($temp)); - $generator = new BigInteger(); - - $n = $this->one->copy(); + $n = clone self::$one; if (!empty($partial)) { extract(unserialize($partial)); } else { $exponents = $coefficients = $primes = array(); $lcm = array( - 'top' => $this->one->copy(), + 'top' => clone self::$one, 'bottom' => false ); } @@ -610,12 +516,12 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) if ($i == $num_primes) { list($min, $temp) = $absoluteMin->divide($n); - if (!$temp->equals($this->zero)) { - $min = $min->add($this->one); // ie. ceil() + if (!$temp->equals(self::$zero)) { + $min = $min->add(self::$one); // ie. ceil() } - $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); + $primes[$i] = BigInteger::randomPrime($min, $finalMax, $timeout); } else { - $primes[$i] = $generator->randomPrime($min, $max, $timeout); + $primes[$i] = BigInteger::randomPrime($min, $max, $timeout); } if ($primes[$i] === false) { // if we've reached the timeout @@ -632,8 +538,8 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) } return array( - 'privatekey' => '', - 'publickey' => '', + 'privatekey' => false, + 'publickey' => false, 'partialkey' => $partialkey ); } @@ -646,7 +552,7 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) $n = $n->multiply($primes[$i]); - $temp = $primes[$i]->subtract($this->one); + $temp = $primes[$i]->subtract(self::$one); // textbook RSA implementations use Euler's totient function instead of the least common multiple. // see http://en.wikipedia.org/wiki/Euler%27s_totient_function @@ -659,7 +565,7 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) list($temp) = $lcm['top']->divide($lcm['bottom']); $gcd = $temp->gcd($e); $i0 = 1; - } while (!$gcd->equals($this->one)); + } while (!$gcd->equals(self::$one)); $d = $e->modInverse($temp); @@ -676,664 +582,64 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) // exponent1 INTEGER, -- d mod (p-1) // exponent2 INTEGER, -- d mod (q-1) // coefficient INTEGER, -- (inverse of q) mod p - // otherPrimeInfos OtherPrimeInfos OPTIONAL - // } - - return array( - 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), - 'publickey' => $this->_convertPublicKey($n, $e), - 'partialkey' => false - ); - } - - /** - * Convert a private key to the appropriate format. - * - * @access private - * @see setPrivateKeyFormat() - * @param String $RSAPrivateKey - * @return String - */ - function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) - { - $num_primes = count($primes); - $raw = array( - 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi - '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) - ); - - // if the format in question does not support multi-prime rsa and multi-prime rsa was used, - // call _convertPublicKey() instead. - switch ($this->privateKeyFormat) { - case CRYPT_RSA_PRIVATE_FORMAT_XML: - if ($num_primes != 2) { - return false; - } - return "\r\n" . - ' ' . base64_encode($raw['modulus']) . "\r\n" . - ' ' . base64_encode($raw['publicExponent']) . "\r\n" . - '

' . base64_encode($raw['prime1']) . "

\r\n" . - ' ' . base64_encode($raw['prime2']) . "\r\n" . - ' ' . base64_encode($raw['exponent1']) . "\r\n" . - ' ' . base64_encode($raw['exponent2']) . "\r\n" . - ' ' . base64_encode($raw['coefficient']) . "\r\n" . - ' ' . base64_encode($raw['privateExponent']) . "\r\n" . - '
'; - break; - case CRYPT_RSA_PRIVATE_FORMAT_PUTTY: - if ($num_primes != 2) { - return false; - } - $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; - $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; - $key.= $encryption; - $key.= "\r\nComment: " . $this->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($this->comment), $this->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($this->password) && !is_string($this->password)) { - $source.= pack('Na*', strlen($private), $private); - $hashkey = 'putty-private-key-file-mac-key'; - } else { - $private.= crypt_random_string(16 - (strlen($private) & 15)); - $source.= pack('Na*', strlen($private), $private); - $sequence = 0; - $symkey = ''; - while (strlen($symkey) < 32) { - $temp = pack('Na*', $sequence++, $this->password); - $symkey.= pack('H*', sha1($temp)); - } - $symkey = substr($symkey, 0, 32); - $crypto = new AES(); - - $crypto->setKey($symkey); - $crypto->disablePadding(); - $private = $crypto->encrypt($private); - $hashkey = 'putty-private-key-file-mac-key' . $this->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(pack('H*', sha1($hashkey))); - $key.= 'Private-MAC: ' . bin2hex($hash->_hash($source)) . "\r\n"; - - return $key; - default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1 - $components = array(); - foreach ($raw as $name => $value) { - $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); - } - - $RSAPrivateKey = implode('', $components); - - if ($num_primes > 2) { - $OtherPrimeInfos = ''; - for ($i = 3; $i <= $num_primes; $i++) { - // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - // - // OtherPrimeInfo ::= SEQUENCE { - // prime INTEGER, -- ri - // exponent INTEGER, -- di - // coefficient INTEGER -- ti - // } - $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); - $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); - } - $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); - } - - $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - if (!empty($this->password) || is_string($this->password)) { - $iv = crypt_random_string(8); - $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key - $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); - $des = new TripleDES(); - $des->setKey($symkey); - $des->setIV($iv); - $iv = strtoupper(bin2hex($iv)); - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - "Proc-Type: 4,ENCRYPTED\r\n" . - "DEK-Info: DES-EDE3-CBC,$iv\r\n" . - "\r\n" . - chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . - '-----END RSA PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END RSA PRIVATE KEY-----'; - } - - return $RSAPrivateKey; - } - } - - /** - * Convert a public key to the appropriate format - * - * @access private - * @see setPublicKeyFormat() - * @param String $RSAPrivateKey - * @return String - */ - function _convertPublicKey($n, $e) - { - $modulus = $n->toBytes(true); - $publicExponent = $e->toBytes(true); - - switch ($this->publicKeyFormat) { - case CRYPT_RSA_PUBLIC_FORMAT_RAW: - return array('e' => $e->copy(), 'n' => $n->copy()); - case CRYPT_RSA_PUBLIC_FORMAT_XML: - return "\r\n" . - ' ' . base64_encode($modulus) . "\r\n" . - ' ' . base64_encode($publicExponent) . "\r\n" . - ''; - break; - case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: - // from : - // 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) . ' ' . $this->comment; - - return $RSAPublicKey; - default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1 - // from : - // RSAPublicKey ::= SEQUENCE { - // modulus INTEGER, -- n - // publicExponent INTEGER -- e - // } - $components = array( - 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), - 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) - ); - - $RSAPublicKey = pack('Ca*a*a*', - CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), - $components['modulus'], $components['publicExponent'] - ); - - if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { - // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; - - $RSAPublicKey = pack('Ca*a*', - CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey - ); - } - - $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($RSAPublicKey), 64) . - '-----END PUBLIC KEY-----'; - - return $RSAPublicKey; - } - } - - /** - * Break a public or private key down into its constituant components - * - * @access private - * @see _convertPublicKey() - * @see _convertPrivateKey() - * @param String $key - * @param Integer $type - * @return Array - */ - function _parseKey($key, $type) - { - if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) { - return false; - } - - switch ($type) { - case CRYPT_RSA_PUBLIC_FORMAT_RAW: - if (!is_array($key)) { - return false; - } - $components = array(); - switch (true) { - case isset($key['e']): - $components['publicExponent'] = $key['e']->copy(); - break; - case isset($key['exponent']): - $components['publicExponent'] = $key['exponent']->copy(); - break; - case isset($key['publicExponent']): - $components['publicExponent'] = $key['publicExponent']->copy(); - break; - case isset($key[0]): - $components['publicExponent'] = $key[0]->copy(); - } - switch (true) { - case isset($key['n']): - $components['modulus'] = $key['n']->copy(); - break; - case isset($key['modulo']): - $components['modulus'] = $key['modulo']->copy(); - break; - case isset($key['modulus']): - $components['modulus'] = $key['modulus']->copy(); - break; - case isset($key[1]): - $components['modulus'] = $key[1]->copy(); - } - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; - case CRYPT_RSA_PRIVATE_FORMAT_PKCS1: - case CRYPT_RSA_PUBLIC_FORMAT_PKCS1: - /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is - "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to - protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding - two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: - - http://tools.ietf.org/html/rfc1421#section-4.6.1.1 - http://tools.ietf.org/html/rfc1421#section-4.6.1.3 - - DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. - DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation - function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's - own implementation. ie. the implementation *is* the standard and any bugs that may exist in that - implementation are part of the standard, as well. - - * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ - if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { - $iv = pack('H*', trim($matches[2])); - $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key - $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); - // remove the Proc-Type / DEK-Info sections as they're no longer needed - $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); - $ciphertext = $this->_extractBER($key); - if ($ciphertext === false) { - $ciphertext = $key; - } - switch ($matches[1]) { - case 'AES-256-CBC': - $crypto = new AES(); - break; - case 'AES-128-CBC': - $symkey = substr($symkey, 0, 16); - $crypto = new AES(); - break; - case 'DES-EDE3-CFB': - $crypto = new TripleDES(CRYPT_DES_MODE_CFB); - break; - case 'DES-EDE3-CBC': - $symkey = substr($symkey, 0, 24); - $crypto = new TripleDES(); - break; - case 'DES-CBC': - $crypto = new DES(); - break; - default: - return false; - } - $crypto->setKey($symkey); - $crypto->setIV($iv); - $decoded = $crypto->decrypt($ciphertext); - } else { - $decoded = $this->_extractBER($key); - } - - if ($decoded !== false) { - $key = $decoded; - } - - $components = array(); - - if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($key) != strlen($key)) { - return false; - } - - $tag = ord($this->_string_shift($key)); - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 631 cons: SEQUENCE - 4:d=1 hl=2 l= 1 prim: INTEGER :00 - 7:d=1 hl=2 l= 13 cons: SEQUENCE - 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 20:d=2 hl=2 l= 0 prim: NULL - 22:d=1 hl=4 l= 609 prim: OCTET STRING */ - - if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { - $this->_string_shift($key, 3); - $tag = CRYPT_RSA_ASN1_SEQUENCE; - } - - if ($tag == CRYPT_RSA_ASN1_SEQUENCE) { - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 290 cons: SEQUENCE - 4:d=1 hl=2 l= 13 cons: SEQUENCE - 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 17:d=2 hl=2 l= 0 prim: NULL - 19:d=1 hl=4 l= 271 prim: BIT STRING */ - $this->_string_shift($key, $this->_decodeLength($key)); - $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag - $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length - // "The initial octet shall encode, as an unsigned binary integer wtih 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 (section 8.6.2.2) - if ($tag == CRYPT_RSA_ASN1_BITSTRING) { - $this->_string_shift($key); - } - if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($key) != strlen($key)) { - return false; - } - $tag = ord($this->_string_shift($key)); - } - if ($tag != CRYPT_RSA_ASN1_INTEGER) { - return false; - } - - $length = $this->_decodeLength($key); - $temp = $this->_string_shift($key, $length); - if (strlen($temp) != 1 || ord($temp) > 2) { - $components['modulus'] = new BigInteger($temp, 256); - $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER - $length = $this->_decodeLength($key); - $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - - return $components; - } - if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) { - return false; - } - $length = $this->_decodeLength($key); - $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); - - if (!empty($key)) { - if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { - return false; - } - $this->_decodeLength($key); - while (!empty($key)) { - if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { - return false; - } - $this->_decodeLength($key); - $key = substr($key, 1); - $length = $this->_decodeLength($key); - $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); - } - } - - return $components; - case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: - $parts = explode(' ', $key, 3); - - $key = isset($parts[1]) ? base64_decode($parts[1]) : false; - if ($key === false) { - return false; - } - - $comment = isset($parts[2]) ? $parts[2] : false; - - $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; - - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $modulus = new BigInteger($this->_string_shift($key, $length), -256); - - if ($cleanup && strlen($key)) { - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $realModulus = new BigInteger($this->_string_shift($key, $length), -256); - return strlen($key) ? false : array( - 'modulus' => $realModulus, - 'publicExponent' => $modulus, - 'comment' => $comment - ); - } else { - return strlen($key) ? false : array( - 'modulus' => $modulus, - 'publicExponent' => $publicExponent, - 'comment' => $comment - ); - } - // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue - // http://en.wikipedia.org/wiki/XML_Signature - case CRYPT_RSA_PRIVATE_FORMAT_XML: - case CRYPT_RSA_PUBLIC_FORMAT_XML: - $this->components = array(); - - $xml = xml_parser_create('UTF-8'); - xml_set_object($xml, $this); - xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); - xml_set_character_data_handler($xml, '_data_handler'); - // add to account for "dangling" tags like ... that are sometimes added - if (!xml_parse($xml, '' . $key . '')) { - return false; - } - - return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; - // from PuTTY's SSHPUBK.C - case CRYPT_RSA_PRIVATE_FORMAT_PUTTY: - $components = array(); - $key = preg_split('#\r\n|\r|\n#', $key); - $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); - if ($type != 'ssh-rsa') { - return false; - } - $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); - $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); - - $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); - $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); - $public = substr($public, 11); - extract(unpack('Nlength', $this->_string_shift($public, 4))); - $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); - extract(unpack('Nlength', $this->_string_shift($public, 4))); - $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); - - $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); - $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); - - switch ($encryption) { - case 'aes256-cbc': - $symkey = ''; - $sequence = 0; - while (strlen($symkey) < 32) { - $temp = pack('Na*', $sequence++, $this->password); - $symkey.= pack('H*', sha1($temp)); - } - $symkey = substr($symkey, 0, 32); - $crypto = new AES(); - } - - if ($encryption != 'none') { - $crypto->setKey($symkey); - $crypto->disablePadding(); - $private = $crypto->decrypt($private); - if ($private === false) { - return false; - } - } - - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); - - $temp = $components['primes'][1]->subtract($this->one); - $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); - $temp = $components['primes'][2]->subtract($this->one); - $components['exponents'][] = $components['publicExponent']->modInverse($temp); - - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); - - return $components; - } - } - - /** - * Returns the key size - * - * More specifically, this returns the size of the modulo in bits. - * - * @access public - * @return Integer - */ - function getSize() - { - return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); - } + // otherPrimeInfos OtherPrimeInfos OPTIONAL + // } + $privatekey = new RSA(); + $privatekey->modulus = $n; + $privatekey->k = $bits >> 3; + $privatekey->publicExponent = $e; + $privatekey->exponent = $d; + $privatekey->privateExponent = $e; + $privatekey->primes = $primes; + $privatekey->exponents = $exponents; + $privatekey->coefficients = $coefficients; + + $publickey = new RSA(); + $publickey->modulus = $n; + $publickey->k = $bits >> 3; + $publickey->exponent = $e; - /** - * Start Element Handler - * - * Called by xml_set_element_handler() - * - * @access private - * @param Resource $parser - * @param String $name - * @param Array $attribs - */ - function _start_element_handler($parser, $name, $attribs) - { - //$name = strtoupper($name); - switch ($name) { - case 'MODULUS': - $this->current = &$this->components['modulus']; - break; - case 'EXPONENT': - $this->current = &$this->components['publicExponent']; - break; - case 'P': - $this->current = &$this->components['primes'][1]; - break; - case 'Q': - $this->current = &$this->components['primes'][2]; - break; - case 'DP': - $this->current = &$this->components['exponents'][1]; - break; - case 'DQ': - $this->current = &$this->components['exponents'][2]; - break; - case 'INVERSEQ': - $this->current = &$this->components['coefficients'][2]; - break; - case 'D': - $this->current = &$this->components['privateExponent']; - } - $this->current = ''; + return array( + 'privatekey' => $privatekey, + 'publickey' => $publickey, + 'partialkey' => false + ); } /** - * Stop Element Handler + * Add a fileformat plugin * - * Called by xml_set_element_handler() + * The plugin needs to either already be loaded or be auto-loadable. + * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin. * - * @access private - * @param Resource $parser - * @param String $name + * @see self::load() + * @param string $fullname + * @access public + * @return bool */ - function _stop_element_handler($parser, $name) + static function addFileFormat($fullname) { - if (isset($this->current)) { - $this->current = new BigInteger(base64_decode($this->current), 256); - unset($this->current); + self::_initialize_static_variables(); + + if (class_exists($fullname)) { + $meta = new \ReflectionClass($path); + $shortname = $meta->getShortName(); + self::$fileFormats[strtolower($shortname)] = $fullname; + self::$origFileFormats[] = $shortname; } } /** - * Data Handler + * Returns a list of supported formats. * - * Called by xml_set_character_data_handler() - * - * @access private - * @param Resource $parser - * @param String $data + * @access public + * @return array */ - function _data_handler($parser, $data) + static function getSupportedFormats() { - if (!isset($this->current) || is_object($this->current)) { - return; - } - $this->current.= trim($data); + self::_initialize_static_variables(); + + return self::$origFileFormats; } /** @@ -1342,23 +648,19 @@ function _data_handler($parser, $data) * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) * * @access public - * @param String $key - * @param Integer $type optional + * @param string $key + * @param int $type optional */ - function loadKey($key, $type = false) + function load($key, $type = false) { - if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') { + if ($key instanceof RSA) { $this->privateKeyFormat = $key->privateKeyFormat; $this->publicKeyFormat = $key->publicKeyFormat; $this->k = $key->k; $this->hLen = $key->hLen; $this->sLen = $key->sLen; $this->mgfHLen = $key->mgfHLen; - $this->encryptionMode = $key->encryptionMode; - $this->signatureMode = $key->signatureMode; $this->password = $key->password; - $this->configFile = $key->configFile; - $this->comment = $key->comment; if (is_object($key->hash)) { $this->hash = new Hash($key->hash->getHash()); @@ -1368,13 +670,13 @@ function loadKey($key, $type = false) } if (is_object($key->modulus)) { - $this->modulus = $key->modulus->copy(); + $this->modulus = clone $key->modulus; } if (is_object($key->exponent)) { - $this->exponent = $key->exponent->copy(); + $this->exponent = clone $key->exponent; } if (is_object($key->publicExponent)) { - $this->publicExponent = $key->publicExponent->copy(); + $this->publicExponent = clone $key->publicExponent; } $this->primes = array(); @@ -1382,44 +684,49 @@ function loadKey($key, $type = false) $this->coefficients = array(); foreach ($this->primes as $prime) { - $this->primes[] = $prime->copy(); + $this->primes[] = clone $prime; } foreach ($this->exponents as $exponent) { - $this->exponents[] = $exponent->copy(); + $this->exponents[] = clone $exponent; } foreach ($this->coefficients as $coefficient) { - $this->coefficients[] = $coefficient->copy(); + $this->coefficients[] = clone $coefficient; } return true; } + $components = false; if ($type === false) { - $types = array( - CRYPT_RSA_PUBLIC_FORMAT_RAW, - CRYPT_RSA_PRIVATE_FORMAT_PKCS1, - CRYPT_RSA_PRIVATE_FORMAT_XML, - CRYPT_RSA_PRIVATE_FORMAT_PUTTY, - CRYPT_RSA_PUBLIC_FORMAT_OPENSSH - ); - foreach ($types as $type) { - $components = $this->_parseKey($key, $type); + foreach (self::$fileFormats as $format) { + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } if ($components !== false) { break; } } - } else { - $components = $this->_parseKey($key, $type); + $format = strtolower($type); + if (isset(self::$fileFormats[$format])) { + $format = self::$fileFormats[$format]; + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } + } } if ($components === false) { + $this->format = false; return false; } - if (isset($components['comment']) && $components['comment'] !== false) { - $this->comment = $components['comment']; - } + $this->format = $format; + $this->modulus = $components['modulus']; $this->k = strlen($this->modulus->toBytes()); $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; @@ -1435,19 +742,88 @@ function loadKey($key, $type = false) $this->publicExponent = false; } + if ($components['isPublicKey']) { + $this->setPublicKey(); + } + return true; } + /** + * Returns the format of the loaded key. + * + * If the key that was loaded wasn't in a valid or if the key was auto-generated + * with RSA::createKey() then this will return false. + * + * @see self::load() + * @access public + * @return mixed + */ + function getLoadedFormat() + { + if ($this->format === false) { + return false; + } + + $meta = new \ReflectionClass($this->format); + return $meta->getShortName(); + } + + /** + * Returns the private key + * + * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * + * @see self::getPublicKey() + * @access public + * @param string $type optional + * @return mixed + */ + function getPrivateKey($type = 'PKCS1') + { + $type = strtolower($type); + if (!isset(self::$fileFormats[$type])) { + return false; + } + $type = self::$fileFormats[$type]; + if (!method_exists($type, 'savePrivateKey')) { + return false; + } + + if (empty($this->primes)) { + return false; + } + + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password); + $this->privateKeyFormat = $oldFormat; + return $temp; + } + + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @access public + * @return int + */ + function getSize() + { + return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); + } + /** * Sets the password * * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. * Or rather, pass in $password such that empty($password) && !is_string($password) is true. * - * @see createKey() - * @see loadKey() + * @see self::createKey() + * @see self::load() * @access public - * @param String $password + * @param string $password */ function setPassword($password = false) { @@ -1461,17 +837,19 @@ function setPassword($password = false) * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public - * exponent this won't work unless you manually add the public exponent. + * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used + * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being + * public. * * Do note that when a new key is loaded the index will be cleared. * * Returns true on success, false on failure * - * @see getPublicKey() + * @see self::getPublicKey() * @access public - * @param String $key optional - * @param Integer $type optional - * @return Boolean + * @param string $key optional + * @param int $type optional + * @return bool */ function setPublicKey($key = false, $type = false) { @@ -1485,27 +863,40 @@ function setPublicKey($key = false, $type = false) return true; } + $components = false; if ($type === false) { - $types = array( - CRYPT_RSA_PUBLIC_FORMAT_RAW, - CRYPT_RSA_PUBLIC_FORMAT_PKCS1, - CRYPT_RSA_PUBLIC_FORMAT_XML, - CRYPT_RSA_PUBLIC_FORMAT_OPENSSH - ); - foreach ($types as $type) { - $components = $this->_parseKey($key, $type); + foreach (self::$fileFormats as $format) { + if (!method_exists($format, 'savePublicKey')) { + continue; + } + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } if ($components !== false) { break; } } } else { - $components = $this->_parseKey($key, $type); + $format = strtolower($type); + if (isset(self::$fileFormats[$format])) { + $format = self::$fileFormats[$format]; + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } + } } if ($components === false) { + $this->format = false; return false; } + $this->format = $format; + if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { $this->modulus = $components['modulus']; $this->exponent = $this->publicExponent = $components['publicExponent']; @@ -1517,6 +908,40 @@ function setPublicKey($key = false, $type = false) return true; } + /** + * Defines the private key + * + * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force + * phpseclib to treat the key as a private key. This function will do that. + * + * Do note that when a new key is loaded the index will be cleared. + * + * Returns true on success, false on failure + * + * @see self::getPublicKey() + * @access public + * @param string $key optional + * @param int $type optional + * @return bool + */ + function setPrivateKey($key = false, $type = false) + { + if ($key === false && !empty($this->publicExponent)) { + $this->publicExponent = false; + return true; + } + + $rsa = new RSA(); + if (!$rsa->load($key, $type)) { + return false; + } + $rsa->publicExponent = false; + + // don't overwrite the old key if the new key is invalid + $this->load($rsa); + return true; + } + /** * Returns the public key * @@ -1524,45 +949,66 @@ function setPublicKey($key = false, $type = false) * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. * - * @see getPublicKey() + * @see self::getPrivateKey() * @access public - * @param String $key - * @param Integer $type optional + * @param string $type optional + * @return mixed */ - function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) + function getPublicKey($type = 'PKCS8') { + $type = strtolower($type); + if (!isset(self::$fileFormats[$type])) { + return false; + } + $type = self::$fileFormats[$type]; + if (!method_exists($type, 'savePublicKey')) { + return false; + } + if (empty($this->modulus) || empty($this->publicExponent)) { return false; } $oldFormat = $this->publicKeyFormat; $this->publicKeyFormat = $type; - $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); + $temp = $type::savePublicKey($this->modulus, $this->publicExponent); $this->publicKeyFormat = $oldFormat; return $temp; } /** - * Returns the private key + * Returns the public key's fingerprint * - * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is + * no public key currently loaded, false is returned. + * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716) * - * @see getPublicKey() * @access public - * @param String $key - * @param Integer $type optional + * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned + * for invalid values. + * @return mixed */ - function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) + function getPublicKeyFingerprint($algorithm = 'md5') { - if (empty($this->primes)) { + if (empty($this->modulus) || empty($this->publicExponent)) { return false; } - $oldFormat = $this->privateKeyFormat; - $this->privateKeyFormat = $type; - $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); - $this->privateKeyFormat = $oldFormat; - return $temp; + $modulus = $this->modulus->toBytes(true); + $publicExponent = $this->publicExponent->toBytes(true); + + $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); + + switch ($algorithm) { + case 'sha256': + $hash = new Hash('sha256'); + $base = Base64::encode($hash->hash($RSAPublicKey)); + return substr($base, 0, strlen($base) - 1); + case 'md5': + return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1); + default: + return false; + } } /** @@ -1571,48 +1017,60 @@ function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) * Returns the private key without the prime number constituants. Structurally identical to a public key that * hasn't been set as the public key * - * @see getPrivateKey() + * @see self::getPrivateKey() * @access private - * @param String $key - * @param Integer $type optional + * @param string $type optional + * @return mixed */ - function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) + function _getPrivatePublicKey($type = 'PKCS8') { + $type = strtolower($type); + if (!isset(self::$fileFormats[$type])) { + return false; + } + $type = self::$fileFormats[$type]; + if (!method_exists($type, 'savePublicKey')) { + return false; + } + if (empty($this->modulus) || empty($this->exponent)) { return false; } $oldFormat = $this->publicKeyFormat; - $this->publicKeyFormat = $mode; - $temp = $this->_convertPublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $type; + $temp = $type::savePublicKey($this->modulus, $this->exponent); $this->publicKeyFormat = $oldFormat; return $temp; } + /** - * __toString() magic method + * __toString() magic method * * @access public + * @return string */ function __toString() { $key = $this->getPrivateKey($this->privateKeyFormat); - if ($key !== false) { + if (is_string($key)) { return $key; } $key = $this->_getPrivatePublicKey($this->publicKeyFormat); - return $key !== false ? $key : ''; + return is_string($key) ? $key : ''; } /** - * __clone() magic method + * __clone() magic method * * @access public + * @return \phpseclib\Crypt\RSA */ function __clone() { $key = new RSA(); - $key->loadKey($this); + $key->load($this); return $key; } @@ -1620,10 +1078,10 @@ function __clone() * Generates the smallest and largest numbers requiring $bits bits * * @access private - * @param Integer $bits - * @return Array + * @param int $bits + * @return array */ - function _generateMinMax($bits) + static function _generateMinMax($bits) { $bytes = $bits >> 3; $min = str_repeat(chr(0), $bytes); @@ -1649,13 +1107,13 @@ function _generateMinMax($bits) * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private - * @param String $string - * @return Integer + * @param string $string + * @return int */ function _decodeLength(&$string) { $length = ord($this->_string_shift($string)); - if ( $length & 0x80 ) { // definite length, long form + if ($length & 0x80) { // definite length, long form $length&= 0x7F; $temp = $this->_string_shift($string, $length); list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); @@ -1670,8 +1128,8 @@ function _decodeLength(&$string) * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private - * @param Integer $length - * @return String + * @param int $length + * @return string */ function _encodeLength($length) { @@ -1688,9 +1146,9 @@ function _encodeLength($length) * * Inspired by array_shift * - * @param String $string - * @param optional Integer $index - * @return String + * @param string $string + * @param int $index + * @return string * @access private */ function _string_shift(&$string, $index = 1) @@ -1703,9 +1161,9 @@ function _string_shift(&$string, $index = 1) /** * Determines the private key format * - * @see createKey() + * @see self::createKey() * @access public - * @param Integer $format + * @param int $format */ function setPrivateKeyFormat($format) { @@ -1715,9 +1173,9 @@ function setPrivateKeyFormat($format) /** * Determines the public key format * - * @see createKey() + * @see self::createKey() * @access public - * @param Integer $format + * @param int $format */ function setPublicKeyFormat($format) { @@ -1727,15 +1185,15 @@ function setPublicKeyFormat($format) /** * Determines which hashing function should be used * - * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and - * decryption. If $hash isn't supported, sha1 is used. + * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and + * decryption. If $hash isn't supported, sha256 is used. * * @access public - * @param String $hash + * @param string $hash */ function setHash($hash) { - // Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. switch ($hash) { case 'md2': case 'md5': @@ -1743,12 +1201,15 @@ function setHash($hash) case 'sha256': case 'sha384': case 'sha512': + case 'sha224': + case 'sha512/224': + case 'sha512/256': $this->hash = new Hash($hash); $this->hashName = $hash; break; default: - $this->hash = new Hash('sha1'); - $this->hashName = 'sha1'; + $this->hash = new Hash('sha256'); + $this->hashName = 'sha256'; } $this->hLen = $this->hash->getLength(); } @@ -1756,15 +1217,15 @@ function setHash($hash) /** * Determines which hashing function should be used for the mask generation function * - * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's + * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's * best if Hash and MGFHash are set to the same thing this is not a requirement. * * @access public - * @param String $hash + * @param string $hash */ function setMGFHash($hash) { - // Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. switch ($hash) { case 'md2': case 'md5': @@ -1772,10 +1233,13 @@ function setMGFHash($hash) case 'sha256': case 'sha384': case 'sha512': + case 'sha224': + case 'sha512/224': + case 'sha512/256': $this->mgfHash = new Hash($hash); break; default: - $this->mgfHash = new Hash('sha1'); + $this->mgfHash = new Hash('sha256'); } $this->mgfHLen = $this->mgfHash->getLength(); } @@ -1789,7 +1253,7 @@ function setMGFHash($hash) * of the hash function Hash) and 0. * * @access public - * @param Integer $format + * @param int $format */ function setSaltLength($sLen) { @@ -1802,15 +1266,17 @@ function setSaltLength($sLen) * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. * * @access private - * @param BigInteger $x - * @param Integer $xLen - * @return String + * @param bool|\phpseclib\Math\BigInteger $x + * @param int $xLen + * @return bool|string */ function _i2osp($x, $xLen) { + if ($x === false) { + return false; + } $x = $x->toBytes(); if (strlen($x) > $xLen) { - user_error('Integer too large'); return false; } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); @@ -1822,8 +1288,8 @@ function _i2osp($x, $xLen) * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * * @access private - * @param String $x - * @return BigInteger + * @param string $x + * @return \phpseclib\Math\BigInteger */ function _os2ip($x) { @@ -1836,13 +1302,19 @@ function _os2ip($x) * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}. * * @access private - * @param BigInteger $x - * @return BigInteger + * @param \phpseclib\Math\BigInteger $x + * @return \phpseclib\Math\BigInteger */ function _exponentiate($x) { - if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) { - return $x->modPow($this->exponent, $this->modulus); + switch (true) { + case empty($this->primes): + case $this->primes[1]->equals(self::$zero): + case empty($this->coefficients): + case $this->coefficients[2]->equals(self::$zero): + case empty($this->exponents): + case $this->exponents[1]->equals(self::$zero): + return $x->modPow($this->exponent, $this->modulus); } $num_primes = count($this->primes); @@ -1877,9 +1349,7 @@ function _exponentiate($x) } } - $one = new BigInteger(1); - - $r = $one->random($one, $smallest->subtract($one)); + $r = BigInteger::random(self::$one, $smallest->subtract(self::$one)); $m_i = array( 1 => $this->_blind($x, $r, 1), @@ -1914,10 +1384,10 @@ function _exponentiate($x) * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) * * @access private - * @param BigInteger $x - * @param BigInteger $r - * @param Integer $i - * @return BigInteger + * @param \phpseclib\Math\BigInteger $x + * @param \phpseclib\Math\BigInteger $r + * @param int $i + * @return \phpseclib\Math\BigInteger */ function _blind($x, $r, $i) { @@ -1941,9 +1411,9 @@ function _blind($x, $r, $i) * Thanks for the heads up singpolyma! * * @access private - * @param String $x - * @param String $y - * @return Boolean + * @param string $x + * @param string $y + * @return bool */ function _equals($x, $y) { @@ -1965,13 +1435,12 @@ function _equals($x, $y) * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. * * @access private - * @param BigInteger $m - * @return BigInteger + * @param \phpseclib\Math\BigInteger $m + * @return bool|\phpseclib\Math\BigInteger */ function _rsaep($m) { - if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($m); @@ -1983,13 +1452,12 @@ function _rsaep($m) * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. * * @access private - * @param BigInteger $c - * @return BigInteger + * @param \phpseclib\Math\BigInteger $c + * @return bool|\phpseclib\Math\BigInteger */ function _rsadp($c) { - if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { - user_error('Ciphertext representative out of range'); + if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($c); @@ -2001,13 +1469,12 @@ function _rsadp($c) * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. * * @access private - * @param BigInteger $m - * @return BigInteger + * @param \phpseclib\Math\BigInteger $m + * @return bool|\phpseclib\Math\BigInteger */ function _rsasp1($m) { - if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($m); @@ -2019,13 +1486,12 @@ function _rsasp1($m) * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}. * * @access private - * @param BigInteger $s - * @return BigInteger + * @param \phpseclib\Math\BigInteger $s + * @return bool|\phpseclib\Math\BigInteger */ function _rsavp1($s) { - if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { - user_error('Signature representative out of range'); + if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($s); @@ -2037,9 +1503,9 @@ function _rsavp1($s) * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. * * @access private - * @param String $mgfSeed - * @param Integer $mgfLen - * @return String + * @param string $mgfSeed + * @param int $mgfLen + * @return string */ function _mgf1($mgfSeed, $maskLen) { @@ -2049,7 +1515,7 @@ function _mgf1($mgfSeed, $maskLen) $count = ceil($maskLen / $this->mgfHLen); for ($i = 0; $i < $count; $i++) { $c = pack('N', $i); - $t.= $this->mgfHash->_hash($mgfSeed . $c); + $t.= $this->mgfHash->hash($mgfSeed . $c); } return substr($t, 0, $maskLen); @@ -2062,9 +1528,10 @@ function _mgf1($mgfSeed, $maskLen) * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. * * @access private - * @param String $m - * @param String $l - * @return String + * @param string $m + * @param string $l + * @throws \OutOfBoundsException if strlen($m) > $this->k - 2 * $this->hLen - 2 + * @return string */ function _rsaes_oaep_encrypt($m, $l = '') { @@ -2076,16 +1543,15 @@ function _rsaes_oaep_encrypt($m, $l = '') // be output. if ($mLen > $this->k - 2 * $this->hLen - 2) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-OAEP encoding - $lHash = $this->hash->_hash($l); + $lHash = $this->hash->hash($l); $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); $db = $lHash . $ps . chr(1) . $m; - $seed = crypt_random_string($this->hLen); + $seed = Random::string($this->hLen); $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); $maskedDB = $db ^ $dbMask; $seedMask = $this->_mgf1($maskedDB, $this->hLen); @@ -2125,9 +1591,9 @@ function _rsaes_oaep_encrypt($m, $l = '') * this document. * * @access private - * @param String $c - * @param String $l - * @return String + * @param string $c + * @param string $l + * @return bool|string */ function _rsaes_oaep_decrypt($c, $l = '') { @@ -2137,7 +1603,6 @@ function _rsaes_oaep_decrypt($c, $l = '') // be output. if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { - user_error('Decryption error'); return false; } @@ -2145,15 +1610,14 @@ function _rsaes_oaep_decrypt($c, $l = '') $c = $this->_os2ip($c); $m = $this->_rsadp($c); - if ($m === false) { - user_error('Decryption error'); + $em = $this->_i2osp($m, $this->k); + if ($em === false) { return false; } - $em = $this->_i2osp($m, $this->k); // EME-OAEP decoding - $lHash = $this->hash->_hash($l); + $lHash = $this->hash->hash($l); $y = ord($em[0]); $maskedSeed = substr($em, 1, $this->hLen); $maskedDB = substr($em, $this->hLen + 1); @@ -2164,12 +1628,10 @@ function _rsaes_oaep_decrypt($c, $l = '') $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); if ($lHash != $lHash2) { - user_error('Decryption error'); return false; } $m = ltrim($m, chr(0)); if (ord($m[0]) != 1) { - user_error('Decryption error'); return false; } @@ -2178,24 +1640,46 @@ function _rsaes_oaep_decrypt($c, $l = '') return substr($m, 1); } + /** + * Raw Encryption / Decryption + * + * Doesn't use padding and is not recommended. + * + * @access private + * @param string $m + * @return bool|string + * @throws \OutOfBoundsException if strlen($m) > $this->k + */ + function _raw_encrypt($m) + { + if (strlen($m) > $this->k) { + throw new \OutOfBoundsException('Message too long'); + } + + $temp = $this->_os2ip($m); + $temp = $this->_rsaep($temp); + return $this->_i2osp($temp, $this->k); + } + /** * RSAES-PKCS1-V1_5-ENCRYPT * * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. * * @access private - * @param String $m - * @return String + * @param string $m + * @param bool $pkcs15_compat optional + * @throws \OutOfBoundsException if strlen($m) > $this->k - 11 + * @return bool|string */ - function _rsaes_pkcs1_v1_5_encrypt($m) + function _rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false) { $mLen = strlen($m); // Length checking if ($mLen > $this->k - 11) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-PKCS1-v1_5 encoding @@ -2203,13 +1687,13 @@ function _rsaes_pkcs1_v1_5_encrypt($m) $psLen = $this->k - $mLen - 3; $ps = ''; while (strlen($ps) != $psLen) { - $temp = crypt_random_string($psLen - strlen($ps)); + $temp = Random::string($psLen - strlen($ps)); $temp = str_replace("\x00", '', $temp); $ps.= $temp; } $type = 2; // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done - if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { + if ($pkcs15_compat && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { $type = 1; // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" $ps = str_repeat("\xFF", $psLen); @@ -2231,27 +1715,26 @@ function _rsaes_pkcs1_v1_5_encrypt($m) * * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. * - * For compatability purposes, this function departs slightly from the description given in RFC3447. + * For compatibility purposes, this function departs slightly from the description given in RFC3447. * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed - * to be 2 regardless of which key is used. For compatability purposes, we'll just check to make sure the + * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the * second byte is 2 or less. If it is, we'll accept the decrypted string as valid. * - * As a consequence of this, a private key encrypted ciphertext produced with RSA may not decrypt + * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but * not private key encrypted ciphertext's. * * @access private - * @param String $c - * @return String + * @param string $c + * @return bool|string */ function _rsaes_pkcs1_v1_5_decrypt($c) { // Length checking if (strlen($c) != $this->k) { // or if k < 11 - user_error('Decryption error'); return false; } @@ -2259,17 +1742,14 @@ function _rsaes_pkcs1_v1_5_decrypt($c) $c = $this->_os2ip($c); $m = $this->_rsadp($c); - - if ($m === false) { - user_error('Decryption error'); + $em = $this->_i2osp($m, $this->k); + if ($em === false) { return false; } - $em = $this->_i2osp($m, $this->k); // EME-PKCS1-v1_5 decoding if (ord($em[0]) != 0 || ord($em[1]) > 2) { - user_error('Decryption error'); return false; } @@ -2277,7 +1757,6 @@ function _rsaes_pkcs1_v1_5_decrypt($c) $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { - user_error('Decryption error'); return false; } @@ -2292,8 +1771,9 @@ function _rsaes_pkcs1_v1_5_decrypt($c) * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. * * @access private - * @param String $m - * @param Integer $emBits + * @param string $m + * @throws \RuntimeException on encoding error + * @param int $emBits */ function _emsa_pss_encode($m, $emBits) { @@ -2301,17 +1781,16 @@ function _emsa_pss_encode($m, $emBits) // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) - $sLen = $this->sLen == false ? $this->hLen : $this->sLen; + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; - $mHash = $this->hash->_hash($m); + $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { - user_error('Encoding error'); return false; } - $salt = crypt_random_string($sLen); + $salt = Random::string($sLen); $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; - $h = $this->hash->_hash($m2); + $h = $this->hash->hash($m2); $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); $db = $ps . chr(1) . $salt; $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); @@ -2328,10 +1807,10 @@ function _emsa_pss_encode($m, $emBits) * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. * * @access private - * @param String $m - * @param String $em - * @param Integer $emBits - * @return String + * @param string $m + * @param string $em + * @param int $emBits + * @return string */ function _emsa_pss_verify($m, $em, $emBits) { @@ -2339,9 +1818,9 @@ function _emsa_pss_verify($m, $em, $emBits) // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8); - $sLen = $this->sLen == false ? $this->hLen : $this->sLen; + $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; - $mHash = $this->hash->_hash($m); + $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { return false; } @@ -2365,7 +1844,7 @@ function _emsa_pss_verify($m, $em, $emBits) } $salt = substr($db, $temp + 1); // should be $sLen long $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; - $h2 = $this->hash->_hash($m2); + $h2 = $this->hash->hash($m2); return $this->_equals($h, $h2); } @@ -2375,8 +1854,8 @@ function _emsa_pss_verify($m, $em, $emBits) * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. * * @access private - * @param String $m - * @return String + * @param string $m + * @return bool|string */ function _rsassa_pss_sign($m) { @@ -2401,16 +1880,15 @@ function _rsassa_pss_sign($m) * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. * * @access private - * @param String $m - * @param String $s - * @return String + * @param string $m + * @param string $s + * @return bool|string */ function _rsassa_pss_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); return false; } @@ -2420,13 +1898,8 @@ function _rsassa_pss_verify($m, $s) $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); - if ($m2 === false) { - user_error('Invalid signature'); - return false; - } $em = $this->_i2osp($m2, $modBits >> 3); if ($em === false) { - user_error('Invalid signature'); return false; } @@ -2441,43 +1914,50 @@ function _rsassa_pss_verify($m, $s) * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. * * @access private - * @param String $m - * @param Integer $emLen - * @return String + * @param string $m + * @param int $emLen + * @throws \LengthException if the intended encoded message length is too short + * @return string */ function _emsa_pkcs1_v1_5_encode($m, $emLen) { - $h = $this->hash->_hash($m); - if ($h === false) { - return false; - } + $h = $this->hash->hash($m); // see http://tools.ietf.org/html/rfc3447#page-43 switch ($this->hashName) { case 'md2': - $t = pack('H*', '3020300c06082a864886f70d020205000410'); + $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10"; break; case 'md5': - $t = pack('H*', '3020300c06082a864886f70d020505000410'); + $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"; break; case 'sha1': - $t = pack('H*', '3021300906052b0e03021a05000414'); + $t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; break; case 'sha256': - $t = pack('H*', '3031300d060960864801650304020105000420'); + $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; break; case 'sha384': - $t = pack('H*', '3041300d060960864801650304020205000430'); + $t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"; break; case 'sha512': - $t = pack('H*', '3051300d060960864801650304020305000440'); + $t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"; + break; + // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 + case 'sha224': + $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c"; + break; + case 'sha512/224': + $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c"; + break; + case 'sha512/256': + $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20"; } $t.= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { - user_error('Intended encoded message length too short'); - return false; + throw new \LengthException('Intended encoded message length too short'); } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); @@ -2493,17 +1973,20 @@ function _emsa_pkcs1_v1_5_encode($m, $emLen) * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. * * @access private - * @param String $m - * @return String + * @param string $m + * @throws \LengthException if the RSA modulus is too short + * @return bool|string */ function _rsassa_pkcs1_v1_5_sign($m) { // EMSA-PKCS1-v1_5 encoding - $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em === false) { - user_error('RSA modulus too short'); - return false; + // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus + // too short" and stop. + try { + $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + } catch (\LengthException $e) { + throw new \LengthException('RSA modulus too short'); } // RSA signature @@ -2523,15 +2006,16 @@ function _rsassa_pkcs1_v1_5_sign($m) * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. * * @access private - * @param String $m - * @return String + * @param string $m + * @param string $s + * @throws \LengthException if the RSA modulus is too short + * @return bool */ function _rsassa_pkcs1_v1_5_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); return false; } @@ -2539,22 +2023,19 @@ function _rsassa_pkcs1_v1_5_verify($m, $s) $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); - if ($m2 === false) { - user_error('Invalid signature'); - return false; - } $em = $this->_i2osp($m2, $this->k); if ($em === false) { - user_error('Invalid signature'); return false; } // EMSA-PKCS1-v1_5 encoding - $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em2 === false) { - user_error('RSA modulus too short'); - return false; + // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus + // too short" and stop. + try { + $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + } catch (\LengthException $e) { + throw new \LengthException('RSA modulus too short'); } // Compare @@ -2562,153 +2043,179 @@ function _rsassa_pkcs1_v1_5_verify($m, $s) } /** - * Set Encryption Mode + * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) * - * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1. + * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 + * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. + * This means that under rare conditions you can have a perfectly valid v1.5 signature + * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends + * that if you're going to validate these types of signatures you "should indicate + * whether the underlying BER encoding is a DER encoding and hence whether the signature + * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do + * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of + * RSA::PADDING_PKCS1... that means BER encoding was used. * - * @access public - * @param Integer $mode + * @access private + * @param string $m + * @param string $s + * @return bool */ - function setEncryptionMode($mode) + function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s) { - $this->encryptionMode = $mode; - } + // Length checking - /** - * Set Signature Mode - * - * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1 - * - * @access public - * @param Integer $mode - */ - function setSignatureMode($mode) - { - $this->signatureMode = $mode; - } + if (strlen($s) != $this->k) { + return false; + } - /** - * Set public key comment. - * - * @access public - * @param String $comment - */ - function setComment($comment) - { - $this->comment = $comment; - } + // RSA verification - /** - * Get public key comment. - * - * @access public - * @return String - */ - function getComment() - { - return $this->comment; + $s = $this->_os2ip($s); + $m2 = $this->_rsavp1($s); + if ($m2 === false) { + return false; + } + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + return false; + } + + if ($this->_string_shift($em, 2) != "\0\1") { + return false; + } + + $em = ltrim($em, "\xFF"); + if ($this->_string_shift($em) != "\0") { + return false; + } + + $asn1 = new ASN1(); + $decoded = $asn1->decodeBER($em); + if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { + return false; + } + + $AlgorithmIdentifier = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'parameters' => array( + 'type' => ASN1::TYPE_ANY, + 'optional' => true + ) + ) + ); + + $DigestInfo = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'digestAlgorithm' => $AlgorithmIdentifier, + 'digest' => array('type' => ASN1::TYPE_OCTET_STRING) + ) + ); + + $oids = array( + '1.2.840.113549.2.2' => 'md2', + '1.2.840.113549.2.4' => 'md4', // from PKCS1 v1.5 + '1.2.840.113549.2.5' => 'md5', + '1.3.14.3.2.26' => 'sha1', + '2.16.840.1.101.3.4.2.1' => 'sha256', + '2.16.840.1.101.3.4.2.2' => 'sha384', + '2.16.840.1.101.3.4.2.3' => 'sha512', + // from PKCS1 v2.2 + '2.16.840.1.101.3.4.2.4' => 'sha224', + '2.16.840.1.101.3.4.2.5' => 'sha512/224', + '2.16.840.1.101.3.4.2.6' => 'sha512/256', + ); + + $asn1->loadOIDs($oids); + + $decoded = $asn1->asn1map($decoded[0], $DigestInfo); + if (!isset($decoded) || $decoded === false) { + return false; + } + + if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { + return false; + } + + $hash = new Hash($decoded['digestAlgorithm']['algorithm']); + $em = $hash->hash($m); + $em2 = Base64::decode($decoded['digest']); + + return $this->_equals($em, $em2); } /** * Encryption * - * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. + * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be. * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will * be concatenated together. * - * @see decrypt() + * @see self::decrypt() * @access public - * @param String $plaintext - * @return String + * @param string $plaintext + * @param int $padding optional + * @return bool|string + * @throws \LengthException if the RSA modulus is too short */ - function encrypt($plaintext) + function encrypt($plaintext, $padding = self::PADDING_OAEP) { - switch ($this->encryptionMode) { - case CRYPT_RSA_ENCRYPTION_PKCS1: - $length = $this->k - 11; - if ($length <= 0) { - return false; - } - - $plaintext = str_split($plaintext, $length); - $ciphertext = ''; - foreach ($plaintext as $m) { - $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); - } - return $ciphertext; - //case CRYPT_RSA_ENCRYPTION_OAEP: + switch ($padding) { + case self::PADDING_NONE: + return $this->_raw_encrypt($plaintext); + case self::PADDING_PKCS15_COMPAT: + case self::PADDING_PKCS1: + return $this->_rsaes_pkcs1_v1_5_encrypt($plaintext, $padding == self::PADDING_PKCS15_COMPAT); + //case self::PADDING_OAEP: default: - $length = $this->k - 2 * $this->hLen - 2; - if ($length <= 0) { - return false; - } - - $plaintext = str_split($plaintext, $length); - $ciphertext = ''; - foreach ($plaintext as $m) { - $ciphertext.= $this->_rsaes_oaep_encrypt($m); - } - return $ciphertext; + return $this->_rsaes_oaep_encrypt($plaintext); } } /** * Decryption * - * @see encrypt() + * @see self::encrypt() * @access public - * @param String $plaintext - * @return String + * @param string $plaintext + * @param int $padding optional + * @return bool|string */ - function decrypt($ciphertext) + function decrypt($ciphertext, $padding = self::PADDING_OAEP) { - if ($this->k <= 0) { - return false; - } - - $ciphertext = str_split($ciphertext, $this->k); - $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); - - $plaintext = ''; - - switch ($this->encryptionMode) { - case CRYPT_RSA_ENCRYPTION_PKCS1: - $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; - break; - //case CRYPT_RSA_ENCRYPTION_OAEP: + switch ($padding) { + case self::PADDING_NONE: + return $this->_raw_encrypt($ciphertext); + case self::PADDING_PKCS1: + return $this->_rsaes_pkcs1_v1_5_decrypt($ciphertext); + //case self::PADDING_OAEP: default: - $decrypt = '_rsaes_oaep_decrypt'; - } - - foreach ($ciphertext as $c) { - $temp = $this->$decrypt($c); - if ($temp === false) { - return false; - } - $plaintext.= $temp; + return $this->_rsaes_oaep_decrypt($ciphertext); } - - return $plaintext; } /** * Create a signature * - * @see verify() + * @see self::verify() * @access public - * @param String $message - * @return String + * @param string $message + * @param int $padding optional + * @return string */ - function sign($message) + function sign($message, $padding = self::PADDING_PSS) { if (empty($this->modulus) || empty($this->exponent)) { return false; } - switch ($this->signatureMode) { - case CRYPT_RSA_SIGNATURE_PKCS1: + switch ($padding) { + case self::PADDING_PKCS1: + case self::PADDING_RELAXED_PKCS1: return $this->_rsassa_pkcs1_v1_5_sign($message); - //case CRYPT_RSA_SIGNATURE_PSS: + //case self::PADDING_PSS: default: return $this->_rsassa_pss_sign($message); } @@ -2717,51 +2224,27 @@ function sign($message) /** * Verifies a signature * - * @see sign() + * @see self::sign() * @access public - * @param String $message - * @param String $signature - * @return Boolean + * @param string $message + * @param string $signature + * @param int $padding optional + * @return bool */ - function verify($message, $signature) + function verify($message, $signature, $padding = self::PADDING_PSS) { if (empty($this->modulus) || empty($this->exponent)) { return false; } - switch ($this->signatureMode) { - case CRYPT_RSA_SIGNATURE_PKCS1: + switch ($padding) { + case self::PADDING_RELAXED_PKCS1: + return $this->_rsassa_pkcs1_v1_5_relaxed_verify($message, $signature); + case self::PADDING_PKCS1: return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); - //case CRYPT_RSA_SIGNATURE_PSS: + //case self::PADDING_PSS: default: return $this->_rsassa_pss_verify($message, $signature); } } - - /** - * Extract raw BER from Base64 encoding - * - * @access private - * @param String $str - * @return String - */ - function _extractBER($str) - { - /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them - * above and beyond the ceritificate. - * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: - * - * Bag Attributes - * localKeyID: 01 00 00 00 - * subject=/O=organization/OU=org unit/CN=common name - * issuer=/O=organization/CN=common name - */ - $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); - // remove new lines - $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; - return $temp != false ? $temp : $str; - } } diff --git a/src/phpseclib/Crypt/RSA/MSBLOB.php b/src/phpseclib/Crypt/RSA/MSBLOB.php new file mode 100755 index 00000000..b99dc2f0 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/MSBLOB.php @@ -0,0 +1,224 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Math\BigInteger; + +/** + * Microsoft BLOB Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class MSBLOB +{ + /**#@+ + * @access private + */ + /** + * Public/Private Key Pair + */ + const PRIVATEKEYBLOB = 0x7; + /** + * Public Key + */ + const PUBLICKEYBLOB = 0x6; + /** + * Public Key + */ + const PUBLICKEYBLOBEX = 0xA; + /** + * RSA public key exchange algorithm + */ + const CALG_RSA_KEYX = 0x0000A400; + /** + * RSA public key exchange algorithm + */ + const CALG_RSA_SIGN = 0x00002400; + /** + * Public Key + */ + const RSA1 = 0x31415352; + /** + * Private Key + */ + const RSA2 = 0x32415352; + /**#@-*/ + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_string($key)) { + return false; + } + + $key = Base64::decode($key); + + if (!is_string($key) || strlen($key) < 20) { + return false; + } + + // PUBLICKEYSTRUC publickeystruc + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx + extract(unpack('atype/aversion/vreserved/Valgo', self::_string_shift($key, 8))); + switch (ord($type)) { + case self::PUBLICKEYBLOB: + case self::PUBLICKEYBLOBEX: + $publickey = true; + break; + case self::PRIVATEKEYBLOB: + $publickey = false; + break; + default: + return false; + } + + $components = array('isPublicKey' => $publickey); + + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx + switch ($algo) { + case self::CALG_RSA_KEYX: + case self::CALG_RSA_SIGN: + break; + default: + return false; + } + + // RSAPUBKEY rsapubkey + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx + // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit + extract(unpack('Vmagic/Vbitlen/a4pubexp', self::_string_shift($key, 12))); + switch ($magic) { + case self::RSA2: + $components['isPublicKey'] = false; + case self::RSA1: + break; + default: + return false; + } + + $baseLength = $bitlen / 16; + if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) { + return false; + } + + $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256); + // BYTE modulus[rsapubkey.bitlen/8] + $components['modulus'] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 8)), 256); + + if ($publickey) { + return $components; + } + + $components['isPublicKey'] = false; + + // BYTE prime1[rsapubkey.bitlen/16] + $components['primes'] = array(1 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256)); + // BYTE prime2[rsapubkey.bitlen/16] + $components['primes'][] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256); + // BYTE exponent1[rsapubkey.bitlen/16] + $components['exponents'] = array(1 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256)); + // BYTE exponent2[rsapubkey.bitlen/16] + $components['exponents'][] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256); + // BYTE coefficient[rsapubkey.bitlen/16] + $components['coefficients'] = array(2 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256)); + if (isset($components['privateExponent'])) { + $components['publicExponent'] = $components['privateExponent']; + } + // BYTE privateExponent[rsapubkey.bitlen/8] + $components['privateExponent'] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 8)), 256); + + return $components; + } + + /** + * 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, "\0"); + $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); + } + + /** + * 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) + { + $n = strrev($n->toBytes()); + $e = str_pad(strrev($e->toBytes()), 4, "\0"); + $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); + $key.= pack('VVa*', self::RSA1, 8 * strlen($n), $e); + $key.= $n; + + return Base64::encode($key); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} diff --git a/src/phpseclib/Crypt/RSA/OpenSSH.php b/src/phpseclib/Crypt/RSA/OpenSSH.php new file mode 100755 index 00000000..8cd53282 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/OpenSSH.php @@ -0,0 +1,141 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Math\BigInteger; + +/** + * OpenSSH Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class OpenSSH +{ + /** + * Default comment + * + * @var string + * @access private + */ + static $comment = 'phpseclib-generated-key'; + + /** + * Sets the default comment + * + * @access public + * @param string $comment + */ + static function setComment($comment) + { + self::$comment = str_replace(array("\r", "\n"), '', $comment); + } + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_string($key)) { + return false; + } + + $parts = explode(' ', $key, 3); + + $key = isset($parts[1]) ? Base64::decode($parts[1]) : Base64::decode($parts[0]); + if ($key === false) { + return false; + } + + $comment = isset($parts[2]) ? $parts[2] : false; + + if (substr($key, 0, 11) != "\0\0\0\7ssh-rsa") { + return false; + } + self::_string_shift($key, 11); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', self::_string_shift($key, 4))); + if (strlen($key) <= $length) { + return false; + } + $publicExponent = new BigInteger(self::_string_shift($key, $length), -256); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', self::_string_shift($key, 4))); + if (strlen($key) != $length) { + return false; + } + $modulus = new BigInteger(self::_string_shift($key, $length), -256); + + return array( + 'isPublicKey' => true, + 'modulus' => $modulus, + 'publicExponent' => $publicExponent, + 'comment' => $comment + ); + } + + /** + * 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 : + // 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; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } +} diff --git a/src/phpseclib/Crypt/RSA/PKCS.php b/src/phpseclib/Crypt/RSA/PKCS.php new file mode 100755 index 00000000..b0ff2559 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/PKCS.php @@ -0,0 +1,487 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\Base; +use phpseclib\Crypt\DES; +use phpseclib\Crypt\TripleDES; +use phpseclib\Math\BigInteger; + +/** + * PKCS Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +abstract class PKCS +{ + /**#@+ + * @access private + * @see \phpseclib\Crypt\RSA::createKey() + */ + /** + * ASN1 Integer + */ + const ASN1_INTEGER = 2; + /** + * ASN1 Bit String + */ + const ASN1_BITSTRING = 3; + /** + * ASN1 Octet String + */ + const ASN1_OCTETSTRING = 4; + /** + * ASN1 Object Identifier + */ + const ASN1_OBJECT = 6; + /** + * ASN1 Sequence (with the constucted bit set) + */ + const ASN1_SEQUENCE = 48; + /**#@-*/ + + /**#@+ + * @access private + */ + /** + * Auto-detect the format + */ + const MODE_ANY = 0; + /** + * Require base64-encoded PEM's be supplied + */ + const MODE_PEM = 1; + /** + * Require raw DER's be supplied + */ + const MODE_DER = 2; + /**#@-*/ + + /** + * Is the key a base-64 encoded PEM, DER or should it be auto-detected? + * + * @access private + * @param int + */ + static $format = self::MODE_ANY; + + /** + * Returns the mode constant corresponding to the mode string + * + * @access public + * @param string $mode + * @return int + * @throws \UnexpectedValueException if the block cipher mode is unsupported + */ + static function getEncryptionMode($mode) + { + switch ($mode) { + case 'CBC': + return Base::MODE_CBC; + case 'ECB': + return Base::MODE_ECB; + case 'CFB': + return Base::MODE_CFB; + case 'OFB': + return Base::MODE_OFB; + case 'CTR': + return Base::MODE_CTR; + } + throw new \UnexpectedValueException('Unsupported block cipher mode of operation'); + } + + /** + * Returns a cipher object corresponding to a string + * + * @access public + * @param string $algo + * @return string + * @throws \UnexpectedValueException if the encryption algorithm is unsupported + */ + static function getEncryptionObject($algo) + { + $modes = '(CBC|ECB|CFB|OFB|CTR)'; + switch (true) { + case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches): + $cipher = new AES(self::getEncryptionMode($matches[2])); + $cipher->setKeyLength($matches[1]); + return $cipher; + case preg_match("#^DES-EDE3-$modes$#", $algo, $matches): + return new TripleDES(self::getEncryptionMode($matches[1])); + case preg_match("#^DES-$modes$#", $algo, $matches): + return new DES(self::getEncryptionMode($matches[1])); + default: + throw new \UnexpectedValueException('Unsupported encryption algorithmn'); + } + } + + /** + * Generate a symmetric key for PKCS#1 keys + * + * @access public + * @param string $password + * @param string $iv + * @param int $length + * @return string + */ + static function generateSymmetricKey($password, $iv, $length) + { + $symkey = ''; + $iv = substr($iv, 0, 8); + while (strlen($symkey) < $length) { + $symkey.= md5($symkey . $password . $iv, true); + } + return substr($symkey, 0, $length); + } + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_string($key)) { + return false; + } + + $components = array('isPublicKey' => strpos($key, 'PUBLIC') !== false); + + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = Hex::decode(trim($matches[2])); + // remove the Proc-Type / DEK-Info sections as they're no longer needed + $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); + $ciphertext = self::_extractBER($key); + if ($ciphertext === false) { + $ciphertext = $key; + } + $crypto = self::getEncryptionObject($matches[1]); + $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); + $crypto->setIV($iv); + $key = $crypto->decrypt($ciphertext); + if ($key === false) { + return false; + } + } else { + if (self::$format != self::MODE_DER) { + $decoded = self::_extractBER($key); + if ($decoded !== false) { + $key = $decoded; + } elseif (self::$format == self::MODE_PEM) { + return false; + } + } + } + + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if (self::_decodeLength($key) != strlen($key)) { + return false; + } + + $tag = ord(self::_string_shift($key)); + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 631 cons: SEQUENCE + 4:d=1 hl=2 l= 1 prim: INTEGER :00 + 7:d=1 hl=2 l= 13 cons: SEQUENCE + 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 20:d=2 hl=2 l= 0 prim: NULL + 22:d=1 hl=4 l= 609 prim: OCTET STRING + + ie. PKCS8 keys */ + + if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { + self::_string_shift($key, 3); + $tag = self::ASN1_SEQUENCE; + } + + if ($tag == self::ASN1_SEQUENCE) { + $temp = self::_string_shift($key, self::_decodeLength($key)); + if (ord(self::_string_shift($temp)) != self::ASN1_OBJECT) { + return false; + } + $length = self::_decodeLength($temp); + switch (self::_string_shift($temp, $length)) { + case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + break; + case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC + /* + PBEParameter ::= SEQUENCE { + salt OCTET STRING (SIZE(8)), + iterationCount INTEGER } + */ + if (ord(self::_string_shift($temp)) != self::ASN1_SEQUENCE) { + return false; + } + if (self::_decodeLength($temp) != strlen($temp)) { + return false; + } + self::_string_shift($temp); // assume it's an octet string + $salt = self::_string_shift($temp, self::_decodeLength($temp)); + if (ord(self::_string_shift($temp)) != self::ASN1_INTEGER) { + return false; + } + self::_decodeLength($temp); + list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); + self::_string_shift($key); // assume it's an octet string + $length = self::_decodeLength($key); + if (strlen($key) != $length) { + return false; + } + + $crypto = new DES(DES::MODE_CBC); + $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount); + $key = $crypto->decrypt($key); + if ($key === false) { + return false; + } + return self::load($key); + default: + return false; + } + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING */ + $tag = ord(self::_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag + self::_decodeLength($key); // skip over the BIT STRING / OCTET STRING length + // "The initial octet shall encode, as an unsigned binary integer wtih 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 (section 8.6.2.2) + if ($tag == self::ASN1_BITSTRING) { + self::_string_shift($key); + } + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + if (self::_decodeLength($key) != strlen($key)) { + return false; + } + $tag = ord(self::_string_shift($key)); + } + if ($tag != self::ASN1_INTEGER) { + return false; + } + + $length = self::_decodeLength($key); + $temp = self::_string_shift($key, $length); + if (strlen($temp) != 1 || ord($temp) > 2) { + $components['modulus'] = new BigInteger($temp, 256); + self::_string_shift($key); // skip over self::ASN1_INTEGER + $length = self::_decodeLength($key); + $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256); + + return $components; + } + if (ord(self::_string_shift($key)) != self::ASN1_INTEGER) { + return false; + } + $length = self::_decodeLength($key); + $components['modulus'] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['publicExponent'] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['primes'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256)); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['exponents'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256)); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($key, $length), 256)); + + if (!empty($key)) { + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + self::_decodeLength($key); + while (!empty($key)) { + if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { + return false; + } + self::_decodeLength($key); + $key = substr($key, 1); + $length = self::_decodeLength($key); + $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256); + self::_string_shift($key); + $length = self::_decodeLength($key); + $components['coefficients'][] = new BigInteger(self::_string_shift($key, $length), 256); + } + } + + return $components; + } + + /** + * Require base64-encoded PEM's be supplied + * + * @see self::load() + * @access public + */ + static function requirePEM() + { + self::$format = self::MODE_PEM; + } + + /** + * Require raw DER's be supplied + * + * @see self::load() + * @access public + */ + static function requireDER() + { + self::$format = self::MODE_DER; + } + + /** + * Accept any format and auto detect the format + * + * This is the default setting + * + * @see self::load() + * @access public + */ + static function requireAny() + { + self::$format = self::MODE_ANY; + } + + /** + * DER-decode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param string $string + * @return int + */ + static function _decodeLength(&$string) + { + $length = ord(self::_string_shift($string)); + if ($length & 0x80) { // definite length, long form + $length&= 0x7F; + $temp = self::_string_shift($string, $length); + list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); + } + return $length; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @access private + * @param int $length + * @return string + */ + static function _encodeLength($length) + { + if ($length <= 0x7F) { + return chr($length); + } + + $temp = ltrim(pack('N', $length), chr(0)); + return pack('Ca*', 0x80 | strlen($temp), $temp); + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param string $str + * @return string + */ + static function _extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; + return $temp != false ? $temp : $str; + } +} diff --git a/src/phpseclib/Crypt/RSA/PKCS1.php b/src/phpseclib/Crypt/RSA/PKCS1.php new file mode 100755 index 00000000..e5d6e1d6 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/PKCS1.php @@ -0,0 +1,174 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\DES; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\TripleDES; +use phpseclib\Math\BigInteger; + +/** + * PKCS#1 Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class PKCS1 extends PKCS +{ + /** + * Default encryption algorithm + * + * @var string + * @access private + */ + static $defaultEncryptionAlgorithm = 'DES-EDE3-CBC'; + + /** + * Sets the default encryption algorithm + * + * @access public + * @param string $algo + */ + static function setEncryptionAlgorithm($algo) + { + self::$defaultEncryptionAlgorithm = $algo; + } + + /** + * 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 = '') + { + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + '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) + ); + + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + if (!empty($password) || is_string($password)) { + $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm); + $iv = Random::string($cipher->getBlockLength() >> 3); + $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3)); + $cipher->setIV($iv); + $iv = strtoupper(Hex::encode($iv)); + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + "Proc-Type: 4,ENCRYPTED\r\n" . + "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" . + "\r\n" . + chunk_split(Base64::encode($cipher->encrypt($RSAPrivateKey)), 64) . + '-----END RSA PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + chunk_split(Base64::encode($RSAPrivateKey), 64) . + '-----END RSA PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + + /** + * 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 : + // 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; + } +} diff --git a/src/phpseclib/Crypt/RSA/PKCS8.php b/src/phpseclib/Crypt/RSA/PKCS8.php new file mode 100755 index 00000000..787c89a5 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/PKCS8.php @@ -0,0 +1,209 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Crypt\DES; +use phpseclib\Crypt\Random; +use phpseclib\Math\BigInteger; + +/** + * PKCS#8 Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class PKCS8 extends PKCS +{ + /** + * 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 = '') + { + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + '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) + ); + + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_INTEGER, + "\01\00", + $rsaOID, + 4, + self::_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + if (!empty($password) || is_string($password)) { + $salt = Random::string(8); + $iterationCount = 2048; + + $crypto = new DES(DES::MODE_CBC); + $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount); + $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); + + $parameters = pack( + 'Ca*a*Ca*N', + self::ASN1_OCTETSTRING, + self::_encodeLength(strlen($salt)), + $salt, + self::ASN1_INTEGER, + self::_encodeLength(4), + $iterationCount + ); + $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; + + $encryptionAlgorithm = pack( + 'Ca*a*Ca*a*', + self::ASN1_OBJECT, + self::_encodeLength(strlen($pbeWithMD5AndDES_CBC)), + $pbeWithMD5AndDES_CBC, + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($parameters)), + $parameters + ); + + $RSAPrivateKey = pack( + 'Ca*a*Ca*a*', + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($encryptionAlgorithm)), + $encryptionAlgorithm, + self::ASN1_OCTETSTRING, + self::_encodeLength(strlen($RSAPrivateKey)), + $RSAPrivateKey + ); + + $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . + chunk_split(Base64::encode($RSAPrivateKey), 64) . + '-----END ENCRYPTED PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . + chunk_split(Base64::encode($RSAPrivateKey), 64) . + '-----END PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + + /** + * 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 : + // 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'] + ); + + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . self::_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; + + $RSAPublicKey = pack( + 'Ca*a*', + self::ASN1_SEQUENCE, + self::_encodeLength(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey + ); + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(Base64::encode($RSAPublicKey), 64) . + '-----END PUBLIC KEY-----'; + + return $RSAPublicKey; + } +} diff --git a/src/phpseclib/Crypt/RSA/PuTTY.php b/src/phpseclib/Crypt/RSA/PuTTY.php new file mode 100755 index 00000000..04c4ae20 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/PuTTY.php @@ -0,0 +1,313 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\Hash; +use phpseclib\Math\BigInteger; + +/** + * PuTTY Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class PuTTY +{ + /** + * Default comment + * + * @var string + * @access private + */ + static $comment = 'phpseclib-generated-key'; + + /** + * Sets the default comment + * + * @access public + * @param string $comment + */ + static function setComment($comment) + { + self::$comment = str_replace(array("\r", "\n"), '', $comment); + } + + /** + * Generate a symmetric key for PuTTY keys + * + * @access public + * @param string $password + * @param string $iv + * @param int $length + * @return string + */ + static function generateSymmetricKey($password, $length) + { + $symkey = ''; + $sequence = 0; + while (strlen($symkey) < $length) { + $temp = pack('Na*', $sequence++, $password); + $symkey.= Hex::decode(sha1($temp)); + } + return substr($symkey, 0, $length); + } + + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_string($key)) { + return false; + } + + static $one; + if (!isset($one)) { + $one = new BigInteger(1); + } + + if (strpos($key, 'BEGIN SSH2 PUBLIC KEY')) { + $data = preg_split('#[\r\n]+#', $key); + $data = array_splice($data, 2, -1); + $data = implode('', $data); + + $components = OpenSSH::load($data); + if ($components === false) { + return false; + } + + if (!preg_match('#Comment: "(.+)"#', $key, $matches)) { + return false; + } + $components['comment'] = str_replace(array('\\\\', '\"'), array('\\', '"'), $matches[1]); + + return $components; + } + + $components = array('isPublicKey' => false); + $key = preg_split('#\r\n|\r|\n#', $key); + $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); + if ($type != 'ssh-rsa') { + return false; + } + $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $components['comment'] = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); + + $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); + $public = Base64::decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); + $public = substr($public, 11); + extract(unpack('Nlength', self::_string_shift($public, 4))); + $components['publicExponent'] = new BigInteger(self::_string_shift($public, $length), -256); + extract(unpack('Nlength', self::_string_shift($public, 4))); + $components['modulus'] = new BigInteger(self::_string_shift($public, $length), -256); + + $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); + $private = Base64::decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); + + switch ($encryption) { + case 'aes256-cbc': + $symkey = static::generateSymmetricKey($password, 32); + $crypto = new AES(AES::MODE_CBC); + } + + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->setIV(str_repeat("\0", $crypto->getBlockLength() >> 3)); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + if ($private === false) { + return false; + } + } + + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['privateExponent'] = new BigInteger(self::_string_shift($private, $length), -256); + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'] = array(1 => new BigInteger(self::_string_shift($private, $length), -256)); + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'][] = new BigInteger(self::_string_shift($private, $length), -256); + + $temp = $components['primes'][1]->subtract($one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + extract(unpack('Nlength', self::_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($private, $length), -256)); + + return $components; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + static function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * 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("\0", $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; + } + + /** + * 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) + { + $n = $n->toBytes(true); + $e = $e->toBytes(true); + + $key = pack( + 'Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($e), + $e, + strlen($n), + $n + ); + $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" . + 'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\"'), self::$comment) . "\"\r\n"; + chunk_split(Base64::encode($key), 64) . + '---- END SSH2 PUBLIC KEY ----'; + + return $key; + } +} diff --git a/src/phpseclib/Crypt/RSA/Raw.php b/src/phpseclib/Crypt/RSA/Raw.php new file mode 100755 index 00000000..d3992521 --- /dev/null +++ b/src/phpseclib/Crypt/RSA/Raw.php @@ -0,0 +1,103 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use phpseclib\Math\BigInteger; + +/** + * Raw RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class Raw +{ + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_array($key)) { + return false; + } + if (isset($key['isPublicKey']) && isset($key['modulus'])) { + if (isset($key['privateExponent']) || isset($key['publicExponent'])) { + if (!isset($key['primes'])) { + return $key; + } + if (isset($key['exponents']) && isset($key['coefficients']) && isset($key['publicExponent']) && isset($key['privateExponent'])) { + return $key; + } + } + } + $components = array('isPublicKey' => true); + switch (true) { + case isset($key['e']): + $components['publicExponent'] = $key['e']; + break; + case isset($key['exponent']): + $components['publicExponent'] = $key['exponent']; + break; + case isset($key['publicExponent']): + $components['publicExponent'] = $key['publicExponent']; + break; + case isset($key[0]): + $components['publicExponent'] = $key[0]; + } + switch (true) { + case isset($key['n']): + $components['modulus'] = $key['n']; + break; + case isset($key['modulo']): + $components['modulus'] = $key['modulo']; + break; + case isset($key['modulus']): + $components['modulus'] = $key['modulus']; + break; + case isset($key[1]): + $components['modulus'] = $key[1]; + } + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + } + + /** + * 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 array('e' => clone $e, 'n' => clone $n); + } +} diff --git a/src/phpseclib/Crypt/RSA/XML.php b/src/phpseclib/Crypt/RSA/XML.php new file mode 100755 index 00000000..a257033b --- /dev/null +++ b/src/phpseclib/Crypt/RSA/XML.php @@ -0,0 +1,147 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt\RSA; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Math\BigInteger; + +/** + * XML Formatted RSA Key Handler + * + * @package RSA + * @author Jim Wigginton + * @access public + */ +class XML +{ + /** + * Break a public or private key down into its constituent components + * + * @access public + * @param string $key + * @param string $password optional + * @return array + */ + static function load($key, $password = '') + { + if (!is_string($key)) { + return false; + } + + $components = array( + 'isPublicKey' => false, + 'primes' => array(), + 'exponents' => array(), + 'coefficients' => array() + ); + + $use_errors = libxml_use_internal_errors(true); + + $dom = new \DOMDocument(); + if (!$dom->loadXML('' . $key . '')) { + return false; + } + $xpath = new \DOMXPath($dom); + $keys = array('modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd'); + foreach ($keys as $key) { + // $dom->getElementsByTagName($key) is case-sensitive + $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']"); + if (!$temp->length) { + continue; + } + $value = new BigInteger(Base64::decode($temp->item(0)->nodeValue), 256); + switch ($key) { + case 'modulus': + $components['modulus'] = $value; + break; + case 'exponent': + $components['publicExponent'] = $value; + break; + case 'p': + $components['primes'][1] = $value; + break; + case 'q': + $components['primes'][2] = $value; + break; + case 'dp': + $components['exponents'][1] = $value; + break; + case 'dq': + $components['exponents'][2] = $value; + break; + case 'inverseq': + $components['coefficients'][2] = $value; + break; + case 'd': + $components['privateExponent'] = $value; + } + } + + libxml_use_internal_errors($use_errors); + + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + } + + /** + * 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; + } + return "\r\n" . + ' ' . Base64::encode($n->toBytes()) . "\r\n" . + ' ' . Base64::encode($e->toBytes()) . "\r\n" . + '

' . Base64::encode($primes[1]->toBytes()) . "

\r\n" . + ' ' . Base64::encode($primes[2]->toBytes()) . "\r\n" . + ' ' . Base64::encode($exponents[1]->toBytes()) . "\r\n" . + ' ' . Base64::encode($exponents[2]->toBytes()) . "\r\n" . + ' ' . Base64::encode($coefficients[2]->toBytes()) . "\r\n" . + ' ' . Base64::encode($d->toBytes()) . "\r\n" . + '
'; + } + + /** + * 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 "\r\n" . + ' ' . Base64::encode($n->toBytes()) . "\r\n" . + ' ' . Base64::encode($e->toBytes()) . "\r\n" . + ''; + } +} diff --git a/src/phpseclib/Crypt/Random.php b/src/phpseclib/Crypt/Random.php old mode 100644 new mode 100755 index 5df68f55..6bf61468 --- a/src/phpseclib/Crypt/Random.php +++ b/src/phpseclib/Crypt/Random.php @@ -3,39 +3,21 @@ /** * Random Number Generator * - * PHP versions 4 and 5 + * PHP version 5 * * Here's a short example of how to use this library: * * * * - * LICENSE: 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. - * * @category Crypt * @package Random * @author Jim Wigginton - * @copyright MMVII Jim Wigginton + * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ @@ -43,182 +25,148 @@ namespace phpseclib\Crypt; /** - * "Is Windows" test - * - * @access private - */ -@define('CRYPT_RANDOM_IS_WINDOWS', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); - - -/** - * Generate a random string. - * - * Although microoptimizations are generally discouraged as they impair readability this function is ripe with - * microoptimizations because this function has the potential of being called a huge number of times. - * eg. for RSA key generation. + * Pure-PHP Random Number Generator * - * @param Integer $length - * @return String - * @access public + * @package Random + * @author Jim Wigginton + * @access public */ - -class Random{ - -} - -if(!function_exists("phpseclib\\Crypt\\crypt_random_string")){ -function crypt_random_string($length) +class Random { - if (CRYPT_RANDOM_IS_WINDOWS) { - // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. - // ie. class_alias is a function that was introduced in PHP 5.3 - if (function_exists('mcrypt_create_iv') && function_exists('class_alias')) { - return mcrypt_create_iv($length); - } - // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was, - // to quote , "possible blocking behavior". as of 5.3.4 - // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both - // call php_win32_get_random_bytes(): - // - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008 - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392 - // - // php_win32_get_random_bytes() is defined thusly: - // - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80 - // - // we're calling it, all the same, in the off chance that the mcrypt extension is not available - if (function_exists('openssl_random_pseudo_bytes') && version_compare(PHP_VERSION, '5.3.4', '>=')) { - return openssl_random_pseudo_bytes($length); + /** + * Generate a random string. + * + * Although microoptimizations are generally discouraged as they impair readability this function is ripe with + * microoptimizations because this function has the potential of being called a huge number of times. + * eg. for RSA key generation. + * + * @param int $length + * @throws \RuntimeException if a symmetric cipher is needed but not loaded + * @return string + */ + static function string($length) + { + try { + return \random_bytes($length); + } catch (\Exception $e) { + // random_compat will throw an Exception, which in PHP 5 does not implement Throwable + } catch (\Throwable $e) { + // If a sufficient source of randomness is unavailable, random_bytes() will throw an + // object that implements the Throwable interface (Exception, TypeError, Error). + // We don't actually need to do anything here. The string() method should just continue + // as normal. Note, however, that if we don't have a sufficient source of randomness for + // random_bytes(), most of the other calls here will fail too, so we'll end up using + // the PHP implementation. } - } else { - // method 1. the fastest - if (function_exists('openssl_random_pseudo_bytes')) { - return openssl_random_pseudo_bytes($length); - } - // method 2 - static $fp = true; - if ($fp === true) { - // warning's will be output unles the error suppression operator is used. errors such as - // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc. - $fp = @fopen('/dev/urandom', 'rb'); - } - if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() - return fread($fp, $length); - } - // method 3. pretty much does the same thing as method 2 per the following url: - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391 - // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're - // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir - // restrictions or some such - if (function_exists('mcrypt_create_iv')) { - return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); - } - } - // at this point we have no choice but to use a pure-PHP CSPRNG + // at this point we have no choice but to use a pure-PHP CSPRNG - // cascade entropy across multiple PHP instances by fixing the session and collecting all - // environmental variables, including the previous session data and the current session - // data. - // - // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) - // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but - // PHP isn't low level to be able to use those as sources and on a web server there's not likely - // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use - // however. a ton of people visiting the website. obviously you don't want to base your seeding - // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled - // by the user and (2) this isn't just looking at the data sent by the current user - it's based - // on the data sent by all users. one user requests the page and a hash of their info is saved. - // another user visits the page and the serialization of their data is utilized along with the - // server envirnment stuff and a hash of the previous http request data (which itself utilizes - // a hash of the session data before that). certainly an attacker should be assumed to have - // full control over his own http requests. he, however, is not going to have control over - // everyone's http requests. - static $crypto = false, $v; - if ($crypto === false) { - // save old session data - $old_session_id = session_id(); - $old_use_cookies = ini_get('session.use_cookies'); - $old_session_cache_limiter = session_cache_limiter(); - $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; - if ($old_session_id != '') { - session_write_close(); - } + // cascade entropy across multiple PHP instances by fixing the session and collecting all + // environmental variables, including the previous session data and the current session + // data. + // + // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) + // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but + // PHP isn't low level to be able to use those as sources and on a web server there's not likely + // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use + // however, a ton of people visiting the website. obviously you don't want to base your seeding + // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled + // by the user and (2) this isn't just looking at the data sent by the current user - it's based + // on the data sent by all users. one user requests the page and a hash of their info is saved. + // another user visits the page and the serialization of their data is utilized along with the + // server envirnment stuff and a hash of the previous http request data (which itself utilizes + // a hash of the session data before that). certainly an attacker should be assumed to have + // full control over his own http requests. he, however, is not going to have control over + // everyone's http requests. + static $crypto = false, $v; + if ($crypto === false) { + // save old session data + $old_session_id = session_id(); + $old_use_cookies = ini_get('session.use_cookies'); + $old_session_cache_limiter = session_cache_limiter(); + $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; + if ($old_session_id != '') { + session_write_close(); + } - session_id(1); - ini_set('session.use_cookies', 0); - session_cache_limiter(''); - session_start(); + session_id(1); + ini_set('session.use_cookies', 0); + session_cache_limiter(''); + session_start(); - $v = $seed = $_SESSION['seed'] = pack('H*', sha1( - serialize($_SERVER) . - serialize($_POST) . - serialize($_GET) . - serialize($_COOKIE) . - serialize($GLOBALS) . - serialize($_SESSION) . - serialize($_OLD_SESSION) - )); - if (!isset($_SESSION['count'])) { - $_SESSION['count'] = 0; - } - $_SESSION['count']++; + $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') . + (isset($_POST) ? self::safe_serialize($_POST) : '') . + (isset($_GET) ? self::safe_serialize($_GET) : '') . + (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') . + self::safe_serialize($GLOBALS) . + self::safe_serialize($_SESSION) . + self::safe_serialize($_OLD_SESSION); + $v = $seed = $_SESSION['seed'] = sha1($v, true); + if (!isset($_SESSION['count'])) { + $_SESSION['count'] = 0; + } + $_SESSION['count']++; - session_write_close(); + session_write_close(); - // restore old session data - if ($old_session_id != '') { - session_id($old_session_id); - session_start(); - ini_set('session.use_cookies', $old_use_cookies); - session_cache_limiter($old_session_cache_limiter); - } else { - if ($_OLD_SESSION !== false) { - $_SESSION = $_OLD_SESSION; - unset($_OLD_SESSION); + // restore old session data + if ($old_session_id != '') { + session_id($old_session_id); + session_start(); + ini_set('session.use_cookies', $old_use_cookies); + session_cache_limiter($old_session_cache_limiter); } else { - unset($_SESSION); + if ($_OLD_SESSION !== false) { + $_SESSION = $_OLD_SESSION; + unset($_OLD_SESSION); + } else { + unset($_SESSION); + } } - } - // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. - // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. - // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the - // original hash and the current hash. we'll be emulating that. for more info see the following URL: - // - // http://tools.ietf.org/html/rfc4253#section-7.2 - // - // see the is_string($crypto) part for an example of how to expand the keys - $key = pack('H*', sha1($seed . 'A')); - $iv = pack('H*', sha1($seed . 'C')); + // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. + // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. + // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the + // original hash and the current hash. we'll be emulating that. for more info see the following URL: + // + // http://tools.ietf.org/html/rfc4253#section-7.2 + // + // see the is_string($crypto) part for an example of how to expand the keys + $key = sha1($seed . 'A', true); + $iv = sha1($seed . 'C', true); + + // ciphers are used as per the nist.gov link below. also, see this link: + // + // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives + switch (true) { + case class_exists('\phpseclib\Crypt\AES'): + $crypto = new AES(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\Twofish'): + $crypto = new Twofish(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\Blowfish'): + $crypto = new Blowfish(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\TripleDES'): + $crypto = new TripleDES(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\DES'): + $crypto = new DES(Base::MODE_CTR); + break; + case class_exists('\phpseclib\Crypt\RC4'): + $crypto = new RC4(); + break; + default: + throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded'); + } - // ciphers are used as per the nist.gov link below. also, see this link: - // - // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives - switch (true) { - case class_exists('phpseclib\\Crypt\\AES'): - $crypto = new AES(CRYPT_AES_MODE_CTR); - break; - case class_exists('phpseclib\\Crypt\\TripleDES'): - $crypto = new TripleDES(CRYPT_DES_MODE_CTR); - break; - case class_exists('phpseclib\\Crypt\\DES'): - $crypto = new DES(CRYPT_DES_MODE_CTR); - break; - case class_exists('phpseclib\\Crypt\\RC4'): - $crypto = new RC4(); - break; - default: - $crypto = $seed; - return crypt_random_string($length); + $crypto->setKey(substr($key, 0, $crypto->getKeyLength() >> 3)); + $crypto->setIV(substr($iv, 0, $crypto->getBlockLength() >> 3)); + $crypto->enableContinuousBuffer(); } - $crypto->setKey($key); - $crypto->setIV($iv); - $crypto->enableContinuousBuffer(); - } + //return $crypto->encrypt(str_repeat("\0", $length)); - if (is_string($crypto)) { // the following is based off of ANSI X9.31: // // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf @@ -227,31 +175,45 @@ function crypt_random_string($length) // // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c // (do a search for "ANS X9.31 A.2.4") - // - // ANSI X9.31 recommends ciphers be used and phpseclib does use them if they're available (see - // later on in the code) but if they're not we'll use sha1 $result = ''; - while (strlen($result) < $length) { // each loop adds 20 bytes - // microtime() isn't packed as "densely" as it could be but then neither is that the idea. - // the idea is simply to ensure that each "block" has a unique element to it. - $i = pack('H*', sha1(microtime())); - $r = pack('H*', sha1($i ^ $v)); - $v = pack('H*', sha1($r ^ $i)); + while (strlen($result) < $length) { + $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21 + $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20 + $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20 $result.= $r; } return substr($result, 0, $length); } - //return $crypto->encrypt(str_repeat("\0", $length)); - - $result = ''; - while (strlen($result) < $length) { - $i = $crypto->encrypt(microtime()); - $r = $crypto->encrypt($i ^ $v); - $v = $crypto->encrypt($r ^ $i); - $result.= $r; + /** + * Safely serialize variables + * + * If a class has a private __sleep() it'll emit a warning + * + * @param mixed $arr + * @access public + */ + static function safe_serialize(&$arr) + { + if (is_object($arr)) { + return ''; + } + if (!is_array($arr)) { + return serialize($arr); + } + // prevent circular array recursion + if (isset($arr['__phpseclib_marker'])) { + return ''; + } + $safearr = array(); + $arr['__phpseclib_marker'] = true; + foreach (array_keys($arr) as $key) { + // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage + if ($key !== '__phpseclib_marker') { + $safearr[$key] = self::safe_serialize($arr[$key]); + } + } + unset($arr['__phpseclib_marker']); + return serialize($safearr); } - return substr($result, 0, $length); -} - } diff --git a/src/phpseclib/Crypt/Rijndael.php b/src/phpseclib/Crypt/Rijndael.php old mode 100644 new mode 100755 index fc1bbcfc..c98f02e2 --- a/src/phpseclib/Crypt/Rijndael.php +++ b/src/phpseclib/Crypt/Rijndael.php @@ -5,13 +5,13 @@ * * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * - * PHP versions 4 and 5 + * PHP version 5 * - * If {@link Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If - * {@link Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from - * {@link Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's + * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If + * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from + * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until - * {@link Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated. + * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated. * * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example, * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256. @@ -28,9 +28,9 @@ * Here's a short example of how to use this library: * * setKey('abcdefghijklmnop'); * @@ -44,128 +44,37 @@ * ?> * * - * LICENSE: 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. - * * @category Crypt * @package Rijndael * @author Jim Wigginton - * @copyright MMVIII Jim Wigginton + * @copyright 2008 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/**#@+ - * @access public - * @see Rijndael::encrypt() - * @see Rijndael::decrypt() - */ -/** - * Encrypt / decrypt using the Counter mode. - * - * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 - */ -@define('CRYPT_RIJNDAEL_MODE_CTR', CRYPT_MODE_CTR); -/** - * Encrypt / decrypt using the Electronic Code Book mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 - */ -@define('CRYPT_RIJNDAEL_MODE_ECB', CRYPT_MODE_ECB); -/** - * Encrypt / decrypt using the Code Book Chaining mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 - */ -@define('CRYPT_RIJNDAEL_MODE_CBC', CRYPT_MODE_CBC); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 - */ -@define('CRYPT_RIJNDAEL_MODE_CFB', CRYPT_MODE_CFB); -/** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 - */ -@define('CRYPT_RIJNDAEL_MODE_OFB', CRYPT_MODE_OFB); -/**#@-*/ - -/**#@+ - * @access private - * @see Rijndael::Rijndael() - */ -/** - * Toggles the internal implementation - */ -@define('CRYPT_RIJNDAEL_MODE_INTERNAL', CRYPT_MODE_INTERNAL); -/** - * Toggles the mcrypt implementation - */ -@define('CRYPT_RIJNDAEL_MODE_MCRYPT', CRYPT_MODE_MCRYPT); -/**#@-*/ - /** * Pure-PHP implementation of Rijndael. * * @package Rijndael * @author Jim Wigginton - * @version 0.1.0 * @access public */ class Rijndael extends Base { - /** - * The default password key_size used by setPassword() - * - * @see Base::password_key_size - * @see Base::setPassword() - * @var Integer - * @access private - */ - var $password_key_size = 16; - - /** - * The namespace used by the cipher for its constants. - * - * @see Base::const_namespace - * @var String - * @access private - */ - var $const_namespace = 'RIJNDAEL'; - /** * The mcrypt specific name of the cipher * - * Mcrypt is useable for 128/192/256-bit $block_size/$key_size. For 160/224 not. - * Rijndael determines automatically whether mcrypt is useable - * or not for the current $block_size/$key_size. - * In case of, $cipher_name_mcrypt will be set dynamicaly at run time accordingly. + * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not. + * \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is useable + * or not for the current $block_size/$key_length. + * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly. * - * @see Base::cipher_name_mcrypt - * @see Base::engine - * @see _setupEngine() - * @var String + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @see \phpseclib\Crypt\Base::engine + * @see self::isValidEngine() + * @var string * @access private */ var $cipher_name_mcrypt = 'rijndael-128'; @@ -173,27 +82,18 @@ class Rijndael extends Base /** * The default salt used by setPassword() * - * @see Base::password_default_salt - * @see Base::setPassword() - * @var String + * @see \phpseclib\Crypt\Base::password_default_salt + * @see \phpseclib\Crypt\Base::setPassword() + * @var string * @access private */ var $password_default_salt = 'phpseclib'; - /** - * Has the key length explicitly been set or should it be derived from the key, itself? - * - * @see setKeyLength() - * @var Boolean - * @access private - */ - var $explicit_key_length = false; - /** * The Key Schedule * - * @see _setup() - * @var Array + * @see self::_setup() + * @var array * @access private */ var $w; @@ -201,8 +101,8 @@ class Rijndael extends Base /** * The Inverse Key Schedule * - * @see _setup() - * @var Array + * @see self::_setup() + * @var array * @access private */ var $dw; @@ -210,35 +110,34 @@ class Rijndael extends Base /** * The Block Length divided by 32 * - * @see setBlockLength() - * @var Integer + * @see self::setBlockLength() + * @var int * @access private * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once. - * */ var $Nb = 4; /** - * The Key Length + * The Key Length (in bytes) * - * @see setKeyLength() - * @var Integer + * @see self::setKeyLength() + * @var int * @access private * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk - * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could - * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could + * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once. */ - var $key_size = 16; + var $key_length = 16; /** * The Key Length divided by 32 * - * @see setKeyLength() - * @var Integer + * @see self::setKeyLength() + * @var int * @access private * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4 */ @@ -247,7 +146,7 @@ class Rijndael extends Base /** * The Number of Rounds * - * @var Integer + * @var int * @access private * @internal The max value is 14, the min value is 10. */ @@ -256,7 +155,7 @@ class Rijndael extends Base /** * Shift offsets * - * @var Array + * @var array * @access private */ var $c; @@ -264,482 +163,31 @@ class Rijndael extends Base /** * Holds the last used key- and block_size information * - * @var Array + * @var array * @access private */ var $kl; - /** - * Precomputed mixColumns table - * - * According to (section 5.2.1), - * precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so - * those are the names we'll use. - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $t0 = array( - 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, - 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, - 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, - 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, - 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, - 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, - 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, - 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, - 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, - 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, - 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, - 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, - 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, - 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, - 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, - 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, - 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, - 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, - 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, - 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, - 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, - 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, - 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, - 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, - 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, - 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, - 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, - 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, - 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, - 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, - 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, - 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A - ); - - /** - * Precomputed mixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $t1 = array( - 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, - 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, - 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, - 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, - 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, - 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, - 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, - 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, - 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, - 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, - 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, - 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, - 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, - 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, - 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, - 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, - 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, - 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, - 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, - 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, - 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, - 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, - 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, - 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, - 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, - 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, - 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, - 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, - 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, - 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, - 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, - 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616 - ); - - /** - * Precomputed mixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $t2 = array( - 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, - 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, - 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, - 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, - 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, - 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, - 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, - 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, - 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, - 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, - 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, - 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, - 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, - 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, - 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, - 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, - 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, - 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, - 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, - 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, - 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, - 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, - 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, - 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, - 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, - 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, - 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, - 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, - 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, - 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, - 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, - 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16 - ); - - /** - * Precomputed mixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $t3 = array( - 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, - 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, - 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, - 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, - 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, - 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, - 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, - 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, - 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, - 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, - 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, - 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, - 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, - 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, - 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, - 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, - 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, - 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, - 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, - 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, - 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, - 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, - 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, - 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, - 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, - 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, - 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, - 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, - 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, - 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, - 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, - 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C - ); - - /** - * Precomputed invMixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $dt0 = array( - 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, 0xACFA58AB, 0x4BE30393, - 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, - 0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, - 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, 0x49E06929, 0x8EC9C844, - 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, - 0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, - 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, 0xE31F8F57, 0x6655AB2A, - 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, - 0x8ACF1C2B, 0xA779B492, 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A, - 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051, - 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, - 0x1998FB24, 0xD6BDE997, 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB, - 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E, - 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, - 0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, - 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8, - 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, - 0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, - 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, 0x0D8652EC, 0x77C1E3D0, - 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, - 0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, - 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, 0x82C3AFF5, - 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, - 0xCD267809, 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6, - 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0, - 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, - 0x764DD68D, 0x43EFB04D, 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F, - 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713, - 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, - 0x9CD2DF59, 0x55F2733F, 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, - 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, 0x283C498B, 0xFF0D9541, - 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742 - ); - - /** - * Precomputed invMixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $dt1 = array( - 0x5051F4A7, 0x537E4165, 0xC31A17A4, 0x963A275E, 0xCB3BAB6B, 0xF11F9D45, 0xABACFA58, 0x934BE303, - 0x552030FA, 0xF6AD766D, 0x9188CC76, 0x25F5024C, 0xFC4FE5D7, 0xD7C52ACB, 0x80263544, 0x8FB562A3, - 0x49DEB15A, 0x6725BA1B, 0x9845EA0E, 0xE15DFEC0, 0x02C32F75, 0x12814CF0, 0xA38D4697, 0xC66BD3F9, - 0xE7038F5F, 0x9515929C, 0xEBBF6D7A, 0xDA955259, 0x2DD4BE83, 0xD3587421, 0x2949E069, 0x448EC9C8, - 0x6A75C289, 0x78F48E79, 0x6B99583E, 0xDD27B971, 0xB6BEE14F, 0x17F088AD, 0x66C920AC, 0xB47DCE3A, - 0x1863DF4A, 0x82E51A31, 0x60975133, 0x4562537F, 0xE0B16477, 0x84BB6BAE, 0x1CFE81A0, 0x94F9082B, - 0x58704868, 0x198F45FD, 0x8794DE6C, 0xB7527BF8, 0x23AB73D3, 0xE2724B02, 0x57E31F8F, 0x2A6655AB, - 0x07B2EB28, 0x032FB5C2, 0x9A86C57B, 0xA5D33708, 0xF2302887, 0xB223BFA5, 0xBA02036A, 0x5CED1682, - 0x2B8ACF1C, 0x92A779B4, 0xF0F307F2, 0xA14E69E2, 0xCD65DAF4, 0xD50605BE, 0x1FD13462, 0x8AC4A6FE, - 0x9D342E53, 0xA0A2F355, 0x32058AE1, 0x75A4F6EB, 0x390B83EC, 0xAA4060EF, 0x065E719F, 0x51BD6E10, - 0xF93E218A, 0x3D96DD06, 0xAEDD3E05, 0x464DE6BD, 0xB591548D, 0x0571C45D, 0x6F0406D4, 0xFF605015, - 0x241998FB, 0x97D6BDE9, 0xCC894043, 0x7767D99E, 0xBDB0E842, 0x8807898B, 0x38E7195B, 0xDB79C8EE, - 0x47A17C0A, 0xE97C420F, 0xC9F8841E, 0x00000000, 0x83098086, 0x48322BED, 0xAC1E1170, 0x4E6C5A72, - 0xFBFD0EFF, 0x560F8538, 0x1E3DAED5, 0x27362D39, 0x640A0FD9, 0x21685CA6, 0xD19B5B54, 0x3A24362E, - 0xB10C0A67, 0x0F9357E7, 0xD2B4EE96, 0x9E1B9B91, 0x4F80C0C5, 0xA261DC20, 0x695A774B, 0x161C121A, - 0x0AE293BA, 0xE5C0A02A, 0x433C22E0, 0x1D121B17, 0x0B0E090D, 0xADF28BC7, 0xB92DB6A8, 0xC8141EA9, - 0x8557F119, 0x4CAF7507, 0xBBEE99DD, 0xFDA37F60, 0x9FF70126, 0xBC5C72F5, 0xC544663B, 0x345BFB7E, - 0x768B4329, 0xDCCB23C6, 0x68B6EDFC, 0x63B8E4F1, 0xCAD731DC, 0x10426385, 0x40139722, 0x2084C611, - 0x7D854A24, 0xF8D2BB3D, 0x11AEF932, 0x6DC729A1, 0x4B1D9E2F, 0xF3DCB230, 0xEC0D8652, 0xD077C1E3, - 0x6C2BB316, 0x99A970B9, 0xFA119448, 0x2247E964, 0xC4A8FC8C, 0x1AA0F03F, 0xD8567D2C, 0xEF223390, - 0xC787494E, 0xC1D938D1, 0xFE8CCAA2, 0x3698D40B, 0xCFA6F581, 0x28A57ADE, 0x26DAB78E, 0xA43FADBF, - 0xE42C3A9D, 0x0D507892, 0x9B6A5FCC, 0x62547E46, 0xC2F68D13, 0xE890D8B8, 0x5E2E39F7, 0xF582C3AF, - 0xBE9F5D80, 0x7C69D093, 0xA96FD52D, 0xB3CF2512, 0x3BC8AC99, 0xA710187D, 0x6EE89C63, 0x7BDB3BBB, - 0x09CD2678, 0xF46E5918, 0x01EC9AB7, 0xA8834F9A, 0x65E6956E, 0x7EAAFFE6, 0x0821BCCF, 0xE6EF15E8, - 0xD9BAE79B, 0xCE4A6F36, 0xD4EA9F09, 0xD629B07C, 0xAF31A4B2, 0x312A3F23, 0x30C6A594, 0xC035A266, - 0x37744EBC, 0xA6FC82CA, 0xB0E090D0, 0x1533A7D8, 0x4AF10498, 0xF741ECDA, 0x0E7FCD50, 0x2F1791F6, - 0x8D764DD6, 0x4D43EFB0, 0x54CCAA4D, 0xDFE49604, 0xE39ED1B5, 0x1B4C6A88, 0xB8C12C1F, 0x7F466551, - 0x049D5EEA, 0x5D018C35, 0x73FA8774, 0x2EFB0B41, 0x5AB3671D, 0x5292DBD2, 0x33E91056, 0x136DD647, - 0x8C9AD761, 0x7A37A10C, 0x8E59F814, 0x89EB133C, 0xEECEA927, 0x35B761C9, 0xEDE11CE5, 0x3C7A47B1, - 0x599CD2DF, 0x3F55F273, 0x791814CE, 0xBF73C737, 0xEA53F7CD, 0x5B5FFDAA, 0x14DF3D6F, 0x867844DB, - 0x81CAAFF3, 0x3EB968C4, 0x2C382434, 0x5FC2A340, 0x72161DC3, 0x0CBCE225, 0x8B283C49, 0x41FF0D95, - 0x7139A801, 0xDE080CB3, 0x9CD8B4E4, 0x906456C1, 0x617BCB84, 0x70D532B6, 0x74486C5C, 0x42D0B857 - ); - - /** - * Precomputed invMixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $dt2 = array( - 0xA75051F4, 0x65537E41, 0xA4C31A17, 0x5E963A27, 0x6BCB3BAB, 0x45F11F9D, 0x58ABACFA, 0x03934BE3, - 0xFA552030, 0x6DF6AD76, 0x769188CC, 0x4C25F502, 0xD7FC4FE5, 0xCBD7C52A, 0x44802635, 0xA38FB562, - 0x5A49DEB1, 0x1B6725BA, 0x0E9845EA, 0xC0E15DFE, 0x7502C32F, 0xF012814C, 0x97A38D46, 0xF9C66BD3, - 0x5FE7038F, 0x9C951592, 0x7AEBBF6D, 0x59DA9552, 0x832DD4BE, 0x21D35874, 0x692949E0, 0xC8448EC9, - 0x896A75C2, 0x7978F48E, 0x3E6B9958, 0x71DD27B9, 0x4FB6BEE1, 0xAD17F088, 0xAC66C920, 0x3AB47DCE, - 0x4A1863DF, 0x3182E51A, 0x33609751, 0x7F456253, 0x77E0B164, 0xAE84BB6B, 0xA01CFE81, 0x2B94F908, - 0x68587048, 0xFD198F45, 0x6C8794DE, 0xF8B7527B, 0xD323AB73, 0x02E2724B, 0x8F57E31F, 0xAB2A6655, - 0x2807B2EB, 0xC2032FB5, 0x7B9A86C5, 0x08A5D337, 0x87F23028, 0xA5B223BF, 0x6ABA0203, 0x825CED16, - 0x1C2B8ACF, 0xB492A779, 0xF2F0F307, 0xE2A14E69, 0xF4CD65DA, 0xBED50605, 0x621FD134, 0xFE8AC4A6, - 0x539D342E, 0x55A0A2F3, 0xE132058A, 0xEB75A4F6, 0xEC390B83, 0xEFAA4060, 0x9F065E71, 0x1051BD6E, - 0x8AF93E21, 0x063D96DD, 0x05AEDD3E, 0xBD464DE6, 0x8DB59154, 0x5D0571C4, 0xD46F0406, 0x15FF6050, - 0xFB241998, 0xE997D6BD, 0x43CC8940, 0x9E7767D9, 0x42BDB0E8, 0x8B880789, 0x5B38E719, 0xEEDB79C8, - 0x0A47A17C, 0x0FE97C42, 0x1EC9F884, 0x00000000, 0x86830980, 0xED48322B, 0x70AC1E11, 0x724E6C5A, - 0xFFFBFD0E, 0x38560F85, 0xD51E3DAE, 0x3927362D, 0xD9640A0F, 0xA621685C, 0x54D19B5B, 0x2E3A2436, - 0x67B10C0A, 0xE70F9357, 0x96D2B4EE, 0x919E1B9B, 0xC54F80C0, 0x20A261DC, 0x4B695A77, 0x1A161C12, - 0xBA0AE293, 0x2AE5C0A0, 0xE0433C22, 0x171D121B, 0x0D0B0E09, 0xC7ADF28B, 0xA8B92DB6, 0xA9C8141E, - 0x198557F1, 0x074CAF75, 0xDDBBEE99, 0x60FDA37F, 0x269FF701, 0xF5BC5C72, 0x3BC54466, 0x7E345BFB, - 0x29768B43, 0xC6DCCB23, 0xFC68B6ED, 0xF163B8E4, 0xDCCAD731, 0x85104263, 0x22401397, 0x112084C6, - 0x247D854A, 0x3DF8D2BB, 0x3211AEF9, 0xA16DC729, 0x2F4B1D9E, 0x30F3DCB2, 0x52EC0D86, 0xE3D077C1, - 0x166C2BB3, 0xB999A970, 0x48FA1194, 0x642247E9, 0x8CC4A8FC, 0x3F1AA0F0, 0x2CD8567D, 0x90EF2233, - 0x4EC78749, 0xD1C1D938, 0xA2FE8CCA, 0x0B3698D4, 0x81CFA6F5, 0xDE28A57A, 0x8E26DAB7, 0xBFA43FAD, - 0x9DE42C3A, 0x920D5078, 0xCC9B6A5F, 0x4662547E, 0x13C2F68D, 0xB8E890D8, 0xF75E2E39, 0xAFF582C3, - 0x80BE9F5D, 0x937C69D0, 0x2DA96FD5, 0x12B3CF25, 0x993BC8AC, 0x7DA71018, 0x636EE89C, 0xBB7BDB3B, - 0x7809CD26, 0x18F46E59, 0xB701EC9A, 0x9AA8834F, 0x6E65E695, 0xE67EAAFF, 0xCF0821BC, 0xE8E6EF15, - 0x9BD9BAE7, 0x36CE4A6F, 0x09D4EA9F, 0x7CD629B0, 0xB2AF31A4, 0x23312A3F, 0x9430C6A5, 0x66C035A2, - 0xBC37744E, 0xCAA6FC82, 0xD0B0E090, 0xD81533A7, 0x984AF104, 0xDAF741EC, 0x500E7FCD, 0xF62F1791, - 0xD68D764D, 0xB04D43EF, 0x4D54CCAA, 0x04DFE496, 0xB5E39ED1, 0x881B4C6A, 0x1FB8C12C, 0x517F4665, - 0xEA049D5E, 0x355D018C, 0x7473FA87, 0x412EFB0B, 0x1D5AB367, 0xD25292DB, 0x5633E910, 0x47136DD6, - 0x618C9AD7, 0x0C7A37A1, 0x148E59F8, 0x3C89EB13, 0x27EECEA9, 0xC935B761, 0xE5EDE11C, 0xB13C7A47, - 0xDF599CD2, 0x733F55F2, 0xCE791814, 0x37BF73C7, 0xCDEA53F7, 0xAA5B5FFD, 0x6F14DF3D, 0xDB867844, - 0xF381CAAF, 0xC43EB968, 0x342C3824, 0x405FC2A3, 0xC372161D, 0x250CBCE2, 0x498B283C, 0x9541FF0D, - 0x017139A8, 0xB3DE080C, 0xE49CD8B4, 0xC1906456, 0x84617BCB, 0xB670D532, 0x5C74486C, 0x5742D0B8 - ); - - /** - * Precomputed invMixColumns table - * - * @see Rijndael:_encryptBlock() - * @see Rijndael:_decryptBlock() - * @var Array - * @access private - */ - var $dt3 = array( - 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, - 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, - 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, - 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, - 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, - 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, - 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, - 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, - 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, - 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, - 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, - 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, - 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, - 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, - 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, - 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, - 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, - 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, - 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, - 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, - 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, - 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, - 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, - 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, - 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, - 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, - 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, - 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, - 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, - 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, - 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, - 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 - ); - - /** - * The SubByte S-Box - * - * @see Rijndael::_encryptBlock() - * @var Array - * @access private - */ - var $sbox = array( - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 - ); - - /** - * The inverse SubByte S-Box - * - * @see Rijndael::_decryptBlock() - * @var Array - * @access private - */ - var $isbox = array( - 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D - ); - /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. - * - * $mode could be: - * - * - CRYPT_RIJNDAEL_MODE_ECB - * - * - CRYPT_RIJNDAEL_MODE_CBC - * - * - CRYPT_RIJNDAEL_MODE_CTR - * - * - CRYPT_RIJNDAEL_MODE_CFB - * - * - CRYPT_RIJNDAEL_MODE_OFB - * - * If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used. - * - * @see Base::Base() - * @param optional Integer $mode - * @access public - */ - - function __construct($mode = CRYPT_RIJNDAEL_MODE_CBC) - { - parent::__construct($mode); - } - - /** - * Sets the key. - * - * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and - * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length - * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the - * excess bits. - * - * If the key is not explicitly set, it'll be assumed to be all null bytes. - * - * Note: 160/224-bit keys must explicitly set by setKeyLength(), otherwise they will be round/pad up to 192/256 bits. - * - * @see Base:setKey() - * @see setKeyLength() + * @param int $mode * @access public - * @param String $key + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ - function setKey($key) + function __construct($mode) { - parent::setKey($key); - - if (!$this->explicit_key_length) { - $length = strlen($key); - switch (true) { - case $length <= 16: - $this->key_size = 16; - break; - case $length <= 24: - $this->key_size = 24; - break; - default: - $this->key_size = 32; - } - $this->_setupEngine(); + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); } + + parent::__construct($mode); } /** - * Sets the key length + * Sets the key length. * - * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * Valid key lengths are 128, 160, 192, 224, and 256. * * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to @@ -749,147 +197,132 @@ function setKey($key) * you should not setKeyLength(160) or setKeyLength(224). * * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use - * the mcrypt php extention, even if available. + * the mcrypt php extension, even if available. * This results then in slower encryption. * * @access public - * @param Integer $length + * @throws \LengthException if the key length is invalid + * @param int $length */ function setKeyLength($length) { - switch (true) { - case $length == 160: - $this->key_size = 20; - break; - case $length == 224: - $this->key_size = 28; - break; - case $length <= 128: - $this->key_size = 16; - break; - case $length <= 192: - $this->key_size = 24; + switch ($length) { + case 128: + case 160: + case 192: + case 224: + case 256: + $this->key_length = $length >> 3; break; default: - $this->key_size = 32; + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); } - $this->explicit_key_length = true; - $this->changed = true; - $this->_setupEngine(); + parent::setKeyLength($length); } /** - * Sets the block length + * Sets the key. * - * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * Rijndael supports five different key lengths * + * @see setKeyLength() * @access public - * @param Integer $length + * @param string $key + * @throws \LengthException if the key length isn't supported */ - function setBlockLength($length) + function setKey($key) { - $length >>= 5; - if ($length > 8) { - $length = 8; - } else if ($length < 4) { - $length = 4; + switch (strlen($key)) { + case 16: + case 20: + case 24: + case 28: + case 32: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported'); } - $this->Nb = $length; - $this->block_size = $length << 2; - $this->changed = true; - $this->_setupEngine(); + + parent::setKey($key); } /** - * Setup the fastest possible $engine - * - * Determines if the mcrypt (MODE_MCRYPT) $engine available - * and usable for the current $block_size and $key_size. + * Sets the block length * - * If not, the slower MODE_INTERNAL $engine will be set. + * Valid block lengths are 128, 160, 192, 224, and 256. * - * @see setKey() - * @see setKeyLength() - * @see setBlockLength() - * @access private + * @access public + * @param int $length */ - function _setupEngine() + function setBlockLength($length) { - if (@constant('CRYPT_' . $this->const_namespace . '_MODE') == CRYPT_MODE_INTERNAL) { - // No mcrypt support at all for rijndael - return; - } - - // The required mcrypt module name for the current $block_size of rijndael - $cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); - - // Determining the availibility/usability of $cipher_name_mcrypt - switch (true) { - case $this->key_size % 8: // mcrypt is not usable for 160/224-bit keys, only for 128/192/256-bit keys - case !in_array($cipher_name_mcrypt, mcrypt_list_algorithms()): // $cipher_name_mcrypt is not available for the current $block_size - $engine = CRYPT_MODE_INTERNAL; + switch ($length) { + case 128: + case 160: + case 192: + case 224: + case 256: break; default: - $engine = CRYPT_MODE_MCRYPT; - } - - if ($this->engine == $engine && $this->cipher_name_mcrypt == $cipher_name_mcrypt) { - // allready set, so we not unnecessary close $this->enmcrypt/demcrypt/ecb - return; + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); } - // Set the $engine - $this->engine = $engine; - $this->cipher_name_mcrypt = $cipher_name_mcrypt; - - if ($this->enmcrypt) { - // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, - // (re)open them with the module named in $this->cipher_name_mcrypt - mcrypt_module_close($this->enmcrypt); - mcrypt_module_close($this->demcrypt); - $this->enmcrypt = null; - $this->demcrypt = null; - - if ($this->ecb) { - mcrypt_module_close($this->ecb); - $this->ecb = null; - } - } + $this->Nb = $length >> 5; + $this->block_size = $length >> 3; + $this->changed = true; + $this->_setEngine(); } /** - * Setup the CRYPT_MODE_MCRYPT $engine + * Test for engine validity * - * @see Base::_setupMcrypt() - * @access private + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool */ - function _setupMcrypt() + function isValidEngine($engine) { - $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0"); - parent::_setupMcrypt(); + switch ($engine) { + case self::ENGINE_OPENSSL: + if ($this->block_size != 16) { + return false; + } + $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb'; + $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode(); + break; + case self::ENGINE_MCRYPT: + $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); + if ($this->key_length % 8) { // is it a 160/224-bit key? + // mcrypt is not usable for them, only for 128/192/256-bit keys + return false; + } + } + + return parent::isValidEngine($engine); } /** * Encrypts a block * * @access private - * @param String $in - * @return String + * @param string $in + * @return string */ function _encryptBlock($in) { - static $t0, $t1, $t2, $t3, $sbox; - if (!$t0) { - for ($i = 0; $i < 256; ++$i) { - $t0[] = (int)$this->t0[$i]; - $t1[] = (int)$this->t1[$i]; - $t2[] = (int)$this->t2[$i]; - $t3[] = (int)$this->t3[$i]; - $sbox[] = (int)$this->sbox[$i]; - } + static $tables; + if (empty($tables)) { + $tables = &$this->_getTables(); } + $t0 = $tables[0]; + $t1 = $tables[1]; + $t2 = $tables[2]; + $t3 = $tables[3]; + $sbox = $tables[4]; $state = array(); $words = unpack('N*', $in); @@ -900,9 +333,9 @@ function _encryptBlock($in) $Nr = $this->Nr; // addRoundKey - $i = -1; + $wc = $Nb - 1; foreach ($words as $word) { - $state[] = $word ^ $w[0][++$i]; + $state[] = $word ^ $w[++$wc]; } // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - @@ -925,7 +358,7 @@ function _encryptBlock($in) $t1[$state[$j] >> 16 & 0x000000FF] ^ $t2[$state[$k] >> 8 & 0x000000FF] ^ $t3[$state[$l] & 0x000000FF] ^ - $w[$round][$i]; + $w[++$wc]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -952,7 +385,7 @@ function _encryptBlock($in) ($state[$j] & 0x00FF0000) ^ ($state[$k] & 0x0000FF00) ^ ($state[$l] & 0x000000FF) ^ - $w[$Nr][$i]; + $w[$i]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -977,21 +410,20 @@ function _encryptBlock($in) * Decrypts a block * * @access private - * @param String $in - * @return String + * @param string $in + * @return string */ function _decryptBlock($in) { - static $dt0, $dt1, $dt2, $dt3, $isbox; - if (!$dt0) { - for ($i = 0; $i < 256; ++$i) { - $dt0[] = (int)$this->dt0[$i]; - $dt1[] = (int)$this->dt1[$i]; - $dt2[] = (int)$this->dt2[$i]; - $dt3[] = (int)$this->dt3[$i]; - $isbox[] = (int)$this->isbox[$i]; - } + static $invtables; + if (empty($invtables)) { + $invtables = &$this->_getInvTables(); } + $dt0 = $invtables[0]; + $dt1 = $invtables[1]; + $dt2 = $invtables[2]; + $dt3 = $invtables[3]; + $isbox = $invtables[4]; $state = array(); $words = unpack('N*', $in); @@ -1002,9 +434,9 @@ function _decryptBlock($in) $Nr = $this->Nr; // addRoundKey - $i = -1; + $wc = $Nb - 1; foreach ($words as $word) { - $state[] = $word ^ $dw[$Nr][++$i]; + $state[] = $word ^ $dw[++$wc]; } $temp = array(); @@ -1019,7 +451,7 @@ function _decryptBlock($in) $dt1[$state[$j] >> 16 & 0x000000FF] ^ $dt2[$state[$k] >> 8 & 0x000000FF] ^ $dt3[$state[$l] & 0x000000FF] ^ - $dw[$round][$i]; + $dw[++$wc]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -1040,10 +472,10 @@ function _decryptBlock($in) ($state[$k] & 0x0000FF00) | ($state[$l] & 0x000000FF); - $temp[$i] = $dw[0][$i] ^ ($isbox[$word & 0x000000FF] | - ($isbox[$word >> 8 & 0x000000FF] << 8) | - ($isbox[$word >> 16 & 0x000000FF] << 16) | - ($isbox[$word >> 24 & 0x000000FF] << 24)); + $temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] | + ($isbox[$word >> 8 & 0x000000FF] << 8) | + ($isbox[$word >> 16 & 0x000000FF] << 16) | + ($isbox[$word >> 24 & 0x000000FF] << 24)); ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -1067,7 +499,7 @@ function _decryptBlock($in) /** * Setup the key (expansion) * - * @see Base::_setupKey() + * @see \phpseclib\Crypt\Base::_setupKey() * @access private */ function _setupKey() @@ -1083,15 +515,13 @@ function _setupKey() 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 ); - $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0"); - - if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_size === $this->kl['key_size'] && $this->block_size === $this->kl['block_size']) { + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) { // already expanded return; } - $this->kl = array('key' => $this->key, 'key_size' => $this->key_size, 'block_size' => $this->block_size); + $this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size); - $this->Nk = $this->key_size >> 2; + $this->Nk = $this->key_length >> 2; // see Rijndael-ammended.pdf#page=44 $this->Nr = max($this->Nk, $this->Nb) + 6; @@ -1124,7 +554,7 @@ function _setupKey() // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is. $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk]; - } else if ($this->Nk > 6 && $i % $this->Nk == 4) { + } elseif ($this->Nk > 6 && $i % $this->Nk == 4) { $temp = $this->_subWord($temp); } $w[$i] = $w[$i - $this->Nk] ^ $temp; @@ -1137,6 +567,7 @@ function _setupKey() // 1. Apply the Key Expansion. // 2. Apply InvMixColumn to all Round Keys except the first and the last one." // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" + list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables(); $temp = $this->w = $this->dw = array(); for ($i = $row = $col = 0; $i < $length; $i++, $col++) { if ($col == $this->Nb) { @@ -1147,10 +578,10 @@ function _setupKey() $j = 0; while ($j < $this->Nb) { $dw = $this->_subWord($this->w[$row][$j]); - $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^ - $this->dt1[$dw >> 16 & 0x000000FF] ^ - $this->dt2[$dw >> 8 & 0x000000FF] ^ - $this->dt3[$dw & 0x000000FF]; + $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^ + $dt1[$dw >> 16 & 0x000000FF] ^ + $dt2[$dw >> 8 & 0x000000FF] ^ + $dt3[$dw & 0x000000FF]; $j++; } $this->dw[$row] = $temp; @@ -1164,31 +595,32 @@ function _setupKey() $this->dw[$row] = $this->w[$row]; - // In case of $this->use_inline_crypt === true we have to use 1-dim key arrays (both ascending) - if ($this->use_inline_crypt) { - $this->dw = array_reverse($this->dw); - $w = array_pop($this->w); - $dw = array_pop($this->dw); - foreach ($this->w as $r => $wr) { - foreach ($wr as $c => $wc) { - $w[] = $wc; - $dw[] = $this->dw[$r][$c]; - } + // Converting to 1-dim key arrays (both ascending) + $this->dw = array_reverse($this->dw); + $w = array_pop($this->w); + $dw = array_pop($this->dw); + foreach ($this->w as $r => $wr) { + foreach ($wr as $c => $wc) { + $w[] = $wc; + $dw[] = $this->dw[$r][$c]; } - $this->w = $w; - $this->dw = $dw; } + $this->w = $w; + $this->dw = $dw; } /** * Performs S-Box substitutions * * @access private - * @param Integer $word + * @param int $word */ function _subWord($word) { - $sbox = $this->sbox; + static $sbox; + if (empty($sbox)) { + list(, , , , $sbox) = $this->_getTables(); + } return $sbox[$word & 0x000000FF] | ($sbox[$word >> 8 & 0x000000FF] << 8) | @@ -1196,10 +628,183 @@ function _subWord($word) ($sbox[$word >> 24 & 0x000000FF] << 24); } + /** + * Provides the mixColumns and sboxes tables + * + * @see self::_encryptBlock() + * @see self::_setupInlineCrypt() + * @see self::_subWord() + * @access private + * @return array &$tables + */ + function &_getTables() + { + static $tables; + if (empty($tables)) { + // according to (section 5.2.1), + // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so + // those are the names we'll use. + $t3 = array_map('intval', array( + // with array_map('intval', ...) we ensure we have only int's and not + // some slower floats converted by php automatically on high values + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C + )); + + foreach ($t3 as $t3i) { + $t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >> 8) & 0x00FFFFFF); + $t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF); + $t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF); + } + + $tables = array( + // The Precomputed mixColumns tables t0 - t3 + $t0, + $t1, + $t2, + $t3, + // The SubByte S-Box + array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + ) + ); + } + return $tables; + } + + /** + * Provides the inverse mixColumns and inverse sboxes tables + * + * @see self::_decryptBlock() + * @see self::_setupInlineCrypt() + * @see self::_setupKey() + * @access private + * @return array &$tables + */ + function &_getInvTables() + { + static $tables; + if (empty($tables)) { + $dt3 = array_map('intval', array( + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, + 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, + 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, + 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, + 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, + 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, + 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, + 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, + 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, + 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, + 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, + 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, + 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, + 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, + 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, + 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, + 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, + 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, + 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, + 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, + 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, + 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, + 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, + 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, + 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, + 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, + 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, + 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, + 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, + 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 + )); + + foreach ($dt3 as $dt3i) { + $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF); + $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF); + $dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF); + }; + + $tables = array( + // The Precomputed inverse mixColumns tables dt0 - dt3 + $dt0, + $dt1, + $dt2, + $dt3, + // The inverse SubByte S-Box + array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D + ) + ); + } + return $tables; + } + /** * Setup the performance-optimized function for de/encrypt() * - * @see Base::_setupInlineCrypt() + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() * @access private */ function _setupInlineCrypt() @@ -1208,44 +813,52 @@ function _setupInlineCrypt() // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt(). // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible. - $lambda_functions =& Rijndael::_getLambdaFunctions(); - - // The first 10 generated $lambda_functions will use the key-words hardcoded for better performance. - // For memory reason we limit those ultra-optimized functions. - // After that, we use pure (extracted) integer vars for the key-words which is faster than accessing them via array. - if (count($lambda_functions) < 10) { - $w = $this->w; - $dw = $this->dw; - $init_encrypt = ''; - $init_decrypt = ''; - } else { - for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { - $w[] = '$w[' . $i . ']'; - $dw[] = '$dw[' . $i . ']'; - } - $init_encrypt = '$w = $self->w;'; - $init_decrypt = '$dw = $self->dw;'; - } + $lambda_functions =& self::_getLambdaFunctions(); - $code_hash = md5(str_pad("Rijndael, {$this->mode}, {$this->block_size}, ", 32, "\0") . implode(',', $w)); + // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. + // (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit) + // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a uniqe hash for our generated code + $code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + // The hi-optimized $lambda_functions will use the key-words hardcoded for better performance. + $w = $this->w; + $dw = $this->dw; + $init_encrypt = ''; + $init_decrypt = ''; + break; + default: + for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { + $w[] = '$w[' . $i . ']'; + $dw[] = '$dw[' . $i . ']'; + } + $init_encrypt = '$w = $self->w;'; + $init_decrypt = '$dw = $self->dw;'; + } + $Nr = $this->Nr; $Nb = $this->Nb; $c = $this->c; // Generating encrypt code: $init_encrypt.= ' - static $t0, $t1, $t2, $t3, $sbox; - if (!$t0) { - for ($i = 0; $i < 256; ++$i) { - $t0[$i] = (int)$self->t0[$i]; - $t1[$i] = (int)$self->t1[$i]; - $t2[$i] = (int)$self->t2[$i]; - $t3[$i] = (int)$self->t3[$i]; - $sbox[$i] = (int)$self->sbox[$i]; - } + static $tables; + if (empty($tables)) { + $tables = &$self->_getTables(); } + $t0 = $tables[0]; + $t1 = $tables[1]; + $t2 = $tables[2]; + $t3 = $tables[3]; + $sbox = $tables[4]; '; $s = 'e'; @@ -1284,26 +897,25 @@ function _setupInlineCrypt() $encrypt_block .= '$in = pack("N*"'."\n"; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block.= ', - ($'.$e.$i .' & 0xFF000000) ^ - ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000) ^ - ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00) ^ - ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF) ^ + ($'.$e.$i .' & '.((int)0xFF000000).') ^ + ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^ + ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^ + ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^ '.$w[$i]."\n"; } $encrypt_block .= ');'; // Generating decrypt code: $init_decrypt.= ' - static $dt0, $dt1, $dt2, $dt3, $isbox; - if (!$dt0) { - for ($i = 0; $i < 256; ++$i) { - $dt0[$i] = (int)$self->dt0[$i]; - $dt1[$i] = (int)$self->dt1[$i]; - $dt2[$i] = (int)$self->dt2[$i]; - $dt3[$i] = (int)$self->dt3[$i]; - $isbox[$i] = (int)$self->isbox[$i]; - } + static $invtables; + if (empty($invtables)) { + $invtables = &$self->_getInvTables(); } + $dt0 = $invtables[0]; + $dt1 = $invtables[1]; + $dt2 = $invtables[2]; + $dt3 = $invtables[3]; + $isbox = $invtables[4]; '; $s = 'e'; @@ -1342,10 +954,10 @@ function _setupInlineCrypt() $decrypt_block .= '$in = pack("N*"'."\n"; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block.= ', - ($'.$e.$i. ' & 0xFF000000) ^ - ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000) ^ - ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00) ^ - ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF) ^ + ($'.$e.$i. ' & '.((int)0xFF000000).') ^ + ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^ + ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^ + ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^ '.$dw[$i]."\n"; } $decrypt_block .= ');'; diff --git a/src/phpseclib/Crypt/TripleDES.php b/src/phpseclib/Crypt/TripleDES.php old mode 100644 new mode 100755 index 5205dec8..29c6eb9d --- a/src/phpseclib/Crypt/TripleDES.php +++ b/src/phpseclib/Crypt/TripleDES.php @@ -5,14 +5,14 @@ * * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt). * - * PHP versions 4 and 5 + * PHP version 5 * * Here's a short example of how to use this library: * * setKey('abcdefghijklmnopqrstuvwx'); * @@ -26,99 +26,64 @@ * ?> * * - * LICENSE: 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. - * * @category Crypt * @package TripleDES * @author Jim Wigginton - * @copyright MMVII Jim Wigginton + * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -/** - * Include DES - */ - -/** - * Encrypt / decrypt using inner chaining - * - * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3). - */ -@define('CRYPT_DES_MODE_3CBC', -2); - -/** - * Encrypt / decrypt using outer chaining - * - * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC. - */ -@define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC); - /** * Pure-PHP implementation of Triple DES. * * @package TripleDES * @author Jim Wigginton - * @version 0.1.0 * @access public */ class TripleDES extends DES { /** - * The default password key_size used by setPassword() + * Encrypt / decrypt using inner chaining * - * @see DES::password_key_size - * @see Base::password_key_size - * @see Base::setPassword() - * @var Integer - * @access private + * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3). */ - var $password_key_size = 24; + const MODE_3CBC = -2; /** - * The default salt used by setPassword() + * Encrypt / decrypt using outer chaining * - * @see Base::password_default_salt - * @see Base::setPassword() - * @var String + * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib\Crypt\Base::MODE_CBC. + */ + const MODE_CBC3 = Base::MODE_CBC; + + /** + * Key Length (in bytes) + * + * @see \phpseclib\Crypt\TripleDES::setKeyLength() + * @var int * @access private */ - var $password_default_salt = 'phpseclib'; + var $key_length = 24; /** - * The namespace used by the cipher for its constants. + * The default salt used by setPassword() * - * @see DES::const_namespace - * @see Base::const_namespace - * @var String + * @see \phpseclib\Crypt\Base::password_default_salt + * @see \phpseclib\Crypt\Base::setPassword() + * @var string * @access private */ - var $const_namespace = 'DES'; + var $password_default_salt = 'phpseclib'; /** * The mcrypt specific name of the cipher * - * @see DES::cipher_name_mcrypt - * @see Base::cipher_name_mcrypt - * @var String + * @see \phpseclib\Crypt\DES::cipher_name_mcrypt + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string * @access private */ var $cipher_name_mcrypt = 'tripledes'; @@ -126,8 +91,8 @@ class TripleDES extends DES /** * Optimizing value while CFB-encrypting * - * @see Base::cfb_init_len - * @var Integer + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int * @access private */ var $cfb_init_len = 750; @@ -135,27 +100,27 @@ class TripleDES extends DES /** * max possible size of $key * - * @see TripleDES::setKey() - * @see DES::setKey() - * @var String + * @see self::setKey() + * @see \phpseclib\Crypt\DES::setKey() + * @var string * @access private */ - var $key_size_max = 24; + var $key_length_max = 24; /** - * Internal flag whether using CRYPT_DES_MODE_3CBC or not + * Internal flag whether using self::MODE_3CBC or not * - * @var Boolean + * @var bool * @access private */ var $mode_3cbc; /** - * The DES objects + * The \phpseclib\Crypt\DES objects * * Used only if $mode_3cbc === true * - * @var Array + * @var array * @access private */ var $des; @@ -163,65 +128,83 @@ class TripleDES extends DES /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. + * Determines whether or not the mcrypt or OpenSSL extensions should be used. * * $mode could be: * - * - CRYPT_DES_MODE_ECB - * - * - CRYPT_DES_MODE_CBC + * - \phpseclib\Crypt\Base::MODE_ECB * - * - CRYPT_DES_MODE_CTR + * - \phpseclib\Crypt\Base::MODE_CBC * - * - CRYPT_DES_MODE_CFB + * - \phpseclib\Crypt\Base::MODE_CTR * - * - CRYPT_DES_MODE_OFB + * - \phpseclib\Crypt\Base::MODE_CFB * - * - CRYPT_DES_MODE_3CBC + * - \phpseclib\Crypt\Base::MODE_OFB * - * If not explictly set, CRYPT_DES_MODE_CBC will be used. + * - \phpseclib\Crypt\TripleDES::MODE_3CB * - * @see DES::DES() - * @see Base::Base() - * @param optional Integer $mode + * @see \phpseclib\Crypt\DES::__construct() + * @see \phpseclib\Crypt\Base::__construct() + * @param int $mode * @access public */ - function TripleDES($mode = CRYPT_DES_MODE_CBC) + function __construct($mode) { switch ($mode) { - // In case of CRYPT_DES_MODE_3CBC, we init as CRYPT_DES_MODE_CBC + // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC // and additional flag us internally as 3CBC - case CRYPT_DES_MODE_3CBC: - parent::DES(CRYPT_DES_MODE_CBC); + case self::MODE_3CBC: + parent::__construct(Base::MODE_CBC); $this->mode_3cbc = true; // This three $des'es will do the 3CBC work (if $key > 64bits) $this->des = array( - new DES(CRYPT_DES_MODE_CBC), - new DES(CRYPT_DES_MODE_CBC), - new DES(CRYPT_DES_MODE_CBC), + new DES(Base::MODE_CBC), + new DES(Base::MODE_CBC), + new DES(Base::MODE_CBC), ); - // we're going to be doing the padding, ourselves, so disable it in the DES objects + // we're going to be doing the padding, ourselves, so disable it in the \phpseclib\Crypt\DES objects $this->des[0]->disablePadding(); $this->des[1]->disablePadding(); $this->des[2]->disablePadding(); break; // If not 3CBC, we init as usual default: - parent::DES($mode); + parent::__construct($mode); + } + } + + /** + * Test for engine validity + * + * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * + * @see \phpseclib\Crypt\Base::__construct() + * @param int $engine + * @access public + * @return bool + */ + function isValidEngine($engine) + { + if ($engine == self::ENGINE_OPENSSL) { + $this->cipher_name_openssl_ecb = 'des-ede3'; + $mode = $this->_openssl_translate_mode(); + $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode; } + + return parent::isValidEngine($engine); } /** - * Sets the initialization vector. (optional) + * Sets the initialization vector. * - * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed - * to be all zero's. + * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. * - * @see Base::setIV() + * @see \phpseclib\Crypt\Base::setIV() * @access public - * @param String $iv + * @param string $iv */ function setIV($iv) { @@ -233,39 +216,66 @@ function setIV($iv) } } + /** + * Sets the key length. + * + * Valid key lengths are 128 and 192 bits. + * + * If you want to use a 64-bit key use DES.php + * + * @see \phpseclib\Crypt\Base:setKeyLength() + * @access public + * @throws \LengthException if the key length is invalid + * @param int $length + */ + function setKeyLength($length) + { + switch ($length) { + case 128: + case 192: + break; + default: + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported'); + } + + parent::setKeyLength($length); + } + /** * Sets the key. * - * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or - * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate. + * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * - * If the key is not explicitly set, it'll be assumed to be all null bytes. - * * @access public - * @see DES::setKey() - * @see Base::setKey() - * @param String $key + * @see \phpseclib\Crypt\DES::setKey() + * @see \phpseclib\Crypt\Base::setKey() + * @throws \LengthException if the key length is invalid + * @param string $key */ function setKey($key) { - $length = strlen($key); - if ($length > 8) { - $key = str_pad(substr($key, 0, 24), 24, chr(0)); - // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: - // http://php.net/function.mcrypt-encrypt#47973 - //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); - } else { - $key = str_pad($key, 8, chr(0)); + if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { + throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); } - parent::setKey($key); - // And in case of CRYPT_DES_MODE_3CBC: - // if key <= 64bits we not need the 3 $des to work, - // because we will then act as regular DES-CBC with just a <= 64bit key. - // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. - if ($this->mode_3cbc && $length > 8) { + switch (strlen($key)) { + case 16: + $key.= substr($key, 0, 8); + case 24: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported'); + } + + // copied from Base::setKey() + $this->key = $key; + $this->key_length = strlen($key); + $this->changed = true; + $this->_setEngine(); + + if ($this->mode_3cbc) { $this->des[0]->setKey(substr($key, 0, 8)); $this->des[1]->setKey(substr($key, 8, 8)); $this->des[2]->setKey(substr($key, 16, 8)); @@ -275,21 +285,25 @@ function setKey($key) /** * Encrypts a message. * - * @see Base::encrypt() + * @see \phpseclib\Crypt\Base::encrypt() * @access public - * @param String $plaintext - * @return String $cipertext + * @param string $plaintext + * @return string $cipertext */ function encrypt($plaintext) { // parent::en/decrypt() is able to do all the work for all modes and keylengths, - // except for: CRYPT_DES_MODE_3CBC (inner chaining CBC) with a key > 64bits + // except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits // if the key is smaller then 8, do what we'd normally do if ($this->mode_3cbc && strlen($this->key) > 8) { return $this->des[2]->encrypt( - $this->des[1]->decrypt( - $this->des[0]->encrypt($this->_pad($plaintext)))); + $this->des[1]->decrypt( + $this->des[0]->encrypt( + $this->_pad($plaintext) + ) + ) + ); } return parent::encrypt($plaintext); @@ -298,17 +312,23 @@ function encrypt($plaintext) /** * Decrypts a message. * - * @see Base::decrypt() + * @see \phpseclib\Crypt\Base::decrypt() * @access public - * @param String $ciphertext - * @return String $plaintext + * @param string $ciphertext + * @return string $plaintext */ function decrypt($ciphertext) { if ($this->mode_3cbc && strlen($this->key) > 8) { - return $this->_unpad($this->des[0]->decrypt( - $this->des[1]->encrypt( - $this->des[2]->decrypt(str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0"))))); + return $this->_unpad( + $this->des[0]->decrypt( + $this->des[1]->encrypt( + $this->des[2]->decrypt( + str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0") + ) + ) + ) + ); } return parent::decrypt($ciphertext); @@ -343,13 +363,13 @@ function decrypt($ciphertext) * outputs. The reason is due to the fact that the initialization vector's change after every encryption / * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. * - * Put another way, when the continuous buffer is enabled, the state of the DES() object changes after each + * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\DES() object changes after each * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * - * @see Base::enableContinuousBuffer() - * @see TripleDES::disableContinuousBuffer() + * @see \phpseclib\Crypt\Base::enableContinuousBuffer() + * @see self::disableContinuousBuffer() * @access public */ function enableContinuousBuffer() @@ -367,8 +387,8 @@ function enableContinuousBuffer() * * The default behavior. * - * @see Base::disableContinuousBuffer() - * @see TripleDES::enableContinuousBuffer() + * @see \phpseclib\Crypt\Base::disableContinuousBuffer() + * @see self::enableContinuousBuffer() * @access public */ function disableContinuousBuffer() @@ -384,8 +404,8 @@ function disableContinuousBuffer() /** * Creates the key schedule * - * @see DES::_setupKey() - * @see Base::_setupKey() + * @see \phpseclib\Crypt\DES::_setupKey() + * @see \phpseclib\Crypt\Base::_setupKey() * @access private */ function _setupKey() @@ -415,4 +435,24 @@ function _setupKey() // setup our key parent::_setupKey(); } + + /** + * Sets the internal crypt engine + * + * @see \phpseclib\Crypt\Base::__construct() + * @see \phpseclib\Crypt\Base::setPreferredEngine() + * @param int $engine + * @access public + * @return int + */ + function setPreferredEngine($engine) + { + if ($this->mode_3cbc) { + $this->des[0]->setPreferredEngine($engine); + $this->des[1]->setPreferredEngine($engine); + $this->des[2]->setPreferredEngine($engine); + } + + return parent::setPreferredEngine($engine); + } } diff --git a/src/phpseclib/Crypt/Twofish.php b/src/phpseclib/Crypt/Twofish.php new file mode 100755 index 00000000..e4d910db --- /dev/null +++ b/src/phpseclib/Crypt/Twofish.php @@ -0,0 +1,846 @@ + + * setKey('12345678901234567890123456789012'); + * + * $plaintext = str_repeat('a', 1024); + * + * echo $twofish->decrypt($twofish->encrypt($plaintext)); + * ?> + * + * + * @category Crypt + * @package Twofish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Crypt; + +/** + * Pure-PHP implementation of Twofish. + * + * @package Twofish + * @author Jim Wigginton + * @author Hans-Juergen Petrich + * @access public + */ +class Twofish extends Base +{ + /** + * The mcrypt specific name of the cipher + * + * @see \phpseclib\Crypt\Base::cipher_name_mcrypt + * @var string + * @access private + */ + var $cipher_name_mcrypt = 'twofish'; + + /** + * Optimizing value while CFB-encrypting + * + * @see \phpseclib\Crypt\Base::cfb_init_len + * @var int + * @access private + */ + var $cfb_init_len = 800; + + /** + * Q-Table + * + * @var array + * @access private + */ + var $q0 = array( + 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, + 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, + 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, + 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, + 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, + 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, + 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, + 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, + 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, + 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, + 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, + 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, + 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, + 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, + 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, + 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, + 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, + 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, + 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, + 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, + 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, + 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, + 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, + 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, + 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, + 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, + 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, + 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, + 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, + 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, + 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, + 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 + ); + + /** + * Q-Table + * + * @var array + * @access private + */ + var $q1 = array( + 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, + 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, + 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, + 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, + 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, + 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, + 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, + 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, + 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, + 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, + 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, + 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, + 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, + 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, + 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, + 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, + 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, + 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, + 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, + 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, + 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, + 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, + 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, + 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, + 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, + 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, + 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, + 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, + 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, + 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, + 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, + 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m0 = array( + 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, + 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, + 0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, + 0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F, + 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, + 0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, + 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3, + 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, + 0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, + 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C, + 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, + 0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, + 0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC, + 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, + 0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9, + 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17, + 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, + 0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, + 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149, + 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, + 0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, + 0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48, + 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, + 0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, + 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5, + 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, + 0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, + 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC, + 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, + 0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, + 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2, + 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91 + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m1 = array( + 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, + 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, + 0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, + 0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E, + 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, + 0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757, + 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D, + 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, + 0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, + 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B, + 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, + 0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, + 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D, + 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, + 0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282, + 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7, + 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, + 0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, + 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E, + 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, + 0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, + 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F, + 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, + 0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, + 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7, + 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, + 0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, + 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323, + 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, + 0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, + 0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000, + 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8 + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m2 = array( + 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, + 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, + 0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, + 0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE, + 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, + 0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, + 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065, + 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, + 0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, + 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF, + 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, + 0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, + 0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF, + 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, + 0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD, + 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC, + 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, + 0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, + 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101, + 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, + 0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, + 0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A, + 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, + 0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D, + 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6, + 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, + 0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, + 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB, + 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, + 0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, + 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746, + 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF + ); + + /** + * M-Table + * + * @var array + * @access private + */ + var $m3 = array( + 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, + 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, + 0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, + 0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A, + 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, + 0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216, + 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63, + 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, + 0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, + 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197, + 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, + 0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, + 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20, + 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, + 0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, + 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730, + 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, + 0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, + 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F, + 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, + 0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, + 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D, + 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, + 0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, + 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6, + 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, + 0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, + 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439, + 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, + 0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, + 0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000, + 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8 + ); + + /** + * The Key Schedule Array + * + * @var array + * @access private + */ + var $K = array(); + + /** + * The Key depended S-Table 0 + * + * @var array + * @access private + */ + var $S0 = array(); + + /** + * The Key depended S-Table 1 + * + * @var array + * @access private + */ + var $S1 = array(); + + /** + * The Key depended S-Table 2 + * + * @var array + * @access private + */ + var $S2 = array(); + + /** + * The Key depended S-Table 3 + * + * @var array + * @access private + */ + var $S3 = array(); + + /** + * Holds the last used key + * + * @var array + * @access private + */ + var $kl; + + /** + * The Key Length (in bytes) + * + * @see Crypt_Twofish::setKeyLength() + * @var int + * @access private + */ + var $key_length = 16; + + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + + /** + * Sets the key length. + * + * Valid key lengths are 128, 192 or 256 bits + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + switch ($length) { + case 128: + case 192: + case 256: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + + parent::setKeyLength($length); + } + + /** + * Sets the key. + * + * Rijndael supports five different key lengths + * + * @see setKeyLength() + * @access public + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + function setKey($key) + { + switch (strlen($key)) { + case 16: + case 24: + case 32: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + + parent::setKey($key); + } + + /** + * Setup the key (expansion) + * + * @see \phpseclib\Crypt\Base::_setupKey() + * @access private + */ + function _setupKey() + { + if (isset($this->kl['key']) && $this->key === $this->kl['key']) { + // already expanded + return; + } + $this->kl = array('key' => $this->key); + + /* Key expanding and generating the key-depended s-boxes */ + $le_longs = unpack('V*', $this->key); + $key = unpack('C*', $this->key); + $m0 = $this->m0; + $m1 = $this->m1; + $m2 = $this->m2; + $m3 = $this->m3; + $q0 = $this->q0; + $q1 = $this->q1; + + $K = $S0 = $S1 = $S2 = $S3 = array(); + + switch (strlen($this->key)) { + case 16: + list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; + } + break; + case 24: + list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]); + list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; + } + break; + default: // 32 + list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]); + list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]); + list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]); + list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]); + for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { + $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ + $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ + $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ + $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; + $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ + $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ + $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ + $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; + $B = ($B << 8) | ($B >> 24 & 0xff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + } + for ($i = 0; $i < 256; ++$i) { + $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; + $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; + $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; + $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; + } + } + + $this->K = $K; + $this->S0 = $S0; + $this->S1 = $S1; + $this->S2 = $S2; + $this->S3 = $S3; + } + + /** + * _mdsrem function using by the twofish cipher algorithm + * + * @access private + * @param string $A + * @param string $B + * @return array + */ + function _mdsrem($A, $B) + { + // No gain by unrolling this loop. + for ($i = 0; $i < 8; ++$i) { + // Get most significant coefficient. + $t = 0xff & ($B >> 24); + + // Shift the others up. + $B = ($B << 8) | (0xff & ($A >> 24)); + $A<<= 8; + + $u = $t << 1; + + // Subtract the modular polynomial on overflow. + if ($t & 0x80) { + $u^= 0x14d; + } + + // Remove t * (a * x^2 + 1). + $B ^= $t ^ ($u << 16); + + // Form u = a*t + t/a = t*(a + 1/a). + $u^= 0x7fffffff & ($t >> 1); + + // Add the modular polynomial on underflow. + if ($t & 0x01) { + $u^= 0xa6 ; + } + + // Remove t * (a + 1/a) * (x^3 + x). + $B^= ($u << 24) | ($u << 8); + } + + return array( + 0xff & $B >> 24, + 0xff & $B >> 16, + 0xff & $B >> 8, + 0xff & $B); + } + + /** + * Encrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _encryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + + $in = unpack("V4", $in); + $R0 = $K[0] ^ $in[1]; + $R1 = $K[1] ^ $in[2]; + $R2 = $K[2] ^ $in[3]; + $R3 = $K[3] ^ $in[4]; + + $ki = 7; + while ($ki < 39) { + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= $t0 + $t1 + $K[++$ki]; + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= ($t0 + $t1 + $K[++$ki]); + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); + } + + // @codingStandardsIgnoreStart + return pack("V4", $K[4] ^ $R2, + $K[5] ^ $R3, + $K[6] ^ $R0, + $K[7] ^ $R1); + // @codingStandardsIgnoreEnd + } + + /** + * Decrypts a block + * + * @access private + * @param string $in + * @return string + */ + function _decryptBlock($in) + { + $S0 = $this->S0; + $S1 = $this->S1; + $S2 = $this->S2; + $S3 = $this->S3; + $K = $this->K; + + $in = unpack("V4", $in); + $R0 = $K[4] ^ $in[1]; + $R1 = $K[5] ^ $in[2]; + $R2 = $K[6] ^ $in[3]; + $R3 = $K[7] ^ $in[4]; + + $ki = 40; + while ($ki > 8) { + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= $t0 + ($t1 << 1) + $K[--$ki]; + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + $K[--$ki]); + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= $t0 + ($t1 << 1) + $K[--$ki]; + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + $K[--$ki]); + } + + // @codingStandardsIgnoreStart + return pack("V4", $K[0] ^ $R2, + $K[1] ^ $R3, + $K[2] ^ $R0, + $K[3] ^ $R1); + // @codingStandardsIgnoreEnd + } + + /** + * Setup the performance-optimized function for de/encrypt() + * + * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @access private + */ + function _setupInlineCrypt() + { + $lambda_functions =& self::_getLambdaFunctions(); + + // Max. 10 Ultra-Hi-optimized inline-crypt functions. After that, we'll (still) create very fast code, but not the ultimate fast one. + // (Currently, for Crypt_Twofish, one generated $lambda_function cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit) + $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); + + // Generation of a uniqe hash for our generated code + $code_hash = "Crypt_Twofish, {$this->mode}"; + if ($gen_hi_opt_code) { + $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + } + + if (!isset($lambda_functions[$code_hash])) { + switch (true) { + case $gen_hi_opt_code: + $K = $this->K; + $init_crypt = ' + static $S0, $S1, $S2, $S3; + if (!$S0) { + for ($i = 0; $i < 256; ++$i) { + $S0[] = (int)$self->S0[$i]; + $S1[] = (int)$self->S1[$i]; + $S2[] = (int)$self->S2[$i]; + $S3[] = (int)$self->S3[$i]; + } + } + '; + break; + default: + $K = array(); + for ($i = 0; $i < 40; ++$i) { + $K[] = '$K_' . $i; + } + $init_crypt = ' + $S0 = $self->S0; + $S1 = $self->S1; + $S2 = $self->S2; + $S3 = $self->S3; + list(' . implode(',', $K) . ') = $self->K; + '; + } + + // Generating encrypt code: + $encrypt_block = ' + $in = unpack("V4", $in); + $R0 = '.$K[0].' ^ $in[1]; + $R1 = '.$K[1].' ^ $in[2]; + $R2 = '.$K[2].' ^ $in[3]; + $R3 = '.$K[3].' ^ $in[4]; + '; + for ($ki = 7, $i = 0; $i < 8; ++$i) { + $encrypt_block.= ' + $t0 = $S0[ $R0 & 0xff] ^ + $S1[($R0 >> 8) & 0xff] ^ + $S2[($R0 >> 16) & 0xff] ^ + $S3[($R0 >> 24) & 0xff]; + $t1 = $S0[($R1 >> 24) & 0xff] ^ + $S1[ $R1 & 0xff] ^ + $S2[($R1 >> 8) & 0xff] ^ + $S3[($R1 >> 16) & 0xff]; + $R2^= ($t0 + $t1 + '.$K[++$ki].'); + $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); + + $t0 = $S0[ $R2 & 0xff] ^ + $S1[($R2 >> 8) & 0xff] ^ + $S2[($R2 >> 16) & 0xff] ^ + $S3[($R2 >> 24) & 0xff]; + $t1 = $S0[($R3 >> 24) & 0xff] ^ + $S1[ $R3 & 0xff] ^ + $S2[($R3 >> 8) & 0xff] ^ + $S3[($R3 >> 16) & 0xff]; + $R0^= ($t0 + $t1 + '.$K[++$ki].'); + $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); + '; + } + $encrypt_block.= ' + $in = pack("V4", '.$K[4].' ^ $R2, + '.$K[5].' ^ $R3, + '.$K[6].' ^ $R0, + '.$K[7].' ^ $R1); + '; + + // Generating decrypt code: + $decrypt_block = ' + $in = unpack("V4", $in); + $R0 = '.$K[4].' ^ $in[1]; + $R1 = '.$K[5].' ^ $in[2]; + $R2 = '.$K[6].' ^ $in[3]; + $R3 = '.$K[7].' ^ $in[4]; + '; + for ($ki = 40, $i = 0; $i < 8; ++$i) { + $decrypt_block.= ' + $t0 = $S0[$R0 & 0xff] ^ + $S1[$R0 >> 8 & 0xff] ^ + $S2[$R0 >> 16 & 0xff] ^ + $S3[$R0 >> 24 & 0xff]; + $t1 = $S0[$R1 >> 24 & 0xff] ^ + $S1[$R1 & 0xff] ^ + $S2[$R1 >> 8 & 0xff] ^ + $S3[$R1 >> 16 & 0xff]; + $R3^= $t0 + ($t1 << 1) + '.$K[--$ki].'; + $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); + + $t0 = $S0[$R2 & 0xff] ^ + $S1[$R2 >> 8 & 0xff] ^ + $S2[$R2 >> 16 & 0xff] ^ + $S3[$R2 >> 24 & 0xff]; + $t1 = $S0[$R3 >> 24 & 0xff] ^ + $S1[$R3 & 0xff] ^ + $S2[$R3 >> 8 & 0xff] ^ + $S3[$R3 >> 16 & 0xff]; + $R1^= $t0 + ($t1 << 1) + '.$K[--$ki].'; + $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); + '; + } + $decrypt_block.= ' + $in = pack("V4", '.$K[0].' ^ $R2, + '.$K[1].' ^ $R3, + '.$K[2].' ^ $R0, + '.$K[3].' ^ $R1); + '; + + $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( + array( + 'init_crypt' => $init_crypt, + 'init_encrypt' => '', + 'init_decrypt' => '', + 'encrypt_block' => $encrypt_block, + 'decrypt_block' => $decrypt_block + ) + ); + } + $this->inline_crypt = $lambda_functions[$code_hash]; + } +} diff --git a/src/phpseclib/Exception/BadConfigurationException.php b/src/phpseclib/Exception/BadConfigurationException.php new file mode 100755 index 00000000..096148a0 --- /dev/null +++ b/src/phpseclib/Exception/BadConfigurationException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * BadConfigurationException + * + * @package BadConfigurationException + * @author Jim Wigginton + */ +class BadConfigurationException extends \RuntimeException +{ +} diff --git a/src/phpseclib/Exception/FileNotFoundException.php b/src/phpseclib/Exception/FileNotFoundException.php new file mode 100755 index 00000000..984edfcc --- /dev/null +++ b/src/phpseclib/Exception/FileNotFoundException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * FileNotFoundException + * + * @package FileNotFoundException + * @author Jim Wigginton + */ +class FileNotFoundException extends \RuntimeException +{ +} diff --git a/src/phpseclib/Exception/NoSupportedAlgorithmsException.php b/src/phpseclib/Exception/NoSupportedAlgorithmsException.php new file mode 100755 index 00000000..bca9a753 --- /dev/null +++ b/src/phpseclib/Exception/NoSupportedAlgorithmsException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * NoSupportedAlgorithmsException + * + * @package NoSupportedAlgorithmsException + * @author Jim Wigginton + */ +class NoSupportedAlgorithmsException extends \RuntimeException +{ +} diff --git a/src/phpseclib/Exception/UnsupportedAlgorithmException.php b/src/phpseclib/Exception/UnsupportedAlgorithmException.php new file mode 100755 index 00000000..47cc41d4 --- /dev/null +++ b/src/phpseclib/Exception/UnsupportedAlgorithmException.php @@ -0,0 +1,26 @@ + + * @copyright 2015 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Exception; + +/** + * UnsupportedAlgorithmException + * + * @package UnsupportedAlgorithmException + * @author Jim Wigginton + */ +class UnsupportedAlgorithmException extends \RuntimeException +{ +} diff --git a/src/phpseclib/File/ANSI.php b/src/phpseclib/File/ANSI.php new file mode 100755 index 00000000..1f3eecb3 --- /dev/null +++ b/src/phpseclib/File/ANSI.php @@ -0,0 +1,574 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File; + +/** + * Pure-PHP ANSI Decoder + * + * @package ANSI + * @author Jim Wigginton + * @access public + */ +class ANSI +{ + /** + * Max Width + * + * @var int + * @access private + */ + var $max_x; + + /** + * Max Height + * + * @var int + * @access private + */ + var $max_y; + + /** + * Max History + * + * @var int + * @access private + */ + var $max_history; + + /** + * History + * + * @var array + * @access private + */ + var $history; + + /** + * History Attributes + * + * @var array + * @access private + */ + var $history_attrs; + + /** + * Current Column + * + * @var int + * @access private + */ + var $x; + + /** + * Current Row + * + * @var int + * @access private + */ + var $y; + + /** + * Old Column + * + * @var int + * @access private + */ + var $old_x; + + /** + * Old Row + * + * @var int + * @access private + */ + var $old_y; + + /** + * An empty attribute cell + * + * @var object + * @access private + */ + var $base_attr_cell; + + /** + * The current attribute cell + * + * @var object + * @access private + */ + var $attr_cell; + + /** + * An empty attribute row + * + * @var array + * @access private + */ + var $attr_row; + + /** + * The current screen text + * + * @var array + * @access private + */ + var $screen; + + /** + * The current screen attributes + * + * @var array + * @access private + */ + var $attrs; + + /** + * Current ANSI code + * + * @var string + * @access private + */ + var $ansi; + + /** + * Tokenization + * + * @var array + * @access private + */ + var $tokenization; + + /** + * Default Constructor. + * + * @return \phpseclib\File\ANSI + * @access public + */ + function __construct() + { + $attr_cell = new \stdClass(); + $attr_cell->bold = false; + $attr_cell->underline = false; + $attr_cell->blink = false; + $attr_cell->background = 'black'; + $attr_cell->foreground = 'white'; + $attr_cell->reverse = false; + $this->base_attr_cell = clone $attr_cell; + $this->attr_cell = clone $attr_cell; + + $this->setHistory(200); + $this->setDimensions(80, 24); + } + + /** + * Set terminal width and height + * + * Resets the screen as well + * + * @param int $x + * @param int $y + * @access public + */ + function setDimensions($x, $y) + { + $this->max_x = $x - 1; + $this->max_y = $y - 1; + $this->x = $this->y = 0; + $this->history = $this->history_attrs = array(); + $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell); + $this->screen = array_fill(0, $this->max_y + 1, ''); + $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row); + $this->ansi = ''; + } + + /** + * Set the number of lines that should be logged past the terminal height + * + * @param int $x + * @param int $y + * @access public + */ + function setHistory($history) + { + $this->max_history = $history; + } + + /** + * Load a string + * + * @param string $source + * @access public + */ + function loadString($source) + { + $this->setDimensions($this->max_x + 1, $this->max_y + 1); + $this->appendString($source); + } + + /** + * Appdend a string + * + * @param string $source + * @access public + */ + function appendString($source) + { + $this->tokenization = array(''); + for ($i = 0; $i < strlen($source); $i++) { + if (strlen($this->ansi)) { + $this->ansi.= $source[$i]; + $chr = ord($source[$i]); + // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements + // single character CSI's not currently supported + switch (true) { + case $this->ansi == "\x1B=": + $this->ansi = ''; + continue 2; + case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['): + case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: + break; + default: + continue 2; + } + $this->tokenization[] = $this->ansi; + $this->tokenization[] = ''; + // http://ascii-table.com/ansi-escape-sequences-vt-100.php + switch ($this->ansi) { + case "\x1B[H": // Move cursor to upper left corner + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $this->y = 0; + break; + case "\x1B[J": // Clear screen from cursor down + $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); + $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); + + $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); + $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row)); + + if (count($this->history) == $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + case "\x1B[K": // Clear screen from cursor right + $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); + + array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - $this->x - 1, $this->base_attr_cell)); + break; + case "\x1B[2K": // Clear entire line + $this->screen[$this->y] = str_repeat(' ', $this->x); + $this->attrs[$this->y] = $this->attr_row; + break; + case "\x1B[?1h": // set cursor key to application + case "\x1B[?25h": // show the cursor + case "\x1B(B": // set united states g0 character set + break; + case "\x1BE": // Move to next line + $this->_newLine(); + $this->x = 0; + break; + default: + switch (true) { + case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines + $this->old_y = $this->y; + $this->y+= $match[1]; + break; + case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h + $this->old_x = $this->x; + $this->old_y = $this->y; + $this->x = $match[2] - 1; + $this->y = $match[1] - 1; + break; + case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines + $this->old_x = $this->x; + $this->x+= $match[1]; + break; + case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines + $this->old_x = $this->x; + $this->x-= $match[1]; + break; + case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window + break; + case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes + $attr_cell = &$this->attr_cell; + $mods = explode(';', $match[1]); + foreach ($mods as $mod) { + switch ($mod) { + case 0: // Turn off character attributes + $attr_cell = clone $this->base_attr_cell; + break; + case 1: // Turn bold mode on + $attr_cell->bold = true; + break; + case 4: // Turn underline mode on + $attr_cell->underline = true; + break; + case 5: // Turn blinking mode on + $attr_cell->blink = true; + break; + case 7: // Turn reverse video on + $attr_cell->reverse = !$attr_cell->reverse; + $temp = $attr_cell->background; + $attr_cell->background = $attr_cell->foreground; + $attr_cell->foreground = $temp; + break; + default: // set colors + //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground; + $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' }; + //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background; + $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; + switch ($mod) { + // @codingStandardsIgnoreStart + case 30: $front = 'black'; break; + case 31: $front = 'red'; break; + case 32: $front = 'green'; break; + case 33: $front = 'yellow'; break; + case 34: $front = 'blue'; break; + case 35: $front = 'magenta'; break; + case 36: $front = 'cyan'; break; + case 37: $front = 'white'; break; + + case 40: $back = 'black'; break; + case 41: $back = 'red'; break; + case 42: $back = 'green'; break; + case 43: $back = 'yellow'; break; + case 44: $back = 'blue'; break; + case 45: $back = 'magenta'; break; + case 46: $back = 'cyan'; break; + case 47: $back = 'white'; break; + // @codingStandardsIgnoreEnd + + default: + //user_error('Unsupported attribute: ' . $mod); + $this->ansi = ''; + break 2; + } + } + } + break; + default: + //user_error("{$this->ansi} is unsupported\r\n"); + } + } + $this->ansi = ''; + continue; + } + + $this->tokenization[count($this->tokenization) - 1].= $source[$i]; + switch ($source[$i]) { + case "\r": + $this->x = 0; + break; + case "\n": + $this->_newLine(); + break; + case "\x08": // backspace + if ($this->x) { + $this->x--; + $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell; + $this->screen[$this->y] = substr_replace( + $this->screen[$this->y], + $source[$i], + $this->x, + 1 + ); + } + break; + case "\x0F": // shift + break; + case "\x1B": // start ANSI escape code + $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1); + //if (!strlen($this->tokenization[count($this->tokenization) - 1])) { + // array_pop($this->tokenization); + //} + $this->ansi.= "\x1B"; + break; + default: + $this->attrs[$this->y][$this->x] = clone $this->attr_cell; + if ($this->x > strlen($this->screen[$this->y])) { + $this->screen[$this->y] = str_repeat(' ', $this->x); + } + $this->screen[$this->y] = substr_replace( + $this->screen[$this->y], + $source[$i], + $this->x, + 1 + ); + + if ($this->x > $this->max_x) { + $this->x = 0; + $this->y++; + } else { + $this->x++; + } + } + } + } + + /** + * Add a new line + * + * Also update the $this->screen and $this->history buffers + * + * @access private + */ + function _newLine() + { + //if ($this->y < $this->max_y) { + // $this->y++; + //} + + while ($this->y >= $this->max_y) { + $this->history = array_merge($this->history, array(array_shift($this->screen))); + $this->screen[] = ''; + + $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); + $this->attrs[] = $this->attr_row; + + if (count($this->history) >= $this->max_history) { + array_shift($this->history); + array_shift($this->history_attrs); + } + + $this->y--; + } + $this->y++; + } + + /** + * Returns the current coordinate without preformating + * + * @access private + * @return string + */ + function _processCoordinate($last_attr, $cur_attr, $char) + { + $output = ''; + + if ($last_attr != $cur_attr) { + $close = $open = ''; + if ($last_attr->foreground != $cur_attr->foreground) { + if ($cur_attr->foreground != 'white') { + $open.= ''; + } + if ($last_attr->foreground != 'white') { + $close = '' . $close; + } + } + if ($last_attr->background != $cur_attr->background) { + if ($cur_attr->background != 'black') { + $open.= ''; + } + if ($last_attr->background != 'black') { + $close = '' . $close; + } + } + if ($last_attr->bold != $cur_attr->bold) { + if ($cur_attr->bold) { + $open.= ''; + } else { + $close = '' . $close; + } + } + if ($last_attr->underline != $cur_attr->underline) { + if ($cur_attr->underline) { + $open.= ''; + } else { + $close = '' . $close; + } + } + if ($last_attr->blink != $cur_attr->blink) { + if ($cur_attr->blink) { + $open.= ''; + } else { + $close = '' . $close; + } + } + $output.= $close . $open; + } + + $output.= htmlspecialchars($char); + + return $output; + } + + /** + * Returns the current screen without preformating + * + * @access private + * @return string + */ + function _getScreen() + { + $output = ''; + $last_attr = $this->base_attr_cell; + for ($i = 0; $i <= $this->max_y; $i++) { + for ($j = 0; $j <= $this->max_x; $j++) { + $cur_attr = $this->attrs[$i][$j]; + $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : ''); + $last_attr = $this->attrs[$i][$j]; + } + $output.= "\r\n"; + } + $output = substr($output, 0, -2); + // close any remaining open tags + $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, ''); + return rtrim($output); + } + + /** + * Returns the current screen + * + * @access public + * @return string + */ + function getScreen() + { + return '
' . $this->_getScreen() . '
'; + } + + /** + * Returns the current screen and the x previous lines + * + * @access public + * @return string + */ + function getHistory() + { + $scrollback = ''; + $last_attr = $this->base_attr_cell; + for ($i = 0; $i < count($this->history); $i++) { + for ($j = 0; $j <= $this->max_x + 1; $j++) { + $cur_attr = $this->history_attrs[$i][$j]; + $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : ''); + $last_attr = $this->history_attrs[$i][$j]; + } + $scrollback.= "\r\n"; + } + $base_attr_cell = $this->base_attr_cell; + $this->base_attr_cell = $last_attr; + $scrollback.= $this->_getScreen(); + $this->base_attr_cell = $base_attr_cell; + + return '
' . $scrollback . '
'; + } +} diff --git a/src/phpseclib/File/ASN1.php b/src/phpseclib/File/ASN1.php old mode 100644 new mode 100755 index b6ac36b0..b3126b5d --- a/src/phpseclib/File/ASN1.php +++ b/src/phpseclib/File/ASN1.php @@ -3,159 +3,109 @@ /** * Pure-PHP ASN.1 Parser * - * PHP versions 4 and 5 + * PHP version 5 * * ASN.1 provides the semantics for data encoded using various schemes. The most commonly * utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded * DER blobs. * - * ASN1 decodes and encodes DER formatted messages and places them in a semantic context. + * \phpseclib\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context. * * Uses the 1988 ASN.1 syntax. * - * LICENSE: 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. - * * @category File * @package ASN1 * @author Jim Wigginton - * @copyright MMXII Jim Wigginton + * @copyright 2012 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\File; -/**#@+ - * Tag Classes - * - * @access private - * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 - */ +use ParagonIE\ConstantTime\Base64; +use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; -@define('FILE_ASN1_CLASS_UNIVERSAL', 0); -@define('FILE_ASN1_CLASS_APPLICATION', 1); -@define('FILE_ASN1_CLASS_CONTEXT_SPECIFIC', 2); -@define('FILE_ASN1_CLASS_PRIVATE', 3); -/**#@-*/ - -/**#@+ - * Tag Classes - * - * @access private - * @link http://www.obj-sys.com/asn1tutorial/node124.html - */ -@define('FILE_ASN1_TYPE_BOOLEAN', 1); -@define('FILE_ASN1_TYPE_INTEGER', 2); -@define('FILE_ASN1_TYPE_BIT_STRING', 3); -@define('FILE_ASN1_TYPE_OCTET_STRING', 4); -@define('FILE_ASN1_TYPE_NULL', 5); -@define('FILE_ASN1_TYPE_OBJECT_IDENTIFIER', 6); -//@define('FILE_ASN1_TYPE_OBJECT_DESCRIPTOR', 7); -//@define('FILE_ASN1_TYPE_INSTANCE_OF', 8); // EXTERNAL -@define('FILE_ASN1_TYPE_REAL', 9); -@define('FILE_ASN1_TYPE_ENUMERATED', 10); -//@define('FILE_ASN1_TYPE_EMBEDDED', 11); -@define('FILE_ASN1_TYPE_UTF8_STRING', 12); -//@define('FILE_ASN1_TYPE_RELATIVE_OID', 13); -@define('FILE_ASN1_TYPE_SEQUENCE', 16); // SEQUENCE OF -@define('FILE_ASN1_TYPE_SET', 17); // SET OF -/**#@-*/ -/**#@+ - * More Tag Classes - * - * @access private - * @link http://www.obj-sys.com/asn1tutorial/node10.html - */ -@define('FILE_ASN1_TYPE_NUMERIC_STRING', 18); -@define('FILE_ASN1_TYPE_PRINTABLE_STRING', 19); -@define('FILE_ASN1_TYPE_TELETEX_STRING', 20); // T61String -@define('FILE_ASN1_TYPE_VIDEOTEX_STRING', 21); -@define('FILE_ASN1_TYPE_IA5_STRING', 22); -@define('FILE_ASN1_TYPE_UTC_TIME', 23); -@define('FILE_ASN1_TYPE_GENERALIZED_TIME', 24); -@define('FILE_ASN1_TYPE_GRAPHIC_STRING', 25); -@define('FILE_ASN1_TYPE_VISIBLE_STRING', 26); // ISO646String -@define('FILE_ASN1_TYPE_GENERAL_STRING', 27); -@define('FILE_ASN1_TYPE_UNIVERSAL_STRING', 28); -//@define('FILE_ASN1_TYPE_CHARACTER_STRING', 29); -@define('FILE_ASN1_TYPE_BMP_STRING', 30); -/**#@-*/ - -/**#@+ - * Tag Aliases - * - * These tags are kinda place holders for other tags. - * - * @access private - */ -@define('FILE_ASN1_TYPE_CHOICE', -1); -@define('FILE_ASN1_TYPE_ANY', -2); -/**#@-*/ - /** - * ASN.1 Element - * - * Bypass normal encoding rules in ASN1::encodeDER() + * Pure-PHP ASN.1 Parser * * @package ASN1 * @author Jim Wigginton - * @version 0.3.0 * @access public */ -class ASN1_Element +class ASN1 { - /** - * Raw element value + /**#@+ + * Tag Classes * - * @var String * @access private + * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 */ - var $element; - - /** - * Constructor + const CLASS_UNIVERSAL = 0; + const CLASS_APPLICATION = 1; + const CLASS_CONTEXT_SPECIFIC = 2; + const CLASS_PRIVATE = 3; + /**#@-*/ + + /**#@+ + * Tag Classes * - * @param String $encoded - * @return ASN1_Element - * @access public - */ - function __construct($encoded) - { - $this->element = $encoded; - } -} + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node124.html + */ + const TYPE_BOOLEAN = 1; + const TYPE_INTEGER = 2; + const TYPE_BIT_STRING = 3; + const TYPE_OCTET_STRING = 4; + const TYPE_NULL = 5; + const TYPE_OBJECT_IDENTIFIER = 6; + //const TYPE_OBJECT_DESCRIPTOR = 7; + //const TYPE_INSTANCE_OF = 8; // EXTERNAL + const TYPE_REAL = 9; + const TYPE_ENUMERATED = 10; + //const TYPE_EMBEDDED = 11; + const TYPE_UTF8_STRING = 12; + //const TYPE_RELATIVE_OID = 13; + const TYPE_SEQUENCE = 16; // SEQUENCE OF + const TYPE_SET = 17; // SET OF + /**#@-*/ + /**#@+ + * More Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node10.html + */ + const TYPE_NUMERIC_STRING = 18; + const TYPE_PRINTABLE_STRING = 19; + const TYPE_TELETEX_STRING = 20; // T61String + const TYPE_VIDEOTEX_STRING = 21; + const TYPE_IA5_STRING = 22; + const TYPE_UTC_TIME = 23; + const TYPE_GENERALIZED_TIME = 24; + const TYPE_GRAPHIC_STRING = 25; + const TYPE_VISIBLE_STRING = 26; // ISO646String + const TYPE_GENERAL_STRING = 27; + const TYPE_UNIVERSAL_STRING = 28; + //const TYPE_CHARACTER_STRING = 29; + const TYPE_BMP_STRING = 30; + /**#@-*/ + + /**#@+ + * Tag Aliases + * + * These tags are kinda place holders for other tags. + * + * @access private + */ + const TYPE_CHOICE = -1; + const TYPE_ANY = -2; + /**#@-*/ -/** - * Pure-PHP ASN.1 Parser - * - * @package ASN1 - * @author Jim Wigginton - * @version 0.3.0 - * @access public - */ -class ASN1 -{ /** * ASN.1 object identifier * - * @var Array + * @var array * @access private * @link http://en.wikipedia.org/wiki/Object_identifier */ @@ -164,19 +114,19 @@ class ASN1 /** * Default date format * - * @var String + * @var string * @access private * @link http://php.net/class.datetime */ - var $format = 'D, d M y H:i:s O'; + var $format = 'D, d M Y H:i:s O'; /** * Default date format * - * @var Array + * @var array * @access private - * @see ASN1::setTimeFormat() - * @see ASN1::asn1map() + * @see self::setTimeFormat() + * @see self::asn1map() * @link http://php.net/class.datetime */ var $encoded; @@ -184,47 +134,47 @@ class ASN1 /** * Filters * - * If the mapping type is FILE_ASN1_TYPE_ANY what do we actually encode it as? + * If the mapping type is self::TYPE_ANY what do we actually encode it as? * - * @var Array + * @var array * @access private - * @see ASN1::_encode_der() + * @see self::_encode_der() */ var $filters; /** * Type mapping table for the ANY type. * - * Structured or unknown types are mapped to a FILE_ASN1_Element. + * Structured or unknown types are mapped to a \phpseclib\File\ASN1\Element. * Unambiguous types get the direct mapping (int/real/bool). * Others are mapped as a choice, with an extra indexing level. * - * @var Array + * @var array * @access public */ var $ANYmap = array( - FILE_ASN1_TYPE_BOOLEAN => true, - FILE_ASN1_TYPE_INTEGER => true, - FILE_ASN1_TYPE_BIT_STRING => 'bitString', - FILE_ASN1_TYPE_OCTET_STRING => 'octetString', - FILE_ASN1_TYPE_NULL => 'null', - FILE_ASN1_TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', - FILE_ASN1_TYPE_REAL => true, - FILE_ASN1_TYPE_ENUMERATED => 'enumerated', - FILE_ASN1_TYPE_UTF8_STRING => 'utf8String', - FILE_ASN1_TYPE_NUMERIC_STRING => 'numericString', - FILE_ASN1_TYPE_PRINTABLE_STRING => 'printableString', - FILE_ASN1_TYPE_TELETEX_STRING => 'teletexString', - FILE_ASN1_TYPE_VIDEOTEX_STRING => 'videotexString', - FILE_ASN1_TYPE_IA5_STRING => 'ia5String', - FILE_ASN1_TYPE_UTC_TIME => 'utcTime', - FILE_ASN1_TYPE_GENERALIZED_TIME => 'generalTime', - FILE_ASN1_TYPE_GRAPHIC_STRING => 'graphicString', - FILE_ASN1_TYPE_VISIBLE_STRING => 'visibleString', - FILE_ASN1_TYPE_GENERAL_STRING => 'generalString', - FILE_ASN1_TYPE_UNIVERSAL_STRING => 'universalString', - //FILE_ASN1_TYPE_CHARACTER_STRING => 'characterString', - FILE_ASN1_TYPE_BMP_STRING => 'bmpString' + self::TYPE_BOOLEAN => true, + self::TYPE_INTEGER => true, + self::TYPE_BIT_STRING => 'bitString', + self::TYPE_OCTET_STRING => 'octetString', + self::TYPE_NULL => 'null', + self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', + self::TYPE_REAL => true, + self::TYPE_ENUMERATED => 'enumerated', + self::TYPE_UTF8_STRING => 'utf8String', + self::TYPE_NUMERIC_STRING => 'numericString', + self::TYPE_PRINTABLE_STRING => 'printableString', + self::TYPE_TELETEX_STRING => 'teletexString', + self::TYPE_VIDEOTEX_STRING => 'videotexString', + self::TYPE_IA5_STRING => 'ia5String', + self::TYPE_UTC_TIME => 'utcTime', + self::TYPE_GENERALIZED_TIME => 'generalTime', + self::TYPE_GRAPHIC_STRING => 'graphicString', + self::TYPE_VISIBLE_STRING => 'visibleString', + self::TYPE_GENERAL_STRING => 'generalString', + self::TYPE_UNIVERSAL_STRING => 'universalString', + //self::TYPE_CHARACTER_STRING => 'characterString', + self::TYPE_BMP_STRING => 'bmpString' ); /** @@ -233,308 +183,342 @@ class ASN1 * Non-convertable types are absent from this table. * size == 0 indicates variable length encoding. * - * @var Array + * @var array * @access public */ var $stringTypeSize = array( - FILE_ASN1_TYPE_UTF8_STRING => 0, - FILE_ASN1_TYPE_BMP_STRING => 2, - FILE_ASN1_TYPE_UNIVERSAL_STRING => 4, - FILE_ASN1_TYPE_PRINTABLE_STRING => 1, - FILE_ASN1_TYPE_TELETEX_STRING => 1, - FILE_ASN1_TYPE_IA5_STRING => 1, - FILE_ASN1_TYPE_VISIBLE_STRING => 1, + self::TYPE_UTF8_STRING => 0, + self::TYPE_BMP_STRING => 2, + self::TYPE_UNIVERSAL_STRING => 4, + self::TYPE_PRINTABLE_STRING => 1, + self::TYPE_TELETEX_STRING => 1, + self::TYPE_IA5_STRING => 1, + self::TYPE_VISIBLE_STRING => 1, ); - /** - * Default Constructor. - * - * @access public - */ - function __construct() - { - static $static_init = null; - if (!$static_init) { - $static_init = true; - } - } - /** * Parse BER-encoding * * Serves a similar purpose to openssl's asn1parse * - * @param String $encoded - * @return Array + * @param string $encoded + * @return array * @access public */ function decodeBER($encoded) { - if (is_object($encoded) && strtolower(get_class($encoded)) == 'file_asn1_element') { + if ($encoded instanceof Element) { $encoded = $encoded->element; } $this->encoded = $encoded; - return $this->_decode_ber($encoded); + // encapsulate in an array for BC with the old decodeBER + return array($this->_decode_ber($encoded)); } /** * Parse BER-encoding (Helper function) * * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode. - * $encoded is passed by reference for the recursive calls done for FILE_ASN1_TYPE_BIT_STRING and - * FILE_ASN1_TYPE_OCTET_STRING. In those cases, the indefinite length is used. + * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and + * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used. * - * @param String $encoded - * @param Integer $start - * @return Array + * @param string $encoded + * @param int $start + * @param int $encoded_pos + * @return array * @access private */ - function _decode_ber(&$encoded, $start = 0) + function _decode_ber($encoded, $start = 0, $encoded_pos = 0) { - $decoded = array(); - - while ( strlen($encoded) ) { - $current = array('start' => $start); - - $type = ord($this->_string_shift($encoded)); - $start++; - - $constructed = ($type >> 5) & 1; - - $tag = $type & 0x1F; - if ($tag == 0x1F) { - $tag = 0; - // process septets (since the eighth bit is ignored, it's not an octet) - do { - $loop = ord($encoded[0]) >> 7; - $tag <<= 7; - $tag |= ord($this->_string_shift($encoded)) & 0x7F; - $start++; - } while ( $loop ); - } + $current = array('start' => $start); + + $type = ord($encoded[$encoded_pos++]); + $start++; + + $constructed = ($type >> 5) & 1; + + $tag = $type & 0x1F; + if ($tag == 0x1F) { + $tag = 0; + // process septets (since the eighth bit is ignored, it's not an octet) + do { + $loop = ord($encoded[0]) >> 7; + $tag <<= 7; + $tag |= ord($encoded[$encoded_pos++]) & 0x7F; + $start++; + } while ($loop); + } - // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 - $length = ord($this->_string_shift($encoded)); - $start++; - if ( $length == 0x80 ) { // indefinite length - // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all - // immediately available." -- paragraph 8.1.3.2.c - //if ( !$constructed ) { - // return false; - //} - $length = strlen($encoded); - } elseif ( $length & 0x80 ) { // definite length, long form - // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only - // support it up to four. - $length&= 0x7F; - $temp = $this->_string_shift($encoded, $length); - // tags of indefinite length don't really have a header length; this length includes the tag - $current+= array('headerlength' => $length + 2); - $start+= $length; - extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); - } else { - $current+= array('headerlength' => 2); - } + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 + $length = ord($encoded[$encoded_pos++]); + $start++; + if ($length == 0x80) { // indefinite length + // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all + // immediately available." -- paragraph 8.1.3.2.c + $length = strlen($encoded) - $encoded_pos; + } elseif ($length & 0x80) { // definite length, long form + // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only + // support it up to four. + $length&= 0x7F; + $temp = substr($encoded, $encoded_pos, $length); + $encoded_pos += $length; + // tags of indefinte length don't really have a header length; this length includes the tag + $current+= array('headerlength' => $length + 2); + $start+= $length; + extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); + } else { + $current+= array('headerlength' => 2); + } - // End-of-content, see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 - if (!$type && !$length) { - return $decoded; - } - $content = $this->_string_shift($encoded, $length); - - /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 - built-in types. It defines an application-independent data type that must be distinguishable from all other - data types. The other three classes are user defined. The APPLICATION class distinguishes data types that - have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within - a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the - alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this - data type; the term CONTEXT-SPECIFIC does not appear. - - -- http://www.obj-sys.com/asn1tutorial/node12.html */ - $class = ($type >> 6) & 3; - switch ($class) { - case FILE_ASN1_CLASS_APPLICATION: - case FILE_ASN1_CLASS_PRIVATE: - case FILE_ASN1_CLASS_CONTEXT_SPECIFIC: - $decoded[] = array( + if ($length > (strlen($encoded) - $encoded_pos)) { + return false; + } + + $content = substr($encoded, $encoded_pos, $length); + $content_pos = 0; + + // at this point $length can be overwritten. it's only accurate for definite length things as is + + /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 + built-in types. It defines an application-independent data type that must be distinguishable from all other + data types. The other three classes are user defined. The APPLICATION class distinguishes data types that + have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within + a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the + alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this + data type; the term CONTEXT-SPECIFIC does not appear. + + -- http://www.obj-sys.com/asn1tutorial/node12.html */ + $class = ($type >> 6) & 3; + switch ($class) { + case self::CLASS_APPLICATION: + case self::CLASS_PRIVATE: + case self::CLASS_CONTEXT_SPECIFIC: + if (!$constructed) { + return array( 'type' => $class, 'constant' => $tag, - 'content' => $constructed ? $this->_decode_ber($content, $start) : $content, + 'content' => $content, 'length' => $length + $start - $current['start'] - ) + $current; + ); + } + + $newcontent = array(); + $remainingLength = $length; + while ($remainingLength > 0) { + $temp = $this->_decode_ber($content, $start); + $length = $temp['length']; + // end-of-content octets - see paragraph 8.1.5 + if (substr($content, $content_pos + $length, 2) == "\0\0") { + $length+= 2; + $start+= $length; + $newcontent[] = $temp; + break; + } $start+= $length; - continue 2; - } + $remainingLength-= $length; + $newcontent[] = $temp; + $content_pos += $length; + } - $current+= array('type' => $tag); + return array( + 'type' => $class, + 'constant' => $tag, + // the array encapsulation is for BC with the old format + 'content' => $newcontent, + // the only time when $content['headerlength'] isn't defined is when the length is indefinite. + // the absence of $content['headerlength'] is how we know if something is indefinite or not. + // technically, it could be defined to be 2 and then another indicator could be used but whatever. + 'length' => $start - $current['start'] + ) + $current; + } - // decode UNIVERSAL tags - switch ($tag) { - case FILE_ASN1_TYPE_BOOLEAN: - // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 - //if (strlen($content) != 1) { - // return false; - //} - $current['content'] = (bool) ord($content[0]); - break; - case FILE_ASN1_TYPE_INTEGER: - case FILE_ASN1_TYPE_ENUMERATED: - $current['content'] = new BigInteger($content, -256); - break; - case FILE_ASN1_TYPE_REAL: // not currently supported - return false; - case FILE_ASN1_TYPE_BIT_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. - if (!$constructed) { - $current['content'] = $content; - } else { - $temp = $this->_decode_ber($content, $start); - $length-= strlen($content); - $last = count($temp) - 1; - for ($i = 0; $i < $last; $i++) { - // all subtags should be bit strings - //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) { - // return false; - //} - $current['content'].= substr($temp[$i]['content'], 1); - } + $current+= array('type' => $tag); + + // decode UNIVERSAL tags + switch ($tag) { + case self::TYPE_BOOLEAN: + // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 + //if (strlen($content) != 1) { + // return false; + //} + $current['content'] = (bool) ord($content[$content_pos]); + break; + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: + $current['content'] = new BigInteger(substr($content, $content_pos), -256); + break; + case self::TYPE_REAL: // not currently supported + return false; + case self::TYPE_BIT_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. + if (!$constructed) { + $current['content'] = substr($content, $content_pos); + } else { + $temp = $this->_decode_ber($content, $start, $content_pos); + $length-= (strlen($content) - $content_pos); + $last = count($temp) - 1; + for ($i = 0; $i < $last; $i++) { // all subtags should be bit strings - //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) { + //if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { // return false; //} - $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); - } - break; - case FILE_ASN1_TYPE_OCTET_STRING: - if (!$constructed) { - $current['content'] = $content; - } else { - $temp = $this->_decode_ber($content, $start); - $length-= strlen($content); - for ($i = 0, $size = count($temp); $i < $size; $i++) { - // all subtags should be octet strings - //if ($temp[$i]['type'] != FILE_ASN1_TYPE_OCTET_STRING) { - // return false; - //} - $current['content'].= $temp[$i]['content']; - } - // $length = + $current['content'].= substr($temp[$i]['content'], 1); } - break; - case FILE_ASN1_TYPE_NULL: - // "The contents octets shall not contain any octets." -- paragraph 8.8.2 - //if (strlen($content)) { + // all subtags should be bit strings + //if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { // return false; //} - break; - case FILE_ASN1_TYPE_SEQUENCE: - case FILE_ASN1_TYPE_SET: - $current['content'] = $this->_decode_ber($content, $start); - break; - case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: - $temp = ord($this->_string_shift($content)); - $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); - $valuen = 0; - // process septets - while (strlen($content)) { - $temp = ord($this->_string_shift($content)); - $valuen <<= 7; - $valuen |= $temp & 0x7F; - if (~$temp & 0x80) { - $current['content'].= ".$valuen"; - $valuen = 0; - } + $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); + } + break; + case self::TYPE_OCTET_STRING: + if (!$constructed) { + $current['content'] = substr($content, $content_pos); + } else { + $current['content'] = ''; + $length = 0; + while (substr($content, $content_pos, 2) != "\0\0") { + $temp = $this->_decode_ber($content, $length + $start, $content_pos); + $content_pos += $temp['length']; + // all subtags should be octet strings + //if ($temp['type'] != self::TYPE_OCTET_STRING) { + // return false; + //} + $current['content'].= $temp['content']; + $length+= $temp['length']; } - // the eighth bit of the last byte should not be 1 - //if ($temp >> 7) { - // return false; - //} - break; - /* Each character string type shall be encoded as if it had been declared: - [UNIVERSAL x] IMPLICIT OCTET STRING - - -- X.690-0207.pdf#page=23 (paragraph 8.21.3) - - Per that, we're not going to do any validation. If there are any illegal characters in the string, - we don't really care */ - case FILE_ASN1_TYPE_NUMERIC_STRING: - // 0,1,2,3,4,5,6,7,8,9, and space - case FILE_ASN1_TYPE_PRINTABLE_STRING: - // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, - // hyphen, full stop, solidus, colon, equal sign, question mark - case FILE_ASN1_TYPE_TELETEX_STRING: - // The Teletex character set in CCITT's T61, space, and delete - // see http://en.wikipedia.org/wiki/Teletex#Character_sets - case FILE_ASN1_TYPE_VIDEOTEX_STRING: - // The Videotex character set in CCITT's T.100 and T.101, space, and delete - case FILE_ASN1_TYPE_VISIBLE_STRING: - // Printing character sets of international ASCII, and space - case FILE_ASN1_TYPE_IA5_STRING: - // International Alphabet 5 (International ASCII) - case FILE_ASN1_TYPE_GRAPHIC_STRING: - // All registered G sets, and space - case FILE_ASN1_TYPE_GENERAL_STRING: - // All registered C and G sets, space and delete - case FILE_ASN1_TYPE_UTF8_STRING: - // ???? - case FILE_ASN1_TYPE_BMP_STRING: - $current['content'] = $content; - break; - case FILE_ASN1_TYPE_UTC_TIME: - case FILE_ASN1_TYPE_GENERALIZED_TIME: - $current['content'] = $this->_decodeTime($content, $tag); - default: - - } - - $start+= $length; - $decoded[] = $current + array('length' => $start - $current['start']); + if (substr($content, $content_pos, 2) == "\0\0") { + $length+= 2; // +2 for the EOC + } + } + break; + case self::TYPE_NULL: + // "The contents octets shall not contain any octets." -- paragraph 8.8.2 + //if (strlen($content)) { + // return false; + //} + break; + case self::TYPE_SEQUENCE: + case self::TYPE_SET: + $offset = 0; + $current['content'] = array(); + $content_len = strlen($content); + while ($content_pos < $content_len) { + // if indefinite length construction was used and we have an end-of-content string next + // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 + if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") { + $length = $offset + 2; // +2 for the EOC + break 2; + } + $temp = $this->_decode_ber($content, $start + $offset, $content_pos); + $content_pos += $temp['length']; + $current['content'][] = $temp; + $offset+= $temp['length']; + } + break; + case self::TYPE_OBJECT_IDENTIFIER: + $temp = ord($content[$content_pos++]); + $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); + $valuen = 0; + // process septets + $content_len = strlen($content); + while ($content_pos < $content_len) { + $temp = ord($content[$content_pos++]); + $valuen <<= 7; + $valuen |= $temp & 0x7F; + if (~$temp & 0x80) { + $current['content'].= ".$valuen"; + $valuen = 0; + } + } + // the eighth bit of the last byte should not be 1 + //if ($temp >> 7) { + // return false; + //} + break; + /* Each character string type shall be encoded as if it had been declared: + [UNIVERSAL x] IMPLICIT OCTET STRING + + -- X.690-0207.pdf#page=23 (paragraph 8.21.3) + + Per that, we're not going to do any validation. If there are any illegal characters in the string, + we don't really care */ + case self::TYPE_NUMERIC_STRING: + // 0,1,2,3,4,5,6,7,8,9, and space + case self::TYPE_PRINTABLE_STRING: + // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, + // hyphen, full stop, solidus, colon, equal sign, question mark + case self::TYPE_TELETEX_STRING: + // The Teletex character set in CCITT's T61, space, and delete + // see http://en.wikipedia.org/wiki/Teletex#Character_sets + case self::TYPE_VIDEOTEX_STRING: + // The Videotex character set in CCITT's T.100 and T.101, space, and delete + case self::TYPE_VISIBLE_STRING: + // Printing character sets of international ASCII, and space + case self::TYPE_IA5_STRING: + // International Alphabet 5 (International ASCII) + case self::TYPE_GRAPHIC_STRING: + // All registered G sets, and space + case self::TYPE_GENERAL_STRING: + // All registered C and G sets, space and delete + case self::TYPE_UTF8_STRING: + // ???? + case self::TYPE_BMP_STRING: + $current['content'] = substr($content, $content_pos); + break; + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: + $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); + default: } - return $decoded; + $start+= $length; + + // ie. length is the length of the full TLV encoding - it's not just the length of the value + return $current + array('length' => $start - $current['start']); } /** - * ASN.1 Decode + * ASN.1 Map * * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * * "Special" mappings may be applied on a per tag-name basis via $special. * - * @param Array $decoded - * @param Array $mapping - * @param Array $special - * @return Array + * @param array $decoded + * @param array $mapping + * @param array $special + * @return array * @access public */ function asn1map($decoded, $mapping, $special = array()) { - if (isset($mapping['explicit'])) { + if (isset($mapping['explicit']) && is_array($decoded['content'])) { $decoded = $decoded['content'][0]; } switch (true) { - case $mapping['type'] == FILE_ASN1_TYPE_ANY: + case $mapping['type'] == self::TYPE_ANY: $intype = $decoded['type']; if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) { - return new ASN1_Element(substr($this->encoded, $decoded['start'], $decoded['length'])); + return new Element(substr($this->encoded, $decoded['start'], $decoded['length'])); } $inmap = $this->ANYmap[$intype]; if (is_string($inmap)) { return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special)); } break; - case $mapping['type'] == FILE_ASN1_TYPE_CHOICE: + case $mapping['type'] == self::TYPE_CHOICE: foreach ($mapping['children'] as $key => $option) { switch (true) { case isset($option['constant']) && $option['constant'] == $decoded['constant']: case !isset($option['constant']) && $option['type'] == $decoded['type']: $value = $this->asn1map($decoded, $option, $special); break; - case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE: + case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE: $v = $this->asn1map($decoded, $option, $special); if (isset($v)) { $value = $v; @@ -553,7 +537,15 @@ function asn1map($decoded, $mapping, $special = array()) case $decoded['type'] == $mapping['type']: break; default: - return null; + // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings, + // let it through + switch (true) { + case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18 + case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30 + case $mapping['type'] < 18: + case $mapping['type'] > 30: + return null; + } } if (isset($mapping['implicit'])) { @@ -561,7 +553,7 @@ function asn1map($decoded, $mapping, $special = array()) } switch ($decoded['type']) { - case FILE_ASN1_TYPE_SEQUENCE: + case self::TYPE_SEQUENCE: $map = array(); // ignore the min and max @@ -584,18 +576,18 @@ function asn1map($decoded, $mapping, $special = array()) if ($maymatch) { $temp = $decoded['content'][$i]; - if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { + if ($child['type'] != self::TYPE_CHOICE) { // Get the mapping and input class & constant. - $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; + $childClass = $tempClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; } if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { - $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $childClass = self::CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } @@ -604,7 +596,7 @@ function asn1map($decoded, $mapping, $special = array()) $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { // Can only match if no constant expected and type matches or is generic. - $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false; + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; } } } @@ -630,10 +622,10 @@ function asn1map($decoded, $mapping, $special = array()) } // Fail mapping if all input items have not been consumed. - return $i < $n? null: $map; + return $i < $n ? null: $map; // the main diff between sets and sequences is the encapsulation of the foreach in another for loop - case FILE_ASN1_TYPE_SET: + case self::TYPE_SET: $map = array(); // ignore the min and max @@ -650,9 +642,9 @@ function asn1map($decoded, $mapping, $special = array()) for ($i = 0; $i < count($decoded['content']); $i++) { $temp = $decoded['content'][$i]; - $tempClass = FILE_ASN1_CLASS_UNIVERSAL; + $tempClass = self::CLASS_UNIVERSAL; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; } foreach ($mapping['children'] as $key => $child) { @@ -660,14 +652,14 @@ function asn1map($decoded, $mapping, $special = array()) continue; } $maymatch = true; - if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { - $childClass = FILE_ASN1_CLASS_UNIVERSAL; + if ($child['type'] != self::TYPE_CHOICE) { + $childClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { - $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $childClass = self::CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } @@ -676,7 +668,7 @@ function asn1map($decoded, $mapping, $special = array()) $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { // Can only match if no constant expected and type matches or is generic. - $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false; + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; } } @@ -709,15 +701,15 @@ function asn1map($decoded, $mapping, $special = array()) } } return $map; - case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + case self::TYPE_OBJECT_IDENTIFIER: return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; - case FILE_ASN1_TYPE_UTC_TIME: - case FILE_ASN1_TYPE_GENERALIZED_TIME: + case self::TYPE_UTC_TIME: + case self::TYPE_GENERALIZED_TIME: if (isset($mapping['implicit'])) { $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); } return @date($this->format, $decoded['content']); - case FILE_ASN1_TYPE_BIT_STRING: + case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $offset = ord($decoded['content'][0]); $size = (strlen($decoded['content']) - 1) * 8 - $offset; @@ -746,26 +738,26 @@ function asn1map($decoded, $mapping, $special = array()) } return $values; } - case FILE_ASN1_TYPE_OCTET_STRING: - return base64_encode($decoded['content']); - case FILE_ASN1_TYPE_NULL: + case self::TYPE_OCTET_STRING: + return Base64::encode($decoded['content']); + case self::TYPE_NULL: return ''; - case FILE_ASN1_TYPE_BOOLEAN: + case self::TYPE_BOOLEAN: return $decoded['content']; - case FILE_ASN1_TYPE_NUMERIC_STRING: - case FILE_ASN1_TYPE_PRINTABLE_STRING: - case FILE_ASN1_TYPE_TELETEX_STRING: - case FILE_ASN1_TYPE_VIDEOTEX_STRING: - case FILE_ASN1_TYPE_IA5_STRING: - case FILE_ASN1_TYPE_GRAPHIC_STRING: - case FILE_ASN1_TYPE_VISIBLE_STRING: - case FILE_ASN1_TYPE_GENERAL_STRING: - case FILE_ASN1_TYPE_UNIVERSAL_STRING: - case FILE_ASN1_TYPE_UTF8_STRING: - case FILE_ASN1_TYPE_BMP_STRING: + case self::TYPE_NUMERIC_STRING: + case self::TYPE_PRINTABLE_STRING: + case self::TYPE_TELETEX_STRING: + case self::TYPE_VIDEOTEX_STRING: + case self::TYPE_IA5_STRING: + case self::TYPE_GRAPHIC_STRING: + case self::TYPE_VISIBLE_STRING: + case self::TYPE_GENERAL_STRING: + case self::TYPE_UNIVERSAL_STRING: + case self::TYPE_UTF8_STRING: + case self::TYPE_BMP_STRING: return $decoded['content']; - case FILE_ASN1_TYPE_INTEGER: - case FILE_ASN1_TYPE_ENUMERATED: + case self::TYPE_INTEGER: + case self::TYPE_ENUMERATED: $temp = $decoded['content']; if (isset($mapping['implicit'])) { $temp = new BigInteger($decoded['content'], -256); @@ -788,10 +780,10 @@ function asn1map($decoded, $mapping, $special = array()) * * "Special" mappings can be applied via $special. * - * @param String $source - * @param String $mapping - * @param Integer $idx - * @return String + * @param string $source + * @param string $mapping + * @param int $idx + * @return string * @access public */ function encodeDER($source, $mapping, $special = array()) @@ -803,25 +795,16 @@ function encodeDER($source, $mapping, $special = array()) /** * ASN.1 Encode (Helper function) * - * @param String $source - * @param Array $mapping - * @param Integer $idx - * @param Array $special - * @return String - * @access private - */ - /** - * ASN.1 Encode (Helper function) - * - * @param String $source - * @param String $mapping - * @param Integer $idx - * @return String + * @param string $source + * @param string $mapping + * @param int $idx + * @return string + * @throws \RuntimeException if the input has an error in it * @access private */ function _encode_der($source, $mapping, $idx = null, $special = array()) { - if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') { + if ($source instanceof Element) { return $source->element; } @@ -840,8 +823,8 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $tag = $mapping['type']; switch ($tag) { - case FILE_ASN1_TYPE_SET: // Children order is not important, thus process in sequence. - case FILE_ASN1_TYPE_SEQUENCE: + case self::TYPE_SET: // Children order is not important, thus process in sequence. + case self::TYPE_SEQUENCE: $tag|= 0x20; // set the constructed bit $value = ''; @@ -860,7 +843,7 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } foreach ($mapping['children'] as $key => $child) { - if (!isset($source[$key])) { + if (!array_key_exists($key, $source)) { if (!isset($child['optional'])) { return false; } @@ -889,18 +872,18 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) 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'] == FILE_ASN1_TYPE_CHOICE) { - $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $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((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); + $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } $value.= $temp; } break; - case FILE_ASN1_TYPE_CHOICE: + case self::TYPE_CHOICE: $temp = false; foreach ($mapping['children'] as $key => $child) { @@ -923,11 +906,11 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) // 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'] == FILE_ASN1_TYPE_CHOICE) { - $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $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((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); + $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } @@ -942,9 +925,12 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } return $temp; - case FILE_ASN1_TYPE_INTEGER: - case FILE_ASN1_TYPE_ENUMERATED: + 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']); @@ -958,13 +944,13 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $value = chr(0); } break; - case FILE_ASN1_TYPE_UTC_TIME: - case FILE_ASN1_TYPE_GENERALIZED_TIME: - $format = $mapping['type'] == FILE_ASN1_TYPE_UTC_TIME ? 'y' : 'Y'; + 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 FILE_ASN1_TYPE_BIT_STRING: + case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $bits = array_fill(0, count($mapping['mapping']), 0); $size = 0; @@ -975,6 +961,10 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } } + if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { + $size = $mapping['min'] - 1; + } + $offset = 8 - (($size + 1) & 7); $offset = $offset !== 8 ? $offset : 0; @@ -992,17 +982,17 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) break; } - case FILE_ASN1_TYPE_OCTET_STRING: + 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); + $value = Base64::decode($source); break; - case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + case self::TYPE_OBJECT_IDENTIFIER: $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); if ($oid === false) { - user_error('Invalid OID'); + throw new \RuntimeException('Invalid OID'); return false; } $value = ''; @@ -1022,7 +1012,7 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $value.= $temp; } break; - case FILE_ASN1_TYPE_ANY: + case self::TYPE_ANY: $loc = $this->location; if (isset($idx)) { array_pop($this->location); @@ -1030,21 +1020,21 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) switch (true) { case !isset($source): - return $this->_encode_der(null, array('type' => FILE_ASN1_TYPE_NULL) + $mapping, null, $special); + return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special); case is_int($source): - case is_object($source) && strtolower(get_class($source)) == 'math_biginteger': - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, null, $special); + 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' => FILE_ASN1_TYPE_REAL) + $mapping, null, $special); + return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special); case is_bool($source): - return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping, null, $special); + 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) { @@ -1055,31 +1045,31 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $filters = $filters[$part]; } if ($filters === false) { - user_error('No filters defined for ' . implode('/', $loc)); + throw new \RuntimeException('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); - case FILE_ASN1_TYPE_NULL: + case self::TYPE_NULL: $value = ''; break; - case FILE_ASN1_TYPE_NUMERIC_STRING: - case FILE_ASN1_TYPE_TELETEX_STRING: - case FILE_ASN1_TYPE_PRINTABLE_STRING: - case FILE_ASN1_TYPE_UNIVERSAL_STRING: - case FILE_ASN1_TYPE_UTF8_STRING: - case FILE_ASN1_TYPE_BMP_STRING: - case FILE_ASN1_TYPE_IA5_STRING: - case FILE_ASN1_TYPE_VISIBLE_STRING: - case FILE_ASN1_TYPE_VIDEOTEX_STRING: - case FILE_ASN1_TYPE_GRAPHIC_STRING: - case FILE_ASN1_TYPE_GENERAL_STRING: + 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 FILE_ASN1_TYPE_BOOLEAN: + case self::TYPE_BOOLEAN: $value = $source ? "\xFF" : "\x00"; break; default: - user_error('Mapping provides no type definition for ' . implode('/', $this->location)); + throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } @@ -1088,7 +1078,12 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } if (isset($mapping['cast'])) { - $tag = ($mapping['class'] << 6) | ($tag & 0x20) | $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; @@ -1101,8 +1096,8 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private - * @param Integer $length - * @return String + * @param int $length + * @return string */ function _encodeLength($length) { @@ -1120,9 +1115,9 @@ function _encodeLength($length) * Called by _decode_ber() and in the case of implicit tags asn1map(). * * @access private - * @param String $content - * @param Integer $tag - * @return String + * @param string $content + * @param int $tag + * @return string */ function _decodeTime($content, $tag) { @@ -1134,7 +1129,7 @@ function _decodeTime($content, $tag) http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://www.obj-sys.com/asn1tutorial/node14.html */ - $pattern = $tag == FILE_ASN1_TYPE_UTC_TIME ? + $pattern = $tag == self::TYPE_UTC_TIME ? '#(..)(..)(..)(..)(..)(..)(.*)#' : '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#'; @@ -1142,7 +1137,7 @@ function _decodeTime($content, $tag) list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches; - if ($tag == FILE_ASN1_TYPE_UTC_TIME) { + if ($tag == self::TYPE_UTC_TIME) { $year = $year >= 50 ? "19$year" : "20$year"; } @@ -1169,7 +1164,7 @@ function _decodeTime($content, $tag) * Sets the time / date format for asn1map(). * * @access public - * @param String $format + * @param string $format */ function setTimeFormat($format) { @@ -1182,7 +1177,7 @@ function setTimeFormat($format) * Load the relevant OIDs for a particular ASN.1 semantic mapping. * * @access public - * @param Array $oids + * @param array $oids */ function loadOIDs($oids) { @@ -1192,10 +1187,10 @@ function loadOIDs($oids) /** * Load filters * - * See X059, etc, for an example. + * See \phpseclib\File\X509, etc, for an example. * * @access public - * @param Array $filters + * @param array $filters */ function loadFilters($filters) { @@ -1207,9 +1202,9 @@ function loadFilters($filters) * * Inspired by array_shift * - * @param String $string - * @param optional Integer $index - * @return String + * @param string $string + * @param int $index + * @return string * @access private */ function _string_shift(&$string, $index = 1) @@ -1225,13 +1220,13 @@ function _string_shift(&$string, $index = 1) * This is a lazy conversion, dealing only with character size. * No real conversion table is used. * - * @param String $in - * @param optional Integer $from - * @param optional Integer $to - * @return String + * @param string $in + * @param int $from + * @param int $to + * @return string * @access public */ - function convert($in, $from = FILE_ASN1_TYPE_UTF8_STRING, $to = FILE_ASN1_TYPE_UTF8_STRING) + function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING) { if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) { return false; diff --git a/src/phpseclib/File/ASN1/Element.php b/src/phpseclib/File/ASN1/Element.php new file mode 100755 index 00000000..68246e2b --- /dev/null +++ b/src/phpseclib/File/ASN1/Element.php @@ -0,0 +1,47 @@ + + * @copyright 2012 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\File\ASN1; + +/** + * ASN.1 Element + * + * Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER() + * + * @package ASN1 + * @author Jim Wigginton + * @access public + */ +class Element +{ + /** + * Raw element value + * + * @var string + * @access private + */ + var $element; + + /** + * Constructor + * + * @param string $encoded + * @return \phpseclib\File\ASN1\Element + * @access public + */ + function __construct($encoded) + { + $this->element = $encoded; + } +} diff --git a/src/phpseclib/File/X509.php b/src/phpseclib/File/X509.php old mode 100644 new mode 100755 index 55cf8ce4..b49bc01d --- a/src/phpseclib/File/X509.php +++ b/src/phpseclib/File/X509.php @@ -3,7 +3,7 @@ /** * Pure-PHP X.509 Parser * - * PHP versions 4 and 5 + * PHP version 5 * * Encode and decode X.509 certificates. * @@ -16,123 +16,115 @@ * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the * the certificate all together unless the certificate is re-signed. * - * LICENSE: 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. - * * @category File - * @package X059 + * @package X509 * @author Jim Wigginton - * @copyright MMXII Jim Wigginton + * @copyright 2012 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\File; -/** - * Flag to only accept signatures signed by certificate authorities - * - * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs - * - * @access public - */ +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; use phpseclib\Crypt\Hash; +use phpseclib\Crypt\Random; use phpseclib\Crypt\RSA; +use phpseclib\Exception\UnsupportedAlgorithmException; +use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; -@define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1); - -/**#@+ - * @access public - * @see X059::getDN() - */ -/** - * Return internal array representation - */ -@define('FILE_X509_DN_ARRAY', 0); -/** - * Return string - */ -@define('FILE_X509_DN_STRING', 1); -/** - * Return ASN.1 name string - */ -@define('FILE_X509_DN_ASN1', 2); -/** - * Return OpenSSL compatible array - */ -@define('FILE_X509_DN_OPENSSL', 3); -/** - * Return canonical ASN.1 RDNs string - */ -@define('FILE_X509_DN_CANON', 4); -/** - * Return name hash for file indexing - */ -@define('FILE_X509_DN_HASH', 5); -/**#@-*/ - -/**#@+ - * @access public - * @see X059::saveX509() - * @see X059::saveCSR() - * @see X059::saveCRL() - */ -/** - * Save as PEM - * - * ie. a base64-encoded PEM with a header and a footer - */ -@define('FILE_X509_FORMAT_PEM', 0); -/** - * Save as DER - */ -@define('FILE_X509_FORMAT_DER', 1); -/** - * Save as a SPKAC - * - * Only works on CSRs. Not currently supported. - */ -@define('FILE_X509_FORMAT_SPKAC', 2); -/**#@-*/ - -/** - * Attribute value disposition. - * If disposition is >= 0, this is the index of the target value. - */ -@define('FILE_X509_ATTR_ALL', -1); // All attribute values (array). -@define('FILE_X509_ATTR_APPEND', -2); // Add a value. -@define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value. - /** * Pure-PHP X.509 Parser * - * @package X059 + * @package X509 * @author Jim Wigginton - * @version 0.3.1 * @access public */ -class X059 +class X509 { + /** + * Flag to only accept signatures signed by certificate authorities + * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * + * @access public + */ + const VALIDATE_SIGNATURE_BY_CA = 1; + + /**#@+ + * @access public + * @see \phpseclib\File\X509::getDN() + */ + /** + * Return internal array representation + */ + const DN_ARRAY = 0; + /** + * Return string + */ + const DN_STRING = 1; + /** + * Return ASN.1 name string + */ + const DN_ASN1 = 2; + /** + * Return OpenSSL compatible array + */ + const DN_OPENSSL = 3; + /** + * Return canonical ASN.1 RDNs string + */ + const DN_CANON = 4; + /** + * Return name hash for file indexing + */ + const DN_HASH = 5; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\File\X509::saveX509() + * @see \phpseclib\File\X509::saveCSR() + * @see \phpseclib\File\X509::saveCRL() + */ + /** + * Save as PEM + * + * ie. a base64-encoded PEM with a header and a footer + */ + const FORMAT_PEM = 0; + /** + * Save as DER + */ + const FORMAT_DER = 1; + /** + * Save as a SPKAC + * + * Only works on CSRs. Not currently supported. + */ + const FORMAT_SPKAC = 2; + /** + * Auto-detect the format + * + * Used only by the load*() functions + */ + const FORMAT_AUTO_DETECT = 3; + /**#@-*/ + + /** + * Attribute value disposition. + * If disposition is >= 0, this is the index of the target value. + */ + const ATTR_ALL = -1; // All attribute values (array). + const ATTR_APPEND = -2; // Add a value. + const ATTR_REPLACE = -3; // Clear first, then add a value. + /** * ASN.1 syntax for X.509 certificates * - * @var Array + * @var array * @access private */ var $Certificate; @@ -155,6 +147,7 @@ class X059 var $CertificatePolicies; var $AuthorityInfoAccessSyntax; var $SubjectAltName; + var $SubjectDirectoryAttributes; var $PrivateKeyUsagePeriod; var $IssuerAltName; var $PolicyMappings; @@ -178,10 +171,18 @@ class X059 var $SignedPublicKeyAndChallenge; /**#@-*/ + /**#@+ + * ASN.1 syntax for various DN attributes + * + * @access private + */ + var $PostalAddress; + /**#@-*/ + /** * ASN.1 syntax for Certificate Signing Requests (RFC2986) * - * @var Array + * @var array * @access private */ var $CertificationRequest; @@ -189,7 +190,7 @@ class X059 /** * ASN.1 syntax for Certificate Revocation Lists (RFC5280) * - * @var Array + * @var array * @access private */ var $CertificateList; @@ -197,7 +198,7 @@ class X059 /** * Distinguished Name * - * @var Array + * @var array * @access private */ var $dn; @@ -205,7 +206,7 @@ class X059 /** * Public key * - * @var String + * @var string * @access private */ var $publicKey; @@ -213,7 +214,7 @@ class X059 /** * Private key * - * @var String + * @var string * @access private */ var $privateKey; @@ -221,7 +222,7 @@ class X059 /** * Object identifiers for X.509 certificates * - * @var Array + * @var array * @access private * @link http://en.wikipedia.org/wiki/Object_identifier */ @@ -230,7 +231,7 @@ class X059 /** * The certificate authorities * - * @var Array + * @var array * @access private */ var $CAs; @@ -238,7 +239,7 @@ class X059 /** * The currently loaded certificate * - * @var Array + * @var array * @access private */ var $currentCert; @@ -246,10 +247,10 @@ class X059 /** * The signature subject * - * There's no guarantee X059 is going to reencode an X.509 cert in the same way it was originally + * There's no guarantee \phpseclib\File\X509 is going to reencode an X.509 cert in the same way it was originally * encoded so we take save the portion of the original cert that the signature would have made for. * - * @var String + * @var string * @access private */ var $signatureSubject; @@ -257,7 +258,7 @@ class X059 /** * Certificate Start Date * - * @var String + * @var string * @access private */ var $startDate; @@ -265,7 +266,7 @@ class X059 /** * Certificate End Date * - * @var String + * @var string * @access private */ var $endDate; @@ -273,7 +274,7 @@ class X059 /** * Serial Number * - * @var String + * @var string * @access private */ var $serialNumber; @@ -284,7 +285,7 @@ class X059 * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}. * - * @var String + * @var string * @access private */ var $currentKeyIdentifier; @@ -292,48 +293,55 @@ class X059 /** * CA Flag * - * @var Boolean + * @var bool * @access private */ var $caFlag = false; + /** + * SPKAC Challenge + * + * @var string + * @access private + */ + var $challenge; + /** * Default Constructor. * - * @return X059 + * @return \phpseclib\File\X509 * @access public */ function __construct() { - // Explicitly Tagged Module, 1988 Syntax // http://tools.ietf.org/html/rfc5280#appendix-A.1 $this->DirectoryString = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( - 'teletexString' => array('type' => FILE_ASN1_TYPE_TELETEX_STRING), - 'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING), - 'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING), - 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING), - 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING) + 'teletexString' => array('type' => ASN1::TYPE_TELETEX_STRING), + 'printableString' => array('type' => ASN1::TYPE_PRINTABLE_STRING), + 'universalString' => array('type' => ASN1::TYPE_UNIVERSAL_STRING), + 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING), + 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING) ) ); $this->PKCS9String = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( - 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING), + 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), 'directoryString' => $this->DirectoryString ) ); - $this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY); + $this->AttributeValue = array('type' => ASN1::TYPE_ANY); - $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + $AttributeType = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $AttributeTypeAndValue = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'type' => $AttributeType, 'value'=> $this->AttributeValue @@ -348,7 +356,7 @@ function __construct() - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName */ $this->RelativeDistinguishedName = array( - 'type' => FILE_ASN1_TYPE_SET, + 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $AttributeTypeAndValue @@ -356,7 +364,7 @@ function __construct() // http://tools.ietf.org/html/rfc5280#section-4.1.2.4 $RDNSequence = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, // RDNSequence does not define a min or a max, which means it doesn't have one 'min' => 0, 'max' => -1, @@ -364,7 +372,7 @@ function __construct() ); $this->Name = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'rdnSequence' => $RDNSequence ) @@ -372,11 +380,11 @@ function __construct() // http://tools.ietf.org/html/rfc5280#section-4.1.1.2 $AlgorithmIdentifier = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( - 'algorithm' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'parameters' => array( - 'type' => FILE_ASN1_TYPE_ANY, + 'type' => ASN1::TYPE_ANY, 'optional' => true ) ) @@ -390,20 +398,20 @@ function __construct() http://tools.ietf.org/html/rfc5280#section-4.2 */ $Extension = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( - 'extnId' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'extnId' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'critical' => array( - 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'type' => ASN1::TYPE_BOOLEAN, 'optional' => true, 'default' => false ), - 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING) + 'extnValue' => array('type' => ASN1::TYPE_OCTET_STRING) ) ); $this->Extensions = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, // technically, it's MAX, but we'll assume anything < 0 is MAX 'max' => -1, @@ -412,42 +420,42 @@ function __construct() ); $SubjectPublicKeyInfo = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'algorithm' => $AlgorithmIdentifier, - 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING) ) ); - $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING); + $UniqueIdentifier = array('type' => ASN1::TYPE_BIT_STRING); $Time = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( - 'utcTime' => array('type' => FILE_ASN1_TYPE_UTC_TIME), - 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME) + 'utcTime' => array('type' => ASN1::TYPE_UTC_TIME), + 'generalTime' => array('type' => ASN1::TYPE_GENERALIZED_TIME) ) ); // http://tools.ietf.org/html/rfc5280#section-4.1.2.5 $Validity = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'notBefore' => $Time, 'notAfter' => $Time ) ); - $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER); + $CertificateSerialNumber = array('type' => ASN1::TYPE_INTEGER); $Version = array( - 'type' => FILE_ASN1_TYPE_INTEGER, + 'type' => ASN1::TYPE_INTEGER, 'mapping' => array('v1', 'v2', 'v3') ); // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm']) $TBSCertificate = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( // technically, default implies optional, but we'll define it as being optional, none-the-less, just to // reenforce that fact @@ -485,16 +493,16 @@ function __construct() ); $this->Certificate = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'tbsCertificate' => $TBSCertificate, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $this->KeyUsage = array( - 'type' => FILE_ASN1_TYPE_BIT_STRING, + 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => array( 'digitalSignature', 'nonRepudiation', @@ -509,52 +517,52 @@ function __construct() ); $this->BasicConstraints = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'cA' => array( - 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'type' => ASN1::TYPE_BOOLEAN, 'optional' => true, 'default' => false ), 'pathLenConstraint' => array( - 'type' => FILE_ASN1_TYPE_INTEGER, + 'type' => ASN1::TYPE_INTEGER, 'optional' => true ) ) ); - $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING); + $this->KeyIdentifier = array('type' => ASN1::TYPE_OCTET_STRING); $OrganizationalUnitNames = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 4, // ub-organizational-units - 'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + 'children' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ); $PersonalName = array( - 'type' => FILE_ASN1_TYPE_SET, + 'type' => ASN1::TYPE_SET, 'children' => array( 'surname' => array( - 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ), 'given-name' => array( - 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ), 'initials' => array( - 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true ), 'generation-qualifier' => array( - 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 3, 'optional' => true, 'implicit' => true @@ -562,52 +570,52 @@ function __construct() ) ); - $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING); + $NumericUserIdentifier = array('type' => ASN1::TYPE_NUMERIC_STRING); - $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING); + $OrganizationName = array('type' => ASN1::TYPE_PRINTABLE_STRING); $PrivateDomainName = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( - 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), - 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), + 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); - $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING); + $TerminalIdentifier = array('type' => ASN1::TYPE_PRINTABLE_STRING); - $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING); + $NetworkAddress = array('type' => ASN1::TYPE_NUMERIC_STRING); $AdministrationDomainName = array( - 'type' => FILE_ASN1_TYPE_CHOICE, - // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or - // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC - 'class' => FILE_ASN1_CLASS_APPLICATION, + 'type' => ASN1::TYPE_CHOICE, + // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or + // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC + 'class' => ASN1::CLASS_APPLICATION, 'cast' => 2, 'children' => array( - 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), - 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), + 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $CountryName = array( - 'type' => FILE_ASN1_TYPE_CHOICE, - // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or - // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC - 'class' => FILE_ASN1_CLASS_APPLICATION, + 'type' => ASN1::TYPE_CHOICE, + // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or + // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC + 'class' => ASN1::CLASS_APPLICATION, 'cast' => 1, 'children' => array( - 'x121-dcc-code' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), - 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + 'x121-dcc-code' => array('type' => ASN1::TYPE_NUMERIC_STRING), + 'iso-3166-alpha2-code' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $AnotherName = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( - 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'type-id' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'value' => array( - 'type' => FILE_ASN1_TYPE_ANY, + 'type' => ASN1::TYPE_ANY, 'constant' => 0, 'optional' => true, 'explicit' => true @@ -616,16 +624,16 @@ function __construct() ); $ExtensionAttribute = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'extension-attribute-type' => array( - 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, + 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ), 'extension-attribute-value' => array( - 'type' => FILE_ASN1_TYPE_ANY, + 'type' => ASN1::TYPE_ANY, 'constant' => 1, 'optional' => true, 'explicit' => true @@ -634,29 +642,29 @@ function __construct() ); $ExtensionAttributes = array( - 'type' => FILE_ASN1_TYPE_SET, + 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => 256, // ub-extension-attributes 'children' => $ExtensionAttribute ); $BuiltInDomainDefinedAttribute = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( - 'type' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING), - 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) + 'type' => array('type' => ASN1::TYPE_PRINTABLE_STRING), + 'value' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $BuiltInDomainDefinedAttributes = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 4, // ub-domain-defined-attributes 'children' => $BuiltInDomainDefinedAttribute ); $BuiltInStandardAttributes = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'country-name' => array('optional' => true) + $CountryName, 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName, @@ -699,7 +707,7 @@ function __construct() ); $ORAddress = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'built-in-standard-attributes' => $BuiltInStandardAttributes, 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes, @@ -708,14 +716,14 @@ function __construct() ); $EDIPartyName = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'nameAssigner' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $this->DirectoryString, - // partyName is technically required but ASN1 doesn't currently support non-optional constants and + // partyName is technically required but \phpseclib\File\ASN1 doesn't currently support non-optional constants and // setting it to optional gets the job done in any event. 'partyName' => array( 'constant' => 1, @@ -726,7 +734,7 @@ function __construct() ); $GeneralName = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'otherName' => array( 'constant' => 0, @@ -734,13 +742,13 @@ function __construct() 'implicit' => true ) + $AnotherName, 'rfc822Name' => array( - 'type' => FILE_ASN1_TYPE_IA5_STRING, + 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ), 'dNSName' => array( - 'type' => FILE_ASN1_TYPE_IA5_STRING, + 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true @@ -761,19 +769,19 @@ function __construct() 'implicit' => true ) + $EDIPartyName, 'uniformResourceIdentifier' => array( - 'type' => FILE_ASN1_TYPE_IA5_STRING, + 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 6, 'optional' => true, 'implicit' => true ), 'iPAddress' => array( - 'type' => FILE_ASN1_TYPE_OCTET_STRING, + 'type' => ASN1::TYPE_OCTET_STRING, 'constant' => 7, 'optional' => true, 'implicit' => true ), 'registeredID' => array( - 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER, + 'type' => ASN1::TYPE_OBJECT_IDENTIFIER, 'constant' => 8, 'optional' => true, 'implicit' => true @@ -782,7 +790,7 @@ function __construct() ); $GeneralNames = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $GeneralName @@ -791,7 +799,7 @@ function __construct() $this->IssuerAltName = $GeneralNames; $ReasonFlags = array( - 'type' => FILE_ASN1_TYPE_BIT_STRING, + 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => array( 'unused', 'keyCompromise', @@ -806,7 +814,7 @@ function __construct() ); $DistributionPointName = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'fullName' => array( 'constant' => 0, @@ -822,7 +830,7 @@ function __construct() ); $DistributionPoint = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'distributionPoint' => array( 'constant' => 0, @@ -843,14 +851,14 @@ function __construct() ); $this->CRLDistributionPoints = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $DistributionPoint ); $this->AuthorityKeyIdentifier = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'keyIdentifier' => array( 'constant' => 0, @@ -870,24 +878,24 @@ function __construct() ) ); - $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + $PolicyQualifierId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $PolicyQualifierInfo = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'policyQualifierId' => $PolicyQualifierId, - 'qualifier' => array('type' => FILE_ASN1_TYPE_ANY) + 'qualifier' => array('type' => ASN1::TYPE_ANY) ) ); - $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + $CertPolicyId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $PolicyInformation = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'policyIdentifier' => $CertPolicyId, 'policyQualifiers' => array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 0, 'max' => -1, 'optional' => true, @@ -897,18 +905,18 @@ function __construct() ); $this->CertificatePolicies = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $PolicyInformation ); $this->PolicyMappings = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'issuerDomainPolicy' => $CertPolicyId, 'subjectDomainPolicy' => $CertPolicyId @@ -916,25 +924,25 @@ function __construct() ) ); - $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + $KeyPurposeId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $this->ExtKeyUsageSyntax = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $KeyPurposeId ); $AccessDescription = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( - 'accessMethod' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), + 'accessMethod' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'accessLocation' => $GeneralName ) ); $this->AuthorityInfoAccessSyntax = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $AccessDescription @@ -943,25 +951,25 @@ function __construct() $this->SubjectAltName = $GeneralNames; $this->PrivateKeyUsagePeriod = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'notBefore' => array( 'constant' => 0, 'optional' => true, 'implicit' => true, - 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME), + 'type' => ASN1::TYPE_GENERALIZED_TIME), 'notAfter' => array( 'constant' => 1, 'optional' => true, 'implicit' => true, - 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME) + 'type' => ASN1::TYPE_GENERALIZED_TIME) ) ); - $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER); + $BaseDistance = array('type' => ASN1::TYPE_INTEGER); $GeneralSubtree = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'base' => $GeneralName, 'minimum' => array( @@ -979,14 +987,14 @@ function __construct() ); $GeneralSubtrees = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $GeneralSubtree ); $this->NameConstraints = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'permittedSubtrees' => array( 'constant' => 0, @@ -1001,33 +1009,33 @@ function __construct() ) ); - $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING); + $this->CPSuri = array('type' => ASN1::TYPE_IA5_STRING); $DisplayText = array( - 'type' => FILE_ASN1_TYPE_CHOICE, + 'type' => ASN1::TYPE_CHOICE, 'children' => array( - 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING), - 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING), - 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING), - 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING) + 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), + 'visibleString' => array('type' => ASN1::TYPE_VISIBLE_STRING), + 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING), + 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING) ) ); $NoticeReference = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'organization' => $DisplayText, 'noticeNumbers' => array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 200, - 'children' => array('type' => FILE_ASN1_TYPE_INTEGER) + 'children' => array('type' => ASN1::TYPE_INTEGER) ) ) ); $this->UserNotice = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'noticeRef' => array( 'optional' => true, @@ -1042,7 +1050,7 @@ function __construct() // mapping is from $this->netscape_cert_type = array( - 'type' => FILE_ASN1_TYPE_BIT_STRING, + 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => array( 'SSLClient', 'SSLServer', @@ -1055,17 +1063,17 @@ function __construct() ) ); - $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING); - $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING); + $this->netscape_comment = array('type' => ASN1::TYPE_IA5_STRING); + $this->netscape_ca_policy_url = array('type' => ASN1::TYPE_IA5_STRING); // attribute is used in RFC2986 but we're using the RFC5280 definition $Attribute = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'type' => $AttributeType, 'value'=> array( - 'type' => FILE_ASN1_TYPE_SET, + 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $this->AttributeValue @@ -1073,20 +1081,27 @@ function __construct() ) ); + $this->SubjectDirectoryAttributes = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $Attribute + ); + // adapted from $Attributes = array( - 'type' => FILE_ASN1_TYPE_SET, + 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $Attribute ); $CertificationRequestInfo = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'version' => array( - 'type' => FILE_ASN1_TYPE_INTEGER, + 'type' => ASN1::TYPE_INTEGER, 'mapping' => array('v1') ), 'subject' => $this->Name, @@ -1100,16 +1115,16 @@ function __construct() ); $this->CertificationRequest = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'certificationRequestInfo' => $CertificationRequestInfo, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $RevokedCertificate = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'userCertificate' => $CertificateSerialNumber, 'revocationDate' => $Time, @@ -1120,7 +1135,7 @@ function __construct() ); $TBSCertList = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'version' => array( 'optional' => true, @@ -1133,7 +1148,7 @@ function __construct() 'optional' => true ) + $Time, 'revokedCertificates' => array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'optional' => true, 'min' => 0, 'max' => -1, @@ -1148,17 +1163,17 @@ function __construct() ); $this->CertificateList = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'tbsCertList' => $TBSCertList, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); - $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER); + $this->CRLNumber = array('type' => ASN1::TYPE_INTEGER); - $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED, + $this->CRLReason = array('type' => ASN1::TYPE_ENUMERATED, 'mapping' => array( 'unspecified', 'keyCompromise', @@ -1174,7 +1189,7 @@ function __construct() ) ); - $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE, + $this->IssuingDistributionPoint = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'distributionPoint' => array( 'constant' => 0, @@ -1182,14 +1197,14 @@ function __construct() 'explicit' => true ) + $DistributionPointName, 'onlyContainsUserCerts' => array( - 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 1, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlyContainsCACerts' => array( - 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 2, 'optional' => true, 'default' => false, @@ -1201,14 +1216,14 @@ function __construct() 'implicit' => true ) + $ReasonFlags, 'indirectCRL' => array( - 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 4, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlyContainsAttributeCerts' => array( - 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 5, 'optional' => true, 'default' => false, @@ -1217,29 +1232,37 @@ function __construct() ) ); - $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME); + $this->InvalidityDate = array('type' => ASN1::TYPE_GENERALIZED_TIME); $this->CertificateIssuer = $GeneralNames; - $this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); + $this->HoldInstructionCode = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $PublicKeyAndChallenge = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'spki' => $SubjectPublicKeyInfo, - 'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING) + 'challenge' => array('type' => ASN1::TYPE_IA5_STRING) ) ); $this->SignedPublicKeyAndChallenge = array( - 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'publicKeyAndChallenge' => $PublicKeyAndChallenge, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); + $this->PostalAddress = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'optional' => true, + 'min' => 1, + 'max' => -1, + 'children' => $this->DirectoryString + ); + // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 $this->oids = array( '1.3.6.1.5.5.7' => 'id-pkix', @@ -1274,6 +1297,7 @@ function __construct() '2.5.4.9' => 'id-at-streetAddress', '2.5.4.45' => 'id-at-uniqueIdentifier', '2.5.4.72' => 'id-at-role', + '2.5.4.16' => 'id-at-postalAddress', '0.9.2342.19200300.100.1.25' => 'id-domainComponent', '1.2.840.113549.1.9' => 'pkcs-9', @@ -1411,11 +1435,12 @@ function __construct() * * Returns an associative array describing the X.509 cert or a false if the cert failed to load * - * @param String $cert + * @param string $cert + * @param int $mode * @access public - * @return Mixed + * @return mixed */ - function loadX509($cert) + function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($cert) && isset($cert['tbsCertificate'])) { unset($this->currentCert); @@ -1436,7 +1461,13 @@ function loadX509($cert) $asn1 = new ASN1(); - $cert = $this->_extractBER($cert); + if ($mode != self::FORMAT_DER) { + $newcert = $this->_extractBER($cert); + if ($mode == self::FORMAT_PEM && $cert == $newcert) { + return false; + } + $cert = $newcert; + } if ($cert === false) { $this->currentCert = false; @@ -1456,7 +1487,11 @@ function loadX509($cert) $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); + if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) { + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); + } + $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); + $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); @@ -1473,12 +1508,12 @@ function loadX509($cert) /** * Save X.509 certificate * - * @param Array $cert - * @param Integer $format optional + * @param array $cert + * @param int $format optional * @access public - * @return String + * @return string */ - function saveX509($cert, $format = FILE_X509_FORMAT_PEM) + function saveX509($cert, $format = self::FORMAT_PEM) { if (!is_array($cert) || !isset($cert['tbsCertificate'])) { return false; @@ -1493,7 +1528,17 @@ function saveX509($cert, $format = FILE_X509_FORMAT_PEM) switch ($algorithm) { case 'rsaEncryption': $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] - = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); + = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); + /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier." + -- https://tools.ietf.org/html/rfc3279#section-2.3.1 + + given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank, + it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever. + */ + $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null; + // https://tools.ietf.org/html/rfc3279#section-2.2.1 + $cert['signatureAlgorithm']['parameters'] = null; + $cert['tbsCertificate']['signature']['parameters'] = null; } } @@ -1501,7 +1546,7 @@ function saveX509($cert, $format = FILE_X509_FORMAT_PEM) $asn1->loadOIDs($this->oids); $filters = array(); - $type_utf8_string = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + $type_utf8_string = array('type' => ASN1::TYPE_UTF8_STRING); $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string; $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string; @@ -1513,25 +1558,27 @@ function saveX509($cert, $format = FILE_X509_FORMAT_PEM) $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string; $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string; - /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING. - FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random + /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib\File\ASN1::TYPE_IA5_STRING. + \phpseclib\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random characters. */ $filters['policyQualifiers']['qualifier'] - = array('type' => FILE_ASN1_TYPE_IA5_STRING); + = array('type' => ASN1::TYPE_IA5_STRING); $asn1->loadFilters($filters); $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1); + $this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1); + $this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1); $cert = $asn1->encodeDER($cert, $this->Certificate); switch ($format) { - case FILE_X509_FORMAT_DER: + case self::FORMAT_DER: return $cert; - // case FILE_X509_FORMAT_PEM: + // case self::FORMAT_PEM: default: - return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----'; + return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(Base64::encode($cert), 64) . '-----END CERTIFICATE-----'; } } @@ -1539,20 +1586,20 @@ function saveX509($cert, $format = FILE_X509_FORMAT_PEM) * Map extension values from octet string to extension-specific internal * format. * - * @param Array ref $root - * @param String $path - * @param Object $asn1 + * @param array ref $root + * @param string $path + * @param object $asn1 * @access private */ function _mapInExtensions(&$root, $path, $asn1) { - $extensions = &$this->_subArray($root, $path); + $extensions = &$this->_subArrayUnchecked($root, $path); - if (is_array($extensions)) { + if ($extensions) { for ($i = 0; $i < count($extensions); $i++) { $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; - $value = base64_decode($value); + $value = Base64::decode($value); $decoded = $asn1->decodeBER($value); /* [extnValue] contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID */ @@ -1578,8 +1625,8 @@ function _mapInExtensions(&$root, $path, $asn1) } } } - } elseif ($map) { - $value = base64_encode($value); + } else { + $value = Base64::encode($value); } } } @@ -1589,9 +1636,9 @@ function _mapInExtensions(&$root, $path, $asn1) * Map extension values from extension-specific internal format to * octet string. * - * @param Array ref $root - * @param String $path - * @param Object $asn1 + * @param array ref $root + * @param string $path + * @param object $asn1 * @access private */ function _mapOutExtensions(&$root, $path, $asn1) @@ -1601,6 +1648,10 @@ function _mapOutExtensions(&$root, $path, $asn1) if (is_array($extensions)) { $size = count($extensions); for ($i = 0; $i < $size; $i++) { + if ($extensions[$i] instanceof Element) { + continue; + } + $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; @@ -1615,9 +1666,9 @@ function _mapOutExtensions(&$root, $path, $asn1) $map = $this->_getMapping($subid); $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; if ($map !== false) { - // by default ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's - // actual type is FILE_ASN1_TYPE_ANY - $subvalue = new ASN1_Element($asn1->encodeDER($subvalue, $map)); + // by default \phpseclib\File\ASN1 will try to render qualifier as a \phpseclib\File\ASN1::TYPE_IA5_STRING since it's + // actual type is \phpseclib\File\ASN1::TYPE_ANY + $subvalue = new Element($asn1->encodeDER($subvalue, $map)); } } } @@ -1625,8 +1676,8 @@ function _mapOutExtensions(&$root, $path, $asn1) case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string if (isset($value['authorityCertSerialNumber'])) { if ($value['authorityCertSerialNumber']->toBytes() == '') { - $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; - $value['authorityCertSerialNumber'] = new ASN1_Element($temp); + $temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; + $value['authorityCertSerialNumber'] = new Element($temp); } } } @@ -1636,12 +1687,12 @@ function _mapOutExtensions(&$root, $path, $asn1) $map = $this->_getMapping($id); if (is_bool($map)) { if (!$map) { - user_error($id . ' is not a currently supported extension'); + //user_error($id . ' is not a currently supported extension'); unset($extensions[$i]); } } else { $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); - $value = base64_encode($temp); + $value = Base64::encode($temp); } } } @@ -1651,9 +1702,9 @@ function _mapOutExtensions(&$root, $path, $asn1) * Map attribute values from ANY type to attribute-specific internal * format. * - * @param Array ref $root - * @param String $path - * @param Object $asn1 + * @param array ref $root + * @param string $path + * @param object $asn1 * @access private */ function _mapInAttributes(&$root, $path, $asn1) @@ -1676,11 +1727,11 @@ function _mapInAttributes(&$root, $path, $asn1) if ($mapped !== false) { $values[$j] = $mapped; } - if ($id == 'pkcs-9-at-extensionRequest') { + if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { $this->_mapInExtensions($values, $j, $asn1); } } elseif ($map) { - $values[$j] = base64_encode($value); + $values[$j] = Base64::encode($value); } } } @@ -1692,9 +1743,9 @@ function _mapInAttributes(&$root, $path, $asn1) * Map attribute values from attribute-specific internal format to * ANY type. * - * @param Array ref $root - * @param String $path - * @param Object $asn1 + * @param array ref $root + * @param string $path + * @param object $asn1 * @access private */ function _mapOutAttributes(&$root, $path, $asn1) @@ -1709,7 +1760,7 @@ function _mapOutAttributes(&$root, $path, $asn1) $id = $attributes[$i]['type']; $map = $this->_getMapping($id); if ($map === false) { - user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); unset($attributes[$i]); } elseif (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; @@ -1731,16 +1782,78 @@ function _mapOutAttributes(&$root, $path, $asn1) } } + /** + * Map DN values from ANY type to DN-specific internal + * format. + * + * @param array ref $root + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapInDNs(&$root, $path, $asn1) + { + $dns = &$this->_subArray($root, $path); + + if (is_array($dns)) { + for ($i = 0; $i < count($dns); $i++) { + for ($j = 0; $j < count($dns[$i]); $j++) { + $type = $dns[$i][$j]['type']; + $value = &$dns[$i][$j]['value']; + if (is_object($value) && $value instanceof Element) { + $map = $this->_getMapping($type); + if (!is_bool($map)) { + $decoded = $asn1->decodeBER($value); + $value = $asn1->asn1map($decoded[0], $map); + } + } + } + } + } + } + + /** + * Map DN values from DN-specific internal format to + * ANY type. + * + * @param array ref $root + * @param string $path + * @param object $asn1 + * @access private + */ + function _mapOutDNs(&$root, $path, $asn1) + { + $dns = &$this->_subArray($root, $path); + + if (is_array($dns)) { + $size = count($dns); + for ($i = 0; $i < $size; $i++) { + for ($j = 0; $j < count($dns[$i]); $j++) { + $type = $dns[$i][$j]['type']; + $value = &$dns[$i][$j]['value']; + if (is_object($value) && $value instanceof Element) { + continue; + } + + $map = $this->_getMapping($type); + if (!is_bool($map)) { + $value = new Element($asn1->encodeDER($value, $map)); + } + } + } + } + } + /** * Associate an extension ID to an extension mapping * - * @param String $extnId + * @param string $extnId * @access private - * @return Mixed + * @return mixed */ function _getMapping($extnId) { - if (!is_string($extnId)) { // eg. if it's a ASN1_Element object + if (!is_string($extnId)) { // eg. if it's a \phpseclib\File\ASN1\Element object return true; } @@ -1763,6 +1876,8 @@ function _getMapping($extnId) return $this->AuthorityInfoAccessSyntax; case 'id-ce-subjectAltName': return $this->SubjectAltName; + case 'id-ce-subjectDirectoryAttributes': + return $this->SubjectDirectoryAttributes; case 'id-ce-privateKeyUsagePeriod': return $this->PrivateKeyUsagePeriod; case 'id-ce-issuerAltName': @@ -1822,6 +1937,8 @@ function _getMapping($extnId) return $this->CertificateIssuer; case 'id-ce-holdInstructionCode': return $this->HoldInstructionCode; + case 'id-at-postalAddress': + return $this->PostalAddress; } return false; @@ -1830,9 +1947,9 @@ function _getMapping($extnId) /** * Load an X.509 certificate as a certificate authority * - * @param String $cert + * @param string $cert * @access public - * @return Boolean + * @return bool */ function loadCA($cert) { @@ -1897,9 +2014,9 @@ function loadCA($cert) * component or component fragment. E.g., *.a.com matches foo.a.com but * not bar.foo.a.com. f*.com matches foo.com but not bar.com. * - * @param String $url + * @param string $url * @access public - * @return Boolean + * @return bool */ function validateURL($url) { @@ -1955,7 +2072,7 @@ function validateURL($url) * * If $date isn't defined it is assumed to be the current date. * - * @param Integer $date optional + * @param int $date optional * @access public */ function validateDate($date = null) @@ -1994,9 +2111,9 @@ function validateDate($date = null) * * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. * - * @param Boolean $caonly optional + * @param bool $caonly optional * @access public - * @return Mixed + * @return mixed */ function validateSignature($caonly = true) { @@ -2013,14 +2130,16 @@ function validateSignature($caonly = true) switch (true) { case isset($this->currentCert['tbsCertificate']): // self-signed cert - if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) { - $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); - $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); - switch (true) { - case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - $signingCert = $this->currentCert; // working cert - } + switch (true) { + case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']: + case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $this->currentCert; // working cert + } } if (!empty($this->CAs)) { @@ -2028,15 +2147,17 @@ function validateSignature($caonly = true) // even if the cert is a self-signed one we still want to see if it's a CA; // if not, we'll conditionally return an error $ca = $this->CAs[$i]; - if ($this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { - $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); - $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); - switch (true) { - case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - $signingCert = $ca; // working cert - break 2; - } + switch (true) { + case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']: + case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 3; + } } } if (count($this->CAs) == $i && $caonly) { @@ -2049,7 +2170,7 @@ function validateSignature($caonly = true) $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['certificationRequestInfo']): @@ -2057,7 +2178,7 @@ function validateSignature($caonly = true) $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['publicKeyAndChallenge']): @@ -2065,22 +2186,24 @@ function validateSignature($caonly = true) $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['tbsCertList']): if (!empty($this->CAs)) { for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; - if ($this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) { - $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); - $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); - switch (true) { - case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - $signingCert = $ca; // working cert - break 2; - } + switch (true) { + case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']: + case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 3; + } } } } @@ -2091,7 +2214,7 @@ function validateSignature($caonly = true) $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); default: @@ -2102,22 +2225,24 @@ function validateSignature($caonly = true) /** * Validates a signature * - * Returns true if the signature is verified, false if it is not correct or null on error + * Returns true if the signature is verified and false if it is not correct. + * If the algorithms are unsupposed an exception is thrown. * - * @param String $publicKeyAlgorithm - * @param String $publicKey - * @param String $signatureAlgorithm - * @param String $signature - * @param String $signatureSubject + * @param string $publicKeyAlgorithm + * @param string $publicKey + * @param string $signatureAlgorithm + * @param string $signature + * @param string $signatureSubject * @access private - * @return Integer + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported + * @return bool */ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) { switch ($publicKeyAlgorithm) { case 'rsaEncryption': $rsa = new RSA(); - $rsa->loadKey($publicKey); + $rsa->load($publicKey); switch ($signatureAlgorithm) { case 'md2WithRSAEncryption': @@ -2128,17 +2253,16 @@ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); - $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); - if (!@$rsa->verify($signatureSubject, $signature)) { + if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) { return false; } break; default: - return null; + throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } break; default: - return null; + throw new UnsupportedAlgorithmException('Public key algorithm unsupported'); } return true; @@ -2149,22 +2273,22 @@ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm * * Reformats a public key to a format supported by phpseclib (if applicable) * - * @param String $algorithm - * @param String $key + * @param string $algorithm + * @param string $key * @access private - * @return String + * @return string */ function _reformatKey($algorithm, $key) { switch ($algorithm) { case 'rsaEncryption': return - "-----BEGIN PUBLIC KEY-----\r\n" . + "-----BEGIN RSA PUBLIC KEY-----\r\n" . // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do. - chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) . - '-----END PUBLIC KEY-----'; + chunk_split(Base64::encode(substr(Base64::decode($key), 1)), 64) . + '-----END RSA PUBLIC KEY-----'; default: return $key; } @@ -2175,15 +2299,13 @@ function _reformatKey($algorithm, $key) * * Takes in a base64 encoded "blob" and returns a human readable IP address * - * @param String $ip + * @param string $ip * @access private - * @return String + * @return string */ function _decodeIP($ip) { - $ip = base64_decode($ip); - list(, $ip) = unpack('N', $ip); - return long2ip($ip); + return inet_ntop(Base64::decode($ip)); } /** @@ -2191,21 +2313,21 @@ function _decodeIP($ip) * * Takes a human readable IP address into a base64-encoded "blob" * - * @param String $ip + * @param string $ip * @access private - * @return String + * @return string */ function _encodeIP($ip) { - return base64_encode(pack('N', ip2long($ip))); + return Base64::encode(inet_pton($ip)); } /** * "Normalizes" a Distinguished Name property * - * @param String $propName + * @param string $propName * @access private - * @return Mixed + * @return mixed */ function _translateDNProp($propName) { @@ -2284,6 +2406,9 @@ function _translateDNProp($propName) case 'uniqueidentifier': case 'x500uniqueidentifier': return 'id-at-uniqueIdentifier'; + case 'postaladdress': + case 'id-at-postaladdress': + return 'id-at-postalAddress'; default: return false; } @@ -2292,11 +2417,11 @@ function _translateDNProp($propName) /** * Set a Distinguished Name property * - * @param String $propName - * @param Mixed $propValue - * @param String $type optional + * @param string $propName + * @param mixed $propValue + * @param string $type optional * @access public - * @return Boolean + * @return bool */ function setDNProp($propName, $propValue, $type = 'utf8String') { @@ -2326,7 +2451,7 @@ function setDNProp($propName, $propValue, $type = 'utf8String') /** * Remove Distinguished Name properties * - * @param String $propName + * @param string $propName * @access public */ function removeDNProp($propName) @@ -2353,10 +2478,10 @@ function removeDNProp($propName) /** * Get Distinguished Name properties * - * @param String $propName - * @param Array $dn optional - * @param Boolean $withType optional - * @return Mixed + * @param string $propName + * @param array $dn optional + * @param bool $withType optional + * @return mixed * @access public */ function getDNProp($propName, $dn = null, $withType = false) @@ -2373,25 +2498,38 @@ function getDNProp($propName, $dn = null, $withType = false) return false; } + $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); $dn = $dn['rdnSequence']; $result = array(); - $asn1 = new ASN1(); for ($i = 0; $i < count($dn); $i++) { if ($dn[$i][0]['type'] == $propName) { $v = $dn[$i][0]['value']; - if (!$withType && is_array($v)) { - foreach ($v as $type => $s) { - $type = array_search($type, $asn1->ANYmap, true); - if ($type !== false && isset($asn1->stringTypeSize[$type])) { - $s = $asn1->convert($s, $type); - if ($s !== false) { - $v = $s; - break; + if (!$withType) { + if (is_array($v)) { + foreach ($v as $type => $s) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $s = $asn1->convert($s, $type); + if ($s !== false) { + $v = $s; + break; + } } } - } - if (is_array($v)) { - $v = array_pop($v); // Always strip data type. + if (is_array($v)) { + $v = array_pop($v); // Always strip data type. + } + } elseif (is_object($v) && $v instanceof Element) { + $map = $this->_getMapping($propName); + if (!is_bool($map)) { + $decoded = $asn1->decodeBER($v); + $v = $asn1->asn1map($decoded[0], $map); + } } } $result[] = $v; @@ -2404,11 +2542,11 @@ function getDNProp($propName, $dn = null, $withType = false) /** * Set a Distinguished Name * - * @param Mixed $dn - * @param Boolean $merge optional - * @param String $type optional + * @param mixed $dn + * @param bool $merge optional + * @param string $type optional * @access public - * @return Boolean + * @return bool */ function setDN($dn, $merge = false, $type = 'utf8String') { @@ -2432,7 +2570,7 @@ function setDN($dn, $merge = false, $type = 'utf8String') } // handles everything else - $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); + $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); for ($i = 1; $i < count($results); $i+=2) { $prop = trim($results[$i], ', =/'); $value = $results[$i + 1]; @@ -2447,55 +2585,42 @@ function setDN($dn, $merge = false, $type = 'utf8String') /** * Get the Distinguished Name for a certificates subject * - * @param Mixed $format optional - * @param Array $dn optional + * @param mixed $format optional + * @param array $dn optional * @access public - * @return Boolean + * @return bool */ - function getDN($format = FILE_X509_DN_ARRAY, $dn = null) + function getDN($format = self::DN_ARRAY, $dn = null) { if (!isset($dn)) { $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; } switch ((int) $format) { - case FILE_X509_DN_ARRAY: + case self::DN_ARRAY: return $dn; - case FILE_X509_DN_ASN1: + case self::DN_ASN1: $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); - $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); return $asn1->encodeDER($dn, $this->Name); - case FILE_X509_DN_OPENSSL: - $dn = $this->getDN(FILE_X509_DN_STRING, $dn); - if ($dn === false) { - return false; - } - $attrs = preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); - $dn = array(); - for ($i = 1; $i < count($attrs); $i += 2) { - $prop = trim($attrs[$i], ', =/'); - $value = $attrs[$i + 1]; - if (!isset($dn[$prop])) { - $dn[$prop] = $value; - } else { - $dn[$prop] = array_merge((array) $dn[$prop], array($value)); - } - } - return $dn; - case FILE_X509_DN_CANON: + case self::DN_CANON: // No SEQUENCE around RDNs and all string values normalized as - // trimmed lowercase UTF-8 with all spacing as one blank. + // trimmed lowercase UTF-8 with all spacing as one blank. + // constructed RDNs will not be canonicalized $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); - $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); $result = ''; + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); foreach ($dn['rdnSequence'] as $rdn) { - foreach ($rdn as &$attr) { + foreach ($rdn as $i => $attr) { + $attr = &$rdn[$i]; if (is_array($attr['value'])) { foreach ($attr['value'] as $type => $v) { $type = array_search($type, $asn1->ANYmap, true); @@ -2513,18 +2638,26 @@ function getDN($format = FILE_X509_DN_ARRAY, $dn = null) $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName); } return $result; - case FILE_X509_DN_HASH: - $dn = $this->getDN(FILE_X509_DN_CANON, $dn); + case self::DN_HASH: + $dn = $this->getDN(self::DN_CANON, $dn); $hash = new Hash('sha1'); - $hash = $hash->_hash($dn); + $hash = $hash->hash($dn); extract(unpack('Vhash', $hash)); - return strtolower(bin2hex(pack('N', $hash))); + return strtolower(Hex::encode(pack('N', $hash))); } - // Defaut is to return a string. + // Default is to return a string. $start = true; $output = ''; + + $result = array(); $asn1 = new ASN1(); + $asn1->loadOIDs($this->oids); + $filters = array(); + $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $asn1->loadFilters($filters); + $this->_mapOutDNs($dn, 'rdnSequence', $asn1); + foreach ($dn['rdnSequence'] as $field) { $prop = $field[0]['type']; $value = $field[0]['value']; @@ -2532,33 +2665,37 @@ function getDN($format = FILE_X509_DN_ARRAY, $dn = null) $delim = ', '; switch ($prop) { case 'id-at-countryName': - $desc = 'C='; + $desc = 'C'; break; case 'id-at-stateOrProvinceName': - $desc = 'ST='; + $desc = 'ST'; break; case 'id-at-organizationName': - $desc = 'O='; + $desc = 'O'; break; case 'id-at-organizationalUnitName': - $desc = 'OU='; + $desc = 'OU'; break; case 'id-at-commonName': - $desc = 'CN='; + $desc = 'CN'; break; case 'id-at-localityName': - $desc = 'L='; + $desc = 'L'; break; case 'id-at-surname': - $desc = 'SN='; + $desc = 'SN'; break; case 'id-at-uniqueIdentifier': $delim = '/'; - $desc = 'x500UniqueIdentifier='; + $desc = 'x500UniqueIdentifier'; + break; + case 'id-at-postalAddress': + $delim = '/'; + $desc = 'postalAddress'; break; default: $delim = '/'; - $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop) . '='; + $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop); } if (!$start) { @@ -2578,22 +2715,28 @@ function getDN($format = FILE_X509_DN_ARRAY, $dn = null) if (is_array($value)) { $value = array_pop($value); // Always strip data type. } + } elseif (is_object($value) && $value instanceof Element) { + $callback = create_function('$x', 'return "\x" . bin2hex($x[0]);'); + $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); } - $output.= $desc . $value; + $output.= $desc . '=' . $value; + $result[$desc] = isset($result[$desc]) ? + array_merge((array) $dn[$prop], array($value)) : + $value; $start = false; } - return $output; + return $format == self::DN_OPENSSL ? $result : $output; } /** * Get the Distinguished Name for a certificate/crl issuer * - * @param Integer $format optional + * @param int $format optional * @access public - * @return Mixed + * @return mixed */ - function getIssuerDN($format = FILE_X509_DN_ARRAY) + function getIssuerDN($format = self::DN_ARRAY) { switch (true) { case !isset($this->currentCert) || !is_array($this->currentCert): @@ -2611,11 +2754,11 @@ function getIssuerDN($format = FILE_X509_DN_ARRAY) * Get the Distinguished Name for a certificate/csr subject * Alias of getDN() * - * @param Integer $format optional + * @param int $format optional * @access public - * @return Mixed + * @return mixed */ - function getSubjectDN($format = FILE_X509_DN_ARRAY) + function getSubjectDN($format = self::DN_ARRAY) { switch (true) { case !empty($this->dn): @@ -2634,10 +2777,10 @@ function getSubjectDN($format = FILE_X509_DN_ARRAY) /** * Get an individual Distinguished Name property for a certificate/crl issuer * - * @param String $propName - * @param Boolean $withType optional + * @param string $propName + * @param bool $withType optional * @access public - * @return Mixed + * @return mixed */ function getIssuerDNProp($propName, $withType = false) { @@ -2656,10 +2799,10 @@ function getIssuerDNProp($propName, $withType = false) /** * Get an individual Distinguished Name property for a certificate/csr subject * - * @param String $propName - * @param Boolean $withType optional + * @param string $propName + * @param bool $withType optional * @access public - * @return Mixed + * @return mixed */ function getSubjectDNProp($propName, $withType = false) { @@ -2681,7 +2824,7 @@ function getSubjectDNProp($propName, $withType = false) * Get the certificate chain for the current cert * * @access public - * @return Mixed + * @return mixed */ function getChain() { @@ -2715,8 +2858,8 @@ function getChain() break; } } - foreach ($chain as $key=>$value) { - $chain[$key] = new X059(); + foreach ($chain as $key => $value) { + $chain[$key] = new X509(); $chain[$key]->loadX509($value); } return $chain; @@ -2725,11 +2868,11 @@ function getChain() /** * Set public key * - * Key needs to be a RSA object + * Key needs to be a \phpseclib\Crypt\RSA object * - * @param Object $key + * @param object $key * @access public - * @return Boolean + * @return bool */ function setPublicKey($key) { @@ -2740,9 +2883,9 @@ function setPublicKey($key) /** * Set private key * - * Key needs to be a RSA object + * Key needs to be a \phpseclib\Crypt\RSA object * - * @param Object $key + * @param object $key * @access public */ function setPrivateKey($key) @@ -2750,13 +2893,26 @@ function setPrivateKey($key) $this->privateKey = $key; } + /** + * Set challenge + * + * Used for SPKAC CSR's + * + * @param string $challenge + * @access public + */ + function setChallenge($challenge) + { + $this->challenge = $challenge; + } + /** * Gets the public key * - * Returns a RSA object or a false. + * Returns a \phpseclib\Crypt\RSA object or a false. * * @access public - * @return Mixed + * @return mixed */ function getPublicKey() { @@ -2781,7 +2937,7 @@ function getPublicKey() switch ($keyinfo['algorithm']['algorithm']) { case 'rsaEncryption': $publicKey = new RSA(); - $publicKey->loadKey($key); + $publicKey->load($key); $publicKey->setPublicKey(); break; default: @@ -2794,11 +2950,11 @@ function getPublicKey() /** * Load a Certificate Signing Request * - * @param String $csr + * @param string $csr * @access public - * @return Mixed + * @return mixed */ - function loadCSR($csr) + function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($csr) && isset($csr['certificationRequestInfo'])) { unset($this->currentCert); @@ -2817,7 +2973,13 @@ function loadCSR($csr) $asn1 = new ASN1(); - $csr = $this->_extractBER($csr); + if ($mode != self::FORMAT_DER) { + $newcsr = $this->_extractBER($csr); + if ($mode == self::FORMAT_PEM && $csr == $newcsr) { + return false; + } + $csr = $newcsr; + } $orig = $csr; if ($csr === false) { @@ -2839,8 +3001,10 @@ function loadCSR($csr) return false; } - $this->dn = $csr['certificationRequestInfo']['subject']; $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1); + $this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); + + $this->dn = $csr['certificationRequestInfo']['subject']; $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); @@ -2851,7 +3015,7 @@ function loadCSR($csr) switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->loadKey($key); + $this->publicKey->load($key); $this->publicKey->setPublicKey(); break; default: @@ -2867,12 +3031,12 @@ function loadCSR($csr) /** * Save CSR request * - * @param Array $csr - * @param Integer $format optional + * @param array $csr + * @param int $format optional * @access public - * @return String + * @return string */ - function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) + function saveCSR($csr, $format = self::FORMAT_PEM) { if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) { return false; @@ -2880,13 +3044,16 @@ function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) switch (true) { case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): - case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']); + case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): break; default: switch ($algorithm) { case 'rsaEncryption': $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] - = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); + = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); + $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null; + $csr['signatureAlgorithm']['parameters'] = null; + $csr['certificationRequestInfo']['signature']['parameters'] = null; } } @@ -2896,19 +3063,20 @@ function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) $filters = array(); $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] - = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); + $this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1); $csr = $asn1->encodeDER($csr, $this->CertificationRequest); switch ($format) { - case FILE_X509_FORMAT_DER: + case self::FORMAT_DER: return $csr; - // case FILE_X509_FORMAT_PEM: + // case self::FORMAT_PEM: default: - return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; + return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(Base64::encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; } } @@ -2919,9 +3087,9 @@ function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) * * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * - * @param String $csr + * @param string $csr * @access public - * @return Mixed + * @return mixed */ function loadSPKAC($spkac) { @@ -2937,8 +3105,9 @@ function loadSPKAC($spkac) $asn1 = new ASN1(); - $temp = preg_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $spkac); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + // OpenSSL produces SPKAC's that are preceeded by the string SPKAC= + $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; if ($temp != false) { $spkac = $temp; } @@ -2973,7 +3142,7 @@ function loadSPKAC($spkac) switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->loadKey($key); + $this->publicKey->load($key); $this->publicKey->setPublicKey(); break; default: @@ -2986,14 +3155,57 @@ function loadSPKAC($spkac) return $spkac; } + /** + * Save a SPKAC CSR request + * + * @param array $csr + * @param int $format optional + * @access public + * @return string + */ + function saveSPKAC($spkac, $format = self::FORMAT_PEM) + { + if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) { + return false; + } + + $algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm'); + switch (true) { + case !$algorithm: + case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): + break; + default: + switch ($algorithm) { + case 'rsaEncryption': + $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] + = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); + } + } + + $asn1 = new ASN1(); + + $asn1->loadOIDs($this->oids); + $spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge); + + switch ($format) { + case self::FORMAT_DER: + return $spkac; + // case self::FORMAT_PEM: + default: + // OpenSSL's implementation of SPKAC requires the SPKAC be preceeded by SPKAC= and since there are pretty much + // no other SPKAC decoders phpseclib will use that same format + return 'SPKAC=' . Base64::encode($spkac); + } + } + /** * Load a Certificate Revocation List * - * @param String $crl + * @param string $crl * @access public - * @return Mixed + * @return mixed */ - function loadCRL($crl) + function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($crl) && isset($crl['tbsCertList'])) { $this->currentCert = $crl; @@ -3003,7 +3215,13 @@ function loadCRL($crl) $asn1 = new ASN1(); - $crl = $this->_extractBER($crl); + if ($mode != self::FORMAT_DER) { + $newcrl = $this->_extractBER($crl); + if ($mode == self::FORMAT_PEM && $crl == $newcrl) { + return false; + } + $crl = $newcrl; + } $orig = $crl; if ($crl === false) { @@ -3027,11 +3245,19 @@ function loadCRL($crl) $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); - $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); - if (is_array($rclist)) { - foreach ($rclist as $i => $extension) { - $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); + $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); + if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + } + if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { + $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); + if ($rclist_ref) { + $rclist = $crl['tbsCertList']['revokedCertificates']; + foreach ($rclist as $i => $extension) { + if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { + $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1); + } + } } } @@ -3044,12 +3270,12 @@ function loadCRL($crl) /** * Save Certificate Revocation List. * - * @param Array $crl - * @param Integer $format optional + * @param array $crl + * @param int $format optional * @access public - * @return String + * @return string */ - function saveCRL($crl, $format = FILE_X509_FORMAT_PEM) + function saveCRL($crl, $format = self::FORMAT_PEM) { if (!is_array($crl) || !isset($crl['tbsCertList'])) { return false; @@ -3061,24 +3287,25 @@ function saveCRL($crl, $format = FILE_X509_FORMAT_PEM) $filters = array(); $filters['tbsCertList']['issuer']['rdnSequence']['value'] - = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + = array('type' => ASN1::TYPE_UTF8_STRING); $filters['tbsCertList']['signature']['parameters'] - = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + = array('type' => ASN1::TYPE_UTF8_STRING); $filters['signatureAlgorithm']['parameters'] - = array('type' => FILE_ASN1_TYPE_UTF8_STRING); + = array('type' => ASN1::TYPE_UTF8_STRING); if (empty($crl['tbsCertList']['signature']['parameters'])) { $filters['tbsCertList']['signature']['parameters'] - = array('type' => FILE_ASN1_TYPE_NULL); + = array('type' => ASN1::TYPE_NULL); } if (empty($crl['signatureAlgorithm']['parameters'])) { $filters['signatureAlgorithm']['parameters'] - = array('type' => FILE_ASN1_TYPE_NULL); + = array('type' => ASN1::TYPE_NULL); } $asn1->loadFilters($filters); + $this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); if (is_array($rclist)) { @@ -3090,11 +3317,33 @@ function saveCRL($crl, $format = FILE_X509_FORMAT_PEM) $crl = $asn1->encodeDER($crl, $this->CertificateList); switch ($format) { - case FILE_X509_FORMAT_DER: + case self::FORMAT_DER: return $crl; - // case FILE_X509_FORMAT_PEM: + // case self::FORMAT_PEM: default: - return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; + return "-----BEGIN X509 CRL-----\r\n" . chunk_split(Base64::encode($crl), 64) . '-----END X509 CRL-----'; + } + } + + /** + * Helper function to build a time field according to RFC 3280 section + * - 4.1.2.5 Validity + * - 5.1.2.4 This Update + * - 5.1.2.5 Next Update + * - 5.1.2.6 Revoked Certificates + * by choosing utcTime iff year of date given is before 2050 and generalTime else. + * + * @param string $date in format date('D, d M Y H:i:s O') + * @access private + * @return array + */ + function _timeField($date) + { + $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this + if ($year < 2050) { + return array('utcTime' => $date); + } else { + return array('generalTime' => $date); } } @@ -3105,13 +3354,13 @@ function saveCRL($crl, $format = FILE_X509_FORMAT_PEM) * $subject can be either an existing X.509 cert (if you want to resign it), * a CSR or something with the DN and public key explicitly set. * - * @param X059 $issuer - * @param X059 $subject - * @param String $signatureAlgorithm optional + * @param \phpseclib\File\X509 $issuer + * @param \phpseclib\File\X509 $subject + * @param string $signatureAlgorithm optional * @access public - * @return Mixed + * @return mixed */ - function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') + function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption') { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; @@ -3130,12 +3379,10 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; if (!empty($this->startDate)) { - $this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate; - unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']); + $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate); } if (!empty($this->endDate)) { - $this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime'] = $this->endDate; - unset($this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime']); + $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate); } if (!empty($this->serialNumber)) { $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; @@ -3150,16 +3397,25 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') if (isset($subject->domains)) { $this->removeExtension('id-ce-subjectAltName'); } - } else if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { + } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { return false; } else { if (!isset($subject->publicKey)) { return false; } - $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O'); - $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M y H:i:s O', strtotime('+1 year')); - $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new BigInteger(); + $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); + $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year')); + /* "The serial number MUST be a positive integer" + "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." + -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 + + for the integer to be positive the leading bit needs to be 0 hence the + application of a bitmap + */ + $serialNumber = !empty($this->serialNumber) ? + $this->serialNumber : + new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256); $this->currentCert = array( 'tbsCertificate' => @@ -3169,14 +3425,14 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later 'validity' => array( - 'notBefore' => array('generalTime' => $startDate), // $this->setStartDate() - 'notAfter' => array('generalTime' => $endDate) // $this->setEndDate() + 'notBefore' => $this->_timeField($startDate), // $this->setStartDate() + 'notAfter' => $this->_timeField($endDate) // $this->setEndDate() ), 'subject' => $subject->dn, 'subjectPublicKeyInfo' => $subjectPublicKey ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); // Copy extensions from CSR. @@ -3197,8 +3453,7 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') // ) //), 'keyIdentifier' => $issuer->currentKeyIdentifier - ) - ); + )); //$extensions = &$this->currentCert['tbsCertificate']['extensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; @@ -3213,7 +3468,7 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') $altName = array(); if (isset($subject->domains) && count($subject->domains) > 1) { - $altName = array_map(array('X059', '_dnsName'), $subject->domains); + $altName = array_map(array('X509', '_dnsName'), $subject->domains); } if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { @@ -3241,7 +3496,8 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') $keyUsage = array(); } - $this->setExtension('id-ce-keyUsage', + $this->setExtension( + 'id-ce-keyUsage', array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) ); @@ -3250,16 +3506,19 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') $basicConstraints = array(); } - $this->setExtension('id-ce-basicConstraints', - array_unique(array_merge(array('cA' => true), $basicConstraints)), true); + $this->setExtension( + 'id-ce-basicConstraints', + array_unique(array_merge(array('cA' => true), $basicConstraints)), + true + ); if (!isset($subject->currentKeyIdentifier)) { - $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); + $this->setExtension('id-ce-subjectKeyIdentifier', Base64::encode($this->computeKeyIdentifier($this->currentCert)), false, false); } } // resync $this->signatureSubject - // save $tbsCertificate in case there are any ASN1_Element objects in it + // save $tbsCertificate in case there are any \phpseclib\File\ASN1\Element objects in it $tbsCertificate = $this->currentCert['tbsCertificate']; $this->loadX509($this->saveX509($this->currentCert)); @@ -3276,7 +3535,7 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') * Sign a CSR * * @access public - * @return Mixed + * @return mixed */ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') { @@ -3287,7 +3546,7 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); - $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->load($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); if (!($publicKey = $this->_formatSubjectPublicKey())) { return false; @@ -3311,13 +3570,13 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') 'subject' => $this->dn, 'subjectPKInfo' => $publicKey ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } // resync $this->signatureSubject - // save $certificationRequestInfo in case there are any ASN1_Element objects in it + // save $certificationRequestInfo in case there are any \phpseclib\File\ASN1\Element objects in it $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; $this->loadCSR($this->saveCSR($this->currentCert)); @@ -3330,16 +3589,81 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') return $result; } + /** + * Sign a SPKAC + * + * @access public + * @return mixed + */ + function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($this->privateKey)) { + return false; + } + + $origPublicKey = $this->publicKey; + $class = get_class($this->privateKey); + $this->publicKey = new $class(); + $this->publicKey->load($this->privateKey->getPublicKey()); + $this->publicKey->setPublicKey(); + $publicKey = $this->_formatSubjectPublicKey(); + if (!$publicKey) { + return false; + } + $this->publicKey = $origPublicKey; + + $currentCert = isset($this->currentCert) ? $this->currentCert : null; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; + + // re-signing a SPKAC seems silly but since everything else supports re-signing why not? + if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) { + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey; + if (!empty($this->challenge)) { + // the bitwise AND ensures that the output is a valid IA5String + $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge)); + } + } else { + $this->currentCert = array( + 'publicKeyAndChallenge' => + array( + 'spki' => $publicKey, + // quoting , + // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified." + // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way + // we could alternatively do this instead if we ignored the specs: + // Random::string(8) & str_repeat("\x7F", 8) + 'challenge' => !empty($this->challenge) ? $this->challenge : '' + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + // resync $this->signatureSubject + // save $publicKeyAndChallenge in case there are any \phpseclib\File\ASN1\Element objects in it + $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge']; + $this->loadSPKAC($this->saveSPKAC($this->currentCert)); + + $result = $this->_sign($this->privateKey, $signatureAlgorithm); + $result['publicKeyAndChallenge'] = $publicKeyAndChallenge; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + /** * Sign a CRL * * $issuer's private key needs to be loaded. * - * @param X059 $issuer - * @param X059 $crl - * @param String $signatureAlgorithm optional + * @param \phpseclib\File\X509 $issuer + * @param \phpseclib\File\X509 $crl + * @param string $signatureAlgorithm optional * @access public - * @return Mixed + * @return mixed */ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') { @@ -3349,7 +3673,7 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; - $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O'); + $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { $this->currentCert = $crl->currentCert; @@ -3362,19 +3686,19 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') 'version' => 'v2', 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later - 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate() + 'thisUpdate' => $this->_timeField($thisUpdate) // $this->setStartDate() ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } $tbsCertList = &$this->currentCert['tbsCertList']; $tbsCertList['issuer'] = $issuer->dn; - $tbsCertList['thisUpdate'] = array('generalTime' => $thisUpdate); + $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate); if (!empty($this->endDate)) { - $tbsCertList['nextUpdate'] = array('generalTime' => $this->endDate); // $this->setEndDate() + $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate() } else { unset($tbsCertList['nextUpdate']); } @@ -3383,6 +3707,11 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') $crlNumber = $this->serialNumber; } else { $crlNumber = $this->getExtension('id-ce-cRLNumber'); + // "The CRL number is a non-critical CRL extension that conveys a + // monotonically increasing sequence number for a given CRL scope and + // CRL issuer. This extension allows users to easily determine when a + // particular CRL supersedes another CRL." + // -- https://tools.ietf.org/html/rfc5280#section-5.2.3 $crlNumber = $crlNumber !== false ? $crlNumber->add(new BigInteger(1)) : null; } @@ -3421,8 +3750,7 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') // ) //), 'keyIdentifier' => $issuer->currentKeyIdentifier - ) - ); + )); //$extensions = &$tbsCertList['crlExtensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; @@ -3444,7 +3772,7 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') unset($tbsCertList); // resync $this->signatureSubject - // save $tbsCertList in case there are any ASN1_Element objects in it + // save $tbsCertList in case there are any \phpseclib\File\ASN1\Element objects in it $tbsCertList = $this->currentCert['tbsCertList']; $this->loadCRL($this->saveCRL($this->currentCert)); @@ -3460,50 +3788,51 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') /** * X.509 certificate signing helper function. * - * @param Object $key - * @param X059 $subject - * @param String $signatureAlgorithm + * @param object $key + * @param \phpseclib\File\X509 $subject + * @param string $signatureAlgorithm * @access public - * @return Mixed + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported + * @return mixed */ function _sign($key, $signatureAlgorithm) { - switch (strtolower(get_class($key))) { - case 'crypt_rsa': - switch ($signatureAlgorithm) { - case 'md2WithRSAEncryption': - case 'md5WithRSAEncryption': - case 'sha1WithRSAEncryption': - case 'sha224WithRSAEncryption': - case 'sha256WithRSAEncryption': - case 'sha384WithRSAEncryption': - case 'sha512WithRSAEncryption': - $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); - $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); - - $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); - return $this->currentCert; - } - default: - return false; + if ($key instanceof RSA) { + switch ($signatureAlgorithm) { + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); + + $this->currentCert['signature'] = Base64::encode("\0" . $key->sign($this->signatureSubject, RSA::PADDING_PKCS1)); + return $this->currentCert; + default: + throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); + } } + + throw new UnsupportedAlgorithmException('Unsupported public key algorithm'); } /** * Set certificate start date * - * @param String $date + * @param string $date * @access public */ function setStartDate($date) { - $this->startDate = @date('D, d M y H:i:s O', @strtotime($date)); + $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date)); } /** * Set certificate end date * - * @param String $date + * @param string $date * @access public */ function setEndDate($date) @@ -3518,17 +3847,17 @@ function setEndDate($date) if (strtolower($date) == 'lifetime') { $temp = '99991231235959Z'; $asn1 = new ASN1(); - $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; - $this->endDate = new ASN1_Element($temp); + $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; + $this->endDate = new Element($temp); } else { - $this->endDate = @date('D, d M y H:i:s O', @strtotime($date)); + $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date)); } } /** * Set Serial Number * - * @param String $serial + * @param string $serial * @param $base optional * @access public */ @@ -3547,14 +3876,82 @@ function makeCA() $this->caFlag = true; } + /** + * Check for validity of subarray + * + * This is intended for use in conjunction with _subArrayUnchecked(), + * implementing the checks included in _subArray() but without copying + * a potentially large array by passing its reference by-value to is_array(). + * + * @param array $root + * @param string $path + * @return boolean + * @access private + */ + function _isSubArrayValid($root, $path) + { + if (!is_array($root)) { + return false; + } + + foreach (explode('/', $path) as $i) { + if (!is_array($root)) { + return false; + } + + if (!isset($root[$i])) { + return true; + } + + $root = $root[$i]; + } + + return true; + } + + /** + * Get a reference to a subarray + * + * This variant of _subArray() does no is_array() checking, + * so $root should be checked with _isSubArrayValid() first. + * + * This is here for performance reasons: + * Passing a reference (i.e. $root) by-value (i.e. to is_array()) + * creates a copy. If $root is an especially large array, this is expensive. + * + * @param array $root + * @param string $path absolute path with / as component separator + * @param bool $create optional + * @access private + * @return array|false + */ + function &_subArrayUnchecked(&$root, $path, $create = false) + { + $false = false; + + foreach (explode('/', $path) as $i) { + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + + $root[$i] = array(); + } + + $root = &$root[$i]; + } + + return $root; + } + /** * Get a reference to a subarray * * @param array $root - * @param String $path absolute path with / as component separator - * @param Boolean $create optional + * @param string $path absolute path with / as component separator + * @param bool $create optional * @access private - * @return array item ref or false + * @return array|false */ function &_subArray(&$root, $path, $create = false) { @@ -3587,10 +3984,10 @@ function &_subArray(&$root, $path, $create = false) * Get a reference to an extension subarray * * @param array $root - * @param String $path optional absolute path with / as component separator - * @param Boolean $create optional + * @param string $path optional absolute path with / as component separator + * @param bool $create optional * @access private - * @return array ref or false + * @return array|false */ function &_extensions(&$root, $path = null, $create = false) { @@ -3641,10 +4038,10 @@ function &_extensions(&$root, $path = null, $create = false) /** * Remove an Extension * - * @param String $id - * @param String $path optional + * @param string $id + * @param string $path optional * @access private - * @return Boolean + * @return bool */ function _removeExtension($id, $path = null) { @@ -3671,11 +4068,11 @@ function _removeExtension($id, $path = null) * * Returns the extension if it exists and false if not * - * @param String $id - * @param Array $cert optional - * @param String $path optional + * @param string $id + * @param array $cert optional + * @param string $path optional * @access private - * @return Mixed + * @return mixed */ function _getExtension($id, $cert = null, $path = null) { @@ -3698,9 +4095,9 @@ function _getExtension($id, $cert = null, $path = null) * Returns a list of all extensions in use * * @param array $cert optional - * @param String $path optional + * @param string $path optional * @access private - * @return Array + * @return array */ function _getExtensions($cert = null, $path = null) { @@ -3719,13 +4116,13 @@ function _getExtensions($cert = null, $path = null) /** * Set an Extension * - * @param String $id - * @param Mixed $value - * @param Boolean $critical optional - * @param Boolean $replace optional - * @param String $path optional + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional + * @param string $path optional * @access private - * @return Boolean + * @return bool */ function _setExtension($id, $value, $critical = false, $replace = true, $path = null) { @@ -3755,9 +4152,9 @@ function _setExtension($id, $value, $critical = false, $replace = true, $path = /** * Remove a certificate, CSR or CRL Extension * - * @param String $id + * @param string $id * @access public - * @return Boolean + * @return bool */ function removeExtension($id) { @@ -3769,10 +4166,10 @@ function removeExtension($id) * * Returns the extension if it exists and false if not * - * @param String $id - * @param Array $cert optional + * @param string $id + * @param array $cert optional * @access public - * @return Mixed + * @return mixed */ function getExtension($id, $cert = null) { @@ -3784,7 +4181,7 @@ function getExtension($id, $cert = null) * * @param array $cert optional * @access public - * @return Array + * @return array */ function getExtensions($cert = null) { @@ -3794,12 +4191,12 @@ function getExtensions($cert = null) /** * Set a certificate, CSR or CRL Extension * - * @param String $id - * @param Mixed $value - * @param Boolean $critical optional - * @param Boolean $replace optional + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional * @access public - * @return Boolean + * @return bool */ function setExtension($id, $value, $critical = false, $replace = true) { @@ -3809,12 +4206,12 @@ function setExtension($id, $value, $critical = false, $replace = true) /** * Remove a CSR attribute. * - * @param String $id - * @param Integer $disposition optional + * @param string $id + * @param int $disposition optional * @access public - * @return Boolean + * @return bool */ - function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL) + function removeAttribute($id, $disposition = self::ATTR_ALL) { $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes'); @@ -3827,13 +4224,13 @@ function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL) if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { - case $disposition == FILE_X509_ATTR_APPEND: - case $disposition == FILE_X509_ATTR_REPLACE: + case $disposition == self::ATTR_APPEND: + case $disposition == self::ATTR_REPLACE: return false; case $disposition >= $n: $disposition -= $n; break; - case $disposition == FILE_X509_ATTR_ALL: + case $disposition == self::ATTR_ALL: case $n == 1: unset($attributes[$key]); $result = true; @@ -3844,7 +4241,7 @@ function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL) $result = true; break; } - if ($result && $disposition != FILE_X509_ATTR_ALL) { + if ($result && $disposition != self::ATTR_ALL) { break; } } @@ -3859,13 +4256,13 @@ function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL) * * Returns the attribute if it exists and false if not * - * @param String $id - * @param Integer $disposition optional - * @param Array $csr optional + * @param string $id + * @param int $disposition optional + * @param array $csr optional * @access public - * @return Mixed + * @return mixed */ - function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = null) + function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) { if (empty($csr)) { $csr = $this->currentCert; @@ -3881,10 +4278,10 @@ function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = null) if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { - case $disposition == FILE_X509_ATTR_APPEND: - case $disposition == FILE_X509_ATTR_REPLACE: + case $disposition == self::ATTR_APPEND: + case $disposition == self::ATTR_REPLACE: return false; - case $disposition == FILE_X509_ATTR_ALL: + case $disposition == self::ATTR_ALL: return $attribute['value']; case $disposition >= $n: $disposition -= $n; @@ -3903,7 +4300,7 @@ function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = null) * * @param array $csr optional * @access public - * @return Array + * @return array */ function getAttributes($csr = null) { @@ -3926,13 +4323,13 @@ function getAttributes($csr = null) /** * Set a CSR attribute * - * @param String $id - * @param Mixed $value - * @param Boolean $disposition optional + * @param string $id + * @param mixed $value + * @param bool $disposition optional * @access public - * @return Boolean + * @return bool */ - function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) + function setAttribute($id, $value, $disposition = self::ATTR_ALL) { $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true); @@ -3941,9 +4338,9 @@ function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) } switch ($disposition) { - case FILE_X509_ATTR_REPLACE: - $disposition = FILE_X509_ATTR_APPEND; - case FILE_X509_ATTR_ALL: + case self::ATTR_REPLACE: + $disposition = self::ATTR_APPEND; + case self::ATTR_ALL: $this->removeAttribute($id); break; } @@ -3952,10 +4349,10 @@ function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { - case $disposition == FILE_X509_ATTR_APPEND: + case $disposition == self::ATTR_APPEND: $last = $key; break; - case $disposition >= $n; + case $disposition >= $n: $disposition -= $n; break; default: @@ -3972,7 +4369,7 @@ function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) $attributes[$last]['value'][] = $value; break; default: - $attributes[] = array('type' => $id, 'value' => $disposition == FILE_X509_ATTR_ALL ? $value: array($value)); + $attributes[] = array('type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value: array($value)); break; } @@ -3984,7 +4381,7 @@ function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) * * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. * - * @param String $value + * @param string $value * @access public */ function setKeyIdentifier($value) @@ -3992,7 +4389,7 @@ function setKeyIdentifier($value) if (empty($value)) { unset($this->currentKeyIdentifier); } else { - $this->currentKeyIdentifier = base64_encode($value); + $this->currentKeyIdentifier = Base64::encode($value); } } @@ -4004,15 +4401,15 @@ function setKeyIdentifier($value) * recommended methods (4.2.1.2 RFC 3280). * Highly polymorphic: try to accept all possible forms of key: * - Key object - * - X059 object with public or private key defined + * - \phpseclib\File\X509 object with public or private key defined * - Certificate or CSR array - * - ASN1_Element object + * - \phpseclib\File\ASN1\Element object * - PEM or DER string * - * @param Mixed $key optional - * @param Integer $method optional + * @param mixed $key optional + * @param int $method optional * @access public - * @return String binary key identifier + * @return string binary key identifier */ function computeKeyIdentifier($key = null, $method = 1) { @@ -4029,21 +4426,21 @@ function computeKeyIdentifier($key = null, $method = 1) return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); case !is_object($key): return false; - case strtolower(get_class($key)) == 'file_asn1_element': + case $key instanceof Element: // Assume the element is a bitstring-packed key. $asn1 = new ASN1(); $decoded = $asn1->decodeBER($key->element); if (empty($decoded)) { return false; } - $raw = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING)); if (empty($raw)) { return false; } - $raw = base64_decode($raw); + $raw = Base64::decode($raw); // If the key is private, compute identifier from its corresponding public key. $key = new RSA(); - if (!$key->loadKey($raw)) { + if (!$key->load($raw)) { return false; // Not an unencrypted RSA key. } if ($key->getPrivateKey() !== false) { // If private. @@ -4051,7 +4448,7 @@ function computeKeyIdentifier($key = null, $method = 1) } $key = $raw; // Is a public key. break; - case strtolower(get_class($key)) == 'file_x509': + case $key instanceof X509: if (isset($key->publicKey)) { return $this->computeKeyIdentifier($key->publicKey, $method); } @@ -4062,8 +4459,8 @@ function computeKeyIdentifier($key = null, $method = 1) return $this->computeKeyIdentifier($key->currentCert, $method); } return false; - default: // Should be a key object (i.e.: RSA). - $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW); + default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA). + $key = $key->getPublicKey('PKCS1'); break; } @@ -4072,7 +4469,7 @@ function computeKeyIdentifier($key = null, $method = 1) // Now we have the key string: compute its sha-1 sum. $hash = new Hash('sha1'); - $hash = $hash->_hash($key); + $hash = $hash->hash($key); if ($method == 2) { $hash = substr($hash, -8); @@ -4086,33 +4483,28 @@ function computeKeyIdentifier($key = null, $method = 1) * Format a public key as appropriate * * @access private - * @return Array + * @return array */ function _formatSubjectPublicKey() { - if (!isset($this->publicKey) || !is_object($this->publicKey)) { - return false; + if ($this->publicKey instanceof RSA) { + // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. + // the former is a good example of how to do fuzzing on the public key + //return new Element(Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); + return array( + 'algorithm' => array('algorithm' => 'rsaEncryption'), + 'subjectPublicKey' => $this->publicKey->getPublicKey('PKCS1') + ); } - switch (strtolower(get_class($this->publicKey))) { - case 'crypt_rsa': - // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. - // the former is a good example of how to do fuzzing on the public key - //return new ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); - return array( - 'algorithm' => array('algorithm' => 'rsaEncryption'), - 'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) - ); - default: - return false; - } + return false; } /** * Set the domain name's which the cert is to be valid for * * @access public - * @return Array + * @return array */ function setDomain() { @@ -4125,7 +4517,7 @@ function setDomain() * Set the IP Addresses's which the cert is to be valid for * * @access public - * @param String $ipAddress optional + * @param string $ipAddress optional */ function setIPAddress() { @@ -4142,8 +4534,8 @@ function setIPAddress() * Helper function to build domain array * * @access private - * @param String $domain - * @return Array + * @param string $domain + * @return array */ function _dnsName($domain) { @@ -4156,8 +4548,8 @@ function _dnsName($domain) * (IPv6 is not currently supported) * * @access private - * @param String $address - * @return Array + * @param string $address + * @return array */ function _iPAddress($address) { @@ -4168,10 +4560,10 @@ function _iPAddress($address) * Get the index of a revoked certificate. * * @param array $rclist - * @param String $serial - * @param Boolean $create optional + * @param string $serial + * @param bool $create optional * @access private - * @return Integer or false + * @return int|false */ function _revokedCertificate(&$rclist, $serial, $create = false) { @@ -4189,17 +4581,17 @@ function _revokedCertificate(&$rclist, $serial, $create = false) $i = count($rclist); $rclist[] = array('userCertificate' => $serial, - 'revocationDate' => array('generalTime' => @date('D, d M y H:i:s O'))); + 'revocationDate' => $this->_timeField(@date('D, d M Y H:i:s O'))); return $i; } /** * Revoke a certificate. * - * @param String $serial - * @param String $date optional + * @param string $serial + * @param string $date optional * @access public - * @return Boolean + * @return bool */ function revoke($serial, $date = null) { @@ -4207,9 +4599,8 @@ function revoke($serial, $date = null) if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { - if (!empty($date)) { - $rclist[$i]['revocationDate'] = array('generalTime' => $date); + $rclist[$i]['revocationDate'] = $this->_timeField($date); } return true; @@ -4224,9 +4615,9 @@ function revoke($serial, $date = null) /** * Unrevoke a certificate. * - * @param String $serial + * @param string $serial * @access public - * @return Boolean + * @return bool */ function unrevoke($serial) { @@ -4244,9 +4635,9 @@ function unrevoke($serial) /** * Get a revoked certificate. * - * @param String $serial + * @param string $serial * @access public - * @return Mixed + * @return mixed */ function getRevoked($serial) { @@ -4290,10 +4681,10 @@ function listRevoked($crl = null) /** * Remove a Revoked Certificate Extension * - * @param String $serial - * @param String $id + * @param string $serial + * @param string $id * @access public - * @return Boolean + * @return bool */ function removeRevokedCertificateExtension($serial, $id) { @@ -4311,11 +4702,11 @@ function removeRevokedCertificateExtension($serial, $id) * * Returns the extension if it exists and false if not * - * @param String $serial - * @param String $id - * @param Array $crl optional + * @param string $serial + * @param string $id + * @param array $crl optional * @access public - * @return Mixed + * @return mixed */ function getRevokedCertificateExtension($serial, $id, $crl = null) { @@ -4335,10 +4726,10 @@ function getRevokedCertificateExtension($serial, $id, $crl = null) /** * Returns a list of all extensions in use for a given revoked certificate * - * @param String $serial + * @param string $serial * @param array $crl optional * @access public - * @return Array + * @return array */ function getRevokedCertificateExtensions($serial, $crl = null) { @@ -4358,13 +4749,13 @@ function getRevokedCertificateExtensions($serial, $crl = null) /** * Set a Revoked Certificate Extension * - * @param String $serial - * @param String $id - * @param Mixed $value - * @param Boolean $critical optional - * @param Boolean $replace optional + * @param string $serial + * @param string $id + * @param mixed $value + * @param bool $critical optional + * @param bool $replace optional * @access public - * @return Boolean + * @return bool */ function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) { @@ -4383,8 +4774,8 @@ function setRevokedCertificateExtension($serial, $id, $value, $critical = false, * Extract raw BER from Base64 encoding * * @access private - * @param String $str - * @return String + * @param string $str + * @return string */ function _extractBER($str) { @@ -4397,12 +4788,39 @@ function _extractBER($str) * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ - $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff $temp = preg_replace('#-+[^-]+-+#', '', $temp); // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; return $temp != false ? $temp : $str; } + + /** + * Returns the OID corresponding to a name + * + * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if + * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version + * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able + * to work from version to version. + * + * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that + * what's being passed to it already is an OID and return that instead. A few examples. + * + * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1' + * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1' + * getOID('zzz') == 'zzz' + * + * @access public + * @return string + */ + function getOID($name) + { + static $reverseMap; + if (!isset($reverseMap)) { + $reverseMap = array_flip($this->oids); + } + return isset($reverseMap[$name]) ? $reverseMap[$name] : $name; + } } diff --git a/src/phpseclib/LICENSE b/src/phpseclib/LICENSE deleted file mode 100644 index 7fc2d873..00000000 --- a/src/phpseclib/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright 2007-2013 TerraFrost and other contributors -http://phpseclib.sourceforge.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. \ No newline at end of file diff --git a/src/phpseclib/Math/BigInteger.php b/src/phpseclib/Math/BigInteger.php old mode 100644 new mode 100755 index 071f16b5..66fb48e4 --- a/src/phpseclib/Math/BigInteger.php +++ b/src/phpseclib/Math/BigInteger.php @@ -6,10 +6,10 @@ * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available, * and an internal implementation, otherwise. * - * PHP versions 4 and 5 + * PHP version 5 * * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the - * {@link MATH_BIGINTEGER_MODE_INTERNAL MATH_BIGINTEGER_MODE_INTERNAL} mode) + * {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode) * * BigInteger uses base-2**26 to perform operations such as multiplication and division and * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible @@ -19,12 +19,8 @@ * which only supports integers. Although this fact will slow this library down, the fact that such a high * base is being used should more than compensate. * - * When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again, - * allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition / - * subtraction). - * * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie. - * (new BigInteger(pow(2, 26)))->value = array(0, 1) + * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1) * * Useful resources are as follows: * @@ -35,10 +31,8 @@ * Here's an example of how to use this library: * * add($b); * @@ -46,130 +40,19 @@ * ?> * * - * LICENSE: 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. - * * @category Math * @package BigInteger * @author Jim Wigginton - * @copyright MMVI Jim Wigginton + * @copyright 2006 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://pear.php.net/package/BigInteger + * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib\Math; -/**#@+ - * Reduction constants - * - * @access private - * @see BigInteger::_reduce() - */ -/** - * @see BigInteger::_montgomery() - * @see BigInteger::_prepMontgomery() - */ -@define('MATH_BIGINTEGER_MONTGOMERY', 0); -/** - * @see BigInteger::_barrett() - */ -@define('MATH_BIGINTEGER_BARRETT', 1); -/** - * @see BigInteger::_mod2() - */ -@define('MATH_BIGINTEGER_POWEROF2', 2); -/** - * @see BigInteger::_remainder() - */ -@define('MATH_BIGINTEGER_CLASSIC', 3); -/** - * @see BigInteger::__clone() - */ -@define('MATH_BIGINTEGER_NONE', 4); -/**#@-*/ - -/**#@+ - * Array constants - * - * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and - * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. - * - * @access private - */ -/** - * $result[MATH_BIGINTEGER_VALUE] contains the value. - */ -@define('MATH_BIGINTEGER_VALUE', 0); -/** - * $result[MATH_BIGINTEGER_SIGN] contains the sign. - */ -@define('MATH_BIGINTEGER_SIGN', 1); -/**#@-*/ - -/**#@+ - * @access private - * @see BigInteger::_montgomery() - * @see BigInteger::_barrett() - */ -/** - * Cache constants - * - * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid. - */ -@define('MATH_BIGINTEGER_VARIABLE', 0); -/** - * $cache[MATH_BIGINTEGER_DATA] contains the cached data. - */ -@define('MATH_BIGINTEGER_DATA', 1); -/**#@-*/ - -/**#@+ - * Mode constants. - * - * @access private - * @see BigInteger::BigInteger() - */ -/** - * To use the pure-PHP implementation - */ -@define('MATH_BIGINTEGER_MODE_INTERNAL', 1); -/** - * To use the BCMath library - * - * (if enabled; otherwise, the internal implementation will be used) - */ -@define('MATH_BIGINTEGER_MODE_BCMATH', 2); -/** - * To use the GMP library - * - * (if present; otherwise, either the BCMath or the internal implementation will be used) - */ -@define('MATH_BIGINTEGER_MODE_GMP', 3); -/**#@-*/ - -/** - * Karatsuba Cutoff - * - * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? - * - * @access private - */ -@define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25); +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; +use phpseclib\Crypt\Random; /** * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 @@ -177,39 +60,151 @@ * * @package BigInteger * @author Jim Wigginton - * @version 1.0.0RC4 * @access public */ class BigInteger { + /**#@+ + * Reduction constants + * + * @access private + * @see BigInteger::_reduce() + */ /** - * Holds the BigInteger's value. + * @see BigInteger::_montgomery() + * @see BigInteger::_prepMontgomery() + */ + const MONTGOMERY = 0; + /** + * @see BigInteger::_barrett() + */ + const BARRETT = 1; + /** + * @see BigInteger::_mod2() + */ + const POWEROF2 = 2; + /** + * @see BigInteger::_remainder() + */ + const CLASSIC = 3; + /** + * @see BigInteger::__clone() + */ + const NONE = 4; + /**#@-*/ + + /**#@+ + * Array constants + * + * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and + * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. * - * @var Array * @access private + */ + /** + * $result[self::VALUE] contains the value. */ - var $value; + const VALUE = 0; + /** + * $result[self::SIGN] contains the sign. + */ + const SIGN = 1; + /**#@-*/ + /**#@+ + * @access private + * @see BigInteger::_montgomery() + * @see BigInteger::_barrett() + */ /** - * Holds the BigInteger's magnitude. + * Cache constants + * + * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. + */ + const VARIABLE = 0; + /** + * $cache[self::DATA] contains the cached data. + */ + const DATA = 1; + /**#@-*/ + + /**#@+ + * Mode constants. * - * @var Boolean * @access private + * @see BigInteger::__construct() + */ + /** + * To use the pure-PHP implementation */ - var $is_negative = false; + const MODE_INTERNAL = 1; + /** + * To use the BCMath library + * + * (if enabled; otherwise, the internal implementation will be used) + */ + const MODE_BCMATH = 2; + /** + * To use the GMP library + * + * (if present; otherwise, either the BCMath or the internal implementation will be used) + */ + const MODE_GMP = 3; + /**#@-*/ /** - * Random number generator function + * Karatsuba Cutoff + * + * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? * - * @see setRandomGenerator() * @access private */ - var $generator = 'mt_rand'; + const KARATSUBA_CUTOFF = 25; + + /**#@+ + * Static properties used by the pure-PHP implementation. + * + * @see __construct() + */ + protected static $base; + protected static $baseFull; + protected static $maxDigit; + protected static $msb; + + /** + * $max10 in greatest $max10Len satisfying + * $max10 = 10**$max10Len <= 2**$base. + */ + protected static $max10; + + /** + * $max10Len in greatest $max10Len satisfying + * $max10 = 10**$max10Len <= 2**$base. + */ + protected static $max10Len; + protected static $maxDigit2; + /**#@-*/ + + /** + * Holds the BigInteger's value. + * + * @var array + * @access private + */ + var $value; + + /** + * Holds the BigInteger's magnitude. + * + * @var bool + * @access private + */ + var $is_negative = false; /** * Precision * - * @see setPrecision() + * @see self::setPrecision() * @access private */ var $precision = -1; @@ -217,7 +212,7 @@ class BigInteger /** * Precision Bitmask * - * @see setPrecision() + * @see self::setPrecision() * @access private */ var $bitmask = false; @@ -229,9 +224,9 @@ class BigInteger * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, * however, $this->hex is only calculated when $this->__sleep() is called. * - * @see __sleep() - * @see __wakeup() - * @var String + * @see self::__sleep() + * @see self::__wakeup() + * @var string * @access private */ var $hex; @@ -244,105 +239,76 @@ class BigInteger * * Here's an example: * - * <?php - * include('Math/BigInteger.php'); - * - * $a = new BigInteger('0x32', 16); // 50 in base-16 + * toString(); // outputs 50 - * ?> + * ?> * * - * @param optional $x base-10 number or base-$base number if $base set. - * @param optional integer $base - * @return BigInteger + * @param $x base-10 number or base-$base number if $base set. + * @param int $base + * @return \phpseclib\Math\BigInteger * @access public */ function __construct($x = 0, $base = 10) { - if ( !defined('MATH_BIGINTEGER_MODE') ) { + if (!defined('MATH_BIGINTEGER_MODE')) { switch (true) { case extension_loaded('gmp'): - @define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP); + define('MATH_BIGINTEGER_MODE', self::MODE_GMP); break; case extension_loaded('bcmath'): - @define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH); + define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH); break; default: - @define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL); + define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL); } } - if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - ob_start(); - phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - - $versions = array(); - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - } - } - - // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ - switch (true) { - case !isset($versions['Header']): - case !isset($versions['Library']): - case $versions['Header'] == $versions['Library']: - @define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); - break; - default: - @define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); - } + if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); } if (!defined('PHP_INT_SIZE')) { - @define('PHP_INT_SIZE', 4); + define('PHP_INT_SIZE', 4); } - if (!defined('MATH_BIGINTEGER_BASE') && MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_INTERNAL) { + if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) { switch (PHP_INT_SIZE) { case 8: // use 64-bit integers if int size is 8 bytes - @define('MATH_BIGINTEGER_BASE', 31); - @define('MATH_BIGINTEGER_BASE_FULL', 0x80000000); - @define('MATH_BIGINTEGER_MAX_DIGIT', 0x7FFFFFFF); - @define('MATH_BIGINTEGER_MSB', 0x40000000); - // 10**9 is the closest we can get to 2**31 without passing it - @define('MATH_BIGINTEGER_MAX10', 1000000000); - @define('MATH_BIGINTEGER_MAX10_LEN', 9); - // the largest digit that may be used in addition / subtraction - @define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 62)); + self::$base = 31; + self::$baseFull = 0x80000000; + self::$maxDigit = 0x7FFFFFFF; + self::$msb = 0x40000000; + self::$max10 = 1000000000; + self::$max10Len = 9; + self::$maxDigit2 = pow(2, 62); break; //case 4: // use 64-bit floats if int size is 4 bytes default: - @define('MATH_BIGINTEGER_BASE', 26); - @define('MATH_BIGINTEGER_BASE_FULL', 0x4000000); - @define('MATH_BIGINTEGER_MAX_DIGIT', 0x3FFFFFF); - @define('MATH_BIGINTEGER_MSB', 0x2000000); - // 10**7 is the closest to 2**26 without passing it - @define('MATH_BIGINTEGER_MAX10', 10000000); - @define('MATH_BIGINTEGER_MAX10_LEN', 7); - // the largest digit that may be used in addition / subtraction - // we do pow(2, 52) instead of using 4503599627370496 directly because some - // PHP installations will truncate 4503599627370496. - @define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 52)); + self::$base = 26; + self::$baseFull = 0x4000000; + self::$maxDigit = 0x3FFFFFF; + self::$msb = 0x2000000; + self::$max10 = 10000000; + self::$max10Len = 7; + self::$maxDigit2 = pow(2, 52); // pow() prevents truncation } } - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - if (is_resource($x) && get_resource_type($x) == 'GMP integer') { - $this->value = $x; - return; + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + switch (true) { + case is_resource($x) && get_resource_type($x) == 'GMP integer': + // PHP 5.6 switched GMP from using resources to objects + case $x instanceof \GMP: + $this->value = $x; + return; } $this->value = gmp_init(0); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $this->value = '0'; break; default: @@ -361,13 +327,13 @@ function __construct($x = 0, $base = 10) $x = ~$x; $this->is_negative = true; } - case 256: - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + case 256: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: $sign = $this->is_negative ? '-' : ''; - $this->value = gmp_init($sign . '0x' . bin2hex($x)); + $this->value = gmp_init($sign . '0x' . Hex::encode($x)); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: // round $len to the nearest 4 (thanks, DavidMJ!) $len = (strlen($x) + 3) & 0xFFFFFFFC; @@ -386,19 +352,19 @@ function __construct($x = 0, $base = 10) // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) default: while (strlen($x)) { - $this->value[] = $this->_bytes2int($this->_base256_rshift($x, MATH_BIGINTEGER_BASE)); + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base)); } } if ($this->is_negative) { - if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) { + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { $this->is_negative = false; } - $temp = $this->add(new BigInteger('-1')); + $temp = $this->add(new static('-1')); $this->value = $temp->value; } break; - case 16: + case 16: case -16: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; @@ -410,70 +376,70 @@ function __construct($x = 0, $base = 10) $is_negative = false; if ($base < 0 && hexdec($x[0]) >= 8) { $this->is_negative = $is_negative = true; - $x = bin2hex(~pack('H*', $x)); + $x = Hex::encode(~Hex::decode($x)); } - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: $temp = $this->is_negative ? '-0x' . $x : '0x' . $x; $this->value = gmp_init($temp); $this->is_negative = false; break; - case MATH_BIGINTEGER_MODE_BCMATH: - $x = ( strlen($x) & 1 ) ? '0' . $x : $x; - $temp = new BigInteger(pack('H*', $x), 256); + case self::MODE_BCMATH: + $x = (strlen($x) & 1) ? '0' . $x : $x; + $temp = new static(Hex::decode($x), 256); $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; $this->is_negative = false; break; default: - $x = ( strlen($x) & 1 ) ? '0' . $x : $x; - $temp = new BigInteger(pack('H*', $x), 256); + $x = (strlen($x) & 1) ? '0' . $x : $x; + $temp = new static(Hex::decode($x), 256); $this->value = $temp->value; } if ($is_negative) { - $temp = $this->add(new BigInteger('-1')); + $temp = $this->add(new static('-1')); $this->value = $temp->value; } break; - case 10: + case 10: case -10: // (?value = gmp_init($x); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different // results then doing it on '-1' does (modInverse does $x[0]) $this->value = $x === '-' ? '0' : (string) $x; break; default: - $temp = new BigInteger(); + $temp = new static(); - $multiplier = new BigInteger(); - $multiplier->value = array(MATH_BIGINTEGER_MAX10); + $multiplier = new static(); + $multiplier->value = array(self::$max10); if ($x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } - $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT); + $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT); while (strlen($x)) { $temp = $temp->multiply($multiplier); - $temp = $temp->add(new BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256)); - $x = substr($x, MATH_BIGINTEGER_MAX10_LEN); + $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256)); + $x = substr($x, self::$max10Len); } $this->value = $temp->value; } break; - case 2: // base-2 support originally implemented by Lluis Pamies - thanks! + case 2: // base-2 support originally implemented by Lluis Pamies - thanks! case -2: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; @@ -494,7 +460,7 @@ function __construct($x = 0, $base = 10) $str = '-' . $str; } - $temp = new BigInteger($str, 8 * $base); // ie. either -16 or +16 + $temp = new static($str, 8 * $base); // ie. either -16 or +16 $this->value = $temp->value; $this->is_negative = $temp->is_negative; @@ -513,28 +479,26 @@ function __construct($x = 0, $base = 10) * Here's an example: * * toBytes(); // outputs chr(65) * ?> * * - * @param Boolean $twos_compliment - * @return String + * @param bool $twos_compliment + * @return string * @access public * @internal Converts a base-2**26 number to base-2**8 */ function toBytes($twos_compliment = false) { if ($twos_compliment) { - $comparison = $this->compare(new BigInteger()); + $comparison = $this->compare(new static()); if ($comparison == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $temp = $comparison < 0 ? $this->add(new BigInteger(1)) : $this->copy(); + $temp = $comparison < 0 ? $this->add(new static(1)) : $this; $bytes = $temp->toBytes(); if (empty($bytes)) { // eg. if the number we're trying to convert is -1 @@ -548,20 +512,20 @@ function toBytes($twos_compliment = false) return $comparison < 0 ? ~$bytes : $bytes; } - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: if (gmp_cmp($this->value, gmp_init(0)) == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = gmp_strval(gmp_abs($this->value), 16); - $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp; - $temp = pack('H*', $temp); + $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; + $temp = Hex::decode($temp); return $this->precision > 0 ? substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : ltrim($temp, chr(0)); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: if ($this->value === '0') { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } @@ -587,13 +551,11 @@ function toBytes($twos_compliment = false) if (!count($this->value)) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $result = $this->_int2bytes($this->value[count($this->value) - 1]); - - $temp = $this->copy(); + $result = self::_int2bytes($this->value[count($this->value) - 1]); - for ($i = count($temp->value) - 2; $i >= 0; --$i) { - $temp->_base256_lshift($result, MATH_BIGINTEGER_BASE); - $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); + for ($i = count($this->value) - 2; $i >= 0; --$i) { + self::_base256_lshift($result, self::$base); + $result = $result | str_pad(self::_int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } return $this->precision > 0 ? @@ -610,22 +572,20 @@ function toBytes($twos_compliment = false) * Here's an example: * * toHex(); // outputs '41' * ?> * * - * @param Boolean $twos_compliment - * @return String + * @param bool $twos_compliment + * @return string * @access public * @internal Converts a base-2**26 number to base-2**8 */ function toHex($twos_compliment = false) { - return bin2hex($this->toBytes($twos_compliment)); + return Hex::encode($this->toBytes($twos_compliment)); } /** @@ -637,16 +597,14 @@ function toHex($twos_compliment = false) * Here's an example: * * toBits(); // outputs '1000001' * ?> * * - * @param Boolean $twos_compliment - * @return String + * @param bool $twos_compliment + * @return string * @access public * @internal Converts a base-2**26 number to base-2**2 */ @@ -662,7 +620,7 @@ function toBits($twos_compliment = false) } $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); - if ($twos_compliment && $this->compare(new BigInteger()) > 0 && $this->precision <= 0) { + if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { return '0' . $result; } @@ -675,24 +633,22 @@ function toBits($twos_compliment = false) * Here's an example: * * toString(); // outputs 50 * ?> * * - * @return String + * @return string * @access public * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10) */ function toString() { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: return gmp_strval($this->value); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: if ($this->value === '0') { return '0'; } @@ -704,15 +660,15 @@ function toString() return '0'; } - $temp = $this->copy(); + $temp = clone $this; $temp->is_negative = false; - $divisor = new BigInteger(); - $divisor->value = array(MATH_BIGINTEGER_MAX10); + $divisor = new static(); + $divisor->value = array(self::$max10); $result = ''; while (count($temp->value)) { list($temp, $mod) = $temp->divide($divisor); - $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', MATH_BIGINTEGER_MAX10_LEN, '0', STR_PAD_LEFT) . $result; + $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result; } $result = ltrim($result, '0'); if (empty($result)) { @@ -726,29 +682,6 @@ function toString() return $result; } - /** - * Copy an object - * - * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee - * that all objects are passed by value, when appropriate. More information can be found here: - * - * {@link http://php.net/language.oop5.basic#51624} - * - * @access public - * @see __clone() - * @return BigInteger - */ - function copy() - { - $temp = new BigInteger(); - $temp->value = $this->value; - $temp->is_negative = $this->is_negative; - $temp->generator = $this->generator; - $temp->precision = $this->precision; - $temp->bitmask = $this->bitmask; - return $temp; - } - /** * __toString() magic method * @@ -763,43 +696,22 @@ function __toString() return $this->toString(); } - /** - * __clone() magic method - * - * Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() - * directly in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5 - * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5, - * call BigInteger::copy(), instead. - * - * @access public - * @see copy() - * @return BigInteger - */ - function __clone() - { - return $this->copy(); - } - /** * __sleep() magic method * * Will be called, automatically, when serialize() is called on a BigInteger object. * - * @see __wakeup() + * @see self::__wakeup() * @access public */ function __sleep() { $this->hex = $this->toHex(true); $vars = array('hex'); - if ($this->generator != 'mt_rand') { - $vars[] = 'generator'; - } if ($this->precision > 0) { $vars[] = 'precision'; } return $vars; - } /** @@ -807,31 +719,61 @@ function __sleep() * * Will be called, automatically, when unserialize() is called on a BigInteger object. * - * @see __sleep() + * @see self::__sleep() * @access public */ function __wakeup() { - $temp = new BigInteger($this->hex, -16); + $temp = new static($this->hex, -16); $this->value = $temp->value; $this->is_negative = $temp->is_negative; - $this->setRandomGenerator($this->generator); if ($this->precision > 0) { // recalculate $this->bitmask $this->setPrecision($this->precision); } } + /** + * __debugInfo() magic method + * + * Will be called, automatically, when print_r() or var_dump() are called + * + * @access public + */ + function __debugInfo() + { + $opts = array(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $engine = 'gmp'; + break; + case self::MODE_BCMATH: + $engine = 'bcmath'; + break; + case self::MODE_INTERNAL: + $engine = 'internal'; + $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit'; + } + if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + $opts[] = 'OpenSSL'; + } + if (!empty($opts)) { + $engine.= ' (' . implode($opts, ', ') . ')'; + } + return array( + 'value' => '0x' . $this->toHex(true), + 'engine' => $engine + ); + } + /** * Adds two BigIntegers. * * Here's an example: * * add($b); * @@ -839,31 +781,31 @@ function __wakeup() * ?> * * - * @param BigInteger $y - * @return BigInteger + * @param \phpseclib\Math\BigInteger $y + * @return \phpseclib\Math\BigInteger * @access public * @internal Performs base-2**52 addition */ - function add($y) + function add(BigInteger $y) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_add($this->value, $y->value); return $this->_normalize($temp); - case MATH_BIGINTEGER_MODE_BCMATH: - $temp = new BigInteger(); + case self::MODE_BCMATH: + $temp = new static(); $temp->value = bcadd($this->value, $y->value, 0); return $this->_normalize($temp); } - $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); + $temp = self::_add($this->value, $this->is_negative, $y->value, $y->is_negative); - $result = new BigInteger(); - $result->value = $temp[MATH_BIGINTEGER_VALUE]; - $result->is_negative = $temp[MATH_BIGINTEGER_SIGN]; + $result = new static(); + $result->value = $temp[self::VALUE]; + $result->is_negative = $temp[self::SIGN]; return $this->_normalize($result); } @@ -871,41 +813,41 @@ function add($y) /** * Performs addition. * - * @param Array $x_value - * @param Boolean $x_negative - * @param Array $y_value - * @param Boolean $y_negative - * @return Array + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array * @access private */ - function _add($x_value, $x_negative, $y_value, $y_negative) + static function _add($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return array( - MATH_BIGINTEGER_VALUE => $y_value, - MATH_BIGINTEGER_SIGN => $y_negative + self::VALUE => $y_value, + self::SIGN => $y_negative ); - } else if ($y_size == 0) { + } elseif ($y_size == 0) { return array( - MATH_BIGINTEGER_VALUE => $x_value, - MATH_BIGINTEGER_SIGN => $x_negative + self::VALUE => $x_value, + self::SIGN => $x_negative ); } // subtract, if appropriate - if ( $x_negative != $y_negative ) { - if ( $x_value == $y_value ) { + if ($x_negative != $y_negative) { + if ($x_value == $y_value) { return array( - MATH_BIGINTEGER_VALUE => array(), - MATH_BIGINTEGER_SIGN => false + self::VALUE => array(), + self::SIGN => false ); } - $temp = $this->_subtract($x_value, false, $y_value, false); - $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? + $temp = self::_subtract($x_value, false, $y_value, false); + $temp[self::SIGN] = self::_compare($x_value, false, $y_value, false) > 0 ? $x_negative : $y_negative; return $temp; @@ -919,37 +861,37 @@ function _add($x_value, $x_negative, $y_value, $y_negative) $value = $x_value; } - $value[] = 0; // just in case the carry adds an extra digit + $value[count($value)] = 0; // just in case the carry adds an extra digit $carry = 0; for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { - $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] + $y_value[$j] * MATH_BIGINTEGER_BASE_FULL + $y_value[$i] + $carry; - $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 - $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum; + $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry; + $carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - self::$maxDigit2 : $sum; - $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); + $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); - $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) $value[$j] = $temp; } if ($j == $size) { // ie. if $y_size is odd $sum = $x_value[$i] + $y_value[$i] + $carry; - $carry = $sum >= MATH_BIGINTEGER_BASE_FULL; - $value[$i] = $carry ? $sum - MATH_BIGINTEGER_BASE_FULL : $sum; + $carry = $sum >= self::$baseFull; + $value[$i] = $carry ? $sum - self::$baseFull : $sum; ++$i; // ie. let $i = $j since we've just done $value[$i] } if ($carry) { - for (; $value[$i] == MATH_BIGINTEGER_MAX_DIGIT; ++$i) { + for (; $value[$i] == self::$maxDigit; ++$i) { $value[$i] = 0; } ++$value[$i]; } return array( - MATH_BIGINTEGER_VALUE => $this->_trim($value), - MATH_BIGINTEGER_SIGN => $x_negative + self::VALUE => self::_trim($value), + self::SIGN => $x_negative ); } @@ -959,10 +901,8 @@ function _add($x_value, $x_negative, $y_value, $y_negative) * Here's an example: * * subtract($b); * @@ -970,31 +910,31 @@ function _add($x_value, $x_negative, $y_value, $y_negative) * ?> * * - * @param BigInteger $y - * @return BigInteger + * @param \phpseclib\Math\BigInteger $y + * @return \phpseclib\Math\BigInteger * @access public * @internal Performs base-2**52 subtraction */ - function subtract($y) + function subtract(BigInteger $y) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_sub($this->value, $y->value); return $this->_normalize($temp); - case MATH_BIGINTEGER_MODE_BCMATH: - $temp = new BigInteger(); + case self::MODE_BCMATH: + $temp = new static(); $temp->value = bcsub($this->value, $y->value, 0); return $this->_normalize($temp); } - $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); + $temp = self::_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); - $result = new BigInteger(); - $result->value = $temp[MATH_BIGINTEGER_VALUE]; - $result->is_negative = $temp[MATH_BIGINTEGER_SIGN]; + $result = new static(); + $result->value = $temp[self::VALUE]; + $result->is_negative = $temp[self::SIGN]; return $this->_normalize($result); } @@ -1002,49 +942,49 @@ function subtract($y) /** * Performs subtraction. * - * @param Array $x_value - * @param Boolean $x_negative - * @param Array $y_value - * @param Boolean $y_negative - * @return Array + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array * @access private */ - function _subtract($x_value, $x_negative, $y_value, $y_negative) + static function _subtract($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return array( - MATH_BIGINTEGER_VALUE => $y_value, - MATH_BIGINTEGER_SIGN => !$y_negative + self::VALUE => $y_value, + self::SIGN => !$y_negative ); - } else if ($y_size == 0) { + } elseif ($y_size == 0) { return array( - MATH_BIGINTEGER_VALUE => $x_value, - MATH_BIGINTEGER_SIGN => $x_negative + self::VALUE => $x_value, + self::SIGN => $x_negative ); } // add, if appropriate (ie. -$x - +$y or +$x - -$y) - if ( $x_negative != $y_negative ) { - $temp = $this->_add($x_value, false, $y_value, false); - $temp[MATH_BIGINTEGER_SIGN] = $x_negative; + if ($x_negative != $y_negative) { + $temp = self::_add($x_value, false, $y_value, false); + $temp[self::SIGN] = $x_negative; return $temp; } - $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); + $diff = self::_compare($x_value, $x_negative, $y_value, $y_negative); - if ( !$diff ) { + if (!$diff) { return array( - MATH_BIGINTEGER_VALUE => array(), - MATH_BIGINTEGER_SIGN => false + self::VALUE => array(), + self::SIGN => false ); } // switch $x and $y around, if appropriate. - if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) { + if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; @@ -1059,33 +999,33 @@ function _subtract($x_value, $x_negative, $y_value, $y_negative) $carry = 0; for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { - $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] - $y_value[$j] * MATH_BIGINTEGER_BASE_FULL - $y_value[$i] - $carry; + $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry; $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 - $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum; + $sum = $carry ? $sum + self::$maxDigit2 : $sum; - $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); + $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); - $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); + $x_value[$i] = (int) ($sum - self::$baseFull * $temp); $x_value[$j] = $temp; } if ($j == $y_size) { // ie. if $y_size is odd $sum = $x_value[$i] - $y_value[$i] - $carry; $carry = $sum < 0; - $x_value[$i] = $carry ? $sum + MATH_BIGINTEGER_BASE_FULL : $sum; + $x_value[$i] = $carry ? $sum + self::$baseFull : $sum; ++$i; } if ($carry) { for (; !$x_value[$i]; ++$i) { - $x_value[$i] = MATH_BIGINTEGER_MAX_DIGIT; + $x_value[$i] = self::$maxDigit; } --$x_value[$i]; } return array( - MATH_BIGINTEGER_VALUE => $this->_trim($x_value), - MATH_BIGINTEGER_SIGN => $x_negative + self::VALUE => self::_trim($x_value), + self::SIGN => $x_negative ); } @@ -1095,10 +1035,8 @@ function _subtract($x_value, $x_negative, $y_value, $y_negative) * Here's an example: * * multiply($b); * @@ -1106,30 +1044,30 @@ function _subtract($x_value, $x_negative, $y_value, $y_negative) * ?> * * - * @param BigInteger $x - * @return BigInteger + * @param \phpseclib\Math\BigInteger $x + * @return \phpseclib\Math\BigInteger * @access public */ - function multiply($x) + function multiply(BigInteger $x) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_mul($this->value, $x->value); return $this->_normalize($temp); - case MATH_BIGINTEGER_MODE_BCMATH: - $temp = new BigInteger(); + case self::MODE_BCMATH: + $temp = new static(); $temp->value = bcmul($this->value, $x->value, 0); return $this->_normalize($temp); } - $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); + $temp = self::_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); - $product = new BigInteger(); - $product->value = $temp[MATH_BIGINTEGER_VALUE]; - $product->is_negative = $temp[MATH_BIGINTEGER_SIGN]; + $product = new static(); + $product->value = $temp[self::VALUE]; + $product->is_negative = $temp[self::SIGN]; return $this->_normalize($product); } @@ -1137,37 +1075,37 @@ function multiply($x) /** * Performs multiplication. * - * @param Array $x_value - * @param Boolean $x_negative - * @param Array $y_value - * @param Boolean $y_negative - * @return Array + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return array * @access private */ - function _multiply($x_value, $x_negative, $y_value, $y_negative) + static function _multiply($x_value, $x_negative, $y_value, $y_negative) { //if ( $x_value == $y_value ) { // return array( - // MATH_BIGINTEGER_VALUE => $this->_square($x_value), - // MATH_BIGINTEGER_SIGN => $x_sign != $y_value + // self::VALUE => $this->_square($x_value), + // self::SIGN => $x_sign != $y_value // ); //} $x_length = count($x_value); $y_length = count($y_value); - if ( !$x_length || !$y_length ) { // a 0 is being multiplied + if (!$x_length || !$y_length) { // a 0 is being multiplied return array( - MATH_BIGINTEGER_VALUE => array(), - MATH_BIGINTEGER_SIGN => false + self::VALUE => array(), + self::SIGN => false ); } return array( - MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ? - $this->_trim($this->_regularMultiply($x_value, $y_value)) : - $this->_trim($this->_karatsuba($x_value, $y_value)), - MATH_BIGINTEGER_SIGN => $x_negative != $y_negative + self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? + self::_trim(self::_regularMultiply($x_value, $y_value)) : + self::_trim(self::_karatsuba($x_value, $y_value)), + self::SIGN => $x_negative != $y_negative ); } @@ -1176,21 +1114,21 @@ function _multiply($x_value, $x_negative, $y_value, $y_negative) * * Modeled after 'multiply' in MutableBigInteger.java. * - * @param Array $x_value - * @param Array $y_value - * @return Array + * @param array $x_value + * @param array $y_value + * @return array * @access private */ - function _regularMultiply($x_value, $y_value) + static function _regularMultiply($x_value, $y_value) { $x_length = count($x_value); $y_length = count($y_value); - if ( !$x_length || !$y_length ) { // a 0 is being multiplied + if (!$x_length || !$y_length) { // a 0 is being multiplied return array(); } - if ( $x_length < $y_length ) { + if ($x_length < $y_length) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; @@ -1199,7 +1137,7 @@ function _regularMultiply($x_value, $y_value) $y_length = count($y_value); } - $product_value = $this->_array_repeat(0, $x_length + $y_length); + $product_value = self::_array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but @@ -1211,8 +1149,8 @@ function _regularMultiply($x_value, $y_value) for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$j] = (int) ($temp - self::$baseFull * $carry); } $product_value[$j] = $carry; @@ -1224,8 +1162,8 @@ function _regularMultiply($x_value, $y_value) for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$k] = (int) ($temp - self::$baseFull * $carry); } $product_value[$k] = $carry; @@ -1240,17 +1178,17 @@ function _regularMultiply($x_value, $y_value) * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. * - * @param Array $x_value - * @param Array $y_value - * @return Array + * @param array $x_value + * @param array $y_value + * @return array * @access private */ - function _karatsuba($x_value, $y_value) + static function _karatsuba($x_value, $y_value) { $m = min(count($x_value) >> 1, count($y_value) >> 1); - if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) { - return $this->_regularMultiply($x_value, $y_value); + if ($m < self::KARATSUBA_CUTOFF) { + return self::_regularMultiply($x_value, $y_value); } $x1 = array_slice($x_value, $m); @@ -1258,36 +1196,36 @@ function _karatsuba($x_value, $y_value) $y1 = array_slice($y_value, $m); $y0 = array_slice($y_value, 0, $m); - $z2 = $this->_karatsuba($x1, $y1); - $z0 = $this->_karatsuba($x0, $y0); + $z2 = self::_karatsuba($x1, $y1); + $z0 = self::_karatsuba($x0, $y0); - $z1 = $this->_add($x1, false, $x0, false); - $temp = $this->_add($y1, false, $y0, false); - $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]); - $temp = $this->_add($z2, false, $z0, false); - $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false); + $z1 = self::_add($x1, false, $x0, false); + $temp = self::_add($y1, false, $y0, false); + $z1 = self::_karatsuba($z1[self::VALUE], $temp[self::VALUE]); + $temp = self::_add($z2, false, $z0, false); + $z1 = self::_subtract($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); - $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]); + $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); - $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]); - $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false); + $xy = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xy = self::_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); - return $xy[MATH_BIGINTEGER_VALUE]; + return $xy[self::VALUE]; } /** * Performs squaring * - * @param Array $x - * @return Array + * @param array $x + * @return array * @access private */ - function _square($x = false) + static function _square($x = false) { - return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ? - $this->_trim($this->_baseSquare($x)) : - $this->_trim($this->_karatsubaSquare($x)); + return count($x) < 2 * self::KARATSUBA_CUTOFF ? + self::_trim(self::_baseSquare($x)) : + self::_trim(self::_karatsubaSquare($x)); } /** @@ -1297,29 +1235,29 @@ function _square($x = false) * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. * - * @param Array $value - * @return Array + * @param array $value + * @return array * @access private */ - function _baseSquare($value) + static function _baseSquare($value) { - if ( empty($value) ) { + if (empty($value)) { return array(); } - $square_value = $this->_array_repeat(0, 2 * count($value)); + $square_value = self::_array_repeat(0, 2 * count($value)); for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { $i2 = $i << 1; $temp = $square_value[$i2] + $value[$i] * $value[$i]; - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $square_value[$i2] = (int) ($temp - self::$baseFull * $carry); // note how we start from $i+1 instead of 0 as we do in multiplication. for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $square_value[$k] = (int) ($temp - self::$baseFull * $carry); } // the following line can yield values larger 2**15. at this point, PHP should switch @@ -1336,36 +1274,36 @@ function _baseSquare($value) * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. * - * @param Array $value - * @return Array + * @param array $value + * @return array * @access private */ - function _karatsubaSquare($value) + static function _karatsubaSquare($value) { $m = count($value) >> 1; - if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) { - return $this->_baseSquare($value); + if ($m < self::KARATSUBA_CUTOFF) { + return self::_baseSquare($value); } $x1 = array_slice($value, $m); $x0 = array_slice($value, 0, $m); - $z2 = $this->_karatsubaSquare($x1); - $z0 = $this->_karatsubaSquare($x0); + $z2 = self::_karatsubaSquare($x1); + $z0 = self::_karatsubaSquare($x0); - $z1 = $this->_add($x1, false, $x0, false); - $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]); - $temp = $this->_add($z2, false, $z0, false); - $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false); + $z1 = self::_add($x1, false, $x0, false); + $z1 = self::_karatsubaSquare($z1[self::VALUE]); + $temp = self::_add($z2, false, $z0, false); + $z1 = self::_subtract($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); - $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]); + $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); - $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]); - $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false); + $xx = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xx = self::_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); - return $xx[MATH_BIGINTEGER_VALUE]; + return $xx[self::VALUE]; } /** @@ -1379,10 +1317,8 @@ function _karatsubaSquare($value) * Here's an example: * * divide($b); * @@ -1392,17 +1328,17 @@ function _karatsubaSquare($value) * ?> * * - * @param BigInteger $y - * @return Array + * @param \phpseclib\Math\BigInteger $y + * @return array * @access public * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. */ - function divide($y) + function divide(BigInteger $y) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $quotient = new BigInteger(); - $remainder = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $quotient = new static(); + $remainder = new static(); list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); @@ -1411,9 +1347,9 @@ function divide($y) } return array($this->_normalize($quotient), $this->_normalize($remainder)); - case MATH_BIGINTEGER_MODE_BCMATH: - $quotient = new BigInteger(); - $remainder = new BigInteger(); + case self::MODE_BCMATH: + $quotient = new static(); + $remainder = new static(); $quotient->value = bcdiv($this->value, $y->value, 0); $remainder->value = bcmod($this->value, $y->value); @@ -1427,8 +1363,8 @@ function divide($y) if (count($y->value) == 1) { list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); - $quotient = new BigInteger(); - $remainder = new BigInteger(); + $quotient = new static(); + $remainder = new static(); $quotient->value = $q; $remainder->value = array($r); $quotient->is_negative = $this->is_negative != $y->is_negative; @@ -1436,12 +1372,12 @@ function divide($y) } static $zero; - if ( !isset($zero) ) { - $zero = new BigInteger(); + if (!isset($zero)) { + $zero = new static(); } - $x = $this->copy(); - $y = $y->copy(); + $x = clone $this; + $y = clone $y; $x_sign = $x->is_negative; $y_sign = $y->is_negative; @@ -1450,24 +1386,24 @@ function divide($y) $diff = $x->compare($y); - if ( !$diff ) { - $temp = new BigInteger(); + if (!$diff) { + $temp = new static(); $temp->value = array(1); $temp->is_negative = $x_sign != $y_sign; - return array($this->_normalize($temp), $this->_normalize(new BigInteger())); + return array($this->_normalize($temp), $this->_normalize(new static())); } - if ( $diff < 0 ) { + if ($diff < 0) { // if $x is negative, "add" $y. - if ( $x_sign ) { + if ($x_sign) { $x = $y->subtract($x); } - return array($this->_normalize(new BigInteger()), $this->_normalize($x)); + return array($this->_normalize(new static()), $this->_normalize($x)); } // normalize $x and $y as described in HAC 14.23 / 14.24 $msb = $y->value[count($y->value) - 1]; - for ($shift = 0; !($msb & MATH_BIGINTEGER_MSB); ++$shift) { + for ($shift = 0; !($msb & self::$msb); ++$shift) { $msb <<= 1; } $x->_lshift($shift); @@ -1477,15 +1413,15 @@ function divide($y) $x_max = count($x->value) - 1; $y_max = count($y->value) - 1; - $quotient = new BigInteger(); + $quotient = new static(); $quotient_value = &$quotient->value; $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); static $temp, $lhs, $rhs; if (!isset($temp)) { - $temp = new BigInteger(); - $lhs = new BigInteger(); - $rhs = new BigInteger(); + $temp = new static(); + $lhs = new static(); + $rhs = new static(); } $temp_value = &$temp->value; $rhs_value = &$rhs->value; @@ -1493,7 +1429,7 @@ function divide($y) // $temp = $y << ($x_max - $y_max-1) in base 2**26 $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); - while ( $x->compare($temp) >= 0 ) { + while ($x->compare($temp) >= 0) { // calculate the "common residue" ++$quotient_value[$x_max - $y_max]; $x = $x->subtract($temp); @@ -1509,16 +1445,15 @@ function divide($y) ); $y_window = array( $y_value[$y_max], - ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0 + ($y_max > 0) ? $y_value[$y_max - 1] : 0 ); $q_index = $i - $y_max - 1; if ($x_window[0] == $y_window[0]) { - $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT; + $quotient_value[$q_index] = self::$maxDigit; } else { - $quotient_value[$q_index] = (int) ( - ($x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1]) - / + $quotient_value[$q_index] = $this->_safe_divide( + $x_window[0] * self::$baseFull + $x_window[1], $y_window[0] ); } @@ -1530,7 +1465,7 @@ function divide($y) $rhs_value = array($x_window[2], $x_window[1], $x_window[0]); - while ( $lhs->compare($rhs) > 0 ) { + while ($lhs->compare($rhs) > 0) { --$quotient_value[$q_index]; $lhs->value = array($quotient_value[$q_index]); @@ -1561,7 +1496,7 @@ function divide($y) $quotient->is_negative = $x_sign != $y_sign; // calculate the "common residue", if appropriate - if ( $x_sign ) { + if ($x_sign) { $y->_rshift($shift); $x = $y->subtract($x); } @@ -1574,19 +1509,19 @@ function divide($y) * * abc / x = a00 / x + b0 / x + c / x * - * @param Array $dividend - * @param Array $divisor - * @return Array + * @param array $dividend + * @param array $divisor + * @return array * @access private */ - function _divide_digit($dividend, $divisor) + static function _divide_digit($dividend, $divisor) { $carry = 0; $result = array(); for ($i = count($dividend) - 1; $i >= 0; --$i) { - $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i]; - $result[$i] = (int) ($temp / $divisor); + $temp = self::$baseFull * $carry + $dividend[$i]; + $result[$i] = self::_safe_divide($temp, $divisor); $carry = (int) ($temp - $divisor * $result[$i]); } @@ -1599,11 +1534,9 @@ function _divide_digit($dividend, $divisor) * Here's an example: * * modPow($b, $c); * @@ -1611,9 +1544,9 @@ function _divide_digit($dividend, $divisor) * ?> * * - * @param BigInteger $e - * @param BigInteger $n - * @return BigInteger + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger * @access public * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and * and although the approach involving repeated squaring does vastly better, it, too, is impractical @@ -1635,11 +1568,11 @@ function _divide_digit($dividend, $divisor) * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. */ - function modPow($e, $n) + function modPow(BigInteger $e, BigInteger $n) { $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); - if ($e->compare(new BigInteger()) < 0) { + if ($e->compare(new static()) < 0) { $e = $e->abs(); $temp = $this->modInverse($n); @@ -1650,14 +1583,14 @@ function modPow($e, $n) return $this->_normalize($temp->modPow($e, $n)); } - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP ) { - $temp = new BigInteger(); + if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { + $temp = new static(); $temp->value = gmp_powm($this->value, $e->value, $n->value); return $this->_normalize($temp); } - if ($this->compare(new BigInteger()) < 0 || $this->compare($n) > 0) { + if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { list(, $temp) = $this->divide($n); return $temp->modPow($e, $n); } @@ -1669,70 +1602,81 @@ function modPow($e, $n) ); $components = array( - 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), - 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) + 'modulus' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), + 'publicExponent' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) ); - $RSAPublicKey = pack('Ca*a*a*', - 48, $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), - $components['modulus'], $components['publicExponent'] + $RSAPublicKey = pack( + 'Ca*a*a*', + 48, + self::_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] ); - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; + $RSAPublicKey = chr(3) . self::_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; - $encapsulated = pack('Ca*a*', - 48, $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey + $encapsulated = pack( + 'Ca*a*', + 48, + self::_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), + $rsaOID . $RSAPublicKey ); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($encapsulated)) . + chunk_split(Base64::encode($encapsulated)) . '-----END PUBLIC KEY-----'; $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { - return new BigInteger($result, 256); + return new static($result, 256); } } - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { - $temp = new BigInteger(); - $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $temp = new static(); + $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); - return $this->_normalize($temp); + return $this->_normalize($temp); } - if ( empty($e->value) ) { - $temp = new BigInteger(); + if (empty($e->value)) { + $temp = new static(); $temp->value = array(1); return $this->_normalize($temp); } - if ( $e->value == array(1) ) { + if ($e->value == array(1)) { list(, $temp) = $this->divide($n); return $this->_normalize($temp); } - if ( $e->value == array(2) ) { - $temp = new BigInteger(); - $temp->value = $this->_square($this->value); + if ($e->value == array(2)) { + $temp = new static(); + $temp->value = self::_square($this->value); list(, $temp) = $temp->divide($n); return $this->_normalize($temp); } - return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT)); + return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); + + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable // is the modulo odd? - if ( $n->value[0] & 1 ) { - return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY)); + if ($n->value[0] & 1) { + return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); } // if it's not, it's even // find the lowest set bit (eg. the max pow of 2 that divides $n) for ($i = 0; $i < count($n->value); ++$i) { - if ( $n->value[$i] ) { + if ($n->value[$i]) { $temp = decbin($n->value[$i]); $j = strlen($temp) - strrpos($temp, '1') - 1; $j+= 26 * $i; @@ -1741,14 +1685,14 @@ function modPow($e, $n) } // at this point, 2^$j * $n/(2^$j) == $n - $mod1 = $n->copy(); + $mod1 = clone $n; $mod1->_rshift($j); - $mod2 = new BigInteger(); + $mod2 = new static(); $mod2->value = array(1); $mod2->_lshift($j); - $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new BigInteger(); - $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2); + $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); + $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); $y1 = $mod2->modInverse($mod1); $y2 = $mod1->modInverse($mod2); @@ -1768,14 +1712,14 @@ function modPow($e, $n) /** * Performs modular exponentiation. * - * Alias for BigInteger::modPow() + * Alias for modPow(). * - * @param BigInteger $e - * @param BigInteger $n - * @return BigInteger + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger * @access public */ - function powMod($e, $n) + function powMod(BigInteger $e, BigInteger $n) { return $this->modPow($e, $n); } @@ -1788,10 +1732,10 @@ function powMod($e, $n) * however, this function performs a modular reduction after every multiplication and squaring operation. * As such, this function has the same preconditions that the reductions being used do. * - * @param BigInteger $e - * @param BigInteger $n - * @param Integer $mode - * @return BigInteger + * @param \phpseclib\Math\BigInteger $e + * @param \phpseclib\Math\BigInteger $n + * @param int $mode + * @return \phpseclib\Math\BigInteger * @access private */ function _slidingWindow($e, $n, $mode) @@ -1803,56 +1747,58 @@ function _slidingWindow($e, $n, $mode) $e_length = count($e_value) - 1; $e_bits = decbin($e_value[$e_length]); for ($i = $e_length - 1; $i >= 0; --$i) { - $e_bits.= str_pad(decbin($e_value[$i]), MATH_BIGINTEGER_BASE, '0', STR_PAD_LEFT); + $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT); } $e_length = strlen($e_bits); // calculate the appropriate window size. // $window_size == 3 if $window_ranges is between 25 and 81, for example. - for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i); + for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { + } $n_value = $n->value; // precompute $this^0 through $this^$window_size $powers = array(); - $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); - $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); + $powers[1] = self::_prepareReduce($this->value, $n_value, $mode); + $powers[2] = self::_squareReduce($powers[1], $n_value, $mode); // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end // in a 1. ie. it's supposed to be odd. $temp = 1 << ($window_size - 1); for ($i = 1; $i < $temp; ++$i) { $i2 = $i << 1; - $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); + $powers[$i2 + 1] = self::_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); } $result = array(1); - $result = $this->_prepareReduce($result, $n_value, $mode); + $result = self::_prepareReduce($result, $n_value, $mode); - for ($i = 0; $i < $e_length; ) { - if ( !$e_bits[$i] ) { - $result = $this->_squareReduce($result, $n_value, $mode); + for ($i = 0; $i < $e_length;) { + if (!$e_bits[$i]) { + $result = self::_squareReduce($result, $n_value, $mode); ++$i; } else { for ($j = $window_size - 1; $j > 0; --$j) { - if ( !empty($e_bits[$i + $j]) ) { + if (!empty($e_bits[$i + $j])) { break; } } - for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1) - $result = $this->_squareReduce($result, $n_value, $mode); + // eg. the length of substr($e_bits, $i, $j + 1) + for ($k = 0; $k <= $j; ++$k) { + $result = self::_squareReduce($result, $n_value, $mode); } - $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); + $result = self::_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); - $i+=$j + 1; + $i += $j + 1; } } - $temp = new BigInteger(); - $temp->value = $this->_reduce($result, $n_value, $mode); + $temp = new static(); + $temp->value = self::_reduce($result, $n_value, $mode); return $temp; } @@ -1862,34 +1808,34 @@ function _slidingWindow($e, $n, $mode) * * For most $modes this will return the remainder. * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $n - * @param Integer $mode - * @return Array + * @param array $x + * @param array $n + * @param int $mode + * @return array */ - function _reduce($x, $n, $mode) + static function _reduce($x, $n, $mode) { switch ($mode) { - case MATH_BIGINTEGER_MONTGOMERY: - return $this->_montgomery($x, $n); - case MATH_BIGINTEGER_BARRETT: - return $this->_barrett($x, $n); - case MATH_BIGINTEGER_POWEROF2: - $lhs = new BigInteger(); + case self::MONTGOMERY: + return self::_montgomery($x, $n); + case self::BARRETT: + return self::_barrett($x, $n); + case self::POWEROF2: + $lhs = new static(); $lhs->value = $x; - $rhs = new BigInteger(); + $rhs = new static(); $rhs->value = $n; return $x->_mod2($n); - case MATH_BIGINTEGER_CLASSIC: - $lhs = new BigInteger(); + case self::CLASSIC: + $lhs = new static(); $lhs->value = $x; - $rhs = new BigInteger(); + $rhs = new static(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; - case MATH_BIGINTEGER_NONE: + case self::NONE: return $x; default: // an invalid $mode was provided @@ -1899,57 +1845,57 @@ function _reduce($x, $n, $mode) /** * Modular reduction preperation * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $n - * @param Integer $mode - * @return Array + * @param array $x + * @param array $n + * @param int $mode + * @return array */ - function _prepareReduce($x, $n, $mode) + static function _prepareReduce($x, $n, $mode) { - if ($mode == MATH_BIGINTEGER_MONTGOMERY) { - return $this->_prepMontgomery($x, $n); + if ($mode == self::MONTGOMERY) { + return self::_prepMontgomery($x, $n); } - return $this->_reduce($x, $n, $mode); + return self::_reduce($x, $n, $mode); } /** * Modular multiply * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $y - * @param Array $n - * @param Integer $mode - * @return Array + * @param array $x + * @param array $y + * @param array $n + * @param int $mode + * @return array */ - function _multiplyReduce($x, $y, $n, $mode) + static function _multiplyReduce($x, $y, $n, $mode) { - if ($mode == MATH_BIGINTEGER_MONTGOMERY) { - return $this->_montgomeryMultiply($x, $y, $n); + if ($mode == self::MONTGOMERY) { + return self::_montgomeryMultiply($x, $y, $n); } - $temp = $this->_multiply($x, false, $y, false); - return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode); + $temp = self::_multiply($x, false, $y, false); + return self::_reduce($temp[self::VALUE], $n, $mode); } /** * Modular square * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $n - * @param Integer $mode - * @return Array + * @param array $x + * @param array $n + * @param int $mode + * @return array */ - function _squareReduce($x, $n, $mode) + static function _squareReduce($x, $n, $mode) { - if ($mode == MATH_BIGINTEGER_MONTGOMERY) { - return $this->_montgomeryMultiply($x, $x, $n); + if ($mode == self::MONTGOMERY) { + return self::_montgomeryMultiply($x, $x, $n); } - return $this->_reduce($this->_square($x), $n, $mode); + return self::_reduce(self::_square($x), $n, $mode); } /** @@ -1958,14 +1904,14 @@ function _squareReduce($x, $n, $mode) * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1), * we'll just use this function as a wrapper for doing that. * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param BigInteger - * @return BigInteger + * @param \phpseclib\Math\BigInteger + * @return \phpseclib\Math\BigInteger */ function _mod2($n) { - $temp = new BigInteger(); + $temp = new static(); $temp->value = array(1); return $this->bitwise_and($n->subtract($temp)); } @@ -1988,25 +1934,25 @@ function _mod2($n) * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line * comments for details. * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param Array $n - * @param Array $m - * @return Array + * @param array $n + * @param array $m + * @return array */ - function _barrett($n, $m) + static function _barrett($n, $m) { static $cache = array( - MATH_BIGINTEGER_VARIABLE => array(), - MATH_BIGINTEGER_DATA => array() + self::VARIABLE => array(), + self::DATA => array() ); $m_length = count($m); - // if ($this->_compare($n, $this->_square($m)) >= 0) { + // if (self::_compare($n, self::_square($m)) >= 0) { if (count($n) > 2 * $m_length) { - $lhs = new BigInteger(); - $rhs = new BigInteger(); + $lhs = new static(); + $rhs = new static(); $lhs->value = $n; $rhs->value = $m; list(, $temp) = $lhs->divide($rhs); @@ -2015,68 +1961,68 @@ function _barrett($n, $m) // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced if ($m_length < 5) { - return $this->_regularBarrett($n, $m); + return self::_regularBarrett($n, $m); } // n = 2 * m.length - if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { - $key = count($cache[MATH_BIGINTEGER_VARIABLE]); - $cache[MATH_BIGINTEGER_VARIABLE][] = $m; + if (($key = array_search($m, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; - $lhs = new BigInteger(); + $lhs = new static(); $lhs_value = &$lhs->value; - $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value = self::_array_repeat(0, $m_length + ($m_length >> 1)); $lhs_value[] = 1; - $rhs = new BigInteger(); + $rhs = new static(); $rhs->value = $m; list($u, $m1) = $lhs->divide($rhs); $u = $u->value; $m1 = $m1->value; - $cache[MATH_BIGINTEGER_DATA][] = array( + $cache[self::DATA][] = array( 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) 'm1'=> $m1 // m.length ); } else { - extract($cache[MATH_BIGINTEGER_DATA][$key]); + extract($cache[self::DATA][$key]); } $cutoff = $m_length + ($m_length >> 1); $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) $msd = array_slice($n, $cutoff); // m.length >> 1 - $lsd = $this->_trim($lsd); - $temp = $this->_multiply($msd, false, $m1, false); - $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1 + $lsd = self::_trim($lsd); + $temp = self::_multiply($msd, false, $m1, false); + $n = self::_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 if ($m_length & 1) { - return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m); + return self::_regularBarrett($n[self::VALUE], $m); } // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 - $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1); + $temp = array_slice($n[self::VALUE], $m_length - 1); // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 - $temp = $this->_multiply($temp, false, $u, false); + $temp = self::_multiply($temp, false, $u, false); // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) - $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1); + $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) - $temp = $this->_multiply($temp, false, $m, false); + $temp = self::_multiply($temp, false, $m, false); // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). - $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); + $result = self::_subtract($n[self::VALUE], false, $temp[self::VALUE], false); - while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) { - $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false); + while (self::_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { + $result = self::_subtract($result[self::VALUE], $result[self::SIGN], $m, false); } - return $result[MATH_BIGINTEGER_VALUE]; + return $result[self::VALUE]; } /** @@ -2085,70 +2031,70 @@ function _barrett($n, $m) * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this * is that this function does not fold the denominator into a smaller form. * - * @see _slidingWindow() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $n - * @return Array + * @param array $x + * @param array $n + * @return array */ - function _regularBarrett($x, $n) + static function _regularBarrett($x, $n) { static $cache = array( - MATH_BIGINTEGER_VARIABLE => array(), - MATH_BIGINTEGER_DATA => array() + self::VARIABLE => array(), + self::DATA => array() ); $n_length = count($n); if (count($x) > 2 * $n_length) { - $lhs = new BigInteger(); - $rhs = new BigInteger(); + $lhs = new static(); + $rhs = new static(); $lhs->value = $x; $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } - if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { - $key = count($cache[MATH_BIGINTEGER_VARIABLE]); - $cache[MATH_BIGINTEGER_VARIABLE][] = $n; - $lhs = new BigInteger(); + if (($key = array_search($n, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $n; + $lhs = new static(); $lhs_value = &$lhs->value; - $lhs_value = $this->_array_repeat(0, 2 * $n_length); + $lhs_value = self::_array_repeat(0, 2 * $n_length); $lhs_value[] = 1; - $rhs = new BigInteger(); + $rhs = new static(); $rhs->value = $n; list($temp, ) = $lhs->divide($rhs); // m.length - $cache[MATH_BIGINTEGER_DATA][] = $temp->value; + $cache[self::DATA][] = $temp->value; } // 2 * m.length - (m.length - 1) = m.length + 1 $temp = array_slice($x, $n_length - 1); // (m.length + 1) + m.length = 2 * m.length + 1 - $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false); + $temp = self::_multiply($temp, false, $cache[self::DATA][$key], false); // (2 * m.length + 1) - (m.length - 1) = m.length + 2 - $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1); + $temp = array_slice($temp[self::VALUE], $n_length + 1); // m.length + 1 $result = array_slice($x, 0, $n_length + 1); // m.length + 1 - $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); - // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1) + $temp = self::_multiplyLower($temp, false, $n, false, $n_length + 1); + // $temp == array_slice(self::_multiply($temp, false, $n, false)->value, 0, $n_length + 1) - if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) { - $corrector_value = $this->_array_repeat(0, $n_length + 1); - $corrector_value[] = 1; - $result = $this->_add($result, false, $corrector_value, false); - $result = $result[MATH_BIGINTEGER_VALUE]; + if (self::_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { + $corrector_value = self::_array_repeat(0, $n_length + 1); + $corrector_value[count($corrector_value)] = 1; + $result = self::_add($result, false, $corrector_value, false); + $result = $result[self::VALUE]; } // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits - $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]); - while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) { - $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false); + $result = self::_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); + while (self::_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { + $result = self::_subtract($result[self::VALUE], $result[self::SIGN], $n, false); } - return $result[MATH_BIGINTEGER_VALUE]; + return $result[self::VALUE]; } /** @@ -2156,28 +2102,28 @@ function _regularBarrett($x, $n) * * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. * - * @see _regularBarrett() - * @param Array $x_value - * @param Boolean $x_negative - * @param Array $y_value - * @param Boolean $y_negative - * @param Integer $stop - * @return Array + * @see self::_regularBarrett() + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @param int $stop + * @return array * @access private */ - function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) + static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) { $x_length = count($x_value); $y_length = count($y_value); - if ( !$x_length || !$y_length ) { // a 0 is being multiplied + if (!$x_length || !$y_length) { // a 0 is being multiplied return array( - MATH_BIGINTEGER_VALUE => array(), - MATH_BIGINTEGER_SIGN => false + self::VALUE => array(), + self::SIGN => false ); } - if ( $x_length < $y_length ) { + if ($x_length < $y_length) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; @@ -2186,7 +2132,7 @@ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) $y_length = count($y_value); } - $product_value = $this->_array_repeat(0, $x_length + $y_length); + $product_value = self::_array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but @@ -2198,8 +2144,8 @@ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$j] = (int) ($temp - self::$baseFull * $carry); } if ($j < $stop) { @@ -2214,8 +2160,8 @@ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $product_value[$k] = (int) ($temp - self::$baseFull * $carry); } if ($k < $stop) { @@ -2224,8 +2170,8 @@ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) } return array( - MATH_BIGINTEGER_VALUE => $this->_trim($product_value), - MATH_BIGINTEGER_SIGN => $x_negative != $y_negative + self::VALUE => self::_trim($product_value), + self::SIGN => $x_negative != $y_negative ); } @@ -2237,45 +2183,45 @@ function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function * to work correctly. * - * @see _prepMontgomery() - * @see _slidingWindow() + * @see self::_prepMontgomery() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $n - * @return Array + * @param array $x + * @param array $n + * @return array */ - function _montgomery($x, $n) + static function _montgomery($x, $n) { static $cache = array( - MATH_BIGINTEGER_VARIABLE => array(), - MATH_BIGINTEGER_DATA => array() + self::VARIABLE => array(), + self::DATA => array() ); - if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { - $key = count($cache[MATH_BIGINTEGER_VARIABLE]); - $cache[MATH_BIGINTEGER_VARIABLE][] = $x; - $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n); + if (($key = array_search($n, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $x; + $cache[self::DATA][] = self::_modInverse67108864($n); } $k = count($n); - $result = array(MATH_BIGINTEGER_VALUE => $x); + $result = array(self::VALUE => $x); for ($i = 0; $i < $k; ++$i) { - $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key]; - $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); - $temp = $this->_regularMultiply(array($temp), $n); + $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = self::_regularMultiply(array($temp), $n); $temp = array_merge($this->_array_repeat(0, $i), $temp); - $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false); + $result = self::_add($result[self::VALUE], false, $temp, false); } - $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k); + $result[self::VALUE] = array_slice($result[self::VALUE], $k); - if ($this->_compare($result, false, $n, false) >= 0) { - $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false); + if (self::_compare($result, false, $n, false) >= 0) { + $result = self::_subtract($result[self::VALUE], false, $n, false); } - return $result[MATH_BIGINTEGER_VALUE]; + return $result[self::VALUE]; } /** @@ -2284,65 +2230,70 @@ function _montgomery($x, $n) * Interleaves the montgomery reduction and long multiplication algorithms together as described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} * - * @see _prepMontgomery() - * @see _montgomery() + * @see self::_prepMontgomery() + * @see self::_montgomery() * @access private - * @param Array $x - * @param Array $y - * @param Array $m - * @return Array + * @param array $x + * @param array $y + * @param array $m + * @return array */ - function _montgomeryMultiply($x, $y, $m) + static function _montgomeryMultiply($x, $y, $m) { - $temp = $this->_multiply($x, false, $y, false); - return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m); + $temp = self::_multiply($x, false, $y, false); + return self::_montgomery($temp[self::VALUE], $m); + + // the following code, although not callable, can be run independently of the above code + // although the above code performed better in my benchmarks the following could might + // perform better under different circumstances. in lieu of deleting it it's just been + // made uncallable static $cache = array( - MATH_BIGINTEGER_VARIABLE => array(), - MATH_BIGINTEGER_DATA => array() + self::VARIABLE => array(), + self::DATA => array() ); - if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { - $key = count($cache[MATH_BIGINTEGER_VARIABLE]); - $cache[MATH_BIGINTEGER_VARIABLE][] = $m; - $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m); + if (($key = array_search($m, $cache[self::VARIABLE])) === false) { + $key = count($cache[self::VARIABLE]); + $cache[self::VARIABLE][] = $m; + $cache[self::DATA][] = self::_modInverse67108864($m); } $n = max(count($x), count($y), count($m)); $x = array_pad($x, $n, 0); $y = array_pad($y, $n, 0); $m = array_pad($m, $n, 0); - $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1)); + $a = array(self::VALUE => self::_array_repeat(0, $n + 1)); for ($i = 0; $i < $n; ++$i) { - $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0]; - $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); - $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key]; - $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); - $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); - $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); - $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1); + $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = $temp * $cache[self::DATA][$key]; + $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); + $temp = self::_add(self::_regularMultiply(array($x[$i]), $y), false, self::_regularMultiply(array($temp), $m), false); + $a = self::_add($a[self::VALUE], false, $temp[self::VALUE], false); + $a[self::VALUE] = array_slice($a[self::VALUE], 1); } - if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) { - $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false); + if (self::_compare($a[self::VALUE], false, $m, false) >= 0) { + $a = self::_subtract($a[self::VALUE], false, $m, false); } - return $a[MATH_BIGINTEGER_VALUE]; + return $a[self::VALUE]; } /** * Prepare a number for use in Montgomery Modular Reductions * - * @see _montgomery() - * @see _slidingWindow() + * @see self::_montgomery() + * @see self::_slidingWindow() * @access private - * @param Array $x - * @param Array $n - * @return Array + * @param array $x + * @param array $n + * @return array */ - function _prepMontgomery($x, $n) + static function _prepMontgomery($x, $n) { - $lhs = new BigInteger(); - $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); - $rhs = new BigInteger(); + $lhs = new static(); + $lhs->value = array_merge(self::_array_repeat(0, count($n)), $x); + $rhs = new static(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); @@ -2370,10 +2321,10 @@ function _prepMontgomery($x, $n) * * Thanks to Pedro Gimeno Fortea for input! * - * @see _montgomery() + * @see self::_montgomery() * @access private - * @param Array $x - * @return Integer + * @param array $x + * @return int */ function _modInverse67108864($x) // 2**26 == 67,108,864 { @@ -2382,8 +2333,8 @@ function _modInverse67108864($x) // 2**26 == 67,108,864 $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 - $result = fmod($result * (2 - fmod($x * $result, MATH_BIGINTEGER_BASE_FULL)), MATH_BIGINTEGER_BASE_FULL); // x**-1 mod 2**26 - return $result & MATH_BIGINTEGER_MAX_DIGIT; + $result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26 + return $result & self::$maxDigit; } /** @@ -2394,10 +2345,8 @@ function _modInverse67108864($x) // 2**26 == 67,108,864 * Here's an example: * * modInverse($b); * echo $c->toString(); // outputs 4 @@ -2410,25 +2359,25 @@ function _modInverse67108864($x) // 2**26 == 67,108,864 * ?> * * - * @param BigInteger $n - * @return mixed false, if no modular inverse exists, BigInteger, otherwise. + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger|false * @access public * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. */ - function modInverse($n) + function modInverse(BigInteger $n) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_invert($this->value, $n->value); - return ( $temp->value === false ) ? false : $this->_normalize($temp); + return ($temp->value === false) ? false : $this->_normalize($temp); } static $zero, $one; if (!isset($zero)) { - $zero = new BigInteger(); - $one = new BigInteger(1); + $zero = new static(); + $one = new static(1); } // $x mod -$n == $x mod $n. @@ -2462,10 +2411,8 @@ function modInverse($n) * Here's an example: * * extendedGCD($b)); * @@ -2474,25 +2421,25 @@ function modInverse($n) * ?> * * - * @param BigInteger $n - * @return BigInteger + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger * @access public * @internal Calculates the GCD using the binary xGCD algorithim described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, * the more traditional algorithim requires "relatively costly multiple-precision divisions". */ - function extendedGCD($n) + function extendedGCD(BigInteger $n) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: extract(gmp_gcdext($this->value, $n->value)); return array( - 'gcd' => $this->_normalize(new BigInteger($g)), - 'x' => $this->_normalize(new BigInteger($s)), - 'y' => $this->_normalize(new BigInteger($t)) + 'gcd' => $this->_normalize(new static($g)), + 'x' => $this->_normalize(new static($s)), + 'y' => $this->_normalize(new static($t)) ); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, // the basic extended euclidean algorithim is what we're using. @@ -2522,38 +2469,38 @@ function extendedGCD($n) } return array( - 'gcd' => $this->_normalize(new BigInteger($u)), - 'x' => $this->_normalize(new BigInteger($a)), - 'y' => $this->_normalize(new BigInteger($b)) + 'gcd' => $this->_normalize(new static($u)), + 'x' => $this->_normalize(new static($a)), + 'y' => $this->_normalize(new static($b)) ); } - $y = $n->copy(); - $x = $this->copy(); - $g = new BigInteger(); + $y = clone $n; + $x = clone $this; + $g = new static(); $g->value = array(1); - while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) { + while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) { $x->_rshift(1); $y->_rshift(1); $g->_lshift(1); } - $u = $x->copy(); - $v = $y->copy(); + $u = clone $x; + $v = clone $y; - $a = new BigInteger(); - $b = new BigInteger(); - $c = new BigInteger(); - $d = new BigInteger(); + $a = new static(); + $b = new static(); + $c = new static(); + $d = new static(); $a->value = $d->value = $g->value = array(1); $b->value = $c->value = array(); - while ( !empty($u->value) ) { - while ( !($u->value[0] & 1) ) { + while (!empty($u->value)) { + while (!($u->value[0] & 1)) { $u->_rshift(1); - if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) { + if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) { $a = $a->add($y); $b = $b->subtract($x); } @@ -2561,9 +2508,9 @@ function extendedGCD($n) $b->_rshift(1); } - while ( !($v->value[0] & 1) ) { + while (!($v->value[0] & 1)) { $v->_rshift(1); - if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) { + if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) { $c = $c->add($y); $d = $d->subtract($x); } @@ -2597,10 +2544,8 @@ function extendedGCD($n) * Here's an example: * * extendedGCD($b); * @@ -2608,11 +2553,11 @@ function extendedGCD($n) * ?> * * - * @param BigInteger $n - * @return BigInteger + * @param \phpseclib\Math\BigInteger $n + * @return \phpseclib\Math\BigInteger * @access public */ - function gcd($n) + function gcd(BigInteger $n) { extract($this->extendedGCD($n)); return $gcd; @@ -2621,18 +2566,18 @@ function gcd($n) /** * Absolute value. * - * @return BigInteger + * @return \phpseclib\Math\BigInteger * @access public */ function abs() { - $temp = new BigInteger(); + $temp = new static(); - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: $temp->value = gmp_abs($this->value); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; break; default: @@ -2654,45 +2599,45 @@ function abs() * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * - * @param BigInteger $y - * @return Integer < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @param \phpseclib\Math\BigInteger $y + * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @access public - * @see equals() + * @see self::equals() * @internal Could return $this->subtract($x), but that's not as fast as what we do do. */ - function compare($y) + function compare(BigInteger $y) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: return gmp_cmp($this->value, $y->value); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: return bccomp($this->value, $y->value, 0); } - return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); + return self::_compare($this->value, $this->is_negative, $y->value, $y->is_negative); } /** * Compares two numbers. * - * @param Array $x_value - * @param Boolean $x_negative - * @param Array $y_value - * @param Boolean $y_negative - * @return Integer - * @see compare() + * @param array $x_value + * @param bool $x_negative + * @param array $y_value + * @param bool $y_negative + * @return int + * @see self::compare() * @access private */ - function _compare($x_value, $x_negative, $y_value, $y_negative) + static function _compare($x_value, $x_negative, $y_value, $y_negative) { - if ( $x_negative != $y_negative ) { - return ( !$x_negative && $y_negative ) ? 1 : -1; + if ($x_negative != $y_negative) { + return (!$x_negative && $y_negative) ? 1 : -1; } $result = $x_negative ? -1 : 1; - if ( count($x_value) != count($y_value) ) { - return ( count($x_value) > count($y_value) ) ? $result : -$result; + if (count($x_value) != count($y_value)) { + return (count($x_value) > count($y_value)) ? $result : -$result; } $size = max(count($x_value), count($y_value)); @@ -2701,7 +2646,7 @@ function _compare($x_value, $x_negative, $y_value, $y_negative) for ($i = count($x_value) - 1; $i >= 0; --$i) { if ($x_value[$i] != $y_value[$i]) { - return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result; + return ($x_value[$i] > $y_value[$i]) ? $result : -$result; } } @@ -2713,15 +2658,15 @@ function _compare($x_value, $x_negative, $y_value, $y_negative) * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * - * @param BigInteger $x - * @return Boolean + * @param \phpseclib\Math\BigInteger $x + * @return bool * @access public - * @see compare() + * @see self::compare() */ - function equals($x) + function equals(BigInteger $x) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: return gmp_cmp($this->value, $x->value) == 0; default: return $this->value === $x->value && $this->is_negative == $x->is_negative; @@ -2734,39 +2679,57 @@ function equals($x) * Some bitwise operations give different results depending on the precision being used. Examples include left * shift, not, and rotates. * - * @param Integer $bits + * @param int $bits * @access public */ function setPrecision($bits) { + if ($bits < 1) { + $this->precision = -1; + $this->bitmask = false; + + return; + } $this->precision = $bits; - if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) { - $this->bitmask = new BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); + if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { + $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); } else { - $this->bitmask = new BigInteger(bcpow('2', $bits, 0)); + $this->bitmask = new static(bcpow('2', $bits, 0)); } $temp = $this->_normalize($this); $this->value = $temp->value; } + /** + * Get Precision + * + * @return int + * @see self::setPrecision() + * @access public + */ + function getPrecision() + { + return $this->precision; + } + /** * Logical And * - * @param BigInteger $x + * @param \phpseclib\Math\BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return BigInteger + * @return \phpseclib\Math\BigInteger */ - function bitwise_and($x) + function bitwise_and(BigInteger $x) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_and($this->value, $x->value); return $this->_normalize($temp); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); @@ -2775,10 +2738,10 @@ function bitwise_and($x) $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); - return $this->_normalize(new BigInteger($left & $right, 256)); + return $this->_normalize(new static($left & $right, 256)); } - $result = $this->copy(); + $result = clone $this; $length = min(count($x->value), count($this->value)); @@ -2794,20 +2757,20 @@ function bitwise_and($x) /** * Logical Or * - * @param BigInteger $x + * @param \phpseclib\Math\BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return BigInteger + * @return \phpseclib\Math\BigInteger */ - function bitwise_or($x) + function bitwise_or(BigInteger $x) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_or($this->value, $x->value); return $this->_normalize($temp); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); @@ -2816,11 +2779,11 @@ function bitwise_or($x) $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); - return $this->_normalize(new BigInteger($left | $right, 256)); + return $this->_normalize(new static($left | $right, 256)); } $length = max(count($this->value), count($x->value)); - $result = $this->copy(); + $result = clone $this; $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2834,20 +2797,20 @@ function bitwise_or($x) /** * Logical Exclusive-Or * - * @param BigInteger $x + * @param \phpseclib\Math\BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return BigInteger + * @return \phpseclib\Math\BigInteger */ - function bitwise_xor($x) + function bitwise_xor(BigInteger $x) { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - $temp = new BigInteger(); + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + $temp = new static(); $temp->value = gmp_xor($this->value, $x->value); return $this->_normalize($temp); - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); @@ -2856,11 +2819,11 @@ function bitwise_xor($x) $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); - return $this->_normalize(new BigInteger($left ^ $right, 256)); + return $this->_normalize(new static($left ^ $right, 256)); } $length = max(count($this->value), count($x->value)); - $result = $this->copy(); + $result = clone $this; $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2876,13 +2839,16 @@ function bitwise_xor($x) * * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return BigInteger + * @return \phpseclib\Math\BigInteger */ function bitwise_not() { // calculuate "not" without regard to $this->precision // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) $temp = $this->toBytes(); + if ($temp == '') { + return ''; + } $pre_msb = decbin(ord($temp[0])); $temp = ~$temp; $msb = decbin(ord($temp[0])); @@ -2895,16 +2861,17 @@ function bitwise_not() $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; $new_bits = $this->precision - $current_bits; if ($new_bits <= 0) { - return $this->_normalize(new BigInteger($temp, 256)); + return $this->_normalize(new static($temp, 256)); } // generate as many leading 1's as we need to. $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); - $this->_base256_lshift($leading_ones, $current_bits); - $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT); + self::_base256_lshift($leading_ones, $current_bits); + + $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); - return $this->_normalize(new BigInteger($leading_ones | $temp, 256)); + return $this->_normalize(new static($leading_ones | $temp, 256)); } /** @@ -2912,17 +2879,17 @@ function bitwise_not() * * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. * - * @param Integer $shift - * @return BigInteger + * @param int $shift + * @return \phpseclib\Math\BigInteger * @access public * @internal The only version that yields any speed increases is the internal version. */ function bitwise_rightShift($shift) { - $temp = new BigInteger(); + $temp = new static(); - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: static $two; if (!isset($two)) { @@ -2932,7 +2899,7 @@ function bitwise_rightShift($shift) $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); break; @@ -2950,17 +2917,17 @@ function bitwise_rightShift($shift) * * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. * - * @param Integer $shift - * @return BigInteger + * @param int $shift + * @return \phpseclib\Math\BigInteger * @access public * @internal The only version that yields any speed increases is the internal version. */ function bitwise_leftShift($shift) { - $temp = new BigInteger(); + $temp = new static(); - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: static $two; if (!isset($two)) { @@ -2970,7 +2937,7 @@ function bitwise_leftShift($shift) $temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); break; @@ -2988,8 +2955,8 @@ function bitwise_leftShift($shift) * * Instead of the top x bits being dropped they're appended to the shifted bit string. * - * @param Integer $shift - * @return BigInteger + * @param int $shift + * @return \phpseclib\Math\BigInteger * @access public */ function bitwise_leftRotate($shift) @@ -2998,15 +2965,16 @@ function bitwise_leftRotate($shift) if ($this->precision > 0) { $precision = $this->precision; - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { - $mask = $this->bitmask->subtract(new BigInteger(1)); + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + $mask = $this->bitmask->subtract(new static(1)); $mask = $mask->toBytes(); } else { $mask = $this->bitmask->toBytes(); } } else { $temp = ord($bits[0]); - for ($i = 0; $temp >> $i; ++$i); + for ($i = 0; $temp >> $i; ++$i) { + } $precision = 8 * strlen($bits) - 8 + $i; $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); } @@ -3017,13 +2985,13 @@ function bitwise_leftRotate($shift) $shift%= $precision; if (!$shift) { - return $this->copy(); + return clone $this; } $left = $this->bitwise_leftShift($shift); - $left = $left->bitwise_and(new BigInteger($mask, 256)); + $left = $left->bitwise_and(new static($mask, 256)); $right = $this->bitwise_rightShift($precision - $shift); - $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); + $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); return $this->_normalize($result); } @@ -3032,8 +3000,8 @@ function bitwise_leftRotate($shift) * * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. * - * @param Integer $shift - * @return BigInteger + * @param int $shift + * @return \phpseclib\Math\BigInteger * @access public */ function bitwise_rightRotate($shift) @@ -3041,32 +3009,19 @@ function bitwise_rightRotate($shift) return $this->bitwise_leftRotate(-$shift); } - /** - * Set random number generator function - * - * This function is deprecated. - * - * @param String $generator - * @access public - */ - function setRandomGenerator($generator) - { - } - /** * Generates a random BigInteger * - * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not. + * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. * - * @param Integer $length - * @return BigInteger + * @param int $length + * @return \phpseclib\Math\BigInteger * @access private */ - function _random_number_helper($size) + static function _random_number_helper($size) { - $crypt_random = function_exists('phpseclib\\Crypt\\crypt_random_string') || (!class_exists('phpseclib\\Crypt\\Random') && function_exists('phpseclib\\Crypt\\crypt_random_string')); - if ($crypt_random) { - $random = \phpseclib\Crypt\crypt_random_string($size); + if (class_exists('\phpseclib\Crypt\Random')) { + $random = Random::string($size); } else { $random = ''; @@ -3081,32 +3036,30 @@ function _random_number_helper($size) } } - return new BigInteger($random, 256); + return new static($random, 256); } /** * Generate a random number * - * @param optional Integer $min - * @param optional Integer $max - * @return BigInteger + * Returns a random number between $min and $max where $min and $max + * can be defined using one of the two methods: + * + * BigInteger::random($min, $max) + * BigInteger::random($max, $min) + * + * @param \phpseclib\Math\BigInteger $arg1 + * @param \phpseclib\Math\BigInteger $arg2 + * @return \phpseclib\Math\BigInteger * @access public */ - function random($min = false, $max = false) + static function random(BigInteger $min, BigInteger $max) { - if ($min === false) { - $min = new BigInteger(0); - } - - if ($max === false) { - $max = new BigInteger(0x7FFFFFFF); - } - $compare = $max->compare($min); if (!$compare) { return $this->_normalize($min); - } else if ($compare < 0) { + } elseif ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; @@ -3115,7 +3068,7 @@ function random($min = false, $max = false) static $one; if (!isset($one)) { - $one = new BigInteger(1); + $one = new static(1); } $max = $max->subtract($min->subtract($one)); @@ -3136,8 +3089,8 @@ function random($min = false, $max = false) http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string */ - $random_max = new BigInteger(chr(1) . str_repeat("\0", $size), 256); - $random = $this->_random_number_helper($size); + $random_max = new static(chr(1) . str_repeat("\0", $size), 256); + $random = static::_random_number_helper($size); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); @@ -3146,44 +3099,36 @@ function random($min = false, $max = false) $random = $random->subtract($max_multiple); $random_max = $random_max->subtract($max_multiple); $random = $random->bitwise_leftShift(8); - $random = $random->add($this->_random_number_helper(1)); + $random = $random->add(self::_random_number_helper(1)); $random_max = $random_max->bitwise_leftShift(8); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); } list(, $random) = $random->divide($max); - return $this->_normalize($random->add($min)); + return $random->add($min); } /** * 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. + * 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 optional Integer $min - * @param optional Integer $max - * @param optional Integer $timeout - * @return BigInteger + * @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}. */ - function randomPrime($min = false, $max = false, $timeout = false) + static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) { - if ($min === false) { - $min = new BigInteger(0); - } - - if ($max === false) { - $max = new BigInteger(0x7FFFFFFF); - } - $compare = $max->compare($min); if (!$compare) { return $min->isPrime() ? $min : false; - } else if ($compare < 0) { + } elseif ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; @@ -3192,17 +3137,17 @@ function randomPrime($min = false, $max = false, $timeout = false) static $one, $two; if (!isset($one)) { - $one = new BigInteger(1); - $two = new BigInteger(2); + $one = new static(1); + $two = new static(2); } $start = time(); - $x = $this->random($min, $max); + $x = self::random($min, $max); // gmp_nextprime() requires PHP 5 >= 5.2.0 per . - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { - $p = new BigInteger(); + if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { + $p = new static(); $p->value = gmp_nextprime($x->value); if ($p->compare($max) <= 0) { @@ -3213,7 +3158,7 @@ function randomPrime($min = false, $max = false, $timeout = false) $x = $x->subtract($one); } - return $x->randomPrime($min, $x); + return self::randomPrime($min, $x); } if ($x->equals($two)) { @@ -3226,11 +3171,11 @@ function randomPrime($min = false, $max = false, $timeout = false) if ($min->equals($max)) { return false; } - $x = $min->copy(); + $x = clone $min; $x->_make_odd(); } - $initial_x = $x->copy(); + $initial_x = clone $x; while (true) { if ($timeout !== false && time() - $start > $timeout) { @@ -3244,7 +3189,7 @@ function randomPrime($min = false, $max = false, $timeout = false) $x = $x->add($two); if ($x->compare($max) > 0) { - $x = $min->copy(); + $x = clone $min; if ($x->equals($two)) { return $x; } @@ -3262,16 +3207,16 @@ function randomPrime($min = false, $max = false, $timeout = false) * * If the current number is odd it'll be unchanged. If it's even, one will be added to it. * - * @see randomPrime() + * @see self::randomPrime() * @access private */ function _make_odd() { - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: gmp_setbit($this->value, 0); break; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: if ($this->value[strlen($this->value) - 1] % 2 == 0) { $this->value = bcadd($this->value, '1'); } @@ -3285,11 +3230,11 @@ function _make_odd() * Checks a numer to see if it's prime * * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the - * $t parameter is distributability. BigInteger::randomPrime() can be distributed accross multiple pageloads + * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads * on a website instead of just one. * - * @param optional Integer $t - * @return Boolean + * @param \phpseclib\Math\BigInteger $t + * @return bool * @access public * @internal Uses the * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See @@ -3319,10 +3264,10 @@ function isPrime($t = false) // ie. gmp_testbit($this, 0) // ie. isEven() or !isOdd() - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: return gmp_prob_prime($this->value, $t) != 0; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: if ($this->value === '2') { return true; } @@ -3356,15 +3301,15 @@ function isPrime($t = false) 953, 967, 971, 977, 983, 991, 997 ); - if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) { + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { for ($i = 0; $i < count($primes); ++$i) { - $primes[$i] = new BigInteger($primes[$i]); + $primes[$i] = new static($primes[$i]); } } - $zero = new BigInteger(); - $one = new BigInteger(1); - $two = new BigInteger(2); + $zero = new static(); + $one = new static(1); + $two = new static(2); } if ($this->equals($one)) { @@ -3372,7 +3317,7 @@ function isPrime($t = false) } // see HAC 4.4.1 "Random search for probable primes" - if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) { + if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { foreach ($primes as $prime) { list(, $r) = $this->divide($prime); if ($r->equals($zero)) { @@ -3382,21 +3327,21 @@ function isPrime($t = false) } else { $value = $this->value; foreach ($primes as $prime) { - list(, $r) = $this->_divide_digit($value, $prime); + list(, $r) = self::_divide_digit($value, $prime); if (!$r) { return count($value) == 1 && $value[0] == $prime; } } } - $n = $this->copy(); + $n = clone $this; $n_1 = $n->subtract($one); $n_2 = $n->subtract($two); - $r = $n_1->copy(); + $r = clone $n_1; $r_value = $r->value; // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); - if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { $s = 0; // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier while ($r->value[strlen($r->value) - 1] % 2 == 0) { @@ -3406,7 +3351,8 @@ function isPrime($t = false) } else { for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { $temp = ~$r_value[$i] & 0xFFFFFF; - for ($j = 1; ($temp >> $j) & 1; ++$j); + for ($j = 1; ($temp >> $j) & 1; ++$j) { + } if ($j != 25) { break; } @@ -3416,7 +3362,7 @@ function isPrime($t = false) } for ($i = 0; $i < $t; ++$i) { - $a = $this->random($two, $n_2); + $a = self::random($two, $n_2); $y = $a->modPow($r, $n); if (!$y->equals($one) && !$y->equals($n_1)) { @@ -3440,29 +3386,29 @@ function isPrime($t = false) * * Shifts BigInteger's by $shift bits. * - * @param Integer $shift + * @param int $shift * @access private */ function _lshift($shift) { - if ( $shift == 0 ) { + if ($shift == 0) { return; } - $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); - $shift %= MATH_BIGINTEGER_BASE; + $num_digits = (int) ($shift / self::$base); + $shift %= self::$base; $shift = 1 << $shift; $carry = 0; for ($i = 0; $i < count($this->value); ++$i) { $temp = $this->value[$i] * $shift + $carry; - $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); - $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL); + $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); + $this->value[$i] = (int) ($temp - $carry * self::$baseFull); } - if ( $carry ) { - $this->value[] = $carry; + if ($carry) { + $this->value[count($this->value)] = $carry; } while ($num_digits--) { @@ -3475,7 +3421,7 @@ function _lshift($shift) * * Shifts BigInteger's by $shift bits. * - * @param Integer $shift + * @param int $shift * @access private */ function _rshift($shift) @@ -3484,12 +3430,12 @@ function _rshift($shift) return; } - $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); - $shift %= MATH_BIGINTEGER_BASE; - $carry_shift = MATH_BIGINTEGER_BASE - $shift; + $num_digits = (int) ($shift / self::$base); + $shift %= self::$base; + $carry_shift = self::$base - $shift; $carry_mask = (1 << $shift) - 1; - if ( $num_digits ) { + if ($num_digits) { $this->value = array_slice($this->value, $num_digits); } @@ -3509,9 +3455,9 @@ function _rshift($shift) * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * - * @param BigInteger - * @return BigInteger - * @see _trim() + * @param \phpseclib\Math\BigInteger + * @return \phpseclib\Math\BigInteger + * @see self::_trim() * @access private */ function _normalize($result) @@ -3519,14 +3465,14 @@ function _normalize($result) $result->precision = $this->precision; $result->bitmask = $this->bitmask; - switch ( MATH_BIGINTEGER_MODE ) { - case MATH_BIGINTEGER_MODE_GMP: - if (!empty($result->bitmask->value)) { + switch (MATH_BIGINTEGER_MODE) { + case self::MODE_GMP: + if ($this->bitmask !== false) { $result->value = gmp_and($result->value, $result->bitmask->value); } return $result; - case MATH_BIGINTEGER_MODE_BCMATH: + case self::MODE_BCMATH: if (!empty($result->bitmask->value)) { $result->value = bcmod($result->value, $result->bitmask->value); } @@ -3536,7 +3482,7 @@ function _normalize($result) $value = &$result->value; - if ( !count($value) ) { + if (!count($value)) { return $result; } @@ -3559,14 +3505,14 @@ function _normalize($result) * * Removes leading zeros * - * @param Array $value - * @return BigInteger + * @param array $value + * @return \phpseclib\Math\BigInteger * @access private */ - function _trim($value) + static function _trim($value) { for ($i = count($value) - 1; $i >= 0; --$i) { - if ( $value[$i] ) { + if ($value[$i]) { break; } unset($value[$i]); @@ -3580,10 +3526,10 @@ function _trim($value) * * @param $input Array * @param $multiplier mixed - * @return Array + * @return array * @access private */ - function _array_repeat($input, $multiplier) + static function _array_repeat($input, $multiplier) { return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); } @@ -3595,10 +3541,10 @@ function _array_repeat($input, $multiplier) * * @param $x String * @param $shift Integer - * @return String + * @return string * @access private */ - function _base256_lshift(&$x, $shift) + static function _base256_lshift(&$x, $shift) { if ($shift == 0) { return; @@ -3624,10 +3570,10 @@ function _base256_lshift(&$x, $shift) * * @param $x String * @param $shift Integer - * @return String + * @return string * @access private */ - function _base256_rshift(&$x, $shift) + static function _base256_rshift(&$x, $shift) { if ($shift == 0) { $x = ltrim($x, chr(0)); @@ -3664,11 +3610,11 @@ function _base256_rshift(&$x, $shift) /** * Converts 32-bit integers to bytes. * - * @param Integer $x - * @return String + * @param int $x + * @return string * @access private */ - function _int2bytes($x) + static function _int2bytes($x) { return ltrim(pack('N', $x), chr(0)); } @@ -3676,11 +3622,11 @@ function _int2bytes($x) /** * Converts bytes to 32-bit integers * - * @param String $x - * @return Integer + * @param string $x + * @return int * @access private */ - function _bytes2int($x) + static function _bytes2int($x) { $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); return $temp['int']; @@ -3691,12 +3637,12 @@ function _bytes2int($x) * * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL * - * @see modPow() + * @see self::modPow() * @access private - * @param Integer $length - * @return String + * @param int $length + * @return string */ - function _encodeASN1Length($length) + static function _encodeASN1Length($length) { if ($length <= 0x7F) { return chr($length); @@ -3705,4 +3651,27 @@ function _encodeASN1Length($length) $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } + + /** + * Single digit division + * + * Even if int64 is being used the division operator will return a float64 value + * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't + * have the precision of int64 this is a problem so, when int64 is being used, + * we'll guarantee that the dividend is divisible by first subtracting the remainder. + * + * @access private + * @param int $x + * @param int $y + * @return int + */ + static function _safe_divide($x, $y) + { + if (self::$base === 26) { + return (int) ($x / $y); + } + + // self::$base === 31 + return ($x - ($x % $y)) / $y; + } } diff --git a/src/phpseclib/Net/SCP.php b/src/phpseclib/Net/SCP.php new file mode 100755 index 00000000..4c28d8b0 --- /dev/null +++ b/src/phpseclib/Net/SCP.php @@ -0,0 +1,338 @@ + + * login('username', 'password')) { + * exit('bad login'); + * } + * $scp = new \phpseclib\Net\SCP($ssh); + * + * $scp->put('abcd', str_repeat('x', 1024*1024)); + * ?> + * + * + * @category Net + * @package SCP + * @author Jim Wigginton + * @copyright 2010 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +use phpseclib\Exception\FileNotFoundException; + +/** + * Pure-PHP implementations of SCP. + * + * @package SCP + * @author Jim Wigginton + * @access public + */ +class SCP +{ + /**#@+ + * @access public + * @see \phpseclib\Net\SCP::put() + */ + /** + * Reads data from a local file. + */ + const SOURCE_LOCAL_FILE = 1; + /** + * Reads data from a string. + */ + const SOURCE_STRING = 2; + /**#@-*/ + + /**#@+ + * @access private + * @see \phpseclib\Net\SCP::_send() + * @see \phpseclib\Net\SCP::_receive() + */ + /** + * SSH1 is being used. + */ + const MODE_SSH1 = 1; + /** + * SSH2 is being used. + */ + const MODE_SSH2 = 2; + /**#@-*/ + + /** + * SSH Object + * + * @var object + * @access private + */ + var $ssh; + + /** + * Packet Size + * + * @var int + * @access private + */ + var $packet_size; + + /** + * Mode + * + * @var int + * @access private + */ + var $mode; + + /** + * Default Constructor. + * + * Connects to an SSH server + * + * @param string $host + * @param int $port + * @param int $timeout + * @return \phpseclib\Net\SCP + * @access public + */ + function __construct($ssh) + { + if ($ssh instanceof SSH2) { + $this->mode = self::MODE_SSH2; + } elseif ($ssh instanceof SSH1) { + $this->packet_size = 50000; + $this->mode = self::MODE_SSH1; + } else { + return; + } + + $this->ssh = $ssh; + } + + /** + * Uploads a file to the SCP server. + * + * By default, \phpseclib\Net\SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * @param string $remote_file + * @param string $data + * @param int $mode + * @param callable $callback + * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist + * @return bool + * @access public + */ + function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to + return false; + } + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + if ($this->mode == self::MODE_SSH2) { + $this->packet_size = $this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4; + } + + $remote_file = basename($remote_file); + + if ($mode == self::SOURCE_STRING) { + $size = strlen($data); + } else { + if (!is_file($data)) { + throw new FileNotFoundException("$data is not a valid file"); + } + + $fp = @fopen($data, 'rb'); + if (!$fp) { + return false; + } + $size = filesize($data); + } + + $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n"); + + $temp = $this->_receive(); + if ($temp !== chr(0)) { + return false; + } + + $sent = 0; + while ($sent < $size) { + $temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size); + $this->_send($temp); + $sent+= strlen($temp); + + if (is_callable($callback)) { + call_user_func($callback, $sent); + } + } + $this->_close(); + + if ($mode != self::SOURCE_STRING) { + fclose($fp); + } + + return true; + } + + /** + * Downloads a file from the SCP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation + * + * @param string $remote_file + * @param string $local_file + * @return mixed + * @access public + */ + function get($remote_file, $local_file = false) + { + if (!isset($this->ssh)) { + return false; + } + + if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { // -f = from + return false; + } + + $this->_send("\0"); + + if (!preg_match('#(?[^ ]+) (?\d+) (?.+)#', rtrim($this->_receive()), $info)) { + return false; + } + + $this->_send("\0"); + + $size = 0; + + if ($local_file !== false) { + $fp = @fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } + + $content = ''; + while ($size < $info['size']) { + $data = $this->_receive(); + // SCP usually seems to split stuff out into 16k chunks + $size+= strlen($data); + + if ($local_file === false) { + $content.= $data; + } else { + fputs($fp, $data); + } + } + + $this->_close(); + + if ($local_file !== false) { + fclose($fp); + return true; + } + + return $content; + } + + /** + * Sends a packet to an SSH server + * + * @param string $data + * @access private + */ + function _send($data) + { + switch ($this->mode) { + case self::MODE_SSH2: + $this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC, $data); + break; + case self::MODE_SSH1: + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data); + $this->ssh->_send_binary_packet($data); + } + } + + /** + * Receives a packet from an SSH server + * + * @return string + * @throws \UnexpectedValueException on receipt of an unexpected packet + * @access private + */ + function _receive() + { + switch ($this->mode) { + case self::MODE_SSH2: + return $this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true); + case self::MODE_SSH1: + if (!$this->ssh->bitmap) { + return false; + } + while (true) { + $response = $this->ssh->_get_binary_packet(); + switch ($response[SSH1::RESPONSE_TYPE]) { + case NET_SSH1_SMSG_STDOUT_DATA: + extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); + return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); + case NET_SSH1_SMSG_STDERR_DATA: + break; + case NET_SSH1_SMSG_EXITSTATUS: + $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION)); + fclose($this->ssh->fsock); + $this->ssh->bitmap = 0; + return false; + default: + throw new \UnexpectedValueException('Unknown packet received'); + } + } + } + } + + /** + * Closes the connection to an SSH server + * + * @access private + */ + function _close() + { + switch ($this->mode) { + case self::MODE_SSH2: + $this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true); + break; + case self::MODE_SSH1: + $this->ssh->disconnect(); + } + } +} diff --git a/src/phpseclib/Net/SFTP.php b/src/phpseclib/Net/SFTP.php new file mode 100755 index 00000000..6dc498fc --- /dev/null +++ b/src/phpseclib/Net/SFTP.php @@ -0,0 +1,2947 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $sftp->pwd() . "\r\n"; + * $sftp->put('filename.ext', 'hello, world!'); + * print_r($sftp->nlist()); + * ?> + * + * + * @category Net + * @package SFTP + * @author Jim Wigginton + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +use ParagonIE\ConstantTime\Hex; +use phpseclib\Exception\FileNotFoundException; + +/** + * Pure-PHP implementations of SFTP. + * + * @package SFTP + * @author Jim Wigginton + * @access public + */ +class SFTP extends SSH2 +{ + /** + * SFTP channel constant + * + * \phpseclib\Net\SSH2::exec() uses 0 and \phpseclib\Net\SSH2::read() / \phpseclib\Net\SSH2::write() use 1. + * + * @see \phpseclib\Net\SSH2::_send_channel_packet() + * @see \phpseclib\Net\SSH2::_get_channel_packet() + * @access private + */ + const CHANNEL = 0x100; + + /**#@+ + * @access public + * @see \phpseclib\Net\SFTP::put() + */ + /** + * Reads data from a local file. + */ + const SOURCE_LOCAL_FILE = 1; + /** + * Reads data from a string. + */ + // this value isn't really used anymore but i'm keeping it reserved for historical reasons + const SOURCE_STRING = 2; + /** + * Reads data from callback: + * function callback($length) returns string to proceed, null for EOF + */ + const SOURCE_CALLBACK = 16; + /** + * Resumes an upload + */ + const RESUME = 4; + /** + * Append a local file to an already existing remote file + */ + const RESUME_START = 8; + /**#@-*/ + + /** + * Packet Types + * + * @see self::__construct() + * @var array + * @access private + */ + var $packet_types = array(); + + /** + * Status Codes + * + * @see self::__construct() + * @var array + * @access private + */ + var $status_codes = array(); + + /** + * The Request ID + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var int + * @see self::_send_sftp_packet() + * @access private + */ + var $request_id = false; + + /** + * The Packet Type + * + * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support + * concurrent actions, so it's somewhat academic, here. + * + * @var int + * @see self::_get_sftp_packet() + * @access private + */ + var $packet_type = -1; + + /** + * Packet Buffer + * + * @var string + * @see self::_get_sftp_packet() + * @access private + */ + var $packet_buffer = ''; + + /** + * Extensions supported by the server + * + * @var array + * @see self::_initChannel() + * @access private + */ + var $extensions = array(); + + /** + * Server SFTP version + * + * @var int + * @see self::_initChannel() + * @access private + */ + var $version; + + /** + * Current working directory + * + * @var string + * @see self::_realpath() + * @see self::chdir() + * @access private + */ + var $pwd = false; + + /** + * Packet Type Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $packet_type_log = array(); + + /** + * Packet Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $packet_log = array(); + + /** + * Error information + * + * @see self::getSFTPErrors() + * @see self::getLastSFTPError() + * @var string + * @access private + */ + var $sftp_errors = array(); + + /** + * Stat Cache + * + * Rather than always having to open a directory and close it immediately there after to see if a file is a directory + * we'll cache the results. + * + * @see self::_update_stat_cache() + * @see self::_remove_from_stat_cache() + * @see self::_query_stat_cache() + * @var array + * @access private + */ + var $stat_cache = array(); + + /** + * Max SFTP Packet Size + * + * @see self::__construct() + * @see self::get() + * @var array + * @access private + */ + var $max_sftp_packet; + + /** + * Stat Cache Flag + * + * @see self::disableStatCache() + * @see self::enableStatCache() + * @var bool + * @access private + */ + var $use_stat_cache = true; + + /** + * Sort Options + * + * @see self::_comparator() + * @see self::setListOrder() + * @var array + * @access private + */ + var $sortOptions = array(); + + /** + * Default Constructor. + * + * Connects to an SFTP server + * + * @param string $host + * @param int $port + * @param int $timeout + * @return \phpseclib\Net\SFTP + * @access public + */ + function __construct($host, $port = 22, $timeout = 10) + { + parent::__construct($host, $port, $timeout); + + $this->max_sftp_packet = 1 << 15; + + $this->packet_types = array( + 1 => 'NET_SFTP_INIT', + 2 => 'NET_SFTP_VERSION', + /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+: + SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1 + pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */ + 3 => 'NET_SFTP_OPEN', + 4 => 'NET_SFTP_CLOSE', + 5 => 'NET_SFTP_READ', + 6 => 'NET_SFTP_WRITE', + 7 => 'NET_SFTP_LSTAT', + 9 => 'NET_SFTP_SETSTAT', + 11 => 'NET_SFTP_OPENDIR', + 12 => 'NET_SFTP_READDIR', + 13 => 'NET_SFTP_REMOVE', + 14 => 'NET_SFTP_MKDIR', + 15 => 'NET_SFTP_RMDIR', + 16 => 'NET_SFTP_REALPATH', + 17 => 'NET_SFTP_STAT', + /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+: + SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */ + 18 => 'NET_SFTP_RENAME', + 19 => 'NET_SFTP_READLINK', + 20 => 'NET_SFTP_SYMLINK', + + 101=> 'NET_SFTP_STATUS', + 102=> 'NET_SFTP_HANDLE', + /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+: + SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4 + pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */ + 103=> 'NET_SFTP_DATA', + 104=> 'NET_SFTP_NAME', + 105=> 'NET_SFTP_ATTRS', + + 200=> 'NET_SFTP_EXTENDED' + ); + $this->status_codes = array( + 0 => 'NET_SFTP_STATUS_OK', + 1 => 'NET_SFTP_STATUS_EOF', + 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', + 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', + 4 => 'NET_SFTP_STATUS_FAILURE', + 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', + 6 => 'NET_SFTP_STATUS_NO_CONNECTION', + 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', + 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', + 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', + 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', + 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', + 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', + 13 => 'NET_SFTP_STATUS_NO_MEDIA', + 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', + 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', + 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', + 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', + 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', + 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', + 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', + 21 => 'NET_SFTP_STATUS_LINK_LOOP', + 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', + 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', + 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', + 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', + 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', + 27 => 'NET_SFTP_STATUS_DELETE_PENDING', + 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', + 29 => 'NET_SFTP_STATUS_OWNER_INVALID', + 30 => 'NET_SFTP_STATUS_GROUP_INVALID', + 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 + // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why + $this->attributes = array( + 0x00000001 => 'NET_SFTP_ATTR_SIZE', + 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ + 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', + 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', + // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers + // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in + // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. + // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. + -1 << 31 => 'NET_SFTP_ATTR_EXTENDED' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 + // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name + // the array for that $this->open5_flags and similarily alter the constant names. + $this->open_flags = array( + 0x00000001 => 'NET_SFTP_OPEN_READ', + 0x00000002 => 'NET_SFTP_OPEN_WRITE', + 0x00000004 => 'NET_SFTP_OPEN_APPEND', + 0x00000008 => 'NET_SFTP_OPEN_CREATE', + 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', + 0x00000020 => 'NET_SFTP_OPEN_EXCL' + ); + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 + // see \phpseclib\Net\SFTP::_parseLongname() for an explanation + $this->file_types = array( + 1 => 'NET_SFTP_TYPE_REGULAR', + 2 => 'NET_SFTP_TYPE_DIRECTORY', + 3 => 'NET_SFTP_TYPE_SYMLINK', + 4 => 'NET_SFTP_TYPE_SPECIAL', + 5 => 'NET_SFTP_TYPE_UNKNOWN', + // the followin types were first defined for use in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + 6 => 'NET_SFTP_TYPE_SOCKET', + 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', + 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', + 9 => 'NET_SFTP_TYPE_FIFO' + ); + $this->_define_array( + $this->packet_types, + $this->status_codes, + $this->attributes, + $this->open_flags, + $this->file_types + ); + + if (!defined('NET_SFTP_QUEUE_SIZE')) { + define('NET_SFTP_QUEUE_SIZE', 50); + } + } + + /** + * Login + * + * @param string $username + * @param string $password + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + * @access public + */ + function login($username) + { + $args = func_get_args(); + if (!call_user_func_array(array(&$this, '_login'), $args)) { + return false; + } + + $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL, + $this->window_size, + 0x4000 + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL); + if ($response === false) { + return false; + } + + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL], + strlen('subsystem'), + 'subsystem', + 1, + strlen('sftp'), + 'sftp' + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL); + if ($response === false) { + // from PuTTY's psftp.exe + $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . + "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . + "exec sftp-server"; + // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does + // is redundant + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL], + strlen('exec'), + 'exec', + 1, + strlen($command), + $command + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL); + if ($response === false) { + return false; + } + } + + $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; + + if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_VERSION) { + throw new \UnexpectedValueException('Expected SSH_FXP_VERSION'); + } + + extract(unpack('Nversion', $this->_string_shift($response, 4))); + $this->version = $version; + while (!empty($response)) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $key = $this->_string_shift($response, $length); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + $this->extensions[$key] = $value; + } + + /* + SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', + however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's + not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for + one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that + 'newline@vandyke.com' would. + */ + /* + if (isset($this->extensions['newline@vandyke.com'])) { + $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; + unset($this->extensions['newline@vandyke.com']); + } + */ + + $this->request_id = 1; + + /* + A Note on SFTPv4/5/6 support: + states the following: + + "If the client wishes to interoperate with servers that support noncontiguous version + numbers it SHOULD send '3'" + + Given that the server only sends its version number after the client has already done so, the above + seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the + most popular. + + states the following; + + "If the server did not send the "versions" extension, or the version-from-list was not included, the + server MAY send a status response describing the failure, but MUST then close the channel without + processing any further requests." + + So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and + a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements + v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed + in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the + channel and reopen it with a new and updated SSH_FXP_INIT packet. + */ + switch ($this->version) { + case 2: + case 3: + break; + default: + return false; + } + + $this->pwd = $this->_realpath('.'); + + $this->_update_stat_cache($this->pwd, array()); + + return true; + } + + /** + * Disable the stat cache + * + * @access public + */ + function disableStatCache() + { + $this->use_stat_cache = false; + } + + /** + * Enable the stat cache + * + * @access public + */ + function enableStatCache() + { + $this->use_stat_cache = true; + } + + /** + * Clear the stat cache + * + * @access public + */ + function clearStatCache() + { + $this->stat_cache = array(); + } + + /** + * Returns the current directory name + * + * @return mixed + * @access public + */ + function pwd() + { + return $this->pwd; + } + + /** + * Logs errors + * + * @param string $response + * @param int $status + * @access public + */ + function _logError($response, $status = -1) + { + if ($status == -1) { + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + } + + $error = $this->status_codes[$status]; + + if ($this->version > 2) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); + } else { + $this->sftp_errors[] = $error; + } + } + + /** + * Canonicalize the Server-Side Path Name + * + * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns + * the absolute (canonicalized) path. + * + * @see self::chdir() + * @param string $path + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + * @access private + */ + function _realpath($path) + { + if ($this->pwd === false) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 + if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following + // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks + // at is the first part and that part is defined the same in SFTP versions 3 through 6. + $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway + extract(unpack('Nlength', $this->_string_shift($response, 4))); + return $this->_string_shift($response, $length); + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + } + } + + if ($path[0] != '/') { + $path = $this->pwd . '/' . $path; + } + + $path = explode('/', $path); + $new = array(); + foreach ($path as $dir) { + if (!strlen($dir)) { + continue; + } + switch ($dir) { + case '..': + array_pop($new); + case '.': + break; + default: + $new[] = $dir; + } + } + + return '/' . implode('/', $new); + } + + /** + * Changes the current directory + * + * @param string $dir + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + * @access public + */ + function chdir($dir) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + // assume current dir if $dir is empty + if ($dir === '') { + $dir = './'; + // suffix a slash if needed + } elseif ($dir[strlen($dir) - 1] != '/') { + $dir.= '/'; + } + + $dir = $this->_realpath($dir); + + // confirm that $dir is, in fact, a valid directory + if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) { + $this->pwd = $dir; + return true; + } + + // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us + // the currently logged in user has the appropriate permissions or not. maybe you could see if + // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy + // way to get those with SFTP + + if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + // see \phpseclib\Net\SFTP::nlist() for a more thorough explanation of the following + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + } + + if (!$this->_close_handle($handle)) { + return false; + } + + $this->_update_stat_cache($dir, array()); + + $this->pwd = $dir; + return true; + } + + /** + * Returns a list of files in the given directory + * + * @param string $dir + * @param bool $recursive + * @return mixed + * @access public + */ + function nlist($dir = '.', $recursive = false) + { + return $this->_nlist_helper($dir, $recursive, ''); + } + + /** + * Helper method for nlist + * + * @param string $dir + * @param bool $recursive + * @param string $relativeDir + * @return mixed + * @access private + */ + function _nlist_helper($dir, $recursive, $relativeDir) + { + $files = $this->_list($dir, false); + + if (!$recursive || $files === false) { + return $files; + } + + $result = array(); + foreach ($files as $value) { + if ($value == '.' || $value == '..') { + if ($relativeDir == '') { + $result[] = $value; + } + continue; + } + if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { + $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); + $result = array_merge($result, $temp); + } else { + $result[] = $relativeDir . $value; + } + } + + return $result; + } + + /** + * Returns a detailed list of files in the given directory + * + * @param string $dir + * @param bool $recursive + * @return mixed + * @access public + */ + function rawlist($dir = '.', $recursive = false) + { + $files = $this->_list($dir, true); + if (!$recursive || $files === false) { + return $files; + } + + static $depth = 0; + + foreach ($files as $key => $value) { + if ($depth != 0 && $key == '..') { + unset($files[$key]); + continue; + } + if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) { + $depth++; + $files[$key] = $this->rawlist($dir . '/' . $key, true); + $depth--; + } else { + $files[$key] = (object) $value; + } + } + + return $files; + } + + /** + * Reads a list, be it detailed or not, of files in the given directory + * + * @param string $dir + * @param bool $raw + * @return mixed + * @throws \UnexpectedValueException on receipt of unexpected packets + * @access private + */ + function _list($dir, $raw = true) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $dir = $this->_realpath($dir . '/'); + if ($dir === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2 + if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2 + // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that + // represent the length of the string and leave it at that + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + $this->_logError($response); + return false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + } + + $this->_update_stat_cache($dir, array()); + + $contents = array(); + while (true) { + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2 + // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many + // SSH_MSG_CHANNEL_DATA messages is not known to me. + if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $shortname = $this->_string_shift($response, $length); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $longname = $this->_string_shift($response, $length); + $attributes = $this->_parseAttributes($response); + if (!isset($attributes['type'])) { + $fileType = $this->_parseLongname($longname); + if ($fileType) { + $attributes['type'] = $fileType; + } + } + $contents[$shortname] = $attributes + array('filename' => $shortname); + + if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { + $this->_update_stat_cache($dir . '/' . $shortname, array()); + } else { + if ($shortname == '..') { + $temp = $this->_realpath($dir . '/..') . '/.'; + } else { + $temp = $dir . '/' . $shortname; + } + $this->_update_stat_cache($temp, (object) array('lstat' => $attributes)); + } + // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the + // final SSH_FXP_STATUS packet should tell us that, already. + } + break; + case NET_SFTP_STATUS: + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_EOF) { + $this->_logError($response, $status); + return false; + } + break 2; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + } + } + + if (!$this->_close_handle($handle)) { + return false; + } + + if (count($this->sortOptions)) { + uasort($contents, array(&$this, '_comparator')); + } + + return $raw ? $contents : array_keys($contents); + } + + /** + * Compares two rawlist entries using parameters set by setListOrder() + * + * Intended for use with uasort() + * + * @param array $a + * @param array $b + * @return int + * @access private + */ + function _comparator($a, $b) + { + switch (true) { + case $a['filename'] === '.' || $b['filename'] === '.': + if ($a['filename'] === $b['filename']) { + return 0; + } + return $a['filename'] === '.' ? -1 : 1; + case $a['filename'] === '..' || $b['filename'] === '..': + if ($a['filename'] === $b['filename']) { + return 0; + } + return $a['filename'] === '..' ? -1 : 1; + case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY: + if (!isset($b['type'])) { + return 1; + } + if ($b['type'] !== $a['type']) { + return -1; + } + break; + case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY: + return 1; + } + foreach ($this->sortOptions as $sort => $order) { + if (!isset($a[$sort]) || !isset($b[$sort])) { + if (isset($a[$sort])) { + return -1; + } + if (isset($b[$sort])) { + return 1; + } + return 0; + } + switch ($sort) { + case 'filename': + $result = strcasecmp($a['filename'], $b['filename']); + if ($result) { + return $order === SORT_DESC ? -$result : $result; + } + break; + case 'permissions': + case 'mode': + $a[$sort]&= 07777; + $b[$sort]&= 07777; + default: + if ($a[$sort] === $b[$sort]) { + break; + } + return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort]; + } + } + } + + /** + * Defines how nlist() and rawlist() will be sorted - if at all. + * + * If sorting is enabled directories and files will be sorted independently with + * directories appearing before files in the resultant array that is returned. + * + * Any parameter returned by stat is a valid sort parameter for this function. + * Filename comparisons are case insensitive. + * + * Examples: + * + * $sftp->setListOrder('filename', SORT_ASC); + * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC); + * $sftp->setListOrder(true); + * Separates directories from files but doesn't do any sorting beyond that + * $sftp->setListOrder(); + * Don't do any sort of sorting + * + * @access public + */ + function setListOrder() + { + $this->sortOptions = array(); + $args = func_get_args(); + if (empty($args)) { + return; + } + $len = count($args) & 0x7FFFFFFE; + for ($i = 0; $i < $len; $i+=2) { + $this->sortOptions[$args[$i]] = $args[$i + 1]; + } + if (!count($this->sortOptions)) { + $this->sortOptions = array('bogus' => true); + } + } + + /** + * Returns the file size, in bytes, or false, on failure + * + * Files larger than 4GB will show up as being exactly 4GB. + * + * @param string $filename + * @return mixed + * @access public + */ + function size($filename) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $result = $this->stat($filename); + if ($result === false) { + return false; + } + return isset($result['size']) ? $result['size'] : -1; + } + + /** + * Save files / directories to cache + * + * @param string $path + * @param mixed $value + * @access private + */ + function _update_stat_cache($path, $value) + { + if ($this->use_stat_cache === false) { + return; + } + + // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/')) + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); + + $temp = &$this->stat_cache; + $max = count($dirs) - 1; + foreach ($dirs as $i => $dir) { + // if $temp is an object that means one of two things. + // 1. a file was deleted and changed to a directory behind phpseclib's back + // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to + if (is_object($temp)) { + $temp = array(); + } + if (!isset($temp[$dir])) { + $temp[$dir] = array(); + } + if ($i === $max) { + if (is_object($temp[$dir])) { + if (!isset($value->stat) && isset($temp[$dir]->stat)) { + $value->stat = $temp[$dir]->stat; + } + if (!isset($value->lstat) && isset($temp[$dir]->lstat)) { + $value->lstat = $temp[$dir]->lstat; + } + } + $temp[$dir] = $value; + break; + } + $temp = &$temp[$dir]; + } + } + + /** + * Remove files / directories from cache + * + * @param string $path + * @return bool + * @access private + */ + function _remove_from_stat_cache($path) + { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); + + $temp = &$this->stat_cache; + $max = count($dirs) - 1; + foreach ($dirs as $i => $dir) { + if ($i === $max) { + unset($temp[$dir]); + return true; + } + if (!isset($temp[$dir])) { + return false; + } + $temp = &$temp[$dir]; + } + } + + /** + * Checks cache for path + * + * Mainly used by file_exists + * + * @param string $dir + * @return mixed + * @access private + */ + function _query_stat_cache($path) + { + $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); + + $temp = &$this->stat_cache; + foreach ($dirs as $dir) { + if (!isset($temp[$dir])) { + return null; + } + $temp = &$temp[$dir]; + } + return $temp; + } + + /** + * Returns general information about a file. + * + * Returns an array on success and false otherwise. + * + * @param string $filename + * @return mixed + * @access public + */ + function stat($filename) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($this->use_stat_cache) { + $result = $this->_query_stat_cache($filename); + if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) { + return $result['.']->stat; + } + if (is_object($result) && isset($result->stat)) { + return $result->stat; + } + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); + if ($stat === false) { + $this->_remove_from_stat_cache($filename); + return false; + } + if (isset($stat['type'])) { + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('stat' => $stat)); + return $stat; + } + + $pwd = $this->pwd; + $stat['type'] = $this->chdir($filename) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('stat' => $stat)); + + return $stat; + } + + /** + * Returns general information about a file or symbolic link. + * + * Returns an array on success and false otherwise. + * + * @param string $filename + * @return mixed + * @access public + */ + function lstat($filename) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if ($this->use_stat_cache) { + $result = $this->_query_stat_cache($filename); + if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) { + return $result['.']->lstat; + } + if (is_object($result) && isset($result->lstat)) { + return $result->lstat; + } + } + + $lstat = $this->_stat($filename, NET_SFTP_LSTAT); + if ($lstat === false) { + $this->_remove_from_stat_cache($filename); + return false; + } + if (isset($lstat['type'])) { + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); + return $lstat; + } + + $stat = $this->_stat($filename, NET_SFTP_STAT); + + if ($lstat != $stat) { + $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); + $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); + return $stat; + } + + $pwd = $this->pwd; + $lstat['type'] = $this->chdir($filename) ? + NET_SFTP_TYPE_DIRECTORY : + NET_SFTP_TYPE_REGULAR; + $this->pwd = $pwd; + + if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { + $filename.= '/.'; + } + $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); + + return $lstat; + } + + /** + * Returns general information about a file or symbolic link + * + * Determines information without calling \phpseclib\Net\SFTP::_realpath(). + * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. + * + * @param string $filename + * @param int $type + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + * @access private + */ + function _stat($filename, $type) + { + // SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = pack('Na*', strlen($filename), $filename); + if (!$this->_send_sftp_packet($type, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + return $this->_parseAttributes($response); + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + } + + throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); + } + + /** + * Truncates a file to a given length + * + * @param string $filename + * @param int $new_size + * @return bool + * @access public + */ + function truncate($filename, $new_size) + { + $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32 + + return $this->_setstat($filename, $attr, false); + } + + /** + * Sets access and modification time of file. + * + * If the file does not exist, it will be created. + * + * @param string $filename + * @param int $time + * @param int $atime + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + * @access public + */ + function touch($filename, $time = null, $atime = null) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + if (!isset($time)) { + $time = time(); + } + if (!isset($atime)) { + $atime = $time; + } + + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL; + $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime); + $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return $this->_close_handle(substr($response, 4)); + case NET_SFTP_STATUS: + $this->_logError($response); + break; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + } + + return $this->_setstat($filename, $attr, false); + } + + /** + * Changes file or directory owner + * + * Returns true on success or false on error. + * + * @param string $filename + * @param int $uid + * @param bool $recursive + * @return bool + * @access public + */ + function chown($filename, $uid, $recursive = false) + { + // quoting from , + // "if the owner or group is specified as -1, then that ID is not changed" + $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Changes file or directory group + * + * Returns true on success or false on error. + * + * @param string $filename + * @param int $gid + * @param bool $recursive + * @return bool + * @access public + */ + function chgrp($filename, $gid, $recursive = false) + { + $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid); + + return $this->_setstat($filename, $attr, $recursive); + } + + /** + * Set permissions on a file. + * + * Returns the new file permissions on success or false on error. + * If $recursive is true than this just returns true or false. + * + * @param int $mode + * @param string $filename + * @param bool $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + * @access public + */ + function chmod($mode, $filename, $recursive = false) + { + if (is_string($mode) && is_int($filename)) { + $temp = $mode; + $mode = $filename; + $filename = $temp; + } + + $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + if (!$this->_setstat($filename, $attr, $recursive)) { + return false; + } + if ($recursive) { + return true; + } + + $filename = $this->_realPath($filename); + // rather than return what the permissions *should* be, we'll return what they actually are. this will also + // tell us if the file actually exists. + // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: + $packet = pack('Na*', strlen($filename), $filename); + if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_ATTRS: + $attrs = $this->_parseAttributes($response); + return $attrs['permissions']; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + } + + throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); + } + + /** + * Sets information about a file + * + * @param string $filename + * @param string $attr + * @param bool $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + * @access private + */ + function _setstat($filename, $attr, $recursive) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $filename = $this->_realpath($filename); + if ($filename === false) { + return false; + } + + $this->_remove_from_stat_cache($filename); + + if ($recursive) { + $i = 0; + $result = $this->_setstat_recursive($filename, $attr, $i); + $this->_read_put_responses($i); + return $result; + } + + // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to + // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT. + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { + return false; + } + + /* + "Because some systems must use separate system calls to set various attributes, it is possible that a failure + response will be returned, but yet some of the attributes may be have been successfully modified. If possible, + servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." + + -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 + */ + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Recursively sets information on directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param string $path + * @param string $attr + * @param int $i + * @return bool + * @access private + */ + function _setstat_recursive($path, $attr, &$i) + { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + $entries = $this->_list($path, true); + + if ($entries === false) { + return $this->_setstat($path, $attr, false); + } + + // normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading + if (empty($entries)) { + return false; + } + + unset($entries['.'], $entries['..']); + foreach ($entries as $filename => $props) { + if (!isset($props['type'])) { + return false; + } + + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->_setstat_recursive($temp, $attr, $i)) { + return false; + } + } else { + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) { + return false; + } + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + } + } + + if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) { + return false; + } + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + + return true; + } + + /** + * Return the target of a symbolic link + * + * @param string $link + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + * @access public + */ + function readlink($link) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $link = $this->_realpath($link); + + if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_NAME: + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); + } + + extract(unpack('Ncount', $this->_string_shift($response, 4))); + // the file isn't a symlink + if (!$count) { + return false; + } + + extract(unpack('Nlength', $this->_string_shift($response, 4))); + return $this->_string_shift($response, $length); + } + + /** + * Create a symlink + * + * symlink() creates a symbolic link to the existing target with the specified name link. + * + * @param string $target + * @param string $link + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + * @access public + */ + function symlink($target, $link) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $target = $this->_realpath($target); + $link = $this->_realpath($link); + + $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link); + if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Creates a directory. + * + * @param string $dir + * @return bool + * @access public + */ + function mkdir($dir, $mode = -1, $recursive = false) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $dir = $this->_realpath($dir); + // by not providing any permissions, hopefully the server will use the logged in users umask - their + // default permissions. + $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); + + if ($recursive) { + $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); + if (empty($dirs[0])) { + array_shift($dirs); + $dirs[0] = '/' . $dirs[0]; + } + for ($i = 0; $i < count($dirs); $i++) { + $temp = array_slice($dirs, 0, $i + 1); + $temp = implode('/', $temp); + $result = $this->_mkdir_helper($temp, $attr); + } + return $result; + } + + return $this->_mkdir_helper($dir, $attr); + } + + /** + * Helper function for directory creation + * + * @param string $dir + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @access private + */ + function _mkdir_helper($dir, $attr) + { + if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Removes a directory. + * + * @param string $dir + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return bool + * @access public + */ + function rmdir($dir) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $dir = $this->_realpath($dir); + if ($dir === false) { + return false; + } + + if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? + $this->_logError($response, $status); + return false; + } + + $this->_remove_from_stat_cache($dir); + // the following will do a soft delete, which would be useful if you deleted a file + // and then tried to do a stat on the deleted file. the above, in contrast, does + // a hard delete + //$this->_update_stat_cache($dir, false); + + return true; + } + + /** + * Uploads a file to the SFTP server. + * + * By default, \phpseclib\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. + * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SFTP::get(), you will get a file, twelve bytes + * long, containing 'filename.ext' as its contents. + * + * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will + * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how + * large $remote_file will be, as well. + * + * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number of bytes to return, and returns a string if there is some data or null if there is no more data + * + * If $data is a resource then it'll be used as a resource instead. + * + * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take + * care of that, yourself. + * + * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with + * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: + * + * self::SOURCE_LOCAL_FILE | self::RESUME + * + * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace + * self::RESUME with self::RESUME_START. + * + * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed. + * + * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME + * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle + * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the + * middle of one. + * + * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE. + * + * @param string $remote_file + * @param string|resource $data + * @param int $mode + * @param int $start + * @param int $local_start + * @param callable|null $progressCallback + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid + * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist + * @return bool + * @access public + * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode(). + */ + function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $remote_file = $this->_realpath($remote_file); + if ($remote_file === false) { + return false; + } + + $this->_remove_from_stat_cache($remote_file); + + $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; + // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." + // in practice, it doesn't seem to do that. + //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; + + if ($start >= 0) { + $offset = $start; + } elseif ($mode & self::RESUME) { + // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called + $size = $this->size($remote_file); + $offset = $size !== false ? $size : 0; + } else { + $offset = 0; + $flags|= NET_SFTP_OPEN_TRUNCATE; + } + + $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: + $this->_logError($response); + return false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 + $dataCallback = false; + switch (true) { + case $mode & self::SOURCE_CALLBACK: + if (!is_callable($data)) { + throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); + } + $dataCallback = $data; + // do nothing + break; + case is_resource($data): + $mode = $mode & ~self::SOURCE_LOCAL_FILE; + $fp = $data; + break; + case $mode & self::SOURCE_LOCAL_FILE: + if (!is_file($data)) { + throw new FileNotFoundException("$data is not a valid file"); + } + $fp = @fopen($data, 'rb'); + if (!$fp) { + return false; + } + } + + if (isset($fp)) { + $stat = fstat($fp); + $size = $stat['size']; + + if ($local_start >= 0) { + fseek($fp, $local_start); + $size-= $local_start; + } + } elseif ($dataCallback) { + $size = 0; + } else { + $size = strlen($data); + } + + $sent = 0; + $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; + + $sftp_packet_size = 4096; // PuTTY uses 4096 + // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size-= strlen($handle) + 25; + $i = 0; + while ($dataCallback || ($size === 0 || $sent < $size)) { + if ($dataCallback) { + $temp = call_user_func($dataCallback, $sftp_packet_size); + if (is_null($temp)) { + break; + } + } else { + $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); + if ($temp === false) { + break; + } + } + + $subtemp = $offset + $sent; + $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); + if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { + if ($mode & self::SOURCE_LOCAL_FILE) { + fclose($fp); + } + return false; + } + $sent+= strlen($temp); + if (is_callable($progressCallback)) { + call_user_func($progressCallback, $sent); + } + + $i++; + + if ($i == NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + $i = 0; + break; + } + $i = 0; + } + } + + if (!$this->_read_put_responses($i)) { + if ($mode & self::SOURCE_LOCAL_FILE) { + fclose($fp); + } + $this->_close_handle($handle); + return false; + } + + if ($mode & self::SOURCE_LOCAL_FILE) { + fclose($fp); + } + + return $this->_close_handle($handle); + } + + /** + * Reads multiple successive SSH_FXP_WRITE responses + * + * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i + * SSH_FXP_WRITEs, in succession, and then reading $i responses. + * + * @param int $i + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @access private + */ + function _read_put_responses($i) + { + while ($i--) { + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + break; + } + } + + return $i < 0; + } + + /** + * Close handle + * + * @param string $handle + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @access private + */ + function _close_handle($handle) + { + if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { + return false; + } + + // "The client MUST release all resources associated with the handle regardless of the status." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + return true; + } + + /** + * Downloads a file from the SFTP server. + * + * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if + * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the + * operation. + * + * $offset and $length can be used to download files in chunks. + * + * @param string $remote_file + * @param string $local_file + * @param int $offset + * @param int $length + * @throws \UnexpectedValueException on receipt of unexpected packets + * @return mixed + * @access public + */ + function get($remote_file, $local_file = false, $offset = 0, $length = -1) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $remote_file = $this->_realpath($remote_file); + if ($remote_file === false) { + return false; + } + + $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + $handle = substr($response, 4); + break; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + $this->_logError($response); + return false; + default: + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + } + + if (is_resource($local_file)) { + $fp = $local_file; + $stat = fstat($fp); + $res_offset = $stat['size']; + } else { + $res_offset = 0; + if ($local_file !== false) { + $fp = fopen($local_file, 'wb'); + if (!$fp) { + return false; + } + } else { + $content = ''; + } + } + + $fclose_check = $local_file !== false && !is_resource($local_file); + + $start = $offset; + $read = 0; + while (true) { + $i = 0; + + while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) { + $tempoffset = $start + $read; + + $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; + + $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); + if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { + if ($fclose_check) { + fclose($fp); + } + return false; + } + $packet = null; + $read+= $packet_size; + $i++; + } + + if (!$i) { + break; + } + + $clear_responses = false; + while ($i > 0) { + $i--; + + if ($clear_responses) { + $this->_get_sftp_packet(); + continue; + } else { + $response = $this->_get_sftp_packet(); + } + + switch ($this->packet_type) { + case NET_SFTP_DATA: + $temp = substr($response, 4); + $offset+= strlen($temp); + if ($local_file === false) { + $content.= $temp; + } else { + fputs($fp, $temp); + } + $temp = null; + break; + case NET_SFTP_STATUS: + // could, in theory, return false if !strlen($content) but we'll hold off for the time being + $this->_logError($response); + $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses + break; + default: + if ($fclose_check) { + fclose($fp); + } + throw new \UnexpectedValueException('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); + } + $response = null; + } + + if ($clear_responses) { + break; + } + } + + if ($length > 0 && $length <= $offset - $start) { + if ($local_file === false) { + $content = substr($content, 0, $length); + } else { + ftruncate($fp, $length + $res_offset); + } + } + + if ($fclose_check) { + fclose($fp); + } + + if (!$this->_close_handle($handle)) { + return false; + } + + // if $content isn't set that means a file was written to + return isset($content) ? $content : true; + } + + /** + * Deletes a file on the SFTP server. + * + * @param string $path + * @param bool $recursive + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @access public + */ + function delete($path, $recursive = true) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $path = $this->_realpath($path); + if ($path === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + if (!$recursive) { + return false; + } + $i = 0; + $result = $this->_delete_recursive($path, $i); + $this->_read_put_responses($i); + return $result; + } + + $this->_remove_from_stat_cache($path); + + return true; + } + + /** + * Recursively deletes directories on the SFTP server + * + * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. + * + * @param string $path + * @param int $i + * @return bool + * @access private + */ + function _delete_recursive($path, &$i) + { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + $entries = $this->_list($path, true); + + // normally $entries would have at least . and .. but it might not if the directories + // permissions didn't allow reading + if (empty($entries)) { + return false; + } + + unset($entries['.'], $entries['..']); + foreach ($entries as $filename => $props) { + if (!isset($props['type'])) { + return false; + } + + $temp = $path . '/' . $filename; + if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { + if (!$this->_delete_recursive($temp, $i)) { + return false; + } + } else { + if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) { + return false; + } + $this->_remove_from_stat_cache($temp); + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + } + } + + if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) { + return false; + } + $this->_remove_from_stat_cache($path); + + $i++; + + if ($i >= NET_SFTP_QUEUE_SIZE) { + if (!$this->_read_put_responses($i)) { + return false; + } + $i = 0; + } + + return true; + } + + /** + * Checks whether a file or directory exists + * + * @param string $path + * @return bool + * @access public + */ + function file_exists($path) + { + if ($this->use_stat_cache) { + $path = $this->_realpath($path); + + $result = $this->_query_stat_cache($path); + + if (isset($result)) { + // return true if $result is an array or if it's an stdClass object + return $result !== false; + } + } + + return $this->stat($path) !== false; + } + + /** + * Tells whether the filename is a directory + * + * @param string $path + * @return bool + * @access public + */ + function is_dir($path) + { + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; + } + return $result === NET_SFTP_TYPE_DIRECTORY; + } + + /** + * Tells whether the filename is a regular file + * + * @param string $path + * @return bool + * @access public + */ + function is_file($path) + { + $result = $this->_get_stat_cache_prop($path, 'type'); + if ($result === false) { + return false; + } + return $result === NET_SFTP_TYPE_REGULAR; + } + + /** + * Tells whether the filename is a symbolic link + * + * @param string $path + * @return bool + * @access public + */ + function is_link($path) + { + $result = $this->_get_lstat_cache_prop($path, 'type'); + if ($result === false) { + return false; + } + return $result === NET_SFTP_TYPE_SYMLINK; + } + + /** + * Tells whether a file exists and is readable + * + * @param string $path + * @return bool + * @access public + */ + function is_readable($path) + { + $path = $this->_realpath($path); + + $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return true; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + } + + /** + * Tells whether the filename is writable + * + * @param string $path + * @return bool + * @access public + */ + function is_writable($path) + { + $path = $this->_realpath($path); + + $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0); + if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + switch ($this->packet_type) { + case NET_SFTP_HANDLE: + return true; + case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + return false; + default: + user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); + return false; + } + } + + /** + * Tells whether the filename is writeable + * + * Alias of is_writable + * + * @param string $path + * @return bool + * @access public + */ + function is_writeable($path) + { + return $this->is_writable($path); + } + + /** + * Gets last access time of file + * + * @param string $path + * @return mixed + * @access public + */ + function fileatime($path) + { + return $this->_get_stat_cache_prop($path, 'atime'); + } + + /** + * Gets file modification time + * + * @param string $path + * @return mixed + * @access public + */ + function filemtime($path) + { + return $this->_get_stat_cache_prop($path, 'mtime'); + } + + /** + * Gets file permissions + * + * @param string $path + * @return mixed + * @access public + */ + function fileperms($path) + { + return $this->_get_stat_cache_prop($path, 'permissions'); + } + + /** + * Gets file owner + * + * @param string $path + * @return mixed + * @access public + */ + function fileowner($path) + { + return $this->_get_stat_cache_prop($path, 'uid'); + } + + /** + * Gets file group + * + * @param string $path + * @return mixed + * @access public + */ + function filegroup($path) + { + return $this->_get_stat_cache_prop($path, 'gid'); + } + + /** + * Gets file size + * + * @param string $path + * @return mixed + * @access public + */ + function filesize($path) + { + return $this->_get_stat_cache_prop($path, 'size'); + } + + /** + * Gets file type + * + * @param string $path + * @return mixed + * @access public + */ + function filetype($path) + { + $type = $this->_get_stat_cache_prop($path, 'type'); + if ($type === false) { + return false; + } + + switch ($type) { + case NET_SFTP_TYPE_BLOCK_DEVICE: + return 'block'; + case NET_SFTP_TYPE_CHAR_DEVICE: + return 'char'; + case NET_SFTP_TYPE_DIRECTORY: + return 'dir'; + case NET_SFTP_TYPE_FIFO: + return 'fifo'; + case NET_SFTP_TYPE_REGULAR: + return 'file'; + case NET_SFTP_TYPE_SYMLINK: + return 'link'; + default: + return false; + } + } + + /** + * Return a stat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + * @access private + */ + function _get_stat_cache_prop($path, $prop) + { + return $this->_get_xstat_cache_prop($path, $prop, 'stat'); + } + + /** + * Return an lstat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + * @access private + */ + function _get_lstat_cache_prop($path, $prop) + { + return $this->_get_xstat_cache_prop($path, $prop, 'lstat'); + } + + /** + * Return a stat or lstat properity + * + * Uses cache if appropriate. + * + * @param string $path + * @param string $prop + * @return mixed + * @access private + */ + function _get_xstat_cache_prop($path, $prop, $type) + { + if ($this->use_stat_cache) { + $path = $this->_realpath($path); + + $result = $this->_query_stat_cache($path); + + if (is_object($result) && isset($result->$type)) { + return $result->{$type}[$prop]; + } + } + + $result = $this->$type($path); + + if ($result === false || !isset($result[$prop])) { + return false; + } + + return $result[$prop]; + } + + /** + * Renames a file or a directory on the SFTP server + * + * @param string $oldname + * @param string $newname + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @access public + */ + function rename($oldname, $newname) + { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { + return false; + } + + $oldname = $this->_realpath($oldname); + $newname = $this->_realpath($newname); + if ($oldname === false || $newname === false) { + return false; + } + + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 + $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); + if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { + return false; + } + + $response = $this->_get_sftp_packet(); + if ($this->packet_type != NET_SFTP_STATUS) { + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); + } + + // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + extract(unpack('Nstatus', $this->_string_shift($response, 4))); + if ($status != NET_SFTP_STATUS_OK) { + $this->_logError($response, $status); + return false; + } + + // don't move the stat cache entry over since this operation could very well change the + // atime and mtime attributes + //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname)); + $this->_remove_from_stat_cache($oldname); + $this->_remove_from_stat_cache($newname); + + return true; + } + + /** + * Parse Attributes + * + * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param string $response + * @return array + * @access private + */ + function _parseAttributes(&$response) + { + $attr = array(); + extract(unpack('Nflags', $this->_string_shift($response, 4))); + // SFTPv4+ have a type field (a byte) that follows the above flag field + foreach ($this->attributes as $key => $value) { + switch ($flags & $key) { + case NET_SFTP_ATTR_SIZE: // 0x00000001 + // The size attribute is defined as an unsigned 64-bit integer. + // The following will use floats on 32-bit platforms, if necessary. + // As can be seen in the BigInteger class, floats are generally + // IEEE 754 binary64 "double precision" on such platforms and + // as such can represent integers of at least 2^50 without loss + // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. + $attr['size'] = hexdec(Hex::encode($this->_string_shift($response, 8))); + break; + case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) + $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); + break; + case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 + $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); + // mode == permissions; permissions was the original array key and is retained for bc purposes. + // mode was added because that's the more industry standard terminology + $attr+= array('mode' => $attr['permissions']); + $fileType = $this->_parseMode($attr['permissions']); + if ($fileType !== false) { + $attr+= array('type' => $fileType); + } + break; + case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 + $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); + break; + case NET_SFTP_ATTR_EXTENDED: // 0x80000000 + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $key = $this->_string_shift($response, $length); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $attr[$key] = $this->_string_shift($response, $length); + } + } + } + return $attr; + } + + /** + * Attempt to identify the file type + * + * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway + * + * @param int $mode + * @return int + * @access private + */ + function _parseMode($mode) + { + // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 + // see, also, http://linux.die.net/man/2/stat + switch ($mode & 0170000) {// ie. 1111 0000 0000 0000 + case 0000000: // no file type specified - figure out the file type using alternative means + return false; + case 0040000: + return NET_SFTP_TYPE_DIRECTORY; + case 0100000: + return NET_SFTP_TYPE_REGULAR; + case 0120000: + return NET_SFTP_TYPE_SYMLINK; + // new types introduced in SFTPv5+ + // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 + case 0010000: // named pipe (fifo) + return NET_SFTP_TYPE_FIFO; + case 0020000: // character special + return NET_SFTP_TYPE_CHAR_DEVICE; + case 0060000: // block special + return NET_SFTP_TYPE_BLOCK_DEVICE; + case 0140000: // socket + return NET_SFTP_TYPE_SOCKET; + case 0160000: // whiteout + // "SPECIAL should be used for files that are of + // a known type which cannot be expressed in the protocol" + return NET_SFTP_TYPE_SPECIAL; + default: + return NET_SFTP_TYPE_UNKNOWN; + } + } + + /** + * Parse Longname + * + * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open + * a file as a directory and see if an error is returned or you could try to parse the + * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does. + * The result is returned using the + * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}. + * + * If the longname is in an unrecognized format bool(false) is returned. + * + * @param string $longname + * @return mixed + * @access private + */ + function _parseLongname($longname) + { + // http://en.wikipedia.org/wiki/Unix_file_types + // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions + if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { + switch ($longname[0]) { + case '-': + return NET_SFTP_TYPE_REGULAR; + case 'd': + return NET_SFTP_TYPE_DIRECTORY; + case 'l': + return NET_SFTP_TYPE_SYMLINK; + default: + return NET_SFTP_TYPE_SPECIAL; + } + } + + return false; + } + + /** + * Sends SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * @param int $type + * @param string $data + * @see self::_get_sftp_packet() + * @see self::_send_channel_packet() + * @return bool + * @access private + */ + function _send_sftp_packet($type, $data) + { + $packet = $this->request_id !== false ? + pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : + pack('NCa*', strlen($data) + 1, $type, $data); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = $this->_send_channel_packet(self::CHANNEL, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SFTP_LOGGING')) { + $packet_type = '-> ' . $this->packet_types[$type] . + ' (' . round($stop - $start, 4) . 's)'; + if (NET_SFTP_LOGGING == self::LOG_REALTIME) { + echo "
\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n
\r\n"; + flush(); + ob_flush(); + } else { + $this->packet_type_log[] = $packet_type; + if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { + $this->packet_log[] = $data; + } + } + } + + return $result; + } + + /** + * Receives SFTP Packets + * + * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. + * + * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. + * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA + * messages containing one SFTP packet. + * + * @see self::_send_sftp_packet() + * @return string + * @access private + */ + function _get_sftp_packet() + { + $this->curTimeout = false; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + + // SFTP packet length + while (strlen($this->packet_buffer) < 4) { + $temp = $this->_get_channel_packet(self::CHANNEL); + if (is_bool($temp)) { + $this->packet_type = false; + $this->packet_buffer = ''; + return false; + } + $this->packet_buffer.= $temp; + } + extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); + $tempLength = $length; + $tempLength-= strlen($this->packet_buffer); + + // SFTP packet type and data payload + while ($tempLength > 0) { + $temp = $this->_get_channel_packet(self::CHANNEL); + if (is_bool($temp)) { + $this->packet_type = false; + $this->packet_buffer = ''; + return false; + } + $this->packet_buffer.= $temp; + $tempLength-= strlen($temp); + } + + $stop = strtok(microtime(), ' ') + strtok(''); + + $this->packet_type = ord($this->_string_shift($this->packet_buffer)); + + if ($this->request_id !== false) { + $this->_string_shift($this->packet_buffer, 4); // remove the request id + $length-= 5; // account for the request id and the packet type + } else { + $length-= 1; // account for the packet type + } + + $packet = $this->_string_shift($this->packet_buffer, $length); + + if (defined('NET_SFTP_LOGGING')) { + $packet_type = '<- ' . $this->packet_types[$this->packet_type] . + ' (' . round($stop - $start, 4) . 's)'; + if (NET_SFTP_LOGGING == self::LOG_REALTIME) { + echo "
\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n
\r\n"; + flush(); + ob_flush(); + } else { + $this->packet_type_log[] = $packet_type; + if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { + $this->packet_log[] = $packet; + } + } + } + + return $packet; + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') + * + * @access public + * @return string or Array + */ + function getSFTPLog() + { + if (!defined('NET_SFTP_LOGGING')) { + return false; + } + + switch (NET_SFTP_LOGGING) { + case self::LOG_COMPLEX: + return $this->_format_log($this->packet_log, $this->packet_type_log); + break; + //case self::LOG_SIMPLE: + default: + return $this->packet_type_log; + } + } + + /** + * Returns all errors + * + * @return string + * @access public + */ + function getSFTPErrors() + { + return $this->sftp_errors; + } + + /** + * Returns the last error + * + * @return string + * @access public + */ + function getLastSFTPError() + { + return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; + } + + /** + * Get supported SFTP versions + * + * @return array + * @access public + */ + function getSupportedVersions() + { + $temp = array('version' => $this->version); + if (isset($this->extensions['versions'])) { + $temp['extensions'] = $this->extensions['versions']; + } + return $temp; + } + + /** + * Disconnect + * + * @param int $reason + * @return bool + * @access private + */ + function _disconnect($reason) + { + $this->pwd = false; + parent::_disconnect($reason); + } +} diff --git a/src/phpseclib/Net/SFTP/Stream.php b/src/phpseclib/Net/SFTP/Stream.php new file mode 100755 index 00000000..d19d08b8 --- /dev/null +++ b/src/phpseclib/Net/SFTP/Stream.php @@ -0,0 +1,795 @@ + + * @copyright 2013 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net\SFTP; + +use phpseclib\Crypt\RSA; +use phpseclib\Net\SFTP; +use phpseclib\Net\SSH2; + +/** + * SFTP Stream Wrapper + * + * @package SFTP + * @author Jim Wigginton + * @access public + */ +class Stream +{ + /** + * SFTP instances + * + * Rather than re-create the connection we re-use instances if possible + * + * @var array + */ + static $instances; + + /** + * SFTP instance + * + * @var object + * @access private + */ + var $sftp; + + /** + * Path + * + * @var string + * @access private + */ + var $path; + + /** + * Mode + * + * @var string + * @access private + */ + var $mode; + + /** + * Position + * + * @var int + * @access private + */ + var $pos; + + /** + * Size + * + * @var int + * @access private + */ + var $size; + + /** + * Directory entries + * + * @var array + * @access private + */ + var $entries; + + /** + * EOF flag + * + * @var bool + * @access private + */ + var $eof; + + /** + * Context resource + * + * Technically this needs to be publically accessible so PHP can set it directly + * + * @var resource + * @access public + */ + var $context; + + /** + * Notification callback function + * + * @var callable + * @access public + */ + var $notification; + + /** + * Registers this class as a URL wrapper. + * + * @param string $protocol The wrapper name to be registered. + * @return bool True on success, false otherwise. + * @access public + */ + static function register($protocol = 'sftp') + { + if (in_array($protocol, stream_get_wrappers(), true)) { + return false; + } + return stream_wrapper_register($protocol, get_called_class()); + } + + /** + * The Constructor + * + * @access public + */ + function __construct() + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo "__construct()\r\n"; + } + } + + /** + * Path Parser + * + * Extract a path from a URI and actually connect to an SSH server if appropriate + * + * If "notification" is set as a context parameter the message code for successful login is + * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. + * + * @param string $path + * @return string + * @access private + */ + function _parse_path($path) + { + $orig = $path; + extract(parse_url($path) + array('port' => 22)); + if (isset($query)) { + $path.= '?' . $query; + } elseif (preg_match('/(\?|\?#)$/', $orig)) { + $path.= '?'; + } + if (isset($fragment)) { + $path.= '#' . $fragment; + } elseif ($orig[strlen($orig) - 1] == '#') { + $path.= '#'; + } + + if (!isset($host)) { + return false; + } + + if (isset($this->context)) { + $context = stream_context_get_params($this->context); + if (isset($context['notification'])) { + $this->notification = $context['notification']; + } + } + + if (preg_match('/^{[a-z0-9]+}$/i', $host)) { + $host = SSH2::getConnectionByResourceId($host); + if ($host === false) { + return false; + } + $this->sftp = $host; + } else { + if (isset($this->context)) { + $context = stream_context_get_options($this->context); + } + if (isset($context[$scheme]['session'])) { + $sftp = $context[$scheme]['session']; + } + if (isset($context[$scheme]['sftp'])) { + $sftp = $context[$scheme]['sftp']; + } + if (isset($sftp) && $sftp instanceof SFTP) { + $this->sftp = $sftp; + return $path; + } + if (isset($context[$scheme]['username'])) { + $user = $context[$scheme]['username']; + } + if (isset($context[$scheme]['password'])) { + $pass = $context[$scheme]['password']; + } + if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof RSA) { + $pass = $context[$scheme]['privkey']; + } + + if (!isset($user) || !isset($pass)) { + return false; + } + + // casting $pass to a string is necessary in the event that it's a \phpseclib\Crypt\RSA object + if (isset(self::$instances[$host][$port][$user][(string) $pass])) { + $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; + } else { + $this->sftp = new SFTP($host, $port); + $this->sftp->disableStatCache(); + if (isset($this->notification) && is_callable($this->notification)) { + /* if !is_callable($this->notification) we could do this: + + user_error('fopen(): failed to call user notifier', E_USER_WARNING); + + the ftp wrapper gives errors like that when the notifier isn't callable. + i've opted not to do that, however, since the ftp wrapper gives the line + on which the fopen occurred as the line number - not the line that the + user_error is on. + */ + call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + if (!$this->sftp->login($user, $pass)) { + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); + return false; + } + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); + } else { + if (!$this->sftp->login($user, $pass)) { + return false; + } + } + self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; + } + } + + return $path; + } + + /** + * Opens file or URL + * + * @param string $path + * @param string $mode + * @param int $options + * @param string $opened_path + * @return bool + * @access public + */ + function _stream_open($path, $mode, $options, &$opened_path) + { + $path = $this->_parse_path($path); + + if ($path === false) { + return false; + } + $this->path = $path; + + $this->size = $this->sftp->size($path); + $this->mode = preg_replace('#[bt]$#', '', $mode); + $this->eof = false; + + if ($this->size === false) { + if ($this->mode[0] == 'r') { + return false; + } else { + $this->sftp->touch($path); + $this->size = 0; + } + } else { + switch ($this->mode[0]) { + case 'x': + return false; + case 'w': + $this->sftp->truncate($path, 0); + $this->size = 0; + } + } + + $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; + + return true; + } + + /** + * Read from stream + * + * @param int $count + * @return mixed + * @access public + */ + function _stream_read($count) + { + switch ($this->mode) { + case 'w': + case 'a': + case 'x': + case 'c': + return false; + } + + // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite + //if ($this->pos >= $this->size) { + // $this->eof = true; + // return false; + //} + + $result = $this->sftp->get($this->path, false, $this->pos, $count); + if (isset($this->notification) && is_callable($this->notification)) { + if ($result === false) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP calls stream_read in 8k chunks + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); + } + + if (empty($result)) { // ie. false or empty string + $this->eof = true; + return false; + } + $this->pos+= strlen($result); + + return $result; + } + + /** + * Write to stream + * + * @param string $data + * @return mixed + * @access public + */ + function _stream_write($data) + { + switch ($this->mode) { + case 'r': + return false; + } + + $result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos); + if (isset($this->notification) && is_callable($this->notification)) { + if (!$result) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP splits up strings into 8k blocks before calling stream_write + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); + } + + if ($result === false) { + return false; + } + $this->pos+= strlen($data); + if ($this->pos > $this->size) { + $this->size = $this->pos; + } + $this->eof = false; + return strlen($data); + } + + /** + * Retrieve the current position of a stream + * + * @return int + * @access public + */ + function _stream_tell() + { + return $this->pos; + } + + /** + * Tests for end-of-file on a file pointer + * + * In my testing there are four classes functions that normally effect the pointer: + * fseek, fputs / fwrite, fgets / fread and ftruncate. + * + * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() + * will return false. do fread($fp, 1) and feof() will then return true. + * + * @return bool + * @access public + */ + function _stream_eof() + { + return $this->eof; + } + + /** + * Seeks to specific location in a stream + * + * @param int $offset + * @param int $whence + * @return bool + * @access public + */ + function _stream_seek($offset, $whence) + { + switch ($whence) { + case SEEK_SET: + if ($offset >= $this->size || $offset < 0) { + return false; + } + break; + case SEEK_CUR: + $offset+= $this->pos; + break; + case SEEK_END: + $offset+= $this->size; + } + + $this->pos = $offset; + $this->eof = false; + return true; + } + + /** + * Change stream options + * + * @param string $path + * @param int $option + * @param mixed $var + * @return bool + * @access public + */ + function _stream_metadata($path, $option, $var) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined + // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 + // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 + switch ($option) { + case 1: // PHP_STREAM_META_TOUCH + return $this->sftp->touch($path, $var[0], $var[1]); + case 2: // PHP_STREAM_OWNER_NAME + case 3: // PHP_STREAM_GROUP_NAME + return false; + case 4: // PHP_STREAM_META_OWNER + return $this->sftp->chown($path, $var); + case 5: // PHP_STREAM_META_GROUP + return $this->sftp->chgrp($path, $var); + case 6: // PHP_STREAM_META_ACCESS + return $this->sftp->chmod($path, $var) !== false; + } + } + + /** + * Retrieve the underlaying resource + * + * @param int $cast_as + * @return resource + * @access public + */ + function _stream_cast($cast_as) + { + return $this->sftp->fsock; + } + + /** + * Advisory file locking + * + * @param int $operation + * @return bool + * @access public + */ + function _stream_lock($operation) + { + return false; + } + + /** + * Renames a file or directory + * + * Attempts to rename oldname to newname, moving it between directories if necessary. + * If newname exists, it will be overwritten. This is a departure from what \phpseclib\Net\SFTP + * does. + * + * @param string $path_from + * @param string $path_to + * @return bool + * @access public + */ + function _rename($path_from, $path_to) + { + $path1 = parse_url($path_from); + $path2 = parse_url($path_to); + unset($path1['path'], $path2['path']); + if ($path1 != $path2) { + return false; + } + + $path_from = $this->_parse_path($path_from); + $path_to = parse_url($path_to); + if ($path_from === false) { + return false; + } + + $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2 + // "It is an error if there already exists a file with the name specified by newpath." + // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 + if (!$this->sftp->rename($path_from, $path_to)) { + if ($this->sftp->stat($path_to)) { + return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); + } + return false; + } + + return true; + } + + /** + * Open directory handle + * + * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and + * removed in 5.4 I'm just going to ignore it. + * + * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client + * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting + * the SFTP specs: + * + * The SSH_FXP_NAME response has the following format: + * + * uint32 id + * uint32 count + * repeats count times: + * string filename + * string longname + * ATTRS attrs + * + * @param string $path + * @param int $options + * @return bool + * @access public + */ + function _dir_opendir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + $this->pos = 0; + $this->entries = $this->sftp->nlist($path); + return $this->entries !== false; + } + + /** + * Read entry from directory handle + * + * @return mixed + * @access public + */ + function _dir_readdir() + { + if (isset($this->entries[$this->pos])) { + return $this->entries[$this->pos++]; + } + return false; + } + + /** + * Rewind directory handle + * + * @return bool + * @access public + */ + function _dir_rewinddir() + { + $this->pos = 0; + return true; + } + + /** + * Close directory handle + * + * @return bool + * @access public + */ + function _dir_closedir() + { + return true; + } + + /** + * Create a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + * @access public + */ + function _mkdir($path, $mode, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); + } + + /** + * Removes a directory + * + * Only valid $options is STREAM_MKDIR_RECURSIVE per , however, + * does not have a $recursive parameter as mkdir() does so I don't know how + * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as + * $options. What does 8 correspond to? + * + * @param string $path + * @param int $mode + * @param int $options + * @return bool + * @access public + */ + function _rmdir($path, $options) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->rmdir($path); + } + + /** + * Flushes the output + * + * See . Always returns true because \phpseclib\Net\SFTP doesn't cache stuff before writing + * + * @return bool + * @access public + */ + function _stream_flush() + { + return true; + } + + /** + * Retrieve information about a file resource + * + * @return mixed + * @access public + */ + function _stream_stat() + { + $results = $this->sftp->stat($this->path); + if ($results === false) { + return false; + } + return $results; + } + + /** + * Delete a file + * + * @param string $path + * @return bool + * @access public + */ + function _unlink($path) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + return $this->sftp->delete($path, false); + } + + /** + * Retrieve information about a file + * + * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib\Net\SFTP\Stream is quiet by default + * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll + * cross that bridge when and if it's reached + * + * @param string $path + * @param int $flags + * @return mixed + * @access public + */ + function _url_stat($path, $flags) + { + $path = $this->_parse_path($path); + if ($path === false) { + return false; + } + + $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); + if ($results === false) { + return false; + } + + return $results; + } + + /** + * Truncate stream + * + * @param int $new_size + * @return bool + * @access public + */ + function _stream_truncate($new_size) + { + if (!$this->sftp->truncate($this->path, $new_size)) { + return false; + } + + $this->eof = false; + $this->size = $new_size; + + return true; + } + + /** + * Change stream options + * + * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. + * The other two aren't supported because of limitations in \phpseclib\Net\SFTP. + * + * @param int $option + * @param int $arg1 + * @param int $arg2 + * @return bool + * @access public + */ + function _stream_set_option($option, $arg1, $arg2) + { + return false; + } + + /** + * Close an resource + * + * @access public + */ + function _stream_close() + { + } + + /** + * __call Magic Method + * + * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. + * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function + * lets you figure that out. + * + * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not + * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. + * + * @param string + * @param array + * @return mixed + * @access public + */ + function __call($name, $arguments) + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo $name . '('; + $last = count($arguments) - 1; + foreach ($arguments as $i => $argument) { + var_export($argument); + if ($i != $last) { + echo ','; + } + } + echo ")\r\n"; + } + $name = '_' . $name; + if (!method_exists($this, $name)) { + return false; + } + return call_user_func_array(array($this, $name), $arguments); + } +} diff --git a/src/phpseclib/Net/SSH1.php b/src/phpseclib/Net/SSH1.php new file mode 100755 index 00000000..2ed4a002 --- /dev/null +++ b/src/phpseclib/Net/SSH1.php @@ -0,0 +1,1607 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('ls -la'); + * ?> + * + * + * Here's another short example: + * + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * More information on the SSHv1 specification can be found by reading + * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}. + * + * @category Net + * @package SSH1 + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +use ParagonIE\ConstantTime\Hex; +use phpseclib\Crypt\DES; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\TripleDES; +use phpseclib\Math\BigInteger; + +/** + * Pure-PHP implementation of SSHv1. + * + * @package SSH1 + * @author Jim Wigginton + * @access public + */ +class SSH1 +{ + /**#@+ + * Encryption Methods + * + * @see \phpseclib\Net\SSH1::getSupportedCiphers() + * @access public + */ + /** + * No encryption + * + * Not supported. + */ + const CIPHER_NONE = 0; + /** + * IDEA in CFB mode + * + * Not supported. + */ + const CIPHER_IDEA = 1; + /** + * DES in CBC mode + */ + const CIPHER_DES = 2; + /** + * Triple-DES in CBC mode + * + * All implementations are required to support this + */ + const CIPHER_3DES = 3; + /** + * TRI's Simple Stream encryption CBC + * + * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h), + * although it doesn't use it (see cipher.c) + */ + const CIPHER_BROKEN_TSS = 4; + /** + * RC4 + * + * Not supported. + * + * @internal According to the SSH1 specs: + * + * "The first 16 bytes of the session key are used as the key for + * the server to client direction. The remaining 16 bytes are used + * as the key for the client to server direction. This gives + * independent 128-bit keys for each direction." + * + * This library currently only supports encryption when the same key is being used for both directions. This is + * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps). + */ + const CIPHER_RC4 = 5; + /** + * Blowfish + * + * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and + * uses it (see cipher.c) + */ + const CIPHER_BLOWFISH = 6; + /**#@-*/ + + /**#@+ + * Authentication Methods + * + * @see \phpseclib\Net\SSH1::getSupportedAuthentications() + * @access public + */ + /** + * .rhosts or /etc/hosts.equiv + */ + const AUTH_RHOSTS = 1; + /** + * pure RSA authentication + */ + const AUTH_RSA = 2; + /** + * password authentication + * + * This is the only method that is supported by this library. + */ + const AUTH_PASSWORD = 3; + /** + * .rhosts with RSA host authentication + */ + const AUTH_RHOSTS_RSA = 4; + /**#@-*/ + + /**#@+ + * Terminal Modes + * + * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html + * @access private + */ + const TTY_OP_END = 0; + /**#@-*/ + + /** + * The Response Type + * + * @see \phpseclib\Net\SSH1::_get_binary_packet() + * @access private + */ + const RESPONSE_TYPE = 1; + + /** + * The Response Data + * + * @see \phpseclib\Net\SSH1::_get_binary_packet() + * @access private + */ + const RESPONSE_DATA = 2; + + /**#@+ + * Execution Bitmap Masks + * + * @see \phpseclib\Net\SSH1::bitmap + * @access private + */ + const MASK_CONSTRUCTOR = 0x00000001; + const MASK_CONNECTED = 0x00000002; + const MASK_LOGIN = 0x00000004; + const MASK_SHELL = 0x00000008; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH1::getLog() + */ + /** + * Returns the message numbers + */ + const LOG_SIMPLE = 1; + /** + * Returns the message content + */ + const LOG_COMPLEX = 2; + /** + * Outputs the content real-time + */ + const LOG_REALTIME = 3; + /** + * Dumps the content real-time to a file + */ + const LOG_REALTIME_FILE = 4; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH1::read() + */ + /** + * Returns when a string matching $expect exactly is found + */ + const READ_SIMPLE = 1; + /** + * Returns when a string matching the regular expression $expect is found + */ + const READ_REGEX = 2; + /**#@-*/ + + /** + * The SSH identifier + * + * @var string + * @access private + */ + var $identifier = 'SSH-1.5-phpseclib'; + + /** + * The Socket Object + * + * @var object + * @access private + */ + var $fsock; + + /** + * The cryptography object + * + * @var object + * @access private + */ + var $crypto = false; + + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var int + * @access private + */ + var $bitmap = 0; + + /** + * The Server Key Public Exponent + * + * Logged for debug purposes + * + * @see self::getServerKeyPublicExponent() + * @var string + * @access private + */ + var $server_key_public_exponent; + + /** + * The Server Key Public Modulus + * + * Logged for debug purposes + * + * @see self::getServerKeyPublicModulus() + * @var string + * @access private + */ + var $server_key_public_modulus; + + /** + * The Host Key Public Exponent + * + * Logged for debug purposes + * + * @see self::getHostKeyPublicExponent() + * @var string + * @access private + */ + var $host_key_public_exponent; + + /** + * The Host Key Public Modulus + * + * Logged for debug purposes + * + * @see self::getHostKeyPublicModulus() + * @var string + * @access private + */ + var $host_key_public_modulus; + + /** + * Supported Ciphers + * + * Logged for debug purposes + * + * @see self::getSupportedCiphers() + * @var array + * @access private + */ + var $supported_ciphers = array( + self::CIPHER_NONE => 'No encryption', + self::CIPHER_IDEA => 'IDEA in CFB mode', + self::CIPHER_DES => 'DES in CBC mode', + self::CIPHER_3DES => 'Triple-DES in CBC mode', + self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC', + self::CIPHER_RC4 => 'RC4', + self::CIPHER_BLOWFISH => 'Blowfish' + ); + + /** + * Supported Authentications + * + * Logged for debug purposes + * + * @see self::getSupportedAuthentications() + * @var array + * @access private + */ + var $supported_authentications = array( + self::AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv', + self::AUTH_RSA => 'pure RSA authentication', + self::AUTH_PASSWORD => 'password authentication', + self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication' + ); + + /** + * Server Identification + * + * @see self::getServerIdentification() + * @var string + * @access private + */ + var $server_identification = ''; + + /** + * Protocol Flags + * + * @see self::__construct() + * @var array + * @access private + */ + var $protocol_flags = array(); + + /** + * Protocol Flag Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $protocol_flag_log = array(); + + /** + * Message Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $message_log = array(); + + /** + * Real-time log file pointer + * + * @see self::_append_log() + * @var resource + * @access private + */ + var $realtime_log_file; + + /** + * Real-time log file size + * + * @see self::_append_log() + * @var int + * @access private + */ + var $realtime_log_size; + + /** + * Real-time log file wrap boolean + * + * @see self::_append_log() + * @var bool + * @access private + */ + var $realtime_log_wrap; + + /** + * Interactive Buffer + * + * @see self::read() + * @var array + * @access private + */ + var $interactiveBuffer = ''; + + /** + * Timeout + * + * @see self::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see self::_get_channel_packet() + * @access private + */ + var $curTimeout; + + /** + * Log Boundary + * + * @see self::_format_log() + * @access private + */ + var $log_boundary = ':'; + + /** + * Log Long Width + * + * @see self::_format_log() + * @access private + */ + var $log_long_width = 65; + + /** + * Log Short Width + * + * @see self::_format_log() + * @access private + */ + var $log_short_width = 16; + + /** + * Hostname + * + * @see self::__construct() + * @see self::_connect() + * @var string + * @access private + */ + var $host; + + /** + * Port Number + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $port; + + /** + * Timeout for initial connection + * + * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like + * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor, + * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be + * 10 seconds. It is used by fsockopen() in that function. + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $connectionTimeout; + + /** + * Default cipher + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $cipher; + + /** + * Default Constructor. + * + * Connects to an SSHv1 server + * + * @param string $host + * @param int $port + * @param int $timeout + * @param int $cipher + * @return \phpseclib\Net\SSH1 + * @access public + */ + function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES) + { + $this->protocol_flags = array( + 1 => 'NET_SSH1_MSG_DISCONNECT', + 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', + 3 => 'NET_SSH1_CMSG_SESSION_KEY', + 4 => 'NET_SSH1_CMSG_USER', + 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD', + 10 => 'NET_SSH1_CMSG_REQUEST_PTY', + 12 => 'NET_SSH1_CMSG_EXEC_SHELL', + 13 => 'NET_SSH1_CMSG_EXEC_CMD', + 14 => 'NET_SSH1_SMSG_SUCCESS', + 15 => 'NET_SSH1_SMSG_FAILURE', + 16 => 'NET_SSH1_CMSG_STDIN_DATA', + 17 => 'NET_SSH1_SMSG_STDOUT_DATA', + 18 => 'NET_SSH1_SMSG_STDERR_DATA', + 19 => 'NET_SSH1_CMSG_EOF', + 20 => 'NET_SSH1_SMSG_EXITSTATUS', + 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION' + ); + + $this->_define_array($this->protocol_flags); + + $this->host = $host; + $this->port = $port; + $this->connectionTimeout = $timeout; + $this->cipher = $cipher; + } + + /** + * Connect to an SSHv1 server + * + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @access private + */ + function _connect() + { + $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); + if (!$this->fsock) { + throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); + } + + $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)) { + throw new \RuntimeException('Can only connect to SSH servers'); + } + if ($parts[1][0] != 1) { + throw new \RuntimeException("Cannot connect to $parts[1] servers"); + } + + fputs($this->fsock, $this->identifier."\r\n"); + + $response = $this->_get_binary_packet(); + if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { + throw new \UnexpectedValueException('Expected SSH_SMSG_PUBLIC_KEY'); + } + + $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 = md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie, true); + + $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)) { + throw new \RuntimeException('Error sending SSH_CMSG_SESSION_KEY'); + } + + switch ($cipher) { + //case self::CIPHER_NONE: + // $this->crypto = new \phpseclib\Crypt\Null(); + // break; + case self::CIPHER_DES: + $this->crypto = new DES(DES::MODE_CBC); + $this->crypto->disablePadding(); + $this->crypto->enableContinuousBuffer(); + $this->crypto->setKey(substr($session_key, 0, 8)); + // "The iv (initialization vector) is initialized to all zeroes." + $this->crypto->setIV(str_repeat("\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)); + // "All three initialization vectors are initialized to zero." + $this->crypto->setIV(str_repeat("\0", 8)); + 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) { + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); + } + + $this->bitmap = self::MASK_CONNECTED; + + return true; + } + + /** + * Login + * + * @param string $username + * @param string $password + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @access public + */ + function login($username, $password = '') + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + $this->bitmap |= self::MASK_CONSTRUCTOR; + if (!$this->_connect()) { + return false; + } + } + + if (!($this->bitmap & self::MASK_CONNECTED)) { + return false; + } + + $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Error sending SSH_CMSG_USER'); + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); + } + + $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Error sending SSH_CMSG_AUTH_PASSWORD'); + } + + // remove the username and password from the last logged packet + if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) { + $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password'); + $this->message_log[count($this->message_log) - 1] = $data; + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { + return false; + } else { + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); + } + } + + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param mixed $timeout + */ + function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + + /** + * Executes a command on a non-interactive shell, returns the output, and quits. + * + * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2 + * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a + * shell with the -s option, as discussed in the following links: + * + * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html} + * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html} + * + * To execute further commands, a new \phpseclib\Net\SSH1 object will need to be created. + * + * Returns false on failure and the output, otherwise. + * + * @see self::interactiveRead() + * @see self::interactiveWrite() + * @param string $cmd + * @return mixed + * @throws \RuntimeException on error sending command + * @access public + */ + function exec($cmd, $block = true) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); + } + + $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Error sending SSH_CMSG_EXEC_CMD'); + } + + if (!$block) { + return true; + } + + $output = ''; + $response = $this->_get_binary_packet(); + + if ($response !== false) { + do { + $output.= substr($response[self::RESPONSE_DATA], 4); + $response = $this->_get_binary_packet(); + } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS); + } + + $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); + + // i don't think it's really all that important if this packet gets sent or not. + $this->_send_binary_packet($data); + + fclose($this->fsock); + + // reset the execution bitmap - a new \phpseclib\Net\SSH1 object needs to be created. + $this->bitmap = 0; + + return $output; + } + + /** + * Creates an interactive shell + * + * @see self::interactiveRead() + * @see self::interactiveWrite() + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @access private + */ + function _initShell() + { + // connect using the sample parameters in protocol-1.5.txt. + // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text + // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell. + $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Error sending SSH_CMSG_REQUEST_PTY'); + } + + $response = $this->_get_binary_packet(); + + if ($response === true) { + return false; + } + if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); + } + + $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Error sending SSH_CMSG_EXEC_SHELL'); + } + + $this->bitmap |= self::MASK_SHELL; + + //stream_set_blocking($this->fsock, 0); + + return true; + } + + /** + * Inputs a command into an interactive shell. + * + * @see self::interactiveWrite() + * @param string $cmd + * @return bool + * @access public + */ + function write($cmd) + { + return $this->interactiveWrite($cmd); + } + + /** + * Returns the output of an interactive shell when there's a match for $expect + * + * $expect can take the form of a string literal or, if $mode == self::READ__REGEX, + * a regular expression. + * + * @see self::write() + * @param string $expect + * @param int $mode + * @return bool + * @throws \RuntimeException on connection error + * @access public + */ + function read($expect, $mode = self::READ__SIMPLE) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + + $match = $expect; + while (true) { + if ($mode == self::READ__REGEX) { + preg_match($expect, $this->interactiveBuffer, $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; + if ($pos !== false) { + return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); + } + $response = $this->_get_binary_packet(); + + if ($response === true) { + return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); + } + $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4); + } + } + + /** + * Inputs a command into an interactive shell. + * + * @see self::interactiveRead() + * @param string $cmd + * @return bool + * @throws \RuntimeException on connection error + * @access public + */ + function interactiveWrite($cmd) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + + $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Error sending SSH_CMSG_STDIN'); + } + + return true; + } + + /** + * Returns the output of an interactive shell when no more output is available. + * + * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like + * "^[[00m", you're seeing ANSI escape codes. According to + * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT + * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user, + * there's not going to be much recourse. + * + * @see self::interactiveRead() + * @return string + * @throws \RuntimeException on connection error + * @access public + */ + function interactiveRead() + { + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + + $read = array($this->fsock); + $write = $except = null; + if (stream_select($read, $write, $except, 0)) { + $response = $this->_get_binary_packet(); + return substr($response[self::RESPONSE_DATA], 4); + } else { + return ''; + } + } + + /** + * Disconnect + * + * @access public + */ + function disconnect() + { + $this->_disconnect(); + } + + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + * @access public + */ + function __destruct() + { + $this->_disconnect(); + } + + /** + * Disconnect + * + * @param string $msg + * @access private + */ + function _disconnect($msg = 'Client Quit') + { + if ($this->bitmap) { + $data = pack('C', NET_SSH1_CMSG_EOF); + $this->_send_binary_packet($data); + /* + $response = $this->_get_binary_packet(); + if ($response === true) { + $response = array(self::RESPONSE_TYPE => -1); + } + switch ($response[self::RESPONSE_TYPE]) { + case NET_SSH1_SMSG_EXITSTATUS: + $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); + break; + default: + $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); + } + */ + $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); + + $this->_send_binary_packet($data); + fclose($this->fsock); + $this->bitmap = 0; + } + } + + /** + * Gets Binary Packets + * + * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info. + * + * Also, this function could be improved upon by adding detection for the following exploit: + * http://www.securiteam.com/securitynews/5LP042K3FY.html + * + * @see self::_send_binary_packet() + * @return array + * @access private + */ + function _get_binary_packet() + { + if (feof($this->fsock)) { + //user_error('connection closed prematurely'); + return false; + } + + if ($this->curTimeout) { + $read = array($this->fsock); + $write = $except = null; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + //$this->_disconnect('Timeout'); + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $temp = unpack('Nlength', fread($this->fsock, 4)); + + $padding_length = 8 - ($temp['length'] & 7); + $length = $temp['length'] + $padding_length; + $raw = ''; + + while ($length > 0) { + $temp = fread($this->fsock, $length); + $raw.= $temp; + $length-= strlen($temp); + } + $stop = strtok(microtime(), ' ') + strtok(''); + + if (strlen($raw) && $this->crypto !== false) { + $raw = $this->crypto->decrypt($raw); + } + + $padding = substr($raw, 0, $padding_length); + $type = $raw[$padding_length]; + $data = substr($raw, $padding_length + 1, -4); + + $temp = unpack('Ncrc', substr($raw, -4)); + + //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { + // user_error('Bad CRC in packet from server'); + // return false; + //} + + $type = ord($type); + + if (defined('NET_SSH1_LOGGING')) { + $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN'; + $temp = '<- ' . $temp . + ' (' . round($stop - $start, 4) . 's)'; + $this->_append_log($temp, $data); + } + + return array( + self::RESPONSE_TYPE => $type, + self::RESPONSE_DATA => $data + ); + } + + /** + * Sends Binary Packets + * + * Returns true on success, false on failure. + * + * @see self::_get_binary_packet() + * @param string $data + * @return bool + * @access private + */ + function _send_binary_packet($data) + { + if (feof($this->fsock)) { + //user_error('connection closed prematurely'); + return false; + } + + $length = strlen($data) + 4; + + $padding = Random::string(8 - ($length & 7)); + + $orig = $data; + $data = $padding . $data; + $data.= pack('N', $this->_crc($data)); + + if ($this->crypto !== false) { + $data = $this->crypto->encrypt($data); + } + + $packet = pack('Na*', $length, $data); + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + $result = strlen($packet) == fputs($this->fsock, $packet); + $stop = strtok(microtime(), ' ') + strtok(''); + + if (defined('NET_SSH1_LOGGING')) { + $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN'; + $temp = '-> ' . $temp . + ' (' . round($stop - $start, 4) . 's)'; + $this->_append_log($temp, $orig); + } + + return $result; + } + + /** + * Cyclic Redundancy Check (CRC) + * + * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so + * we've reimplemented it. A more detailed discussion of the differences can be found after + * $crc_lookup_table's initialization. + * + * @see self::_get_binary_packet() + * @see self::_send_binary_packet() + * @param string $data + * @return int + * @access private + */ + function _crc($data) + { + static $crc_lookup_table = array( + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + ); + + // For this function to yield the same output as PHP's crc32 function, $crc would have to be + // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is. + $crc = 0x00000000; + $length = strlen($data); + + for ($i=0; $i<$length; $i++) { + // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all + // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example, + // yields 0xFF800000 - not 0x00800000. The following link elaborates: + // http://www.php.net/manual/en/language.operators.bitwise.php#57281 + $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])]; + } + + // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with + // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would. + return $crc; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * RSA Encrypt + * + * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e + * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that + * calls this call modexp, instead, but I think this makes things clearer, maybe... + * + * @see self::__construct() + * @param BigInteger $m + * @param array $key + * @return BigInteger + * @access private + */ + function _rsa_crypt($m, $key) + { + /* + $rsa = new RSA(); + $rsa->load($key, 'raw'); + $rsa->setHash('sha1'); + return $rsa->encrypt($m, RSA::PADDING_PKCS1); + */ + + // To quote from protocol-1.5.txt: + // The most significant byte (which is only partial as the value must be + // less than the public modulus, which is never a power of two) is zero. + // + // The next byte contains the value 2 (which stands for public-key + // encrypted data in the PKCS standard [PKCS#1]). Then, there are non- + // zero random bytes to fill any unused space, a zero byte, and the data + // to be encrypted in the least significant bytes, the last byte of the + // data in the least significant byte. + + // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation", + // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL: + // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf + $modulus = $key[1]->toBytes(); + $length = strlen($modulus) - strlen($m) - 3; + $random = ''; + while (strlen($random) != $length) { + $block = Random::string($length - strlen($random)); + $block = str_replace("\x00", '', $block); + $random.= $block; + } + $temp = chr(0) . chr(2) . $random . chr(0) . $m; + + $m = new BigInteger($temp, 256); + $m = $m->modPow($key[0], $key[1]); + + return $m->toBytes(); + } + + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @param array $array + * @access private + */ + function _define_array() + { + $args = func_get_args(); + foreach ($args as $arg) { + foreach ($arg as $key => $value) { + if (!defined($value)) { + define($value, $key); + } else { + break 2; + } + } + } + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING') + * + * @access public + * @return array|false|string + */ + function getLog() + { + if (!defined('NET_SSH1_LOGGING')) { + return false; + } + + switch (NET_SSH1_LOGGING) { + case self::LOG_SIMPLE: + return $this->message_number_log; + break; + case self::LOG_COMPLEX: + return $this->_format_log($this->message_log, $this->protocol_flags_log); + break; + default: + return false; + } + } + + /** + * Formats a log for printing + * + * @param array $message_log + * @param array $message_number_log + * @access private + * @return string + */ + function _format_log($message_log, $message_number_log) + { + $output = ''; + for ($i = 0; $i < count($message_log); $i++) { + $output.= $message_number_log[$i] . "\r\n"; + $current_log = $message_log[$i]; + $j = 0; + do { + if (strlen($current_log)) { + $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; + } + $fragment = $this->_string_shift($current_log, $this->log_short_width); + $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); + $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; + $j++; + } while (strlen($current_log)); + $output.= "\r\n"; + } + + return $output; + } + + /** + * Helper function for _format_log + * + * For use with preg_replace_callback() + * + * @param array $matches + * @access private + * @return string + */ + function _format_log_helper($matches) + { + return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); + } + + /** + * Return the server key public exponent + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getServerKeyPublicExponent($raw_output = false) + { + return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString(); + } + + /** + * Return the server key public modulus + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getServerKeyPublicModulus($raw_output = false) + { + return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString(); + } + + /** + * Return the host key public exponent + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getHostKeyPublicExponent($raw_output = false) + { + return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString(); + } + + /** + * Return the host key public modulus + * + * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, + * the raw bytes. This behavior is similar to PHP's md5() function. + * + * @param bool $raw_output + * @return string + * @access public + */ + function getHostKeyPublicModulus($raw_output = false) + { + return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString(); + } + + /** + * Return a list of ciphers supported by SSH1 server. + * + * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output + * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll + * get array(self::CIPHER_3DES). + * + * @param bool $raw_output + * @return array + * @access public + */ + function getSupportedCiphers($raw_output = false) + { + return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers); + } + + /** + * Return a list of authentications supported by SSH1 server. + * + * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output + * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll + * get array(self::AUTH_PASSWORD). + * + * @param bool $raw_output + * @return array + * @access public + */ + function getSupportedAuthentications($raw_output = false) + { + return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications); + } + + /** + * Return the server identification. + * + * @return string + * @access public + */ + function getServerIdentification() + { + return rtrim($this->server_identification); + } + + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param string $data + * @access private + */ + function _append_log($protocol_flags, $message) + { + switch (NET_SSH1_LOGGING) { + // useful for benchmarks + case self::LOG_SIMPLE: + $this->protocol_flags_log[] = $protocol_flags; + break; + // the most useful log for SSH1 + case self::LOG_COMPLEX: + $this->protocol_flags_log[] = $protocol_flags; + $this->_string_shift($message); + $this->log_size+= strlen($message); + $this->message_log[] = $message; + while ($this->log_size > self::LOG_MAX_SIZE) { + $this->log_size-= strlen(array_shift($this->message_log)); + array_shift($this->protocol_flags_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case self::LOG_REALTIME: + echo "
\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n
\r\n"; + @flush(); + @ob_flush(); + break; + // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE + // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case self::LOG_REALTIME_FILE: + if (!isset($this->realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = self::LOG_REALTIME_FILE; + $fp = fopen($filename, 'w'); + $this->realtime_log_file = $fp; + } + if (!is_resource($this->realtime_log_file)) { + break; + } + $entry = $this->_format_log(array($message), array($protocol_flags)); + if ($this->realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry.= $temp; + fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); + } + $this->realtime_log_size+= strlen($entry); + if ($this->realtime_log_size > self::LOG_MAX_SIZE) { + fseek($this->realtime_log_file, 0); + $this->realtime_log_size = strlen($entry); + $this->realtime_log_wrap = true; + } + fputs($this->realtime_log_file, $entry); + } + } +} diff --git a/src/phpseclib/Net/SSH2.php b/src/phpseclib/Net/SSH2.php new file mode 100755 index 00000000..97a37a0b --- /dev/null +++ b/src/phpseclib/Net/SSH2.php @@ -0,0 +1,4224 @@ + + * login('username', 'password')) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * + * setPassword('whatever'); + * $key->load(file_get_contents('privatekey')); + * + * $ssh = new \phpseclib\Net\SSH2('www.domain.tld'); + * if (!$ssh->login('username', $key)) { + * exit('Login Failed'); + * } + * + * echo $ssh->read('username@username:~$'); + * $ssh->write("ls -la\n"); + * echo $ssh->read('username@username:~$'); + * ?> + * + * + * @category Net + * @package SSH2 + * @author Jim Wigginton + * @copyright 2007 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + */ + +namespace phpseclib\Net; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Crypt\Base; +use phpseclib\Crypt\Blowfish; +use phpseclib\Crypt\Hash; +use phpseclib\Crypt\Random; +use phpseclib\Crypt\RC4; +use phpseclib\Crypt\Rijndael; +use phpseclib\Crypt\RSA; +use phpseclib\Crypt\TripleDES; +use phpseclib\Crypt\Twofish; +use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. +use phpseclib\System\SSH\Agent; +use phpseclib\Exception\NoSupportedAlgorithmsException; + +/** + * Pure-PHP implementation of SSHv2. + * + * @package SSH2 + * @author Jim Wigginton + * @access public + */ +class SSH2 +{ + /**#@+ + * Execution Bitmap Masks + * + * @see \phpseclib\Net\SSH2::bitmap + * @access private + */ + const MASK_CONSTRUCTOR = 0x00000001; + const MASK_CONNECTED = 0x00000002; + const MASK_LOGIN_REQ = 0x00000004; + const MASK_LOGIN = 0x00000008; + const MASK_SHELL = 0x00000010; + const MASK_WINDOW_ADJUST = 0x00000020; + /**#@-*/ + + /**#@+ + * Channel constants + * + * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer + * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with + * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a + * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel + * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet: + * The 'recipient channel' is the channel number given in the original + * open request, and 'sender channel' is the channel number allocated by + * the other side. + * + * @see \phpseclib\Net\SSH2::_send_channel_packet() + * @see \phpseclib\Net\SSH2::_get_channel_packet() + * @access private + */ + const CHANNEL_EXEC = 0; // PuTTy uses 0x100 + const CHANNEL_SHELL = 1; + const CHANNEL_SUBSYSTEM = 2; + const CHANNEL_AGENT_FORWARD = 3; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH2::getLog() + */ + /** + * Returns the message numbers + */ + const LOG_SIMPLE = 1; + /** + * Returns the message content + */ + const LOG_COMPLEX = 2; + /** + * Outputs the content real-time + */ + const LOG_REALTIME = 3; + /** + * Dumps the content real-time to a file + */ + const LOG_REALTIME_FILE = 4; + /**#@-*/ + + /**#@+ + * @access public + * @see \phpseclib\Net\SSH2::read() + */ + /** + * Returns when a string matching $expect exactly is found + */ + const READ_SIMPLE = 1; + /** + * Returns when a string matching the regular expression $expect is found + */ + const READ_REGEX = 2; + /** + * Make sure that the log never gets larger than this + */ + const LOG_MAX_SIZE = 1048576; // 1024 * 1024 + /**#@-*/ + + /** + * The SSH identifier + * + * @var string + * @access private + */ + var $identifier; + + /** + * The Socket Object + * + * @var object + * @access private + */ + var $fsock; + + /** + * Execution Bitmap + * + * The bits that are set represent functions that have been called already. This is used to determine + * if a requisite function has been successfully executed. If not, an error should be thrown. + * + * @var int + * @access private + */ + var $bitmap = 0; + + /** + * Error information + * + * @see self::getErrors() + * @see self::getLastError() + * @var string + * @access private + */ + var $errors = array(); + + /** + * Server Identifier + * + * @see self::getServerIdentification() + * @var array|false + * @access private + */ + var $server_identifier = false; + + /** + * Key Exchange Algorithms + * + * @see self::getKexAlgorithims() + * @var array|false + * @access private + */ + var $kex_algorithms = false; + + /** + * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + * @access private + */ + var $kex_dh_group_size_min = 1536; + + /** + * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + * @access private + */ + var $kex_dh_group_size_preferred = 2048; + + /** + * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods + * + * @see self::_key_exchange() + * @var int + * @access private + */ + var $kex_dh_group_size_max = 4096; + + /** + * Server Host Key Algorithms + * + * @see self::getServerHostKeyAlgorithms() + * @var array|false + * @access private + */ + var $server_host_key_algorithms = false; + + /** + * Encryption Algorithms: Client to Server + * + * @see self::getEncryptionAlgorithmsClient2Server() + * @var array|false + * @access private + */ + var $encryption_algorithms_client_to_server = false; + + /** + * Encryption Algorithms: Server to Client + * + * @see self::getEncryptionAlgorithmsServer2Client() + * @var array|false + * @access private + */ + var $encryption_algorithms_server_to_client = false; + + /** + * MAC Algorithms: Client to Server + * + * @see self::getMACAlgorithmsClient2Server() + * @var array|false + * @access private + */ + var $mac_algorithms_client_to_server = false; + + /** + * MAC Algorithms: Server to Client + * + * @see self::getMACAlgorithmsServer2Client() + * @var array|false + * @access private + */ + var $mac_algorithms_server_to_client = false; + + /** + * Compression Algorithms: Client to Server + * + * @see self::getCompressionAlgorithmsClient2Server() + * @var array|false + * @access private + */ + var $compression_algorithms_client_to_server = false; + + /** + * Compression Algorithms: Server to Client + * + * @see self::getCompressionAlgorithmsServer2Client() + * @var array|false + * @access private + */ + var $compression_algorithms_server_to_client = false; + + /** + * Languages: Server to Client + * + * @see self::getLanguagesServer2Client() + * @var array|false + * @access private + */ + var $languages_server_to_client = false; + + /** + * Languages: Client to Server + * + * @see self::getLanguagesClient2Server() + * @var array|false + * @access private + */ + var $languages_client_to_server = false; + + /** + * Block Size for Server to Client Encryption + * + * "Note that the length of the concatenation of 'packet_length', + * 'padding_length', 'payload', and 'random padding' MUST be a multiple + * of the cipher block size or 8, whichever is larger. This constraint + * MUST be enforced, even when using stream ciphers." + * + * -- http://tools.ietf.org/html/rfc4253#section-6 + * + * @see self::__construct() + * @see self::_send_binary_packet() + * @var int + * @access private + */ + var $encrypt_block_size = 8; + + /** + * Block Size for Client to Server Encryption + * + * @see self::__construct() + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $decrypt_block_size = 8; + + /** + * Server to Client Encryption Object + * + * @see self::_get_binary_packet() + * @var object + * @access private + */ + var $decrypt = false; + + /** + * Client to Server Encryption Object + * + * @see self::_send_binary_packet() + * @var object + * @access private + */ + var $encrypt = false; + + /** + * Client to Server HMAC Object + * + * @see self::_send_binary_packet() + * @var object + * @access private + */ + var $hmac_create = false; + + /** + * Server to Client HMAC Object + * + * @see self::_get_binary_packet() + * @var object + * @access private + */ + var $hmac_check = false; + + /** + * Size of server to client HMAC + * + * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read. + * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is + * append it. + * + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $hmac_size = false; + + /** + * Server Public Host Key + * + * @see self::getServerPublicHostKey() + * @var string + * @access private + */ + var $server_public_host_key; + + /** + * Session identifer + * + * "The exchange hash H from the first key exchange is additionally + * used as the session identifier, which is a unique identifier for + * this connection." + * + * -- http://tools.ietf.org/html/rfc4253#section-7.2 + * + * @see self::_key_exchange() + * @var string + * @access private + */ + var $session_id = false; + + /** + * Exchange hash + * + * The current exchange hash + * + * @see self::_key_exchange() + * @var string + * @access private + */ + var $exchange_hash = false; + + /** + * Message Numbers + * + * @see self::__construct() + * @var array + * @access private + */ + var $message_numbers = array(); + + /** + * Disconnection Message 'reason codes' defined in RFC4253 + * + * @see self::__construct() + * @var array + * @access private + */ + var $disconnect_reasons = array(); + + /** + * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 + * + * @see self::__construct() + * @var array + * @access private + */ + var $channel_open_failure_reasons = array(); + + /** + * Terminal Modes + * + * @link http://tools.ietf.org/html/rfc4254#section-8 + * @see self::__construct() + * @var array + * @access private + */ + var $terminal_modes = array(); + + /** + * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes + * + * @link http://tools.ietf.org/html/rfc4254#section-5.2 + * @see self::__construct() + * @var array + * @access private + */ + var $channel_extended_data_type_codes = array(); + + /** + * Send Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see self::_send_binary_packet() + * @var int + * @access private + */ + var $send_seq_no = 0; + + /** + * Get Sequence Number + * + * See 'Section 6.4. Data Integrity' of rfc4253 for more info. + * + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $get_seq_no = 0; + + /** + * Server Channels + * + * Maps client channels to server channels + * + * @see self::_get_channel_packet() + * @see self::exec() + * @var array + * @access private + */ + var $server_channels = array(); + + /** + * Channel Buffers + * + * If a client requests a packet from one channel but receives two packets from another those packets should + * be placed in a buffer + * + * @see self::_get_channel_packet() + * @see self::exec() + * @var array + * @access private + */ + var $channel_buffers = array(); + + /** + * Channel Status + * + * Contains the type of the last sent message + * + * @see self::_get_channel_packet() + * @var array + * @access private + */ + var $channel_status = array(); + + /** + * Packet Size + * + * Maximum packet size indexed by channel + * + * @see self::_send_channel_packet() + * @var array + * @access private + */ + var $packet_size_client_to_server = array(); + + /** + * Message Number Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $message_number_log = array(); + + /** + * Message Log + * + * @see self::getLog() + * @var array + * @access private + */ + var $message_log = array(); + + /** + * The Window Size + * + * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) + * + * @var int + * @see self::_send_channel_packet() + * @see self::exec() + * @access private + */ + var $window_size = 0x7FFFFFFF; + + /** + * Window size, server to client + * + * Window size indexed by channel + * + * @see self::_send_channel_packet() + * @var array + * @access private + */ + var $window_size_server_to_client = array(); + + /** + * Window size, client to server + * + * Window size indexed by channel + * + * @see self::_get_channel_packet() + * @var array + * @access private + */ + var $window_size_client_to_server = array(); + + /** + * Server signature + * + * Verified against $this->session_id + * + * @see self::getServerPublicHostKey() + * @var string + * @access private + */ + var $signature = ''; + + /** + * Server signature format + * + * ssh-rsa or ssh-dss. + * + * @see self::getServerPublicHostKey() + * @var string + * @access private + */ + var $signature_format = ''; + + /** + * Interactive Buffer + * + * @see self::read() + * @var array + * @access private + */ + var $interactiveBuffer = ''; + + /** + * Current log size + * + * Should never exceed self::LOG_MAX_SIZE + * + * @see self::_send_binary_packet() + * @see self::_get_binary_packet() + * @var int + * @access private + */ + var $log_size; + + /** + * Timeout + * + * @see self::setTimeout() + * @access private + */ + var $timeout; + + /** + * Current Timeout + * + * @see self::_get_channel_packet() + * @access private + */ + var $curTimeout; + + /** + * Real-time log file pointer + * + * @see self::_append_log() + * @var resource + * @access private + */ + var $realtime_log_file; + + /** + * Real-time log file size + * + * @see self::_append_log() + * @var int + * @access private + */ + var $realtime_log_size; + + /** + * Has the signature been validated? + * + * @see self::getServerPublicHostKey() + * @var bool + * @access private + */ + var $signature_validated = false; + + /** + * Real-time log file wrap boolean + * + * @see self::_append_log() + * @access private + */ + var $realtime_log_wrap; + + /** + * Flag to suppress stderr from output + * + * @see self::enableQuietMode() + * @access private + */ + var $quiet_mode = false; + + /** + * Time of first network activity + * + * @var int + * @access private + */ + var $last_packet; + + /** + * Exit status returned from ssh if any + * + * @var int + * @access private + */ + var $exit_status; + + /** + * Flag to request a PTY when using exec() + * + * @var bool + * @see self::enablePTY() + * @access private + */ + var $request_pty = false; + + /** + * Flag set while exec() is running when using enablePTY() + * + * @var bool + * @access private + */ + var $in_request_pty_exec = false; + + /** + * Flag set after startSubsystem() is called + * + * @var bool + * @access private + */ + var $in_subsystem; + + /** + * Contents of stdError + * + * @var string + * @access private + */ + var $stdErrorLog; + + /** + * The Last Interactive Response + * + * @see self::_keyboard_interactive_process() + * @var string + * @access private + */ + var $last_interactive_response = ''; + + /** + * Keyboard Interactive Request / Responses + * + * @see self::_keyboard_interactive_process() + * @var array + * @access private + */ + var $keyboard_requests_responses = array(); + + /** + * Banner Message + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @see self::_filter() + * @see self::getBannerMessage() + * @var string + * @access private + */ + var $banner_message = ''; + + /** + * Did read() timeout or return normally? + * + * @see self::isTimeout() + * @var bool + * @access private + */ + var $is_timeout = false; + + /** + * Log Boundary + * + * @see self::_format_log() + * @var string + * @access private + */ + var $log_boundary = ':'; + + /** + * Log Long Width + * + * @see self::_format_log() + * @var int + * @access private + */ + var $log_long_width = 65; + + /** + * Log Short Width + * + * @see self::_format_log() + * @var int + * @access private + */ + var $log_short_width = 16; + + /** + * Hostname + * + * @see self::__construct() + * @see self::_connect() + * @var string + * @access private + */ + var $host; + + /** + * Port Number + * + * @see self::__construct() + * @see self::_connect() + * @var int + * @access private + */ + var $port; + + /** + * Number of columns for terminal window size + * + * @see self::getWindowColumns() + * @see self::setWindowColumns() + * @see self::setWindowSize() + * @var int + * @access private + */ + var $windowColumns = 80; + + /** + * Number of columns for terminal window size + * + * @see self::getWindowRows() + * @see self::setWindowRows() + * @see self::setWindowSize() + * @var int + * @access private + */ + var $windowRows = 24; + + /** + * Crypto Engine + * + * @see self::setCryptoEngine() + * @see self::_key_exchange() + * @var int + * @access private + */ + var $crypto_engine = false; + + /** + * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario + * + * @var System_SSH_Agent + * @access private + */ + var $agent; + + /** + * Connection storage to replicates ssh2 extension functionality: + * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} + * + * @var SSH2[] + */ + static $connections; + + /** + * Default Constructor. + * + * $host can either be a string, representing the host, or a stream resource. + * + * @param mixed $host + * @param int $port + * @param int $timeout + * @see self::login() + * @return \phpseclib\Net\SSH2 + * @access public + */ + function __construct($host, $port = 22, $timeout = 10) + { + $this->message_numbers = array( + 1 => 'NET_SSH2_MSG_DISCONNECT', + 2 => 'NET_SSH2_MSG_IGNORE', + 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', + 4 => 'NET_SSH2_MSG_DEBUG', + 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', + 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', + 20 => 'NET_SSH2_MSG_KEXINIT', + 21 => 'NET_SSH2_MSG_NEWKEYS', + 30 => 'NET_SSH2_MSG_KEXDH_INIT', + 31 => 'NET_SSH2_MSG_KEXDH_REPLY', + 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', + 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', + 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', + 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', + + 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', + 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', + 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', + 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', + 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', + 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', + 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', + 94 => 'NET_SSH2_MSG_CHANNEL_DATA', + 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', + 96 => 'NET_SSH2_MSG_CHANNEL_EOF', + 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', + 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', + 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', + 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' + ); + $this->disconnect_reasons = array( + 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', + 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', + 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', + 4 => 'NET_SSH2_DISCONNECT_RESERVED', + 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', + 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', + 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', + 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', + 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', + 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', + 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', + 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', + 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', + 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', + 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' + ); + $this->channel_open_failure_reasons = array( + 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' + ); + $this->terminal_modes = array( + 0 => 'NET_SSH2_TTY_OP_END' + ); + $this->channel_extended_data_type_codes = array( + 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' + ); + + $this->_define_array( + $this->message_numbers, + $this->disconnect_reasons, + $this->channel_open_failure_reasons, + $this->terminal_modes, + $this->channel_extended_data_type_codes, + array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'), + array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'), + array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', + 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'), + // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} + array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', + 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', + 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', + 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', + 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'), + // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) + array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', + 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') + ); + + self::$connections[$this->getResourceId()] = $this; + + if (is_resource($host)) { + $this->fsock = $host; + return; + } + + if (is_string($host)) { + $this->host = $host; + $this->port = $port; + $this->timeout = $timeout; + } + } + + /** + * Set Crypto Engine Mode + * + * Possible $engine values: + * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT + * + * @param int $engine + * @access private + */ + function setCryptoEngine($engine) + { + $this->crypto_engine = $engine; + } + + /** + * Connect to an SSHv2 server + * + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @access private + */ + function _connect() + { + if ($this->bitmap & self::MASK_CONSTRUCTOR) { + return false; + } + + $this->bitmap |= self::MASK_CONSTRUCTOR; + + $this->curTimeout = $this->timeout; + + $this->last_packet = microtime(true); + + if (!is_resource($this->fsock)) { + $start = microtime(true); + $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout); + if (!$this->fsock) { + $host = $this->host . ':' . $this->port; + throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); + } + $elapsed = microtime(true) - $start; + + $this->curTimeout-= $elapsed; + + if ($this->curTimeout <= 0) { + $this->is_timeout = true; + return false; + } + } + + /* According to the SSH2 specs, + + "The server MAY send other lines of data before sending the version + string. Each line SHOULD be terminated by a Carriage Return and Line + Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded + in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients + MUST be able to process such lines." */ + $data = ''; + while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) { + $line = ''; + while (true) { + if ($this->curTimeout) { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return false; + } + $read = array($this->fsock); + $write = $except = null; + $start = microtime(true); + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + // the !count() is done as a workaround for + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return false; + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + + $temp = stream_get_line($this->fsock, 255, "\n"); + if (strlen($temp) == 255) { + continue; + } + + $line.= "$temp\n"; + if (substr($line, -2) == "\r\n") { + break; + } + } + $data.= $line; + } + + if (feof($this->fsock)) { + throw new \RuntimeException('Connection closed by server'); + } + + $extra = $matches[1]; + + $this->identifier = $this->_generate_identifier(); + + if (defined('NET_SSH2_LOGGING')) { + $this->_append_log('<-', $matches[0]); + $this->_append_log('->', $this->identifier . "\r\n"); + } + + $this->server_identifier = trim($temp, "\r\n"); + if (strlen($extra)) { + $this->errors[] = utf8_decode($data); + } + + if ($matches[3] != '1.99' && $matches[3] != '2.0') { + throw new \RuntimeException("Cannot connect to SSH $matches[1] servers"); + } + + fputs($this->fsock, $this->identifier . "\r\n"); + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); + } + + if (!$this->_key_exchange($response)) { + return false; + } + + $this->bitmap|= self::MASK_CONNECTED; + + return true; + } + + /** + * Generates the SSH identifier + * + * You should overwrite this method in your own class if you want to use another identifier + * + * @access protected + * @return string + */ + function _generate_identifier() + { + $identifier = 'SSH-2.0-phpseclib_2.0'; + + $ext = array(); + if (extension_loaded('libsodium')) { + $ext[] = 'libsodium'; + } + + if (extension_loaded('openssl')) { + $ext[] = 'openssl'; + } elseif (extension_loaded('mcrypt')) { + $ext[] = 'mcrypt'; + } + + if (extension_loaded('gmp')) { + $ext[] = 'gmp'; + } elseif (extension_loaded('bcmath')) { + $ext[] = 'bcmath'; + } + + if (!empty($ext)) { + $identifier .= ' (' . implode(', ', $ext) . ')'; + } + + return $identifier; + } + + /** + * Key Exchange + * + * @param string $kexinit_payload_server + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible + * @access private + */ + function _key_exchange($kexinit_payload_server) + { + $kex_algorithms = array( + // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using + // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the + // libssh repository for more information. + 'curve25519-sha256@libssh.org', + + // Diffie-Hellman Key Agreement (DH) using integer modulo prime + // groups. + 'diffie-hellman-group1-sha1', // REQUIRED + 'diffie-hellman-group14-sha1', // REQUIRED + 'diffie-hellman-group-exchange-sha1', // RFC 4419 + 'diffie-hellman-group-exchange-sha256', // RFC 4419 + ); + if (!function_exists('\\Sodium\\library_version_major')) { + $kex_algorithms = array_diff( + $kex_algorithms, + array('curve25519-sha256@libssh.org') + ); + } + + $server_host_key_algorithms = array( + 'ssh-rsa', // RECOMMENDED sign Raw RSA Key + 'ssh-dss' // REQUIRED sign Raw DSS Key + ); + + $encryption_algorithms = array( + // from : + 'arcfour256', + 'arcfour128', + + //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + + // CTR modes from : + 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', // RECOMMENDED AES with 192-bit key + 'aes256-ctr', // RECOMMENDED AES with 256-bit key + + 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key + 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key + 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key + + 'aes128-cbc', // RECOMMENDED AES with a 128-bit key + 'aes192-cbc', // OPTIONAL AES with a 192-bit key + 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key + 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key + 'twofish256-cbc', + 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" + // (this is being retained for historical reasons) + + 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode + + 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode + + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + '3des-cbc', // REQUIRED three-key 3DES in CBC mode + //'none' // OPTIONAL no encryption; NOT RECOMMENDED + ); + + if (extension_loaded('openssl') && !extension_loaded('mcrypt')) { + // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to + // instances that do not use continuous buffers + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('arcfour256', 'arcfour128', 'arcfour') + ); + } + + if (class_exists('\phpseclib\Crypt\RC4') === false) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('arcfour256', 'arcfour128', 'arcfour') + ); + } + if (class_exists('\phpseclib\Crypt\Rijndael') === false) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc') + ); + } + if (class_exists('\phpseclib\Crypt\Twofish') === false) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc') + ); + } + if (class_exists('\phpseclib\Crypt\Blowfish') === false) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('blowfish-ctr', 'blowfish-cbc') + ); + } + if (class_exists('\phpseclib\Crypt\TripleDES') === false) { + $encryption_algorithms = array_diff( + $encryption_algorithms, + array('3des-ctr', '3des-cbc') + ); + } + $encryption_algorithms = array_values($encryption_algorithms); + + $mac_algorithms = array( + // from : + 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32) + + 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) + 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) + 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) + 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) + //'none' // OPTIONAL no MAC; NOT RECOMMENDED + ); + + $compression_algorithms = array( + 'none' // REQUIRED no compression + //'zlib' // OPTIONAL ZLIB (LZ77) compression + ); + + // some SSH servers have buggy implementations of some of the above algorithms + switch ($this->server_identifier) { + case 'SSH-2.0-SSHD': + $mac_algorithms = array_values(array_diff( + $mac_algorithms, + array('hmac-sha1-96', 'hmac-md5-96') + )); + } + + $str_kex_algorithms = implode(',', $kex_algorithms); + $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms); + $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms); + $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms); + $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms); + + $client_cookie = Random::string(16); + + $response = $kexinit_payload_server; + $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) + $server_cookie = $this->_string_shift($response, 16); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); + $first_kex_packet_follows = $first_kex_packet_follows != 0; + + // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. + $kexinit_payload_client = pack( + 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', + NET_SSH2_MSG_KEXINIT, + $client_cookie, + strlen($str_kex_algorithms), + $str_kex_algorithms, + strlen($str_server_host_key_algorithms), + $str_server_host_key_algorithms, + strlen($encryption_algorithms_client_to_server), + $encryption_algorithms_client_to_server, + strlen($encryption_algorithms_server_to_client), + $encryption_algorithms_server_to_client, + strlen($mac_algorithms_client_to_server), + $mac_algorithms_client_to_server, + strlen($mac_algorithms_server_to_client), + $mac_algorithms_server_to_client, + strlen($compression_algorithms_client_to_server), + $compression_algorithms_client_to_server, + strlen($compression_algorithms_server_to_client), + $compression_algorithms_server_to_client, + 0, + '', + 0, + '', + 0, + 0 + ); + + if (!$this->_send_binary_packet($kexinit_payload_client)) { + return false; + } + // here ends the second place. + + // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange + + // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the + // diffie-hellman key exchange as fast as possible + $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client); + $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); + if ($decryptKeyLength === null) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); + } + + $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server); + $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); + if ($encryptKeyLength === null) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); + } + + // through diffie-hellman key exchange a symmetric key is obtained + $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); + if ($kex_algorithm === false) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); + } + + // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. + $exchange_hash_rfc4419 = ''; + + if ($kex_algorithm === 'curve25519-sha256@libssh.org') { + $x = Random::string(32); + $eBytes = \Sodium\crypto_box_publickey_from_secretkey($x); + $clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT; + $serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY; + $kexHash = new Hash('sha256'); + } else { + if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) { + $dh_group_sizes_packed = pack( + 'NNN', + $this->kex_dh_group_size_min, + $this->kex_dh_group_size_preferred, + $this->kex_dh_group_size_max + ); + $packet = pack( + 'Ca*', + NET_SSH2_MSG_KEXDH_GEX_REQUEST, + $dh_group_sizes_packed + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + user_error('Connection closed by server'); + return false; + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) { + user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP'); + return false; + } + + extract(unpack('NprimeLength', $this->_string_shift($response, 4))); + $primeBytes = $this->_string_shift($response, $primeLength); + $prime = new BigInteger($primeBytes, -256); + + extract(unpack('NgLength', $this->_string_shift($response, 4))); + $gBytes = $this->_string_shift($response, $gLength); + $g = new BigInteger($gBytes, -256); + + $exchange_hash_rfc4419 = pack( + 'a*Na*Na*', + $dh_group_sizes_packed, + $primeLength, + $primeBytes, + $gLength, + $gBytes + ); + + $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT; + $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY; + } else { + switch ($kex_algorithm) { + // see http://tools.ietf.org/html/rfc2409#section-6.2 and + // http://tools.ietf.org/html/rfc2412, appendex E + case 'diffie-hellman-group1-sha1': + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; + break; + // see http://tools.ietf.org/html/rfc3526#section-3 + case 'diffie-hellman-group14-sha1': + $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . + '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . + '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . + '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . + '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; + break; + } + // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 + // the generator field element is 2 (decimal) and the hash function is sha1. + $g = new BigInteger(2); + $prime = new BigInteger($prime, 16); + $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT; + $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY; + } + + switch ($kex_algorithm) { + case 'diffie-hellman-group-exchange-sha256': + $kexHash = new Hash('sha256'); + break; + default: + $kexHash = new Hash('sha1'); + } + + /* To increase the speed of the key exchange, both client and server may + reduce the size of their private exponents. It should be at least + twice as long as the key material that is generated from the shared + secret. For more details, see the paper by van Oorschot and Wiener + [VAN-OORSCHOT]. + + -- http://tools.ietf.org/html/rfc4419#section-6.2 */ + $one = new BigInteger(1); + $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength)); + $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength + $max = $max->subtract($one); + + $x = BigInteger::random($one, $max); + $e = $g->modPow($x, $prime); + + $eBytes = $e->toBytes(true); + } + $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes); + + if (!$this->_send_binary_packet($data)) { + throw new \RuntimeException('Connection closed by server'); + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != $serverKexReplyMessage) { + throw new \UnexpectedValueException('Expected SSH_MSG_KEXDH_REPLY'); + } + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); + $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $fBytes = $this->_string_shift($response, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->signature = $this->_string_shift($response, $temp['length']); + + $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); + $this->signature_format = $this->_string_shift($this->signature, $temp['length']); + + if ($kex_algorithm === 'curve25519-sha256@libssh.org') { + if (strlen($fBytes) !== 32) { + user_error('Received curve25519 public key of invalid length.'); + return false; + } + $key = new BigInteger(\Sodium\crypto_scalarmult($x, $fBytes), 256); + \Sodium\memzero($x); + } else { + $f = new BigInteger($fBytes, -256); + $key = $f->modPow($x, $prime); + } + $keyBytes = $key->toBytes(true); + + $this->exchange_hash = pack( + 'Na*Na*Na*Na*Na*a*Na*Na*Na*', + strlen($this->identifier), + $this->identifier, + strlen($this->server_identifier), + $this->server_identifier, + strlen($kexinit_payload_client), + $kexinit_payload_client, + strlen($kexinit_payload_server), + $kexinit_payload_server, + strlen($this->server_public_host_key), + $this->server_public_host_key, + $exchange_hash_rfc4419, + strlen($eBytes), + $eBytes, + strlen($fBytes), + $fBytes, + strlen($keyBytes), + $keyBytes + ); + + $this->exchange_hash = $kexHash->hash($this->exchange_hash); + + if ($this->session_id === false) { + $this->session_id = $this->exchange_hash; + } + + $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); + if ($server_host_key_algorithm === false) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); + } + + if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Server Host Key Algorithm Mismatch'); + } + + $packet = pack( + 'C', + NET_SSH2_MSG_NEWKEYS + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_NEWKEYS) { + throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); + } + + $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); + + $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); + if ($this->encrypt) { + if ($this->crypto_engine) { + $this->encrypt->setEngine($this->crypto_engine); + } + if ($this->encrypt->block_size) { + $this->encrypt_block_size = $this->encrypt->block_size; + } + $this->encrypt->enableContinuousBuffer(); + $this->encrypt->disablePadding(); + + if ($this->encrypt->usesIV()) { + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); + while ($this->encrypt_block_size > strlen($iv)) { + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); + } + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); + while ($encryptKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); + } + + $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); + if ($this->decrypt) { + if ($this->crypto_engine) { + $this->decrypt->setEngine($this->crypto_engine); + } + if ($this->decrypt->block_size) { + $this->decrypt_block_size = $this->decrypt->block_size; + } + $this->decrypt->enableContinuousBuffer(); + $this->decrypt->disablePadding(); + + if ($this->decrypt->usesIV()) { + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); + while ($this->decrypt_block_size > strlen($iv)) { + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); + } + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); + while ($decryptKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); + } + + /* The "arcfour128" algorithm is the RC4 cipher, as described in + [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream + generated by the cipher MUST be discarded, and the first byte of the + first encrypted packet MUST be encrypted using the 1537th byte of + keystream. + + -- http://tools.ietf.org/html/rfc4345#section-4 */ + if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { + $this->encrypt->encrypt(str_repeat("\0", 1536)); + } + if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { + $this->decrypt->decrypt(str_repeat("\0", 1536)); + } + + $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server); + if ($mac_algorithm === false) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); + } + + $createKeyLength = 0; // ie. $mac_algorithm == 'none' + switch ($mac_algorithm) { + case 'hmac-sha2-256': + $this->hmac_create = new Hash('sha256'); + $createKeyLength = 32; + break; + case 'hmac-sha1': + $this->hmac_create = new Hash('sha1'); + $createKeyLength = 20; + break; + case 'hmac-sha1-96': + $this->hmac_create = new Hash('sha1-96'); + $createKeyLength = 20; + break; + case 'hmac-md5': + $this->hmac_create = new Hash('md5'); + $createKeyLength = 16; + break; + case 'hmac-md5-96': + $this->hmac_create = new Hash('md5-96'); + $createKeyLength = 16; + } + + $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client); + if ($mac_algorithm === false) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); + } + + $checkKeyLength = 0; + $this->hmac_size = 0; + switch ($mac_algorithm) { + case 'hmac-sha2-256': + $this->hmac_check = new Hash('sha256'); + $checkKeyLength = 32; + $this->hmac_size = 32; + break; + case 'hmac-sha1': + $this->hmac_check = new Hash('sha1'); + $checkKeyLength = 20; + $this->hmac_size = 20; + break; + case 'hmac-sha1-96': + $this->hmac_check = new Hash('sha1-96'); + $checkKeyLength = 20; + $this->hmac_size = 12; + break; + case 'hmac-md5': + $this->hmac_check = new Hash('md5'); + $checkKeyLength = 16; + $this->hmac_size = 16; + break; + case 'hmac-md5-96': + $this->hmac_check = new Hash('md5-96'); + $checkKeyLength = 16; + $this->hmac_size = 12; + } + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); + while ($createKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); + + $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); + while ($checkKeyLength > strlen($key)) { + $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); + } + $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); + + $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client); + if ($compression_algorithm === false) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); + } + $this->decompress = $compression_algorithm == 'zlib'; + + $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server); + if ($compression_algorithm === false) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); + } + $this->compress = $compression_algorithm == 'zlib'; + + return true; + } + + /** + * Maps an encryption algorithm name to the number of key bytes. + * + * @param string $algorithm Name of the encryption algorithm + * @return int|null Number of bytes as an integer or null for unknown + * @access private + */ + function _encryption_algorithm_to_key_size($algorithm) + { + switch ($algorithm) { + case 'none': + return 0; + case 'aes128-cbc': + case 'aes128-ctr': + case 'arcfour': + case 'arcfour128': + case 'blowfish-cbc': + case 'blowfish-ctr': + case 'twofish128-cbc': + case 'twofish128-ctr': + return 16; + case '3des-cbc': + case '3des-ctr': + case 'aes192-cbc': + case 'aes192-ctr': + case 'twofish192-cbc': + case 'twofish192-ctr': + return 24; + case 'aes256-cbc': + case 'aes256-ctr': + case 'arcfour256': + case 'twofish-cbc': + case 'twofish256-cbc': + case 'twofish256-ctr': + return 32; + } + return null; + } + + /** + * Maps an encryption algorithm name to an instance of a subclass of + * \phpseclib\Crypt\Base. + * + * @param string $algorithm Name of the encryption algorithm + * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown + * @access private + */ + function _encryption_algorithm_to_crypt_instance($algorithm) + { + switch ($algorithm) { + case '3des-cbc': + return new TripleDES(Base::MODE_CBC); + case '3des-ctr': + return new TripleDES(Base::MODE_CTR); + case 'aes256-cbc': + case 'aes192-cbc': + case 'aes128-cbc': + return new Rijndael(Base::MODE_CBC); + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + return new Rijndael(Base::MODE_CTR); + case 'blowfish-cbc': + return new Blowfish(Base::MODE_CBC); + case 'blowfish-ctr': + return new Blowfish(Base::MODE_CTR); + case 'twofish128-cbc': + case 'twofish192-cbc': + case 'twofish256-cbc': + case 'twofish-cbc': + return new Twofish(Base::MODE_CBC); + case 'twofish128-ctr': + case 'twofish192-ctr': + case 'twofish256-ctr': + return new Twofish(Base::MODE_CTR); + case 'arcfour': + case 'arcfour128': + case 'arcfour256': + return new RC4(); + } + return null; + } + + /** + * Login + * + * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array + * + * @param string $username + * @param mixed $password + * @param mixed $... + * @return bool + * @see self::_login() + * @access public + */ + function login($username) + { + $args = func_get_args(); + return call_user_func_array(array(&$this, '_login'), $args); + } + + /** + * Login Helper + * + * @param string $username + * @param mixed $password + * @param mixed $... + * @return bool + * @see self::_login_helper() + * @access private + */ + function _login($username) + { + if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { + if (!$this->_connect()) { + return false; + } + } + + $args = array_slice(func_get_args(), 1); + if (empty($args)) { + return $this->_login_helper($username); + } + + foreach ($args as $arg) { + if ($this->_login_helper($username, $arg)) { + return true; + } + } + return false; + } + + /** + * Login Helper + * + * @param string $username + * @param string $password + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @access private + * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages. + */ + function _login_helper($username, $password = null) + { + if (!($this->bitmap & self::MASK_CONNECTED)) { + return false; + } + + if (!($this->bitmap & self::MASK_LOGIN_REQ)) { + $packet = pack( + 'CNa*', + NET_SSH2_MSG_SERVICE_REQUEST, + strlen('ssh-userauth'), + 'ssh-userauth' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { + throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); + } + $this->bitmap |= self::MASK_LOGIN_REQ; + } + + if (strlen($this->last_interactive_response)) { + return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); + } + + if ($password instanceof RSA) { + return $this->_privatekey_login($username, $password); + } elseif ($password instanceof Agent) { + return $this->_ssh_agent_login($username, $password); + } + + if (is_array($password)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } + return false; + } + + if (!isset($password)) { + $packet = pack( + 'CNa*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('none'), + 'none' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + //case NET_SSH2_MSG_USERAUTH_FAILURE: + default: + return false; + } + } + + $packet = pack( + 'CNa*Na*Na*CNa*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('password'), + 'password', + 0, + strlen($password), + $password + ); + + // remove the username and password from the logged packet + if (!defined('NET_SSH2_LOGGING')) { + $logged = null; + } else { + $logged = pack( + 'CNa*Na*Na*CNa*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen('username'), + 'username', + strlen('ssh-connection'), + 'ssh-connection', + strlen('password'), + 'password', + 0, + strlen('password'), + 'password' + ); + } + + if (!$this->_send_binary_packet($packet, $logged)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed + if (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); + return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); + case NET_SSH2_MSG_USERAUTH_FAILURE: + // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees + // multi-factor authentication + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $auth_methods = explode(',', $this->_string_shift($response, $length)); + extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); + $partial_success = $partial_success != 0; + + if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { + if ($this->_keyboard_interactive_login($username, $password)) { + $this->bitmap |= self::MASK_LOGIN; + return true; + } + return false; + } + return false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + } + + return false; + } + + /** + * Login via keyboard-interactive authentication + * + * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. + * + * @param string $username + * @param string $password + * @return bool + * @access private + */ + function _keyboard_interactive_login($username, $password) + { + $packet = pack( + 'CNa*Na*Na*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('keyboard-interactive'), + 'keyboard-interactive', + 0, + '', + 0, + '' + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + return $this->_keyboard_interactive_process($password); + } + + /** + * Handle the keyboard-interactive requests / responses. + * + * @param string $responses... + * @return bool + * @throws \RuntimeException on connection error + * @access private + */ + function _keyboard_interactive_process() + { + $responses = func_get_args(); + + if (strlen($this->last_interactive_response)) { + $response = $this->last_interactive_response; + } else { + $orig = $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // name; may be empty + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // instruction; may be empty + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->_string_shift($response, $length); // language tag; may be empty + extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); + + for ($i = 0; $i < count($responses); $i++) { + if (is_array($responses[$i])) { + foreach ($responses[$i] as $key => $value) { + $this->keyboard_requests_responses[$key] = $value; + } + unset($responses[$i]); + } + } + $responses = array_values($responses); + + if (isset($this->keyboard_requests_responses)) { + for ($i = 0; $i < $num_prompts; $i++) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + // prompt - ie. "Password: "; must not be empty + $prompt = $this->_string_shift($response, $length); + //$echo = $this->_string_shift($response) != chr(0); + foreach ($this->keyboard_requests_responses as $key => $value) { + if (substr($prompt, 0, strlen($key)) == $key) { + $responses[] = $value; + break; + } + } + } + } + + // see http://tools.ietf.org/html/rfc4256#section-3.2 + if (strlen($this->last_interactive_response)) { + $this->last_interactive_response = ''; + } elseif (defined('NET_SSH2_LOGGING')) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + + if (!count($responses) && $num_prompts) { + $this->last_interactive_response = $orig; + return false; + } + + /* + After obtaining the requested information from the user, the client + MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. + */ + // see http://tools.ietf.org/html/rfc4256#section-3.4 + $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); + for ($i = 0; $i < count($responses); $i++) { + $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]); + $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); + } + + if (!$this->_send_binary_packet($packet, $logged)) { + return false; + } + + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE', + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + + /* + After receiving the response, the server MUST send either an + SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another + SSH_MSG_USERAUTH_INFO_REQUEST message. + */ + // maybe phpseclib should force close the connection after x request / responses? unless something like that is done + // there could be an infinite loop of request / responses. + return $this->_keyboard_interactive_process(); + case NET_SSH2_MSG_USERAUTH_SUCCESS: + return true; + case NET_SSH2_MSG_USERAUTH_FAILURE: + return false; + } + + return false; + } + + /** + * Login with an ssh-agent provided key + * + * @param string $username + * @param \phpseclib\System\SSH\Agent $agent + * @return bool + * @access private + */ + function _ssh_agent_login($username, $agent) + { + $this->agent = $agent; + $keys = $agent->requestIdentities(); + foreach ($keys as $key) { + if ($this->_privatekey_login($username, $key)) { + return true; + } + } + + return false; + } + + /** + * Login with an RSA private key + * + * @param string $username + * @param \phpseclib\Crypt\RSA $password + * @return bool + * @throws \RuntimeException on connection error + * @access private + * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} + * by sending dummy SSH_MSG_IGNORE messages. + */ + function _privatekey_login($username, $privatekey) + { + // see http://tools.ietf.org/html/rfc4253#page-15 + $publickey = $privatekey->getPublicKey('Raw'); + if ($publickey === false) { + return false; + } + + $publickey = array( + 'e' => $publickey['e']->toBytes(true), + 'n' => $publickey['n']->toBytes(true) + ); + $publickey = pack( + 'Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($publickey['e']), + $publickey['e'], + strlen($publickey['n']), + $publickey['n'] + ); + + $part1 = pack( + 'CNa*Na*Na*', + NET_SSH2_MSG_USERAUTH_REQUEST, + strlen($username), + $username, + strlen('ssh-connection'), + 'ssh-connection', + strlen('publickey'), + 'publickey' + ); + $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); + + $packet = $part1 . chr(0) . $part2; + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); + return false; + case NET_SSH2_MSG_USERAUTH_PK_OK: + // we'll just take it on faith that the public key blob and the public key algorithm name are as + // they should be + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + 'UNKNOWN', + 'NET_SSH2_MSG_USERAUTH_PK_OK', + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + } + + $packet = $part1 . chr(1) . $part2; + $privatekey->setHash('sha1'); + $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet), RSA::PADDING_PKCS1); + $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature); + $packet.= pack('Na*', strlen($signature), $signature); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + switch ($type) { + case NET_SSH2_MSG_USERAUTH_FAILURE: + // either the login is bad or the server employs multi-factor authentication + return false; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + } + + return false; + } + + /** + * Set Timeout + * + * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. + * Setting $timeout to false or 0 will mean there is no timeout. + * + * @param mixed $timeout + * @access public + */ + function setTimeout($timeout) + { + $this->timeout = $this->curTimeout = $timeout; + } + + /** + * Get the output from stdError + * + * @access public + */ + function getStdError() + { + return $this->stdErrorLog; + } + + /** + * Execute Command + * + * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually. + * In all likelihood, this is not a feature you want to be taking advantage of. + * + * @param string $command + * @param Callback $callback + * @return string + * @throws \RuntimeException on connection error + * @access public + */ + function exec($command, $callback = null) + { + $this->curTimeout = $this->timeout; + $this->is_timeout = false; + $this->stdErrorLog = ''; + + if (!($this->bitmap & self::MASK_LOGIN)) { + return false; + } + + // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to + // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, + // honestly, if you're transfering more than 2GB, you probably shouldn't be using phpseclib, anyway. + // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info + $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; + // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy + // uses 0x4000, that's what will be used here, as well. + $packet_size = 0x4000; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_EXEC, + $this->window_size_server_to_client[self::CHANNEL_EXEC], + $packet_size + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL_EXEC); + if ($response === false) { + return false; + } + + if ($this->request_pty === true) { + $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); + $packet = pack( + 'CNNa*CNa*N5a*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_EXEC], + strlen('pty-req'), + 'pty-req', + 1, + strlen('vt100'), + 'vt100', + $this->windowColumns, + $this->windowRows, + 0, + 0, + strlen($terminal_modes), + $terminal_modes + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + list(, $type) = unpack('C', $this->_string_shift($response, 1)); + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + break; + case NET_SSH2_MSG_CHANNEL_FAILURE: + default: + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to request pseudo-terminal'); + } + $this->in_request_pty_exec = true; + } + + // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things + // down. the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &'). + // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then + // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but + // neither will your script. + + // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by + // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the + // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_EXEC], + strlen('exec'), + 'exec', + 1, + strlen($command), + $command + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL_EXEC); + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; + + if ($callback === false || $this->in_request_pty_exec) { + return true; + } + + $output = ''; + while (true) { + $temp = $this->_get_channel_packet(self::CHANNEL_EXEC); + switch (true) { + case $temp === true: + return is_callable($callback) ? true : $output; + case $temp === false: + return false; + default: + if (is_callable($callback)) { + if (call_user_func($callback, $temp) === true) { + $this->_close_channel(self::CHANNEL_EXEC); + return true; + } + } else { + $output.= $temp; + } + } + } + } + + /** + * Creates an interactive shell + * + * @see self::read() + * @see self::write() + * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @access private + */ + function _initShell() + { + if ($this->in_request_pty_exec === true) { + return true; + } + + $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; + $packet_size = 0x4000; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_SHELL, + $this->window_size_server_to_client[self::CHANNEL_SHELL], + $packet_size + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); + $packet = pack( + 'CNNa*CNa*N5a*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SHELL], + strlen('pty-req'), + 'pty-req', + 1, + strlen('vt100'), + 'vt100', + $this->windowColumns, + $this->windowRows, + 0, + 0, + strlen($terminal_modes), + $terminal_modes + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + + list(, $type) = unpack('C', $this->_string_shift($response, 1)); + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + // if a pty can't be opened maybe commands can still be executed + case NET_SSH2_MSG_CHANNEL_FAILURE: + break; + default: + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \UnexpectedValueException('Unable to request pseudo-terminal'); + } + + $packet = pack( + 'CNNa*C', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SHELL], + strlen('shell'), + 'shell', + 1 + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL_SHELL); + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= self::MASK_SHELL; + + return true; + } + + /** + * Return the channel to be used with read() / write() + * + * @see self::read() + * @see self::write() + * @return int + * @access public + */ + function _get_interactive_channel() + { + switch (true) { + case $this->in_subsystem: + return self::CHANNEL_SUBSYSTEM; + case $this->in_request_pty_exec: + return self::CHANNEL_EXEC; + default: + return self::CHANNEL_SHELL; + } + } + + /** + * Return an available open channel + * + * @return int + * @access public + */ + function _get_open_channel() + { + $channel = self::CHANNEL_EXEC; + do { + if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { + return $channel; + } + } while ($channel++ < self::CHANNEL_SUBSYSTEM); + + return false; + } + + /** + * Returns the output of an interactive shell + * + * Returns when there's a match for $expect, which can take the form of a string literal or, + * if $mode == self::READ_REGEX, a regular expression. + * + * @see self::write() + * @param string $expect + * @param int $mode + * @return string + * @throws \RuntimeException on connection error + * @access public + */ + function read($expect = '', $mode = self::READ_SIMPLE) + { + $this->curTimeout = $this->timeout; + $this->is_timeout = false; + + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + + $channel = $this->_get_interactive_channel(); + + $match = $expect; + while (true) { + if ($mode == self::READ_REGEX) { + preg_match($expect, substr($this->interactiveBuffer, -1024), $matches); + $match = isset($matches[0]) ? $matches[0] : ''; + } + $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; + if ($pos !== false) { + return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); + } + $response = $this->_get_channel_packet($channel); + if (is_bool($response)) { + $this->in_request_pty_exec = false; + return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; + } + + $this->interactiveBuffer.= $response; + } + } + + /** + * Inputs a command into an interactive shell. + * + * @see self::read() + * @param string $cmd + * @return bool + * @throws \RuntimeException on connection error + * @access public + */ + function write($cmd) + { + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); + } + + if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { + throw new \RuntimeException('Unable to initiate an interactive shell session'); + } + + return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); + } + + /** + * Start a subsystem. + * + * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept + * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. + * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and + * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented + * if there's sufficient demand for such a feature. + * + * @see self::stopSubsystem() + * @param string $subsystem + * @return bool + * @access public + */ + function startSubsystem($subsystem) + { + $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; + + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_SUBSYSTEM, + $this->window_size, + 0x4000 + ); + + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); + if ($response === false) { + return false; + } + + $packet = pack( + 'CNNa*CNa*', + NET_SSH2_MSG_CHANNEL_REQUEST, + $this->server_channels[self::CHANNEL_SUBSYSTEM], + strlen('subsystem'), + 'subsystem', + 1, + strlen($subsystem), + $subsystem + ); + if (!$this->_send_binary_packet($packet)) { + return false; + } + + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; + + $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); + + if ($response === false) { + return false; + } + + $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; + + $this->bitmap |= self::MASK_SHELL; + $this->in_subsystem = true; + + return true; + } + + /** + * Stops a subsystem. + * + * @see self::startSubsystem() + * @return bool + * @access public + */ + function stopSubsystem() + { + $this->in_subsystem = false; + $this->_close_channel(self::CHANNEL_SUBSYSTEM); + return true; + } + + /** + * Closes a channel + * + * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call + * + * @access public + */ + function reset() + { + $this->_close_channel($this->_get_interactive_channel()); + } + + /** + * Is timeout? + * + * Did exec() or read() return because they timed out or because they encountered the end? + * + * @access public + */ + function isTimeout() + { + return $this->is_timeout; + } + + /** + * Disconnect + * + * @access public + */ + function disconnect() + { + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { + fclose($this->realtime_log_file); + } + unset(self::$connections[$this->getResourceId()]); + } + + /** + * Destructor. + * + * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call + * disconnect(). + * + * @access public + */ + function __destruct() + { + $this->disconnect(); + } + + /** + * Is the connection still active? + * + * @return bool + * @access public + */ + function isConnected() + { + return (bool) ($this->bitmap & self::MASK_CONNECTED); + } + + /** + * Have you successfully been logged in? + * + * @return bool + * @access public + */ + function isAuthenticated() + { + return (bool) ($this->bitmap & self::MASK_LOGIN); + } + + /** + * Gets Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @see self::_send_binary_packet() + * @return string + * @throws \RuntimeException on connection errors + * @access private + */ + function _get_binary_packet() + { + if (!is_resource($this->fsock) || feof($this->fsock)) { + $this->bitmap = 0; + throw new \RuntimeException('Connection closed prematurely'); + } + + $start = microtime(true); + $raw = stream_get_contents($this->fsock, $this->decrypt_block_size); + + if (!strlen($raw)) { + return ''; + } + + if ($this->decrypt !== false) { + $raw = $this->decrypt->decrypt($raw); + } + if ($raw === false) { + throw new \RuntimeException('Unable to decrypt content'); + } + + extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); + + $remaining_length = $packet_length + 4 - $this->decrypt_block_size; + + // quoting , + // "implementations SHOULD check that the packet length is reasonable" + // PuTTY uses 0x9000 as the actual max packet size and so to shall we + if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { + throw new \RuntimeException('Invalid size'); + } + + $buffer = ''; + while ($remaining_length > 0) { + $temp = stream_get_contents($this->fsock, $remaining_length); + if ($temp === false || feof($this->fsock)) { + $this->bitmap = 0; + throw new \RuntimeException('Error reading from socket'); + } + $buffer.= $temp; + $remaining_length-= strlen($temp); + } + $stop = microtime(true); + if (strlen($buffer)) { + $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; + } + + $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1); + $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty + + if ($this->hmac_check !== false) { + $hmac = stream_get_contents($this->fsock, $this->hmac_size); + if ($hmac === false || strlen($hmac) != $this->hmac_size) { + $this->bitmap = 0; + throw new \RuntimeException('Error reading socket'); + } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { + throw new \RuntimeException('Invalid HMAC'); + } + } + + //if ($this->decompress) { + // $payload = gzinflate(substr($payload, 2)); + //} + + $this->get_seq_no++; + + if (defined('NET_SSH2_LOGGING')) { + $current = microtime(true); + $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; + $message_number = '<- ' . $message_number . + ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; + $this->_append_log($message_number, $payload); + $this->last_packet = $current; + } + + return $this->_filter($payload); + } + + /** + * Filter Binary Packets + * + * Because some binary packets need to be ignored... + * + * @see self::_get_binary_packet() + * @return string + * @access private + */ + function _filter($payload) + { + switch (ord($payload[0])) { + case NET_SSH2_MSG_DISCONNECT: + $this->_string_shift($payload, 1); + extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); + $this->bitmap = 0; + return false; + case NET_SSH2_MSG_IGNORE: + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_DEBUG: + $this->_string_shift($payload, 2); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length)); + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_UNIMPLEMENTED: + return false; + case NET_SSH2_MSG_KEXINIT: + if ($this->session_id !== false) { + if (!$this->_key_exchange($payload)) { + $this->bitmap = 0; + return false; + } + $payload = $this->_get_binary_packet(); + } + } + + // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in + if (($this->bitmap & self::MASK_CONNECTED) && !($this->bitmap & self::MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + $this->_string_shift($payload, 1); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); + $payload = $this->_get_binary_packet(); + } + + // only called when we've already logged in + if (($this->bitmap & self::MASK_CONNECTED) && ($this->bitmap & self::MASK_LOGIN)) { + switch (ord($payload[0])) { + case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length); + + if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) { + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 + $this->_string_shift($payload, 1); + extract(unpack('Nlength', $this->_string_shift($payload, 4))); + $data = $this->_string_shift($payload, $length); + extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); + switch ($data) { + case 'auth-agent': + case 'auth-agent@openssh.com': + if (isset($this->agent)) { + $new_channel = self::CHANNEL_AGENT_FORWARD; + + extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4))); + extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4))); + + $this->packet_size_client_to_server[$new_channel] = $remote_window_size; + $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size; + $this->window_size_client_to_server[$new_channel] = $this->window_size; + + $packet_size = 0x4000; + + $packet = pack( + 'CN4', + NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, + $server_channel, + $new_channel, + $packet_size, + $packet_size + ); + + $this->server_channels[$new_channel] = $server_channel; + $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; + if (!$this->_send_binary_packet($packet)) { + return false; + } + } + break; + default: + $packet = pack( + 'CN3a*Na*', + NET_SSH2_MSG_REQUEST_FAILURE, + $server_channel, + NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, + 0, + '', + 0, + '' + ); + + if (!$this->_send_binary_packet($packet)) { + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + } + $payload = $this->_get_binary_packet(); + break; + case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: + $this->_string_shift($payload, 1); + extract(unpack('Nchannel', $this->_string_shift($payload, 4))); + extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); + $this->window_size_client_to_server[$channel]+= $window_size; + + $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); + } + } + + return $payload; + } + + /** + * Enable Quiet Mode + * + * Suppress stderr from output + * + * @access public + */ + function enableQuietMode() + { + $this->quiet_mode = true; + } + + /** + * Disable Quiet Mode + * + * Show stderr in output + * + * @access public + */ + function disableQuietMode() + { + $this->quiet_mode = false; + } + + /** + * Returns whether Quiet Mode is enabled or not + * + * @see self::enableQuietMode() + * @see self::disableQuietMode() + * @access public + * @return bool + */ + function isQuietModeEnabled() + { + return $this->quiet_mode; + } + + /** + * Enable request-pty when using exec() + * + * @access public + */ + function enablePTY() + { + $this->request_pty = true; + } + + /** + * Disable request-pty when using exec() + * + * @access public + */ + function disablePTY() + { + $this->request_pty = false; + } + + /** + * Returns whether request-pty is enabled or not + * + * @see self::enablePTY() + * @see self::disablePTY() + * @access public + * @return bool + */ + function isPTYEnabled() + { + return $this->request_pty; + } + + /** + * Gets channel data + * + * Returns the data as a string if it's available and false if not. + * + * @param $client_channel + * @return mixed + * @throws \RuntimeException on connection error + * @access private + */ + function _get_channel_packet($client_channel, $skip_extended = false) + { + if (!empty($this->channel_buffers[$client_channel])) { + return array_shift($this->channel_buffers[$client_channel]); + } + + while (true) { + if ($this->curTimeout) { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + + $read = array($this->fsock); + $write = $except = null; + + $start = microtime(true); + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return true; + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } + if ($client_channel == -1 && $response === true) { + return true; + } + if (!strlen($response)) { + return ''; + } + + extract(unpack('Ctype', $this->_string_shift($response, 1))); + + if ($type == NET_SSH2_MSG_CHANNEL_OPEN) { + extract(unpack('Nlength', $this->_string_shift($response, 4))); + } else { + extract(unpack('Nchannel', $this->_string_shift($response, 4))); + } + + // will not be setup yet on incoming channel open request + if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) { + $this->window_size_server_to_client[$channel]-= strlen($response); + + // resize the window, if appropriate + if ($this->window_size_server_to_client[$channel] < 0) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size); + if (!$this->_send_binary_packet($packet)) { + return false; + } + $this->window_size_server_to_client[$channel]+= $this->window_size; + } + + switch ($this->channel_status[$channel]) { + case NET_SSH2_MSG_CHANNEL_OPEN: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); + $this->server_channels[$channel] = $server_channel; + extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); + if ($window_size < 0) { + $window_size&= 0x7FFFFFFF; + $window_size+= 0x80000000; + } + $this->window_size_client_to_server[$channel] = $window_size; + $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); + $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; + $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); + $this->_on_channel_open(); + return $result; + //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: + default: + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to open channel'); + } + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + switch ($type) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + return true; + case NET_SSH2_MSG_CHANNEL_FAILURE: + return false; + default: + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to fulfill channel request'); + } + case NET_SSH2_MSG_CHANNEL_CLOSE: + return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); + } + } + + // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_DATA: + /* + if ($channel == self::CHANNEL_EXEC) { + // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server + // this actually seems to make things twice as fast. more to the point, the message right after + // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. + // in OpenSSH it slows things down but only by a couple thousandths of a second. + $this->_send_channel_packet($channel, chr(0)); + } + */ + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $data = $this->_string_shift($response, $length); + + if ($channel == self::CHANNEL_AGENT_FORWARD) { + $agent_response = $this->agent->_forward_data($data); + if (!is_bool($agent_response)) { + $this->_send_channel_packet($channel, $agent_response); + } + break; + } + + if ($client_channel == $channel) { + return $data; + } + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); + } + $this->channel_buffers[$channel][] = $data; + break; + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + /* + if ($client_channel == self::CHANNEL_EXEC) { + $this->_send_channel_packet($client_channel, chr(0)); + } + */ + // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR + extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); + $data = $this->_string_shift($response, $length); + $this->stdErrorLog.= $data; + if ($skip_extended || $this->quiet_mode) { + break; + } + if ($client_channel == $channel) { + return $data; + } + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); + } + $this->channel_buffers[$channel][] = $data; + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + switch ($value) { + case 'exit-signal': + $this->_string_shift($response, 1); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); + $this->_string_shift($response, 1); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if ($length) { + $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); + } + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; + case 'exit-status': + extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); + $this->exit_status = $exit_status; + + // "The client MAY ignore these messages." + // -- http://tools.ietf.org/html/rfc4254#section-6.10 + + break; + default: + // "Some systems may not implement signals, in which case they SHOULD ignore this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.9 + break; + } + break; + case NET_SSH2_MSG_CHANNEL_CLOSE: + $this->curTimeout = 0; + + if ($this->bitmap & self::MASK_SHELL) { + $this->bitmap&= ~self::MASK_SHELL; + } + if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + } + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + return true; + case NET_SSH2_MSG_CHANNEL_EOF: + break; + default: + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Error reading channel data'); + } + } + } + + /** + * Sends Binary Packets + * + * See '6. Binary Packet Protocol' of rfc4253 for more info. + * + * @param string $data + * @param string $logged + * @see self::_get_binary_packet() + * @return bool + * @access private + */ + function _send_binary_packet($data, $logged = null) + { + if (!is_resource($this->fsock) || feof($this->fsock)) { + $this->bitmap = 0; + throw new \RuntimeException('Connection closed prematurely'); + } + + //if ($this->compress) { + // // the -4 removes the checksum: + // // http://php.net/function.gzcompress#57710 + // $data = substr(gzcompress($data), 0, -4); + //} + + // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 + $packet_length = strlen($data) + 9; + // round up to the nearest $this->encrypt_block_size + $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; + // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length + $padding_length = $packet_length - strlen($data) - 5; + $padding = Random::string($padding_length); + + // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself + $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); + + $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : ''; + $this->send_seq_no++; + + if ($this->encrypt !== false) { + $packet = $this->encrypt->encrypt($packet); + } + + $packet.= $hmac; + + $start = microtime(true); + $result = strlen($packet) == fputs($this->fsock, $packet); + $stop = microtime(true); + + if (defined('NET_SSH2_LOGGING')) { + $current = microtime(true); + $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')'; + $message_number = '-> ' . $message_number . + ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; + $this->_append_log($message_number, isset($logged) ? $logged : $data); + $this->last_packet = $current; + } + + return $result; + } + + /** + * Logs data packets + * + * Makes sure that only the last 1MB worth of packets will be logged + * + * @param string $data + * @access private + */ + function _append_log($message_number, $message) + { + // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) + if (strlen($message_number) > 2) { + $this->_string_shift($message); + } + + switch (NET_SSH2_LOGGING) { + // useful for benchmarks + case self::LOG_SIMPLE: + $this->message_number_log[] = $message_number; + break; + // the most useful log for SSH2 + case self::LOG_COMPLEX: + $this->message_number_log[] = $message_number; + $this->log_size+= strlen($message); + $this->message_log[] = $message; + while ($this->log_size > self::LOG_MAX_SIZE) { + $this->log_size-= strlen(array_shift($this->message_log)); + array_shift($this->message_number_log); + } + break; + // dump the output out realtime; packets may be interspersed with non packets, + // passwords won't be filtered out and select other packets may not be correctly + // identified + case self::LOG_REALTIME: + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                        $stop = '
'; + } + echo $start . $this->_format_log(array($message), array($message_number)) . $stop; + @flush(); + @ob_flush(); + break; + // basically the same thing as self::LOG_REALTIME with the caveat that NET_SFTP_LOG_REALTIME_FILENAME + // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. + // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily + // at the beginning of the file + case self::LOG_REALTIME_FILE: + if (!isset($this->realtime_log_file)) { + // PHP doesn't seem to like using constants in fopen() + $filename = NET_SSH2_LOG_REALTIME_FILENAME; + $fp = fopen($filename, 'w'); + $this->realtime_log_file = $fp; + } + if (!is_resource($this->realtime_log_file)) { + break; + } + $entry = $this->_format_log(array($message), array($message_number)); + if ($this->realtime_log_wrap) { + $temp = "<<< START >>>\r\n"; + $entry.= $temp; + fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); + } + $this->realtime_log_size+= strlen($entry); + if ($this->realtime_log_size > self::LOG_MAX_SIZE) { + fseek($this->realtime_log_file, 0); + $this->realtime_log_size = strlen($entry); + $this->realtime_log_wrap = true; + } + fputs($this->realtime_log_file, $entry); + } + } + + /** + * Sends channel data + * + * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate + * + * @param int $client_channel + * @param string $data + * @return bool + * @access private + */ + function _send_channel_packet($client_channel, $data) + { + while (strlen($data)) { + if (!$this->window_size_client_to_server[$client_channel]) { + $this->bitmap^= self::MASK_WINDOW_ADJUST; + // using an invalid channel will let the buffers be built up for the valid channels + $this->_get_channel_packet(-1); + $this->bitmap^= self::MASK_WINDOW_ADJUST; + } + + /* The maximum amount of data allowed is determined by the maximum + packet size for the channel, and the current window size, whichever + is smaller. + -- http://tools.ietf.org/html/rfc4254#section-5.2 */ + $max_size = min( + $this->packet_size_client_to_server[$client_channel], + $this->window_size_client_to_server[$client_channel] + ); + + $temp = $this->_string_shift($data, $max_size); + $packet = pack( + 'CN2a*', + NET_SSH2_MSG_CHANNEL_DATA, + $this->server_channels[$client_channel], + strlen($temp), + $temp + ); + $this->window_size_client_to_server[$client_channel]-= strlen($temp); + if (!$this->_send_binary_packet($packet)) { + return false; + } + } + + return true; + } + + /** + * Closes and flushes a channel + * + * \phpseclib\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server + * and for SFTP channels are presumably closed when the client disconnects. This functions is intended + * for SCP more than anything. + * + * @param int $client_channel + * @param bool $want_reply + * @return bool + * @access private + */ + function _close_channel($client_channel, $want_reply = false) + { + // see http://tools.ietf.org/html/rfc4254#section-5.3 + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + + if (!$want_reply) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + } + + $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; + + $this->curTimeout = 0; + + while (!is_bool($this->_get_channel_packet($client_channel))) { + } + + if ($want_reply) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); + } + + if ($this->bitmap & self::MASK_SHELL) { + $this->bitmap&= ~self::MASK_SHELL; + } + } + + /** + * Disconnect + * + * @param int $reason + * @return bool + * @access private + */ + function _disconnect($reason) + { + if ($this->bitmap & self::MASK_CONNECTED) { + $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, ''); + $this->_send_binary_packet($data); + $this->bitmap = 0; + fclose($this->fsock); + return false; + } + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; + } + + /** + * Define Array + * + * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of + * named constants from it, using the value as the name of the constant and the index as the value of the constant. + * If any of the constants that would be defined already exists, none of the constants will be defined. + * + * @param array $array + * @access private + */ + function _define_array() + { + $args = func_get_args(); + foreach ($args as $arg) { + foreach ($arg as $key => $value) { + if (!defined($value)) { + define($value, $key); + } else { + break 2; + } + } + } + } + + /** + * Returns a log of the packets that have been sent and received. + * + * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') + * + * @access public + * @return array|false|string + */ + function getLog() + { + if (!defined('NET_SSH2_LOGGING')) { + return false; + } + + switch (NET_SSH2_LOGGING) { + case self::LOG_SIMPLE: + return $this->message_number_log; + break; + case self::LOG_COMPLEX: + return $this->_format_log($this->message_log, $this->message_number_log); + break; + default: + return false; + } + } + + /** + * Formats a log for printing + * + * @param array $message_log + * @param array $message_number_log + * @access private + * @return string + */ + function _format_log($message_log, $message_number_log) + { + $output = ''; + for ($i = 0; $i < count($message_log); $i++) { + $output.= $message_number_log[$i] . "\r\n"; + $current_log = $message_log[$i]; + $j = 0; + do { + if (strlen($current_log)) { + $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; + } + $fragment = $this->_string_shift($current_log, $this->log_short_width); + $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); + // replace non ASCII printable characters with dots + // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters + // also replace < with a . since < messes up the output on web browsers + $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); + $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; + $j++; + } while (strlen($current_log)); + $output.= "\r\n"; + } + + return $output; + } + + /** + * Helper function for _format_log + * + * For use with preg_replace_callback() + * + * @param array $matches + * @access private + * @return string + */ + function _format_log_helper($matches) + { + return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); + } + + /** + * Helper function for agent->_on_channel_open() + * + * Used when channels are created to inform agent + * of said channel opening. Must be called after + * channel open confirmation received + * + * @access private + */ + function _on_channel_open() + { + if (isset($this->agent)) { + $this->agent->_on_channel_open($this); + } + } + + /** + * Returns the first value of the intersection of two arrays or false if + * the intersection is empty. The order is defined by the first parameter. + * + * @param array $array1 + * @param array $array2 + * @return mixed False if intersection is empty, else intersected value. + * @access private + */ + function _array_intersect_first($array1, $array2) + { + foreach ($array1 as $value) { + if (in_array($value, $array2)) { + return $value; + } + } + return false; + } + + /** + * Returns all errors + * + * @return string[] + * @access public + */ + function getErrors() + { + return $this->errors; + } + + /** + * Returns the last error + * + * @return string + * @access public + */ + function getLastError() + { + $count = count($this->errors); + + if ($count > 0) { + return $this->errors[$count - 1]; + } + } + + /** + * Return the server identification. + * + * @return string + * @access public + */ + function getServerIdentification() + { + $this->_connect(); + + return $this->server_identifier; + } + + /** + * Return a list of the key exchange algorithms the server supports. + * + * @return array + * @access public + */ + function getKexAlgorithms() + { + $this->_connect(); + + return $this->kex_algorithms; + } + + /** + * Return a list of the host key (public key) algorithms the server supports. + * + * @return array + * @access public + */ + function getServerHostKeyAlgorithms() + { + $this->_connect(); + + return $this->server_host_key_algorithms; + } + + /** + * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getEncryptionAlgorithmsClient2Server() + { + $this->_connect(); + + return $this->encryption_algorithms_client_to_server; + } + + /** + * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getEncryptionAlgorithmsServer2Client() + { + $this->_connect(); + + return $this->encryption_algorithms_server_to_client; + } + + /** + * Return a list of the MAC algorithms the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getMACAlgorithmsClient2Server() + { + $this->_connect(); + + return $this->mac_algorithms_client_to_server; + } + + /** + * Return a list of the MAC algorithms the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getMACAlgorithmsServer2Client() + { + $this->_connect(); + + return $this->mac_algorithms_server_to_client; + } + + /** + * Return a list of the compression algorithms the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getCompressionAlgorithmsClient2Server() + { + $this->_connect(); + + return $this->compression_algorithms_client_to_server; + } + + /** + * Return a list of the compression algorithms the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getCompressionAlgorithmsServer2Client() + { + $this->_connect(); + + return $this->compression_algorithms_server_to_client; + } + + /** + * Return a list of the languages the server supports, when sending stuff to the client. + * + * @return array + * @access public + */ + function getLanguagesServer2Client() + { + $this->_connect(); + + return $this->languages_server_to_client; + } + + /** + * Return a list of the languages the server supports, when receiving stuff from the client. + * + * @return array + * @access public + */ + function getLanguagesClient2Server() + { + $this->_connect(); + + return $this->languages_client_to_server; + } + + /** + * Returns the banner message. + * + * Quoting from the RFC, "in some jurisdictions, sending a warning message before + * authentication may be relevant for getting legal protection." + * + * @return string + * @access public + */ + function getBannerMessage() + { + return $this->banner_message; + } + + /** + * 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 + * @throws \RuntimeException on badly formatted keys + * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format + * @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) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); + } + + $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: + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); + } + + $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, "\0")); + + /* + $temp = unpack('Nlength', $this->_string_shift($signature, 4)); + $signature = $this->_string_shift($signature, $temp['length']); + + $rsa = new RSA(); + $rsa->load(array('e' => $e, 'n' => $n), 'raw'); + $rsa->setHash('sha1'); + if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) { + //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) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); + } + + $s = $s->modPow($e, $n); + $s = $s->toBytes(); + + $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash)); + $h = chr(0x01) . 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: + $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + throw new NoSupportedAlgorithmsException('Unsupported signature format'); + } + + return $this->signature_format . ' ' . Base64::encode($this->server_public_host_key); + } + + /** + * Returns the exit status of an SSH command or false. + * + * @return false|int + * @access public + */ + function getExitStatus() + { + if (is_null($this->exit_status)) { + return false; + } + return $this->exit_status; + } + + /** + * Returns the number of columns for the terminal window size. + * + * @return int + * @access public + */ + function getWindowColumns() + { + return $this->windowColumns; + } + + /** + * Returns the number of rows for the terminal window size. + * + * @return int + * @access public + */ + function getWindowRows() + { + return $this->windowRows; + } + + /** + * Sets the number of columns for the terminal window size. + * + * @param int $value + * @access public + */ + function setWindowColumns($value) + { + $this->windowColumns = $value; + } + + /** + * Sets the number of rows for the terminal window size. + * + * @param int $value + * @access public + */ + function setWindowRows($value) + { + $this->windowRows = $value; + } + + /** + * Sets the number of columns and rows for the terminal window size. + * + * @param int $columns + * @param int $rows + * @access public + */ + function setWindowSize($columns = 80, $rows = 24) + { + $this->windowColumns = $columns; + $this->windowRows = $rows; + } + + /** + * @return string + */ + function __toString() + { + return $this->getResourceId(); + } + + /** + * We use {} because that symbols should not be in URL according to + * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}. + * It will safe us from any conflicts, because otherwise regexp will + * match all alphanumeric domains. + * + * @return string + */ + function getResourceId() + { + return '{' . spl_object_hash($this) . '}'; + } + + /** + * Return existing connection + * + * @param string $id + * + * @return bool|SSH2 will return false if no such connection + */ + static function getConnectionByResourceId($id) + { + return isset(self::$connections[$id]) ? self::$connections[$id] : false; + } + + /** + * Return all excising connections + * + * @return SSH2[] + */ + static function getConnections() + { + return self::$connections; + } +} diff --git a/src/phpseclib/System/SSH/Agent.php b/src/phpseclib/System/SSH/Agent.php new file mode 100755 index 00000000..23bf027a --- /dev/null +++ b/src/phpseclib/System/SSH/Agent.php @@ -0,0 +1,313 @@ + + * login('username', $agent)) { + * exit('Login Failed'); + * } + * + * echo $ssh->exec('pwd'); + * echo $ssh->exec('ls -la'); + * ?> + * + * + * @category System + * @package SSH\Agent + * @author Jim Wigginton + * @copyright 2014 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + * @internal See http://api.libssh.org/rfc/PROTOCOL.agent + */ + +namespace phpseclib\System\SSH; + +use ParagonIE\ConstantTime\Base64; +use phpseclib\Crypt\RSA; +use phpseclib\Exception\BadConfigurationException; +use phpseclib\System\SSH\Agent\Identity; + +/** + * Pure-PHP ssh-agent client identity factory + * + * requestIdentities() method pumps out \phpseclib\System\SSH\Agent\Identity objects + * + * @package SSH\Agent + * @author Jim Wigginton + * @access internal + */ +class Agent +{ + /**#@+ + * Message numbers + * + * @access private + */ + // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1) + const SSH_AGENTC_REQUEST_IDENTITIES = 11; + // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2). + const SSH_AGENT_IDENTITIES_ANSWER = 12; + // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3) + const SSH_AGENTC_SIGN_REQUEST = 13; + // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4) + const SSH_AGENT_SIGN_RESPONSE = 14; + /**#@-*/ + + /**@+ + * Agent forwarding status + * + * @access private + */ + // no forwarding requested and not active + const FORWARD_NONE = 0; + // request agent forwarding when opportune + const FORWARD_REQUEST = 1; + // forwarding has been request and is active + const FORWARD_ACTIVE = 2; + /**#@-*/ + + /** + * Unused + */ + const SSH_AGENT_FAILURE = 5; + + /** + * Socket Resource + * + * @var resource + * @access private + */ + var $fsock; + + /** + * Agent forwarding status + * + * @access private + */ + var $forward_status = self::FORWARD_NONE; + + /** + * Buffer for accumulating forwarded authentication + * agent data arriving on SSH data channel destined + * for agent unix socket + * + * @access private + */ + var $socket_buffer = ''; + + /** + * Tracking the number of bytes we are expecting + * to arrive for the agent socket on the SSH data + * channel + */ + var $expected_bytes = 0; + + /** + * Default Constructor + * + * @return \phpseclib\System\SSH\Agent + * @throws \phpseclib\Exception\BadConfigurationException if SSH_AUTH_SOCK cannot be found + * @throws \RuntimeException on connection errors + * @access public + */ + function __construct() + { + switch (true) { + case isset($_SERVER['SSH_AUTH_SOCK']): + $address = $_SERVER['SSH_AUTH_SOCK']; + break; + case isset($_ENV['SSH_AUTH_SOCK']): + $address = $_ENV['SSH_AUTH_SOCK']; + break; + default: + throw new BadConfigurationException('SSH_AUTH_SOCK not found'); + } + + $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); + if (!$this->fsock) { + throw new \RuntimeException("Unable to connect to ssh-agent (Error $errno: $errstr)"); + } + } + + /** + * Request Identities + * + * See "2.5.2 Requesting a list of protocol 2 keys" + * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects + * + * @return array + * @throws \RuntimeException on receipt of unexpected packets + * @access public + */ + function requestIdentities() + { + if (!$this->fsock) { + return array(); + } + + $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); + if (strlen($packet) != fputs($this->fsock, $packet)) { + throw new \RuntimeException('Connection closed while requesting identities'); + } + + $length = current(unpack('N', fread($this->fsock, 4))); + $type = ord(fread($this->fsock, 1)); + if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { + throw new \RuntimeException('Unable to request identities'); + } + + $identities = array(); + $keyCount = current(unpack('N', fread($this->fsock, 4))); + for ($i = 0; $i < $keyCount; $i++) { + $length = current(unpack('N', fread($this->fsock, 4))); + $key_blob = fread($this->fsock, $length); + $key_str = 'ssh-rsa ' . Base64::encode($key_blob); + $length = current(unpack('N', fread($this->fsock, 4))); + if ($length) { + $key_str.= ' ' . fread($this->fsock, $length); + } + $length = current(unpack('N', substr($key_blob, 0, 4))); + $key_type = substr($key_blob, 4, $length); + switch ($key_type) { + case 'ssh-rsa': + $key = new RSA(); + $key->load($key_str); + break; + case 'ssh-dss': + // not currently supported + break; + } + // resources are passed by reference by default + if (isset($key)) { + $identity = new Identity($this->fsock); + $identity->setPublicKey($key); + $identity->setPublicKeyBlob($key_blob); + $identities[] = $identity; + unset($key); + } + } + + return $identities; + } + + /** + * Signal that agent forwarding should + * be requested when a channel is opened + * + * @param Net_SSH2 $ssh + * @return bool + * @access public + */ + function startSSHForwarding($ssh) + { + if ($this->forward_status == self::FORWARD_NONE) { + $this->forward_status = self::FORWARD_REQUEST; + } + } + + /** + * Request agent forwarding of remote server + * + * @param Net_SSH2 $ssh + * @return bool + * @access private + */ + function _request_forwarding($ssh) + { + $request_channel = $ssh->_get_open_channel(); + if ($request_channel === false) { + return false; + } + + $packet = pack( + 'CNNa*C', + NET_SSH2_MSG_CHANNEL_REQUEST, + $ssh->server_channels[$request_channel], + strlen('auth-agent-req@openssh.com'), + 'auth-agent-req@openssh.com', + 1 + ); + + $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; + + if (!$ssh->_send_binary_packet($packet)) { + return false; + } + + $response = $ssh->_get_channel_packet($request_channel); + if ($response === false) { + return false; + } + + $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; + $this->forward_status = self::FORWARD_ACTIVE; + + return true; + } + + /** + * On successful channel open + * + * This method is called upon successful channel + * open to give the SSH Agent an opportunity + * to take further action. i.e. request agent forwarding + * + * @param Net_SSH2 $ssh + * @access private + */ + function _on_channel_open($ssh) + { + if ($this->forward_status == self::FORWARD_REQUEST) { + $this->_request_forwarding($ssh); + } + } + + /** + * Forward data to SSH Agent and return data reply + * + * @param string $data + * @return data from SSH Agent + * @throws \RuntimeException on connection errors + * @access private + */ + function _forward_data($data) + { + if ($this->expected_bytes > 0) { + $this->socket_buffer.= $data; + $this->expected_bytes -= strlen($data); + } else { + $agent_data_bytes = current(unpack('N', $data)); + $current_data_bytes = strlen($data); + $this->socket_buffer = $data; + if ($current_data_bytes != $agent_data_bytes + 4) { + $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; + return false; + } + } + + if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { + throw new \RuntimeException('Connection closed attempting to forward data to SSH agent'); + } + + $this->socket_buffer = ''; + $this->expected_bytes = 0; + + $agent_reply_bytes = current(unpack('N', fread($this->fsock, 4))); + + $agent_reply_data = fread($this->fsock, $agent_reply_bytes); + $agent_reply_data = current(unpack('a*', $agent_reply_data)); + + return pack('Na*', $agent_reply_bytes, $agent_reply_data); + } +} diff --git a/src/phpseclib/System/SSH/Agent/Identity.php b/src/phpseclib/System/SSH/Agent/Identity.php new file mode 100755 index 00000000..612c414e --- /dev/null +++ b/src/phpseclib/System/SSH/Agent/Identity.php @@ -0,0 +1,170 @@ + + * @copyright 2009 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://phpseclib.sourceforge.net + * @internal See http://api.libssh.org/rfc/PROTOCOL.agent + */ + +namespace phpseclib\System\SSH\Agent; + +use phpseclib\Crypt\RSA; +use phpseclib\Exception\UnsupportedAlgorithmException; +use phpseclib\System\SSH\Agent; + +/** + * Pure-PHP ssh-agent client identity object + * + * Instantiation should only be performed by \phpseclib\System\SSH\Agent class. + * This could be thought of as implementing an interface that phpseclib\Crypt\RSA + * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something. + * The methods in this interface would be getPublicKey and sign since those are the + * methods phpseclib looks for to perform public key authentication. + * + * @package SSH\Agent + * @author Jim Wigginton + * @access internal + */ +class Identity +{ + /** + * Key Object + * + * @var \phpseclib\Crypt\RSA + * @access private + * @see self::getPublicKey() + */ + var $key; + + /** + * Key Blob + * + * @var string + * @access private + * @see self::sign() + */ + var $key_blob; + + /** + * Socket Resource + * + * @var resource + * @access private + * @see self::sign() + */ + var $fsock; + + /** + * Default Constructor. + * + * @param resource $fsock + * @return \phpseclib\System\SSH\Agent\Identity + * @access private + */ + function __construct($fsock) + { + $this->fsock = $fsock; + } + + /** + * Set Public Key + * + * Called by \phpseclib\System\SSH\Agent::requestIdentities() + * + * @param \phpseclib\Crypt\RSA $key + * @access private + */ + function setPublicKey($key) + { + $this->key = $key; + $this->key->setPublicKey(); + } + + /** + * Set Public Key + * + * Called by \phpseclib\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key + * but this saves a small amount of computation. + * + * @param string $key_blob + * @access private + */ + function setPublicKeyBlob($key_blob) + { + $this->key_blob = $key_blob; + } + + /** + * Get Public Key + * + * Wrapper for $this->key->getPublicKey() + * + * @param int $type optional + * @return mixed + * @access public + */ + function getPublicKey($type = 'PKCS8') + { + return $this->key->getPublicKey($type); + } + + /** + * Sets the hash + * + * ssh-agent only supports signatures with sha1 hashes but to maintain BC with RSA.php this function exists + * + * @param string $hash optional + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported + * @access public + */ + function setHash($hash = 'sha1') + { + if ($hash != 'sha1') { + throw new UnsupportedAlgorithmException('ssh-agent can only be used with the sha1 hash'); + } + } + + /** + * Create a signature + * + * See "2.6.2 Protocol 2 private key signature request" + * + * @param string $message + * @param int $padding optional + * @return string + * @throws \RuntimeException on connection errors + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported + * @access public + */ + function sign($message, $padding = RSA::PADDING_PKCS1) + { + if ($padding != RSA::PADDING_PKCS1 && $padding != RSA::PADDING_RELAXED_PKCS1) { + throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures'); + } + + // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE + $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); + $packet = pack('Na*', strlen($packet), $packet); + if (strlen($packet) != fputs($this->fsock, $packet)) { + throw new \RuntimeException('Connection closed during signing'); + } + + $length = current(unpack('N', fread($this->fsock, 4))); + $type = ord(fread($this->fsock, 1)); + if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { + throw new \RuntimeException('Unable to retreive signature'); + } + + $signature_blob = fread($this->fsock, $length - 1); + // the only other signature format defined - ssh-dss - is the same length as ssh-rsa + // the + 12 is for the other various SSH added length fields + return substr($signature_blob, strlen('ssh-rsa') + 12); + } +} diff --git a/src/phpseclib/bootstrap.php b/src/phpseclib/bootstrap.php new file mode 100755 index 00000000..bd4ba0b5 --- /dev/null +++ b/src/phpseclib/bootstrap.php @@ -0,0 +1,20 @@ +getLogger()->warning("No motd has been set. The server description will be empty."); } - if(Info::CURRENT_PROTOCOL === 20){ - $this->translator = new Translator_20(); + if(Info::CURRENT_PROTOCOL === 81){ + $this->translator = new Translator_81(); }else{ $this->getLogger()->critical("Couldn't find a protocol translator for #".Info::CURRENT_PROTOCOL .", disabling plugin"); $this->getPluginLoader()->disablePlugin($this); @@ -102,8 +102,8 @@ public function onEnable(){ public function receiveCryptoKeys($privateKey, $publicKey){ $this->privateKey = $privateKey; $this->publicKey = $publicKey; - $this->rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); - $this->rsa->loadKey($this->privateKey); + //$this->rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); + $this->rsa->load($this->privateKey); $this->enableServer(); } diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php old mode 100644 new mode 100755 index 289f6940..0e200702 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -158,7 +158,7 @@ public function bigBrother_sendChunk($x, $z, $payload){ $this->putRawPacket($pk); } - public function sendChunk($x, $z, $payload){ + public function sendChunk($x, $z, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ } @@ -378,7 +378,7 @@ public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $ } - public function close($message = "", $reason = "generic reason"){ + public function close($message = "", $reason = "generic reason", $notify = true){ if($this->bigBrother_status === 0){ $pk = new LoginDisconnectPacket(); $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); diff --git a/src/shoghicp/BigBrother/network/Info.php b/src/shoghicp/BigBrother/network/Info.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/Packet.php b/src/shoghicp/BigBrother/network/Packet.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/ServerManager.php b/src/shoghicp/BigBrother/network/ServerManager.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/ServerThread.php b/src/shoghicp/BigBrother/network/ServerThread.php old mode 100644 new mode 100755 index 402cad64..7c210f7e --- a/src/shoghicp/BigBrother/network/ServerThread.php +++ b/src/shoghicp/BigBrother/network/ServerThread.php @@ -26,9 +26,9 @@ class ServerThread extends Thread{ /** @var \ThreadedLogger */ protected $logger; protected $loader; - protected $data = []; + protected $data; - public $loadPaths = []; + public $loadPaths; protected $shutdown; diff --git a/src/shoghicp/BigBrother/network/Session.php b/src/shoghicp/BigBrother/network/Session.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php b/src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php b/src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php b/src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php b/src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php b/src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php b/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php b/src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php b/src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php b/src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PingPacket.php b/src/shoghicp/BigBrother/network/protocol/PingPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/RespawnPacket.php b/src/shoghicp/BigBrother/network/protocol/RespawnPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php b/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php b/src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php b/src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php b/src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php b/src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php b/src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/translation/Translator.php b/src/shoghicp/BigBrother/network/translation/Translator.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/network/translation/Translator_20.php b/src/shoghicp/BigBrother/network/translation/Translator_81.php old mode 100644 new mode 100755 similarity index 98% rename from src/shoghicp/BigBrother/network/translation/Translator_20.php rename to src/shoghicp/BigBrother/network/translation/Translator_81.php index 1e76ef9a..68b4e4b5 --- a/src/shoghicp/BigBrother/network/translation/Translator_20.php +++ b/src/shoghicp/BigBrother/network/translation/Translator_81.php @@ -52,7 +52,7 @@ use shoghicp\BigBrother\network\protocol\WindowItemsPacket; use shoghicp\BigBrother\utils\Binary; -class Translator_20 implements Translator{ +class Translator_81 implements Translator{ @@ -214,11 +214,11 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->saturation = 5; return $pk; - case Info::MESSAGE_PACKET: + /*case Info::MESSAGE_PACKET: $pk = new STCChatPacket(); $pk->message = TextFormat::toJSON($packet->message); - return $pk; + return $pk;*/ case Info::SET_TIME_PACKET: $pk = new TimeUpdatePacket(); @@ -234,7 +234,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return $pk; case Info::REMOVE_ENTITY_PACKET: - case Info::REMOVE_PLAYER_PACKET: + //case Info::REMOVE_PLAYER_PACKET: $pk = new DestroyEntitiesPacket(); $pk->ids[] = $packet->eid; return $pk; diff --git a/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php b/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php b/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php b/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php b/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/tasks/OnlineProfile.php b/src/shoghicp/BigBrother/tasks/OnlineProfile.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/utils/AES.php b/src/shoghicp/BigBrother/utils/AES.php old mode 100644 new mode 100755 diff --git a/src/shoghicp/BigBrother/utils/Binary.php b/src/shoghicp/BigBrother/utils/Binary.php old mode 100644 new mode 100755 From 7f26cf6c727ff510bea2b12de3ed2ecf327ae838 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Wed, 29 Jun 2016 18:33:49 -0400 Subject: [PATCH 02/21] fixed connection refused (still stuck on logout) & now 1.10.2 --- src/shoghicp/BigBrother/BigBrother.php | 149 +-- src/shoghicp/BigBrother/DesktopChunk.php | 94 ++ src/shoghicp/BigBrother/DesktopPlayer.php | 472 +++++----- src/shoghicp/BigBrother/network/Info.php | 10 +- src/shoghicp/BigBrother/network/Packet.php | 11 +- .../BigBrother/network/ProtocolInterface.php | 153 +++- .../BigBrother/network/ServerManager.php | 50 +- .../BigBrother/network/ServerThread.php | 20 +- src/shoghicp/BigBrother/network/Session.php | 11 +- .../{ => Login}/EncryptionRequestPacket.php | 2 +- .../{ => Login}/EncryptionResponsePacket.php | 2 +- .../{ => Login}/LoginDisconnectPacket.php | 2 +- .../protocol/{ => Login}/LoginStartPacket.php | 2 +- .../{ => Login}/LoginSuccessPacket.php | 2 +- .../protocol/{ => Login}/PingPacket.php | 2 +- .../network/protocol/Play/AnimatePacket.php | 44 + .../protocol/{ => Play}/BlockChangePacket.php | 2 +- .../protocol/Play/CPlayerAbilitiesPacket.php | 73 ++ .../protocol/{ => Play}/CTSChatPacket.php | 2 +- .../{ => Play}/CTSCloseWindowPacket.php | 2 +- .../protocol/Play/CTabCompletePacket.php | 43 + .../{ => Play}/ChangeGameStatePacket.php | 2 +- .../protocol/{ => Play}/ChunkDataPacket.php | 2 +- .../protocol/Play/ClickWindowPacket.php | 47 + .../protocol/Play/ClientSettingsPacket.php | 45 + .../{ => Play}/ClientStatusPacket.php | 4 +- .../Play/CreativeInventoryActionPacket.php | 39 + .../{ => Play}/DestroyEntitiesPacket.php | 2 +- .../protocol/Play/EntityEquipmentPacket.php | 41 + .../{ => Play}/EntityHeadLookPacket.php | 2 +- .../{ => Play}/EntityMetadataPacket.php | 2 +- .../network/protocol/Play/EntityPacket.php | 37 + .../{ => Play}/EntityTeleportPacket.php | 2 +- .../{ => Play}/EntityVelocityPacket.php | 2 +- .../protocol/Play/HeldItemChangePacket.php | 37 + .../protocol/{ => Play}/JoinGamePacket.php | 2 +- .../protocol/{ => Play}/KeepAlivePacket.php | 2 +- .../protocol/{ => Play}/OpenWindowPacket.php | 2 +- .../{ => Play}/PlayDisconnectPacket.php | 2 +- .../{ => Play}/PlayerAbilitiesPacket.php | 2 +- .../protocol/Play/PlayerArmSwingPacket.php | 34 + .../{ => Play}/PlayerBlockPlacementPacket.php | 2 +- .../{ => Play}/PlayerDiggingPacket.php | 2 +- .../protocol/Play/PlayerListPacket.php | 76 ++ .../protocol/{ => Play}/PlayerLookPacket.php | 2 +- .../network/protocol/Play/PlayerPacket.php | 37 + .../PlayerPositionAndLookPacket.php | 2 +- .../{ => Play}/PlayerPositionPacket.php | 2 +- .../protocol/Play/PluginMessagePacket.php | 58 ++ .../{ => Play}/PositionAndLookPacket.php | 2 +- .../protocol/Play/ResourcePackSendPacket.php | 38 + .../Play/ResourcePackStatusPacket.php | 39 + .../protocol/{ => Play}/RespawnPacket.php | 2 +- .../protocol/{ => Play}/STCChatPacket.php | 2 +- .../{ => Play}/STCCloseWindowPacket.php | 2 +- .../protocol/Play/STabCompletePacket.php | 39 + .../Play/ScoreboardObjectivePacket.php | 42 + .../protocol/Play/ServerDifficultyPacket.php | 36 + .../protocol/{ => Play}/SetSlotPacket.php | 2 +- .../protocol/{ => Play}/SpawnMobPacket.php | 2 +- .../protocol/{ => Play}/SpawnObjectPacket.php | 2 +- .../protocol/{ => Play}/SpawnPlayerPacket.php | 8 +- .../{ => Play}/SpawnPositionPacket.php | 2 +- .../protocol/Play/StatisticsPacket.php | 42 + .../protocol/{ => Play}/TimeUpdatePacket.php | 2 +- .../network/protocol/Play/TitlePacket.php | 55 ++ .../{ => Play}/UpdateHealthPacket.php | 2 +- .../protocol/Play/UpdateSignPacket.php | 47 + .../network/protocol/Play/UseBedPacket.php | 40 + .../protocol/{ => Play}/UseEntityPacket.php | 11 +- .../protocol/{ => Play}/WindowItemsPacket.php | 2 +- .../network/translation/Translator_81.php | 849 ++++++++++++++---- .../BigBrother/tasks/AuthenticateOnline.php | 55 -- .../BigBrother/tasks/GeneratePrivateKey.php | 81 -- .../BigBrother/tasks/LevelDBToAnvil.php | 122 --- .../BigBrother/tasks/McRegionToAnvil.php | 122 --- .../BigBrother/tasks/OnlineProfile.php | 79 -- src/shoghicp/BigBrother/utils/Binary.php | 76 +- 78 files changed, 2348 insertions(+), 1092 deletions(-) create mode 100755 src/shoghicp/BigBrother/DesktopChunk.php rename src/shoghicp/BigBrother/network/protocol/{ => Login}/EncryptionRequestPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Login}/EncryptionResponsePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Login}/LoginDisconnectPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Login}/LoginStartPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Login}/LoginSuccessPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Login}/PingPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/BlockChangePacket.php (95%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/CTSChatPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/CTSCloseWindowPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/ChangeGameStatePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/ChunkDataPacket.php (95%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/ClientStatusPacket.php (90%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/DestroyEntitiesPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/EntityHeadLookPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/EntityMetadataPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/EntityTeleportPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/EntityVelocityPacket.php (95%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/JoinGamePacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/KeepAlivePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/OpenWindowPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayDisconnectPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayerAbilitiesPacket.php (96%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayerBlockPlacementPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayerDiggingPacket.php (95%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayerLookPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayerPositionAndLookPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PlayerPositionPacket.php (95%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/PositionAndLookPacket.php (96%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/RespawnPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/STCChatPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/STCCloseWindowPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/SetSlotPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/SpawnMobPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/SpawnObjectPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/SpawnPlayerPacket.php (85%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/SpawnPositionPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/TimeUpdatePacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/UpdateHealthPacket.php (94%) create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php create mode 100755 src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php rename src/shoghicp/BigBrother/network/protocol/{ => Play}/UseEntityPacket.php (82%) rename src/shoghicp/BigBrother/network/protocol/{ => Play}/WindowItemsPacket.php (95%) delete mode 100755 src/shoghicp/BigBrother/tasks/AuthenticateOnline.php delete mode 100755 src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php delete mode 100755 src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php delete mode 100755 src/shoghicp/BigBrother/tasks/McRegionToAnvil.php delete mode 100755 src/shoghicp/BigBrother/tasks/OnlineProfile.php diff --git a/src/shoghicp/BigBrother/BigBrother.php b/src/shoghicp/BigBrother/BigBrother.php index db2b3547..fc3993b2 100755 --- a/src/shoghicp/BigBrother/BigBrother.php +++ b/src/shoghicp/BigBrother/BigBrother.php @@ -17,26 +17,29 @@ namespace shoghicp\BigBrother; +use pocketmine\plugin\PluginBase; + use phpseclib\Crypt\RSA; -use pocketmine\event\player\PlayerRespawnEvent; -use pocketmine\event\player\PlayerPreLoginEvent; -use shoghicp\BigBrother\network\protocol\RespawnPacket; -use shoghicp\BigBrother\network\translation\Translator; -use pocketmine\event\Listener; use pocketmine\network\protocol\Info; -use pocketmine\plugin\PluginBase; +use pocketmine\network\protocol\PlayerActionPacket; use shoghicp\BigBrother\network\Info as MCInfo; use shoghicp\BigBrother\network\ProtocolInterface; -use shoghicp\BigBrother\network\ServerThread; +use shoghicp\BigBrother\network\translation\Translator; use shoghicp\BigBrother\network\translation\Translator_81; -use shoghicp\BigBrother\tasks\GeneratePrivateKey; +use shoghicp\BigBrother\network\protocol\Play\RespawnPacket; +use shoghicp\BigBrother\network\protocol\Play\ResourcePackSendPacket; -class BigBrother extends PluginBase implements Listener{ +use pocketmine\block\Block; +use pocketmine\math\Vector3; +use pocketmine\tile\Sign; +use pocketmine\Achievement; + +use pocketmine\event\Listener; +use pocketmine\event\player\PlayerPreLoginEvent; +use pocketmine\event\player\PlayerRespawnEvent; +use pocketmine\event\player\PlayerInteractEvent; - /** @var ServerThread */ - private $thread; - private $internalQueue; - private $externalQueue; +class BigBrother extends PluginBase implements Listener{ /** @var ProtocolInterface */ private $interface; @@ -53,19 +56,11 @@ class BigBrother extends PluginBase implements Listener{ /** @var Translator */ protected $translator; - public function onLoad(){ - - class_exists("phpseclib\\Math\\BigInteger", true); - class_exists("phpseclib\\Crypt\\Random", true); - class_exists("phpseclib\\Crypt\\Base", true); - class_exists("phpseclib\\Crypt\\Rijndael", true); - class_exists("phpseclib\\Crypt\\AES", true); - } - public function onEnable(){ - $this->saveDefaultConfig(); $this->saveResource("server-icon.png", false); + $this->saveResource("steve.yml", false); + $this->saveResource("alex.yml", false); $this->reloadConfig(); $this->onlineMode = (bool) $this->getConfig()->get("online-mode"); @@ -76,47 +71,69 @@ public function onEnable(){ if(!$this->getConfig()->exists("motd")){ $this->getLogger()->warning("No motd has been set. The server description will be empty."); + return; } - if(Info::CURRENT_PROTOCOL === 81){ - $this->translator = new Translator_81(); - }else{ - $this->getLogger()->critical("Couldn't find a protocol translator for #".Info::CURRENT_PROTOCOL .", disabling plugin"); - $this->getPluginLoader()->disablePlugin($this); - return; + switch(Info::CURRENT_PROTOCOL){ + case 81: + $this->translator = new Translator_81(); + break; + default: + $this->getLogger()->critical("Couldn't find a protocol translator for #".Info::CURRENT_PROTOCOL .", disabling plugin"); + $this->getPluginLoader()->disablePlugin($this); + return; + break; } $this->rsa = new RSA(); $this->getServer()->getPluginManager()->registerEvents($this, $this); + Achievement::add("openInventory","Taking Inventory"); //this for DesktopPlayer + if($this->onlineMode){ $this->getLogger()->info("Server is being started in the background"); - $task = new GeneratePrivateKey($this->getServer()->getLogger(), $this->getServer()->getLoader()); - $this->getServer()->getScheduler()->scheduleAsyncTask($task); - }else{ - $this->enableServer(); + $this->getLogger()->info("Generating keypair"); + //$this->rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1); + //$this->rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1); + $keys = $this->rsa->createKey(1024); + $this->privateKey = $keys["privatekey"]; + $this->publicKey = $keys["publickey"]; + //$this->rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); + $this->rsa->load($this->privateKey); } - } - - public function receiveCryptoKeys($privateKey, $publicKey){ - $this->privateKey = $privateKey; - $this->publicKey = $publicKey; - //$this->rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); - $this->rsa->load($this->privateKey); $this->enableServer(); } protected function enableServer(){ - $this->externalQueue = new \Threaded; - $this->internalQueue = new \Threaded; - $port = (int) $this->getConfig()->get("port"); - $interface = $this->getConfig()->get("interface"); - $this->getLogger()->info("Starting Minecraft: PC server on ".($interface === "0.0.0.0" ? "*" : $interface).":$port version ".MCInfo::VERSION); - $this->thread = new ServerThread($this->externalQueue, $this->internalQueue, $this->getServer()->getLogger(), $this->getServer()->getLoader(), $port, $interface, (string) $this->getConfig()->get("motd"), $this->getDataFolder() . "server-icon.png"); - - $this->interface = new ProtocolInterface($this, $this->thread, $this->translator); - $this->getServer()->addInterface($this->interface); + $this->getLogger()->info("Starting Minecraft: PC server on ".($this->getIp() === "0.0.0.0" ? "*" : $this->getIp()).":".$this->getPort()." version ".MCInfo::VERSION); + + $disable = true; + foreach($this->getServer()->getInterfaces() as $interface){ + if($interface instanceof ProtocolInterface){ + $disable = false; + } + } + if($disable){ + $this->interface = new ProtocolInterface($this, $this->getServer(), $this->translator); + $this->getServer()->addInterface($this->interface); + } + } + + public function getIp(){ + return $this->getConfig()->get("interface"); + } + + public function getPort(){ + return (int) $this->getConfig()->get("port"); + } + + public function getMotd(){ + return (string) $this->getConfig()->get("motd"); + } + + public function getResourcePackURL(){ + return (string) $this->getConfig()->get("resourcepackurl"); } /** @@ -137,14 +154,6 @@ public function decryptBinary($secret){ return $this->rsa->decrypt($secret); } - public function onDisable(){ - //TODO: make it fully /reload compatible (remove from server) - if($this->interface instanceof ProtocolInterface){ - $this->getServer()->removeInterface($this->interface); - $this->thread->join(); - } - } - /** * @param PlayerPreLoginEvent $event * @@ -159,7 +168,6 @@ public function onPreLogin(PlayerPreLoginEvent $event){ } $player->bigBrother_setCompression($threshold); } - } /** @@ -172,11 +180,32 @@ public function onRespawn(PlayerRespawnEvent $event){ if($player instanceof DesktopPlayer){ $pk = new RespawnPacket(); $pk->dimension = 0; - $pk->gamemode = $player->getGamemode(); $pk->difficulty = $player->getServer()->getDifficulty(); + $pk->gamemode = $player->getGamemode(); $pk->levelType = "default"; $player->putRawPacket($pk); } } -} + public function onTouch(PlayerInteractEvent $event){ + $player = $event->getPlayer(); + if($player instanceof DesktopPlayer){ + $block = $event->getBlock(); + switch($block->getID()){ + case Block::SIGN_POST: + case Block::WALL_SIGN: + $tile = $player->getLevel()->getTile(new Vector3($block->getX(), $block->getY(), $block->getZ())); + if($tile instanceof Sign){ + $text = $tile->getText(); + if($text[0] === "ResourcePack" and $text[1] === "Download" and $this->getResourcePackURL() !== "false"){ + $pk = new ResourcePackSendPacket(); + $pk->url = $this->getResourcePackURL(); + $player->putRawPacket($pk); + } + } + break; + } + } + } + +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/DesktopChunk.php b/src/shoghicp/BigBrother/DesktopChunk.php new file mode 100755 index 00000000..ae7cfec8 --- /dev/null +++ b/src/shoghicp/BigBrother/DesktopChunk.php @@ -0,0 +1,94 @@ +player = $player; + $this->chunkX = $chunkX; + $this->chunkZ = $chunkZ; + $this->provider = $provider = $player->getLevel()->getProvider(); + $this->data = $this->generateChunk(); + } + + public function generateChunk(){ + $chunk = $this->provider->getChunk($this->chunkX, $this->chunkZ, false); + $chunkblockIds = $chunk->getBlockIdArray(); + $chunkblockData = $chunk->getBlockDataArray(); + $chunkblockSkyLight = $chunk->getBlockSkyLightArray(); + $chunkblockLight = $chunk->getBlockLightArray(); + + $chunkbiomeIds = $chunk->getBiomeIdArray(); + + $compressionLevel = Level::$COMPRESSION_LEVEL; + + $ids = ["", "", "", "", "", "", "", ""]; + $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; + + //Complexity: O(MG) + for($Y = 0; $Y < 8; ++$Y){ + for($y = 0; $y < 16; ++$y){ + $offset = ($Y << 4) + $y; + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $index = ($x << 11) + ($z << 7) + $offset; + $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); + if(($y & 1) === 0){ + $data = ord($chunkblockData[$halfIndex]) & 0x0F; + $bLight = ord($chunkblockLight[$halfIndex]) & 0x0F; + + //$sLight = ord($blockSkyLight[$halfIndex]) & 0x0F; + }else{ + $data = ord($chunkblockData[$halfIndex]) >> 4; + $bLight = ord($chunkblockLight[$halfIndex]) >> 4; + //$sLight = ord($blockSkyLight[$halfIndex]) >> 4; + } + $ids[$Y] .= pack("v", (ord($chunkblockIds[$index]) << 4) | $data); + + $blockLight[$Y][] = $bLight; + //$skyLight[$Y][] = $sLight; + } + } + } + } + + foreach($blockLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $blockLight[$Y] = $final; + } + + /* + foreach($skyLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $skyLight[$Y] = $final; + } + */ + + $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; + + $payload = implode($ids) . implode($blockLight) . implode($skyLight) . $chunkbiomeIds; + + return $payload; + } + + public function getData(){ + if(isset($this->data)){ + return $this->data; + } + return null; + } + +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index 0e200702..89506dd3 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -17,137 +17,94 @@ namespace shoghicp\BigBrother; -use pocketmine\event\player\PlayerJoinEvent; -use pocketmine\event\player\PlayerRespawnEvent; -use pocketmine\level\format\anvil\Chunk as AnvilChunk; -use pocketmine\level\format\mcregion\Chunk as McRegionChunk; -use pocketmine\level\format\leveldb\Chunk as LevelDBChunk; -use pocketmine\level\format\generic\EmptyChunkSection; +use pocketmine\event\Timings; use pocketmine\level\Level; -use pocketmine\level\Position; use pocketmine\network\protocol\Info; use pocketmine\network\protocol\LoginPacket; -use pocketmine\network\protocol\SetEntityMotionPacket; -use pocketmine\network\protocol\SetTimePacket; use pocketmine\network\SourceInterface; use pocketmine\Player; -use pocketmine\scheduler\CallbackTask; use pocketmine\Server; -use pocketmine\tile\Spawnable; -use pocketmine\utils\TextFormat; +use pocketmine\tile\Sign; use pocketmine\utils\Utils; +use pocketmine\utils\UUID; +use pocketmine\utils\TextFormat; use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\network\protocol\ChunkDataPacket; -use shoghicp\BigBrother\network\protocol\EncryptionRequestPacket; -use shoghicp\BigBrother\network\protocol\EncryptionResponsePacket; -use shoghicp\BigBrother\network\protocol\EntityMetadataPacket; -use shoghicp\BigBrother\network\protocol\EntityTeleportPacket; -use shoghicp\BigBrother\network\protocol\KeepAlivePacket; -use shoghicp\BigBrother\network\protocol\LoginDisconnectPacket; -use shoghicp\BigBrother\network\protocol\LoginStartPacket; -use shoghicp\BigBrother\network\protocol\LoginSuccessPacket; -use shoghicp\BigBrother\network\protocol\PlayDisconnectPacket; -use shoghicp\BigBrother\network\protocol\SpawnMobPacket; -use shoghicp\BigBrother\network\protocol\SpawnPlayerPacket; +use shoghicp\BigBrother\network\protocol\Login\EncryptionRequestPacket; +use shoghicp\BigBrother\network\protocol\Login\EncryptionResponsePacket; +use shoghicp\BigBrother\network\protocol\Login\LoginSuccessPacket; +use shoghicp\BigBrother\network\protocol\Play\ChunkDataPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerListPacket; +use shoghicp\BigBrother\network\protocol\Play\TitlePacket; use shoghicp\BigBrother\network\ProtocolInterface; -use shoghicp\BigBrother\tasks\AuthenticateOnline; -use shoghicp\BigBrother\tasks\LevelDBToAnvil; -use shoghicp\BigBrother\tasks\McRegionToAnvil; -use shoghicp\BigBrother\tasks\OnlineProfile; use shoghicp\BigBrother\utils\Binary; class DesktopPlayer extends Player{ private $bigBrother_status = 0; //0 = log in, 1 = playing + public $threshold = false; protected $bigBrother_uuid; protected $bigBrother_formatedUUID; protected $bigBrother_properties = []; private $bigBrother_checkToken; private $bigBrother_secret; private $bigBrother_username; - protected $bigBrother_titleBarID = null; - protected $bigBrother_titleBarText; - protected $bigBrother_titleBarLevel; + private $bigbrother_clientId; + protected $Settings = []; /** @var ProtocolInterface */ protected $interface; - public function __construct(SourceInterface $interface, $clientID, $address, $port){ + public function __construct(SourceInterface $interface, $clientID, $address, $port, BigBrother $plugin){ + $this->plugin = $plugin; + $this->bigbrother_clientId = $clientID; parent::__construct($interface, $clientID, $address, $port); - $this->setRemoveFormat(false); + $this->setRemoveFormat(false);// Color Code } - public function bigBrother_updateTitleBar(){ - if($this->bigBrother_titleBarID === null){ - $this->bigBrother_titleBarID = 2147483647; - - $pk = new SpawnMobPacket(); - $pk->eid = $this->bigBrother_titleBarID; - $pk->type = 63; - $pk->x = $this->x; - $pk->y = 250; - $pk->z = $this->z; - $pk->pitch = 0; - $pk->yaw = 0; - $pk->headPitch = 0; - $pk->velocityX = 0; - $pk->velocityY = 0; - $pk->velocityZ = 0; - $pk->metadata = [ - 0 => ["type" => 0, "value" => 0x20], - 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], - 7 => ["type" => 2, "value" => 0], - 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], - 11 => ["type" => 0, "value" => 1] - ]; - $this->putRawPacket($pk); - $this->tasks[] = $this->getServer()->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_updateTitleBar"]), 5, 20); - }else{ - $pk = new EntityTeleportPacket(); - $pk->eid = $this->bigBrother_titleBarID; - $pk->x = $this->x; - $pk->y = 250; - $pk->z = $this->z; - $pk->yaw = 0; - $pk->pitch = 0; - $this->putRawPacket($pk); + public function bigBrother_getStatus(){ + return $this->bigBrother_status; + } - $pk = new EntityMetadataPacket(); - $pk->eid = $this->bigBrother_titleBarID; - $pk->metadata = [ - 0 => ["type" => 0, "value" => 0x20], - 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], - 7 => ["type" => 2, "value" => 0], - 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], - 11 => ["type" => 0, "value" => 1] - ]; - $this->putRawPacket($pk); + public function bigBrother_getPeroperties(){ + return $this->bigBrother_properties; + } - } + public function bigBrother_getUniqueId(){ + return $this->bigBrother_uuid; } - public function bigBrother_setTitleBar($text, $level = 100){ - if($level > 100){ - $level = 100; - }elseif($level < 0){ - $level = 0; + public function getSettings(){ + return $this->Settings; + } + + public function getSetting($settingname = null){ + if(isset($this->Settings[$settingname])){ + return $this->Settings[$settingname]; } + return false; + } - $this->bigBrother_titleBarText = $text; - $this->bigBrother_titleBarLevel = $level; - $this->bigBrother_updateTitleBar(); + public function setSetting($settings){ + $this->Settings = array_merge($this->Settings, $settings); } - public function bigBrother_sendKeepAlive(){ - $pk = new KeepAlivePacket(); - $pk->id = mt_rand(); - $this->putRawPacket($pk); + public function removeSetting($settingname){ + if(isset($this->Settings[$settingname])){ + unset($this->Settings[$settingname]); + } } - public function bigBrother_getStatus(){ - return $this->bigBrother_status; + public function cleanSetting($settingname){ + unset($this->Settings[$settingname]); } public function bigBrother_sendChunk($x, $z, $payload){ + if($this->connected === false){ + return; + } + + $this->usedChunks[Level::chunkHash($x, $z)] = true; + $this->chunkLoadCount++; + $pk = new ChunkDataPacket(); $pk->chunkX = $x; $pk->chunkZ = $z; @@ -156,10 +113,23 @@ public function bigBrother_sendChunk($x, $z, $payload){ $pk->payload = $payload; $pk->primaryBitmap = 0xff; $this->putRawPacket($pk); - } - public function sendChunk($x, $z, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ + foreach($this->level->getChunkTiles($x, $z) as $tile){ + if($tile instanceof Sign){ + $tile->spawnTo($this); + } + } + if($this->spawned){ + foreach($this->level->getChunkPlayers($x, $z) as $player){ + $player->spawnTo($this); + } + /*foreach($this->level->getChunkEntities($x, $z) as $entity){ + if($entity !== $this and !$entity->closed and $entity->isAlive()){ + $entity->spawnTo($this); + } + }*/ + } } protected function sendNextChunk(){ @@ -167,175 +137,117 @@ protected function sendNextChunk(){ return; } + Timings::$playerChunkSendTimer->startTiming(); + $count = 0; foreach($this->loadQueue as $index => $distance){ if($count >= $this->chunksPerTick){ break; } + $X = null; + $Z = null; Level::getXZ($index, $X, $Z); - if(!$this->level->isChunkPopulated($X, $Z)){ - $this->level->generateChunk($X, $Z); - if($this->spawned){ + ++$count; + + $this->usedChunks[$index] = false; + $this->level->registerChunkLoader($this, $X, $Z, false); + + if(!$this->level->populateChunk($X, $Z)){ + if($this->spawned and $this->teleportPosition === null){ continue; }else{ break; } } - ++$count; - unset($this->loadQueue[$index]); - $this->usedChunks[$index] = true; - - $this->level->useChunk($X, $Z, $this); - $chunk = $this->level->getChunk($X, $Z); - if($chunk instanceof AnvilChunk){ - $this->kick("Playing on Anvil worlds is not yet implemented"); - //TODO! - /*$pk = new ChunkDataPacket(); - $pk->chunkX = $X; - $pk->chunkZ = $Z; - $pk->groundUp = true; - $ids = ""; - $meta = ""; - $blockLight = ""; - $skyLight = ""; - $biomeIds = $chunk->getBiomeIdArray(); - $bitmap = 0; - for($s = 0; $s < 8; ++$s){ - $section = $chunk->getSection($s); - if(!($section instanceof EmptyChunkSection)){ - $bitmap |= 1 << $s; - }else{ - continue; - } - $ids .= $section->getIdArray(); - $meta .= $section->getDataArray(); - $blockLight .= $section->getLightArray(); - $skyLight .= $section->getSkyLightArray(); - } - - $pk->payload = zlib_encode($ids . $meta . $blockLight . $skyLight . $biomeIds, ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL); - $pk->primaryBitmap = $bitmap; - $this->putRawPacket($pk);*/ - }elseif($chunk instanceof McRegionChunk){ - $task = new McRegionToAnvil($this, $chunk); - $this->server->getScheduler()->scheduleAsyncTask($task); - }elseif($chunk instanceof LevelDBChunk){ - $task = new LevelDBToAnvil($this, $chunk); - $this->server->getScheduler()->scheduleAsyncTask($task); - } - - foreach($chunk->getEntities() as $entity){ - if($entity !== $this){ - $entity->spawnTo($this); - } - } - foreach($chunk->getTiles() as $tile){ - if($tile instanceof Spawnable){ - $tile->spawnTo($this); - } - } + $chunk = new DesktopChunk($this, $X, $Z); + $this->bigBrother_sendChunk($X, $Z, $chunk->getData()); + $chunk = null; } - if(count($this->usedChunks) >= 4 and $this->spawned === false){ - - $this->bigBrother_setTitleBar(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother.", 0); - - $this->spawned = true; - - $pk = new SetTimePacket(); - $pk->time = $this->level->getTime(); - $pk->started = $this->level->stopTime == false; - $this->dataPacket($pk); - - $pos = $this->level->getSafeSpawn($this); - - $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); - - $this->teleport($ev->getRespawnPosition()); - - $this->sendSettings(); + if($this->chunkLoadCount >= 4 and $this->spawned === false and $this->teleportPosition === null){ + $this->doFirstSpawn(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); - - $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, TextFormat::YELLOW . $this->getName() . " joined the game")); - if(strlen(trim($ev->getJoinMessage())) > 0){ - $this->server->broadcastMessage($ev->getJoinMessage()); - } - - $this->spawnToAll(); - - if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ - $this->server->getUpdater()->showPlayerUpdate($this); - } } - } - public function spawnTo(Player $player){ - if($player instanceof DesktopPlayer){ - if($this !== $player and $this->spawned === true and $player->getLevel() === $this->getLevel() and $player->canSee($this)){ - $this->hasSpawned[$player->getID()] = $player; - $pk = new SpawnPlayerPacket(); - if($player->getRemoveFormat()){ - $pk->name = TextFormat::clean($this->nameTag); - }else{ - $pk->name = $this->nameTag; - } - $pk->eid = $this->getID(); - $pk->uuid = $this->bigBrother_formatedUUID; - $pk->x = $this->x; - $pk->z = $this->y; - $pk->y = $this->z; - $pk->yaw = $this->yaw; - $pk->pitch = $this->pitch; - $pk->item = $this->inventory->getItemInHand()->getID(); - $pk->metadata = $this->getData(); - $pk->data = $this->bigBrother_properties; - $player->putRawPacket($pk); - - $pk = new EntityTeleportPacket(); - $pk->eid = $this->getID(); - $pk->x = $this->x; - $pk->z = $this->y; - $pk->y = $this->z; - $pk->yaw = $this->yaw; - $pk->pitch = $this->pitch; - $player->putRawPacket($pk); - - $pk = new SetEntityMotionPacket(); - $pk->eid = $this->getID(); - $pk->speedX = $this->motionX; - $pk->speedY = $this->motionY; - $pk->speedZ = $this->motionZ; - $player->dataPacket($pk); - - $this->inventory->sendHeldItem($player); - - $this->inventory->sendArmorContents($player); - } - }else{ - parent::spawnTo($player); - } + Timings::$playerChunkSendTimer->stopTiming(); } - public function bigBrother_authenticate($username, $uuid, $onlineModeData = null){ + public function bigBrother_authenticate($uuid, $onlineModeData = null){ if($this->bigBrother_status === 0){ $this->bigBrother_uuid = $uuid; - $this->bigBrother_formatedUUID = Binary::UUIDtoString($this->bigBrother_uuid); + $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toString(); $pk = new LoginSuccessPacket(); $pk->uuid = $this->bigBrother_formatedUUID; - $pk->name = $this->username; + $pk->name = $this->bigBrother_username; $this->putRawPacket($pk); + $this->bigBrother_status = 1; + if($onlineModeData !== null and is_array($onlineModeData)){ $this->bigBrother_properties = $onlineModeData; } - $this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); - $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 1); + foreach($this->bigBrother_properties as $property){ + if($property["name"] === "textures"){ + $skindata = json_decode(base64_decode($property["value"]), true); + if(isset($skindata["textures"]["SKIN"]["url"])){ + $skin = $this->getSkinImage($skindata["textures"]["SKIN"]["url"]); + } + } + } + + $pk = new LoginPacket(); + $pk->username = $this->bigBrother_username; + $pk->clientId = crc32($this->bigbrother_clientId); + $pk->protocol1 = Info::CURRENT_PROTOCOL; + $pk->protocol2 = Info::CURRENT_PROTOCOL; + $pk->clientUUID = UUID::fromString($uuid); + $pk->serverAddress = "127.0.0.1:25565"; + $pk->clientSecret = "BigBrother"; + if($skin === null or $skin === false){ + if($this->plugin->getConfig()->get("skin-slim")){ + $pk->skinname = "Standard_Custom"; + }else{ + $pk->skinname = "Standard_CustomSlim"; + } + $pk->skin = file_get_contents($this->plugin->getDataFolder().$this->plugin->getConfig()->get("skin-yml")); + }else{ + if(!isset($skindata["textures"]["SKIN"]["metadata"]["model"])){ + $pk->skinname = "Standard_Custom"; + }else{ + $pk->skinname = "Standard_CustomSlim"; + } + $pk->skin = $skin; + } + + $this->handleDataPacket($pk); + + $pk = new PlayerListPacket(); + $pk->actionID = PlayerListPacket::TYPE_ADD; + $pk->players[] = [ + $this->bigBrother_uuid, + $this->bigBrother_username, + $this->bigBrother_properties, + $this->getGamemode(), + 0, + false, + ]; + $this->putRawPacket($pk); + + $pk = new TitlePacket(); //Set SubTitle for this + $pk->actionID = TitlePacket::TYPE_SET_TITLE; + $pk->data = TextFormat::toJSON(""); + $this->putRawPacket($pk); + + $pk = new TitlePacket(); + $pk->actionID = TitlePacket::TYPE_SET_SUB_TITLE; + $pk->data = TextFormat::toJSON(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother."); + $this->putRawPacket($pk); } } @@ -346,22 +258,11 @@ public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionR if($token !== $this->bigBrother_checkToken){ $this->close("", "Invalid check token"); }else{ - $task = new AuthenticateOnline($this->clientID, $this->bigBrother_username, Binary::sha1("" . $this->bigBrother_secret . $plugin->getASN1PublicKey())); - $this->server->getScheduler()->scheduleAsyncTask($task); + $this->getAuthenticateOnline($this->bigBrother_username, Binary::sha1("".$this->bigBrother_secret.$plugin->getASN1PublicKey())); } } - public function bigBrother_authenticationCallback($username){ - $pk = new LoginPacket(); - $pk->username = $username; - $pk->clientId = crc32($this->clientID); - $pk->protocol1 = Info::CURRENT_PROTOCOL; - $pk->protocol2 = Info::CURRENT_PROTOCOL; - $pk->loginData = ""; - $this->handleDataPacket($pk); - } - - public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $onlineMode){ + public function bigBrother_handleAuthentication($plugin, $username, $onlineMode = false){ if($this->bigBrother_status === 0){ $this->bigBrother_username = $username; if($onlineMode === true){ @@ -371,27 +272,90 @@ public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $ $pk->verifyToken = $this->bigBrother_checkToken = Utils::getRandomBytes(4, false, true, $pk->publicKey); $this->putRawPacket($pk); }else{ - $task = new OnlineProfile($this->clientID, $this->bigBrother_username); - $this->server->getScheduler()->scheduleAsyncTask($task); + $info = $this->getProfile($username); + if(is_array($info)){ + $this->bigBrother_authenticate($info["id"], $info["properties"]); + } } } + } + + public function getProfile($username){ + $profile = json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); + if(!is_array($profile)){ + return false; + } + $uuid = $profile["id"]; + $info = json_decode(Utils::getURL("https://sessionserver.mojang.com/session/minecraft/profile/".$uuid."", 3), true); + if(!is_array($info)){ + return false; + } + return $info; } - public function close($message = "", $reason = "generic reason", $notify = true){ - if($this->bigBrother_status === 0){ - $pk = new LoginDisconnectPacket(); - $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); - $this->putRawPacket($pk); + public function getAuthenticateOnline($username, $hash){ + $result = json_decode(Utils::getURL("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=".$username."&serverId=".$hash, 5), true); + if(is_array($result) and isset($result["id"])){ + $this->bigBrother_authenticate($result["id"], $result["properties"]); }else{ - $pk = new PlayDisconnectPacket(); - $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); - $this->putRawPacket($pk); + $this->close("", "User not premium"); + } + } + + public function getSkinImage($url){ + if(extension_loaded("gd")){ + $image = imagecreatefrompng($url); + + if($image !== false){ + $width = imagesx($image); + $height = imagesy($image); + $colors = []; + for($y = 0; $y < $height; $y++){ + $y_array = []; + for($x = 0; $x < $width; $x++){ + $rgb = imagecolorat($image, $x, $y); + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + $alpha = imagecolorsforindex($image, $rgb)["alpha"]; + $x_array = [$r, $g, $b, $alpha]; + $y_array[] = $x_array; + } + $colors[] = $y_array; + } + $skin = null; + foreach($colors as $width){ + foreach($width as $height){ + $alpha = 0; + if($height[0] === 255 and $height[1] === 255 and $height[2] === 255){ + $height[0] = 0; + $height[1] = 0; + $height[2] = 0; + if($height[3] === 127){ + $alpha = 255; + }else{ + $alpha = 0; + } + }else{ + if($height[3] === 127){ + $alpha = 0; + }else{ + $alpha = 255; + } + } + $skin = $skin.chr($height[0]).chr($height[1]).chr($height[2]).chr($alpha); + } + } + imagedestroy($image); + return $skin; + } } - parent::close($message, $reason); + return false; } public function bigBrother_setCompression($threshold){ + $this->threshold = $threshold; $this->interface->setCompression($this, $threshold); } diff --git a/src/shoghicp/BigBrother/network/Info.php b/src/shoghicp/BigBrother/network/Info.php index 6c13bcc0..75e6578c 100755 --- a/src/shoghicp/BigBrother/network/Info.php +++ b/src/shoghicp/BigBrother/network/Info.php @@ -18,6 +18,12 @@ namespace shoghicp\BigBrother\network; abstract class Info{ - const VERSION = "1.8"; - const PROTOCOL = 47; + + /** + * Actual Minecraft protocol version + */ + + const VERSION = "1.10.2"; + const PROTOCOL = 210; + } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/Packet.php b/src/shoghicp/BigBrother/network/Packet.php index a5e6a717..8834ff53 100755 --- a/src/shoghicp/BigBrother/network/Packet.php +++ b/src/shoghicp/BigBrother/network/Packet.php @@ -82,16 +82,17 @@ protected function getDouble(){ */ protected function getSlot(){ $itemId = $this->getShort(); - if($itemId === -1){ //Empty + if($itemId === 65535){ //Empty return Item::get(Item::AIR, 0, 0); }else{ $count = $this->getByte(); $damage = $this->getShort(); - $len = $this->getShort(); + $len = $this->getByte(); + $nbt = ""; if($len > 0){ $nbt = $this->get($len); } - return Item::get($itemId, $damage, $count); + return Item::get($itemId, $damage, $count, $nbt); } } @@ -102,7 +103,9 @@ protected function putSlot(Item $item){ $this->putShort($item->getID()); $this->putByte($item->getCount()); $this->putShort($item->getDamage()); - $this->putShort(-1); + $nbt = $item->getCompoundTag(); + $this->putByte(strlen($nbt)); + $this->put($nbt); } } diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index 1225157e..b9ca9834 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -20,20 +20,32 @@ use pocketmine\network\protocol\DataPacket; use pocketmine\network\SourceInterface; use pocketmine\Player; -use pocketmine\utils\TextFormat; use shoghicp\BigBrother\BigBrother; use shoghicp\BigBrother\DesktopPlayer; -use shoghicp\BigBrother\network\protocol\ClientStatusPacket; -use shoghicp\BigBrother\network\protocol\CTSChatPacket; -use shoghicp\BigBrother\network\protocol\CTSCloseWindowPacket; -use shoghicp\BigBrother\network\protocol\EncryptionResponsePacket; -use shoghicp\BigBrother\network\protocol\LoginStartPacket; -use shoghicp\BigBrother\network\protocol\PlayerBlockPlacementPacket; -use shoghicp\BigBrother\network\protocol\PlayerDiggingPacket; -use shoghicp\BigBrother\network\protocol\PlayerLookPacket; -use shoghicp\BigBrother\network\protocol\PlayerPositionAndLookPacket; -use shoghicp\BigBrother\network\protocol\PlayerPositionPacket; -use shoghicp\BigBrother\network\protocol\UseEntityPacket; +use shoghicp\BigBrother\network\Info; //Computer Edition +use shoghicp\BigBrother\network\Packet; +use shoghicp\BigBrother\network\protocol\Login\EncryptionResponsePacket; +use shoghicp\BigBrother\network\protocol\Login\LoginStartPacket; +use shoghicp\BigBrother\network\protocol\Play\AnimatePacket; +use shoghicp\BigBrother\network\protocol\Play\ClientSettingsPacket; +use shoghicp\BigBrother\network\protocol\Play\ClientStatusPacket; +use shoghicp\BigBrother\network\protocol\Play\CreativeInventoryActionPacket; +use shoghicp\BigBrother\network\protocol\Play\CPlayerAbilitiesPacket; +use shoghicp\BigBrother\network\protocol\Play\CTSChatPacket; +use shoghicp\BigBrother\network\protocol\Play\CTSCloseWindowPacket; +use shoghicp\BigBrother\network\protocol\Play\HeldItemChangePacket; +use shoghicp\BigBrother\network\protocol\Play\KeepAlivePacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerArmSwingPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerBlockPlacementPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerDiggingPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerLookPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerPositionAndLookPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerPositionPacket; +use shoghicp\BigBrother\network\protocol\Play\PluginMessagePacket; +use shoghicp\BigBrother\network\protocol\Play\ResourcePackStatusPacket; +use shoghicp\BigBrother\network\protocol\Play\CTabCompletePacket; +use shoghicp\BigBrother\network\protocol\Play\UseEntityPacket; use shoghicp\BigBrother\network\translation\Translator; use shoghicp\BigBrother\utils\Binary; @@ -57,49 +69,61 @@ class ProtocolInterface implements SourceInterface{ protected $identifier = 0; - public function __construct(BigBrother $plugin, ServerThread $thread, Translator $translator){ + public function __construct(BigBrother $plugin, $server, Translator $translator){ $this->plugin = $plugin; + $this->server = $server; $this->translator = $translator; - $this->thread = $thread; + $this->thread = new ServerThread($server->getLogger(), $server->getLoader(), $plugin->getPort(), $plugin->getIp(), $plugin->getMotd(), $plugin->getDataFolder()."server-icon.png"); $this->sessions = new \SplObjectStorage(); } public function emergencyShutdown(){ - $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_EMERGENCY_SHUTDOWN)); + $this->thread->pushMainToThreadPacket(ServerManager::PACKET_EMERGENCY_SHUTDOWN); } public function shutdown(){ - foreach($this->sessionsPlayers as $player){ - $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", $this->plugin->getServer()->getProperty("settings.shutdown-message", "Server closed")); - } - $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_SHUTDOWN)); + $this->thread->pushMainToThreadPacket(ServerManager::PACKET_SHUTDOWN); } public function setName($name){ + $info = $this->plugin->getServer()->getQueryInformation(); + $value = [ + "MaxPlayers" => $info->getMaxPlayerCount(), + "OnlinePlayers" => $info->getPlayerCount(), + ]; + $buffer = ServerManager::PACKET_SET_OPTION.chr(strlen("name"))."name".json_encode($value); + $this->thread->pushMainToThreadPacket($buffer); + } + public function closeSession($identifier){ + if(isset($this->sessionsPlayers[$identifier])){ + $player = $this->sessionsPlayers[$identifier]; + unset($this->sessionsPlayers[$identifier]); + $player->close($player->getLeaveMessage(), "Connection closed"); + } } public function close(Player $player, $reason = "unknown reason"){ if(isset($this->sessions[$player])){ $identifier = $this->sessions[$player]; $this->sessions->detach($player); - unset($this->sessionsPlayers[$identifier]); - $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", "Connection closed"); + unset($this->identifiers[$identifier]); + $this->thread->pushMainToThreadPacket(ServerManager::PACKET_CLOSE_SESSION . Binary::writeInt($identifier)); }else{ return; } - $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_CLOSE_SESSION) . Binary::writeInt($identifier)); } protected function sendPacket($target, Packet $packet){ - $data = chr(ServerManager::PACKET_SEND_PACKET) . Binary::writeInt($target) . $packet->write(); + $data = ServerManager::PACKET_SEND_PACKET . Binary::writeInt($target) . $packet->write(); + $this->thread->pushMainToThreadPacket($data); } public function setCompression(DesktopPlayer $player, $threshold){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; - $data = chr(ServerManager::PACKET_SET_COMPRESSION) . Binary::writeInt($target) . Binary::writeInt($threshold); + $data = ServerManager::PACKET_SET_COMPRESSION . Binary::writeInt($target) . Binary::writeInt($threshold); $this->thread->pushMainToThreadPacket($data); } } @@ -107,7 +131,7 @@ public function setCompression(DesktopPlayer $player, $threshold){ public function enableEncryption(DesktopPlayer $player, $secret){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; - $data = chr(ServerManager::PACKET_ENABLE_ENCRYPTION) . Binary::writeInt($target) . $secret; + $data = ServerManager::PACKET_ENABLE_ENCRYPTION . Binary::writeInt($target) . $secret; $this->thread->pushMainToThreadPacket($data); } } @@ -161,12 +185,18 @@ protected function handlePacket(DesktopPlayer $player, $payload){ if($status === 1){ switch($pid){ + case 0x00: + $pk = new KeepAlivePacket(); + break; case 0x01: $pk = new CTSChatPacket(); break; case 0x02: $pk = new UseEntityPacket(); break; + case 0x03: + $pk = new PlayerPacket(); + break; case 0x04: $pk = new PlayerPositionPacket(); break; @@ -182,17 +212,61 @@ protected function handlePacket(DesktopPlayer $player, $payload){ case 0x08: $pk = new PlayerBlockPlacementPacket(); break; + case 0x09: + $pk = new HeldItemChangePacket(); + break; + case 0x0a: + $pk = new PlayerArmSwingPacket(); + break; + case 0x0b: + $pk = new AnimatePacket(); + break; + /*case 0x0c: + // + break;*/ case 0x0d: $pk = new CTSCloseWindowPacket(); break; + /*case 0x0e: + break; + case 0x0f: + + break;*/ + case 0x10: + $pk = new CreativeInventoryActionPacket(); + break; + /*case 0x11: + + break; + case 0x12: + + break;*/ + case 0x13: + $pk = new CPlayerAbilitiesPacket(); + break; + case 0x14: + $pk = new CTabCompletePacket(); + break; + case 0x15: + $pk = new ClientSettingsPacket(); + break; case 0x16: $pk = new ClientStatusPacket(); break; + case 0x17: + $pk = new PluginMessagePacket(); + break; + /*case 0x18: + // + break;*/ + case 0x19: + $pk = new ResourcePackStatusPacket(); + break; default: + echo "[Receive] 0x".bin2hex(chr($pid))."\n"; //Debug return; } - $pk->read($payload, $offset); $this->receivePacket($player, $pk); }elseif($status === 0){ @@ -205,7 +279,7 @@ protected function handlePacket(DesktopPlayer $player, $payload){ $pk->read($payload, $offset); $player->bigBrother_processAuthentication($this->plugin, $pk); }else{ - $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", "Unexpected packet $pid"); + $player->close($player->getLeaveMessage(), "Unexpected packet $pid"); } } } @@ -228,8 +302,15 @@ public function process(){ $payload = substr($buffer, $offset); try{ $this->handlePacket($this->sessionsPlayers[$id], $payload); - }catch(\Exception $e){ + }catch(\Exception $e){ + //if(\pocketmine\DEBUG > 1){ + $logger = $this->server->getLogger(); + if($logger instanceof MainLogger){ + $logger->debug("DesktopPacket 0x" . bin2hex($payload)); + $logger->logException($e); + } + //} } } }elseif($pid === ServerManager::PACKET_OPEN_SESSION){ @@ -245,21 +326,27 @@ public function process(){ $identifier = "$id:$address:$port"; - $player = new DesktopPlayer($this, $identifier, $address, $port); + $player = new DesktopPlayer($this, $identifier, $address, $port, $this->plugin); $this->sessions->attach($player, $id); $this->sessionsPlayers[$id] = $player; $this->plugin->getServer()->addPlayer($identifier, $player); }elseif($pid === ServerManager::PACKET_CLOSE_SESSION){ $id = Binary::readInt(substr($buffer, $offset, 4)); - if(!isset($this->sessionsPlayers[$id])){ - continue; + $offset += 4; + $flag = Binary::readInt(substr($buffer, $offset, 4)); + + if(isset($this->sessionsPlayers[$id])){ + if($flag === 0){ + $this->close($this->sessionsPlayers[$id]); + }else{ + $this->closeSession($id); + } } - $this->close($this->sessionsPlayers[$id]); } } return true; - } + } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/ServerManager.php b/src/shoghicp/BigBrother/network/ServerManager.php index 0180090a..c774db67 100755 --- a/src/shoghicp/BigBrother/network/ServerManager.php +++ b/src/shoghicp/BigBrother/network/ServerManager.php @@ -17,7 +17,6 @@ namespace shoghicp\BigBrother\network; -use pocketmine\utils\TextFormat; use shoghicp\BigBrother\utils\Binary; class ServerManager{ @@ -65,6 +64,8 @@ class ServerManager{ */ const PACKET_SET_COMPRESSION = 0x05; + const PACKET_SET_OPTION = 0x06; + /* * no payload */ @@ -87,12 +88,14 @@ class ServerManager{ protected $logger; protected $shutdown = false; - public $players = 0; - public $maxPlayers = 20; /** @var string[] */ public $sample = []; public $description; public $favicon; + public $serverdata = [ + "MaxPlayers" => 20, + "OnlinePlayers" => 0, + ]; public function __construct(ServerThread $thread, $port, $interface, $description = "", $favicon = null){ $this->thread = $thread; @@ -123,6 +126,16 @@ public function __construct(ServerThread $thread, $port, $interface, $descriptio $this->process(); } + public function getServerData(){ + return $this->serverdata; + } + + public function shutdown(){ + $this->thread->shutdown(); + usleep(50000); //Sleep for 1 tick + //$this->thread->kill(); + } + protected function processPacket(){ @fread($this->fp, 1); if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){ @@ -135,7 +148,7 @@ protected function processPacket(){ $data = substr($buffer, 4); if(!isset($this->sessions[$id])){ - $this->closeSession($id); + $this->closeSession($id, 0); return true; } $this->sessions[$id]->writeRaw($data); @@ -144,7 +157,7 @@ protected function processPacket(){ $secret = substr($buffer, 4); if(!isset($this->sessions[$id])){ - $this->closeSession($id); + $this->closeSession($id, 0); return true; } $this->sessions[$id]->enableEncryption($secret); @@ -153,20 +166,36 @@ protected function processPacket(){ $threshold = Binary::readInt(substr($buffer, 4, 4)); if(!isset($this->sessions[$id])){ - $this->closeSession($id); + $this->closeSession($id, 0); return true; } $this->sessions[$id]->setCompression($threshold); + }elseif($pid === self::PACKET_SET_OPTION){ + $offset = 1; + $len = ord($packet{$offset++}); + $name = substr($packet, $offset, $len); + $offset += $len; + $value = substr($packet, $offset); + switch($name){ + case "name": + $this->serverdata = json_decode($value, true); + break; + } }elseif($pid === self::PACKET_CLOSE_SESSION){ $id = Binary::readInt(substr($buffer, 0, 4)); if(isset($this->sessions[$id])){ $this->close($this->sessions[$id]); + }else{ + $this->closeSession($id, 1); } }elseif($pid === self::PACKET_SHUTDOWN){ - $this->shutdown = true; foreach($this->sessions as $session){ $session->close(); } + + $this->shutdown(); + stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); + $this->shutdown = true; }elseif($pid === self::PACKET_EMERGENCY_SHUTDOWN){ $this->shutdown = true; } @@ -186,8 +215,8 @@ public function openSession(Session $session){ $this->thread->pushThreadToMainPacket($data); } - protected function closeSession($id){ - $this->thread->pushThreadToMainPacket(chr(self::PACKET_CLOSE_SESSION) . Binary::writeInt($id)); + protected function closeSession($id, $flag){ + $this->thread->pushThreadToMainPacket(chr(self::PACKET_CLOSE_SESSION) . Binary::writeInt($id).Binary::writeInt($flag)); } private function process(){ @@ -237,6 +266,7 @@ public function close(Session $session){ fclose($this->sockets[$identifier]); unset($this->sockets[$identifier]); unset($this->sessions[$identifier]); - $this->closeSession($identifier); + $this->closeSession($identifier, 0); } + } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/ServerThread.php b/src/shoghicp/BigBrother/network/ServerThread.php index 7c210f7e..13265f10 100755 --- a/src/shoghicp/BigBrother/network/ServerThread.php +++ b/src/shoghicp/BigBrother/network/ServerThread.php @@ -52,7 +52,9 @@ class ServerThread extends Thread{ * * @throws \Exception */ - public function __construct(\Threaded $externalQueue, \Threaded $internalQueue, \ThreadedLogger $logger, \ClassLoader $loader, $port, $interface = "0.0.0.0", $motd = "Minecraft: PE server", $icon = null){ + public function __construct(\ThreadedLogger $logger, \ClassLoader $loader, $port, $interface = "0.0.0.0", $motd = "Minecraft: PE server", $icon = null){ + $this->externalQueue = new \Threaded; + $this->internalQueue = new \Threaded; $this->port = (int) $port; if($port < 1 or $port > 65536){ throw new \Exception("Invalid port range"); @@ -80,9 +82,6 @@ public function __construct(\Threaded $externalQueue, \Threaded $internalQueue, $this->externalSocket = $sockets[1]; stream_set_blocking($this->externalSocket, 0); - $this->externalQueue = $externalQueue; - $this->internalQueue = $internalQueue; - $this->start(); } @@ -105,10 +104,13 @@ public function isShutdown(){ } public function shutdown(){ - $this->lock(); $this->shutdown = true; - socket_close($this->internalSocket); - $this->unlock(); + } + + public function shutdownHandler(){ + if($this->shutdown !== true){ + $this->getLogger()->emergency("[ServerThread #". \Thread::getCurrentThreadId() ."] ServerThread crashed!"); + } } public function getPort(){ @@ -150,6 +152,7 @@ public function pushMainToThreadPacket($str){ } public function readMainToThreadPacket(){ + //echo "internalQueue: ".$this->internalQueue."\n"; return $this->internalQueue->shift(); } @@ -169,6 +172,9 @@ public function run(){ } } $this->loader->register(); + + register_shutdown_function([$this, "shutdownHandler"]); + $data = unserialize($this->data); $manager = new ServerManager($this, $this->port, $this->interface, $data["motd"], $data["icon"]); } diff --git a/src/shoghicp/BigBrother/network/Session.php b/src/shoghicp/BigBrother/network/Session.php index 9658b2fa..de81c57a 100755 --- a/src/shoghicp/BigBrother/network/Session.php +++ b/src/shoghicp/BigBrother/network/Session.php @@ -18,11 +18,13 @@ namespace shoghicp\BigBrother\network; use pocketmine\utils\TextFormat; -use shoghicp\BigBrother\network\protocol\LoginDisconnectPacket; -use shoghicp\BigBrother\network\protocol\PingPacket; +use shoghicp\BigBrother\network\protocol\Login\LoginDisconnectPacket; +use shoghicp\BigBrother\network\protocol\Login\PingPacket; use shoghicp\BigBrother\utils\AES; use shoghicp\BigBrother\utils\Binary; +use shoghicp\BigBrother\BigBrother; + class Session{ /** @var ServerManager */ private $manager; @@ -170,8 +172,8 @@ public function process(){ "protocol" => Info::PROTOCOL ], "players" => [ - "max" => $this->manager->maxPlayers, - "online" => $this->manager->players, + "max" => $this->manager->getServerData()["MaxPlayers"], + "online" => $this->manager->getServerData()["OnlinePlayers"], "sample" => $sample, ], "description" => json_decode(TextFormat::toJSON($this->manager->description)) @@ -223,7 +225,6 @@ public function process(){ $this->close("Unexpected packet $pid"); } } - } public function getID(){ diff --git a/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php b/src/shoghicp/BigBrother/network/protocol/Login/EncryptionRequestPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php rename to src/shoghicp/BigBrother/network/protocol/Login/EncryptionRequestPacket.php index 7f4b17fe..6aeee924 100755 --- a/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Login/EncryptionRequestPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Login; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php b/src/shoghicp/BigBrother/network/protocol/Login/EncryptionResponsePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php rename to src/shoghicp/BigBrother/network/protocol/Login/EncryptionResponsePacket.php index 506011dc..bfe8b93c 100755 --- a/src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Login/EncryptionResponsePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Login; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php b/src/shoghicp/BigBrother/network/protocol/Login/LoginDisconnectPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php rename to src/shoghicp/BigBrother/network/protocol/Login/LoginDisconnectPacket.php index 5a6f24bd..f28b9f73 100755 --- a/src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Login/LoginDisconnectPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Login; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php b/src/shoghicp/BigBrother/network/protocol/Login/LoginStartPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php rename to src/shoghicp/BigBrother/network/protocol/Login/LoginStartPacket.php index 4a3f681e..347c01d3 100755 --- a/src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Login/LoginStartPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Login; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php b/src/shoghicp/BigBrother/network/protocol/Login/LoginSuccessPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php rename to src/shoghicp/BigBrother/network/protocol/Login/LoginSuccessPacket.php index fa5ef6eb..3ff7c659 100755 --- a/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Login/LoginSuccessPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Login; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/PingPacket.php b/src/shoghicp/BigBrother/network/protocol/Login/PingPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/PingPacket.php rename to src/shoghicp/BigBrother/network/protocol/Login/PingPacket.php index 22bc4c64..0b72086e 100755 --- a/src/shoghicp/BigBrother/network/protocol/PingPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Login/PingPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Login; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php new file mode 100755 index 00000000..2625c9cf --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php @@ -0,0 +1,44 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class AnimatePacket extends Packet{ + + + + public $eid; + public $action; + public $jump; + + public function pid(){ + return 0x0b; + } + + public function encode(){ + $this->putVarInt($this->eid); + $this->putByte($this->actionID); + } + + public function decode(){ + $this->eid = $this->getVarInt(); + $this->actionID = $this->getVarInt(); + $this->jump = $this->getVarInt(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/BlockChangePacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/BlockChangePacket.php index a3e07c30..dc56cc65 100755 --- a/src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/BlockChangePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php new file mode 100755 index 00000000..3246d724 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php @@ -0,0 +1,73 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class CPlayerAbilitiesPacket extends Packet{ + + public $damageDisabled; + public $canFly; + public $isFlying = false; + public $isCreative; + + public $flyingSpeed; + public $walkingSpeed; + + public function pid(){ + return 0x13; + } + + public function encode(){ + } + + public function decode(){ + $flags = base_convert($this->getByte(), 10, 2); + if(strlen($flags) !== 8){ + $flags = str_repeat("0", 8 - strlen($flags)).$flags; + } + $flags = intval($flags); + + if(($flags & 0x08) !== 0){ + $this->damageDisabled = true; + }else{ + $this->damageDisabled = false; + } + + if(($flags & 0x04) !== 0){ + $this->canFly = true; + }else{ + $this->canFly = false; + } + + if(($flags & 0x02) !== 0){ + $this->isFlying = true; + }else{ + $this->isFlying = false; + } + + if(($flags & 0x01) !== 0){ + $this->isCreative = true; + }else{ + $this->isCreative = false; + } + + $this->flyingSpeed = $this->getFloat(); + $this->walkingSpeed = $this->getFloat(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CTSChatPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/CTSChatPacket.php index da7496ae..af15ae4f 100755 --- a/src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/CTSChatPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CTSCloseWindowPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/CTSCloseWindowPacket.php index 812bc5b6..0f1fb04c 100755 --- a/src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/CTSCloseWindowPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php new file mode 100755 index 00000000..0feeb390 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php @@ -0,0 +1,43 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class CTabCompletePacket extends Packet{ + + public $text; + public $x; + public $y; + public $z; + + public function pid(){ + return 0x14; + } + + public function encode(){ + } + + public function decode(){ + $this->text = $this->getString(); + $flag = (bool) $this->getByte(); + if($flag){ + $this->getPosition($this->x, $this->y, $this->z); + } + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ChangeGameStatePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/ChangeGameStatePacket.php index b6c0f1c3..c3a75b82 100755 --- a/src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/ChangeGameStatePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ChunkDataPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/ChunkDataPacket.php index e7b89299..0fb7eb11 100755 --- a/src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/ChunkDataPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php new file mode 100755 index 00000000..cd90c039 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php @@ -0,0 +1,47 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class ClickWindowPacket extends Packet{ + + public $windowID; + public $slot; + public $button; + public $actionID; + public $mode; + public $clickedItem; + + public function pid(){ + return 0x0e; + } + + public function encode(){ + + } + + public function decode(){ + $this->windowID = $this->getByte(); + $this->slot = $this->getShort(); + $this->button = $this->getByte(); + $this->actionID = $this->getShort(); + $this->mode = $this->getByte(); + $this->clickedItem = $this->getSlot(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php new file mode 100755 index 00000000..a8c74557 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php @@ -0,0 +1,45 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class ClientSettingsPacket extends Packet{ + + public $lang; + public $view; + public $chatmode; + public $chatcolor; + public $skinsetting; + + public function pid(){ + return 0x15; + } + + public function encode(){ + + } + + public function decode(){ + $this->lang = $this->getString(); + $this->view = $this->getByte(); + $this->chatmode = $this->getByte(); + $this->chatcolor = (bool) $this->getByte(); + $this->skinsetting = base_convert($this->getByte(), 10, 2); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ClientStatusPacket.php similarity index 90% rename from src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/ClientStatusPacket.php index 95e4e756..465d834a 100755 --- a/src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/ClientStatusPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; @@ -32,6 +32,6 @@ public function encode(){ } public function decode(){ - $this->actionID = $this->getByte(); + $this->actionID = $this->getVarInt(); } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php new file mode 100755 index 00000000..baf4cb25 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php @@ -0,0 +1,39 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class CreativeInventoryActionPacket extends Packet{ + + public $slot; + public $item; + + public function pid(){ + return 0x10; + } + + public function encode(){ + + } + + public function decode(){ + $this->slot = $this->getShort(); + $this->item = $this->getSlot(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/DestroyEntitiesPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/DestroyEntitiesPacket.php index f9081ec9..dd37085a 100755 --- a/src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/DestroyEntitiesPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php new file mode 100755 index 00000000..04b08e31 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php @@ -0,0 +1,41 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class EntityEquipmentPacket extends Packet{ + + public $eid; + public $slot; + public $item; + + public function pid(){ + return 0x04; + } + + public function encode(){ + $this->putVarInt($this->eid); + $this->putShort($this->slot); + $this->putSlot($this->item); + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityHeadLookPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/EntityHeadLookPacket.php index df5b3d0c..03445d32 100755 --- a/src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/EntityHeadLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityMetadataPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/EntityMetadataPacket.php index f69ee12e..d8ce5a51 100755 --- a/src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/EntityMetadataPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php new file mode 100755 index 00000000..018ef4e8 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php @@ -0,0 +1,37 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class EntityPacket extends Packet{ + + public $eid; + + public function pid(){ + return 0x14; + } + + public function encode(){ + $this->putVarInt($this->eid); + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityTeleportPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/EntityTeleportPacket.php index 4f3199aa..1174e5de 100755 --- a/src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/EntityTeleportPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityVelocityPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/EntityVelocityPacket.php index 6abe3cf2..1971b6ec 100755 --- a/src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/EntityVelocityPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php new file mode 100755 index 00000000..c5fde031 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php @@ -0,0 +1,37 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; +use shoghicp\BigBrother\utils\Binary; + +class HeldItemChangePacket extends Packet{ + + public $selectedSlot; + + public function pid(){ + return 0x09; + } + + public function encode(){ + } + + public function decode(){ + $this->selectedSlot = $this->getShort(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/JoinGamePacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/JoinGamePacket.php index 8c4405f2..71ae1a20 100755 --- a/src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/JoinGamePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/KeepAlivePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/KeepAlivePacket.php index 78037d2f..d20445cb 100755 --- a/src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/KeepAlivePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/OpenWindowPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/OpenWindowPacket.php index 3c8f0d37..9936c28b 100755 --- a/src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/OpenWindowPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayDisconnectPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayDisconnectPacket.php index e83050ac..f95dde31 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayDisconnectPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerAbilitiesPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayerAbilitiesPacket.php index 7242ce7e..a8356f2e 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerAbilitiesPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php new file mode 100755 index 00000000..db63950a --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php @@ -0,0 +1,34 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class PlayerArmSwingPacket extends Packet{ + + public function pid(){ + return 0x0a; + } + + public function encode(){ + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerBlockPlacementPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayerBlockPlacementPacket.php index e3e46f73..9884810b 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerBlockPlacementPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerDiggingPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayerDiggingPacket.php index 56ce3086..258bc5de 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerDiggingPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php new file mode 100755 index 00000000..889d2f12 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php @@ -0,0 +1,76 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; +use shoghicp\BigBrother\utils\Binary; + +class PlayerListPacket extends Packet{ + + const TYPE_ADD = 0; + const TYPE_REMOVE = 4; + + public $actionID; + public $players = []; + + public function pid(){ + return 0x38; + } + + public function clean(){ + $this->players = []; + return parent::clean(); + } + + public function encode(){ + $this->putVarInt($this->actionID); + $this->putVarInt(count($this->players)); + foreach($this->players as $player){ + if($this->actionID === self::TYPE_ADD){//add Player + $this->putLong(substr($player[0], 0, 16));//UUID + $this->putLong(substr($player[0], 16, 16)); + $this->putString($player[1]); //PlayerName + $this->putVarInt(count($player[2])); //Count Peropetry + + foreach($player[2] as $peropetrydata){ + $this->putString($peropetrydata["name"]); //Name + $this->putString($peropetrydata["value"]); //Value + if(isset($peropetrydata["signature"])){ + $this->putByte(1); //Is Signed + $this->putString($peropetrydata["signature"]); //Peropetry + }else{ + $this->putByte(0); //Is Signed + } + } + $this->putVarInt($player[3]); //Gamemode + $this->putVarInt($player[4]); //Ping + $this->putByte($player[5] ? 1 : 0); //has Display name + if($player[5] === true){ + $this->putString($player[6]); //Display name + } + }else{ + $this->putLong(substr($player[0], 0, 16));//UUID + $this->putLong(substr($player[0], 16, 16)); + } + } + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerLookPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayerLookPacket.php index 433537f4..56b9ac3c 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php new file mode 100755 index 00000000..c2ac93bb --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php @@ -0,0 +1,37 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class PlayerPacket extends Packet{ + + public $onGround; + + public function pid(){ + return 0x03; + } + + public function encode(){ + + } + + public function decode(){ + $this->onGround = (bool) $this->getByte(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionAndLookPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionAndLookPacket.php index 613df8ef..5809ff56 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionAndLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionPacket.php index 97bf8539..5cbe6400 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php new file mode 100755 index 00000000..05bea7e7 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php @@ -0,0 +1,58 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class PluginMessagePacket extends Packet{ + + public $channel; + public $data = []; + + public function pid(){ + return 0x17; + } + + public function encode(){ + } + + public function decode(){ + $this->channel = $this->getString(); + switch($this->channel){ + case "REGISTER": + $channels = bin2hex($this->getString()); + $channels = str_split($channels, 2); + $string = ""; + foreach($channels as $num => $str){ + if($str === "00"){ + $this->data[] = hex2bin($string); + $string = ""; + }else{ + $string .= $str; + if(count($channels) -1 === $num){ + $this->data[] = hex2bin($string); + } + } + } + break; + case "MC|Brand": + $this->data = $this->getString(); + break; + } + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PositionAndLookPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/PositionAndLookPacket.php index e750e49d..36bb28b9 100755 --- a/src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/PositionAndLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php new file mode 100755 index 00000000..378c01ff --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php @@ -0,0 +1,38 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class ResourcePackSendPacket extends Packet{ + + public $url; + + public function pid(){ + return 0x48; + } + + public function encode(){ + $this->putString($this->url); + $this->putString(sha1($this->url)); + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php new file mode 100755 index 00000000..4c751bcc --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php @@ -0,0 +1,39 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class ResourcePackStatusPacket extends Packet{ + + public $hash; + public $status; + + public function pid(){ + return 0x19; + } + + public function encode(){ + + } + + public function decode(){ + $this->hash = $this->getString(); + $this->status = $this->getVarInt(); + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/RespawnPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/RespawnPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/RespawnPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/RespawnPacket.php index 6c0b1ed3..e0bb93dd 100755 --- a/src/shoghicp/BigBrother/network/protocol/RespawnPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/RespawnPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/STCChatPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/STCChatPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/STCChatPacket.php index a20e3e53..bb0a4a38 100755 --- a/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/STCChatPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/STCCloseWindowPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/STCCloseWindowPacket.php index 303f8be8..903797d6 100755 --- a/src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/STCCloseWindowPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php new file mode 100755 index 00000000..e7508022 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php @@ -0,0 +1,39 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class STabCompletePacket extends Packet{ + + public $matches = []; + + public function pid(){ + return 0x3a; + } + + public function encode(){ + $this->putVarInt(count($this->matches)); + foreach($this->matches as $match){ + $this->putString($match); + } + } + + public function decode(){ + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php new file mode 100755 index 00000000..5509e270 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php @@ -0,0 +1,42 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class ScoreboardObjectivePacket extends Packet{ + + public $ObjectiveName; + public $Mode; + public $ObjectiveValue; + public $Type; + + public function pid(){ + return 0x3b; + } + + public function encode(){ + $this->putString($this->ObjectiveName); + $this->putByte($this->Mode); + $this->putString($this->ObjectiveValue); + $this->putString($this->Type); + } + + public function decode(){ + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php new file mode 100755 index 00000000..fe0b2e57 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php @@ -0,0 +1,36 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class ServerDifficultyPacket extends Packet{ + + public $difficulty; + + public function pid(){ + return 0x41; + } + + public function encode(){ + $this->putByte($this->difficulty); + } + + public function decode(){ + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/SetSlotPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/SetSlotPacket.php index f5c8c53e..edb7c14a 100755 --- a/src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/SetSlotPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/SpawnMobPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/SpawnMobPacket.php index 5494d579..e88f9ade 100755 --- a/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/SpawnMobPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/SpawnObjectPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/SpawnObjectPacket.php index fa137650..afd8e577 100755 --- a/src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/SpawnObjectPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/SpawnPlayerPacket.php similarity index 85% rename from src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/SpawnPlayerPacket.php index 9d77339f..5e0e5411 100755 --- a/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/SpawnPlayerPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; @@ -38,14 +38,16 @@ public function pid(){ public function encode(){ $this->putVarInt($this->eid); - $this->putString($this->uuid); + $this->putLong(substr($this->uuid, 0, 16));//UUID + $this->putLong(substr($this->uuid, 16, 16)); $this->putInt(intval($this->x * 32)); $this->putInt(intval($this->y * 32)); $this->putInt(intval($this->z * 32)); $this->putByte(($this->yaw / 360) << 8); $this->putByte(($this->pitch / 360) << 8); $this->putShort($this->item); - $this->put(Binary::writeMetadata($this->metadata)); + $meta = Binary::writeMetadata($this->metadata); + $this->put($meta); } public function decode(){ diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/SpawnPositionPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/SpawnPositionPacket.php index 4b71cdac..11fc78bf 100755 --- a/src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/SpawnPositionPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php new file mode 100755 index 00000000..b6c80b41 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php @@ -0,0 +1,42 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class StatisticsPacket extends Packet{ + + public $count; + public $statistic = []; + + public function pid(){ + return 0x37; + } + + public function encode(){ + $this->putVarInt($this->count); + foreach($this->statistic as $statistic){ + $this->putString($statistic[0]); + $this->putVarInt($statistic[1]); + } + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/TimeUpdatePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/TimeUpdatePacket.php index 47d45393..099ce867 100755 --- a/src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/TimeUpdatePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php new file mode 100755 index 00000000..957a0e11 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php @@ -0,0 +1,55 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class TitlePacket extends Packet{ + + const TYPE_SET_TITLE = 0; + const TYPE_SET_SUB_TITLE = 1; + const TYPE_SET_SETTINGS = 2; + const TYPE_HIDE = 3; + const TYPE_RESET = 4; + + public $actionID; + public $data = null; + + public function pid(){ + return 0x45; + } + + public function encode(){ + $this->putVarInt($this->actionID); + switch($this->actionID){ + case self::TYPE_SET_TITLE: + case self::TYPE_SET_SUB_TITLE: + $this->putString($this->data); + break; + case self::TYPE_SET_SETTINGS: + $this->putInt($this->data[0]); + $this->putInt($this->data[1]); + $this->putInt($this->data[2]); + break; + } + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/UpdateHealthPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/UpdateHealthPacket.php index c0d7372c..d3197649 100755 --- a/src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/UpdateHealthPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php new file mode 100755 index 00000000..fd7f976c --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php @@ -0,0 +1,47 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; +use shoghicp\BigBrother\utils\Binary; + +class UpdateSignPacket extends Packet{ + + public $x; + public $y; + public $z; + public $line1; + public $line2; + public $line3; + public $line4; + + public function pid(){ + return 0x33; + } + + public function encode(){ + $this->putPosition($this->x, $this->y, $this->z); + $this->putString($this->line1); + $this->putString($this->line2); + $this->putString($this->line3); + $this->putString($this->line4); + } + + public function decode(){ + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php new file mode 100755 index 00000000..e11bbad3 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php @@ -0,0 +1,40 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol\Play; + +use shoghicp\BigBrother\network\Packet; + +class UseBedPacket extends Packet{ + + public $eid; + public $bedX; + public $bedY; + public $bedZ; + + public function pid(){ + return 0x0a; + } + + public function encode(){ + $this->putVarInt($this->eid); + $this->putPosition($this->bedX, $this->bedY, $this->bedZ); + } + + public function decode(){ + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/UseEntityPacket.php similarity index 82% rename from src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/UseEntityPacket.php index ae252e53..fd1ea4dc 100755 --- a/src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/UseEntityPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; @@ -36,11 +36,10 @@ public function encode(){ public function decode(){ $this->target = $this->getVarInt(); $this->type = $this->getVarInt(); - if($this->type){ - //TODO - //$this->targetX = $this->getFloat(); - //$this->targetY = $this->getFloat(); - //$this->targetZ = $this->getFloat(); + if($this->type === 2){ + $this->targetX = $this->getFloat(); + $this->targetY = $this->getFloat(); + $this->targetZ = $this->getFloat(); } } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/WindowItemsPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php rename to src/shoghicp/BigBrother/network/protocol/Play/WindowItemsPacket.php index 02fb1dd2..7cfca2ac 100755 --- a/src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/Play/WindowItemsPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol; +namespace shoghicp\BigBrother\network\protocol\Play; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/translation/Translator_81.php b/src/shoghicp/BigBrother/network/translation/Translator_81.php index 68b4e4b5..afaf3380 100755 --- a/src/shoghicp/BigBrother/network/translation/Translator_81.php +++ b/src/shoghicp/BigBrother/network/translation/Translator_81.php @@ -17,51 +17,112 @@ namespace shoghicp\BigBrother\network\translation; +use pocketmine\Achievement; +use pocketmine\Player; +use pocketmine\entity\Entity; use pocketmine\item\Item; +use pocketmine\Block\Block; +use pocketmine\level\Level; +use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\protocol\AddItemEntityPacket; +use pocketmine\network\protocol\AddPaintingPacket; +use pocketmine\network\protocol\AddPlayerPacket; +use pocketmine\network\protocol\AdventureSettingsPacket; +use pocketmine\network\protocol\AnimatePacket; use pocketmine\network\protocol\ContainerClosePacket; +use pocketmine\network\protocol\ContainerOpenPacket; +use pocketmine\network\protocol\ContainerSetContentPacket; +use pocketmine\network\protocol\ContainerSetDataPacket; +use pocketmine\network\protocol\ContainerSetSlotPacket; +use pocketmine\network\protocol\CraftingDataPacket; +use pocketmine\network\protocol\CraftingEventPacket; use pocketmine\network\protocol\DataPacket; +use pocketmine\network\protocol\DropItemPacket; +use pocketmine\network\protocol\FullChunkDataPacket; use pocketmine\network\protocol\Info; +use pocketmine\network\protocol\SetEntityLinkPacket; +use pocketmine\network\protocol\TileEntityDataPacket; +use pocketmine\network\protocol\EntityEventPacket; +use pocketmine\network\protocol\ExplodePacket; +use pocketmine\network\protocol\HurtArmorPacket; use pocketmine\network\protocol\InteractPacket; -use pocketmine\network\protocol\MessagePacket; +use pocketmine\network\protocol\LevelEventPacket; +use pocketmine\network\protocol\DisconnectPacket; +use pocketmine\network\protocol\TextPacket; +use pocketmine\network\protocol\MoveEntityPacket; use pocketmine\network\protocol\MovePlayerPacket; +use pocketmine\network\protocol\PlayerActionPacket; +use pocketmine\network\protocol\MobArmorEquipmentPacket; +use pocketmine\network\protocol\MobEquipmentPacket; use pocketmine\network\protocol\RemoveBlockPacket; +use pocketmine\network\protocol\RemoveEntityPacket; +use pocketmine\network\protocol\RemovePlayerPacket; use pocketmine\network\protocol\RespawnPacket; +use pocketmine\network\protocol\SetDifficultyPacket; +use pocketmine\network\protocol\SetEntityDataPacket; +use pocketmine\network\protocol\SetEntityMotionPacket; +use pocketmine\network\protocol\SetSpawnPositionPacket; +use pocketmine\network\protocol\TakeItemEntityPacket; +use pocketmine\network\protocol\TileEventPacket; +use pocketmine\network\protocol\UpdateBlockPacket; use pocketmine\network\protocol\UseItemPacket; +use pocketmine\math\Vector3; +use pocketmine\nbt\NBT; +use pocketmine\tile\Tile; use pocketmine\utils\TextFormat; +use shoghicp\BigBrother\BigBrother; use shoghicp\BigBrother\DesktopPlayer; +use shoghicp\BigBrother\network\Info as CInfo; //Computer Edition use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\network\protocol\BlockChangePacket; -use shoghicp\BigBrother\network\protocol\ChangeGameStatePacket; -use shoghicp\BigBrother\network\protocol\DestroyEntitiesPacket; -use shoghicp\BigBrother\network\protocol\EntityHeadLookPacket; -use shoghicp\BigBrother\network\protocol\EntityMetadataPacket; -use shoghicp\BigBrother\network\protocol\EntityTeleportPacket; -use shoghicp\BigBrother\network\protocol\EntityVelocityPacket; -use shoghicp\BigBrother\network\protocol\JoinGamePacket; -use shoghicp\BigBrother\network\protocol\OpenWindowPacket; -use shoghicp\BigBrother\network\protocol\PlayerAbilitiesPacket; -use shoghicp\BigBrother\network\protocol\PositionAndLookPacket; -use shoghicp\BigBrother\network\protocol\SetSlotPacket; -use shoghicp\BigBrother\network\protocol\SpawnObjectPacket; -use shoghicp\BigBrother\network\protocol\SpawnPlayerPacket; -use shoghicp\BigBrother\network\protocol\SpawnPositionPacket; -use shoghicp\BigBrother\network\protocol\STCChatPacket; -use shoghicp\BigBrother\network\protocol\STCCloseWindowPacket; -use shoghicp\BigBrother\network\protocol\TimeUpdatePacket; -use shoghicp\BigBrother\network\protocol\UpdateHealthPacket; -use shoghicp\BigBrother\network\protocol\WindowItemsPacket; +use shoghicp\BigBrother\network\protocol\Login\LoginDisconnectPacket; +use shoghicp\BigBrother\network\protocol\Play\AnimatePacket as CAnimatePacket; +use shoghicp\BigBrother\network\protocol\Play\BlockChangePacket; +use shoghicp\BigBrother\network\protocol\Play\ChangeGameStatePacket; +use shoghicp\BigBrother\network\protocol\Play\DestroyEntitiesPacket; +use shoghicp\BigBrother\network\protocol\Play\EntityEquipmentPacket; +use shoghicp\BigBrother\network\protocol\Play\EntityHeadLookPacket; +use shoghicp\BigBrother\network\protocol\Play\EntityMetadataPacket; +use shoghicp\BigBrother\network\protocol\Play\EntityTeleportPacket; +use shoghicp\BigBrother\network\protocol\Play\EntityVelocityPacket; +use shoghicp\BigBrother\network\protocol\Play\JoinGamePacket; +use shoghicp\BigBrother\network\protocol\Play\OpenWindowPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayDisconnectPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerAbilitiesPacket; +use shoghicp\BigBrother\network\protocol\Play\PlayerListPacket; +use shoghicp\BigBrother\network\protocol\Play\PositionAndLookPacket; +use shoghicp\BigBrother\network\protocol\Play\STabComletePacket; +use shoghicp\BigBrother\network\protocol\Play\ScoreboardObjectivePacket; +use shoghicp\BigBrother\network\protocol\Play\ServerDifficultyPacket; +use shoghicp\BigBrother\network\protocol\Play\SetSlotPacket; +use shoghicp\BigBrother\network\protocol\Play\SpawnObjectPacket; +use shoghicp\BigBrother\network\protocol\Play\SpawnPlayerPacket; +use shoghicp\BigBrother\network\protocol\Play\SpawnPositionPacket; +use shoghicp\BigBrother\network\protocol\Play\StatisticsPacket; +use shoghicp\BigBrother\network\protocol\Play\RespawnPacket as CRespawnPacket; +use shoghicp\BigBrother\network\protocol\Play\STCChatPacket; +use shoghicp\BigBrother\network\protocol\Play\STCCloseWindowPacket; +use shoghicp\BigBrother\network\protocol\Play\TimeUpdatePacket; +use shoghicp\BigBrother\network\protocol\Play\UpdateHealthPacket; +use shoghicp\BigBrother\network\protocol\Play\UpdateSignPacket; +use shoghicp\BigBrother\network\protocol\Play\UseBedPacket; +use shoghicp\BigBrother\network\protocol\Play\WindowItemsPacket; use shoghicp\BigBrother\utils\Binary; class Translator_81 implements Translator{ - - public function interfaceToServer(DesktopPlayer $player, Packet $packet){ + if($packet->pid() !== 0x00 and $packet->pid() !== 0x03 and $packet->pid() !== 0x04 and $packet->pid() !== 0x05 and $packet->pid() !== 0x06){ + echo "[Receive] 0x".bin2hex(chr($packet->pid()))."\n"; //Debug + } switch($packet->pid()){ - // TODO: move to Info + case 0x00: //KeepAlivePacket + $pk->id = mt_rand(); + $player->putRawPacket($pk); + return null; - case 0x01: //CTSChatPacket - $pk = new MessagePacket(); + case 0x01: //ChatPacket + $pk = new TextPacket(); + $pk->type = 1;//Chat Type $pk->source = ""; $pk->message = $packet->message; return $pk; @@ -69,13 +130,17 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x02: //UseEntityPacket $pk = new InteractPacket(); $pk->target = $packet->target; - $pk->action = $packet->mouse; + $pk->action = $packet->type; return $pk; - case 0x04: //PlayerPositionPacket + case 0x03: //PlayerPacket + $player->setSetting(["onGround" => $packet->onGround]); + return null; + + case 0x04: //PlayerPositonPacket $pk = new MovePlayerPacket(); $pk->x = $packet->x; - $pk->y = $packet->y; + $pk->y = $packet->y + $player->getEyeHeight(); $pk->z = $packet->z; $pk->yaw = $player->yaw; $pk->bodyYaw = $player->yaw; @@ -85,7 +150,7 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x05: //PlayerLookPacket $pk = new MovePlayerPacket(); $pk->x = $player->x; - $pk->y = $player->y; + $pk->y = $player->y + $player->getEyeHeight(); $pk->z = $player->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; @@ -95,7 +160,7 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x06: //PlayerPositionAndLookPacket $pk = new MovePlayerPacket(); $pk->x = $packet->x; - $pk->y = $packet->y; + $pk->y = $packet->y + $player->getEyeHeight(); $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; @@ -103,61 +168,334 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ return $pk; case 0x07: //PlayerDiggingPacket - if($packet->status === 2 or ($player->getGamemode() === 1 and $packet->status === 0)){ //Finished digging - $pk = new RemoveBlockPacket(); - $pk->eid = 0; + switch($packet->status){ + case 0: + if($player->getGamemode() === 1){ + $pk = new RemoveBlockPacket(); + $pk->eid = 0; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + return $pk; + }else{ + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_START_BREAK; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->face; + return $pk; + } + break; + case 1: + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_ABORT_BREAK; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->face; + return $pk; + break; + case 2: + if($player->getGamemode() !== 1){ + $packets = []; + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_STOP_BREAK; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->face; + $packets[] = $pk; + + $pk = new RemoveBlockPacket(); + $pk->eid = 0; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $packets[] = $pk; + return $packets; + }else{ + echo "PlayerDiggingPacket: ".$packet->status."\n"; + } + break; + default: + echo "PlayerDiggingPacket: ".$packet->status."\n"; + break; + } + + return null; + + case 0x08; //PlayerBlockPlacementPacket + echo "PlayerBlockPlacementPacket: ".$packet->direction."\n"; + + if($packet->direction !== 255){ + $pk = new UseItemPacket(); $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; + $pk->face = $packet->direction; + $pk->item = $packet->heldItem; + $pk->fx = $packet->cursorX / 16; + $pk->fy = $packet->cursorY / 16; + $pk->fz = $packet->cursorZ / 16; + $pk->posX = $player->getX(); + $pk->posY = $player->getY(); + $pk->posZ = $player->getZ(); return $pk; + }else{ + } + return null; - case 0x08; //PlayerBlockPlacementPacket - $pk = new UseItemPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->face = $packet->direction; - $pk->item = $packet->heldItem->getID(); - $pk->meta = $packet->heldItem->getDamage(); + case 0x09: //HeldItemChangePacket + $item = $player->getInventory()->getItem($packet->selectedSlot); + $olditem = $player->getInventory()->getItem($player->getInventory()->getHeldItemIndex()); + + if($item->getId() !== 0 or $item->getId() === 0 and $olditem->getId() !== 0){ + $pk = new MobEquipmentPacket(); + $pk->eid = 0; + $pk->item = $item; + if($item->getId() === 0){ + $pk->slot = 255; + }else{ + $pk->slot = Item::getCreativeItemIndex($item) + 9; + } + $pk->selectedSlot = $packet->selectedSlot; + return $pk; + } + + return null; + + case 0x0a: //PlayerArmSwingPacket + $pk = new AnimatePacket(); + $pk->action = 1; $pk->eid = 0; - $pk->fx = $packet->cursorX / 16; - $pk->fy = $packet->cursorY / 16; - $pk->fz = $packet->cursorZ / 16; return $pk; + case 0x0b: //AnimatePacket + switch($packet->actionID){ + case 0: + if(!$player->getSetting("isFlying")){ + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_START_SNEAK; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + } + break; + case 1: + if(!$player->getSetting("isFlying")){ + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_STOP_SNEAK; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + } + break; + case 2: + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_STOP_SLEEPING; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + break; + case 3: + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_START_SPRINT; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + break; + case 4: + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_STOP_SPRINT; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + break; + /*case 6: + + break;*/ + default: + echo "[AnimatePacket] ".$packet->actionID."\n";//Debug Code + break; + } + return null; + case 0x0d: //CTSCloseWindowPacket - $pk = new ContainerClosePacket(); - $pk->windowid = $packet->windowID; - return $pk; + if($packet->windowID !== 0x00){ + $pk = new ContainerClosePacket(); + $pk->windowid = $packet->windowID; + return $pk; + } - case 0x16: //ClientStatusPacket - if($packet->actionID === 0){ - $pk = new RespawnPacket(); - $pk->eid = 0; - $pk->x = $player->getSpawn()->getX(); - $pk->y = $player->getSpawn()->getX(); - $pk->z = $player->getSpawn()->getX(); + case 0x10: //CreativeInventoryActionPacket + echo "Slot: ".$packet->slot."\n"; + echo "ItemId: ".$packet->item->getId()." : ".$packet->item->getDamage()."\n"; + + /*if($packet->slot === 65535){ + $pk = new DropItemPacket(); + $pk->type = 0; + $pk->item = $packet->item; return $pk; + }else{ + $pk = new ContainerSetSlotPacket(); + $pk->windowid = 0; + $pk->slot = $packet->slot; + $pk->item = $packet->item; + return $pk; + }*/ + + return null; + + case 0x13: //CPlayerAbilitiesPacket + $player->setSetting(["isFlying" => $packet->isFlying]); + return null; + + case 0x14: //CTabCompletePacket + /*$pk = new STabComletePacket(); + + foreach($player->getServer()->getCommandMap()->getCommands() as $command){ + if($command->testPermissionSilent($player)){ + $pk->matches[] = $command->getName(); + } + } + + foreach($player->getServer()->getOnlinePlayers() as $packetplayer){ + $pk->matches[] = $packetplayer->getName(); + } + + //TODO + + //echo $packet->text."\n"; + + return $pk;*/ + return null; + + case 0x15: //ClientSettingsPacket + $player->setSetting([ + "Lang" => $packet->lang, + "View" => $packet->view, + "ChatMode" => $packet->chatmode, + "ChatColor" => $packet->chatcolor, + "SkinSettings" => $packet->skinsetting, + ]); + + return null; + + case 0x16: //ClientStatusPacket + switch($packet->actionID){ + case 0: + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_RESPAWN; + $pk->x = 0; + $pk->y = 0; + $pk->z = 0; + $pk->face = 0; + return $pk; + break; + case 1: + $statistic = []; + $statistic[] = ["achievement.openInventory", 1];// + foreach($player->achievements as $achievement => $count){ + $statistic[] = ["achievement.".$achievement, $count]; + } + + //stat + //https://gist.github.com/thinkofdeath/a1842c21a0cf2e1fb5e0 + + $pk = new StatisticsPacket(); + $pk->count = count($statistic);//TODO stat + $pk->statistic = $statistic; + $player->putRawPacket($pk); + break; + case 2: + //$player->awardAchievement("openInventory"); this for DesktopPlayer + //Achievement::broadcast($player, "openInventory");//Debug + break; } return null; + case 0x17: //PluginMessagePacket + switch($packet->channel){ + case "REGISTER"://Mods Register + $player->setSetting(["Channels" => $packet->data]); + break; + case "MC|Brand": //ServerType + $player->setSetting(["ServerType" => $packet->data]); + break; + default: + echo "PluginChannel: ".$packet->channel."\n"; + break; + } + return null; + + case 0x19: //ResourcePackStatusPacket + $player->setSetting(["ResourceStatus" => $packet->status, "ResourceHash" => $packet->hash]); + return null; + default: + echo "[Receive] 0x".bin2hex(chr($packet->pid()))."\n"; return null; } } public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ switch($packet->pid()){ + case Info::DISCONNECT_PACKET: + if($player->bigBrother_getStatus() === 0){ + $pk = new LoginDisconnectPacket(); + $pk->reason = TextFormat::toJSON($packet->message === "" ? "You have been disconnected." : $packet->message); + }else{ + $pk = new PlayDisconnectPacket(); + $pk->reason = TextFormat::toJSON($packet->message === "" ? "You have been disconnected." : $packet->message); + } + return $pk; - case Info::UPDATE_BLOCK_PACKET: - $pk = new BlockChangePacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->blockId = $packet->block; - $pk->blockMeta = $packet->meta; + case Info::TEXT_PACKET: + + echo $player->getSetting("Lang")."\n"; + + /*if($packet->message === "chat.type.achievement"){ + /*$pk = new ScoreboardObjectivePacket(); + $pk->ObjectiveName = $packet->parameters[0]; + $pk->Mode = 0; + $pk->ObjectiveValue = 3; + return $pk;*/ + /*echo "TextPacket: achievement\n"; + return null; + }else{ + $pk = new STCChatPacket(); + $pk->message = BigBrother::toJSON($packet->message, $packet->type, $packet->parameters); + }*/ + + //return $pk; + return null; + + case Info::SET_TIME_PACKET: + $pk = new TimeUpdatePacket(); + $pk->age = $packet->time; + $pk->time = $packet->time; //TODO: calculate offset from MCPE return $pk; case Info::START_GAME_PACKET: @@ -165,13 +503,19 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk = new JoinGamePacket(); $pk->eid = $packet->eid; - $pk->gamemode = $player->getGamemode(); + $pk->gamemode = $packet->gamemode; $pk->dimension = 0; $pk->difficulty = $player->getServer()->getDifficulty(); $pk->maxPlayers = $player->getServer()->getMaxPlayers(); $pk->levelType = "default"; $packets[] = $pk; + $pk = new SpawnPositionPacket(); + $pk->spawnX = $packet->spawnX; + $pk->spawnY = $packet->spawnY; + $pk->spawnZ = $packet->spawnZ; + $packets[] = $pk; + $pk = new PlayerAbilitiesPacket(); $pk->flyingSpeed = 0.05; $pk->walkingSpeed = 0.1; @@ -179,22 +523,6 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; $pk->isFlying = false; $pk->isCreative = ($player->getGamemode() & 0x01) > 0; - if($player->spawned === true){ - $packets = [$pk]; - - $pk = new ChangeGameStatePacket(); - $pk->reason = 3; - $pk->value = $player->getGamemode(); - $packets[] = $pk; - return $packets; - }else{ - $packets[] = $pk; - } - - $pk = new SpawnPositionPacket(); - $pk->spawnX = $packet->spawnX; - $pk->spawnY = $packet->spawnY; - $pk->spawnZ = $packet->spawnZ; $packets[] = $pk; $pk = new PositionAndLookPacket(); @@ -203,47 +531,126 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->z = $packet->z; $pk->yaw = $player->yaw; $pk->pitch = $player->pitch; - $pk->onGround = $player->isOnGround(); $packets[] = $pk; + return $packets; - case Info::SET_HEALTH_PACKET: - $pk = new UpdateHealthPacket(); - $pk->health = $packet->health; - $pk->food = 20; - $pk->saturation = 5; - return $pk; + case Info::ADD_PLAYER_PACKET: + $packets = []; + $packetplayer = $player->getServer()->getPlayerExact($packet->username); + + $pk = new PlayerListPacket(); + $pk->actionID = PlayerListPacket::TYPE_ADD; + + $pk->players[] = [ + $packetplayer->getUniqueId()->toBinary(), + $packetplayer->getName(), + [], + $packetplayer->getGamemode(), + 0, + false, + ]; - /*case Info::MESSAGE_PACKET: - $pk = new STCChatPacket(); + if($packetplayer instanceof DesktopPlayer){ + $pk->players[0][2] = $packetplayer->bigBrother_getPeroperties(); + } + $packets[] = $pk; - $pk->message = TextFormat::toJSON($packet->message); - return $pk;*/ + $pk = new SpawnPlayerPacket(); + $pk->eid = $packet->eid; + $pk->uuid = $packetplayer->getUniqueId()->toBinary(); + $pk->x = $packet->x; + $pk->z = $packet->z; + $pk->y = $packet->y; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; + $pk->item = $packetplayer->getInventory()->getItemInHand()->getId(); + $pk->metadata = $packet->metadata; + $packets[] = $pk; - case Info::SET_TIME_PACKET: - $pk = new TimeUpdatePacket(); - $pk->age = $packet->time; - $pk->time = $packet->time; //TODO: calculate offset from MCPE - return $pk; + $pk = new EntityTeleportPacket(); + $pk->eid = $packet->eid; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; + $packets[] = $pk; - case Info::SET_SPAWN_POSITION_PACKET: - $pk = new SpawnPositionPacket(); - $pk->spawnX = $packet->x; - $pk->spawnY = $packet->y; - $pk->spawnZ = $packet->z; - return $pk; + return $packets; + + /*case Info::ADD_ENTITY_PACKET: + return null;*/ + + case Info::REMOVE_PLAYER_PACKET: + $pk = new PlayerListPacket(); + $pk->actionID = PlayerListPacket::TYPE_REMOVE; + + $pk->players[] = [ + $packet->clientId->toBinary() + ]; + $packets[] = $pk; + + $pk = new DestroyEntitiesPacket(); + $pk->ids[] = $packet->eid; + $packets[] = $pk; + + return $packets; case Info::REMOVE_ENTITY_PACKET: - //case Info::REMOVE_PLAYER_PACKET: $pk = new DestroyEntitiesPacket(); $pk->ids[] = $packet->eid; return $pk; + case Info::ADD_ITEM_ENTITY_PACKET: + $packets = []; + $pk = new SpawnObjectPacket(); + $pk->eid = $packet->eid; + $pk->type = 2; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->yaw = $player->yaw; + $pk->pitch = $player->pitch; + $packets[] = $pk; + + $pk = new EntityMetadataPacket(); + $pk->eid = $packet->eid; + $pk->metadata = [ + 0 => [0 => 0, 1 => 0], + 10 => [0 => 5, 1 => $packet->item], + ]; + $packets[] = $pk; + + return $packets; + + /*case Info::REMOVE_ITEM_ENTITY_PACKET: + return null;*/ + + case Info::MOVE_ENTITY_PACKET: + $packets = []; + foreach($packet->entities as $d){ + $pk = new EntityTeleportPacket(); + $pk->eid = $d[0]; + $pk->x = $d[1]; + $pk->y = $d[2] - $player->getEyeHeight(); + $pk->z = $d[3]; + $pk->yaw = $d[4]; + $pk->pitch = $d[6]; + $packets[] = $pk; + + $pk = new EntityHeadLookPacket(); + $pk->eid = $d[0]; + $pk->yaw = $d[5]; + $packets[] = $pk; + } + return $packets; + case Info::MOVE_PLAYER_PACKET: if($packet->eid === 0){ $pk = new PositionAndLookPacket(); $pk->x = $packet->x; - $pk->y = $packet->y; + $pk->y = $packet->y - $player->getEyeHeight(); $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; @@ -254,7 +661,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk = new EntityTeleportPacket(); $pk->eid = $packet->eid; $pk->x = $packet->x; - $pk->y = $packet->y; + $pk->y = $packet->y - $player->getEyeHeight(); $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; @@ -267,25 +674,62 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return $packets; } - case Info::MOVE_ENTITY_PACKET: + case Info::UPDATE_BLOCK_PACKET://TODO + $pk = new BlockChangePacket(); + $count = count($packet->records) - 1; + $pk->x = $packet->records[$count][0]; + $pk->y = $packet->records[$count][2]; + $pk->z = $packet->records[$count][1]; + $pk->blockId = $packet->records[$count][3]; + $pk->blockMeta = $packet->records[$count][4]; + return $pk; + + case Info::MOB_EQUIPMENT_PACKET: + $pk = new EntityEquipmentPacket(); + $pk->eid = $packet->eid; + $pk->slot = $packet->slot; + $pk->item = $packet->item; + + return $pk; + + case Info::MOB_ARMOR_EQUIPMENT_PACKET: $packets = []; - foreach($packet->entities as $d){ - $pk = new EntityTeleportPacket(); - $pk->eid = $d[0]; - $pk->x = $d[1]; - $pk->y = $d[2]; - $pk->z = $d[3]; - $pk->yaw = $d[4]; - $pk->pitch = $d[5]; - $packets[] = $pk; - $pk = new EntityHeadLookPacket(); - $pk->eid = $d[0]; - $pk->yaw = $d[4]; + foreach($packet->slots as $num => $item){ + $pk = new EntityEquipmentPacket(); + $pk->eid = $packet->eid; + $pk->slot = $num + 1; + $pk->item = $item; $packets[] = $pk; } + return $packets; + case Info::SET_ENTITY_DATA_PACKET: + /*if(isset($packet->metadata[16])){ + if($packet->metadata[16][1] === 2){ + $pk = new UseBedPacket(); //Bug + $pk->eid = $packet->eid; + $bedXYZ = $player->getSetting("BedXYZ"); + $pk->bedX = $bedXYZ[0]; + $pk->bedY = $bedXYZ[1]; + $pk->bedZ = $bedXYZ[2]; + $player->removeSetting("BedXYZ"); + }else{ + $pk = new CAnimatePacket(); + $pk->eid = $packet->eid; + $pk->actionID = 2; + } + return $pk; + }elseif(isset($packet->metadata[17])){ + $player->setSetting(["BedXYZ" => $packet->metadata[17][1]]); + }*/ + + $pk = new EntityMetadataPacket(); + $pk->eid = $packet->eid; + $pk->metadata = $packet->metadata; + return $pk; + case Info::SET_ENTITY_MOTION_PACKET: $packets = []; foreach($packet->entities as $d){ @@ -298,13 +742,49 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ } return $packets; - /* - case Info::CONTAINER_CLOSE_PACKET: - $pk = new STCCloseWindowPacket(); - $pk->windowID = $packet->windowid; + case Info::SET_HEALTH_PACKET: + $pk = new UpdateHealthPacket(); + $pk->health = $packet->health; + $pk->food = 20; + $pk->saturation = 5; + return $pk; + + case Info::SET_SPAWN_POSITION_PACKET: + $pk = new SpawnPositionPacket(); + $pk->spawnX = $packet->x; + $pk->spawnY = $packet->y; + $pk->spawnZ = $packet->z; return $pk; - case Info::CONTAINER_OPEN_PACKET: + case Info::ANIMATE_PACKET: + switch($packet->action){ + case 1: + $pk = new CAnimatePacket(); + $pk->actionID = 0; + $pk->eid = $packet->eid; + return $pk; + break; + case 3: //LeaveBed + $pk = new CAnimatePacket(); + $pk->actionID = 2; + $pk->eid = $packet->eid; + return $pk; + break; + default: + echo "AnimatePacket: ".$packet->action."\n"; + break; + } + return null; + + case Info::RESPAWN_PACKET: + $pk = new CRespawnPacket(); + $pk->dimension = 0; + $pk->difficulty = $player->getServer()->getDifficulty(); + $pk->gamemode = $player->getGamemode(); + $pk->levelType = "default"; + return $pk; + + /*case Info::CONTAINER_OPEN_PACKET: $pk = new OpenWindowPacket(); $pk->windowID = $packet->windowid; $pk->inventoryType = $packet->type; @@ -312,11 +792,17 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->slots = $packet->slots; return $pk; + case Info::CONTAINER_CLOSE_PACKET: + $pk = new STCCloseWindowPacket(); + $pk->windowID = $packet->windowid; + return $pk; + case Info::CONTAINER_SET_SLOT_PACKET: + echo "ContainerSetSlotPacket: 0x".bin2hex(chr($packet->windowid))."\n"; $pk = new SetSlotPacket(); $pk->windowID = $packet->windowid; - if($pk->windowID === 0){ - $pk->slot = $packet->slot + 9; + if($pk->windowID === 0x00){ + $pk->slot = $packet->slot + 36; }elseif($pk->windowID === 0x78){ $pk->windowID = 0; $pk->slot = $packet->slot + 5; @@ -326,10 +812,10 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->item = $packet->item; return $pk; - case Info::CONTAINER_SET_CONTENT_PACKET: - $pk = new WindowItemsPacket(); - $pk->windowID = $packet->windowid; - if($pk->windowID === 0 or $pk->windowID === 0x78){ + case Info::CONTAINER_SET_CONTENT_PACKET://Bug + echo "ContainerSetContentPacket: 0x".bin2hex(chr($packet->windowid))."\n"; + if($packet->windowid !== 0x79 and $packet->windowid !== 0x78){ + $pk = new WindowItemsPacket(); $pk->windowID = 0; for($i = 0; $i < 5; ++$i){ $pk->items[] = Item::get(Item::AIR, 0, 0); @@ -338,66 +824,83 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->items[] = $player->getInventory()->getChestplate(); $pk->items[] = $player->getInventory()->getLeggings(); $pk->items[] = $player->getInventory()->getBoots(); - $slots = $player->getInventory()->getSize(); - for($i = 0; $i < $slots; ++$i){ + + if($player->getGamemode() === 0){ + for($i = 9; $i < 36; ++$i){ + $pk->items[] = $player->getInventory()->getItem($i); + } + }else{ + for($i = 0; $i < 27; ++$i){ + $pk->items[] = Item::get(Item::AIR, 0, 0); + } + } + for($i = 0; $i < 9; ++$i){ $pk->items[] = $player->getInventory()->getItem($i); } + return $pk; + } + return null;*/ + + case Info::CRAFTING_DATA_PACKET: + $player->setSetting(["Recipes" => $packet->entries, "cleanRecipes" => $packet->cleanRecipes]); + return null; + + case Info::BLOCK_ENTITY_DATA_PACKET: + $nbt = new NBT(NBT::LITTLE_ENDIAN); + $nbt->read($packet->namedtag); + $nbt = $nbt->getData(); + if($nbt["id"] !== Tile::SIGN){ + return null; }else{ - $pk->items = $packet->slots; + $index = Level::chunkHash($packet->x >> 4, $packet->z >> 4); + if(isset($player->usedChunks[$index]) and $player->usedChunks[$index]){ + $pk = new UpdateSignPacket(); + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->line1 = BigBrother::toJSON($nbt["Text1"]); + $pk->line2 = BigBrother::toJSON($nbt["Text2"]); + $pk->line3 = BigBrother::toJSON($nbt["Text3"]); + $pk->line4 = BigBrother::toJSON($nbt["Text4"]); + return $pk; + } } + + return null; + case Info::SET_DIFFICULTY_PACKET: + $pk = new ServerDifficultyPacket(); + $pk->difficulty = $packet->difficulty; return $pk; - */ - case Info::ADD_ITEM_ENTITY_PACKET: - $packets = []; - $pk = new SpawnObjectPacket(); - $pk->eid = $packet->eid; - $pk->type = 2; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $packets[] = $pk; + case Info::SET_PLAYER_GAMETYPE_PACKET: + $packets = []; - $pk = new EntityMetadataPacket(); - $pk->eid = $packet->eid; - $pk->metadata = $pk->metadata = [ - 0 => ["type" => 0, "value" => 0], - 10 => ["type" => 5, "value" => $packet->item], - ]; + $pk = new PlayerAbilitiesPacket(); + $pk->flyingSpeed = 0.05; + $pk->walkingSpeed = 0.1; + $pk->canFly = ($player->getGamemode() & 0x01) > 0; + $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; + $pk->isFlying = false; + $pk->isCreative = ($player->getGamemode() & 0x01) > 0; $packets[] = $pk; - return $packets; - - - case Info::ADD_PLAYER_PACKET: - $packets = []; - $pk = new SpawnPlayerPacket(); - $pk->name = $packet->username; - $pk->eid = $packet->eid; - $pk->uuid = Binary::UUIDtoString("00000000000030008000000000000000"); - $pk->x = $packet->x; - $pk->z = $packet->y; - $pk->y = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $pk->item = 0; - $pk->metadata = $packet->metadata; + $pk = new ChangeGameStatePacket(); + $pk->reason = 3; + $pk->value = $player->getGamemode(); $packets[] = $pk; - $pk = new EntityTeleportPacket(); - $pk->eid = $packet->eid; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $packets[] = $pk; return $packets; + case Info::PLAY_STATUS_PACKET: + case Info::PLAYER_LIST_PACKET: + case Info::ADVENTURE_SETTINGS_PACKET: + case Info::FULL_CHUNK_DATA_PACKET: + case Info::BATCH_PACKET: + return null; + default: + echo "[Send] 0x".bin2hex(chr($packet->pid()))."\n"; return null; } } diff --git a/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php b/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php deleted file mode 100755 index 543ef427..00000000 --- a/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\tasks; - -use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; -use pocketmine\utils\Utils; -use shoghicp\BigBrother\DesktopPlayer; - -class AuthenticateOnline extends AsyncTask{ - - protected $clientID; - protected $username; - protected $hash; - - public function __construct($clientID, $username, $hash){ - $this->clientID = $clientID; - $this->username = $username; - $this->hash = $hash; - } - - public function onRun(){ - $result = Utils::getURL("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=".$this->username."&serverId=".$this->hash, 5); - $this->setResult($result); - } - - public function onCompletion(Server $server){ - foreach($server->getOnlinePlayers() as $clientID => $player){ - if($player instanceof DesktopPlayer and $clientID === $this->clientID){ - $result = json_decode($this->getResult(), true); - if(is_array($result) and isset($result["id"])){ - $player->bigBrother_authenticate($this->username, $result["id"], $result["properties"]); - }else{ - $player->close("", "User not premium"); - } - break; - } - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php b/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php deleted file mode 100755 index 4f7d86fe..00000000 --- a/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\tasks; - -use phpseclib\Crypt\RSA; -use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; -use shoghicp\BigBrother\BigBrother; - -class GeneratePrivateKey extends AsyncTask{ - - /** @var \ThreadedLogger */ - protected $logger; - protected $loader; - /** @var array */ - protected $loadPaths; - - public function __construct(\ThreadedLogger $logger, \ClassLoader $loader){ - $this->logger = $logger; - $this->loader = $loader; - $loadPaths = []; - $this->addDependency($loadPaths, new \ReflectionClass($logger)); - $this->addDependency($loadPaths, new \ReflectionClass($loader)); - $this->loadPaths = array_reverse($loadPaths); - } - - protected function addDependency(array &$loadPaths, \ReflectionClass $dep){ - if($dep->getFileName() !== false){ - $loadPaths[$dep->getName()] = $dep->getFileName(); - } - - if($dep->getParentClass() instanceof \ReflectionClass){ - $this->addDependency($loadPaths, $dep->getParentClass()); - } - - foreach($dep->getInterfaces() as $interface){ - $this->addDependency($loadPaths, $interface); - } - } - - public function onRun(){ - foreach($this->loadPaths as $name => $path){ - if(!class_exists($name, false) and !interface_exists($name, false)){ - require($path); - } - } - $this->loader->register(true); - - $rsa = new RSA(); - $this->logger->info("[BigBrother] Generating keypair"); - $rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1); - $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1); - $keys = $rsa->createKey(1024); - $this->setResult($keys); - } - - public function onCompletion(Server $server){ - $plugin = $server->getPluginManager()->getPlugin("BigBrother"); - if($plugin instanceof BigBrother){ - if($plugin->isEnabled()){ - $result = $this->getResult(); - $plugin->receiveCryptoKeys($result["privatekey"], $result["publickey"]); - } - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php b/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php deleted file mode 100755 index 35b88766..00000000 --- a/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php +++ /dev/null @@ -1,122 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\tasks; - -use pocketmine\level\format\leveldb\Chunk; -use pocketmine\level\Level; -use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; -use pocketmine\utils\Binary; -use shoghicp\BigBrother\DesktopPlayer; - -class LevelDBToAnvil extends AsyncTask{ - - protected $playerName; - - protected $chunkX; - protected $chunkZ; - - public $blockIds; - public $blockData; - public $blockSkyLight; - public $blockLight; - - protected $biomeIds; - protected $compressionLevel; - - - public function __construct(DesktopPlayer $player, Chunk $chunk){ - $this->playerName = $player->getName(); - - $this->chunkX = $chunk->getX(); - $this->chunkZ = $chunk->getZ(); - - $this->blockIds = $chunk->getBlockIdArray(); - $this->blockData = $chunk->getBlockDataArray(); - $this->blockSkyLight = $chunk->getBlockSkyLightArray(); - $this->blockLight = $chunk->getBlockLightArray(); - - $this->biomeIds = $chunk->getBiomeIdArray(); - - $this->compressionLevel = Level::$COMPRESSION_LEVEL; - } - - public function onRun(){ - $ids = ["", "", "", "", "", "", "", ""]; - $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; - - //Complexity: O(MG) - for($Y = 0; $Y < 8; ++$Y){ - for($y = 0; $y < 16; ++$y){ - $offset = ($Y << 4) + $y; - for($z = 0; $z < 16; ++$z){ - for($x = 0; $x < 16; ++$x){ - $index = ($x << 11) + ($z << 7) + $offset; - $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); - if(($y & 1) === 0){ - $data = ord($this->blockData[$halfIndex]) & 0x0F; - $bLight = ord($this->blockLight[$halfIndex]) & 0x0F; - //$sLight = ord($this->blockSkyLight[$halfIndex]) & 0x0F; - }else{ - $data = ord($this->blockData[$halfIndex]) >> 4; - $bLight = ord($this->blockLight[$halfIndex]) >> 4; - //$sLight = ord($this->blockSkyLight[$halfIndex]) >> 4; - } - $ids[$Y] .= pack("v", (ord($this->blockIds[$index]) << 4) | $data); - - $blockLight[$Y][] = $bLight; - //$skyLight[$Y][] = $sLight; - } - } - } - } - - foreach($blockLight as $Y => $data){ - $final = ""; - $len = count($data); - for($i = 0; $i < $len; $i += 2){ - $final .= chr(($data[$i + 1] << 4) | $data[$i]); - } - $blockLight[$Y] = $final; - } - - /* - foreach($skyLight as $Y => $data){ - $final = ""; - $len = count($data); - for($i = 0; $i < $len; $i += 2){ - $final .= chr(($data[$i + 1] << 4) | $data[$i]); - } - $skyLight[$Y] = $final; - } - */ - - $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; - - $this->setResult(implode($ids) . implode($blockLight) . implode($skyLight) . $this->biomeIds); - } - - public function onCompletion(Server $server){ - $player = $server->getPlayerExact($this->playerName); - if($player instanceof DesktopPlayer){ - if(($payload = $this->getResult()) !== null){ - $player->bigBrother_sendChunk($this->chunkX, $this->chunkZ, $payload); - } - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php b/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php deleted file mode 100755 index 8d1f1882..00000000 --- a/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php +++ /dev/null @@ -1,122 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\tasks; - -use pocketmine\level\format\mcregion\Chunk; -use pocketmine\level\Level; -use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; -use pocketmine\utils\Binary; -use shoghicp\BigBrother\DesktopPlayer; - -class McRegionToAnvil extends AsyncTask{ - - protected $playerName; - - protected $chunkX; - protected $chunkZ; - - public $blockIds; - public $blockData; - public $blockSkyLight; - public $blockLight; - - protected $biomeIds; - protected $compressionLevel; - - - public function __construct(DesktopPlayer $player, Chunk $chunk){ - $this->playerName = $player->getName(); - - $this->chunkX = $chunk->getX(); - $this->chunkZ = $chunk->getZ(); - - $this->blockIds = $chunk->getBlockIdArray(); - $this->blockData = $chunk->getBlockDataArray(); - $this->blockSkyLight = $chunk->getBlockSkyLightArray(); - $this->blockLight = $chunk->getBlockLightArray(); - - $this->biomeIds = $chunk->getBiomeIdArray(); - - $this->compressionLevel = Level::$COMPRESSION_LEVEL; - } - - public function onRun(){ - $ids = ["", "", "", "", "", "", "", ""]; - $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; - - //Complexity: O(MG) - for($Y = 0; $Y < 8; ++$Y){ - for($y = 0; $y < 16; ++$y){ - $offset = ($Y << 4) + $y; - for($z = 0; $z < 16; ++$z){ - for($x = 0; $x < 16; ++$x){ - $index = ($x << 11) + ($z << 7) + $offset; - $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); - if(($y & 1) === 0){ - $data = ord($this->blockData[$halfIndex]) & 0x0F; - $bLight = ord($this->blockLight[$halfIndex]) & 0x0F; - //$sLight = ord($this->blockSkyLight[$halfIndex]) & 0x0F; - }else{ - $data = ord($this->blockData[$halfIndex]) >> 4; - $bLight = ord($this->blockLight[$halfIndex]) >> 4; - //$sLight = ord($this->blockSkyLight[$halfIndex]) >> 4; - } - $ids[$Y] .= pack("v", (ord($this->blockIds[$index]) << 4) | $data); - - $blockLight[$Y][] = $bLight; - //$skyLight[$Y][] = $sLight; - } - } - } - } - - foreach($blockLight as $Y => $data){ - $final = ""; - $len = count($data); - for($i = 0; $i < $len; $i += 2){ - $final .= chr(($data[$i + 1] << 4) | $data[$i]); - } - $blockLight[$Y] = $final; - } - - /* - foreach($skyLight as $Y => $data){ - $final = ""; - $len = count($data); - for($i = 0; $i < $len; $i += 2){ - $final .= chr(($data[$i + 1] << 4) | $data[$i]); - } - $skyLight[$Y] = $final; - } - */ - - $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; - - $this->setResult(implode($ids) . implode($blockLight) . implode($skyLight) . $this->biomeIds); - } - - public function onCompletion(Server $server){ - $player = $server->getPlayerExact($this->playerName); - if($player instanceof DesktopPlayer){ - if(($payload = $this->getResult()) !== null){ - $player->bigBrother_sendChunk($this->chunkX, $this->chunkZ, $payload); - } - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/OnlineProfile.php b/src/shoghicp/BigBrother/tasks/OnlineProfile.php deleted file mode 100755 index a538a179..00000000 --- a/src/shoghicp/BigBrother/tasks/OnlineProfile.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\tasks; - -use pocketmine\scheduler\AsyncTask; -use pocketmine\Server; -use pocketmine\utils\Utils; -use shoghicp\BigBrother\DesktopPlayer; - -class OnlineProfile extends AsyncTask{ - - protected $clientID; - protected $username; - - public function __construct($clientID, $username){ - $this->clientID = $clientID; - $this->username = $username; - } - - public function onRun(){ - $ch = curl_init("https://api.mojang.com/profiles/minecraft"); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); - curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([$this->username])); - curl_setopt($ch, CURLOPT_AUTOREFERER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 PocketMine-MP", "Content-Type: application/json")); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 3); - $ret = json_decode(curl_exec($ch), true); - curl_close($ch); - - if(!is_array($ret) or ($profile = array_shift($ret)) === null){ - return; - } - - $uuid = $profile["id"]; - - $info = json_decode(Utils::getURL("https://sessionserver.mojang.com/session/minecraft/profile/$uuid", 3), true); - - if(!is_array($info)){ - return; - } - - $this->setResult($info); - } - - public function onCompletion(Server $server){ - foreach($server->getOnlinePlayers() as $clientID => $player){ - if($player instanceof DesktopPlayer and $clientID === $this->clientID){ - $result = $this->getResult(); - if(is_array($result) and isset($result["id"])){ - $player->bigBrother_authenticate($this->username, $result["id"], $result["properties"]); - }else{ - $player->bigBrother_authenticate($this->username, "00000000000040008000000000000000", null); - } - break; - } - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/utils/Binary.php b/src/shoghicp/BigBrother/utils/Binary.php index 2dd4fbb5..422e8b98 100755 --- a/src/shoghicp/BigBrother/utils/Binary.php +++ b/src/shoghicp/BigBrother/utils/Binary.php @@ -19,6 +19,7 @@ use phpseclib\Math\BigInteger; use shoghicp\BigBrother\network\Session; +use pocketmine\entity\Entity; class Binary extends \pocketmine\utils\Binary{ @@ -28,47 +29,48 @@ public static function sha1($input){ return ($zero->compare($number) <= 0 ? "":"-") . ltrim($number->toHex(), "0"); } - public static function UUIDtoString($uuid){ - return substr($uuid, 0, 8) ."-". substr($uuid, 8, 4) ."-". substr($uuid, 12, 4) ."-". substr($uuid, 16, 4) ."-". substr($uuid, 20); - } - public static function writeMetadata(array $data){ $m = ""; foreach($data as $bottom => $d){ - $m .= chr(($d["type"] << 5) | ($bottom & 0x1F)); - switch($d["type"]){ - case 0: - $m .= self::writeByte($d["value"]); - break; - case 1: - $m .= self::writeShort($d["value"]); - break; - case 2: - $m .= self::writeInt($d["value"]); - break; - case 3: - $m .= self::writeFloat($d["value"]); - break; - case 4: - $m .= self::writeVarInt(strlen($d["value"])) . $d["value"]; - break; - case 5: - /** @var \pocketmine\item\Item $item */ - $item = $d["value"]; - if($item->getID() === 0){ - $m .= self::writeShort(-1); - }else{ - $m .= self::writeShort($item->getID()); - $m .= self::writeByte($item->getCount()); - $m .= self::writeShort($item->getDamage()); - $m .= self::writeShort(-1); - } - break; - case 6: - $m .= self::writeInt($d["value"][0]); - $m .= self::writeInt($d["value"][1]); - $m .= self::writeInt($d["value"][2]); + if($d[0] !== 6){//6 not use + $m .= chr(($d[0] << 5) | ($bottom & 0x1F)); + switch($d[0]){ + case Entity::DATA_TYPE_BYTE://0 + $m .= self::writeByte($d[1]); + break; + case Entity::DATA_TYPE_SHORT://1 + $m .= self::writeShort($d[1]); + break; + case Entity::DATA_TYPE_INT://2 + $m .= self::writeInt($d[1]); + break; + case Entity::DATA_TYPE_FLOAT://3 + $m .= self::writeFloat($d[1]); + break; + case Entity::DATA_TYPE_STRING://4 + $m .= self::writeVarInt(strlen($d[1])) . $d[1]; + break; + case Entity::DATA_TYPE_SLOT://5 + $item = $d[1]; + if($item->getID() === 0){ + $m .= self::writeShort(-1); + }else{ + $m .= self::writeShort($item->getID()); + $m .= self::writeByte($item->getCount()); + $m .= self::writeShort($item->getDamage()); + $nbt = $item->getCompoundTag(); + $m .= self::writeByte(strlen($nbt)).$nbt; + } + break; + case Entity::DATA_TYPE_ROTATION://7 + $m .= self::writeFloat($d[1][0]); + $m .= self::writeFloat($d[1][1]); + $m .= self::writeFloat($d[1][2]); break; + case Entity::DATA_TYPE_LONG://8 + $m .= self::writeLong($d[1]); + break; + } } } $m .= "\x7f"; From 3bdc0c7e5ae9f2be199249259447fb93adee54e3 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 30 Jun 2016 01:43:11 -0400 Subject: [PATCH 03/21] minor fixes and no more protocol --- src/shoghicp/BigBrother/BigBrother.php | 10 ++++--- src/shoghicp/BigBrother/DesktopPlayer.php | 20 +++++++++++++ src/shoghicp/BigBrother/network/Info.php | 4 +-- .../BigBrother/network/ProtocolInterface.php | 28 +++++++++++-------- .../BigBrother/network/ServerManager.php | 2 +- .../BigBrother/network/ServerThread.php | 4 +-- ...anslator_81.php => TranslatorProtocol.php} | 21 +++++++++++--- 7 files changed, 65 insertions(+), 24 deletions(-) rename src/shoghicp/BigBrother/network/translation/{Translator_81.php => TranslatorProtocol.php} (97%) diff --git a/src/shoghicp/BigBrother/BigBrother.php b/src/shoghicp/BigBrother/BigBrother.php index fc3993b2..41eda1dc 100755 --- a/src/shoghicp/BigBrother/BigBrother.php +++ b/src/shoghicp/BigBrother/BigBrother.php @@ -25,7 +25,7 @@ use shoghicp\BigBrother\network\Info as MCInfo; use shoghicp\BigBrother\network\ProtocolInterface; use shoghicp\BigBrother\network\translation\Translator; -use shoghicp\BigBrother\network\translation\Translator_81; +use shoghicp\BigBrother\network\translation\TranslatorProtocol; use shoghicp\BigBrother\network\protocol\Play\RespawnPacket; use shoghicp\BigBrother\network\protocol\Play\ResourcePackSendPacket; @@ -74,16 +74,18 @@ public function onEnable(){ return; } - switch(Info::CURRENT_PROTOCOL){ + $this->translator = new TranslatorProtocol(); + + /*switch(Info::CURRENT_PROTOCOL){ case 81: - $this->translator = new Translator_81(); + $this->translator = new TranslatorProtocol(); break; default: $this->getLogger()->critical("Couldn't find a protocol translator for #".Info::CURRENT_PROTOCOL .", disabling plugin"); $this->getPluginLoader()->disablePlugin($this); return; break; - } + }*/ $this->rsa = new RSA(); diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index 89506dd3..1e70da0f 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -61,22 +61,27 @@ public function __construct(SourceInterface $interface, $clientID, $address, $po } public function bigBrother_getStatus(){ + //echo "bigBrother_getStatus: ".$this->bigBrother_status."\n"; return $this->bigBrother_status; } public function bigBrother_getPeroperties(){ + //echo "bigBrother_getPeroperties"."\n"; return $this->bigBrother_properties; } public function bigBrother_getUniqueId(){ + //echo "bigBrother_getUniqueId"."\n"; return $this->bigBrother_uuid; } public function getSettings(){ + //echo "getSettings"."\n"; return $this->Settings; } public function getSetting($settingname = null){ + //echo "getSetting"."\n"; if(isset($this->Settings[$settingname])){ return $this->Settings[$settingname]; } @@ -84,20 +89,30 @@ public function getSetting($settingname = null){ } public function setSetting($settings){ + //echo "setSetting"; $this->Settings = array_merge($this->Settings, $settings); } public function removeSetting($settingname){ + //echo "removeSetting"; if(isset($this->Settings[$settingname])){ unset($this->Settings[$settingname]); } } public function cleanSetting($settingname){ + //echo "cleanSetting"; unset($this->Settings[$settingname]); } + /*public function sendChunk($x, $z, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ + //echo "sendChunk"; + bigBrother_sendChunk($x, $z, $payload); + }*/ + public function bigBrother_sendChunk($x, $z, $payload){ + + //echo "bigBrother_sendChunk"; if($this->connected === false){ return; } @@ -133,6 +148,7 @@ public function bigBrother_sendChunk($x, $z, $payload){ } protected function sendNextChunk(){ + //echo "sendNextChunk"; if($this->connected === false){ return; } @@ -177,6 +193,7 @@ protected function sendNextChunk(){ } public function bigBrother_authenticate($uuid, $onlineModeData = null){ + //echo "bigBrother_authenticate"; if($this->bigBrother_status === 0){ $this->bigBrother_uuid = $uuid; $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toString(); @@ -252,6 +269,7 @@ public function bigBrother_authenticate($uuid, $onlineModeData = null){ } public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionResponsePacket $packet){ + //echo "bigBrother_processAuthentication"; $this->bigBrother_secret = $plugin->decryptBinary($packet->sharedSecret); $token = $plugin->decryptBinary($packet->verifyToken); $this->interface->enableEncryption($this, $this->bigBrother_secret); @@ -263,6 +281,7 @@ public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionR } public function bigBrother_handleAuthentication($plugin, $username, $onlineMode = false){ + //echo "bigBrother_handleAuthentication"."\n"; if($this->bigBrother_status === 0){ $this->bigBrother_username = $username; if($onlineMode === true){ @@ -270,6 +289,7 @@ public function bigBrother_handleAuthentication($plugin, $username, $onlineMode $pk->serverID = ""; $pk->publicKey = $plugin->getASN1PublicKey(); $pk->verifyToken = $this->bigBrother_checkToken = Utils::getRandomBytes(4, false, true, $pk->publicKey); + //echo "EncryptionRequestPacket\n"; $this->putRawPacket($pk); }else{ $info = $this->getProfile($username); diff --git a/src/shoghicp/BigBrother/network/Info.php b/src/shoghicp/BigBrother/network/Info.php index 75e6578c..7bbc4925 100755 --- a/src/shoghicp/BigBrother/network/Info.php +++ b/src/shoghicp/BigBrother/network/Info.php @@ -23,7 +23,7 @@ abstract class Info{ * Actual Minecraft protocol version */ - const VERSION = "1.10.2"; - const PROTOCOL = 210; + const VERSION = "1.8"; + const PROTOCOL = 47; } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index b9ca9834..a6340af0 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -78,11 +78,11 @@ public function __construct(BigBrother $plugin, $server, Translator $translator) } public function emergencyShutdown(){ - $this->thread->pushMainToThreadPacket(ServerManager::PACKET_EMERGENCY_SHUTDOWN); + $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_EMERGENCY_SHUTDOWN)); } public function shutdown(){ - $this->thread->pushMainToThreadPacket(ServerManager::PACKET_SHUTDOWN); + $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_SHUTDOWN)); } public function setName($name){ @@ -91,7 +91,7 @@ public function setName($name){ "MaxPlayers" => $info->getMaxPlayerCount(), "OnlinePlayers" => $info->getPlayerCount(), ]; - $buffer = ServerManager::PACKET_SET_OPTION.chr(strlen("name"))."name".json_encode($value); + $buffer = chr(ServerManager::PACKET_SET_OPTION).chr(strlen("name"))."name".json_encode($value); $this->thread->pushMainToThreadPacket($buffer); } @@ -108,22 +108,22 @@ public function close(Player $player, $reason = "unknown reason"){ $identifier = $this->sessions[$player]; $this->sessions->detach($player); unset($this->identifiers[$identifier]); - $this->thread->pushMainToThreadPacket(ServerManager::PACKET_CLOSE_SESSION . Binary::writeInt($identifier)); + $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_CLOSE_SESSION) . Binary::writeInt($identifier)); }else{ return; } } protected function sendPacket($target, Packet $packet){ - $data = ServerManager::PACKET_SEND_PACKET . Binary::writeInt($target) . $packet->write(); - + $data = chr(ServerManager::PACKET_SEND_PACKET) . Binary::writeInt($target) . $packet->write(); $this->thread->pushMainToThreadPacket($data); + //echo "pushMainToThreadPacket: ".chr(ServerManager::PACKET_SEND_PACKET)."\n"; } public function setCompression(DesktopPlayer $player, $threshold){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; - $data = ServerManager::PACKET_SET_COMPRESSION . Binary::writeInt($target) . Binary::writeInt($threshold); + $data = chr(ServerManager::PACKET_SET_COMPRESSION) . Binary::writeInt($target) . Binary::writeInt($threshold); $this->thread->pushMainToThreadPacket($data); } } @@ -131,7 +131,8 @@ public function setCompression(DesktopPlayer $player, $threshold){ public function enableEncryption(DesktopPlayer $player, $secret){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; - $data = ServerManager::PACKET_ENABLE_ENCRYPTION . Binary::writeInt($target) . $secret; + + $data = chr(ServerManager::PACKET_ENABLE_ENCRYPTION) . Binary::writeInt($target) . $secret; $this->thread->pushMainToThreadPacket($data); } } @@ -139,12 +140,13 @@ public function enableEncryption(DesktopPlayer $player, $secret){ public function putRawPacket(DesktopPlayer $player, Packet $packet){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; + //echo "sendPacket\n"; $this->sendPacket($target, $packet); } } public function putPacket(Player $player, DataPacket $packet, $needACK = false, $immediate = true){ - $id = 0; + $id = 1; if($needACK){ $id = $this->identifier++; $this->identifiers[$id] = $player; @@ -267,6 +269,7 @@ protected function handlePacket(DesktopPlayer $player, $payload){ return; } + //echo "Receive\n"; $pk->read($payload, $offset); $this->receivePacket($player, $pk); }elseif($status === 0){ @@ -294,8 +297,10 @@ public function process(){ while(strlen($buffer = $this->thread->readThreadToMainPacket()) > 0){ $offset = 1; $pid = ord($buffer{0}); + //echo "pid: ".$pid."\n"; if($pid === ServerManager::PACKET_SEND_PACKET){ + //echo "PACKET_SEND_PACKET\n"; $id = Binary::readInt(substr($buffer, $offset, 4)); $offset += 4; if(isset($this->sessionsPlayers[$id])){ @@ -304,13 +309,13 @@ public function process(){ $this->handlePacket($this->sessionsPlayers[$id], $payload); }catch(\Exception $e){ - //if(\pocketmine\DEBUG > 1){ + if(\pocketmine\DEBUG > 1){ $logger = $this->server->getLogger(); if($logger instanceof MainLogger){ $logger->debug("DesktopPacket 0x" . bin2hex($payload)); $logger->logException($e); } - //} + } } } }elseif($pid === ServerManager::PACKET_OPEN_SESSION){ @@ -330,6 +335,7 @@ public function process(){ $this->sessions->attach($player, $id); $this->sessionsPlayers[$id] = $player; $this->plugin->getServer()->addPlayer($identifier, $player); + //echo "PACKET_OPEN_SESSION\n"; }elseif($pid === ServerManager::PACKET_CLOSE_SESSION){ $id = Binary::readInt(substr($buffer, $offset, 4)); $offset += 4; diff --git a/src/shoghicp/BigBrother/network/ServerManager.php b/src/shoghicp/BigBrother/network/ServerManager.php index c774db67..845aed9b 100755 --- a/src/shoghicp/BigBrother/network/ServerManager.php +++ b/src/shoghicp/BigBrother/network/ServerManager.php @@ -133,7 +133,7 @@ public function getServerData(){ public function shutdown(){ $this->thread->shutdown(); usleep(50000); //Sleep for 1 tick - //$this->thread->kill(); + //$this->thread->quit(); } protected function processPacket(){ diff --git a/src/shoghicp/BigBrother/network/ServerThread.php b/src/shoghicp/BigBrother/network/ServerThread.php index 13265f10..2fb39208 100755 --- a/src/shoghicp/BigBrother/network/ServerThread.php +++ b/src/shoghicp/BigBrother/network/ServerThread.php @@ -152,8 +152,8 @@ public function pushMainToThreadPacket($str){ } public function readMainToThreadPacket(){ - //echo "internalQueue: ".$this->internalQueue."\n"; - return $this->internalQueue->shift(); + $var = $this->internalQueue->shift(); + return $var; } public function pushThreadToMainPacket($str){ diff --git a/src/shoghicp/BigBrother/network/translation/Translator_81.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php similarity index 97% rename from src/shoghicp/BigBrother/network/translation/Translator_81.php rename to src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index afaf3380..abe97b20 100755 --- a/src/shoghicp/BigBrother/network/translation/Translator_81.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -29,6 +29,8 @@ use pocketmine\network\protocol\AddPlayerPacket; use pocketmine\network\protocol\AdventureSettingsPacket; use pocketmine\network\protocol\AnimatePacket; +use pocketmine\network\protocol\BatchPacket; +use pocketmine\network\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\protocol\ContainerClosePacket; use pocketmine\network\protocol\ContainerOpenPacket; use pocketmine\network\protocol\ContainerSetContentPacket; @@ -36,18 +38,23 @@ use pocketmine\network\protocol\ContainerSetSlotPacket; use pocketmine\network\protocol\CraftingDataPacket; use pocketmine\network\protocol\CraftingEventPacket; +use pocketmine\network\protocol\ChangeDimensionPacket; use pocketmine\network\protocol\DataPacket; use pocketmine\network\protocol\DropItemPacket; use pocketmine\network\protocol\FullChunkDataPacket; -use pocketmine\network\protocol\Info; +use pocketmine\network\protocol\ItemFrameDropItemPacket; +use pocketmine\network\protocol\RequestChunkRadiusPacket; use pocketmine\network\protocol\SetEntityLinkPacket; -use pocketmine\network\protocol\TileEntityDataPacket; +use pocketmine\network\protocol\BlockEntityDataPacket; use pocketmine\network\protocol\EntityEventPacket; use pocketmine\network\protocol\ExplodePacket; use pocketmine\network\protocol\HurtArmorPacket; +use pocketmine\network\protocol\Info; use pocketmine\network\protocol\InteractPacket; use pocketmine\network\protocol\LevelEventPacket; use pocketmine\network\protocol\DisconnectPacket; +use pocketmine\network\protocol\LoginPacket; +use pocketmine\network\protocol\PlayStatusPacket; use pocketmine\network\protocol\TextPacket; use pocketmine\network\protocol\MoveEntityPacket; use pocketmine\network\protocol\MovePlayerPacket; @@ -61,11 +68,16 @@ use pocketmine\network\protocol\SetDifficultyPacket; use pocketmine\network\protocol\SetEntityDataPacket; use pocketmine\network\protocol\SetEntityMotionPacket; +use pocketmine\network\protocol\SetHealthPacket; +use pocketmine\network\protocol\SetPlayerGameTypePacket; use pocketmine\network\protocol\SetSpawnPositionPacket; +use pocketmine\network\protocol\SetTimePacket; +use pocketmine\network\protocol\StartGamePacket; use pocketmine\network\protocol\TakeItemEntityPacket; -use pocketmine\network\protocol\TileEventPacket; +use pocketmine\network\protocol\BlockEventPacket; use pocketmine\network\protocol\UpdateBlockPacket; use pocketmine\network\protocol\UseItemPacket; +use pocketmine\network\protocol\PlayerInputPacket; use pocketmine\math\Vector3; use pocketmine\nbt\NBT; use pocketmine\tile\Tile; @@ -108,12 +120,13 @@ use shoghicp\BigBrother\network\protocol\Play\WindowItemsPacket; use shoghicp\BigBrother\utils\Binary; -class Translator_81 implements Translator{ +class TranslatorProtocol implements Translator{ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ if($packet->pid() !== 0x00 and $packet->pid() !== 0x03 and $packet->pid() !== 0x04 and $packet->pid() !== 0x05 and $packet->pid() !== 0x06){ echo "[Receive] 0x".bin2hex(chr($packet->pid()))."\n"; //Debug } + switch($packet->pid()){ case 0x00: //KeepAlivePacket $pk->id = mt_rand(); From 36c02a45c6be8bc8ff479d10c569238a8ddaa3fd Mon Sep 17 00:00:00 2001 From: va2ron1 Date: Thu, 30 Jun 2016 01:48:15 -0400 Subject: [PATCH 04/21] Ups: I've overwritten the wrong file --- src/shoghicp/BigBrother/network/Info.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shoghicp/BigBrother/network/Info.php b/src/shoghicp/BigBrother/network/Info.php index 7bbc4925..99fe9b33 100755 --- a/src/shoghicp/BigBrother/network/Info.php +++ b/src/shoghicp/BigBrother/network/Info.php @@ -23,7 +23,7 @@ abstract class Info{ * Actual Minecraft protocol version */ - const VERSION = "1.8"; - const PROTOCOL = 47; + const VERSION = "1.10.2"; + const PROTOCOL = 210; -} \ No newline at end of file +} From 8a3f8718ecc0adc26c03fbd7bca9500157a4fcbe Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 30 Jun 2016 13:17:19 -0400 Subject: [PATCH 05/21] Next Part: Update TranslateProtocol.php --- resources/config.yml | 11 +++++++++-- src/shoghicp/BigBrother/DesktopPlayer.php | 4 +++- src/shoghicp/BigBrother/network/Info.php | 2 +- .../network/translation/TranslatorProtocol.php | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) mode change 100644 => 100755 resources/config.yml diff --git a/resources/config.yml b/resources/config.yml old mode 100644 new mode 100755 index ba3a0b23..27439153 --- a/resources/config.yml +++ b/resources/config.yml @@ -10,7 +10,7 @@ network-compression-threshold: 256 motd: "§bPocketMine-MP server using §6§lBigBrother§r§b plugin\n§aConnect to Minecraft: PE servers from PC clients" #Use Mojang services to authenticate players -online-mode: true +online-mode: false #Automatically authenticate players on SimpleAuth that were authenticated via Mojang servers online-mode-simpleauth: false @@ -21,4 +21,11 @@ online-mode-simpleauth: false #kickold: kicks the player that was on the server #kicknew: kicks the player that is connecting #none: do nothing, leave it up to PocketMine-MP and other plugins -action-on-conflict: none \ No newline at end of file +action-on-conflict: none + +#This is Skin Settings +skin-yml: "steve.yml" +skin-slim: false + +#This is ResourcePackURL Settings +resourcepackurl: "false" \ No newline at end of file diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index 1e70da0f..14d09f56 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -193,7 +193,7 @@ protected function sendNextChunk(){ } public function bigBrother_authenticate($uuid, $onlineModeData = null){ - //echo "bigBrother_authenticate"; + //echo "bigBrother_authenticate\n"; if($this->bigBrother_status === 0){ $this->bigBrother_uuid = $uuid; $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toString(); @@ -265,6 +265,7 @@ public function bigBrother_authenticate($uuid, $onlineModeData = null){ $pk->actionID = TitlePacket::TYPE_SET_SUB_TITLE; $pk->data = TextFormat::toJSON(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother."); $this->putRawPacket($pk); + //echo "Done\n"; } } @@ -292,6 +293,7 @@ public function bigBrother_handleAuthentication($plugin, $username, $onlineMode //echo "EncryptionRequestPacket\n"; $this->putRawPacket($pk); }else{ + //echo "Getting profile...\n"; $info = $this->getProfile($username); if(is_array($info)){ $this->bigBrother_authenticate($info["id"], $info["properties"]); diff --git a/src/shoghicp/BigBrother/network/Info.php b/src/shoghicp/BigBrother/network/Info.php index 99fe9b33..75e6578c 100755 --- a/src/shoghicp/BigBrother/network/Info.php +++ b/src/shoghicp/BigBrother/network/Info.php @@ -26,4 +26,4 @@ abstract class Info{ const VERSION = "1.10.2"; const PROTOCOL = 210; -} +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index abe97b20..f20de677 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -595,7 +595,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ /*case Info::ADD_ENTITY_PACKET: return null;*/ - case Info::REMOVE_PLAYER_PACKET: + /*case Info::REMOVE_PLAYER_PACKET: $pk = new PlayerListPacket(); $pk->actionID = PlayerListPacket::TYPE_REMOVE; @@ -608,7 +608,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->ids[] = $packet->eid; $packets[] = $pk; - return $packets; + return $packets;*/ case Info::REMOVE_ENTITY_PACKET: $pk = new DestroyEntitiesPacket(); From 695af8586a536bf919191117b1466f9e3031ef43 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Tue, 5 Jul 2016 18:01:36 -0400 Subject: [PATCH 06/21] Changelog: Update 3 --- LICENSE | 165 - plugin.yml | 0 resources/config.yml | 9 +- resources/server-icon.png | Bin src/ParagonIE/.gitignore | 2 - src/ParagonIE/.travis.yml | 15 - src/ParagonIE/ConstantTime/Base32.php | 396 -- src/ParagonIE/ConstantTime/Base32Hex.php | 111 - src/ParagonIE/ConstantTime/Base64.php | 229 - src/ParagonIE/ConstantTime/Base64DotSlash.php | 88 - .../ConstantTime/Base64DotSlashOrdered.php | 82 - src/ParagonIE/ConstantTime/Base64UrlSafe.php | 95 - src/ParagonIE/ConstantTime/Binary.php | 98 - .../ConstantTime/EncoderInterface.php | 52 - src/ParagonIE/ConstantTime/Encoding.php | 245 - src/ParagonIE/ConstantTime/Hex.php | 132 - src/ParagonIE/ConstantTime/RFC4648.php | 166 - src/phpseclib/Crypt/AES.php | 169 +- src/phpseclib/Crypt/Base.php | 1608 ++----- src/phpseclib/Crypt/Blowfish.php | 588 --- src/phpseclib/Crypt/DES.php | 383 +- src/phpseclib/Crypt/Hash.php | 687 ++- src/phpseclib/Crypt/RC2.php | 704 --- src/phpseclib/Crypt/RC4.php | 253 +- src/phpseclib/Crypt/RSA.php | 2165 +++++---- src/phpseclib/Crypt/RSA/MSBLOB.php | 224 - src/phpseclib/Crypt/RSA/OpenSSH.php | 141 - src/phpseclib/Crypt/RSA/PKCS.php | 487 -- src/phpseclib/Crypt/RSA/PKCS1.php | 174 - src/phpseclib/Crypt/RSA/PKCS8.php | 209 - src/phpseclib/Crypt/RSA/PuTTY.php | 313 -- src/phpseclib/Crypt/RSA/Raw.php | 103 - src/phpseclib/Crypt/RSA/XML.php | 147 - src/phpseclib/Crypt/Random.php | 366 +- src/phpseclib/Crypt/Rijndael.php | 1182 +++-- src/phpseclib/Crypt/TripleDES.php | 314 +- src/phpseclib/Crypt/Twofish.php | 846 ---- .../Exception/BadConfigurationException.php | 26 - .../Exception/FileNotFoundException.php | 26 - .../NoSupportedAlgorithmsException.php | 26 - .../UnsupportedAlgorithmException.php | 26 - src/phpseclib/File/ANSI.php | 574 --- src/phpseclib/File/ASN1.php | 985 ++-- src/phpseclib/File/ASN1/Element.php | 47 - src/phpseclib/File/X509.php | 1840 +++---- src/phpseclib/LICENSE | 21 + src/phpseclib/Math/BigInteger.php | 2102 ++++---- src/phpseclib/Net/SCP.php | 338 -- src/phpseclib/Net/SFTP.php | 2947 ------------ src/phpseclib/Net/SFTP/Stream.php | 795 ---- src/phpseclib/Net/SSH1.php | 1607 ------- src/phpseclib/Net/SSH2.php | 4224 ----------------- src/phpseclib/System/SSH/Agent.php | 313 -- src/phpseclib/System/SSH/Agent/Identity.php | 170 - src/phpseclib/bootstrap.php | 20 - src/phpseclib/openssl.cnf | 6 - src/shoghicp/BigBrother/BigBrother.php | 146 +- src/shoghicp/BigBrother/DesktopPlayer.php | 494 +- src/shoghicp/BigBrother/network/Info.php | 10 +- src/shoghicp/BigBrother/network/Packet.php | 187 +- .../BigBrother/network/ProtocolInterface.php | 155 +- .../BigBrother/network/ServerManager.php | 50 +- .../BigBrother/network/ServerThread.php | 22 +- src/shoghicp/BigBrother/network/Session.php | 11 +- .../protocol/{Play => }/BlockChangePacket.php | 2 +- .../protocol/{Play => }/CTSChatPacket.php | 2 +- .../{Play => }/CTSCloseWindowPacket.php | 2 +- .../{Play => }/ChangeGameStatePacket.php | 2 +- .../protocol/{Play => }/ChunkDataPacket.php | 2 +- .../{Play => }/ClientStatusPacket.php | 4 +- .../{Play => }/DestroyEntitiesPacket.php | 2 +- .../{Login => }/EncryptionRequestPacket.php | 2 +- .../{Login => }/EncryptionResponsePacket.php | 2 +- .../{Play => }/EntityHeadLookPacket.php | 2 +- .../{Play => }/EntityMetadataPacket.php | 2 +- .../{Play => }/EntityTeleportPacket.php | 2 +- .../{Play => }/EntityVelocityPacket.php | 4 +- .../protocol/{Play => }/JoinGamePacket.php | 2 +- .../protocol/{Play => }/KeepAlivePacket.php | 2 +- .../{Login => }/LoginDisconnectPacket.php | 2 +- .../protocol/{Login => }/LoginStartPacket.php | 2 +- .../{Login => }/LoginSuccessPacket.php | 4 +- .../protocol/{Play => }/OpenWindowPacket.php | 2 +- .../protocol/{Login => }/PingPacket.php | 2 +- .../network/protocol/Play/AnimatePacket.php | 44 - .../protocol/Play/CPlayerAbilitiesPacket.php | 73 - .../protocol/Play/CTabCompletePacket.php | 43 - .../protocol/Play/ClickWindowPacket.php | 47 - .../protocol/Play/ClientSettingsPacket.php | 45 - .../Play/CreativeInventoryActionPacket.php | 39 - .../protocol/Play/EntityEquipmentPacket.php | 41 - .../network/protocol/Play/EntityPacket.php | 37 - .../protocol/Play/HeldItemChangePacket.php | 37 - .../protocol/Play/PlayerArmSwingPacket.php | 34 - .../protocol/Play/PlayerListPacket.php | 76 - .../network/protocol/Play/PlayerPacket.php | 37 - .../protocol/Play/PluginMessagePacket.php | 58 - .../protocol/Play/ResourcePackSendPacket.php | 38 - .../Play/ResourcePackStatusPacket.php | 39 - .../protocol/Play/STabCompletePacket.php | 39 - .../Play/ScoreboardObjectivePacket.php | 42 - .../protocol/Play/ServerDifficultyPacket.php | 36 - .../protocol/Play/StatisticsPacket.php | 42 - .../network/protocol/Play/TitlePacket.php | 55 - .../protocol/Play/UpdateSignPacket.php | 47 - .../network/protocol/Play/UseBedPacket.php | 40 - .../{Play => }/PlayDisconnectPacket.php | 2 +- .../{Play => }/PlayerAbilitiesPacket.php | 2 +- .../{Play => }/PlayerBlockPlacementPacket.php | 2 +- .../{Play => }/PlayerDiggingPacket.php | 2 +- .../protocol/{Play => }/PlayerLookPacket.php | 2 +- .../PlayerPositionAndLookPacket.php | 2 +- .../{Play => }/PlayerPositionPacket.php | 2 +- .../{Play => }/PositionAndLookPacket.php | 2 +- .../protocol/{Play => }/RespawnPacket.php | 2 +- .../protocol/{Play => }/STCChatPacket.php | 2 +- .../{Play => }/STCCloseWindowPacket.php | 2 +- .../protocol/{Play => }/SetSlotPacket.php | 2 +- .../protocol/{Play => }/SpawnMobPacket.php | 2 +- .../protocol/{Play => }/SpawnObjectPacket.php | 2 +- .../protocol/{Play => }/SpawnPlayerPacket.php | 8 +- .../{Play => }/SpawnPositionPacket.php | 2 +- .../protocol/{Play => }/TimeUpdatePacket.php | 2 +- .../{Play => }/UpdateHealthPacket.php | 2 +- .../protocol/{Play => }/UseEntityPacket.php | 11 +- .../protocol/{Play => }/WindowItemsPacket.php | 2 +- .../translation/TranslatorProtocol.php | 862 +--- .../BigBrother/tasks/AuthenticateOnline.php | 55 + .../BigBrother/tasks/GeneratePrivateKey.php | 81 + .../BigBrother/tasks/LevelDBToAnvil.php | 122 + .../BigBrother/tasks/McRegionToAnvil.php | 122 + .../BigBrother/tasks/OnlineProfile.php | 91 + src/shoghicp/BigBrother/utils/Binary.php | 76 +- 133 files changed, 7445 insertions(+), 25170 deletions(-) delete mode 100644 LICENSE mode change 100644 => 100755 plugin.yml mode change 100644 => 100755 resources/server-icon.png delete mode 100755 src/ParagonIE/.gitignore delete mode 100755 src/ParagonIE/.travis.yml delete mode 100755 src/ParagonIE/ConstantTime/Base32.php delete mode 100755 src/ParagonIE/ConstantTime/Base32Hex.php delete mode 100755 src/ParagonIE/ConstantTime/Base64.php delete mode 100755 src/ParagonIE/ConstantTime/Base64DotSlash.php delete mode 100755 src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php delete mode 100755 src/ParagonIE/ConstantTime/Base64UrlSafe.php delete mode 100755 src/ParagonIE/ConstantTime/Binary.php delete mode 100755 src/ParagonIE/ConstantTime/EncoderInterface.php delete mode 100755 src/ParagonIE/ConstantTime/Encoding.php delete mode 100755 src/ParagonIE/ConstantTime/Hex.php delete mode 100755 src/ParagonIE/ConstantTime/RFC4648.php delete mode 100755 src/phpseclib/Crypt/Blowfish.php delete mode 100755 src/phpseclib/Crypt/RC2.php delete mode 100755 src/phpseclib/Crypt/RSA/MSBLOB.php delete mode 100755 src/phpseclib/Crypt/RSA/OpenSSH.php delete mode 100755 src/phpseclib/Crypt/RSA/PKCS.php delete mode 100755 src/phpseclib/Crypt/RSA/PKCS1.php delete mode 100755 src/phpseclib/Crypt/RSA/PKCS8.php delete mode 100755 src/phpseclib/Crypt/RSA/PuTTY.php delete mode 100755 src/phpseclib/Crypt/RSA/Raw.php delete mode 100755 src/phpseclib/Crypt/RSA/XML.php delete mode 100755 src/phpseclib/Crypt/Twofish.php delete mode 100755 src/phpseclib/Exception/BadConfigurationException.php delete mode 100755 src/phpseclib/Exception/FileNotFoundException.php delete mode 100755 src/phpseclib/Exception/NoSupportedAlgorithmsException.php delete mode 100755 src/phpseclib/Exception/UnsupportedAlgorithmException.php delete mode 100755 src/phpseclib/File/ANSI.php delete mode 100755 src/phpseclib/File/ASN1/Element.php create mode 100755 src/phpseclib/LICENSE delete mode 100755 src/phpseclib/Net/SCP.php delete mode 100755 src/phpseclib/Net/SFTP.php delete mode 100755 src/phpseclib/Net/SFTP/Stream.php delete mode 100755 src/phpseclib/Net/SSH1.php delete mode 100755 src/phpseclib/Net/SSH2.php delete mode 100755 src/phpseclib/System/SSH/Agent.php delete mode 100755 src/phpseclib/System/SSH/Agent/Identity.php delete mode 100755 src/phpseclib/bootstrap.php delete mode 100755 src/phpseclib/openssl.cnf rename src/shoghicp/BigBrother/network/protocol/{Play => }/BlockChangePacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/CTSChatPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/CTSCloseWindowPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/ChangeGameStatePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/ChunkDataPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/ClientStatusPacket.php (90%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/DestroyEntitiesPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Login => }/EncryptionRequestPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Login => }/EncryptionResponsePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/EntityHeadLookPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/EntityMetadataPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/EntityTeleportPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/EntityVelocityPacket.php (91%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/JoinGamePacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/KeepAlivePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Login => }/LoginDisconnectPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Login => }/LoginStartPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Login => }/LoginSuccessPacket.php (91%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/OpenWindowPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{Login => }/PingPacket.php (94%) delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php delete mode 100755 src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayDisconnectPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayerAbilitiesPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayerBlockPlacementPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayerDiggingPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayerLookPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayerPositionAndLookPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PlayerPositionPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/PositionAndLookPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/RespawnPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/STCChatPacket.php (95%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/STCCloseWindowPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/SetSlotPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/SpawnMobPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/SpawnObjectPacket.php (96%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/SpawnPlayerPacket.php (85%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/SpawnPositionPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/TimeUpdatePacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/UpdateHealthPacket.php (94%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/UseEntityPacket.php (82%) rename src/shoghicp/BigBrother/network/protocol/{Play => }/WindowItemsPacket.php (95%) create mode 100755 src/shoghicp/BigBrother/tasks/AuthenticateOnline.php create mode 100755 src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php create mode 100755 src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php create mode 100755 src/shoghicp/BigBrother/tasks/McRegionToAnvil.php create mode 100755 src/shoghicp/BigBrother/tasks/OnlineProfile.php diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 02bbb60b..00000000 --- a/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. \ No newline at end of file diff --git a/plugin.yml b/plugin.yml old mode 100644 new mode 100755 diff --git a/resources/config.yml b/resources/config.yml index 27439153..0400b0b7 100755 --- a/resources/config.yml +++ b/resources/config.yml @@ -21,11 +21,4 @@ online-mode-simpleauth: false #kickold: kicks the player that was on the server #kicknew: kicks the player that is connecting #none: do nothing, leave it up to PocketMine-MP and other plugins -action-on-conflict: none - -#This is Skin Settings -skin-yml: "steve.yml" -skin-slim: false - -#This is ResourcePackURL Settings -resourcepackurl: "false" \ No newline at end of file +action-on-conflict: none \ No newline at end of file diff --git a/resources/server-icon.png b/resources/server-icon.png old mode 100644 new mode 100755 diff --git a/src/ParagonIE/.gitignore b/src/ParagonIE/.gitignore deleted file mode 100755 index e0caea8f..00000000 --- a/src/ParagonIE/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.idea/ -vendor/ \ No newline at end of file diff --git a/src/ParagonIE/.travis.yml b/src/ParagonIE/.travis.yml deleted file mode 100755 index a78dfe92..00000000 --- a/src/ParagonIE/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: php -sudo: false - -php: - - "7.0" - -matrix: - fast_finish: true - -install: - - composer self-update - - composer update - -script: - - vendor/bin/phpunit diff --git a/src/ParagonIE/ConstantTime/Base32.php b/src/ParagonIE/ConstantTime/Base32.php deleted file mode 100755 index a65c1c73..00000000 --- a/src/ParagonIE/ConstantTime/Base32.php +++ /dev/null @@ -1,396 +0,0 @@ - 96 && $src < 123) $ret += $src - 97 + 1; // -64 - $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96); - - // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 - $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 5-bit integers - * into 8-bit integers. - * - * Uppercase variant. - * - * @param int $src - * @return int - */ - protected static function decode5BitsUpper(int $src): int - { - $ret = -1; - - // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64 - $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); - - // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 - $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 5-bit integers. - * - * @param $src - * @return string - */ - protected static function encode5Bits(int $src): string - { - $diff = 0x61; - - // if ($src > 25) $ret -= 72; - $diff -= ((25 - $src) >> 8) & 73; - - return \pack('C', $src + $diff); - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 5-bit integers. - * - * Uppercase variant. - * - * @param $src - * @return string - */ - protected static function encode5BitsUpper(int $src): string - { - $diff = 0x41; - - // if ($src > 25) $ret -= 40; - $diff -= ((25 - $src) >> 8) & 41; - - return \pack('C', $src + $diff); - } - - - /** - * Base32 decoding - * - * @param string $src - * @param bool $upper - * @param bool $strictPadding - * @return string - */ - protected static function doDecode(string $src, bool $upper = false, bool $strictPadding = false): string - { - // We do this to reduce code duplication: - $method = $upper - ? 'decode5BitsUpper' - : 'decode5Bits'; - - // Remove padding - $srcLen = Binary::safeStrlen($src); - if ($srcLen === 0) { - return ''; - } - if ($strictPadding) { - if (($srcLen & 7) === 0) { - for ($j = 0; $j < 7; ++$j) { - if ($src[$srcLen - 1] === '=') { - $srcLen--; - } else { - break; - } - } - } - if (($srcLen & 7) === 1) { - throw new \RangeException( - 'Incorrect padding' - ); - } - } else { - $src = \rtrim($src, '='); - $srcLen = Binary::safeStrlen($src); - } - - $err = 0; - $dest = ''; - // Main loop (no padding): - for ($i = 0; $i + 8 <= $srcLen; $i += 8) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8)); - $c0 = static::$method($chunk[1]); - $c1 = static::$method($chunk[2]); - $c2 = static::$method($chunk[3]); - $c3 = static::$method($chunk[4]); - $c4 = static::$method($chunk[5]); - $c5 = static::$method($chunk[6]); - $c6 = static::$method($chunk[7]); - $c7 = static::$method($chunk[8]); - - $dest .= \pack( - 'CCCCC', - (($c0 << 3) | ($c1 >> 2) ) & 0xff, - (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, - (($c3 << 4) | ($c4 >> 1) ) & 0xff, - (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff, - (($c6 << 5) | ($c7 ) ) & 0xff - ); - $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8; - } - // The last chunk, which may have padding: - if ($i < $srcLen) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); - $c0 = static::$method($chunk[1]); - - if ($i + 6 < $srcLen) { - $c1 = static::$method($chunk[2]); - $c2 = static::$method($chunk[3]); - $c3 = static::$method($chunk[4]); - $c4 = static::$method($chunk[5]); - $c5 = static::$method($chunk[6]); - $c6 = static::$method($chunk[7]); - - $dest .= \pack( - 'CCCC', - (($c0 << 3) | ($c1 >> 2) ) & 0xff, - (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, - (($c3 << 4) | ($c4 >> 1) ) & 0xff, - (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff - ); - $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; - } elseif ($i + 5 < $srcLen) { - $c1 = static::$method($chunk[2]); - $c2 = static::$method($chunk[3]); - $c3 = static::$method($chunk[4]); - $c4 = static::$method($chunk[5]); - $c5 = static::$method($chunk[6]); - - $dest .= \pack( - 'CCCC', - (($c0 << 3) | ($c1 >> 2) ) & 0xff, - (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, - (($c3 << 4) | ($c4 >> 1) ) & 0xff, - (($c4 << 7) | ($c5 << 2) ) & 0xff - ); - $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8; - } elseif ($i + 4 < $srcLen) { - $c1 = static::$method($chunk[2]); - $c2 = static::$method($chunk[3]); - $c3 = static::$method($chunk[4]); - $c4 = static::$method($chunk[5]); - - $dest .= \pack( - 'CCC', - (($c0 << 3) | ($c1 >> 2) ) & 0xff, - (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, - (($c3 << 4) | ($c4 >> 1) ) & 0xff - ); - $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; - } elseif ($i + 3 < $srcLen) { - $c1 = static::$method($chunk[2]); - $c2 = static::$method($chunk[3]); - $c3 = static::$method($chunk[4]); - - $dest .= \pack( - 'CC', - (($c0 << 3) | ($c1 >> 2) ) & 0xff, - (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff - ); - $err |= ($c0 | $c1 | $c2 | $c3) >> 8; - } elseif ($i + 2 < $srcLen) { - $c1 = static::$method($chunk[2]); - $c2 = static::$method($chunk[3]); - - $dest .= \pack( - 'CC', - (($c0 << 3) | ($c1 >> 2) ) & 0xff, - (($c1 << 6) | ($c2 << 1) ) & 0xff - ); - $err |= ($c0 | $c1 | $c2) >> 8; - } elseif ($i + 1 < $srcLen) { - $c1 = static::$method($chunk[2]); - - $dest .= \pack( - 'C', - (($c0 << 3) | ($c1 >> 2) ) & 0xff - ); - $err |= ($c0 | $c1) >> 8; - } else { - $dest .= \pack( - 'C', - (($c0 << 3) ) & 0xff - ); - $err |= ($c0) >> 8; - } - } - if ($err !== 0) { - throw new \RangeException( - 'Base32::doDecode() only expects characters in the correct base32 alphabet' - ); - } - return $dest; - } - - /** - * Base32 Decoding - * - * @param string $src - * @param bool $upper - * @return string - */ - protected static function doEncode(string $src, bool $upper = false): string - { - // We do this to reduce code duplication: - $method = $upper - ? 'encode5BitsUpper' - : 'encode5Bits'; - - $dest = ''; - $srcLen = Binary::safeStrlen($src); - - // Main loop (no padding): - for ($i = 0; $i + 5 <= $srcLen; $i += 5) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5)); - $b0 = $chunk[1]; - $b1 = $chunk[2]; - $b2 = $chunk[3]; - $b3 = $chunk[4]; - $b4 = $chunk[5]; - $dest .= - static::$method( ($b0 >> 3) & 31) . - static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . - static::$method((($b1 >> 1) ) & 31) . - static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . - static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . - static::$method((($b3 >> 2) ) & 31) . - static::$method((($b3 << 3) | ($b4 >> 5)) & 31) . - static::$method( $b4 & 31); - } - // The last chunk, which may have padding: - if ($i < $srcLen) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); - $b0 = $chunk[1]; - if ($i + 3 < $srcLen) { - $b1 = $chunk[2]; - $b2 = $chunk[3]; - $b3 = $chunk[4]; - $dest .= - static::$method( ($b0 >> 3) & 31) . - static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . - static::$method((($b1 >> 1) ) & 31) . - static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . - static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . - static::$method((($b3 >> 2) ) & 31) . - static::$method((($b3 << 3) ) & 31) . - '='; - } elseif ($i + 2 < $srcLen) { - $b1 = $chunk[2]; - $b2 = $chunk[3]; - $dest .= - static::$method( ($b0 >> 3) & 31) . - static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . - static::$method((($b1 >> 1) ) & 31) . - static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . - static::$method((($b2 << 1) ) & 31) . - '==='; - } elseif ($i + 1 < $srcLen) { - $b1 = $chunk[2]; - $dest .= - static::$method( ($b0 >> 3) & 31) . - static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . - static::$method((($b1 >> 1) ) & 31) . - static::$method((($b1 << 4) ) & 31) . - '===='; - } else { - $dest .= - static::$method( ($b0 >> 3) & 31) . - static::$method( ($b0 << 2) & 31) . - '======'; - } - } - return $dest; - } -} diff --git a/src/ParagonIE/ConstantTime/Base32Hex.php b/src/ParagonIE/ConstantTime/Base32Hex.php deleted file mode 100755 index 5aff0a6b..00000000 --- a/src/ParagonIE/ConstantTime/Base32Hex.php +++ /dev/null @@ -1,111 +0,0 @@ - 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47 - $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47); - - // if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86 - $ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86); - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 5-bit integers - * into 8-bit integers. - * - * @param int $src - * @return int - */ - protected static function decode5BitsUpper(int $src): int - { - $ret = -1; - - // if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47 - $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47); - - // if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54 - $ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54); - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 5-bit integers. - * - * @param int $src - * @return string - */ - protected static function encode5Bits(int $src): string - { - $src += 0x30; - - // if ($src > 0x39) $src += 0x61 - 0x3a; // 39 - $src += ((0x39 - $src) >> 8) & 39; - - return \pack('C', $src); - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 5-bit integers. - * - * Uppercase variant. - * - * @param int $src - * @return string - */ - protected static function encode5BitsUpper(int $src): string - { - $src += 0x30; - - // if ($src > 0x39) $src += 0x41 - 0x3a; // 7 - $src += ((0x39 - $src) >> 8) & 7; - - return \pack('C', $src); - } -} \ No newline at end of file diff --git a/src/ParagonIE/ConstantTime/Base64.php b/src/ParagonIE/ConstantTime/Base64.php deleted file mode 100755 index df801cc8..00000000 --- a/src/ParagonIE/ConstantTime/Base64.php +++ /dev/null @@ -1,229 +0,0 @@ -> 2 ) . - static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . - static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) . - static::encode6Bits( $b2 & 63); - } - // The last chunk, which may have padding: - if ($i < $srcLen) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); - $b0 = $chunk[1]; - if ($i + 1 < $srcLen) { - $b1 = $chunk[2]; - $dest .= - static::encode6Bits( $b0 >> 2 ) . - static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . - static::encode6Bits( ($b1 << 2) & 63) . '='; - } else { - $dest .= - static::encode6Bits( $b0 >> 2) . - static::encode6Bits(($b0 << 4) & 63) . '=='; - } - } - return $dest; - } - - /** - * decode from base64 into binary - * - * Base64 character set "./[A-Z][a-z][0-9]" - * - * @param string $src - * @param bool $strictPadding - * @return string|bool - * @throws \RangeException - */ - public static function decode(string $src, bool $strictPadding = false): string - { - // Remove padding - $srcLen = Binary::safeStrlen($src); - if ($srcLen === 0) { - return ''; - } - - if ($strictPadding) { - if (($srcLen & 3) === 0) { - if ($src[$srcLen - 1] === '=') { - $srcLen--; - if ($src[$srcLen - 1] === '=') { - $srcLen--; - } - } - } - if (($srcLen & 3) === 1) { - throw new \RangeException( - 'Incorrect padding' - ); - } - if ($src[$srcLen - 1] === '=') { - throw new \RangeException( - 'Incorrect padding' - ); - } - } else { - $src = \rtrim($src, '='); - $srcLen = Binary::safeStrlen($src); - } - - $err = 0; - $dest = ''; - // Main loop (no padding): - for ($i = 0; $i + 4 <= $srcLen; $i += 4) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 4)); - $c0 = static::decode6Bits($chunk[1]); - $c1 = static::decode6Bits($chunk[2]); - $c2 = static::decode6Bits($chunk[3]); - $c3 = static::decode6Bits($chunk[4]); - - $dest .= \pack( - 'CCC', - ((($c0 << 2) | ($c1 >> 4)) & 0xff), - ((($c1 << 4) | ($c2 >> 2)) & 0xff), - ((($c2 << 6) | $c3 ) & 0xff) - ); - $err |= ($c0 | $c1 | $c2 | $c3) >> 8; - } - // The last chunk, which may have padding: - if ($i < $srcLen) { - $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); - $c0 = static::decode6Bits($chunk[1]); - - if ($i + 2 < $srcLen) { - $c1 = static::decode6Bits($chunk[2]); - $c2 = static::decode6Bits($chunk[3]); - $dest .= \pack( - 'CC', - ((($c0 << 2) | ($c1 >> 4)) & 0xff), - ((($c1 << 4) | ($c2 >> 2)) & 0xff) - ); - $err |= ($c0 | $c1 | $c2) >> 8; - } elseif ($i + 1 < $srcLen) { - $c1 = static::decode6Bits($chunk[2]); - $dest .= \pack( - 'C', - ((($c0 << 2) | ($c1 >> 4)) & 0xff) - ); - $err |= ($c0 | $c1) >> 8; - } elseif ($i < $srcLen && $strictPadding) { - $err |= 1; - } - } - if ($err !== 0) { - return false; - } - return $dest; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 6-bit integers - * into 8-bit integers. - * - * Base64 character set: - * [A-Z] [a-z] [0-9] + / - * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f - * - * @param int $src - * @return int - */ - protected static function decode6Bits(int $src): int - { - $ret = -1; - - // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 - $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); - - // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 - $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); - - // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 - $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); - - // if ($src == 0x2b) $ret += 62 + 1; - $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63; - - // if ($src == 0x2f) ret += 63 + 1; - $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64; - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 6-bit integers. - * - * @param int $src - * @return string - */ - protected static function encode6Bits(int $src): string - { - $diff = 0x41; - - // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 - $diff += ((25 - $src) >> 8) & 6; - - // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 - $diff -= ((51 - $src) >> 8) & 75; - - // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15 - $diff -= ((61 - $src) >> 8) & 15; - - // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3 - $diff += ((62 - $src) >> 8) & 3; - - return \pack('C', $src + $diff); - } -} diff --git a/src/ParagonIE/ConstantTime/Base64DotSlash.php b/src/ParagonIE/ConstantTime/Base64DotSlash.php deleted file mode 100755 index 7f975139..00000000 --- a/src/ParagonIE/ConstantTime/Base64DotSlash.php +++ /dev/null @@ -1,88 +0,0 @@ - 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45 - $ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45); - - // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62 - $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62); - - // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68 - $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68); - - // if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7 - $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7); - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 6-bit integers. - * - * @param int $src - * @return string - */ - protected static function encode6Bits(int $src): string - { - $src += 0x2e; - - // if ($src > 0x2f) $src += 0x41 - 0x30; // 17 - $src += ((0x2f - $src) >> 8) & 17; - - // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6 - $src += ((0x5a - $src) >> 8) & 6; - - // if ($src > 0x7a) $src += 0x30 - 0x7b; // -75 - $src -= ((0x7a - $src) >> 8) & 75; - - return \pack('C', $src); - } -} diff --git a/src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php b/src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php deleted file mode 100755 index 3fb4209e..00000000 --- a/src/ParagonIE/ConstantTime/Base64DotSlashOrdered.php +++ /dev/null @@ -1,82 +0,0 @@ - 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45 - $ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45); - - // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52 - $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52); - - // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58 - $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58); - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 6-bit integers. - * - * @param int $src - * @return string - */ - protected static function encode6Bits(int $src): string - { - $src += 0x2e; - - // if ($src > 0x39) $src += 0x41 - 0x3a; // 7 - $src += ((0x39 - $src) >> 8) & 7; - - // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6 - $src += ((0x5a - $src) >> 8) & 6; - - return \pack('C', $src); - } -} diff --git a/src/ParagonIE/ConstantTime/Base64UrlSafe.php b/src/ParagonIE/ConstantTime/Base64UrlSafe.php deleted file mode 100755 index f250095d..00000000 --- a/src/ParagonIE/ConstantTime/Base64UrlSafe.php +++ /dev/null @@ -1,95 +0,0 @@ - 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 - $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); - - // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 - $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); - - // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 - $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); - - // if ($src == 0x2c) $ret += 62 + 1; - $ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63; - - // if ($src == 0x5f) ret += 63 + 1; - $ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64; - - return $ret; - } - - /** - * Uses bitwise operators instead of table-lookups to turn 8-bit integers - * into 6-bit integers. - * - * @param int $src - * @return string - */ - protected static function encode6Bits(int $src): string - { - $diff = 0x41; - - // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 - $diff += ((25 - $src) >> 8) & 6; - - // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 - $diff -= ((51 - $src) >> 8) & 75; - - // if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13 - $diff -= ((61 - $src) >> 8) & 13; - - // if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3 - $diff += ((62 - $src) >> 8) & 49; - - return \pack('C', $src + $diff); - } -} diff --git a/src/ParagonIE/ConstantTime/Binary.php b/src/ParagonIE/ConstantTime/Binary.php deleted file mode 100755 index 1028e66e..00000000 --- a/src/ParagonIE/ConstantTime/Binary.php +++ /dev/null @@ -1,98 +0,0 @@ -= 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 ($length !== null) { - return \substr($str, $start, $length); - } else { - return \substr($str, $start); - } - } -} \ No newline at end of file diff --git a/src/ParagonIE/ConstantTime/EncoderInterface.php b/src/ParagonIE/ConstantTime/EncoderInterface.php deleted file mode 100755 index ca699168..00000000 --- a/src/ParagonIE/ConstantTime/EncoderInterface.php +++ /dev/null @@ -1,52 +0,0 @@ -> 4; - $hex .= pack( - 'CC', - (87 + $b + ((($b - 10) >> 8) & ~38)), - (87 + $c + ((($c - 10) >> 8) & ~38)) - ); - } - return $hex; - } - - /** - * Convert a binary string into a hexadecimal string without cache-timing - * leaks, returning uppercase letters (as per RFC 4648) - * - * @param string $bin_string (raw binary) - * @return string - */ - public static function encodeUpper(string $bin_string): string - { - $hex = ''; - $len = Binary::safeStrlen($bin_string); - for ($i = 0; $i < $len; ++$i) { - $chunk = \unpack('C', Binary::safeSubstr($bin_string, $i, 2)); - $c = $chunk[1] & 0xf; - $b = $chunk[1] >> 4; - $hex .= pack( - 'CC', - (55 + $b + ((($b - 10) >> 8) & ~6)), - (55 + $c + ((($c - 10) >> 8) & ~6)) - ); - } - return $hex; - } - - /** - * Convert a hexadecimal string into a binary string without cache-timing - * leaks - * - * @param string $hex_string - * @param bool $strictPadding - * @return string (raw binary) - * @throws \RangeException - */ - public static function decode(string $hexString, bool $strictPadding = false): string - { - $hex_pos = 0; - $bin = ''; - $c_acc = 0; - $hex_len = Binary::safeStrlen($hexString); - $state = 0; - if (($hex_len & 1) !== 0) { - if ($strictPadding) { - throw new \RangeException( - 'Expected an even number of hexadecimal characters' - ); - } else { - $hexString = '0' . $hexString; - ++$hex_len; - } - } - - $chunk = \unpack('C*', $hexString); - while ($hex_pos < $hex_len) { - ++$hex_pos; - $c = $chunk[$hex_pos]; - $c_num = $c ^ 48; - $c_num0 = ($c_num - 10) >> 8; - $c_alpha = ($c & ~32) - 55; - $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; - if (($c_num0 | $c_alpha0) === 0) { - throw new \RangeException( - 'hexEncode() only expects hexadecimal characters' - ); - } - $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); - if ($state === 0) { - $c_acc = $c_val * 16; - } else { - $bin .= \pack('C', $c_acc | $c_val); - } - $state ^= 1; - } - return $bin; - } -} diff --git a/src/ParagonIE/ConstantTime/RFC4648.php b/src/ParagonIE/ConstantTime/RFC4648.php deleted file mode 100755 index 63b2ee69..00000000 --- a/src/ParagonIE/ConstantTime/RFC4648.php +++ /dev/null @@ -1,166 +0,0 @@ - "Zm9v" - * - * @param string $str - * @return string - */ - public function base64Encode(string $str): string - { - return Base64::encode($str); - } - - /** - * RFC 4648 Base64 decoding - * - * "Zm9v" -> "foo" - * - * @param string $str - * @return string - */ - public function base64Decode(string $str): string - { - return Base64::decode($str, true); - } - - /** - * RFC 4648 Base64 (URL Safe) encoding - * - * "foo" -> "Zm9v" - * - * @param string $str - * @return string - */ - public function base64UrlSafeEncode(string $str): string - { - return Base64UrlSafe::encode($str); - } - - /** - * RFC 4648 Base64 (URL Safe) decoding - * - * "Zm9v" -> "foo" - * - * @param string $str - * @return string - */ - public function base64UrlSafeDecode(string $str): string - { - return Base64UrlSafe::decode($str, true); - } - - /** - * RFC 4648 Base32 encoding - * - * "foo" -> "MZXW6===" - * - * @param string $str - * @return string - */ - public function base32Encode(string $str): string - { - return Base32::encodeUpper($str); - } - - /** - * RFC 4648 Base32 encoding - * - * "MZXW6===" -> "foo" - * - * @param string $str - * @return string - */ - public function base32Decode(string $str): string - { - return Base32::decodeUpper($str, true); - } - - /** - * RFC 4648 Base32-Hex encoding - * - * "foo" -> "CPNMU===" - * - * @param string $str - * @return string - */ - public function base32HexEncode(string $str): string - { - return Base32::encodeUpper($str); - } - - /** - * RFC 4648 Base32-Hex decoding - * - * "CPNMU===" -> "foo" - * - * @param string $str - * @return string - */ - public function base32HexDecode(string $str): string - { - return Base32::decodeUpper($str, true); - } - - /** - * RFC 4648 Base16 decoding - * - * "foo" -> "666F6F" - * - * @param string $str - * @return string - */ - public function base16Encode(string $str): string - { - return Hex::encodeUpper($str); - } - - /** - * RFC 4648 Base16 decoding - * - * "666F6F" -> "foo" - * - * @param string $str - * @return string - */ - public function base16Decode(string $str): string - { - return Hex::decode($str, true); - } -} \ No newline at end of file diff --git a/src/phpseclib/Crypt/AES.php b/src/phpseclib/Crypt/AES.php index 8521eb5e..aa650b8e 100755 --- a/src/phpseclib/Crypt/AES.php +++ b/src/phpseclib/Crypt/AES.php @@ -5,27 +5,23 @@ * * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * - * PHP version 5 + * PHP versions 4 and 5 * - * NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually - * just a wrapper to Rijndael.php you may consider using Rijndael.php instead of - * to save one include_once(). - * - * If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from - * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits - * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()} + * If {@link AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from + * {@link AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits + * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link AES::setKey() setKey()} * is called, again, at which point, it'll be recalculated. * - * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't - * make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function, + * Since AES extends Rijndael, some functions are available to be called that, in the context of AES, don't + * make a whole lot of sense. {@link AES::setBlockLength() setBlockLength()}, for instance. Calling that function, * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one). * * Here's a short example of how to use this library: * * setKey('abcdefghijklmnop'); * @@ -39,85 +35,146 @@ * ?> * * + * LICENSE: 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. + * * @category Crypt * @package AES * @author Jim Wigginton - * @copyright 2008 Jim Wigginton + * @copyright MMVIII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/**#@+ + * @access public + * @see AES::encrypt() + * @see AES::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +@define('CRYPT_AES_MODE_CTR', CRYPT_MODE_CTR); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +@define('CRYPT_AES_MODE_ECB', CRYPT_MODE_ECB); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +@define('CRYPT_AES_MODE_CBC', CRYPT_MODE_CBC); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +@define('CRYPT_AES_MODE_CFB', CRYPT_MODE_CFB); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +@define('CRYPT_AES_MODE_OFB', CRYPT_MODE_OFB); +/**#@-*/ + +/**#@+ + * @access private + * @see AES::AES() + */ +/** + * Toggles the internal implementation + */ +@define('CRYPT_AES_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +@define('CRYPT_AES_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + /** * Pure-PHP implementation of AES. * * @package AES * @author Jim Wigginton + * @version 0.1.0 * @access public */ class AES extends Rijndael { /** - * Dummy function - * - * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything. + * The namespace used by the cipher for its constants. * - * @see \phpseclib\Crypt\Rijndael::setBlockLength() - * @access public - * @param int $length - * @throws \BadMethodCallException anytime it's called + * @see Base::const_namespace + * @var String + * @access private */ - function setBlockLength($length) - { - throw new \BadMethodCallException('The block length cannot be set for AES.'); - } + var $const_namespace = 'AES'; /** - * Sets the key length + * Default Constructor. + * + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_AES_MODE_ECB + * + * - CRYPT_AES_MODE_CBC + * + * - CRYPT_AES_MODE_CTR * - * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length + * - CRYPT_AES_MODE_CFB * - * @see \phpseclib\Crypt\Rijndael:setKeyLength() + * - CRYPT_AES_MODE_OFB + * + * If not explictly set, CRYPT_AES_MODE_CBC will be used. + * + * @see Rijndael::Rijndael() + * @see Base::Base() + * @param optional Integer $mode * @access public - * @param int $length - * @throws \LengthException if the key length isn't supported */ - function setKeyLength($length) + function __construct($mode = CRYPT_AES_MODE_CBC) { - switch ($length) { - case 128: - case 192: - case 256: - break; - default: - throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported'); - } - parent::setKeyLength($length); + parent::__construct($mode); } /** - * Sets the key. + * Dummy function * - * Rijndael supports five different key lengths, AES only supports three. + * Since AES extends Rijndael, this function is, technically, available, but it doesn't do anything. * - * @see \phpseclib\Crypt\Rijndael:setKey() - * @see setKeyLength() + * @see Rijndael::setBlockLength() * @access public - * @param string $key - * @throws \LengthException if the key length isn't supported + * @param Integer $length */ - function setKey($key) + function setBlockLength($length) { - switch (strlen($key)) { - case 16: - case 24: - case 32: - break; - default: - throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); - } - - parent::setKey($key); + return; } } diff --git a/src/phpseclib/Crypt/Base.php b/src/phpseclib/Crypt/Base.php index e3cc7b87..34cd67a7 100755 --- a/src/phpseclib/Crypt/Base.php +++ b/src/phpseclib/Crypt/Base.php @@ -1,14 +1,14 @@ * @author Hans-Juergen Petrich - * @copyright 2007 Jim Wigginton + * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 1.0.1 * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/**#@+ + * @access public + * @see Base::encrypt() + * @see Base::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +@define('CRYPT_MODE_CTR', -1); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +@define('CRYPT_MODE_ECB', 1); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +@define('CRYPT_MODE_CBC', 2); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +@define('CRYPT_MODE_CFB', 3); +/** + * Encrypt / decrypt using the Output Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +@define('CRYPT_MODE_OFB', 4); +/** + * Encrypt / decrypt using streaming mode. + * + */ +@define('CRYPT_MODE_STREAM', 5); +/**#@-*/ + +/**#@+ + * @access private + * @see Base::Base() + */ +/** + * Base value for the internal implementation $engine switch + */ +@define('CRYPT_MODE_INTERNAL', 1); +/** + * Base value for the mcrypt implementation $engine switch + */ +@define('CRYPT_MODE_MCRYPT', 2); +/**#@-*/ + /** - * Base Class for all \phpseclib\Crypt\* cipher classes + * Base Class for all * cipher classes * * @package Base * @author Jim Wigginton * @author Hans-Juergen Petrich + * @version 1.0.0 + * @access public */ -abstract class Base +class Base { - /**#@+ - * @access public - * @see \phpseclib\Crypt\Base::encrypt() - * @see \phpseclib\Crypt\Base::decrypt() - */ - /** - * Encrypt / decrypt using the Counter mode. - * - * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 - */ - const MODE_CTR = -1; - /** - * Encrypt / decrypt using the Electronic Code Book mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 - */ - const MODE_ECB = 1; - /** - * Encrypt / decrypt using the Code Book Chaining mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 - */ - const MODE_CBC = 2; - /** - * Encrypt / decrypt using the Cipher Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 - */ - const MODE_CFB = 3; - /** - * Encrypt / decrypt using the Output Feedback mode. - * - * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 - */ - const MODE_OFB = 4; - /** - * Encrypt / decrypt using streaming mode. - */ - const MODE_STREAM = 5; - /**#@-*/ - - /** - * Whirlpool available flag - * - * @see \phpseclib\Crypt\Base::_hashInlineCryptFunction() - * @var bool - * @access private - */ - static $WHIRLPOOL_AVAILABLE; - - /**#@+ - * @access private - * @see \phpseclib\Crypt\Base::__construct() - */ - /** - * Base value for the internal implementation $engine switch - */ - const ENGINE_INTERNAL = 1; - /** - * Base value for the mcrypt implementation $engine switch - */ - const ENGINE_MCRYPT = 2; - /** - * Base value for the mcrypt implementation $engine switch - */ - const ENGINE_OPENSSL = 3; - /**#@-*/ - /** * The Encryption Mode * - * @see self::__construct() - * @var int + * @see Base::Base() + * @var Integer * @access private */ var $mode; @@ -127,7 +136,7 @@ abstract class Base /** * The Block Length of the block cipher * - * @var int + * @var Integer * @access private */ var $block_size = 16; @@ -135,27 +144,27 @@ abstract class Base /** * The Key * - * @see self::setKey() - * @var string + * @see Base::setKey() + * @var String * @access private */ - var $key = false; + var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; /** * The Initialization Vector * - * @see self::setIV() - * @var string + * @see Base::setIV() + * @var String * @access private */ - var $iv = false; + var $iv; /** * A "sliding" Initialization Vector * - * @see self::enableContinuousBuffer() - * @see self::_clearBuffers() - * @var string + * @see Base::enableContinuousBuffer() + * @see Base::_clearBuffers() + * @var String * @access private */ var $encryptIV; @@ -163,9 +172,9 @@ abstract class Base /** * A "sliding" Initialization Vector * - * @see self::enableContinuousBuffer() - * @see self::_clearBuffers() - * @var string + * @see Base::enableContinuousBuffer() + * @see Base::_clearBuffers() + * @var String * @access private */ var $decryptIV; @@ -173,8 +182,8 @@ abstract class Base /** * Continuous Buffer status * - * @see self::enableContinuousBuffer() - * @var bool + * @see Base::enableContinuousBuffer() + * @var Boolean * @access private */ var $continuousBuffer = false; @@ -182,9 +191,9 @@ abstract class Base /** * Encryption buffer for CTR, OFB and CFB modes * - * @see self::encrypt() - * @see self::_clearBuffers() - * @var array + * @see Base::encrypt() + * @see Base::_clearBuffers() + * @var Array * @access private */ var $enbuffer; @@ -192,9 +201,9 @@ abstract class Base /** * Decryption buffer for CTR, OFB and CFB modes * - * @see self::decrypt() - * @see self::_clearBuffers() - * @var array + * @see Base::decrypt() + * @see Base::_clearBuffers() + * @var Array * @access private */ var $debuffer; @@ -205,8 +214,8 @@ abstract class Base * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. * - * @see self::encrypt() - * @var resource + * @see Base::encrypt() + * @var Resource * @access private */ var $enmcrypt; @@ -217,8 +226,8 @@ abstract class Base * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. * - * @see self::decrypt() - * @var resource + * @see Base::decrypt() + * @var Resource * @access private */ var $demcrypt; @@ -226,9 +235,9 @@ abstract class Base /** * Does the enmcrypt resource need to be (re)initialized? * - * @see \phpseclib\Crypt\Twofish::setKey() - * @see \phpseclib\Crypt\Twofish::setIV() - * @var bool + * @see Twofish::setKey() + * @see Twofish::setIV() + * @var Boolean * @access private */ var $enchanged = true; @@ -236,9 +245,9 @@ abstract class Base /** * Does the demcrypt resource need to be (re)initialized? * - * @see \phpseclib\Crypt\Twofish::setKey() - * @see \phpseclib\Crypt\Twofish::setIV() - * @var bool + * @see Twofish::setKey() + * @see Twofish::setIV() + * @var Boolean * @access private */ var $dechanged = true; @@ -254,10 +263,10 @@ abstract class Base * use a separate ECB-mode mcrypt resource. * * @link http://phpseclib.sourceforge.net/cfb-demo.phps - * @see self::encrypt() - * @see self::decrypt() - * @see self::_setupMcrypt() - * @var resource + * @see Base::encrypt() + * @see Base::decrypt() + * @see Base::_setupMcrypt() + * @var Resource * @access private */ var $ecb; @@ -266,20 +275,20 @@ abstract class Base * Optimizing value while CFB-encrypting * * Only relevant if $continuousBuffer enabled - * and $engine == self::ENGINE_MCRYPT + * and $engine == CRYPT_MODE_MCRYPT * * It's faster to re-init $enmcrypt if * $buffer bytes > $cfb_init_len than * using the $ecb resource furthermore. * - * This value depends of the chosen cipher + * This value depends of the choosen cipher * and the time it would be needed for it's * initialization [by mcrypt_generic_init()] * which, typically, depends on the complexity * on its internaly Key-expanding algorithm. * - * @see self::encrypt() - * @var int + * @see Base::encrypt() + * @var Integer * @access private */ var $cfb_init_len = 600; @@ -287,10 +296,10 @@ abstract class Base /** * Does internal cipher state need to be (re)initialized? * - * @see self::setKey() - * @see self::setIV() - * @see self::disableContinuousBuffer() - * @var bool + * @see setKey() + * @see setIV() + * @see disableContinuousBuffer() + * @var Boolean * @access private */ var $changed = true; @@ -298,8 +307,8 @@ abstract class Base /** * Padding status * - * @see self::enablePadding() - * @var bool + * @see Base::enablePadding() + * @var Boolean * @access private */ var $padding = true; @@ -307,8 +316,8 @@ abstract class Base /** * Is the mode one that is paddable? * - * @see self::__construct() - * @var bool + * @see Base::Base() + * @var Boolean * @access private */ var $paddable = false; @@ -318,83 +327,86 @@ abstract class Base * which will be determined automatically on __construct() * * Currently available $engines are: - * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) - * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) - * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required) - * - * @see self::_setEngine() - * @see self::encrypt() - * @see self::decrypt() - * @var int - * @access private - */ - var $engine; - - /** - * Holds the preferred crypt engine + * - CRYPT_MODE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required) + * - CRYPT_MODE_INTERNAL (slower, pure php-engine, no php-extension required) + * + * In the pipeline... maybe. But currently not available: + * - CRYPT_MODE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required) * - * @see self::_setEngine() - * @see self::setPreferredEngine() - * @var int + * If possible, CRYPT_MODE_MCRYPT will be used for each cipher. + * Otherwise CRYPT_MODE_INTERNAL + * + * @see Base::encrypt() + * @see Base::decrypt() + * @var Integer * @access private */ - var $preferredEngine; + var $engine; /** * The mcrypt specific name of the cipher * - * Only used if $engine == self::ENGINE_MCRYPT + * Only used if $engine == CRYPT_MODE_MCRYPT * * @link http://www.php.net/mcrypt_module_open * @link http://www.php.net/mcrypt_list_algorithms - * @see self::_setupMcrypt() - * @var string + * @see Base::_setupMcrypt() + * @var String * @access private */ var $cipher_name_mcrypt; /** - * The openssl specific name of the cipher - * - * Only used if $engine == self::ENGINE_OPENSSL + * The default password key_size used by setPassword() * - * @link http://www.php.net/openssl-get-cipher-methods - * @var string + * @see Base::setPassword() + * @var Integer * @access private */ - var $cipher_name_openssl; + var $password_key_size = 32; /** - * The openssl specific name of the cipher in ECB mode - * - * If OpenSSL does not support the mode we're trying to use (CTR) - * it can still be emulated with ECB mode. + * The default salt used by setPassword() * - * @link http://www.php.net/openssl-get-cipher-methods - * @var string + * @see Base::setPassword() + * @var String * @access private */ - var $cipher_name_openssl_ecb; + var $password_default_salt = 'phpseclib/salt'; /** - * The default salt used by setPassword() + * The namespace used by the cipher for its constants. + * + * ie: AES.php is using CRYPT_AES_MODE_* for its constants + * so $const_namespace is AES + * + * DES.php is using CRYPT_DES_MODE_* for its constants + * so $const_namespace is DES... and so on + * + * All CRYPT_<$const_namespace>_MODE_* are aliases of + * the generic CRYPT_MODE_* constants, so both could be used + * for each cipher. * - * @see self::setPassword() - * @var string + * Example: + * $aes = new AES(CRYPT_AES_MODE_CFB); // $aes will operate in cfb mode + * $aes = new AES(CRYPT_MODE_CFB); // identical + * + * @see Base::Base() + * @var String * @access private */ - var $password_default_salt = 'phpseclib/salt'; + var $const_namespace; /** * The name of the performance-optimized callback function * * Used by encrypt() / decrypt() - * only if $engine == self::ENGINE_INTERNAL + * only if $engine == CRYPT_MODE_INTERNAL * - * @see self::encrypt() - * @see self::decrypt() - * @see self::_setupInlineCrypt() - * @see self::$use_inline_crypt + * @see Base::encrypt() + * @see Base::decrypt() + * @see Base::_setupInlineCrypt() + * @see Base::$use_inline_crypt * @var Callback * @access private */ @@ -403,89 +415,90 @@ abstract class Base /** * Holds whether performance-optimized $inline_crypt() can/should be used. * - * @see self::encrypt() - * @see self::decrypt() - * @see self::inline_crypt + * @see Base::encrypt() + * @see Base::decrypt() + * @see Base::inline_crypt * @var mixed * @access private */ var $use_inline_crypt; /** - * If OpenSSL can be used in ECB but not in CTR we can emulate CTR - * - * @see self::_openssl_ctr_process() - * @var bool - * @access private - */ - var $openssl_emulate_ctr = false; - - /** - * Determines what options are passed to openssl_encrypt/decrypt + * Default Constructor. * - * @see self::isValidEngine() - * @var mixed - * @access private - */ - var $openssl_options; - - /** - * Don't truncate / null pad key + * Determines whether or not the mcrypt extension should be used. * - * @see self::_clearBuffers() - * @var bool - * @access private - */ - var $skip_key_adjustment = false; - - /** - * Has the key length explicitly been set or should it be derived from the key, itself? + * $mode could be: * - * @see self::setKeyLength() - * @var bool - * @access private - */ - var $explicit_key_length = false; - - /** - * Default Constructor. + * - CRYPT_MODE_ECB * - * $mode could be: + * - CRYPT_MODE_CBC * - * - self::MODE_ECB + * - CRYPT_MODE_CTR * - * - self::MODE_CBC + * - CRYPT_MODE_CFB * - * - self::MODE_CTR + * - CRYPT_MODE_OFB * - * - self::MODE_CFB + * (or the alias constants of the choosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...) * - * - self::MODE_OFB + * If not explictly set, CRYPT_MODE_CBC will be used. * - * @param int $mode + * @param optional Integer $mode * @access public - * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ - function __construct($mode) + + public function Base($mode = CRYPT_MODE_CBC){ + $this->__construct($mode); + } + + function __construct($mode = CRYPT_MODE_CBC) { + $const_crypt_mode = 'CRYPT_' . $this->const_namespace . '_MODE'; + + // Determining the availibility of mcrypt support for the cipher + + if (!defined($const_crypt_mode)) { + switch (true) { + case extension_loaded('mcrypt') && in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()): + @define($const_crypt_mode, CRYPT_MODE_MCRYPT); + break; + default: + @define($const_crypt_mode, CRYPT_MODE_INTERNAL); + } + } + + // Determining which internal $engine should be used. + // The fastes possible first. + switch (true) { + case empty($this->cipher_name_mcrypt): // The cipher module has no mcrypt-engine support at all so we force CRYPT_MODE_INTERNAL + $this->engine = CRYPT_MODE_INTERNAL; + break; + case constant($const_crypt_mode) == CRYPT_MODE_MCRYPT: + $this->engine = CRYPT_MODE_MCRYPT; + break; + default: + $this->engine = CRYPT_MODE_INTERNAL; + } + // $mode dependent settings switch ($mode) { - case self::MODE_ECB: - case self::MODE_CBC: + case CRYPT_MODE_ECB: $this->paddable = true; + $this->mode = $mode; break; - case self::MODE_CTR: - case self::MODE_CFB: - case self::MODE_OFB: - case self::MODE_STREAM: - $this->paddable = false; + case CRYPT_MODE_CTR: + case CRYPT_MODE_CFB: + case CRYPT_MODE_OFB: + case CRYPT_MODE_STREAM: + $this->mode = $mode; break; + case CRYPT_MODE_CBC: default: - throw new \InvalidArgumentException('No valid mode has been specified'); + $this->paddable = true; + $this->mode = CRYPT_MODE_CBC; } - $this->mode = $mode; - // Determining whether inline crypting can be used by the cipher if ($this->use_inline_crypt !== false && function_exists('create_function')) { $this->use_inline_crypt = true; @@ -493,85 +506,26 @@ function __construct($mode) } /** - * Sets the initialization vector. + * Sets the initialization vector. (optional) + * + * SetIV is not required when CRYPT_MODE_ECB (or ie for AES: CRYPT_AES_MODE_ECB) is being used. If not explictly set, it'll be assumed + * to be all zero's. * - * setIV() is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. + * Note: Could, but not must, extend by the child * class * * @access public - * @param string $iv - * @throws \LengthException if the IV length isn't equal to the block size - * @throws \InvalidArgumentException if an IV is provided when one shouldn't be - * @internal Can be overwritten by a sub class, but does not have to be + * @param String $iv */ function setIV($iv) { - if ($this->mode == self::MODE_ECB) { - throw new \InvalidArgumentException('This mode does not require an IV.'); - } - - if ($this->mode == self::MODE_STREAM && $this->usesIV()) { - throw new \InvalidArgumentException('This algorithm does not use an IV.'); - } - - if (strlen($iv) != $this->block_size) { - throw new \LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required'); + if ($this->mode == CRYPT_MODE_ECB) { + return; } $this->iv = $iv; $this->changed = true; } - /** - * Returns whether or not the algorithm uses an IV - * - * @access public - * @return bool - */ - function usesIV() - { - return true; - } - - /** - * Returns the current key length in bits - * - * @access public - * @return int - */ - function getKeyLength() - { - return $this->key_length << 3; - } - - /** - * Returns the current block length in bits - * - * @access public - * @return int - */ - function getBlockLength() - { - return $this->block_size << 3; - } - - /** - * Sets the key length. - * - * Keys with explicitly set lengths need to be treated accordingly - * - * @access public - * @param int $length - */ - function setKeyLength($length) - { - $this->explicit_key_length = $length >> 3; - - if (is_string($this->key) && strlen($this->key) != $this->explicit_key_length) { - $this->key = false; - throw new \LengthException('Key has already been set and is not ' .$this->explicit_key_length . ' bytes long'); - } - } - /** * Sets the key. * @@ -582,45 +536,39 @@ function setKeyLength($length) * * If the key is not explicitly set, it'll be assumed to be all null bytes. * + * Note: Could, but not must, extend by the child * class + * * @access public - * @param string $key - * @internal Could, but not must, extend by the child Crypt_* class + * @param String $key */ function setKey($key) { - if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { - throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); - } - $this->key = $key; - $this->key_length = strlen($key); $this->changed = true; - $this->_setEngine(); } /** * Sets the password. * * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows: - * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1: + * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}: * $hash, $salt, $count, $dkLen * * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php * + * Note: Could, but not must, extend by the child * class + * * @see Crypt/Hash.php - * @param string $password - * @param string $method - * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length - * @return bool + * @param String $password + * @param optional String $method * @access public - * @internal Could, but not must, extend by the child Crypt_* class */ function setPassword($password, $method = 'pbkdf2') { $key = ''; switch ($method) { - default: // 'pbkdf2' or 'pbkdf1' + default: // 'pbkdf2' $func_args = func_get_args(); // Hash function @@ -634,31 +582,10 @@ function setPassword($password, $method = 'pbkdf2') $count = isset($func_args[4]) ? $func_args[4] : 1000; // Keylength - if (isset($func_args[5])) { - $dkLen = $func_args[5]; - } else { - $key_length = $this->explicit_key_length !== false ? $this->explicit_key_length : $this->key_length; - $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length; - } + $dkLen = isset($func_args[5]) ? $func_args[5] : $this->password_key_size; + // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable switch (true) { - case $method == 'pbkdf1': - $hashObj = new Hash(); - $hashObj->setHash($hash); - if ($dkLen > $hashObj->getLength()) { - throw new \LengthException('Derived key length cannot be longer than the hash length'); - } - $t = $password . $salt; - for ($i = 0; $i < $count; ++$i) { - $t = $hashObj->hash($t); - } - $key = substr($t, 0, $dkLen); - - $this->setKey(substr($key, 0, $dkLen >> 1)); - $this->setIV(substr($key, $dkLen >> 1)); - - return true; - // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable case !function_exists('hash_pbkdf2'): case !function_exists('hash_algos'): case !in_array($hash, hash_algos()): @@ -667,9 +594,9 @@ function setPassword($password, $method = 'pbkdf2') $hmac = new Hash(); $hmac->setHash($hash); $hmac->setKey($password); - $f = $u = $hmac->hash($salt . pack('N', $i++)); + $f = $u = $hmac->_hash($salt . pack('N', $i++)); for ($j = 2; $j <= $count; ++$j) { - $u = $hmac->hash($u); + $u = $hmac->_hash($u); $f^= $u; } $key.= $f; @@ -682,8 +609,6 @@ function setPassword($password, $method = 'pbkdf2') } $this->setKey($key); - - return true; } /** @@ -700,107 +625,30 @@ function setPassword($password, $method = 'pbkdf2') * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that * length. * - * @see self::decrypt() + * Note: Could, but not must, extend by the child * class + * + * @see Base::decrypt() * @access public - * @param string $plaintext - * @return string $ciphertext - * @internal Could, but not must, extend by the child Crypt_* class + * @param String $plaintext + * @return String $cipertext */ function encrypt($plaintext) { - if ($this->paddable) { - $plaintext = $this->_pad($plaintext); - } - - if ($this->engine === self::ENGINE_OPENSSL) { - if ($this->changed) { - $this->_clearBuffers(); - $this->changed = false; - } - switch ($this->mode) { - case self::MODE_STREAM: - return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); - case self::MODE_ECB: - $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); - return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; - case self::MODE_CBC: - $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); - if (!defined('OPENSSL_RAW_DATA')) { - $result = substr($result, 0, -$this->block_size); - } - if ($this->continuousBuffer) { - $this->encryptIV = substr($result, -$this->block_size); - } - return $result; - case self::MODE_CTR: - return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer); - case self::MODE_CFB: - // cfb loosely routines inspired by openssl's: - // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} - $ciphertext = ''; - if ($this->continuousBuffer) { - $iv = &$this->encryptIV; - $pos = &$this->enbuffer['pos']; - } else { - $iv = $this->encryptIV; - $pos = 0; - } - $len = strlen($plaintext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = $this->block_size - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - $plaintext = substr($plaintext, $i); - } - - $overflow = $len % $this->block_size; - - if ($overflow) { - $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); - $iv = $this->_string_pop($ciphertext, $this->block_size); - - $size = $len - $overflow; - $block = $iv ^ substr($plaintext, -$overflow); - $iv = substr_replace($iv, $block, 0, $overflow); - $ciphertext.= $block; - $pos = $overflow; - } elseif ($len) { - $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); - $iv = substr($ciphertext, -$this->block_size); - } - - return $ciphertext; - case self::MODE_OFB: - return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); - } - } - - if ($this->engine === self::ENGINE_MCRYPT) { + if ($this->engine == CRYPT_MODE_MCRYPT) { if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->enchanged) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->_getIV($this->encryptIV)); + + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); $this->enchanged = false; } // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's // rewritten CFB implementation the above outputs the same thing twice. - if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { + if (false && $this->continuousBuffer) { $block_size = $this->block_size; $iv = &$this->encryptIV; $pos = &$this->enbuffer['pos']; @@ -853,10 +701,14 @@ function encrypt($plaintext) return $ciphertext; } + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { - mcrypt_generic_init($this->enmcrypt, $this->key, $this->_getIV($this->encryptIV)); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); } return $ciphertext; @@ -870,17 +722,20 @@ function encrypt($plaintext) $inline = $this->inline_crypt; return $inline('encrypt', $this, $plaintext); } + if ($this->paddable) { + $plaintext = $this->_pad($plaintext); + } $buffer = &$this->enbuffer; $block_size = $this->block_size; $ciphertext = ''; switch ($this->mode) { - case self::MODE_ECB: + case CRYPT_MODE_ECB: for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); } break; - case self::MODE_CBC: + case CRYPT_MODE_CBC: $xor = $this->encryptIV; for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); @@ -892,76 +747,75 @@ function encrypt($plaintext) $this->encryptIV = $xor; } break; - case self::MODE_CTR: + case CRYPT_MODE_CTR: $xor = $this->encryptIV; - if (strlen($buffer['ciphertext'])) { + if (strlen($buffer['encrypted'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - if (strlen($block) > strlen($buffer['ciphertext'])) { - $buffer['ciphertext'].= $this->_encryptBlock($xor); + if (strlen($block) > strlen($buffer['encrypted'])) { + $buffer['encrypted'].= $this->_encryptBlock($this->_generateXor($xor, $block_size)); } - $this->_increment_str($xor); - $key = $this->_string_shift($buffer['ciphertext'], $block_size); + $key = $this->_stringShift($buffer['encrypted'], $block_size); $ciphertext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - $key = $this->_encryptBlock($xor); - $this->_increment_str($xor); + $key = $this->_encryptBlock($this->_generateXor($xor, $block_size)); $ciphertext.= $block ^ $key; } } if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { - $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; + $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; } } break; - case self::MODE_CFB: - // cfb loosely routines inspired by openssl's: - // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} - if ($this->continuousBuffer) { - $iv = &$this->encryptIV; - $pos = &$buffer['pos']; - } else { - $iv = $this->encryptIV; - $pos = 0; - } - $len = strlen($plaintext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = $block_size - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize - $ciphertext = substr($iv, $orig_pos) ^ $plaintext; - $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); - } - while ($len >= $block_size) { - $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); - $ciphertext.= $iv; - $len-= $block_size; - $i+= $block_size; - } - if ($len) { - $iv = $this->_encryptBlock($iv); - $block = $iv ^ substr($plaintext, $i); - $iv = substr_replace($iv, $block, 0, $len); - $ciphertext.= $block; - $pos = $len; - } + + case CRYPT_MODE_CFB: + // cfb loosely routines inspired by openssl's: + // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} + if ($this->continuousBuffer) { + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; + } + $len = strlen($plaintext); + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = $block_size - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; + } + // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= $block_size) { + $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); + $ciphertext.= $iv; + $len-= $block_size; + $i+= $block_size; + } + if ($len) { + $iv = $this->_encryptBlock($iv); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } break; - case self::MODE_OFB: + case CRYPT_MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { @@ -970,7 +824,7 @@ function encrypt($plaintext) $xor = $this->_encryptBlock($xor); $buffer['xor'].= $xor; } - $key = $this->_string_shift($buffer['xor'], $block_size); + $key = $this->_stringShift($buffer['xor'], $block_size); $ciphertext.= $block ^ $key; } } else { @@ -983,11 +837,11 @@ function encrypt($plaintext) if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; + $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; - case self::MODE_STREAM: + case CRYPT_MODE_STREAM: $ciphertext = $this->_encryptBlock($plaintext); break; } @@ -1001,114 +855,27 @@ function encrypt($plaintext) * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until * it is. * - * @see self::encrypt() + * Note: Could, but not must, extend by the child * class + * + * @see Base::encrypt() * @access public - * @param string $ciphertext - * @return string $plaintext - * @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size - * @internal Could, but not must, extend by the child Crypt_* class + * @param String $ciphertext + * @return String $plaintext */ function decrypt($ciphertext) { - if ($this->paddable && strlen($ciphertext) % $this->block_size) { - throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')'); - } - - if ($this->engine === self::ENGINE_OPENSSL) { - if ($this->changed) { - $this->_clearBuffers(); - $this->changed = false; - } - switch ($this->mode) { - case self::MODE_STREAM: - $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); - break; - case self::MODE_ECB: - if (!defined('OPENSSL_RAW_DATA')) { - $ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); - } - $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); - break; - case self::MODE_CBC: - if (!defined('OPENSSL_RAW_DATA')) { - $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); - $ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); - $offset = 2 * $this->block_size; - } else { - $offset = $this->block_size; - } - $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); - if ($this->continuousBuffer) { - $this->decryptIV = substr($ciphertext, -$offset, $this->block_size); - } - break; - case self::MODE_CTR: - $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer); - break; - case self::MODE_CFB: - // cfb loosely routines inspired by openssl's: - // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1} - $plaintext = ''; - if ($this->continuousBuffer) { - $iv = &$this->decryptIV; - $pos = &$this->buffer['pos']; - } else { - $iv = $this->decryptIV; - $pos = 0; - } - $len = strlen($ciphertext); - $i = 0; - if ($pos) { - $orig_pos = $pos; - $max = $this->block_size - $pos; - if ($len >= $max) { - $i = $max; - $len-= $max; - $pos = 0; - } else { - $i = $len; - $pos+= $len; - $len = 0; - } - // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize - $plaintext = substr($iv, $orig_pos) ^ $ciphertext; - $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); - $ciphertext = substr($ciphertext, $i); - } - $overflow = $len % $this->block_size; - if ($overflow) { - $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); - if ($len - $overflow) { - $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow); - } - $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); - $plaintext.= $iv ^ substr($ciphertext, -$overflow); - $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow); - $pos = $overflow; - } elseif ($len) { - $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); - $iv = substr($ciphertext, -$this->block_size); - } - break; - case self::MODE_OFB: - $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); - } - - return $this->paddable ? $this->_unpad($plaintext) : $plaintext; - } - - if ($this->engine === self::ENGINE_MCRYPT) { + if ($this->engine == CRYPT_MODE_MCRYPT) { $block_size = $this->block_size; if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->dechanged) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->_getIV($this->decryptIV)); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); $this->dechanged = false; } - if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { + if (false && $this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$this->debuffer['pos']; $len = strlen($ciphertext); @@ -1146,10 +913,16 @@ function decrypt($ciphertext) return $plaintext; } + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($block_size - strlen($ciphertext) % $block_size) % $block_size, chr(0)); + } + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { - mcrypt_generic_init($this->demcrypt, $this->key, $this->_getIV($this->decryptIV)); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; @@ -1165,16 +938,20 @@ function decrypt($ciphertext) } $block_size = $this->block_size; + if ($this->paddable) { + // we pad with chr(0) since that's what mcrypt_generic does [...] + $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($block_size - strlen($ciphertext) % $block_size) % $block_size, chr(0)); + } $buffer = &$this->debuffer; $plaintext = ''; switch ($this->mode) { - case self::MODE_ECB: + case CRYPT_MODE_ECB: for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); } break; - case self::MODE_CBC: + case CRYPT_MODE_CBC: $xor = $this->decryptIV; for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); @@ -1185,23 +962,21 @@ function decrypt($ciphertext) $this->decryptIV = $xor; } break; - case self::MODE_CTR: + case CRYPT_MODE_CTR: $xor = $this->decryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { - $buffer['ciphertext'].= $this->_encryptBlock($xor); - $this->_increment_str($xor); + $buffer['ciphertext'].= $this->_encryptBlock($this->_generateXor($xor, $block_size)); } - $key = $this->_string_shift($buffer['ciphertext'], $block_size); + $key = $this->_stringShift($buffer['ciphertext'], $block_size); $plaintext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); - $key = $this->_encryptBlock($xor); - $this->_increment_str($xor); + $key = $this->_encryptBlock($this->_generateXor($xor, $block_size)); $plaintext.= $block ^ $key; } } @@ -1212,7 +987,7 @@ function decrypt($ciphertext) } } break; - case self::MODE_CFB: + case CRYPT_MODE_CFB: if ($this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$buffer['pos']; @@ -1253,7 +1028,7 @@ function decrypt($ciphertext) $pos = $len; } break; - case self::MODE_OFB: + case CRYPT_MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { @@ -1262,7 +1037,7 @@ function decrypt($ciphertext) $xor = $this->_encryptBlock($xor); $buffer['xor'].= $xor; } - $key = $this->_string_shift($buffer['xor'], $block_size); + $key = $this->_stringShift($buffer['xor'], $block_size); $plaintext.= $block ^ $key; } } else { @@ -1275,205 +1050,17 @@ function decrypt($ciphertext) if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % $block_size) { - $buffer['xor'] = substr($key, $start) . $buffer['xor']; + $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; - case self::MODE_STREAM: + case CRYPT_MODE_STREAM: $plaintext = $this->_decryptBlock($ciphertext); break; } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } - /** - * Get the IV - * - * mcrypt requires an IV even if ECB is used - * - * @see self::encrypt() - * @see self::decrypt() - * @param string $iv - * @return string - * @access private - */ - function _getIV($iv) - { - return $this->mode == self::MODE_ECB ? str_repeat("\0", $this->block_size) : $iv; - } - - /** - * OpenSSL CTR Processor - * - * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream - * for CTR is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() - * and Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this - * function will emulate CTR with ECB when necesary. - * - * @see self::encrypt() - * @see self::decrypt() - * @param string $plaintext - * @param string $encryptIV - * @param array $buffer - * @return string - * @access private - */ - function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer) - { - $ciphertext = ''; - - $block_size = $this->block_size; - $key = $this->key; - - if ($this->openssl_emulate_ctr) { - $xor = $encryptIV; - if (strlen($buffer['ciphertext'])) { - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $block = substr($plaintext, $i, $block_size); - if (strlen($block) > strlen($buffer['ciphertext'])) { - $result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); - $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; - $buffer['ciphertext'].= $result; - } - $this->_increment_str($xor); - $otp = $this->_string_shift($buffer['ciphertext'], $block_size); - $ciphertext.= $block ^ $otp; - } - } else { - for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { - $block = substr($plaintext, $i, $block_size); - $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); - $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; - $this->_increment_str($xor); - $ciphertext.= $block ^ $otp; - } - } - if ($this->continuousBuffer) { - $encryptIV = $xor; - if ($start = strlen($plaintext) % $block_size) { - $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; - } - } - - return $ciphertext; - } - - if (strlen($buffer['ciphertext'])) { - $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext)); - $plaintext = substr($plaintext, strlen($ciphertext)); - - if (!strlen($plaintext)) { - return $ciphertext; - } - } - - $overflow = strlen($plaintext) % $block_size; - if ($overflow) { - $plaintext2 = $this->_string_pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2 - $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); - $temp = $this->_string_pop($encrypted, $block_size); - $ciphertext.= $encrypted . ($plaintext2 ^ $temp); - if ($this->continuousBuffer) { - $buffer['ciphertext'] = substr($temp, $overflow); - $encryptIV = $temp; - } - } elseif (!strlen($buffer['ciphertext'])) { - $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); - $temp = $this->_string_pop($ciphertext, $block_size); - if ($this->continuousBuffer) { - $encryptIV = $temp; - } - } - if ($this->continuousBuffer) { - if (!defined('OPENSSL_RAW_DATA')) { - $encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); - } - $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); - if ($overflow) { - $this->_increment_str($encryptIV); - } - } - - return $ciphertext; - } - - /** - * OpenSSL OFB Processor - * - * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream - * for OFB is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() - * and Base::decrypt(). - * - * @see self::encrypt() - * @see self::decrypt() - * @param string $plaintext - * @param string $encryptIV - * @param array $buffer - * @return string - * @access private - */ - function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer) - { - if (strlen($buffer['xor'])) { - $ciphertext = $plaintext ^ $buffer['xor']; - $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); - $plaintext = substr($plaintext, strlen($ciphertext)); - } else { - $ciphertext = ''; - } - - $block_size = $this->block_size; - - $len = strlen($plaintext); - $key = $this->key; - $overflow = $len % $block_size; - - if (strlen($plaintext)) { - if ($overflow) { - $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); - $xor = $this->_string_pop($ciphertext, $block_size); - if ($this->continuousBuffer) { - $encryptIV = $xor; - } - $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow); - if ($this->continuousBuffer) { - $buffer['xor'] = $xor; - } - } else { - $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); - if ($this->continuousBuffer) { - $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size); - } - } - } - - return $ciphertext; - } - - /** - * phpseclib <-> OpenSSL Mode Mapper - * - * May need to be overwritten by classes extending this one in some cases - * - * @return int - * @access private - */ - function _openssl_translate_mode() - { - switch ($this->mode) { - case self::MODE_ECB: - return 'ecb'; - case self::MODE_CBC: - return 'cbc'; - case self::MODE_CTR: - return 'ctr'; - case self::MODE_CFB: - return 'cfb'; - case self::MODE_OFB: - return 'ofb'; - } - } - /** * Pad "packets". * @@ -1486,7 +1073,7 @@ function _openssl_translate_mode() * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is * transmitted separately) * - * @see self::disablePadding() + * @see Base::disablePadding() * @access public */ function enablePadding() @@ -1497,7 +1084,7 @@ function enablePadding() /** * Do not pad packets. * - * @see self::enablePadding() + * @see Base::enablePadding() * @access public */ function disablePadding() @@ -1534,24 +1121,23 @@ function disablePadding() * outputs. The reason is due to the fact that the initialization vector's change after every encryption / * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. * - * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\*() object changes after each + * Put another way, when the continuous buffer is enabled, the state of the *() object changes after each * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * - * @see self::disableContinuousBuffer() + * Note: Could, but not must, extend by the child * class + * + * @see Base::disableContinuousBuffer() * @access public - * @internal Could, but not must, extend by the child Crypt_* class */ function enableContinuousBuffer() { - if ($this->mode == self::MODE_ECB) { + if ($this->mode == CRYPT_MODE_ECB) { return; } $this->continuousBuffer = true; - - $this->_setEngine(); } /** @@ -1559,13 +1145,14 @@ function enableContinuousBuffer() * * The default behavior. * - * @see self::enableContinuousBuffer() + * Note: Could, but not must, extend by the child * class + * + * @see Base::enableContinuousBuffer() * @access public - * @internal Could, but not must, extend by the child Crypt_* class */ function disableContinuousBuffer() { - if ($this->mode == self::MODE_ECB) { + if ($this->mode == CRYPT_MODE_ECB) { return; } if (!$this->continuousBuffer) { @@ -1574,191 +1161,56 @@ function disableContinuousBuffer() $this->continuousBuffer = false; $this->changed = true; - - $this->_setEngine(); - } - - /** - * Test for engine validity - * - * @see self::__construct() - * @param int $engine - * @access public - * @return bool - */ - function isValidEngine($engine) - { - switch ($engine) { - case self::ENGINE_OPENSSL: - if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) { - return false; - } - $this->openssl_emulate_ctr = false; - $result = $this->cipher_name_openssl && - extension_loaded('openssl') && - // PHP 5.3.0 - 5.3.2 did not let you set IV's - version_compare(PHP_VERSION, '5.3.3', '>='); - if (!$result) { - return false; - } - - // prior to PHP 5.4.0 OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer - // $options openssl_encrypt expected a boolean $raw_data. - if (!defined('OPENSSL_RAW_DATA')) { - $this->openssl_options = true; - } else { - $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; - } - - $methods = openssl_get_cipher_methods(); - if (in_array($this->cipher_name_openssl, $methods)) { - return true; - } - // not all of openssl's symmetric cipher's support ctr. for those - // that don't we'll emulate it - switch ($this->mode) { - case self::MODE_CTR: - if (in_array($this->cipher_name_openssl_ecb, $methods)) { - $this->openssl_emulate_ctr = true; - return true; - } - } - return false; - case self::ENGINE_MCRYPT: - return $this->cipher_name_mcrypt && - extension_loaded('mcrypt') && - in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); - case self::ENGINE_INTERNAL: - return true; - } - - return false; - } - - /** - * Sets the preferred crypt engine - * - * Currently, $engine could be: - * - * - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast] - * - * - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast] - * - * - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow] - * - * If the preferred crypt engine is not available the fastest available one will be used - * - * @see self::__construct() - * @param int $engine - * @access public - */ - function setPreferredEngine($engine) - { - switch ($engine) { - //case self::ENGINE_OPENSSL; - case self::ENGINE_MCRYPT: - case self::ENGINE_INTERNAL: - $this->preferredEngine = $engine; - break; - default: - $this->preferredEngine = self::ENGINE_OPENSSL; - } - - $this->_setEngine(); } /** - * Returns the engine currently being utilized + * Encrypts a block * - * @see self::_setEngine() - * @access public - */ - function getEngine() - { - return $this->engine; - } - - /** - * Sets the engine as appropriate + * Note: Must extend by the child * class * - * @see self::__construct() * @access private + * @param String $in + * @return String */ - function _setEngine() + function _encryptBlock($in) { - $this->engine = null; - - $candidateEngines = array( - $this->preferredEngine, - self::ENGINE_OPENSSL, - self::ENGINE_MCRYPT - ); - foreach ($candidateEngines as $engine) { - if ($this->isValidEngine($engine)) { - $this->engine = $engine; - break; - } - } - if (!$this->engine) { - $this->engine = self::ENGINE_INTERNAL; - } - - if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { - // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, - // (re)open them with the module named in $this->cipher_name_mcrypt - mcrypt_module_close($this->enmcrypt); - mcrypt_module_close($this->demcrypt); - $this->enmcrypt = null; - $this->demcrypt = null; - - if ($this->ecb) { - mcrypt_module_close($this->ecb); - $this->ecb = null; - } - } - - $this->changed = true; + user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); } - /** - * Encrypts a block - * - * Note: Must be extended by the child \phpseclib\Crypt\* class - * - * @access private - * @param string $in - * @return string - */ - abstract function _encryptBlock($in); - /** * Decrypts a block * - * Note: Must be extended by the child \phpseclib\Crypt\* class + * Note: Must extend by the child * class * * @access private - * @param string $in - * @return string + * @param String $in + * @return String */ - abstract function _decryptBlock($in); + function _decryptBlock($in) + { + user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + } /** * Setup the key (expansion) * - * Only used if $engine == self::ENGINE_INTERNAL + * Only used if $engine == CRYPT_MODE_INTERNAL * - * Note: Must extend by the child \phpseclib\Crypt\* class + * Note: Must extend by the child * class * - * @see self::_setup() + * @see Base::_setup() * @access private */ - abstract function _setupKey(); + function _setupKey() + { + user_error((version_compare(PHP_VERSION, '5.0.0', '>=') ? __METHOD__ : __FUNCTION__) . '() must extend by class ' . get_class($this), E_USER_ERROR); + } /** - * Setup the self::ENGINE_INTERNAL $engine + * Setup the CRYPT_MODE_INTERNAL $engine * * (re)init, if necessary, the internal cipher $engine and flush all $buffers - * Used (only) if $engine == self::ENGINE_INTERNAL + * Used (only) if $engine == CRYPT_MODE_INTERNAL * * _setup() will be called each time if $changed === true * typically this happens when using one or more of following public methods: @@ -1771,12 +1223,14 @@ abstract function _setupKey(); * * - First run of encrypt() / decrypt() with no init-settings * - * @see self::setKey() - * @see self::setIV() - * @see self::disableContinuousBuffer() + * Internally: _setup() is called always before(!) en/decryption. + * + * Note: Could, but not must, extend by the child * class + * + * @see setKey() + * @see setIV() + * @see disableContinuousBuffer() * @access private - * @internal _setup() is always called before en/decryption. - * @internal Could, but not must, extend by the child Crypt_* class */ function _setup() { @@ -1789,10 +1243,10 @@ function _setup() } /** - * Setup the self::ENGINE_MCRYPT $engine + * Setup the CRYPT_MODE_MCRYPT $engine * * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers - * Used (only) if $engine = self::ENGINE_MCRYPT + * Used (only) if $engine = CRYPT_MODE_MCRYPT * * _setupMcrypt() will be called each time if $changed === true * typically this happens when using one or more of following public methods: @@ -1805,11 +1259,13 @@ function _setup() * * - First run of encrypt() / decrypt() * - * @see self::setKey() - * @see self::setIV() - * @see self::disableContinuousBuffer() + * + * Note: Could, but not must, extend by the child * class + * + * @see setKey() + * @see setIV() + * @see disableContinuousBuffer() * @access private - * @internal Could, but not must, extend by the child Crypt_* class */ function _setupMcrypt() { @@ -1818,12 +1274,12 @@ function _setupMcrypt() if (!isset($this->enmcrypt)) { static $mcrypt_modes = array( - self::MODE_CTR => 'ctr', - self::MODE_ECB => MCRYPT_MODE_ECB, - self::MODE_CBC => MCRYPT_MODE_CBC, - self::MODE_CFB => 'ncfb', - self::MODE_OFB => MCRYPT_MODE_NOFB, - self::MODE_STREAM => MCRYPT_MODE_STREAM, + CRYPT_MODE_CTR => 'ctr', + CRYPT_MODE_ECB => MCRYPT_MODE_ECB, + CRYPT_MODE_CBC => MCRYPT_MODE_CBC, + CRYPT_MODE_CFB => 'cfb', + CRYPT_MODE_OFB => MCRYPT_MODE_NOFB, + CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM, ); $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); @@ -1832,12 +1288,13 @@ function _setupMcrypt() // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} - if ($this->mode == self::MODE_CFB) { + if (false) { $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } + } // else should mcrypt_generic_deinit be called? - if ($this->mode == self::MODE_CFB) { + if (false) { mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } @@ -1852,11 +1309,10 @@ function _setupMcrypt() * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless * and padding will, hence forth, be enabled. * - * @see self::_unpad() - * @param string $text - * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size + * @see Base::_unpad() + * @param String $text * @access private - * @return string + * @return String */ function _pad($text) { @@ -1866,7 +1322,8 @@ function _pad($text) if ($length % $this->block_size == 0) { return $text; } else { - throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); + user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); + $this->padding = true; } } @@ -1881,11 +1338,10 @@ function _pad($text) * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong * and false will be returned. * - * @see self::_pad() - * @param string $text - * @throws \LengthException if the ciphertext's length is not a multiple of the block size + * @see Base::_pad() + * @param String $text * @access private - * @return string + * @return String */ function _unpad($text) { @@ -1896,7 +1352,7 @@ function _unpad($text) $length = ord($text[strlen($text) - 1]); if (!$length || $length > $this->block_size) { - throw new \LengthException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})"); + return false; } return substr($text, 0, -$length); @@ -1909,19 +1365,18 @@ function _unpad($text) * after disableContinuousBuffer() or on cipher $engine (re)init * ie after setKey() or setIV() * - * @access private - * @internal Could, but not must, extend by the child Crypt_* class - * @throws \UnexpectedValueException when an IV is required but not defined + * Note: Could, but not must, extend by the child * class + * + * @access public */ function _clearBuffers() { - $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); - - if ($this->iv === false && !in_array($this->mode, array(self::MODE_STREAM, self::MODE_ECB))) { - throw new \UnexpectedValueException('No IV has been defined'); - } + $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); - $this->encryptIV = $this->decryptIV = $this->iv; + // mcrypt's handling of invalid's $iv: + // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size); + $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0"); } /** @@ -1929,12 +1384,12 @@ function _clearBuffers() * * Inspired by array_shift * - * @param string $string - * @param int $index + * @param String $string + * @param optional Integer $index * @access private - * @return string + * @return String */ - function _string_shift(&$string, $index = 1) + function _stringShift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); @@ -1942,57 +1397,43 @@ function _string_shift(&$string, $index = 1) } /** - * String Pop + * Generate CTR XOR encryption key * - * Inspired by array_pop + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. * - * @param string $string - * @param int $index + * @see Base::decrypt() + * @see Base::encrypt() + * @param String $iv + * @param Integer $length * @access private - * @return string + * @return String $xor */ - function _string_pop(&$string, $index = 1) + function _generateXor(&$iv, $length) { - $substr = substr($string, -$index); - $string = substr($string, 0, -$index); - return $substr; - } - - /** - * Increment the current string - * - * @see self::decrypt() - * @see self::encrypt() - * @param string $var - * @access private - */ - function _increment_str(&$var) - { - for ($i = 4; $i <= strlen($var); $i+= 4) { - $temp = substr($var, -$i, 4); - switch ($temp) { - case "\xFF\xFF\xFF\xFF": - $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4); - break; - case "\x7F\xFF\xFF\xFF": - $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4); - return; - default: - $temp = unpack('Nnum', $temp); - $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4); - return; + $xor = ''; + $block_size = $this->block_size; + $num_blocks = floor(($length + ($block_size - 1)) / $block_size); + for ($i = 0; $i < $num_blocks; $i++) { + $xor.= $iv; + for ($j = 4; $j <= $block_size; $j+= 4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } } } - $remainder = strlen($var) % 4; - - if ($remainder == 0) { - return; - } - - $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT)); - $temp = substr(pack('N', $temp['num'] + 1), -$remainder); - $var = substr_replace($var, $temp, 0, $remainder); + return $xor; } /** @@ -2005,14 +1446,14 @@ function _increment_str(&$var) * * _setupInlineCrypt() would be called only if: * - * - $engine == self::ENGINE_INTERNAL and + * - $engine == CRYPT_MODE_INTERNAL and * * - $use_inline_crypt === true * * - each time on _setup(), after(!) _setupKey() * * - * This ensures that _setupInlineCrypt() has always a + * This ensures that _setupInlineCrypt() has allways a * full ready2go initializated internal cipher $engine state * where, for example, the keys allready expanded, * keys/block_size calculated and such. @@ -2040,7 +1481,7 @@ function _increment_str(&$var) * - short (as good as possible) * * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code. - * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib\Crypt\* class. + * - In case of using inline crypting, _setupInlineCrypt() must extend by the child * class. * - The following variable names are reserved: * - $_* (all variable names prefixed with an underscore) * - $self (object reference to it self. Do not use $this, but $self instead) @@ -2048,18 +1489,19 @@ function _increment_str(&$var) * - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only * * - * @see self::_setup() - * @see self::_createInlineCryptFunction() - * @see self::encrypt() - * @see self::decrypt() + * @see Base::_setup() + * @see Base::_createInlineCryptFunction() + * @see Base::encrypt() + * @see Base::decrypt() * @access private - * @internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt() */ function _setupInlineCrypt() { - // If, for any reason, an extending \phpseclib\Crypt\Base() \phpseclib\Crypt\* class + // If a * class providing inline crypting it must extend _setupInlineCrypt() + + // If, for any reason, an extending Base() * class // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false - // ie in the class var declaration of $use_inline_crypt in general for the \phpseclib\Crypt\* class, + // ie in the class var declaration of $use_inline_crypt in general for the * class, // in the constructor at object instance-time // or, if it's runtime-specific, at runtime @@ -2156,7 +1598,7 @@ function _setupInlineCrypt() * +----------------------------------------------------------------------------------------------+ * * - * See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for + * See also the *::_setupInlineCrypt()'s for * productive inline $cipher_code's how they works. * * Structure of: @@ -2170,12 +1612,12 @@ function _setupInlineCrypt() * ); * * - * @see self::_setupInlineCrypt() - * @see self::encrypt() - * @see self::decrypt() - * @param array $cipher_code + * @see Base::_setupInlineCrypt() + * @see Base::encrypt() + * @see Base::decrypt() + * @param Array $cipher_code * @access private - * @return string (the name of the created callback function) + * @return String (the name of the created callback function) */ function _createInlineCryptFunction($cipher_code) { @@ -2193,9 +1635,10 @@ function _createInlineCryptFunction($cipher_code) // merged with the $cipher_code algorithm // for encrypt- and decryption. switch ($this->mode) { - case self::MODE_ECB: + case CRYPT_MODE_ECB: $encrypt = $init_encrypt . ' $_ciphertext = ""; + $_text = $self->_pad($_text); $_plaintext_len = strlen($_text); for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { @@ -2221,30 +1664,29 @@ function _createInlineCryptFunction($cipher_code) return $self->_unpad($_plaintext); '; break; - case self::MODE_CTR: + case CRYPT_MODE_CTR: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $_xor = $self->encryptIV; $_buffer = &$self->enbuffer; - if (strlen($_buffer["ciphertext"])) { + + if (strlen($_buffer["encrypted"])) { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); - if (strlen($_block) > strlen($_buffer["ciphertext"])) { - $in = $_xor; + if (strlen($_block) > strlen($_buffer["encrypted"])) { + $in = $self->_generateXor($_xor, '.$block_size.'); '.$encrypt_block.' - $self->_increment_str($_xor); - $_buffer["ciphertext"].= $in; + $_buffer["encrypted"].= $in; } - $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); + $_key = $self->_stringShift($_buffer["encrypted"], '.$block_size.'); $_ciphertext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); - $in = $_xor; + $in = $self->_generateXor($_xor, '.$block_size.'); '.$encrypt_block.' - $self->_increment_str($_xor); $_key = $in; $_ciphertext.= $_block ^ $_key; } @@ -2252,7 +1694,7 @@ function _createInlineCryptFunction($cipher_code) if ($self->continuousBuffer) { $self->encryptIV = $_xor; if ($_start = $_plaintext_len % '.$block_size.') { - $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; + $_buffer["encrypted"] = substr($_key, $_start) . $_buffer["encrypted"]; } } @@ -2269,20 +1711,18 @@ function _createInlineCryptFunction($cipher_code) for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); if (strlen($_block) > strlen($_buffer["ciphertext"])) { - $in = $_xor; + $in = $self->_generateXor($_xor, '.$block_size.'); '.$encrypt_block.' - $self->_increment_str($_xor); $_buffer["ciphertext"].= $in; } - $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); + $_key = $self->_stringShift($_buffer["ciphertext"], '.$block_size.'); $_plaintext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); - $in = $_xor; + $in = $self->_generateXor($_xor, '.$block_size.'); '.$encrypt_block.' - $self->_increment_str($_xor); $_key = $in; $_plaintext.= $_block ^ $_key; } @@ -2297,7 +1737,7 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - case self::MODE_CFB: + case CRYPT_MODE_CFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_buffer = &$self->enbuffer; @@ -2396,7 +1836,7 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - case self::MODE_OFB: + case CRYPT_MODE_OFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); @@ -2412,7 +1852,7 @@ function _createInlineCryptFunction($cipher_code) $_xor = $in; $_buffer["xor"].= $_xor; } - $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); + $_key = $self->_stringShift($_buffer["xor"], '.$block_size.'); $_ciphertext.= $_block ^ $_key; } } else { @@ -2448,7 +1888,7 @@ function _createInlineCryptFunction($cipher_code) $_xor = $in; $_buffer["xor"].= $_xor; } - $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); + $_key = $self->_stringShift($_buffer["xor"], '.$block_size.'); $_plaintext.= $_block ^ $_key; } } else { @@ -2469,7 +1909,7 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - case self::MODE_STREAM: + case CRYPT_MODE_STREAM: $encrypt = $init_encrypt . ' $_ciphertext = ""; '.$encrypt_block.' @@ -2481,10 +1921,11 @@ function _createInlineCryptFunction($cipher_code) return $_plaintext; '; break; - // case self::MODE_CBC: + // case CRYPT_MODE_CBC: default: $encrypt = $init_encrypt . ' $_ciphertext = ""; + $_text = $self->_pad($_text); $_plaintext_len = strlen($_text); $in = $self->encryptIV; @@ -2542,46 +1983,11 @@ function _createInlineCryptFunction($cipher_code) * for which $mode the lambda function was created. * * @access private - * @return array &$functions + * @return &Array */ function &_getLambdaFunctions() { static $functions = array(); return $functions; } - - /** - * Generates a digest from $bytes - * - * @see self::_setupInlineCrypt() - * @access private - * @param $bytes - * @return string - */ - function _hashInlineCryptFunction($bytes) - { - if (!isset(self::$WHIRLPOOL_AVAILABLE)) { - self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos()); - } - - $result = ''; - $hash = $bytes; - - switch (true) { - case self::$WHIRLPOOL_AVAILABLE: - foreach (str_split($bytes, 64) as $t) { - $hash = hash('whirlpool', $hash, true); - $result .= $t ^ $hash; - } - return $result . hash('whirlpool', $hash, true); - default: - $len = strlen($bytes); - for ($i = 0; $i < $len; $i+=20) { - $t = substr($bytes, $i, 20); - $hash = sha1($hash, true); - $result .= $t ^ $hash; - } - return $result . sha1($hash, true); - } - } } diff --git a/src/phpseclib/Crypt/Blowfish.php b/src/phpseclib/Crypt/Blowfish.php deleted file mode 100755 index 3500df59..00000000 --- a/src/phpseclib/Crypt/Blowfish.php +++ /dev/null @@ -1,588 +0,0 @@ - - * setKey('12345678901234567890123456789012'); - * - * $plaintext = str_repeat('a', 1024); - * - * echo $blowfish->decrypt($blowfish->encrypt($plaintext)); - * ?> - * - * - * @category Crypt - * @package Blowfish - * @author Jim Wigginton - * @author Hans-Juergen Petrich - * @copyright 2007 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt; - -/** - * Pure-PHP implementation of Blowfish. - * - * @package Blowfish - * @author Jim Wigginton - * @author Hans-Juergen Petrich - * @access public - */ -class Blowfish extends Base -{ - /** - * Block Length of the cipher - * - * @see \phpseclib\Crypt\Base::block_size - * @var int - * @access private - */ - var $block_size = 8; - - /** - * The mcrypt specific name of the cipher - * - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @var string - * @access private - */ - var $cipher_name_mcrypt = 'blowfish'; - - /** - * Optimizing value while CFB-encrypting - * - * @see \phpseclib\Crypt\Base::cfb_init_len - * @var int - * @access private - */ - var $cfb_init_len = 500; - - /** - * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each - * - * S-Box 0 - * - * @access private - * @var array - */ - var $sbox0 = array( - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a - ); - - /** - * S-Box 1 - * - * @access private - * @var array - */ - var $sbox1 = array( - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 - ); - - /** - * S-Box 2 - * - * @access private - * @var array - */ - var $sbox2 = array( - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 - ); - - /** - * S-Box 3 - * - * @access private - * @var array - */ - var $sbox3 = array( - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 - ); - - /** - * P-Array consists of 18 32-bit subkeys - * - * @var array - * @access private - */ - var $parray = array( - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, - 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b - ); - - /** - * The BCTX-working Array - * - * Holds the expanded key [p] and the key-depended s-boxes [sb] - * - * @var array - * @access private - */ - var $bctx; - - /** - * Holds the last used key - * - * @var array - * @access private - */ - var $kl; - - /** - * The Key Length (in bytes) - * - * @see \phpseclib\Crypt\Base::setKeyLength() - * @var int - * @access private - * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk - * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could - * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu - * of that, we'll just precompute it once. - */ - var $key_length = 16; - - /** - * Default Constructor. - * - * @param int $mode - * @access public - * @throws \InvalidArgumentException if an invalid / unsupported mode is provided - */ - function __construct($mode) - { - if ($mode == self::MODE_STREAM) { - throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); - } - - parent::__construct($mode); - } - - /** - * Sets the key length. - * - * Key lengths can be between 32 and 448 bits. - * - * @access public - * @param int $length - */ - function setKeyLength($length) - { - if ($length < 32 || $length > 448) { - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported'); - } - - $this->key_length = $length >> 3; - - parent::setKeyLength($length); - } - - /** - * Test for engine validity - * - * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() - * - * @see \phpseclib\Crypt\Base::isValidEngine() - * @param int $engine - * @access public - * @return bool - */ - function isValidEngine($engine) - { - if ($engine == self::ENGINE_OPENSSL) { - if ($this->key_length != 16) { - return false; - } - $this->cipher_name_openssl_ecb = 'bf-ecb'; - $this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode(); - } - - return parent::isValidEngine($engine); - } - - /** - * Setup the key (expansion) - * - * @see \phpseclib\Crypt\Base::_setupKey() - * @access private - */ - function _setupKey() - { - if (isset($this->kl['key']) && $this->key === $this->kl['key']) { - // already expanded - return; - } - $this->kl = array('key' => $this->key); - - /* key-expanding p[] and S-Box building sb[] */ - $this->bctx = array( - 'p' => array(), - 'sb' => array( - $this->sbox0, - $this->sbox1, - $this->sbox2, - $this->sbox3 - ) - ); - - // unpack binary string in unsigned chars - $key = array_values(unpack('C*', $this->key)); - $keyl = count($key); - for ($j = 0, $i = 0; $i < 18; ++$i) { - // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ... - for ($data = 0, $k = 0; $k < 4; ++$k) { - $data = ($data << 8) | $key[$j]; - if (++$j >= $keyl) { - $j = 0; - } - } - $this->bctx['p'][] = $this->parray[$i] ^ $data; - } - - // encrypt the zero-string, replace P1 and P2 with the encrypted data, - // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys - $data = "\0\0\0\0\0\0\0\0"; - for ($i = 0; $i < 18; $i += 2) { - list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); - $this->bctx['p'][$i ] = $l; - $this->bctx['p'][$i + 1] = $r; - } - for ($i = 0; $i < 4; ++$i) { - for ($j = 0; $j < 256; $j += 2) { - list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); - $this->bctx['sb'][$i][$j ] = $l; - $this->bctx['sb'][$i][$j + 1] = $r; - } - } - } - - /** - * Encrypts a block - * - * @access private - * @param string $in - * @return string - */ - function _encryptBlock($in) - { - $p = $this->bctx["p"]; - // extract($this->bctx["sb"], EXTR_PREFIX_ALL, "sb"); // slower - $sb_0 = $this->bctx["sb"][0]; - $sb_1 = $this->bctx["sb"][1]; - $sb_2 = $this->bctx["sb"][2]; - $sb_3 = $this->bctx["sb"][3]; - - $in = unpack("N*", $in); - $l = $in[1]; - $r = $in[2]; - - for ($i = 0; $i < 16; $i+= 2) { - $l^= $p[$i]; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ - $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; - - $r^= $p[$i + 1]; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ - $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; - } - return pack("N*", $r ^ $p[17], $l ^ $p[16]); - } - - /** - * Decrypts a block - * - * @access private - * @param string $in - * @return string - */ - function _decryptBlock($in) - { - $p = $this->bctx["p"]; - $sb_0 = $this->bctx["sb"][0]; - $sb_1 = $this->bctx["sb"][1]; - $sb_2 = $this->bctx["sb"][2]; - $sb_3 = $this->bctx["sb"][3]; - - $in = unpack("N*", $in); - $l = $in[1]; - $r = $in[2]; - - for ($i = 17; $i > 2; $i-= 2) { - $l^= $p[$i]; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ - $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; - - $r^= $p[$i - 1]; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ - $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; - } - return pack("N*", $r ^ $p[0], $l ^ $p[1]); - } - - /** - * Setup the performance-optimized function for de/encrypt() - * - * @see \phpseclib\Crypt\Base::_setupInlineCrypt() - * @access private - */ - function _setupInlineCrypt() - { - $lambda_functions =& self::_getLambdaFunctions(); - - // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. - // (Currently, for Blowfish, one generated $lambda_function cost on php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit) - // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. - $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); - - // Generation of a unique hash for our generated code - $code_hash = "Crypt_Blowfish, {$this->mode}"; - if ($gen_hi_opt_code) { - $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); - } - - if (!isset($lambda_functions[$code_hash])) { - switch (true) { - case $gen_hi_opt_code: - $p = $this->bctx['p']; - $init_crypt = ' - static $sb_0, $sb_1, $sb_2, $sb_3; - if (!$sb_0) { - $sb_0 = $self->bctx["sb"][0]; - $sb_1 = $self->bctx["sb"][1]; - $sb_2 = $self->bctx["sb"][2]; - $sb_3 = $self->bctx["sb"][3]; - } - '; - break; - default: - $p = array(); - for ($i = 0; $i < 18; ++$i) { - $p[] = '$p_' . $i; - } - $init_crypt = ' - list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"]; - list(' . implode(',', $p) . ') = $self->bctx["p"]; - - '; - } - - // Generating encrypt code: - $encrypt_block = ' - $in = unpack("N*", $in); - $l = $in[1]; - $r = $in[2]; - '; - for ($i = 0; $i < 16; $i+= 2) { - $encrypt_block.= ' - $l^= ' . $p[$i] . '; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ - $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; - - $r^= ' . $p[$i + 1] . '; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ - $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; - '; - } - $encrypt_block.= ' - $in = pack("N*", - $r ^ ' . $p[17] . ', - $l ^ ' . $p[16] . ' - ); - '; - - // Generating decrypt code: - $decrypt_block = ' - $in = unpack("N*", $in); - $l = $in[1]; - $r = $in[2]; - '; - - for ($i = 17; $i > 2; $i-= 2) { - $decrypt_block.= ' - $l^= ' . $p[$i] . '; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ - $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; - - $r^= ' . $p[$i - 1] . '; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ - $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; - '; - } - - $decrypt_block.= ' - $in = pack("N*", - $r ^ ' . $p[0] . ', - $l ^ ' . $p[1] . ' - ); - '; - - $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( - array( - 'init_crypt' => $init_crypt, - 'init_encrypt' => '', - 'init_decrypt' => '', - 'encrypt_block' => $encrypt_block, - 'decrypt_block' => $decrypt_block - ) - ); - } - $this->inline_crypt = $lambda_functions[$code_hash]; - } -} diff --git a/src/phpseclib/Crypt/DES.php b/src/phpseclib/Crypt/DES.php index 14273d28..934632dc 100755 --- a/src/phpseclib/Crypt/DES.php +++ b/src/phpseclib/Crypt/DES.php @@ -5,7 +5,7 @@ * * Uses mcrypt, if available, and an internal implementation, otherwise. * - * PHP version 5 + * PHP versions 4 and 5 * * Useful resources are as follows: * @@ -16,9 +16,9 @@ * Here's a short example of how to use this library: * * setKey('abcdefgh'); * @@ -32,87 +32,164 @@ * ?> * * + * LICENSE: 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. + * * @category Crypt * @package DES * @author Jim Wigginton - * @copyright 2007 Jim Wigginton + * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/**#@+ + * @access private + * @see DES::_setupKey() + * @see DES::_processBlock() + */ +/** + * Contains $keys[CRYPT_DES_ENCRYPT] + */ +@define('CRYPT_DES_ENCRYPT', 0); +/** + * Contains $keys[CRYPT_DES_DECRYPT] + */ +@define('CRYPT_DES_DECRYPT', 1); +/**#@-*/ + +/**#@+ + * @access public + * @see DES::encrypt() + * @see DES::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +@define('CRYPT_DES_MODE_CTR', CRYPT_MODE_CTR); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +@define('CRYPT_DES_MODE_ECB', CRYPT_MODE_ECB); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +@define('CRYPT_DES_MODE_CBC', CRYPT_MODE_CBC); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +@define('CRYPT_DES_MODE_CFB', CRYPT_MODE_CFB); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +@define('CRYPT_DES_MODE_OFB', CRYPT_MODE_OFB); +/**#@-*/ + +/**#@+ + * @access private + * @see DES::DES() + */ +/** + * Toggles the internal implementation + */ +@define('CRYPT_DES_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +@define('CRYPT_DES_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + /** * Pure-PHP implementation of DES. * * @package DES * @author Jim Wigginton + * @version 0.1.0 * @access public */ class DES extends Base { - /**#@+ - * @access private - * @see \phpseclib\Crypt\DES::_setupKey() - * @see \phpseclib\Crypt\DES::_processBlock() - */ /** - * Contains $keys[self::ENCRYPT] - */ - const ENCRYPT = 0; - /** - * Contains $keys[self::DECRYPT] + * Block Length of the cipher + * + * @see Base::block_size + * @var Integer + * @access private */ - const DECRYPT = 1; - /**#@-*/ + var $block_size = 8; /** - * Block Length of the cipher + * The Key * - * @see \phpseclib\Crypt\Base::block_size - * @var int + * @see Base::key + * @see setKey() + * @var String * @access private */ - var $block_size = 8; + var $key = "\0\0\0\0\0\0\0\0"; /** - * Key Length (in bytes) + * The default password key_size used by setPassword() * - * @see \phpseclib\Crypt\Base::setKeyLength() - * @var int + * @see Base::password_key_size + * @see Base::setPassword() + * @var Integer * @access private */ - var $key_length = 8; + var $password_key_size = 8; /** - * The mcrypt specific name of the cipher + * The namespace used by the cipher for its constants. * - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @var string + * @see Base::const_namespace + * @var String * @access private */ - var $cipher_name_mcrypt = 'des'; + var $const_namespace = 'DES'; /** - * The OpenSSL names of the cipher / modes + * The mcrypt specific name of the cipher * - * @see \phpseclib\Crypt\Base::openssl_mode_names - * @var array + * @see Base::cipher_name_mcrypt + * @var String * @access private */ - var $openssl_mode_names = array( - self::MODE_ECB => 'des-ecb', - self::MODE_CBC => 'des-cbc', - self::MODE_CFB => 'des-cfb', - self::MODE_OFB => 'des-ofb' - // self::MODE_CTR is undefined for DES - ); + var $cipher_name_mcrypt = 'des'; /** * Optimizing value while CFB-encrypting * - * @see \phpseclib\Crypt\Base::cfb_init_len - * @var int + * @see Base::cfb_init_len + * @var Integer * @access private */ var $cfb_init_len = 500; @@ -120,11 +197,11 @@ class DES extends Base /** * Switch for DES/3DES encryption * - * Used only if $engine == self::ENGINE_INTERNAL + * Used only if $engine == CRYPT_DES_MODE_INTERNAL * - * @see self::_setupKey() - * @see self::_processBlock() - * @var int + * @see DES::_setupKey() + * @see DES::_processBlock() + * @var Integer * @access private */ var $des_rounds = 1; @@ -132,17 +209,17 @@ class DES extends Base /** * max possible size of $key * - * @see self::setKey() - * @var string + * @see DES::setKey() + * @var String * @access private */ - var $key_length_max = 8; + var $key_size_max = 8; /** * The Key Schedule * - * @see self::_setupKey() - * @var array + * @see DES::_setupKey() + * @var Array * @access private */ var $keys; @@ -154,9 +231,9 @@ class DES extends Base * with each byte containing all bits in the same state as the * corresponding bit in the index value. * - * @see self::_processBlock() - * @see self::_setupKey() - * @var array + * @see DES::_processBlock() + * @see DES::_setupKey() + * @var Array * @access private */ var $shuffle = array( @@ -295,7 +372,7 @@ class DES extends Base * * Indexing this table with each source byte performs the initial bit permutation. * - * @var array + * @var Array * @access private */ var $ipmap = array( @@ -337,7 +414,7 @@ class DES extends Base * Inverse IP mapping helper table. * Indexing this table with a byte value reverses the bit order. * - * @var array + * @var Array * @access private */ var $invipmap = array( @@ -381,7 +458,7 @@ class DES extends Base * Each box ($sbox1-$sbox8) has been vectorized, then each value pre-permuted using the * P table: concatenation can then be replaced by exclusive ORs. * - * @var array + * @var Array * @access private */ var $sbox1 = array( @@ -406,7 +483,7 @@ class DES extends Base /** * Pre-permuted S-box2 * - * @var array + * @var Array * @access private */ var $sbox2 = array( @@ -431,7 +508,7 @@ class DES extends Base /** * Pre-permuted S-box3 * - * @var array + * @var Array * @access private */ var $sbox3 = array( @@ -456,7 +533,7 @@ class DES extends Base /** * Pre-permuted S-box4 * - * @var array + * @var Array * @access private */ var $sbox4 = array( @@ -481,7 +558,7 @@ class DES extends Base /** * Pre-permuted S-box5 * - * @var array + * @var Array * @access private */ var $sbox5 = array( @@ -506,7 +583,7 @@ class DES extends Base /** * Pre-permuted S-box6 * - * @var array + * @var Array * @access private */ var $sbox6 = array( @@ -531,7 +608,7 @@ class DES extends Base /** * Pre-permuted S-box7 * - * @var array + * @var Array * @access private */ var $sbox7 = array( @@ -556,7 +633,7 @@ class DES extends Base /** * Pre-permuted S-box8 * - * @var array + * @var Array * @access private */ var $sbox8 = array( @@ -581,56 +658,52 @@ class DES extends Base /** * Default Constructor. * - * @param int $mode - * @access public - * @throws \InvalidArgumentException if an invalid / unsupported mode is provided - */ - function __construct($mode) - { - if ($mode == self::MODE_STREAM) { - throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); - } - - parent::__construct($mode); - } - - /** - * Test for engine validity + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_DES_MODE_ECB + * + * - CRYPT_DES_MODE_CBC + * + * - CRYPT_DES_MODE_CTR + * + * - CRYPT_DES_MODE_CFB * - * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * - CRYPT_DES_MODE_OFB * - * @see \phpseclib\Crypt\Base::isValidEngine() - * @param int $engine + * If not explictly set, CRYPT_DES_MODE_CBC will be used. + * + * @see Base::Base() + * @param optional Integer $mode * @access public - * @return bool */ - function isValidEngine($engine) + function DES($mode = CRYPT_DES_MODE_CBC) { - if ($this->key_length_max == 8) { - if ($engine == self::ENGINE_OPENSSL) { - $this->cipher_name_openssl_ecb = 'des-ecb'; - $this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode(); - } - } - - return parent::isValidEngine($engine); + parent::Base($mode); } /** * Sets the key. * - * Keys must be 64-bits long or 8 bytes long. + * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we + * only use the first eight, if $key has more then eight characters in it, and pad $key with the + * null byte if it is less then eight characters long. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * - * @see \phpseclib\Crypt\Base::setKey() + * If the key is not explicitly set, it'll be assumed to be all zero's. + * + * @see Base::setKey() * @access public - * @param string $key + * @param String $key */ function setKey($key) { - if (!($this instanceof TripleDES) && strlen($key) != 8) { - throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported'); + // We check/cut here only up to max length of the key. + // Key padding to the proper length will be done in _setupKey() + if (strlen($key) > $this->key_size_max) { + $key = substr($key, 0, $this->key_size_max); } // Sets the key @@ -640,46 +713,46 @@ function setKey($key) /** * Encrypts a block * - * @see \phpseclib\Crypt\Base::_encryptBlock() - * @see \phpseclib\Crypt\Base::encrypt() - * @see self::encrypt() + * @see Base::_encryptBlock() + * @see Base::encrypt() + * @see DES::encrypt() * @access private - * @param string $in - * @return string + * @param String $in + * @return String */ function _encryptBlock($in) { - return $this->_processBlock($in, self::ENCRYPT); + return $this->_processBlock($in, CRYPT_DES_ENCRYPT); } /** * Decrypts a block * - * @see \phpseclib\Crypt\Base::_decryptBlock() - * @see \phpseclib\Crypt\Base::decrypt() - * @see self::decrypt() + * @see Base::_decryptBlock() + * @see Base::decrypt() + * @see DES::decrypt() * @access private - * @param string $in - * @return string + * @param String $in + * @return String */ function _decryptBlock($in) { - return $this->_processBlock($in, self::DECRYPT); + return $this->_processBlock($in, CRYPT_DES_DECRYPT); } /** * Encrypts or decrypts a 64-bit block * - * $mode should be either self::ENCRYPT or self::DECRYPT. See + * $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See * {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general * idea of what this function does. * - * @see self::_encryptBlock() - * @see self::_decryptBlock() + * @see DES::_encryptBlock() + * @see DES::_decryptBlock() * @access private - * @param string $block - * @param int $mode - * @return string + * @param String $block + * @param Integer $mode + * @return String */ function _processBlock($block, $mode) { @@ -759,7 +832,7 @@ function _processBlock($block, $mode) /** * Creates the key schedule * - * @see \phpseclib\Crypt\Base::_setupKey() + * @see Base::_setupKey() * @access private */ function _setupKey() @@ -1240,8 +1313,8 @@ function _setupKey() $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F); $keys[$des_round] = array( - self::ENCRYPT => array(), - self::DECRYPT => array_fill(0, 32, 0) + CRYPT_DES_ENCRYPT => array(), + CRYPT_DES_DECRYPT => array_fill(0, 32, 0) ); for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) { $c <<= $shifts[$i]; @@ -1260,33 +1333,33 @@ function _setupKey() (($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF); $val2 = (($cp << 8) & 0xFF000000) | (($cp << 16) & 0x00FF0000) | (($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF); - $keys[$des_round][self::ENCRYPT][ ] = $val1; - $keys[$des_round][self::DECRYPT][$ki - 1] = $val1; - $keys[$des_round][self::ENCRYPT][ ] = $val2; - $keys[$des_round][self::DECRYPT][$ki ] = $val2; + $keys[$des_round][CRYPT_DES_ENCRYPT][ ] = $val1; + $keys[$des_round][CRYPT_DES_DECRYPT][$ki - 1] = $val1; + $keys[$des_round][CRYPT_DES_ENCRYPT][ ] = $val2; + $keys[$des_round][CRYPT_DES_DECRYPT][$ki ] = $val2; } } switch ($this->des_rounds) { case 3: // 3DES keys $this->keys = array( - self::ENCRYPT => array_merge( - $keys[0][self::ENCRYPT], - $keys[1][self::DECRYPT], - $keys[2][self::ENCRYPT] + CRYPT_DES_ENCRYPT => array_merge( + $keys[0][CRYPT_DES_ENCRYPT], + $keys[1][CRYPT_DES_DECRYPT], + $keys[2][CRYPT_DES_ENCRYPT] ), - self::DECRYPT => array_merge( - $keys[2][self::DECRYPT], - $keys[1][self::ENCRYPT], - $keys[0][self::DECRYPT] + CRYPT_DES_DECRYPT => array_merge( + $keys[2][CRYPT_DES_DECRYPT], + $keys[1][CRYPT_DES_ENCRYPT], + $keys[0][CRYPT_DES_DECRYPT] ) ); break; // case 1: // DES keys default: $this->keys = array( - self::ENCRYPT => $keys[0][self::ENCRYPT], - self::DECRYPT => $keys[0][self::DECRYPT] + CRYPT_DES_ENCRYPT => $keys[0][CRYPT_DES_ENCRYPT], + CRYPT_DES_DECRYPT => $keys[0][CRYPT_DES_DECRYPT] ); } } @@ -1294,12 +1367,12 @@ function _setupKey() /** * Setup the performance-optimized function for de/encrypt() * - * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @see Base::_setupInlineCrypt() * @access private */ function _setupInlineCrypt() { - $lambda_functions =& self::_getLambdaFunctions(); + $lambda_functions =& DES::_getLambdaFunctions(); // Engine configuration for: // - DES ($des_rounds == 1) or @@ -1307,20 +1380,21 @@ function _setupInlineCrypt() $des_rounds = $this->des_rounds; // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. - // (Currently, for DES, one generated $lambda_function cost on php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit) - // (Currently, for TripleDES, one generated $lambda_function cost on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit) // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); // Generation of a uniqe hash for our generated code - $code_hash = "Crypt_DES, $des_rounds, {$this->mode}"; - if ($gen_hi_opt_code) { - // For hi-optimized code, we create for each combination of - // $mode, $des_rounds and $this->key its own encrypt/decrypt function. - // After max 10 hi-optimized functions, we create generic - // (still very fast.. but not ultra) functions for each $mode/$des_rounds - // Currently 2 * 5 generic functions will be then max. possible. - $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + switch (true) { + case $gen_hi_opt_code: + // For hi-optimized code, we create for each combination of + // $mode, $des_rounds and $this->key its own encrypt/decrypt function. + $code_hash = md5(str_pad("DES, $des_rounds, {$this->mode}, ", 32, "\0") . $this->key); + break; + default: + // After max 10 hi-optimized functions, we create generic + // (still very fast.. but not ultra) functions for each $mode/$des_rounds + // Currently 2 * 5 generic functions will be then max. possible. + $code_hash = "DES, $des_rounds, {$this->mode}"; } // Is there a re-usable $lambda_functions in there? If not, we have to create it. @@ -1350,8 +1424,8 @@ function _setupInlineCrypt() // No futher initialisation of the $keys schedule is necessary. // That is the extra performance boost. $k = array( - self::ENCRYPT => $this->keys[self::ENCRYPT], - self::DECRYPT => $this->keys[self::DECRYPT] + CRYPT_DES_ENCRYPT => $this->keys[CRYPT_DES_ENCRYPT], + CRYPT_DES_DECRYPT => $this->keys[CRYPT_DES_DECRYPT] ); $init_encrypt = ''; $init_decrypt = ''; @@ -1360,21 +1434,22 @@ function _setupInlineCrypt() // In generic optimized code mode, we have to use, as the best compromise [currently], // our key schedule as $ke/$kd arrays. (with hardcoded indexes...) $k = array( - self::ENCRYPT => array(), - self::DECRYPT => array() + CRYPT_DES_ENCRYPT => array(), + CRYPT_DES_DECRYPT => array() ); - for ($i = 0, $c = count($this->keys[self::ENCRYPT]); $i < $c; ++$i) { - $k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; - $k[self::DECRYPT][$i] = '$kd[' . $i . ']'; + for ($i = 0, $c = count($this->keys[CRYPT_DES_ENCRYPT]); $i < $c; ++$i) { + $k[CRYPT_DES_ENCRYPT][$i] = '$ke[' . $i . ']'; + $k[CRYPT_DES_DECRYPT][$i] = '$kd[' . $i . ']'; } - $init_encrypt = '$ke = $self->keys[self::ENCRYPT];'; - $init_decrypt = '$kd = $self->keys[self::DECRYPT];'; + $init_encrypt = '$ke = $self->keys[CRYPT_DES_ENCRYPT];'; + $init_decrypt = '$kd = $self->keys[CRYPT_DES_DECRYPT];'; break; } // Creating code for en- and decryption. $crypt_block = array(); - foreach (array(self::ENCRYPT, self::DECRYPT) as $c) { + foreach (array(CRYPT_DES_ENCRYPT, CRYPT_DES_DECRYPT) as $c) { + /* Do the initial IP permutation. */ $crypt_block[$c] = ' $in = unpack("N*", $in); @@ -1441,8 +1516,8 @@ function _setupInlineCrypt() 'init_crypt' => $init_crypt, 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, - 'encrypt_block' => $crypt_block[self::ENCRYPT], - 'decrypt_block' => $crypt_block[self::DECRYPT] + 'encrypt_block' => $crypt_block[CRYPT_DES_ENCRYPT], + 'decrypt_block' => $crypt_block[CRYPT_DES_DECRYPT] ) ); } diff --git a/src/phpseclib/Crypt/Hash.php b/src/phpseclib/Crypt/Hash.php index be48a14a..c8879212 100755 --- a/src/phpseclib/Crypt/Hash.php +++ b/src/phpseclib/Crypt/Hash.php @@ -1,45 +1,87 @@ * setKey('abcdefg'); * - * echo base64_encode($hash->hash('abcdefg')); + * echo base64_encode($hash->_hash('abcdefg')); * ?> * * + * LICENSE: 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. + * * @category Crypt * @package Hash * @author Jim Wigginton - * @copyright 2015 Jim Wigginton - * @author Andreas Fischer - * @copyright 2015 Andreas Fischer + * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/**#@+ + * @access private + * @see Hash::Hash() + */ +/** + * Toggles the internal implementation + */ use phpseclib\Math\BigInteger; -use phpseclib\Exception\UnsupportedAlgorithmException; +@define('CRYPT_HASH_MODE_INTERNAL', 1); +/** + * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+. + */ +@define('CRYPT_HASH_MODE_MHASH', 2); /** + * Toggles the hash() implementation, which works on PHP 5.1.2+. + */ +@define('CRYPT_HASH_MODE_HASH', 3); +/**#@-*/ + +/** + * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. + * * @package Hash * @author Jim Wigginton - * @author Andreas Fischer + * @version 0.1.0 * @access public */ class Hash @@ -47,57 +89,53 @@ class Hash /** * Hash Parameter * - * @see self::setHash() - * @var int + * @see Hash::setHash() + * @var Integer * @access private */ var $hashParam; /** - * Byte-length of hash output (Internal HMAC) + * Byte-length of compression blocks / key (Internal HMAC) * - * @see self::setHash() - * @var int + * @see Hash::setAlgorithm() + * @var Integer * @access private */ - var $length; + var $b; /** - * Hash Algorithm + * Byte-length of hash output (Internal HMAC) * - * @see self::setHash() - * @var string + * @see Hash::setHash() + * @var Integer * @access private */ - var $hash; + var $l = false; /** - * Key + * Hash Algorithm * - * @see self::setKey() - * @var string + * @see Hash::setHash() + * @var String * @access private */ - var $key = false; + var $hash; /** - * Initial Hash - * - * Used only for sha512/* + * Key * - * @see self::_sha512() - * @var array + * @see Hash::setKey() + * @var String * @access private */ - var $initial = false; + var $key = false; /** * Outer XOR (Internal HMAC) * - * Used only for sha512/* - * - * @see self::hash() - * @var string + * @see Hash::setKey() + * @var String * @access private */ var $opad; @@ -105,10 +143,8 @@ class Hash /** * Inner XOR (Internal HMAC) * - * Used only for sha512/* - * - * @see self::hash() - * @var string + * @see Hash::setKey() + * @var String * @access private */ var $ipad; @@ -116,15 +152,26 @@ class Hash /** * Default Constructor. * - * @param string $hash + * @param optional String $hash + * @return Hash * @access public */ - function __construct($hash = 'sha256') + function Hash($hash = 'sha1') { - $this->setHash($hash); + if ( !defined('CRYPT_HASH_MODE') ) { + switch (true) { + case extension_loaded('hash'): + @define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH); + break; + case extension_loaded('mhash'): + @define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH); + break; + default: + @define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL); + } + } - $this->ipad = str_repeat(chr(0x36), 128); - $this->opad = str_repeat(chr(0x5C), 128); + $this->setHash($hash); } /** @@ -133,7 +180,7 @@ function __construct($hash = 'sha256') * Keys can be of any length. * * @access public - * @param string $key + * @param optional String $key */ function setKey($key = false) { @@ -146,7 +193,7 @@ function setKey($key = false) * As set by the constructor or by the setHash() method. * * @access public - * @return string + * @return String */ function getHash() { @@ -157,133 +204,407 @@ function getHash() * Sets the hash function. * * @access public - * @param string $hash + * @param String $hash */ function setHash($hash) { $this->hashParam = $hash = strtolower($hash); switch ($hash) { - case 'md2-96': case 'md5-96': case 'sha1-96': - case 'sha224-96': - case 'sha256-96': - case 'sha384-96': - case 'sha512-96': - case 'sha512/224-96': - case 'sha512/256-96': - $hash = substr($hash, 0, -3); - $this->length = 12; // 96 / 8 = 12 + $this->l = 12; // 96 / 8 = 12 break; case 'md2': case 'md5': - $this->length = 16; + $this->l = 16; break; case 'sha1': - $this->length = 20; - break; - case 'sha224': - case 'sha512/224': - $this->length = 28; + $this->l = 20; break; case 'sha256': - case 'sha512/256': - $this->length = 32; + $this->l = 32; break; case 'sha384': - $this->length = 48; + $this->l = 48; break; case 'sha512': - $this->length = 64; + $this->l = 64; + } + + switch ($hash) { + case 'md2': + $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ? + CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL; + break; + case 'sha384': + case 'sha512': + $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; break; default: - throw new UnsupportedAlgorithmException( - "$hash is not a supported algorithm" - ); + $mode = CRYPT_HASH_MODE; } - if ($hash == 'sha512/224' || $hash == 'sha512/256') { - // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24 - $this->initial = $hash == 'sha512/256' ? - array( - '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD', - '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2' - ) : - array( - '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF', - '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1' - ); - for ($i = 0; $i < 8; $i++) { - $this->initial[$i] = new BigInteger($this->initial[$i], 16); - $this->initial[$i]->setPrecision(64); - } + switch ( $mode ) { + case CRYPT_HASH_MODE_MHASH: + switch ($hash) { + case 'md5': + case 'md5-96': + $this->hash = MHASH_MD5; + break; + case 'sha256': + $this->hash = MHASH_SHA256; + break; + case 'sha1': + case 'sha1-96': + default: + $this->hash = MHASH_SHA1; + } + return; + case CRYPT_HASH_MODE_HASH: + switch ($hash) { + case 'md5': + case 'md5-96': + $this->hash = 'md5'; + return; + case 'md2': + case 'sha256': + case 'sha384': + case 'sha512': + $this->hash = $hash; + return; + case 'sha1': + case 'sha1-96': + default: + $this->hash = 'sha1'; + } + return; + } + + switch ($hash) { + case 'md2': + $this->b = 16; + $this->hash = array($this, '_md2'); + break; + case 'md5': + case 'md5-96': + $this->b = 64; + $this->hash = array($this, '_md5'); + break; + case 'sha256': + $this->b = 64; + $this->hash = array($this, '_sha256'); + break; + case 'sha384': + case 'sha512': + $this->b = 128; + $this->hash = array($this, '_sha512'); + break; + case 'sha1': + case 'sha1-96': + default: + $this->b = 64; + $this->hash = array($this, '_sha1'); } - $this->hash = $hash; + $this->ipad = str_repeat(chr(0x36), $this->b); + $this->opad = str_repeat(chr(0x5C), $this->b); } /** * Compute the HMAC. * * @access public - * @param string $text - * @return string + * @param String $text + * @return String */ - function hash($text) + function _hash($text) { - switch ($this->hash) { - case 'sha512/224': - case 'sha512/256': - if (empty($this->key) || !is_string($this->key)) { - return substr(self::_sha512($text, $this->initial), 0, $this->length); - } - /* "Applications that use keys longer than B bytes will first hash the key using H and then use the - resultant L byte string as the actual key to HMAC." - - -- http://tools.ietf.org/html/rfc2104#section-2 */ - $key = strlen($this->key) > $this->b ? self::_sha512($this->key, $this->initial) : $this->key; - - $key = str_pad($this->key, 128, chr(0)); // step 1 - $temp = $this->ipad ^ $this->key; // step 2 - $temp .= $text; // step 3 - $temp = self::_sha512($temp, $this->initial); // step 4 - $output = $this->opad ^ $this->key; // step 5 - $output.= $temp; // step 6 - $output = self::_sha512($output, $this->initial); // step 7 - - return substr($output, 0, $this->length); + $mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE; + + if (!empty($this->key) || is_string($this->key)) { + switch ( $mode ) { + case CRYPT_HASH_MODE_MHASH: + $output = mhash($this->hash, $text, $this->key); + break; + case CRYPT_HASH_MODE_HASH: + $output = hash_hmac($this->hash, $text, $this->key, true); + break; + case CRYPT_HASH_MODE_INTERNAL: + /* "Applications that use keys longer than B bytes will first hash the key using H and then use the + resultant L byte string as the actual key to HMAC." + + -- http://tools.ietf.org/html/rfc2104#section-2 */ + $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key; + + $key = str_pad($key, $this->b, chr(0)); // step 1 + $temp = $this->ipad ^ $key; // step 2 + $temp .= $text; // step 3 + $temp = call_user_func($this->hash, $temp); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = call_user_func($this->hash, $output); // step 7 + } + } else { + switch ( $mode ) { + case CRYPT_HASH_MODE_MHASH: + $output = mhash($this->hash, $text); + break; + case CRYPT_HASH_MODE_HASH: + $output = hash($this->hash, $text, true); + break; + case CRYPT_HASH_MODE_INTERNAL: + $output = call_user_func($this->hash, $text); + } } - $output = !empty($this->key) || is_string($this->key) ? - hash_hmac($this->hash, $text, $this->key, true) : - hash($this->hash, $text, true); - return strlen($output) > $this->length - ? substr($output, 0, $this->length) - : $output; + return substr($output, 0, $this->l); } /** * Returns the hash length (in bytes) * * @access public - * @return int + * @return Integer */ function getLength() { - return $this->length; + return $this->l; + } + + /** + * Wrapper for MD5 + * + * @access private + * @param String $m + */ + function _md5($m) + { + return pack('H*', md5($m)); + } + + /** + * Wrapper for SHA1 + * + * @access private + * @param String $m + */ + function _sha1($m) + { + return pack('H*', sha1($m)); + } + + /** + * Pure-PHP implementation of MD2 + * + * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. + * + * @access private + * @param String $m + */ + function _md2($m) + { + static $s = array( + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 + ); + + // Step 1. Append Padding Bytes + $pad = 16 - (strlen($m) & 0xF); + $m.= str_repeat(chr($pad), $pad); + + $length = strlen($m); + + // Step 2. Append Checksum + $c = str_repeat(chr(0), 16); + $l = chr(0); + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + // RFC1319 incorrectly states that C[j] should be set to S[c xor L] + //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); + // per , however, C[j] should be set to S[c xor L] xor C[j] + $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j])); + $l = $c[$j]; + } + } + $m.= $c; + + $length+= 16; + + // Step 3. Initialize MD Buffer + $x = str_repeat(chr(0), 48); + + // Step 4. Process Message in 16-Byte Blocks + for ($i = 0; $i < $length; $i+= 16) { + for ($j = 0; $j < 16; $j++) { + $x[$j + 16] = $m[$i + $j]; + $x[$j + 32] = $x[$j + 16] ^ $x[$j]; + } + $t = chr(0); + for ($j = 0; $j < 18; $j++) { + for ($k = 0; $k < 48; $k++) { + $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); + //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]); + } + $t = chr(ord($t) + $j); + } + } + + // Step 5. Output + return substr($x, 0, 16); + } + + /** + * Pure-PHP implementation of SHA256 + * + * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. + * + * @access private + * @param String $m + */ + function _sha256($m) + { + if (extension_loaded('suhosin')) { + return pack('H*', sha256($m)); + } + + // Initialize variables + $hash = array( + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + ); + // Initialize table of round constants + // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) + static $k = array( + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ); + + // Pre-processing + $length = strlen($m); + // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64 + $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); + $m[$length] = chr(0x80); + // we don't support hashing strings 512MB long + $m.= pack('N2', 0, $length << 3); + + // Process the message in successive 512-bit chunks + $chunks = str_split($m, 64); + foreach ($chunks as $chunk) { + $w = array(); + for ($i = 0; $i < 16; $i++) { + extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); + $w[] = $temp; + } + + // Extend the sixteen 32-bit words into sixty-four 32-bit words + for ($i = 16; $i < 64; $i++) { + $s0 = $this->_rightRotate($w[$i - 15], 7) ^ + $this->_rightRotate($w[$i - 15], 18) ^ + $this->_rightShift( $w[$i - 15], 3); + $s1 = $this->_rightRotate($w[$i - 2], 17) ^ + $this->_rightRotate($w[$i - 2], 19) ^ + $this->_rightShift( $w[$i - 2], 10); + $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); + + } + + // Initialize hash value for this chunk + list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; + + // Main loop + for ($i = 0; $i < 64; $i++) { + $s0 = $this->_rightRotate($a, 2) ^ + $this->_rightRotate($a, 13) ^ + $this->_rightRotate($a, 22); + $maj = ($a & $b) ^ + ($a & $c) ^ + ($b & $c); + $t2 = $this->_add($s0, $maj); + + $s1 = $this->_rightRotate($e, 6) ^ + $this->_rightRotate($e, 11) ^ + $this->_rightRotate($e, 25); + $ch = ($e & $f) ^ + ($this->_not($e) & $g); + $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); + + $h = $g; + $g = $f; + $f = $e; + $e = $this->_add($d, $t1); + $d = $c; + $c = $b; + $b = $a; + $a = $this->_add($t1, $t2); + } + + // Add this chunk's hash to result so far + $hash = array( + $this->_add($hash[0], $a), + $this->_add($hash[1], $b), + $this->_add($hash[2], $c), + $this->_add($hash[3], $d), + $this->_add($hash[4], $e), + $this->_add($hash[5], $f), + $this->_add($hash[6], $g), + $this->_add($hash[7], $h) + ); + } + + // Produce the final hash value (big-endian) + return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); } /** - * Pure-PHP implementation of SHA512 + * Pure-PHP implementation of SHA384 and SHA512 * * @access private - * @param string $m + * @param String $m */ - static function _sha512($m, $hash) + function _sha512($m) { - static $k; + + static $init384, $init512, $k; if (!isset($k)) { + // Initialize variables + $init384 = array( // initial values for SHA384 + 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', + '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' + ); + $init512 = array( // initial values for SHA512 + '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( @@ -314,6 +635,8 @@ static function _sha512($m, $hash) } } + $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 @@ -327,7 +650,7 @@ static function _sha512($m, $hash) foreach ($chunks as $chunk) { $w = array(); for ($i = 0; $i < 16; $i++) { - $temp = new BigInteger(self::_string_shift($chunk, 8), 256); + $temp = new BigInteger($this->_string_shift($chunk, 8), 256); $temp->setPrecision(64); $w[] = $temp; } @@ -348,21 +671,21 @@ static function _sha512($m, $hash) ); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); - $w[$i] = clone $w[$i - 16]; + $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 = clone $hash[0]; - $b = clone $hash[1]; - $c = clone $hash[2]; - $d = clone $hash[3]; - $e = clone $hash[4]; - $f = clone $hash[5]; - $g = clone $hash[6]; - $h = clone $hash[7]; + $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++) { @@ -399,13 +722,13 @@ static function _sha512($m, $hash) $t1 = $t1->add($k[$i]); $t1 = $t1->add($w[$i]); - $h = clone $g; - $g = clone $f; - $f = clone $e; + $h = $g->copy(); + $g = $f->copy(); + $f = $e->copy(); $e = $d->add($t1); - $d = clone $c; - $c = clone $b; - $b = clone $a; + $d = $c->copy(); + $c = $b->copy(); + $b = $a->copy(); $a = $t1->add($t2); } @@ -423,24 +746,98 @@ static function _sha512($m, $hash) } // 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) + // (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() . $hash[6]->toBytes() . $hash[7]->toBytes(); + $hash[4]->toBytes() . $hash[5]->toBytes(); + if ($this->l != 48) { + $temp.= $hash[6]->toBytes() . $hash[7]->toBytes(); + } return $temp; } + /** + * Right Rotate + * + * @access private + * @param Integer $int + * @param Integer $amt + * @see _sha256() + * @return Integer + */ + function _rightRotate($int, $amt) + { + $invamt = 32 - $amt; + $mask = (1 << $invamt) - 1; + return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); + } + + /** + * Right Shift + * + * @access private + * @param Integer $int + * @param Integer $amt + * @see _sha256() + * @return Integer + */ + function _rightShift($int, $amt) + { + $mask = (1 << (32 - $amt)) - 1; + return ($int >> $amt) & $mask; + } + + /** + * Not + * + * @access private + * @param Integer $int + * @see _sha256() + * @return Integer + */ + function _not($int) + { + return ~$int & 0xFFFFFFFF; + } + + /** + * Add + * + * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the + * possibility of overflow exists, care has to be taken. BigInteger() could be used but this should be faster. + * + * @param Integer $... + * @return Integer + * @see _sha256() + * @access private + */ + function _add() + { + static $mod; + if (!isset($mod)) { + $mod = pow(2, 32); + } + + $result = 0; + $arguments = func_get_args(); + foreach ($arguments as $argument) { + $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; + } + + return fmod($result, $mod); + } + /** * String Shift * * Inspired by array_shift * - * @param string $string - * @param int $index - * @return string + * @param String $string + * @param optional Integer $index + * @return String * @access private */ - static function _string_shift(&$string, $index = 1) + function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); diff --git a/src/phpseclib/Crypt/RC2.php b/src/phpseclib/Crypt/RC2.php deleted file mode 100755 index 648cf96a..00000000 --- a/src/phpseclib/Crypt/RC2.php +++ /dev/null @@ -1,704 +0,0 @@ - - * setKey('abcdefgh'); - * - * $plaintext = str_repeat('a', 1024); - * - * echo $rc2->decrypt($rc2->encrypt($plaintext)); - * ?> - * - * - * @category Crypt - * @package RC2 - * @author Patrick Monnerat - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt; - -/** - * Pure-PHP implementation of RC2. - * - * @package RC2 - * @access public - */ -class RC2 extends Base -{ - /** - * Block Length of the cipher - * - * @see \phpseclib\Crypt\Base::block_size - * @var int - * @access private - */ - var $block_size = 8; - - /** - * The Key - * - * @see \phpseclib\Crypt\Base::key - * @see self::setKey() - * @var string - * @access private - */ - var $key; - - /** - * The Original (unpadded) Key - * - * @see \phpseclib\Crypt\Base::key - * @see self::setKey() - * @see self::encrypt() - * @see self::decrypt() - * @var string - * @access private - */ - var $orig_key; - - /** - * Don't truncate / null pad key - * - * @see \phpseclib\Crypt\Base::_clearBuffers() - * @var bool - * @access private - */ - var $skip_key_adjustment = true; - - /** - * Key Length (in bytes) - * - * @see \phpseclib\Crypt\RC2::setKeyLength() - * @var int - * @access private - */ - var $key_length = 16; // = 128 bits - - /** - * The mcrypt specific name of the cipher - * - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @var string - * @access private - */ - var $cipher_name_mcrypt = 'rc2'; - - /** - * Optimizing value while CFB-encrypting - * - * @see \phpseclib\Crypt\Base::cfb_init_len - * @var int - * @access private - */ - var $cfb_init_len = 500; - - /** - * The key length in bits. - * - * @see self::setKeyLength() - * @see self::setKey() - * @var int - * @access private - * @internal Should be in range [1..1024]. - * @internal Changing this value after setting the key has no effect. - */ - var $default_key_length = 1024; - - /** - * The key length in bits. - * - * @see self::isValidEnine() - * @see self::setKey() - * @var int - * @access private - * @internal Should be in range [1..1024]. - */ - var $current_key_length; - - /** - * The Key Schedule - * - * @see self::_setupKey() - * @var array - * @access private - */ - var $keys; - - /** - * Key expansion randomization table. - * Twice the same 256-value sequence to save a modulus in key expansion. - * - * @see self::setKey() - * @var array - * @access private - */ - var $pitable = array( - 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, - 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, - 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, - 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, - 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, - 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, - 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, - 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, - 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, - 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, - 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, - 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, - 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, - 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, - 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, - 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, - 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, - 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, - 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, - 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, - 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, - 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, - 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, - 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, - 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, - 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, - 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, - 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, - 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, - 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, - 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, - 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD, - 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, - 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, - 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, - 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, - 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, - 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, - 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, - 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, - 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, - 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, - 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, - 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, - 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, - 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, - 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, - 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, - 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, - 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, - 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, - 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, - 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, - 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, - 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, - 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, - 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, - 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, - 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, - 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, - 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, - 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, - 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, - 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD - ); - - /** - * Inverse key expansion randomization table. - * - * @see self::setKey() - * @var array - * @access private - */ - var $invpitable = array( - 0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66, - 0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4, - 0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20, - 0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53, - 0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68, - 0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B, - 0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12, - 0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB, - 0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3, - 0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26, - 0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67, - 0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB, - 0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC, - 0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60, - 0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7, - 0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD, - 0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24, - 0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31, - 0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE, - 0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99, - 0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C, - 0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA, - 0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35, - 0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61, - 0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72, - 0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3, - 0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F, - 0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9, - 0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77, - 0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75, - 0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87, - 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 - ); - - /** - * Default Constructor. - * - * @param int $mode - * @access public - * @throws \InvalidArgumentException if an invalid / unsupported mode is provided - */ - function __construct($mode) - { - if ($mode == self::MODE_STREAM) { - throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); - } - - parent::__construct($mode); - } - - /** - * Test for engine validity - * - * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() - * - * @see \phpseclib\Crypt\Base::__construct() - * @param int $engine - * @access public - * @return bool - */ - function isValidEngine($engine) - { - switch ($engine) { - case self::ENGINE_OPENSSL: - if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) { - return false; - } - $this->cipher_name_openssl_ecb = 'rc2-ecb'; - $this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode(); - } - - return parent::isValidEngine($engine); - } - - /** - * Sets the key length. - * - * Valid key lengths are 8 to 1024. - * Calling this function after setting the key has no effect until the next - * \phpseclib\Crypt\RC2::setKey() call. - * - * @access public - * @param int $length in bits - * @throws \LengthException if the key length isn't supported - */ - function setKeyLength($length) - { - if ($length < 8 || $length > 1024) { - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); - } - - $this->default_key_length = $this->current_key_length = $length; - } - - /** - * Returns the current key length - * - * @access public - * @return int - */ - function getKeyLength() - { - return $this->current_key_length; - } - - /** - * Sets the key. - * - * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg. - * strlen($key) <= 128), however, we only use the first 128 bytes if $key - * has more then 128 bytes in it, and set $key to a single null byte if - * it is empty. - * - * If the key is not explicitly set, it'll be assumed to be a single - * null byte. - * - * @see \phpseclib\Crypt\Base::setKey() - * @access public - * @param string $key - * @param int $t1 optional Effective key length in bits. - * @throws \LengthException if the key length isn't supported - */ - function setKey($key, $t1 = false) - { - $this->orig_key = $key; - - if ($t1 === false) { - $t1 = $this->default_key_length; - } - - if ($t1 < 1 || $t1 > 1024) { - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); - } - - $this->current_key_length = $t1; - // Key byte count should be 1..128. - $key = strlen($key) ? substr($key, 0, 128) : "\x00"; - $t = strlen($key); - - // The mcrypt RC2 implementation only supports effective key length - // of 1024 bits. It is however possible to handle effective key - // lengths in range 1..1024 by expanding the key and applying - // inverse pitable mapping to the first byte before submitting it - // to mcrypt. - - // Key expansion. - $l = array_values(unpack('C*', $key)); - $t8 = ($t1 + 7) >> 3; - $tm = 0xFF >> (8 * $t8 - $t1); - - // Expand key. - $pitable = $this->pitable; - for ($i = $t; $i < 128; $i++) { - $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; - } - $i = 128 - $t8; - $l[$i] = $pitable[$l[$i] & $tm]; - while ($i--) { - $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; - } - - // Prepare the key for mcrypt. - $l[0] = $this->invpitable[$l[0]]; - array_unshift($l, 'C*'); - - parent::setKey(call_user_func_array('pack', $l)); - } - - /** - * Encrypts a message. - * - * Mostly a wrapper for \phpseclib\Crypt\Base::encrypt, with some additional OpenSSL handling code - * - * @see self::decrypt() - * @access public - * @param string $plaintext - * @return string $ciphertext - */ - function encrypt($plaintext) - { - if ($this->engine == self::ENGINE_OPENSSL) { - $temp = $this->key; - $this->key = $this->orig_key; - $result = parent::encrypt($plaintext); - $this->key = $temp; - return $result; - } - - return parent::encrypt($plaintext); - } - - /** - * Decrypts a message. - * - * Mostly a wrapper for \phpseclib\Crypt\Base::decrypt, with some additional OpenSSL handling code - * - * @see self::encrypt() - * @access public - * @param string $ciphertext - * @return string $plaintext - */ - function decrypt($ciphertext) - { - if ($this->engine == self::ENGINE_OPENSSL) { - $temp = $this->key; - $this->key = $this->orig_key; - $result = parent::decrypt($ciphertext); - $this->key = $temp; - return $result; - } - - return parent::decrypt($ciphertext); - } - - /** - * Encrypts a block - * - * @see \phpseclib\Crypt\Base::_encryptBlock() - * @see \phpseclib\Crypt\Base::encrypt() - * @access private - * @param string $in - * @return string - */ - function _encryptBlock($in) - { - list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); - $keys = $this->keys; - $limit = 20; - $actions = array($limit => 44, 44 => 64); - $j = 0; - - for (;;) { - // Mixing round. - $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; - $r0 |= $r0 >> 16; - $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; - $r1 |= $r1 >> 16; - $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; - $r2 |= $r2 >> 16; - $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; - $r3 |= $r3 >> 16; - - if ($j === $limit) { - if ($limit === 64) { - break; - } - - // Mashing round. - $r0 += $keys[$r3 & 0x3F]; - $r1 += $keys[$r0 & 0x3F]; - $r2 += $keys[$r1 & 0x3F]; - $r3 += $keys[$r2 & 0x3F]; - $limit = $actions[$limit]; - } - } - - return pack('vvvv', $r0, $r1, $r2, $r3); - } - - /** - * Decrypts a block - * - * @see \phpseclib\Crypt\Base::_decryptBlock() - * @see \phpseclib\Crypt\Base::decrypt() - * @access private - * @param string $in - * @return string - */ - function _decryptBlock($in) - { - list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); - $keys = $this->keys; - $limit = 44; - $actions = array($limit => 20, 20 => 0); - $j = 64; - - for (;;) { - // R-mixing round. - $r3 = ($r3 | ($r3 << 16)) >> 5; - $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; - $r2 = ($r2 | ($r2 << 16)) >> 3; - $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; - $r1 = ($r1 | ($r1 << 16)) >> 2; - $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; - $r0 = ($r0 | ($r0 << 16)) >> 1; - $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF; - - if ($j === $limit) { - if ($limit === 0) { - break; - } - - // R-mashing round. - $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; - $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; - $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; - $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF; - $limit = $actions[$limit]; - } - } - - return pack('vvvv', $r0, $r1, $r2, $r3); - } - - /** - * Setup the \phpseclib\Crypt\Base::ENGINE_MCRYPT $engine - * - * @see \phpseclib\Crypt\Base::_setupMcrypt() - * @access private - */ - function _setupMcrypt() - { - if (!isset($this->key)) { - $this->setKey(''); - } - - parent::_setupMcrypt(); - } - - /** - * Creates the key schedule - * - * @see \phpseclib\Crypt\Base::_setupKey() - * @access private - */ - function _setupKey() - { - if (!isset($this->key)) { - $this->setKey(''); - } - - // Key has already been expanded in \phpseclib\Crypt\RC2::setKey(): - // Only the first value must be altered. - $l = unpack('Ca/Cb/v*', $this->key); - array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8)); - unset($l['a']); - unset($l['b']); - $this->keys = $l; - } - - /** - * Setup the performance-optimized function for de/encrypt() - * - * @see \phpseclib\Crypt\Base::_setupInlineCrypt() - * @access private - */ - function _setupInlineCrypt() - { - $lambda_functions =& self::_getLambdaFunctions(); - - // The first 10 generated $lambda_functions will use the $keys hardcoded as integers - // for the mixing rounds, for better inline crypt performance [~20% faster]. - // But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10. - // (Currently, for Crypt_RC2, one generated $lambda_function cost on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit) - $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); - - // Generation of a uniqe hash for our generated code - $code_hash = "Crypt_RC2, {$this->mode}"; - if ($gen_hi_opt_code) { - $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); - } - - // Is there a re-usable $lambda_functions in there? - // If not, we have to create it. - if (!isset($lambda_functions[$code_hash])) { - // Init code for both, encrypt and decrypt. - $init_crypt = '$keys = $self->keys;'; - - switch (true) { - case $gen_hi_opt_code: - $keys = $this->keys; - default: - $keys = array(); - foreach ($this->keys as $k => $v) { - $keys[$k] = '$keys[' . $k . ']'; - } - } - - // $in is the current 8 bytes block which has to be en/decrypt - $encrypt_block = $decrypt_block = ' - $in = unpack("v4", $in); - $r0 = $in[1]; - $r1 = $in[2]; - $r2 = $in[3]; - $r3 = $in[4]; - '; - - // Create code for encryption. - $limit = 20; - $actions = array($limit => 44, 44 => 64); - $j = 0; - - for (;;) { - // Mixing round. - $encrypt_block .= ' - $r0 = (($r0 + ' . $keys[$j++] . ' + - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; - $r0 |= $r0 >> 16; - $r1 = (($r1 + ' . $keys[$j++] . ' + - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; - $r1 |= $r1 >> 16; - $r2 = (($r2 + ' . $keys[$j++] . ' + - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; - $r2 |= $r2 >> 16; - $r3 = (($r3 + ' . $keys[$j++] . ' + - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; - $r3 |= $r3 >> 16;'; - - if ($j === $limit) { - if ($limit === 64) { - break; - } - - // Mashing round. - $encrypt_block .= ' - $r0 += $keys[$r3 & 0x3F]; - $r1 += $keys[$r0 & 0x3F]; - $r2 += $keys[$r1 & 0x3F]; - $r3 += $keys[$r2 & 0x3F];'; - $limit = $actions[$limit]; - } - } - - $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; - - // Create code for decryption. - $limit = 44; - $actions = array($limit => 20, 20 => 0); - $j = 64; - - for (;;) { - // R-mixing round. - $decrypt_block .= ' - $r3 = ($r3 | ($r3 << 16)) >> 5; - $r3 = ($r3 - ' . $keys[--$j] . ' - - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; - $r2 = ($r2 | ($r2 << 16)) >> 3; - $r2 = ($r2 - ' . $keys[--$j] . ' - - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; - $r1 = ($r1 | ($r1 << 16)) >> 2; - $r1 = ($r1 - ' . $keys[--$j] . ' - - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; - $r0 = ($r0 | ($r0 << 16)) >> 1; - $r0 = ($r0 - ' . $keys[--$j] . ' - - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; - - if ($j === $limit) { - if ($limit === 0) { - break; - } - - // R-mashing round. - $decrypt_block .= ' - $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; - $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; - $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; - $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; - $limit = $actions[$limit]; - } - } - - $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; - - // Creates the inline-crypt function - $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( - array( - 'init_crypt' => $init_crypt, - 'encrypt_block' => $encrypt_block, - 'decrypt_block' => $decrypt_block - ) - ); - } - - // Set the inline-crypt function as callback in: $this->inline_crypt - $this->inline_crypt = $lambda_functions[$code_hash]; - } -} diff --git a/src/phpseclib/Crypt/RC4.php b/src/phpseclib/Crypt/RC4.php index 3da70b6e..dadb2d48 100755 --- a/src/phpseclib/Crypt/RC4.php +++ b/src/phpseclib/Crypt/RC4.php @@ -5,7 +5,7 @@ * * Uses mcrypt, if available, and an internal implementation, otherwise. * - * PHP version 5 + * PHP versions 4 and 5 * * Useful resources are as follows: * @@ -18,9 +18,9 @@ * Here's a short example of how to use this library: * * setKey('abcdefgh'); * @@ -34,59 +34,102 @@ * ?> * * + * LICENSE: 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. + * * @category Crypt * @package RC4 * @author Jim Wigginton - * @copyright 2007 Jim Wigginton + * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/**#@+ + * @access private + * @see RC4::RC4() + */ +/** + * Toggles the internal implementation + */ +@define('CRYPT_RC4_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +@define('CRYPT_RC4_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + +/**#@+ + * @access private + * @see RC4::_crypt() + */ +@define('CRYPT_RC4_ENCRYPT', 0); +@define('CRYPT_RC4_DECRYPT', 1); +/**#@-*/ + /** * Pure-PHP implementation of RC4. * * @package RC4 * @author Jim Wigginton + * @version 0.1.0 * @access public */ class RC4 extends Base { - /**#@+ - * @access private - * @see \phpseclib\Crypt\RC4::_crypt() - */ - const ENCRYPT = 0; - const DECRYPT = 1; - /**#@-*/ - /** * Block Length of the cipher * * RC4 is a stream cipher * so we the block_size to 0 * - * @see \phpseclib\Crypt\Base::block_size - * @var int + * @see Base::block_size + * @var Integer * @access private */ var $block_size = 0; /** - * Key Length (in bytes) + * The default password key_size used by setPassword() * - * @see \phpseclib\Crypt\RC4::setKeyLength() - * @var int + * @see Base::password_key_size + * @see Base::setPassword() + * @var Integer * @access private */ - var $key_length = 128; // = 1024 bits + var $password_key_size = 128; // = 1024 bits + + /** + * The namespace used by the cipher for its constants. + * + * @see Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'RC4'; /** * The mcrypt specific name of the cipher * - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @var string + * @see Base::cipher_name_mcrypt + * @var String * @access private */ var $cipher_name_mcrypt = 'arcfour'; @@ -94,7 +137,7 @@ class RC4 extends Base /** * Holds whether performance-optimized $inline_crypt() can/should be used. * - * @see \phpseclib\Crypt\Base::inline_crypt + * @see Base::inline_crypt * @var mixed * @access private */ @@ -103,8 +146,8 @@ class RC4 extends Base /** * The Key * - * @see self::setKey() - * @var string + * @see RC4::setKey() + * @var String * @access private */ var $key = "\0"; @@ -112,8 +155,8 @@ class RC4 extends Base /** * The Key Stream for decryption and encryption * - * @see self::setKey() - * @var array + * @see RC4::setKey() + * @var Array * @access private */ var $stream; @@ -121,167 +164,107 @@ class RC4 extends Base /** * Default Constructor. * - * @see \phpseclib\Crypt\Base::__construct() - * @return \phpseclib\Crypt\RC4 - * @access public - */ - function __construct() - { - parent::__construct(Base::MODE_STREAM); - } - - /** - * Test for engine validity + * Determines whether or not the mcrypt extension should be used. * - * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() - * - * @see \phpseclib\Crypt\Base::__construct() - * @param int $engine + * @see Base::Base() + * @return RC4 * @access public - * @return bool */ - function isValidEngine($engine) + function RC4() { - switch ($engine) { - case Base::ENGINE_OPENSSL: - switch (strlen($this->key)) { - case 5: - $this->cipher_name_openssl = 'rc4-40'; - break; - case 8: - $this->cipher_name_openssl = 'rc4-64'; - break; - case 16: - $this->cipher_name_openssl = 'rc4'; - break; - default: - return false; - } - } - - return parent::isValidEngine($engine); + parent::Base(CRYPT_MODE_STREAM); } /** - * RC4 does not use an IV + * Dummy function. * - * @access public - * @return bool - */ - function usesIV() - { - return false; - } - - /** - * Sets the key length + * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1]. + * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before + * calling setKey(). + * + * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol, + * the IV's are relatively easy to predict, an attack described by + * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir} + * can be used to quickly guess at the rest of the key. The following links elaborate: * - * Keys can be between 1 and 256 bytes long. + * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009} + * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack} * + * @param String $iv + * @see RC4::setKey() * @access public - * @param int $length - * @throws \LengthException if the key length is invalid */ - function setKeyLength($length) + function setIV($iv) { - if ($length < 8 || $length > 2048) { - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); - } - - $this->key_length = $length >> 3; - - parent::setKeyLength($length); } /** - * Sets the key length + * Sets the key. * - * Keys can be between 1 and 256 bytes long. + * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will + * be used. If no key is explicitly set, it'll be assumed to be a single null byte. * * @access public - * @param int $length - * @throws \LengthException if the key length is invalid + * @see Base::setKey() + * @param String $key */ function setKey($key) { - $length = strlen($key); - if ($length < 1 || $length > 256) { - throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); - } - - parent::setKey($key); + parent::setKey(substr($key, 0, 256)); } /** * Encrypts a message. * - * @see \phpseclib\Crypt\Base::decrypt() - * @see self::_crypt() + * @see Base::decrypt() + * @see RC4::_crypt() * @access public - * @param string $plaintext - * @return string $ciphertext + * @param String $plaintext + * @return String $ciphertext */ function encrypt($plaintext) { - if ($this->engine != Base::ENGINE_INTERNAL) { + if ($this->engine == CRYPT_MODE_MCRYPT) { return parent::encrypt($plaintext); } - return $this->_crypt($plaintext, self::ENCRYPT); + return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT); } /** * Decrypts a message. * * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). - * At least if the continuous buffer is disabled. + * Atleast if the continuous buffer is disabled. * - * @see \phpseclib\Crypt\Base::encrypt() - * @see self::_crypt() + * @see Base::encrypt() + * @see RC4::_crypt() * @access public - * @param string $ciphertext - * @return string $plaintext + * @param String $ciphertext + * @return String $plaintext */ function decrypt($ciphertext) { - if ($this->engine != Base::ENGINE_INTERNAL) { + if ($this->engine == CRYPT_MODE_MCRYPT) { return parent::decrypt($ciphertext); } - return $this->_crypt($ciphertext, self::DECRYPT); + return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT); } - /** - * Encrypts a block - * - * @access private - * @param string $in - */ - function _encryptBlock($in) - { - // RC4 does not utilize this method - } - - /** - * Decrypts a block - * - * @access private - * @param string $in - */ - function _decryptBlock($in) - { - // RC4 does not utilize this method - } /** * Setup the key (expansion) * - * @see \phpseclib\Crypt\Base::_setupKey() + * @see Base::_setupKey() * @access private */ function _setupKey() { $key = $this->key; $keyLength = strlen($key); - $keyStream = range(0, 255); + $keyStream = array(); + for ($i = 0; $i < 256; $i++) { + $keyStream[$i] = $i; + } $j = 0; for ($i = 0; $i < 256; $i++) { $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; @@ -291,7 +274,7 @@ function _setupKey() } $this->stream = array(); - $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array( + $this->stream[CRYPT_RC4_DECRYPT] = $this->stream[CRYPT_RC4_ENCRYPT] = array( 0, // index $i 0, // index $j $keyStream @@ -301,12 +284,12 @@ function _setupKey() /** * Encrypts or decrypts a message. * - * @see self::encrypt() - * @see self::decrypt() + * @see RC4::encrypt() + * @see RC4::decrypt() * @access private - * @param string $text - * @param int $mode - * @return string $text + * @param String $text + * @param Integer $mode + * @return String $text */ function _crypt($text, $mode) { @@ -335,7 +318,7 @@ function _crypt($text, $mode) $keyStream[$i] = $ksj; $keyStream[$j] = $ksi; - $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); + $text[$k] = chr(ord($text[$k]) ^ $keyStream[($ksj + $ksi) & 255]); } return $text; diff --git a/src/phpseclib/Crypt/RSA.php b/src/phpseclib/Crypt/RSA.php index d34b6bae..d99b248b 100755 --- a/src/phpseclib/Crypt/RSA.php +++ b/src/phpseclib/Crypt/RSA.php @@ -3,198 +3,271 @@ /** * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA. * - * PHP version 5 + * PHP versions 4 and 5 * * Here's an example of how to encrypt and decrypt text with this library: * * createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $ciphertext = $publickey->encrypt($plaintext); + * $rsa->loadKey($privatekey); + * $ciphertext = $rsa->encrypt($plaintext); * - * echo $privatekey->decrypt($ciphertext); + * $rsa->loadKey($publickey); + * echo $rsa->decrypt($ciphertext); * ?> * * * Here's an example of how to create signatures and verify signatures with this library: * * createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $signature = $privatekey->sign($plaintext); + * $rsa->loadKey($privatekey); + * $signature = $rsa->sign($plaintext); * - * echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * $rsa->loadKey($publickey); + * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * * + * LICENSE: 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. + * * @category Crypt * @package RSA * @author Jim Wigginton - * @copyright 2009 Jim Wigginton + * @copyright MMIX Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; -use ParagonIE\ConstantTime\Base64; -use phpseclib\File\ASN1; +/**#@+ + * @access public + * @see RSA::encrypt() + * @see RSA::decrypt() + */ +/** + * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} + * (OAEP) for encryption / decryption. + * + * Uses sha1 by default. + * + * @see RSA::setHash() + * @see RSA::setMGFHash() + */ use phpseclib\Math\BigInteger; +@define('CRYPT_RSA_ENCRYPTION_OAEP', 1); +/** + * Use PKCS#1 padding. + * + * Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards + * compatability with protocols (like SSH-1) written before OAEP's introduction. + */ +@define('CRYPT_RSA_ENCRYPTION_PKCS1', 2); +/**#@-*/ + +/**#@+ + * @access public + * @see RSA::sign() + * @see RSA::verify() + * @see RSA::setHash() + */ +/** + * Use the Probabilistic Signature Scheme for signing + * + * Uses sha1 by default. + * + * @see RSA::setSaltLength() + * @see RSA::setMGFHash() + */ +@define('CRYPT_RSA_SIGNATURE_PSS', 1); +/** + * Use the PKCS#1 scheme by default. + * + * Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards + * compatability with protocols (like SSH-2) written before PSS's introduction. + */ +@define('CRYPT_RSA_SIGNATURE_PKCS1', 2); +/**#@-*/ + +/**#@+ + * @access private + * @see RSA::createKey() + */ +/** + * ASN1 Integer + */ +@define('CRYPT_RSA_ASN1_INTEGER', 2); +/** + * ASN1 Bit String + */ +@define('CRYPT_RSA_ASN1_BITSTRING', 3); +/** + * ASN1 Sequence (with the constucted bit set) + */ +@define('CRYPT_RSA_ASN1_SEQUENCE', 48); +/**#@-*/ + +/**#@+ + * @access private + * @see RSA::RSA() + */ +/** + * To use the pure-PHP implementation + */ +@define('CRYPT_RSA_MODE_INTERNAL', 1); +/** + * To use the OpenSSL library + * + * (if enabled; otherwise, the internal implementation will be used) + */ +@define('CRYPT_RSA_MODE_OPENSSL', 2); +/**#@-*/ + +/** + * Default openSSL configuration file. + */ +@define('CRYPT_RSA_OPENSSL_CONFIG', dirname(__FILE__) . '/../openssl.cnf'); + +/**#@+ + * @access public + * @see RSA::createKey() + * @see RSA::setPrivateKeyFormat() + */ +/** + * PKCS#1 formatted private key + * + * Used by OpenSSH + */ +@define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0); +/** + * PuTTY formatted private key + */ +@define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1); +/** + * XML formatted private key + */ +@define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2); +/**#@-*/ + +/**#@+ + * @access public + * @see RSA::createKey() + * @see RSA::setPublicKeyFormat() + */ +/** + * Raw public key + * + * An array containing two BigInteger objects. + * + * The exponent can be indexed with any of the following: + * + * 0, e, exponent, publicExponent + * + * The modulus can be indexed with any of the following: + * + * 1, n, modulo, modulus + */ +@define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3); +/** + * PKCS#1 formatted public key (raw) + * + * Used by File/X509.php + */ +@define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW', 4); +/** + * XML formatted public key + */ +@define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5); +/** + * OpenSSH formatted public key + * + * Place in $HOME/.ssh/authorized_keys + */ +@define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6); +/** + * PKCS#1 formatted public key (encapsulated) + * + * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) + */ +@define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 7); +/**#@-*/ + /** * Pure-PHP PKCS#1 compliant implementation of RSA. * * @package RSA * @author Jim Wigginton + * @version 0.1.0 * @access public */ class RSA { - /**#@+ - * @access public - * @see self::encrypt() - * @see self::decrypt() - */ - /** - * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} - * (OAEP) for encryption / decryption. - * - * Uses sha256 by default - * - * @see self::setHash() - * @see self::setMGFHash() - */ - const PADDING_OAEP = 1; - /** - * Use PKCS#1 padding. - * - * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards - * compatibility with protocols (like SSH-1) written before OAEP's introduction. - */ - const PADDING_PKCS1 = 2; - /** - * Do not use any padding - * - * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy - * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. - */ - const PADDING_NONE = 3; - /** - * Use PKCS#1 padding with PKCS1 v1.5 compatability - * - * A PKCS1 v2.1 encrypted message may not successfully decrypt with a PKCS1 v1.5 implementation (such as OpenSSL). - */ - const PADDING_PKCS15_COMPAT = 6; - /**#@-*/ - - /**#@+ - * @access public - * @see self::sign() - * @see self::verify() - * @see self::setHash() - */ - /** - * Use the Probabilistic Signature Scheme for signing - * - * Uses sha256 and 0 as the salt length - * - * @see self::setSaltLength() - * @see self::setMGFHash() - * @see self::setHash() - */ - const PADDING_PSS = 4; - /** - * Use a relaxed version of PKCS#1 padding for signature verification - */ - const PADDING_RELAXED_PKCS1 = 5; - /**#@-*/ - - /**#@+ - * @access private - * @see self::createKey() - */ - /** - * ASN1 Integer - */ - const ASN1_INTEGER = 2; - /** - * ASN1 Bit String - */ - const ASN1_BITSTRING = 3; - /** - * ASN1 Octet String - */ - const ASN1_OCTETSTRING = 4; - /** - * ASN1 Object Identifier - */ - const ASN1_OBJECT = 6; - /** - * ASN1 Sequence (with the constucted bit set) - */ - const ASN1_SEQUENCE = 48; - /**#@-*/ - - /**#@+ - * @access private - * @see self::__construct() - */ - /** - * To use the pure-PHP implementation - */ - const MODE_INTERNAL = 1; - /** - * To use the OpenSSL library - * - * (if enabled; otherwise, the internal implementation will be used) - */ - const MODE_OPENSSL = 2; - /**#@-*/ - /** * Precomputed Zero * - * @var array + * @var Array * @access private */ - static $zero; + var $zero; /** * Precomputed One * - * @var array + * @var Array * @access private */ - static $one; + var $one; /** * Private Key Format * - * @var string + * @var Integer * @access private */ - var $privateKeyFormat = 'PKCS1'; + var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1; /** * Public Key Format * - * @var string - * @access private + * @var Integer + * @access public */ - var $publicKeyFormat = 'PKCS8'; + var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1; /** * Modulus (ie. n) * - * @var \phpseclib\Math\BigInteger + * @var BigInteger * @access private */ var $modulus; @@ -202,7 +275,7 @@ class RSA /** * Modulus length * - * @var \phpseclib\Math\BigInteger + * @var BigInteger * @access private */ var $k; @@ -210,7 +283,7 @@ class RSA /** * Exponent (ie. e or d) * - * @var \phpseclib\Math\BigInteger + * @var BigInteger * @access private */ var $exponent; @@ -218,7 +291,7 @@ class RSA /** * Primes for Chinese Remainder Theorem (ie. p and q) * - * @var array + * @var Array * @access private */ var $primes; @@ -226,7 +299,7 @@ class RSA /** * Exponents for Chinese Remainder Theorem (ie. dP and dQ) * - * @var array + * @var Array * @access private */ var $exponents; @@ -234,7 +307,7 @@ class RSA /** * Coefficients for Chinese Remainder Theorem (ie. qInv) * - * @var array + * @var Array * @access private */ var $coefficients; @@ -242,7 +315,7 @@ class RSA /** * Hash name * - * @var string + * @var String * @access private */ var $hashName; @@ -250,7 +323,7 @@ class RSA /** * Hash function * - * @var \phpseclib\Crypt\Hash + * @var Hash * @access private */ var $hash; @@ -258,7 +331,7 @@ class RSA /** * Length of hash function output * - * @var int + * @var Integer * @access private */ var $hLen; @@ -266,7 +339,7 @@ class RSA /** * Length of salt * - * @var int + * @var Integer * @access private */ var $sLen; @@ -274,7 +347,7 @@ class RSA /** * Hash function for the Mask Generation Function * - * @var \phpseclib\Crypt\Hash + * @var Hash * @access private */ var $mgfHash; @@ -282,110 +355,149 @@ class RSA /** * Length of MGF hash function output * - * @var int + * @var Integer * @access private */ var $mgfHLen; /** - * Public Exponent + * Encryption mode * - * @var mixed + * @var Integer * @access private */ - var $publicExponent = false; + var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP; /** - * Password + * Signature mode * - * @var string + * @var Integer * @access private */ - var $password = false; + var $signatureMode = CRYPT_RSA_SIGNATURE_PSS; /** - * Loaded File Format + * Public Exponent * - * @var string + * @var Mixed * @access private */ - var $format = false; + var $publicExponent = false; /** - * OpenSSL configuration file name. - * - * Set to null to use system configuration file. + * Password * - * @see self::createKey() - * @var mixed - * @access public + * @var String + * @access private */ - static $configFile; + var $password = false; /** - * Supported file formats (lower case) + * Components * - * @see self::_initialize_static_variables() - * @var array + * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - + * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. + * + * @see RSA::_start_element_handler() + * @var Array * @access private */ - static $fileFormats = false; + var $components = array(); /** - * Supported file formats (original case) + * Current String + * + * For use with parsing XML formatted keys. * - * @see self::_initialize_static_variables() - * @var array + * @see RSA::_character_handler() + * @see RSA::_stop_element_handler() + * @var Mixed * @access private */ - static $origFileFormats = false; + var $current; + /** + * OpenSSL configuration file name. + * + * Set to null to use system configuration file. + * @see RSA::createKey() + * @var Mixed + * @Access public + */ + var $configFile; /** - * Initialize static variables + * Public key comment field. * + * @var String * @access private */ - static function _initialize_static_variables() - { - if (!isset(self::$zero)) { - self::$zero= new BigInteger(0); - self::$one = new BigInteger(1); - self::$configFile = __DIR__ . '/../openssl.cnf'; - - if (self::$fileFormats === false) { - self::$fileFormats = array(); - foreach (glob(__DIR__ . '/RSA/*.php') as $file) { - $name = pathinfo($file, PATHINFO_FILENAME); - $type = 'phpseclib\Crypt\RSA\\' . $name; - $meta = new \ReflectionClass($type); - if (!$meta->isAbstract()) { - self::$fileFormats[strtolower($name)] = $type; - self::$origFileFormats[] = $name; - } - } - } - } - } + var $comment = 'phpseclib-generated-key'; /** * The constructor * * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason - * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires + * RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late. * - * @return \phpseclib\Crypt\RSA + * @return RSA * @access public */ function __construct() { - self::_initialize_static_variables(); - $this->hash = new Hash('sha256'); + $this->configFile = CRYPT_RSA_OPENSSL_CONFIG; + + if ( !defined('CRYPT_RSA_MODE') ) { + // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, + // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger + // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. + if ( defined('MATH_BIGINTEGER_OPENSSL_DISABLE') ) { + @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); + } + + switch ( !defined('CRYPT_RSA_MODE') ) { // ie. only run this if the above didn't set CRYPT_RSA_MODE already + case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + ob_start(); + phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + $versions = array(); + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL); + break; + default: + @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); + @define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } + break; + case true: + @define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); + } + } + + $this->zero = new BigInteger(); + $this->one = new BigInteger(1); + + $this->hash = new Hash('sha1'); $this->hLen = $this->hash->getLength(); - $this->hashName = 'sha256'; - $this->mgfHash = new Hash('sha256'); + $this->hashName = 'sha1'; + $this->mgfHash = new Hash('sha1'); $this->mgfHLen = $this->mgfHash->getLength(); } @@ -396,65 +508,45 @@ function __construct() * - 'privatekey': The private key. * - 'publickey': The public key. * - 'partialkey': A partially computed key (if the execution time exceeded $timeout). - * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing. + * Will need to be passed back to RSA::createKey() as the third parameter for further processing. * * @access public - * @param int $bits - * @param int $timeout - * @param array $p + * @param optional Integer $bits + * @param optional Integer $timeout + * @param optional BigInteger $p */ - static function createKey($bits = 2048, $timeout = false, $partial = array()) + function createKey($bits = 1024, $timeout = false, $partial = array()) { - self::_initialize_static_variables(); - - if (!defined('CRYPT_RSA_MODE')) { - switch (true) { - // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, - // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger - // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. - case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): - define('CRYPT_RSA_MODE', self::MODE_INTERNAL); - break; - case extension_loaded('openssl') && file_exists(self::$configFile): - define('CRYPT_RSA_MODE', self::MODE_OPENSSL); - break; - default: - define('CRYPT_RSA_MODE', self::MODE_INTERNAL); - } - } - if (!defined('CRYPT_RSA_EXPONENT')) { // http://en.wikipedia.org/wiki/65537_%28number%29 - define('CRYPT_RSA_EXPONENT', '65537'); + @define('CRYPT_RSA_EXPONENT', '65537'); } // per , this number ought not result in primes smaller // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if - // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then + // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key // generation when there's a chance neither gmp nor OpenSSL are installed) if (!defined('CRYPT_RSA_SMALLEST_PRIME')) { - define('CRYPT_RSA_SMALLEST_PRIME', 4096); + @define('CRYPT_RSA_SMALLEST_PRIME', 4096); } // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum - if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { + if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { $config = array(); - if (isset(self::$configFile)) { - $config['config'] = self::$configFile; + if (isset($this->configFile)) { + $config['config'] = $this->configFile; } $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); - openssl_pkey_export($rsa, $privatekeystr, null, $config); - $privatekey = new RSA(); - $privatekey->load($privatekeystr); + openssl_pkey_export($rsa, $privatekey, null, $config); + $publickey = openssl_pkey_get_details($rsa); + $publickey = $publickey['key']; - $publickeyarr = openssl_pkey_get_details($rsa); - $publickey = new RSA(); - $publickey->load($publickeyarr['key']); + $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1))); + $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1))); // clear the buffer of error strings stemming from a minimalistic openssl.cnf - while (openssl_error_string() !== false) { - } + while (openssl_error_string() !== false); return array( 'privatekey' => $privatekey, @@ -468,7 +560,7 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) $e = new BigInteger(CRYPT_RSA_EXPONENT); } - extract(self::_generateMinMax($bits)); + extract($this->_generateMinMax($bits)); $absoluteMin = $min; $temp = $bits >> 1; // divide by two to see how many bits P and Q would be if ($temp > CRYPT_RSA_SMALLEST_PRIME) { @@ -477,17 +569,19 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) } else { $num_primes = 2; } - extract(self::_generateMinMax($temp + $bits % $temp)); + extract($this->_generateMinMax($temp + $bits % $temp)); $finalMax = $max; - extract(self::_generateMinMax($temp)); + extract($this->_generateMinMax($temp)); + + $generator = new BigInteger(); - $n = clone self::$one; + $n = $this->one->copy(); if (!empty($partial)) { extract(unserialize($partial)); } else { $exponents = $coefficients = $primes = array(); $lcm = array( - 'top' => clone self::$one, + 'top' => $this->one->copy(), 'bottom' => false ); } @@ -516,12 +610,12 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) if ($i == $num_primes) { list($min, $temp) = $absoluteMin->divide($n); - if (!$temp->equals(self::$zero)) { - $min = $min->add(self::$one); // ie. ceil() + if (!$temp->equals($this->zero)) { + $min = $min->add($this->one); // ie. ceil() } - $primes[$i] = BigInteger::randomPrime($min, $finalMax, $timeout); + $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); } else { - $primes[$i] = BigInteger::randomPrime($min, $max, $timeout); + $primes[$i] = $generator->randomPrime($min, $max, $timeout); } if ($primes[$i] === false) { // if we've reached the timeout @@ -538,8 +632,8 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) } return array( - 'privatekey' => false, - 'publickey' => false, + 'privatekey' => '', + 'publickey' => '', 'partialkey' => $partialkey ); } @@ -552,7 +646,7 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) $n = $n->multiply($primes[$i]); - $temp = $primes[$i]->subtract(self::$one); + $temp = $primes[$i]->subtract($this->one); // textbook RSA implementations use Euler's totient function instead of the least common multiple. // see http://en.wikipedia.org/wiki/Euler%27s_totient_function @@ -565,7 +659,7 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) list($temp) = $lcm['top']->divide($lcm['bottom']); $gcd = $temp->gcd($e); $i0 = 1; - } while (!$gcd->equals(self::$one)); + } while (!$gcd->equals($this->one)); $d = $e->modInverse($temp); @@ -584,62 +678,662 @@ static function createKey($bits = 2048, $timeout = false, $partial = array()) // coefficient INTEGER, -- (inverse of q) mod p // otherPrimeInfos OtherPrimeInfos OPTIONAL // } - $privatekey = new RSA(); - $privatekey->modulus = $n; - $privatekey->k = $bits >> 3; - $privatekey->publicExponent = $e; - $privatekey->exponent = $d; - $privatekey->privateExponent = $e; - $privatekey->primes = $primes; - $privatekey->exponents = $exponents; - $privatekey->coefficients = $coefficients; - - $publickey = new RSA(); - $publickey->modulus = $n; - $publickey->k = $bits >> 3; - $publickey->exponent = $e; return array( - 'privatekey' => $privatekey, - 'publickey' => $publickey, + 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), + 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false ); } /** - * Add a fileformat plugin + * Convert a private key to the appropriate format. + * + * @access private + * @see setPrivateKeyFormat() + * @param String $RSAPrivateKey + * @return String + */ + function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) + { + $num_primes = count($primes); + $raw = array( + 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi + '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) + ); + + // if the format in question does not support multi-prime rsa and multi-prime rsa was used, + // call _convertPublicKey() instead. + switch ($this->privateKeyFormat) { + case CRYPT_RSA_PRIVATE_FORMAT_XML: + if ($num_primes != 2) { + return false; + } + return "\r\n" . + ' ' . base64_encode($raw['modulus']) . "\r\n" . + ' ' . base64_encode($raw['publicExponent']) . "\r\n" . + '

' . base64_encode($raw['prime1']) . "

\r\n" . + ' ' . base64_encode($raw['prime2']) . "\r\n" . + ' ' . base64_encode($raw['exponent1']) . "\r\n" . + ' ' . base64_encode($raw['exponent2']) . "\r\n" . + ' ' . base64_encode($raw['coefficient']) . "\r\n" . + ' ' . base64_encode($raw['privateExponent']) . "\r\n" . + '
'; + break; + case CRYPT_RSA_PRIVATE_FORMAT_PUTTY: + if ($num_primes != 2) { + return false; + } + $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; + $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; + $key.= $encryption; + $key.= "\r\nComment: " . $this->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($this->comment), $this->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($this->password) && !is_string($this->password)) { + $source.= pack('Na*', strlen($private), $private); + $hashkey = 'putty-private-key-file-mac-key'; + } else { + $private.= crypt_random_string(16 - (strlen($private) & 15)); + $source.= pack('Na*', strlen($private), $private); + $sequence = 0; + $symkey = ''; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new AES(); + + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->encrypt($private); + $hashkey = 'putty-private-key-file-mac-key' . $this->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(pack('H*', sha1($hashkey))); + $key.= 'Private-MAC: ' . bin2hex($hash->_hash($source)) . "\r\n"; + + return $key; + default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1 + $components = array(); + foreach ($raw as $name => $value) { + $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); + } + + $RSAPrivateKey = implode('', $components); + + if ($num_primes > 2) { + $OtherPrimeInfos = ''; + for ($i = 3; $i <= $num_primes; $i++) { + // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + // + // OtherPrimeInfo ::= SEQUENCE { + // prime INTEGER, -- ri + // exponent INTEGER, -- di + // coefficient INTEGER -- ti + // } + $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); + $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); + $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); + } + $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); + } + + $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); + + if (!empty($this->password) || is_string($this->password)) { + $iv = crypt_random_string(8); + $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key + $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); + $des = new TripleDES(); + $des->setKey($symkey); + $des->setIV($iv); + $iv = strtoupper(bin2hex($iv)); + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + "Proc-Type: 4,ENCRYPTED\r\n" . + "DEK-Info: DES-EDE3-CBC,$iv\r\n" . + "\r\n" . + chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . + '-----END RSA PRIVATE KEY-----'; + } else { + $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . + chunk_split(base64_encode($RSAPrivateKey), 64) . + '-----END RSA PRIVATE KEY-----'; + } + + return $RSAPrivateKey; + } + } + + /** + * Convert a public key to the appropriate format + * + * @access private + * @see setPublicKeyFormat() + * @param String $RSAPrivateKey + * @return String + */ + function _convertPublicKey($n, $e) + { + $modulus = $n->toBytes(true); + $publicExponent = $e->toBytes(true); + + switch ($this->publicKeyFormat) { + case CRYPT_RSA_PUBLIC_FORMAT_RAW: + return array('e' => $e->copy(), 'n' => $n->copy()); + case CRYPT_RSA_PUBLIC_FORMAT_XML: + return "\r\n" . + ' ' . base64_encode($modulus) . "\r\n" . + ' ' . base64_encode($publicExponent) . "\r\n" . + ''; + break; + case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: + // from : + // 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) . ' ' . $this->comment; + + return $RSAPublicKey; + default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1 + // from : + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + $components = array( + 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), + 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) + ); + + $RSAPublicKey = pack('Ca*a*a*', + CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], $components['publicExponent'] + ); + + if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $RSAPublicKey = chr(0) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; + + $RSAPublicKey = pack('Ca*a*', + CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey + ); + } + + $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + chunk_split(base64_encode($RSAPublicKey), 64) . + '-----END PUBLIC KEY-----'; + + return $RSAPublicKey; + } + } + + /** + * Break a public or private key down into its constituant components + * + * @access private + * @see _convertPublicKey() + * @see _convertPrivateKey() + * @param String $key + * @param Integer $type + * @return Array + */ + function _parseKey($key, $type) + { + if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) { + return false; + } + + switch ($type) { + case CRYPT_RSA_PUBLIC_FORMAT_RAW: + if (!is_array($key)) { + return false; + } + $components = array(); + switch (true) { + case isset($key['e']): + $components['publicExponent'] = $key['e']->copy(); + break; + case isset($key['exponent']): + $components['publicExponent'] = $key['exponent']->copy(); + break; + case isset($key['publicExponent']): + $components['publicExponent'] = $key['publicExponent']->copy(); + break; + case isset($key[0]): + $components['publicExponent'] = $key[0]->copy(); + } + switch (true) { + case isset($key['n']): + $components['modulus'] = $key['n']->copy(); + break; + case isset($key['modulo']): + $components['modulus'] = $key['modulo']->copy(); + break; + case isset($key['modulus']): + $components['modulus'] = $key['modulus']->copy(); + break; + case isset($key[1]): + $components['modulus'] = $key[1]->copy(); + } + return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; + case CRYPT_RSA_PRIVATE_FORMAT_PKCS1: + case CRYPT_RSA_PUBLIC_FORMAT_PKCS1: + /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is + "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to + protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding + two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + + http://tools.ietf.org/html/rfc1421#section-4.6.1.1 + http://tools.ietf.org/html/rfc1421#section-4.6.1.3 + + DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. + DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation + function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's + own implementation. ie. the implementation *is* the standard and any bugs that may exist in that + implementation are part of the standard, as well. + + * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ + if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { + $iv = pack('H*', trim($matches[2])); + $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key + $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); + // remove the Proc-Type / DEK-Info sections as they're no longer needed + $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); + $ciphertext = $this->_extractBER($key); + if ($ciphertext === false) { + $ciphertext = $key; + } + switch ($matches[1]) { + case 'AES-256-CBC': + $crypto = new AES(); + break; + case 'AES-128-CBC': + $symkey = substr($symkey, 0, 16); + $crypto = new AES(); + break; + case 'DES-EDE3-CFB': + $crypto = new TripleDES(CRYPT_DES_MODE_CFB); + break; + case 'DES-EDE3-CBC': + $symkey = substr($symkey, 0, 24); + $crypto = new TripleDES(); + break; + case 'DES-CBC': + $crypto = new DES(); + break; + default: + return false; + } + $crypto->setKey($symkey); + $crypto->setIV($iv); + $decoded = $crypto->decrypt($ciphertext); + } else { + $decoded = $this->_extractBER($key); + } + + if ($decoded !== false) { + $key = $decoded; + } + + $components = array(); + + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + + $tag = ord($this->_string_shift($key)); + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 631 cons: SEQUENCE + 4:d=1 hl=2 l= 1 prim: INTEGER :00 + 7:d=1 hl=2 l= 13 cons: SEQUENCE + 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 20:d=2 hl=2 l= 0 prim: NULL + 22:d=1 hl=4 l= 609 prim: OCTET STRING */ + + if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { + $this->_string_shift($key, 3); + $tag = CRYPT_RSA_ASN1_SEQUENCE; + } + + if ($tag == CRYPT_RSA_ASN1_SEQUENCE) { + /* intended for keys for which OpenSSL's asn1parse returns the following: + + 0:d=0 hl=4 l= 290 cons: SEQUENCE + 4:d=1 hl=2 l= 13 cons: SEQUENCE + 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption + 17:d=2 hl=2 l= 0 prim: NULL + 19:d=1 hl=4 l= 271 prim: BIT STRING */ + $this->_string_shift($key, $this->_decodeLength($key)); + $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag + $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length + // "The initial octet shall encode, as an unsigned binary integer wtih 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 (section 8.6.2.2) + if ($tag == CRYPT_RSA_ASN1_BITSTRING) { + $this->_string_shift($key); + } + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + if ($this->_decodeLength($key) != strlen($key)) { + return false; + } + $tag = ord($this->_string_shift($key)); + } + if ($tag != CRYPT_RSA_ASN1_INTEGER) { + return false; + } + + $length = $this->_decodeLength($key); + $temp = $this->_string_shift($key, $length); + if (strlen($temp) != 1 || ord($temp) > 2) { + $components['modulus'] = new BigInteger($temp, 256); + $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER + $length = $this->_decodeLength($key); + $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + + return $components; + } + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) { + return false; + } + $length = $this->_decodeLength($key); + $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); + + if (!empty($key)) { + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + while (!empty($key)) { + if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) { + return false; + } + $this->_decodeLength($key); + $key = substr($key, 1); + $length = $this->_decodeLength($key); + $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); + $this->_string_shift($key); + $length = $this->_decodeLength($key); + $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); + } + } + + return $components; + case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH: + $parts = explode(' ', $key, 3); + + $key = isset($parts[1]) ? base64_decode($parts[1]) : false; + if ($key === false) { + return false; + } + + $comment = isset($parts[2]) ? $parts[2] : false; + + $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; + + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $modulus = new BigInteger($this->_string_shift($key, $length), -256); + + if ($cleanup && strlen($key)) { + if (strlen($key) <= 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($key, 4))); + $realModulus = new BigInteger($this->_string_shift($key, $length), -256); + return strlen($key) ? false : array( + 'modulus' => $realModulus, + 'publicExponent' => $modulus, + 'comment' => $comment + ); + } else { + return strlen($key) ? false : array( + 'modulus' => $modulus, + 'publicExponent' => $publicExponent, + 'comment' => $comment + ); + } + // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue + // http://en.wikipedia.org/wiki/XML_Signature + case CRYPT_RSA_PRIVATE_FORMAT_XML: + case CRYPT_RSA_PUBLIC_FORMAT_XML: + $this->components = array(); + + $xml = xml_parser_create('UTF-8'); + xml_set_object($xml, $this); + xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); + xml_set_character_data_handler($xml, '_data_handler'); + // add to account for "dangling" tags like ... that are sometimes added + if (!xml_parse($xml, '' . $key . '')) { + return false; + } + + return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; + // from PuTTY's SSHPUBK.C + case CRYPT_RSA_PRIVATE_FORMAT_PUTTY: + $components = array(); + $key = preg_split('#\r\n|\r|\n#', $key); + $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); + if ($type != 'ssh-rsa') { + return false; + } + $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); + $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); + + $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); + $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); + $public = substr($public, 11); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); + extract(unpack('Nlength', $this->_string_shift($public, 4))); + $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); + + $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); + $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); + + switch ($encryption) { + case 'aes256-cbc': + $symkey = ''; + $sequence = 0; + while (strlen($symkey) < 32) { + $temp = pack('Na*', $sequence++, $this->password); + $symkey.= pack('H*', sha1($temp)); + } + $symkey = substr($symkey, 0, 32); + $crypto = new AES(); + } + + if ($encryption != 'none') { + $crypto->setKey($symkey); + $crypto->disablePadding(); + $private = $crypto->decrypt($private); + if ($private === false) { + return false; + } + } + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + + extract(unpack('Nlength', $this->_string_shift($private, 4))); + if (strlen($private) < $length) { + return false; + } + $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); + + return $components; + } + } + + /** + * Returns the key size + * + * More specifically, this returns the size of the modulo in bits. + * + * @access public + * @return Integer + */ + function getSize() + { + return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); + } + + /** + * Start Element Handler * - * The plugin needs to either already be loaded or be auto-loadable. - * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin. + * Called by xml_set_element_handler() * - * @see self::load() - * @param string $fullname - * @access public - * @return bool + * @access private + * @param Resource $parser + * @param String $name + * @param Array $attribs */ - static function addFileFormat($fullname) + function _start_element_handler($parser, $name, $attribs) { - self::_initialize_static_variables(); - - if (class_exists($fullname)) { - $meta = new \ReflectionClass($path); - $shortname = $meta->getShortName(); - self::$fileFormats[strtolower($shortname)] = $fullname; - self::$origFileFormats[] = $shortname; + //$name = strtoupper($name); + switch ($name) { + case 'MODULUS': + $this->current = &$this->components['modulus']; + break; + case 'EXPONENT': + $this->current = &$this->components['publicExponent']; + break; + case 'P': + $this->current = &$this->components['primes'][1]; + break; + case 'Q': + $this->current = &$this->components['primes'][2]; + break; + case 'DP': + $this->current = &$this->components['exponents'][1]; + break; + case 'DQ': + $this->current = &$this->components['exponents'][2]; + break; + case 'INVERSEQ': + $this->current = &$this->components['coefficients'][2]; + break; + case 'D': + $this->current = &$this->components['privateExponent']; } + $this->current = ''; } /** - * Returns a list of supported formats. + * Stop Element Handler * - * @access public - * @return array + * Called by xml_set_element_handler() + * + * @access private + * @param Resource $parser + * @param String $name */ - static function getSupportedFormats() + function _stop_element_handler($parser, $name) { - self::_initialize_static_variables(); + if (isset($this->current)) { + $this->current = new BigInteger(base64_decode($this->current), 256); + unset($this->current); + } + } - return self::$origFileFormats; + /** + * Data Handler + * + * Called by xml_set_character_data_handler() + * + * @access private + * @param Resource $parser + * @param String $data + */ + function _data_handler($parser, $data) + { + if (!isset($this->current) || is_object($this->current)) { + return; + } + $this->current.= trim($data); } /** @@ -648,19 +1342,23 @@ static function getSupportedFormats() * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) * * @access public - * @param string $key - * @param int $type optional + * @param String $key + * @param Integer $type optional */ - function load($key, $type = false) + function loadKey($key, $type = false) { - if ($key instanceof RSA) { + if (is_object($key) && strtolower(get_class($key)) == 'crypt_rsa') { $this->privateKeyFormat = $key->privateKeyFormat; $this->publicKeyFormat = $key->publicKeyFormat; $this->k = $key->k; $this->hLen = $key->hLen; $this->sLen = $key->sLen; $this->mgfHLen = $key->mgfHLen; + $this->encryptionMode = $key->encryptionMode; + $this->signatureMode = $key->signatureMode; $this->password = $key->password; + $this->configFile = $key->configFile; + $this->comment = $key->comment; if (is_object($key->hash)) { $this->hash = new Hash($key->hash->getHash()); @@ -670,13 +1368,13 @@ function load($key, $type = false) } if (is_object($key->modulus)) { - $this->modulus = clone $key->modulus; + $this->modulus = $key->modulus->copy(); } if (is_object($key->exponent)) { - $this->exponent = clone $key->exponent; + $this->exponent = $key->exponent->copy(); } if (is_object($key->publicExponent)) { - $this->publicExponent = clone $key->publicExponent; + $this->publicExponent = $key->publicExponent->copy(); } $this->primes = array(); @@ -684,49 +1382,44 @@ function load($key, $type = false) $this->coefficients = array(); foreach ($this->primes as $prime) { - $this->primes[] = clone $prime; + $this->primes[] = $prime->copy(); } foreach ($this->exponents as $exponent) { - $this->exponents[] = clone $exponent; + $this->exponents[] = $exponent->copy(); } foreach ($this->coefficients as $coefficient) { - $this->coefficients[] = clone $coefficient; + $this->coefficients[] = $coefficient->copy(); } return true; } - $components = false; if ($type === false) { - foreach (self::$fileFormats as $format) { - try { - $components = $format::load($key, $this->password); - } catch (\Exception $e) { - $components = false; - } + $types = array( + CRYPT_RSA_PUBLIC_FORMAT_RAW, + CRYPT_RSA_PRIVATE_FORMAT_PKCS1, + CRYPT_RSA_PRIVATE_FORMAT_XML, + CRYPT_RSA_PRIVATE_FORMAT_PUTTY, + CRYPT_RSA_PUBLIC_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); if ($components !== false) { break; } } + } else { - $format = strtolower($type); - if (isset(self::$fileFormats[$format])) { - $format = self::$fileFormats[$format]; - try { - $components = $format::load($key, $this->password); - } catch (\Exception $e) { - $components = false; - } - } + $components = $this->_parseKey($key, $type); } if ($components === false) { - $this->format = false; return false; } - $this->format = $format; - + if (isset($components['comment']) && $components['comment'] !== false) { + $this->comment = $components['comment']; + } $this->modulus = $components['modulus']; $this->k = strlen($this->modulus->toBytes()); $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; @@ -742,88 +1435,19 @@ function load($key, $type = false) $this->publicExponent = false; } - if ($components['isPublicKey']) { - $this->setPublicKey(); - } - return true; } - /** - * Returns the format of the loaded key. - * - * If the key that was loaded wasn't in a valid or if the key was auto-generated - * with RSA::createKey() then this will return false. - * - * @see self::load() - * @access public - * @return mixed - */ - function getLoadedFormat() - { - if ($this->format === false) { - return false; - } - - $meta = new \ReflectionClass($this->format); - return $meta->getShortName(); - } - - /** - * Returns the private key - * - * The private key is only returned if the currently loaded key contains the constituent prime numbers. - * - * @see self::getPublicKey() - * @access public - * @param string $type optional - * @return mixed - */ - function getPrivateKey($type = 'PKCS1') - { - $type = strtolower($type); - if (!isset(self::$fileFormats[$type])) { - return false; - } - $type = self::$fileFormats[$type]; - if (!method_exists($type, 'savePrivateKey')) { - return false; - } - - if (empty($this->primes)) { - return false; - } - - $oldFormat = $this->privateKeyFormat; - $this->privateKeyFormat = $type; - $temp = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password); - $this->privateKeyFormat = $oldFormat; - return $temp; - } - - /** - * Returns the key size - * - * More specifically, this returns the size of the modulo in bits. - * - * @access public - * @return int - */ - function getSize() - { - return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); - } - /** * Sets the password * * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false. * Or rather, pass in $password such that empty($password) && !is_string($password) is true. * - * @see self::createKey() - * @see self::load() + * @see createKey() + * @see loadKey() * @access public - * @param string $password + * @param String $password */ function setPassword($password = false) { @@ -837,19 +1461,17 @@ function setPassword($password = false) * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public - * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used - * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being - * public. + * exponent this won't work unless you manually add the public exponent. * * Do note that when a new key is loaded the index will be cleared. * * Returns true on success, false on failure * - * @see self::getPublicKey() + * @see getPublicKey() * @access public - * @param string $key optional - * @param int $type optional - * @return bool + * @param String $key optional + * @param Integer $type optional + * @return Boolean */ function setPublicKey($key = false, $type = false) { @@ -863,40 +1485,27 @@ function setPublicKey($key = false, $type = false) return true; } - $components = false; if ($type === false) { - foreach (self::$fileFormats as $format) { - if (!method_exists($format, 'savePublicKey')) { - continue; - } - try { - $components = $format::load($key, $this->password); - } catch (\Exception $e) { - $components = false; - } + $types = array( + CRYPT_RSA_PUBLIC_FORMAT_RAW, + CRYPT_RSA_PUBLIC_FORMAT_PKCS1, + CRYPT_RSA_PUBLIC_FORMAT_XML, + CRYPT_RSA_PUBLIC_FORMAT_OPENSSH + ); + foreach ($types as $type) { + $components = $this->_parseKey($key, $type); if ($components !== false) { break; } } } else { - $format = strtolower($type); - if (isset(self::$fileFormats[$format])) { - $format = self::$fileFormats[$format]; - try { - $components = $format::load($key, $this->password); - } catch (\Exception $e) { - $components = false; - } - } + $components = $this->_parseKey($key, $type); } if ($components === false) { - $this->format = false; return false; } - $this->format = $format; - if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { $this->modulus = $components['modulus']; $this->exponent = $this->publicExponent = $components['publicExponent']; @@ -908,40 +1517,6 @@ function setPublicKey($key = false, $type = false) return true; } - /** - * Defines the private key - * - * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force - * phpseclib to treat the key as a private key. This function will do that. - * - * Do note that when a new key is loaded the index will be cleared. - * - * Returns true on success, false on failure - * - * @see self::getPublicKey() - * @access public - * @param string $key optional - * @param int $type optional - * @return bool - */ - function setPrivateKey($key = false, $type = false) - { - if ($key === false && !empty($this->publicExponent)) { - $this->publicExponent = false; - return true; - } - - $rsa = new RSA(); - if (!$rsa->load($key, $type)) { - return false; - } - $rsa->publicExponent = false; - - // don't overwrite the old key if the new key is invalid - $this->load($rsa); - return true; - } - /** * Returns the public key * @@ -949,66 +1524,45 @@ function setPrivateKey($key = false, $type = false) * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. * - * @see self::getPrivateKey() + * @see getPublicKey() * @access public - * @param string $type optional - * @return mixed + * @param String $key + * @param Integer $type optional */ - function getPublicKey($type = 'PKCS8') + function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { - $type = strtolower($type); - if (!isset(self::$fileFormats[$type])) { - return false; - } - $type = self::$fileFormats[$type]; - if (!method_exists($type, 'savePublicKey')) { - return false; - } - if (empty($this->modulus) || empty($this->publicExponent)) { return false; } $oldFormat = $this->publicKeyFormat; $this->publicKeyFormat = $type; - $temp = $type::savePublicKey($this->modulus, $this->publicExponent); + $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); $this->publicKeyFormat = $oldFormat; return $temp; } /** - * Returns the public key's fingerprint + * Returns the private key * - * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is - * no public key currently loaded, false is returned. - * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716) + * The private key is only returned if the currently loaded key contains the constituent prime numbers. * + * @see getPublicKey() * @access public - * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned - * for invalid values. - * @return mixed + * @param String $key + * @param Integer $type optional */ - function getPublicKeyFingerprint($algorithm = 'md5') + function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { - if (empty($this->modulus) || empty($this->publicExponent)) { + if (empty($this->primes)) { return false; } - $modulus = $this->modulus->toBytes(true); - $publicExponent = $this->publicExponent->toBytes(true); - - $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); - - switch ($algorithm) { - case 'sha256': - $hash = new Hash('sha256'); - $base = Base64::encode($hash->hash($RSAPublicKey)); - return substr($base, 0, strlen($base) - 1); - case 'md5': - return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1); - default: - return false; - } + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); + $this->privateKeyFormat = $oldFormat; + return $temp; } /** @@ -1017,60 +1571,48 @@ function getPublicKeyFingerprint($algorithm = 'md5') * Returns the private key without the prime number constituants. Structurally identical to a public key that * hasn't been set as the public key * - * @see self::getPrivateKey() + * @see getPrivateKey() * @access private - * @param string $type optional - * @return mixed + * @param String $key + * @param Integer $type optional */ - function _getPrivatePublicKey($type = 'PKCS8') + function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { - $type = strtolower($type); - if (!isset(self::$fileFormats[$type])) { - return false; - } - $type = self::$fileFormats[$type]; - if (!method_exists($type, 'savePublicKey')) { - return false; - } - if (empty($this->modulus) || empty($this->exponent)) { return false; } $oldFormat = $this->publicKeyFormat; - $this->publicKeyFormat = $type; - $temp = $type::savePublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $mode; + $temp = $this->_convertPublicKey($this->modulus, $this->exponent); $this->publicKeyFormat = $oldFormat; return $temp; } - /** - * __toString() magic method + * __toString() magic method * * @access public - * @return string */ function __toString() { $key = $this->getPrivateKey($this->privateKeyFormat); - if (is_string($key)) { + if ($key !== false) { return $key; } $key = $this->_getPrivatePublicKey($this->publicKeyFormat); - return is_string($key) ? $key : ''; + return $key !== false ? $key : ''; } /** - * __clone() magic method + * __clone() magic method * * @access public - * @return \phpseclib\Crypt\RSA */ function __clone() { $key = new RSA(); - $key->load($this); + $key->loadKey($this); return $key; } @@ -1078,10 +1620,10 @@ function __clone() * Generates the smallest and largest numbers requiring $bits bits * * @access private - * @param int $bits - * @return array + * @param Integer $bits + * @return Array */ - static function _generateMinMax($bits) + function _generateMinMax($bits) { $bytes = $bits >> 3; $min = str_repeat(chr(0), $bytes); @@ -1107,13 +1649,13 @@ static function _generateMinMax($bits) * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private - * @param string $string - * @return int + * @param String $string + * @return Integer */ function _decodeLength(&$string) { $length = ord($this->_string_shift($string)); - if ($length & 0x80) { // definite length, long form + if ( $length & 0x80 ) { // definite length, long form $length&= 0x7F; $temp = $this->_string_shift($string, $length); list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); @@ -1128,8 +1670,8 @@ function _decodeLength(&$string) * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private - * @param int $length - * @return string + * @param Integer $length + * @return String */ function _encodeLength($length) { @@ -1146,9 +1688,9 @@ function _encodeLength($length) * * Inspired by array_shift * - * @param string $string - * @param int $index - * @return string + * @param String $string + * @param optional Integer $index + * @return String * @access private */ function _string_shift(&$string, $index = 1) @@ -1161,9 +1703,9 @@ function _string_shift(&$string, $index = 1) /** * Determines the private key format * - * @see self::createKey() + * @see createKey() * @access public - * @param int $format + * @param Integer $format */ function setPrivateKeyFormat($format) { @@ -1173,9 +1715,9 @@ function setPrivateKeyFormat($format) /** * Determines the public key format * - * @see self::createKey() + * @see createKey() * @access public - * @param int $format + * @param Integer $format */ function setPublicKeyFormat($format) { @@ -1185,15 +1727,15 @@ function setPublicKeyFormat($format) /** * Determines which hashing function should be used * - * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and - * decryption. If $hash isn't supported, sha256 is used. + * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and + * decryption. If $hash isn't supported, sha1 is used. * * @access public - * @param string $hash + * @param String $hash */ function setHash($hash) { - // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + // Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. switch ($hash) { case 'md2': case 'md5': @@ -1201,15 +1743,12 @@ function setHash($hash) case 'sha256': case 'sha384': case 'sha512': - case 'sha224': - case 'sha512/224': - case 'sha512/256': $this->hash = new Hash($hash); $this->hashName = $hash; break; default: - $this->hash = new Hash('sha256'); - $this->hashName = 'sha256'; + $this->hash = new Hash('sha1'); + $this->hashName = 'sha1'; } $this->hLen = $this->hash->getLength(); } @@ -1217,15 +1756,15 @@ function setHash($hash) /** * Determines which hashing function should be used for the mask generation function * - * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's + * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's * best if Hash and MGFHash are set to the same thing this is not a requirement. * * @access public - * @param string $hash + * @param String $hash */ function setMGFHash($hash) { - // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. + // Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example. switch ($hash) { case 'md2': case 'md5': @@ -1233,13 +1772,10 @@ function setMGFHash($hash) case 'sha256': case 'sha384': case 'sha512': - case 'sha224': - case 'sha512/224': - case 'sha512/256': $this->mgfHash = new Hash($hash); break; default: - $this->mgfHash = new Hash('sha256'); + $this->mgfHash = new Hash('sha1'); } $this->mgfHLen = $this->mgfHash->getLength(); } @@ -1253,7 +1789,7 @@ function setMGFHash($hash) * of the hash function Hash) and 0. * * @access public - * @param int $format + * @param Integer $format */ function setSaltLength($sLen) { @@ -1266,17 +1802,15 @@ function setSaltLength($sLen) * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. * * @access private - * @param bool|\phpseclib\Math\BigInteger $x - * @param int $xLen - * @return bool|string + * @param BigInteger $x + * @param Integer $xLen + * @return String */ function _i2osp($x, $xLen) { - if ($x === false) { - return false; - } $x = $x->toBytes(); if (strlen($x) > $xLen) { + user_error('Integer too large'); return false; } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); @@ -1288,8 +1822,8 @@ function _i2osp($x, $xLen) * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * * @access private - * @param string $x - * @return \phpseclib\Math\BigInteger + * @param String $x + * @return BigInteger */ function _os2ip($x) { @@ -1302,19 +1836,13 @@ function _os2ip($x) * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}. * * @access private - * @param \phpseclib\Math\BigInteger $x - * @return \phpseclib\Math\BigInteger + * @param BigInteger $x + * @return BigInteger */ function _exponentiate($x) { - switch (true) { - case empty($this->primes): - case $this->primes[1]->equals(self::$zero): - case empty($this->coefficients): - case $this->coefficients[2]->equals(self::$zero): - case empty($this->exponents): - case $this->exponents[1]->equals(self::$zero): - return $x->modPow($this->exponent, $this->modulus); + if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) { + return $x->modPow($this->exponent, $this->modulus); } $num_primes = count($this->primes); @@ -1349,7 +1877,9 @@ function _exponentiate($x) } } - $r = BigInteger::random(self::$one, $smallest->subtract(self::$one)); + $one = new BigInteger(1); + + $r = $one->random($one, $smallest->subtract($one)); $m_i = array( 1 => $this->_blind($x, $r, 1), @@ -1384,10 +1914,10 @@ function _exponentiate($x) * Returns $x->modPow($this->exponents[$i], $this->primes[$i]) * * @access private - * @param \phpseclib\Math\BigInteger $x - * @param \phpseclib\Math\BigInteger $r - * @param int $i - * @return \phpseclib\Math\BigInteger + * @param BigInteger $x + * @param BigInteger $r + * @param Integer $i + * @return BigInteger */ function _blind($x, $r, $i) { @@ -1411,9 +1941,9 @@ function _blind($x, $r, $i) * Thanks for the heads up singpolyma! * * @access private - * @param string $x - * @param string $y - * @return bool + * @param String $x + * @param String $y + * @return Boolean */ function _equals($x, $y) { @@ -1435,12 +1965,13 @@ function _equals($x, $y) * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}. * * @access private - * @param \phpseclib\Math\BigInteger $m - * @return bool|\phpseclib\Math\BigInteger + * @param BigInteger $m + * @return BigInteger */ function _rsaep($m) { - if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); return false; } return $this->_exponentiate($m); @@ -1452,12 +1983,13 @@ function _rsaep($m) * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}. * * @access private - * @param \phpseclib\Math\BigInteger $c - * @return bool|\phpseclib\Math\BigInteger + * @param BigInteger $c + * @return BigInteger */ function _rsadp($c) { - if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { + if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { + user_error('Ciphertext representative out of range'); return false; } return $this->_exponentiate($c); @@ -1469,12 +2001,13 @@ function _rsadp($c) * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}. * * @access private - * @param \phpseclib\Math\BigInteger $m - * @return bool|\phpseclib\Math\BigInteger + * @param BigInteger $m + * @return BigInteger */ function _rsasp1($m) { - if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { + if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { + user_error('Message representative out of range'); return false; } return $this->_exponentiate($m); @@ -1486,12 +2019,13 @@ function _rsasp1($m) * 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 + * @param BigInteger $s + * @return BigInteger */ function _rsavp1($s) { - if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { + if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { + user_error('Signature representative out of range'); return false; } return $this->_exponentiate($s); @@ -1503,9 +2037,9 @@ function _rsavp1($s) * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}. * * @access private - * @param string $mgfSeed - * @param int $mgfLen - * @return string + * @param String $mgfSeed + * @param Integer $mgfLen + * @return String */ function _mgf1($mgfSeed, $maskLen) { @@ -1515,7 +2049,7 @@ function _mgf1($mgfSeed, $maskLen) $count = ceil($maskLen / $this->mgfHLen); for ($i = 0; $i < $count; $i++) { $c = pack('N', $i); - $t.= $this->mgfHash->hash($mgfSeed . $c); + $t.= $this->mgfHash->_hash($mgfSeed . $c); } return substr($t, 0, $maskLen); @@ -1528,10 +2062,9 @@ function _mgf1($mgfSeed, $maskLen) * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}. * * @access private - * @param string $m - * @param string $l - * @throws \OutOfBoundsException if strlen($m) > $this->k - 2 * $this->hLen - 2 - * @return string + * @param String $m + * @param String $l + * @return String */ function _rsaes_oaep_encrypt($m, $l = '') { @@ -1543,15 +2076,16 @@ function _rsaes_oaep_encrypt($m, $l = '') // be output. if ($mLen > $this->k - 2 * $this->hLen - 2) { - throw new \OutOfBoundsException('Message too long'); + user_error('Message too long'); + return false; } // EME-OAEP encoding - $lHash = $this->hash->hash($l); + $lHash = $this->hash->_hash($l); $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); $db = $lHash . $ps . chr(1) . $m; - $seed = Random::string($this->hLen); + $seed = crypt_random_string($this->hLen); $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); $maskedDB = $db ^ $dbMask; $seedMask = $this->_mgf1($maskedDB, $this->hLen); @@ -1591,9 +2125,9 @@ function _rsaes_oaep_encrypt($m, $l = '') * this document. * * @access private - * @param string $c - * @param string $l - * @return bool|string + * @param String $c + * @param String $l + * @return String */ function _rsaes_oaep_decrypt($c, $l = '') { @@ -1603,6 +2137,7 @@ function _rsaes_oaep_decrypt($c, $l = '') // be output. if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { + user_error('Decryption error'); return false; } @@ -1610,14 +2145,15 @@ function _rsaes_oaep_decrypt($c, $l = '') $c = $this->_os2ip($c); $m = $this->_rsadp($c); - $em = $this->_i2osp($m, $this->k); - if ($em === false) { + if ($m === false) { + user_error('Decryption error'); return false; } + $em = $this->_i2osp($m, $this->k); // EME-OAEP decoding - $lHash = $this->hash->hash($l); + $lHash = $this->hash->_hash($l); $y = ord($em[0]); $maskedSeed = substr($em, 1, $this->hLen); $maskedDB = substr($em, $this->hLen + 1); @@ -1628,10 +2164,12 @@ function _rsaes_oaep_decrypt($c, $l = '') $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); if ($lHash != $lHash2) { + user_error('Decryption error'); return false; } $m = ltrim($m, chr(0)); if (ord($m[0]) != 1) { + user_error('Decryption error'); return false; } @@ -1640,46 +2178,24 @@ function _rsaes_oaep_decrypt($c, $l = '') return substr($m, 1); } - /** - * Raw Encryption / Decryption - * - * Doesn't use padding and is not recommended. - * - * @access private - * @param string $m - * @return bool|string - * @throws \OutOfBoundsException if strlen($m) > $this->k - */ - function _raw_encrypt($m) - { - if (strlen($m) > $this->k) { - throw new \OutOfBoundsException('Message too long'); - } - - $temp = $this->_os2ip($m); - $temp = $this->_rsaep($temp); - return $this->_i2osp($temp, $this->k); - } - /** * RSAES-PKCS1-V1_5-ENCRYPT * * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}. * * @access private - * @param string $m - * @param bool $pkcs15_compat optional - * @throws \OutOfBoundsException if strlen($m) > $this->k - 11 - * @return bool|string + * @param String $m + * @return String */ - function _rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false) + function _rsaes_pkcs1_v1_5_encrypt($m) { $mLen = strlen($m); // Length checking if ($mLen > $this->k - 11) { - throw new \OutOfBoundsException('Message too long'); + user_error('Message too long'); + return false; } // EME-PKCS1-v1_5 encoding @@ -1687,13 +2203,13 @@ function _rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false) $psLen = $this->k - $mLen - 3; $ps = ''; while (strlen($ps) != $psLen) { - $temp = Random::string($psLen - strlen($ps)); + $temp = crypt_random_string($psLen - strlen($ps)); $temp = str_replace("\x00", '', $temp); $ps.= $temp; } $type = 2; // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done - if ($pkcs15_compat && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { + if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { $type = 1; // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" $ps = str_repeat("\xFF", $psLen); @@ -1715,26 +2231,27 @@ function _rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false) * * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}. * - * For compatibility purposes, this function departs slightly from the description given in RFC3447. + * For compatability purposes, this function departs slightly from the description given in RFC3447. * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed - * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the + * to be 2 regardless of which key is used. For compatability purposes, we'll just check to make sure the * second byte is 2 or less. If it is, we'll accept the decrypted string as valid. * - * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt + * As a consequence of this, a private key encrypted ciphertext produced with RSA may not decrypt * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but * not private key encrypted ciphertext's. * * @access private - * @param string $c - * @return bool|string + * @param String $c + * @return String */ function _rsaes_pkcs1_v1_5_decrypt($c) { // Length checking if (strlen($c) != $this->k) { // or if k < 11 + user_error('Decryption error'); return false; } @@ -1742,14 +2259,17 @@ function _rsaes_pkcs1_v1_5_decrypt($c) $c = $this->_os2ip($c); $m = $this->_rsadp($c); - $em = $this->_i2osp($m, $this->k); - if ($em === false) { + + if ($m === false) { + user_error('Decryption error'); return false; } + $em = $this->_i2osp($m, $this->k); // EME-PKCS1-v1_5 decoding if (ord($em[0]) != 0 || ord($em[1]) > 2) { + user_error('Decryption error'); return false; } @@ -1757,6 +2277,7 @@ function _rsaes_pkcs1_v1_5_decrypt($c) $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { + user_error('Decryption error'); return false; } @@ -1771,9 +2292,8 @@ function _rsaes_pkcs1_v1_5_decrypt($c) * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}. * * @access private - * @param string $m - * @throws \RuntimeException on encoding error - * @param int $emBits + * @param String $m + * @param Integer $emBits */ function _emsa_pss_encode($m, $emBits) { @@ -1781,16 +2301,17 @@ function _emsa_pss_encode($m, $emBits) // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) - $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + $sLen = $this->sLen == false ? $this->hLen : $this->sLen; - $mHash = $this->hash->hash($m); + $mHash = $this->hash->_hash($m); if ($emLen < $this->hLen + $sLen + 2) { + user_error('Encoding error'); return false; } - $salt = Random::string($sLen); + $salt = crypt_random_string($sLen); $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; - $h = $this->hash->hash($m2); + $h = $this->hash->_hash($m2); $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); $db = $ps . chr(1) . $salt; $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); @@ -1807,10 +2328,10 @@ function _emsa_pss_encode($m, $emBits) * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}. * * @access private - * @param string $m - * @param string $em - * @param int $emBits - * @return string + * @param String $m + * @param String $em + * @param Integer $emBits + * @return String */ function _emsa_pss_verify($m, $em, $emBits) { @@ -1818,9 +2339,9 @@ function _emsa_pss_verify($m, $em, $emBits) // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8); - $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + $sLen = $this->sLen == false ? $this->hLen : $this->sLen; - $mHash = $this->hash->hash($m); + $mHash = $this->hash->_hash($m); if ($emLen < $this->hLen + $sLen + 2) { return false; } @@ -1844,7 +2365,7 @@ function _emsa_pss_verify($m, $em, $emBits) } $salt = substr($db, $temp + 1); // should be $sLen long $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; - $h2 = $this->hash->hash($m2); + $h2 = $this->hash->_hash($m2); return $this->_equals($h, $h2); } @@ -1854,8 +2375,8 @@ function _emsa_pss_verify($m, $em, $emBits) * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}. * * @access private - * @param string $m - * @return bool|string + * @param String $m + * @return String */ function _rsassa_pss_sign($m) { @@ -1880,15 +2401,16 @@ function _rsassa_pss_sign($m) * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}. * * @access private - * @param string $m - * @param string $s - * @return bool|string + * @param String $m + * @param String $s + * @return String */ function _rsassa_pss_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { + user_error('Invalid signature'); return false; } @@ -1898,8 +2420,13 @@ function _rsassa_pss_verify($m, $s) $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } $em = $this->_i2osp($m2, $modBits >> 3); if ($em === false) { + user_error('Invalid signature'); return false; } @@ -1914,50 +2441,43 @@ function _rsassa_pss_verify($m, $s) * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}. * * @access private - * @param string $m - * @param int $emLen - * @throws \LengthException if the intended encoded message length is too short - * @return string + * @param String $m + * @param Integer $emLen + * @return String */ function _emsa_pkcs1_v1_5_encode($m, $emLen) { - $h = $this->hash->hash($m); + $h = $this->hash->_hash($m); + if ($h === false) { + return false; + } // see http://tools.ietf.org/html/rfc3447#page-43 switch ($this->hashName) { case 'md2': - $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10"; + $t = pack('H*', '3020300c06082a864886f70d020205000410'); break; case 'md5': - $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"; + $t = pack('H*', '3020300c06082a864886f70d020505000410'); break; case 'sha1': - $t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; + $t = pack('H*', '3021300906052b0e03021a05000414'); break; case 'sha256': - $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; + $t = pack('H*', '3031300d060960864801650304020105000420'); break; case 'sha384': - $t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"; + $t = pack('H*', '3041300d060960864801650304020205000430'); break; case 'sha512': - $t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"; - break; - // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 - case 'sha224': - $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c"; - break; - case 'sha512/224': - $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c"; - break; - case 'sha512/256': - $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20"; + $t = pack('H*', '3051300d060960864801650304020305000440'); } $t.= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { - throw new \LengthException('Intended encoded message length too short'); + user_error('Intended encoded message length too short'); + return false; } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); @@ -1973,20 +2493,17 @@ function _emsa_pkcs1_v1_5_encode($m, $emLen) * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}. * * @access private - * @param string $m - * @throws \LengthException if the RSA modulus is too short - * @return bool|string + * @param String $m + * @return String */ function _rsassa_pkcs1_v1_5_sign($m) { // EMSA-PKCS1-v1_5 encoding - // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus - // too short" and stop. - try { - $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - } catch (\LengthException $e) { - throw new \LengthException('RSA modulus too short'); + $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em === false) { + user_error('RSA modulus too short'); + return false; } // RSA signature @@ -2006,16 +2523,15 @@ function _rsassa_pkcs1_v1_5_sign($m) * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}. * * @access private - * @param string $m - * @param string $s - * @throws \LengthException if the RSA modulus is too short - * @return bool + * @param String $m + * @return String */ function _rsassa_pkcs1_v1_5_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { + user_error('Invalid signature'); return false; } @@ -2023,19 +2539,22 @@ function _rsassa_pkcs1_v1_5_verify($m, $s) $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); + if ($m2 === false) { + user_error('Invalid signature'); + return false; + } $em = $this->_i2osp($m2, $this->k); if ($em === false) { + user_error('Invalid signature'); return false; } // EMSA-PKCS1-v1_5 encoding - // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus - // too short" and stop. - try { - $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - } catch (\LengthException $e) { - throw new \LengthException('RSA modulus too short'); + $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + if ($em2 === false) { + user_error('RSA modulus too short'); + return false; } // Compare @@ -2043,179 +2562,153 @@ function _rsassa_pkcs1_v1_5_verify($m, $s) } /** - * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) + * Set Encryption Mode * - * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 - * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. - * This means that under rare conditions you can have a perfectly valid v1.5 signature - * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends - * that if you're going to validate these types of signatures you "should indicate - * whether the underlying BER encoding is a DER encoding and hence whether the signature - * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do - * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of - * RSA::PADDING_PKCS1... that means BER encoding was used. + * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1. * - * @access private - * @param string $m - * @param string $s - * @return bool + * @access public + * @param Integer $mode */ - function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s) + function setEncryptionMode($mode) { - // Length checking - - if (strlen($s) != $this->k) { - return false; - } - - // RSA verification - - $s = $this->_os2ip($s); - $m2 = $this->_rsavp1($s); - if ($m2 === false) { - return false; - } - $em = $this->_i2osp($m2, $this->k); - if ($em === false) { - return false; - } - - if ($this->_string_shift($em, 2) != "\0\1") { - return false; - } - - $em = ltrim($em, "\xFF"); - if ($this->_string_shift($em) != "\0") { - return false; - } - - $asn1 = new ASN1(); - $decoded = $asn1->decodeBER($em); - if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { - return false; - } - - $AlgorithmIdentifier = array( - 'type' => ASN1::TYPE_SEQUENCE, - 'children' => array( - 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), - 'parameters' => array( - 'type' => ASN1::TYPE_ANY, - 'optional' => true - ) - ) - ); - - $DigestInfo = array( - 'type' => ASN1::TYPE_SEQUENCE, - 'children' => array( - 'digestAlgorithm' => $AlgorithmIdentifier, - 'digest' => array('type' => ASN1::TYPE_OCTET_STRING) - ) - ); - - $oids = array( - '1.2.840.113549.2.2' => 'md2', - '1.2.840.113549.2.4' => 'md4', // from PKCS1 v1.5 - '1.2.840.113549.2.5' => 'md5', - '1.3.14.3.2.26' => 'sha1', - '2.16.840.1.101.3.4.2.1' => 'sha256', - '2.16.840.1.101.3.4.2.2' => 'sha384', - '2.16.840.1.101.3.4.2.3' => 'sha512', - // from PKCS1 v2.2 - '2.16.840.1.101.3.4.2.4' => 'sha224', - '2.16.840.1.101.3.4.2.5' => 'sha512/224', - '2.16.840.1.101.3.4.2.6' => 'sha512/256', - ); - - $asn1->loadOIDs($oids); - - $decoded = $asn1->asn1map($decoded[0], $DigestInfo); - if (!isset($decoded) || $decoded === false) { - return false; - } + $this->encryptionMode = $mode; + } - if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { - return false; - } + /** + * Set Signature Mode + * + * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1 + * + * @access public + * @param Integer $mode + */ + function setSignatureMode($mode) + { + $this->signatureMode = $mode; + } - $hash = new Hash($decoded['digestAlgorithm']['algorithm']); - $em = $hash->hash($m); - $em2 = Base64::decode($decoded['digest']); + /** + * Set public key comment. + * + * @access public + * @param String $comment + */ + function setComment($comment) + { + $this->comment = $comment; + } - return $this->_equals($em, $em2); + /** + * Get public key comment. + * + * @access public + * @return String + */ + function getComment() + { + return $this->comment; } /** * Encryption * - * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be. + * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will * be concatenated together. * - * @see self::decrypt() + * @see decrypt() * @access public - * @param string $plaintext - * @param int $padding optional - * @return bool|string - * @throws \LengthException if the RSA modulus is too short + * @param String $plaintext + * @return String */ - function encrypt($plaintext, $padding = self::PADDING_OAEP) + function encrypt($plaintext) { - switch ($padding) { - case self::PADDING_NONE: - return $this->_raw_encrypt($plaintext); - case self::PADDING_PKCS15_COMPAT: - case self::PADDING_PKCS1: - return $this->_rsaes_pkcs1_v1_5_encrypt($plaintext, $padding == self::PADDING_PKCS15_COMPAT); - //case self::PADDING_OAEP: + switch ($this->encryptionMode) { + case CRYPT_RSA_ENCRYPTION_PKCS1: + $length = $this->k - 11; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); + } + return $ciphertext; + //case CRYPT_RSA_ENCRYPTION_OAEP: default: - return $this->_rsaes_oaep_encrypt($plaintext); + $length = $this->k - 2 * $this->hLen - 2; + if ($length <= 0) { + return false; + } + + $plaintext = str_split($plaintext, $length); + $ciphertext = ''; + foreach ($plaintext as $m) { + $ciphertext.= $this->_rsaes_oaep_encrypt($m); + } + return $ciphertext; } } /** * Decryption * - * @see self::encrypt() + * @see encrypt() * @access public - * @param string $plaintext - * @param int $padding optional - * @return bool|string + * @param String $plaintext + * @return String */ - function decrypt($ciphertext, $padding = self::PADDING_OAEP) + function decrypt($ciphertext) { - switch ($padding) { - case self::PADDING_NONE: - return $this->_raw_encrypt($ciphertext); - case self::PADDING_PKCS1: - return $this->_rsaes_pkcs1_v1_5_decrypt($ciphertext); - //case self::PADDING_OAEP: + if ($this->k <= 0) { + return false; + } + + $ciphertext = str_split($ciphertext, $this->k); + $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); + + $plaintext = ''; + + switch ($this->encryptionMode) { + case CRYPT_RSA_ENCRYPTION_PKCS1: + $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; + break; + //case CRYPT_RSA_ENCRYPTION_OAEP: default: - return $this->_rsaes_oaep_decrypt($ciphertext); + $decrypt = '_rsaes_oaep_decrypt'; + } + + foreach ($ciphertext as $c) { + $temp = $this->$decrypt($c); + if ($temp === false) { + return false; + } + $plaintext.= $temp; } + + return $plaintext; } /** * Create a signature * - * @see self::verify() + * @see verify() * @access public - * @param string $message - * @param int $padding optional - * @return string + * @param String $message + * @return String */ - function sign($message, $padding = self::PADDING_PSS) + function sign($message) { if (empty($this->modulus) || empty($this->exponent)) { return false; } - switch ($padding) { - case self::PADDING_PKCS1: - case self::PADDING_RELAXED_PKCS1: + switch ($this->signatureMode) { + case CRYPT_RSA_SIGNATURE_PKCS1: return $this->_rsassa_pkcs1_v1_5_sign($message); - //case self::PADDING_PSS: + //case CRYPT_RSA_SIGNATURE_PSS: default: return $this->_rsassa_pss_sign($message); } @@ -2224,27 +2717,51 @@ function sign($message, $padding = self::PADDING_PSS) /** * Verifies a signature * - * @see self::sign() + * @see sign() * @access public - * @param string $message - * @param string $signature - * @param int $padding optional - * @return bool + * @param String $message + * @param String $signature + * @return Boolean */ - function verify($message, $signature, $padding = self::PADDING_PSS) + function verify($message, $signature) { if (empty($this->modulus) || empty($this->exponent)) { return false; } - switch ($padding) { - case self::PADDING_RELAXED_PKCS1: - return $this->_rsassa_pkcs1_v1_5_relaxed_verify($message, $signature); - case self::PADDING_PKCS1: + switch ($this->signatureMode) { + case CRYPT_RSA_SIGNATURE_PKCS1: return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); - //case self::PADDING_PSS: + //case CRYPT_RSA_SIGNATURE_PSS: default: return $this->_rsassa_pss_verify($message, $signature); } } + + /** + * Extract raw BER from Base64 encoding + * + * @access private + * @param String $str + * @return String + */ + function _extractBER($str) + { + /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them + * above and beyond the ceritificate. + * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: + * + * Bag Attributes + * localKeyID: 01 00 00 00 + * subject=/O=organization/OU=org unit/CN=common name + * issuer=/O=organization/CN=common name + */ + $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#-+[^-]+-+#', '', $temp); + // remove new lines + $temp = str_replace(array("\r", "\n", ' '), '', $temp); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + return $temp != false ? $temp : $str; + } } diff --git a/src/phpseclib/Crypt/RSA/MSBLOB.php b/src/phpseclib/Crypt/RSA/MSBLOB.php deleted file mode 100755 index b99dc2f0..00000000 --- a/src/phpseclib/Crypt/RSA/MSBLOB.php +++ /dev/null @@ -1,224 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use phpseclib\Math\BigInteger; - -/** - * Microsoft BLOB Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class MSBLOB -{ - /**#@+ - * @access private - */ - /** - * Public/Private Key Pair - */ - const PRIVATEKEYBLOB = 0x7; - /** - * Public Key - */ - const PUBLICKEYBLOB = 0x6; - /** - * Public Key - */ - const PUBLICKEYBLOBEX = 0xA; - /** - * RSA public key exchange algorithm - */ - const CALG_RSA_KEYX = 0x0000A400; - /** - * RSA public key exchange algorithm - */ - const CALG_RSA_SIGN = 0x00002400; - /** - * Public Key - */ - const RSA1 = 0x31415352; - /** - * Private Key - */ - const RSA2 = 0x32415352; - /**#@-*/ - - /** - * Break a public or private key down into its constituent components - * - * @access public - * @param string $key - * @param string $password optional - * @return array - */ - static function load($key, $password = '') - { - if (!is_string($key)) { - return false; - } - - $key = Base64::decode($key); - - if (!is_string($key) || strlen($key) < 20) { - return false; - } - - // PUBLICKEYSTRUC publickeystruc - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx - extract(unpack('atype/aversion/vreserved/Valgo', self::_string_shift($key, 8))); - switch (ord($type)) { - case self::PUBLICKEYBLOB: - case self::PUBLICKEYBLOBEX: - $publickey = true; - break; - case self::PRIVATEKEYBLOB: - $publickey = false; - break; - default: - return false; - } - - $components = array('isPublicKey' => $publickey); - - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx - switch ($algo) { - case self::CALG_RSA_KEYX: - case self::CALG_RSA_SIGN: - break; - default: - return false; - } - - // RSAPUBKEY rsapubkey - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx - // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit - extract(unpack('Vmagic/Vbitlen/a4pubexp', self::_string_shift($key, 12))); - switch ($magic) { - case self::RSA2: - $components['isPublicKey'] = false; - case self::RSA1: - break; - default: - return false; - } - - $baseLength = $bitlen / 16; - if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) { - return false; - } - - $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256); - // BYTE modulus[rsapubkey.bitlen/8] - $components['modulus'] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 8)), 256); - - if ($publickey) { - return $components; - } - - $components['isPublicKey'] = false; - - // BYTE prime1[rsapubkey.bitlen/16] - $components['primes'] = array(1 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256)); - // BYTE prime2[rsapubkey.bitlen/16] - $components['primes'][] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256); - // BYTE exponent1[rsapubkey.bitlen/16] - $components['exponents'] = array(1 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256)); - // BYTE exponent2[rsapubkey.bitlen/16] - $components['exponents'][] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256); - // BYTE coefficient[rsapubkey.bitlen/16] - $components['coefficients'] = array(2 => new BigInteger(strrev(self::_string_shift($key, $bitlen / 16)), 256)); - if (isset($components['privateExponent'])) { - $components['publicExponent'] = $components['privateExponent']; - } - // BYTE privateExponent[rsapubkey.bitlen/8] - $components['privateExponent'] = new BigInteger(strrev(self::_string_shift($key, $bitlen / 8)), 256); - - return $components; - } - - /** - * 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, "\0"); - $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); - } - - /** - * 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) - { - $n = strrev($n->toBytes()); - $e = str_pad(strrev($e->toBytes()), 4, "\0"); - $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX); - $key.= pack('VVa*', self::RSA1, 8 * strlen($n), $e); - $key.= $n; - - return Base64::encode($key); - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - static function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; - } -} diff --git a/src/phpseclib/Crypt/RSA/OpenSSH.php b/src/phpseclib/Crypt/RSA/OpenSSH.php deleted file mode 100755 index 8cd53282..00000000 --- a/src/phpseclib/Crypt/RSA/OpenSSH.php +++ /dev/null @@ -1,141 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use phpseclib\Math\BigInteger; - -/** - * OpenSSH Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class OpenSSH -{ - /** - * Default comment - * - * @var string - * @access private - */ - static $comment = 'phpseclib-generated-key'; - - /** - * Sets the default comment - * - * @access public - * @param string $comment - */ - static function setComment($comment) - { - self::$comment = str_replace(array("\r", "\n"), '', $comment); - } - - /** - * Break a public or private key down into its constituent components - * - * @access public - * @param string $key - * @param string $password optional - * @return array - */ - static function load($key, $password = '') - { - if (!is_string($key)) { - return false; - } - - $parts = explode(' ', $key, 3); - - $key = isset($parts[1]) ? Base64::decode($parts[1]) : Base64::decode($parts[0]); - if ($key === false) { - return false; - } - - $comment = isset($parts[2]) ? $parts[2] : false; - - if (substr($key, 0, 11) != "\0\0\0\7ssh-rsa") { - return false; - } - self::_string_shift($key, 11); - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', self::_string_shift($key, 4))); - if (strlen($key) <= $length) { - return false; - } - $publicExponent = new BigInteger(self::_string_shift($key, $length), -256); - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', self::_string_shift($key, 4))); - if (strlen($key) != $length) { - return false; - } - $modulus = new BigInteger(self::_string_shift($key, $length), -256); - - return array( - 'isPublicKey' => true, - 'modulus' => $modulus, - 'publicExponent' => $publicExponent, - 'comment' => $comment - ); - } - - /** - * 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 : - // 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; - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - static function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; - } -} diff --git a/src/phpseclib/Crypt/RSA/PKCS.php b/src/phpseclib/Crypt/RSA/PKCS.php deleted file mode 100755 index b0ff2559..00000000 --- a/src/phpseclib/Crypt/RSA/PKCS.php +++ /dev/null @@ -1,487 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use ParagonIE\ConstantTime\Hex; -use phpseclib\Crypt\AES; -use phpseclib\Crypt\Base; -use phpseclib\Crypt\DES; -use phpseclib\Crypt\TripleDES; -use phpseclib\Math\BigInteger; - -/** - * PKCS Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -abstract class PKCS -{ - /**#@+ - * @access private - * @see \phpseclib\Crypt\RSA::createKey() - */ - /** - * ASN1 Integer - */ - const ASN1_INTEGER = 2; - /** - * ASN1 Bit String - */ - const ASN1_BITSTRING = 3; - /** - * ASN1 Octet String - */ - const ASN1_OCTETSTRING = 4; - /** - * ASN1 Object Identifier - */ - const ASN1_OBJECT = 6; - /** - * ASN1 Sequence (with the constucted bit set) - */ - const ASN1_SEQUENCE = 48; - /**#@-*/ - - /**#@+ - * @access private - */ - /** - * Auto-detect the format - */ - const MODE_ANY = 0; - /** - * Require base64-encoded PEM's be supplied - */ - const MODE_PEM = 1; - /** - * Require raw DER's be supplied - */ - const MODE_DER = 2; - /**#@-*/ - - /** - * Is the key a base-64 encoded PEM, DER or should it be auto-detected? - * - * @access private - * @param int - */ - static $format = self::MODE_ANY; - - /** - * Returns the mode constant corresponding to the mode string - * - * @access public - * @param string $mode - * @return int - * @throws \UnexpectedValueException if the block cipher mode is unsupported - */ - static function getEncryptionMode($mode) - { - switch ($mode) { - case 'CBC': - return Base::MODE_CBC; - case 'ECB': - return Base::MODE_ECB; - case 'CFB': - return Base::MODE_CFB; - case 'OFB': - return Base::MODE_OFB; - case 'CTR': - return Base::MODE_CTR; - } - throw new \UnexpectedValueException('Unsupported block cipher mode of operation'); - } - - /** - * Returns a cipher object corresponding to a string - * - * @access public - * @param string $algo - * @return string - * @throws \UnexpectedValueException if the encryption algorithm is unsupported - */ - static function getEncryptionObject($algo) - { - $modes = '(CBC|ECB|CFB|OFB|CTR)'; - switch (true) { - case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches): - $cipher = new AES(self::getEncryptionMode($matches[2])); - $cipher->setKeyLength($matches[1]); - return $cipher; - case preg_match("#^DES-EDE3-$modes$#", $algo, $matches): - return new TripleDES(self::getEncryptionMode($matches[1])); - case preg_match("#^DES-$modes$#", $algo, $matches): - return new DES(self::getEncryptionMode($matches[1])); - default: - throw new \UnexpectedValueException('Unsupported encryption algorithmn'); - } - } - - /** - * Generate a symmetric key for PKCS#1 keys - * - * @access public - * @param string $password - * @param string $iv - * @param int $length - * @return string - */ - static function generateSymmetricKey($password, $iv, $length) - { - $symkey = ''; - $iv = substr($iv, 0, 8); - while (strlen($symkey) < $length) { - $symkey.= md5($symkey . $password . $iv, true); - } - return substr($symkey, 0, $length); - } - - /** - * Break a public or private key down into its constituent components - * - * @access public - * @param string $key - * @param string $password optional - * @return array - */ - static function load($key, $password = '') - { - if (!is_string($key)) { - return false; - } - - $components = array('isPublicKey' => strpos($key, 'PUBLIC') !== false); - - /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is - "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to - protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding - two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: - - http://tools.ietf.org/html/rfc1421#section-4.6.1.1 - http://tools.ietf.org/html/rfc1421#section-4.6.1.3 - - DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. - DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation - function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's - own implementation. ie. the implementation *is* the standard and any bugs that may exist in that - implementation are part of the standard, as well. - - * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ - if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { - $iv = Hex::decode(trim($matches[2])); - // remove the Proc-Type / DEK-Info sections as they're no longer needed - $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); - $ciphertext = self::_extractBER($key); - if ($ciphertext === false) { - $ciphertext = $key; - } - $crypto = self::getEncryptionObject($matches[1]); - $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); - $crypto->setIV($iv); - $key = $crypto->decrypt($ciphertext); - if ($key === false) { - return false; - } - } else { - if (self::$format != self::MODE_DER) { - $decoded = self::_extractBER($key); - if ($decoded !== false) { - $key = $decoded; - } elseif (self::$format == self::MODE_PEM) { - return false; - } - } - } - - if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - if (self::_decodeLength($key) != strlen($key)) { - return false; - } - - $tag = ord(self::_string_shift($key)); - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 631 cons: SEQUENCE - 4:d=1 hl=2 l= 1 prim: INTEGER :00 - 7:d=1 hl=2 l= 13 cons: SEQUENCE - 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 20:d=2 hl=2 l= 0 prim: NULL - 22:d=1 hl=4 l= 609 prim: OCTET STRING - - ie. PKCS8 keys */ - - if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { - self::_string_shift($key, 3); - $tag = self::ASN1_SEQUENCE; - } - - if ($tag == self::ASN1_SEQUENCE) { - $temp = self::_string_shift($key, self::_decodeLength($key)); - if (ord(self::_string_shift($temp)) != self::ASN1_OBJECT) { - return false; - } - $length = self::_decodeLength($temp); - switch (self::_string_shift($temp, $length)) { - case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption - break; - case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC - /* - PBEParameter ::= SEQUENCE { - salt OCTET STRING (SIZE(8)), - iterationCount INTEGER } - */ - if (ord(self::_string_shift($temp)) != self::ASN1_SEQUENCE) { - return false; - } - if (self::_decodeLength($temp) != strlen($temp)) { - return false; - } - self::_string_shift($temp); // assume it's an octet string - $salt = self::_string_shift($temp, self::_decodeLength($temp)); - if (ord(self::_string_shift($temp)) != self::ASN1_INTEGER) { - return false; - } - self::_decodeLength($temp); - list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); - self::_string_shift($key); // assume it's an octet string - $length = self::_decodeLength($key); - if (strlen($key) != $length) { - return false; - } - - $crypto = new DES(DES::MODE_CBC); - $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount); - $key = $crypto->decrypt($key); - if ($key === false) { - return false; - } - return self::load($key); - default: - return false; - } - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 290 cons: SEQUENCE - 4:d=1 hl=2 l= 13 cons: SEQUENCE - 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 17:d=2 hl=2 l= 0 prim: NULL - 19:d=1 hl=4 l= 271 prim: BIT STRING */ - $tag = ord(self::_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag - self::_decodeLength($key); // skip over the BIT STRING / OCTET STRING length - // "The initial octet shall encode, as an unsigned binary integer wtih 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 (section 8.6.2.2) - if ($tag == self::ASN1_BITSTRING) { - self::_string_shift($key); - } - if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - if (self::_decodeLength($key) != strlen($key)) { - return false; - } - $tag = ord(self::_string_shift($key)); - } - if ($tag != self::ASN1_INTEGER) { - return false; - } - - $length = self::_decodeLength($key); - $temp = self::_string_shift($key, $length); - if (strlen($temp) != 1 || ord($temp) > 2) { - $components['modulus'] = new BigInteger($temp, 256); - self::_string_shift($key); // skip over self::ASN1_INTEGER - $length = self::_decodeLength($key); - $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256); - - return $components; - } - if (ord(self::_string_shift($key)) != self::ASN1_INTEGER) { - return false; - } - $length = self::_decodeLength($key); - $components['modulus'] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['publicExponent'] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['privateExponent'] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['primes'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256)); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['exponents'] = array(1 => new BigInteger(self::_string_shift($key, $length), 256)); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($key, $length), 256)); - - if (!empty($key)) { - if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - self::_decodeLength($key); - while (!empty($key)) { - if (ord(self::_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - self::_decodeLength($key); - $key = substr($key, 1); - $length = self::_decodeLength($key); - $components['primes'][] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['exponents'][] = new BigInteger(self::_string_shift($key, $length), 256); - self::_string_shift($key); - $length = self::_decodeLength($key); - $components['coefficients'][] = new BigInteger(self::_string_shift($key, $length), 256); - } - } - - return $components; - } - - /** - * Require base64-encoded PEM's be supplied - * - * @see self::load() - * @access public - */ - static function requirePEM() - { - self::$format = self::MODE_PEM; - } - - /** - * Require raw DER's be supplied - * - * @see self::load() - * @access public - */ - static function requireDER() - { - self::$format = self::MODE_DER; - } - - /** - * Accept any format and auto detect the format - * - * This is the default setting - * - * @see self::load() - * @access public - */ - static function requireAny() - { - self::$format = self::MODE_ANY; - } - - /** - * DER-decode the length - * - * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See - * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. - * - * @access private - * @param string $string - * @return int - */ - static function _decodeLength(&$string) - { - $length = ord(self::_string_shift($string)); - if ($length & 0x80) { // definite length, long form - $length&= 0x7F; - $temp = self::_string_shift($string, $length); - list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); - } - return $length; - } - - /** - * DER-encode the length - * - * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See - * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. - * - * @access private - * @param int $length - * @return string - */ - static function _encodeLength($length) - { - if ($length <= 0x7F) { - return chr($length); - } - - $temp = ltrim(pack('N', $length), chr(0)); - return pack('Ca*', 0x80 | strlen($temp), $temp); - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - static function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; - } - - /** - * Extract raw BER from Base64 encoding - * - * @access private - * @param string $str - * @return string - */ - static function _extractBER($str) - { - /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them - * above and beyond the ceritificate. - * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: - * - * Bag Attributes - * localKeyID: 01 00 00 00 - * subject=/O=organization/OU=org unit/CN=common name - * issuer=/O=organization/CN=common name - */ - $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); - // remove new lines - $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; - return $temp != false ? $temp : $str; - } -} diff --git a/src/phpseclib/Crypt/RSA/PKCS1.php b/src/phpseclib/Crypt/RSA/PKCS1.php deleted file mode 100755 index e5d6e1d6..00000000 --- a/src/phpseclib/Crypt/RSA/PKCS1.php +++ /dev/null @@ -1,174 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use ParagonIE\ConstantTime\Hex; -use phpseclib\Crypt\AES; -use phpseclib\Crypt\DES; -use phpseclib\Crypt\Random; -use phpseclib\Crypt\TripleDES; -use phpseclib\Math\BigInteger; - -/** - * PKCS#1 Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class PKCS1 extends PKCS -{ - /** - * Default encryption algorithm - * - * @var string - * @access private - */ - static $defaultEncryptionAlgorithm = 'DES-EDE3-CBC'; - - /** - * Sets the default encryption algorithm - * - * @access public - * @param string $algo - */ - static function setEncryptionAlgorithm($algo) - { - self::$defaultEncryptionAlgorithm = $algo; - } - - /** - * 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 = '') - { - $num_primes = count($primes); - $raw = array( - 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi - '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) - ); - - $components = array(); - foreach ($raw as $name => $value) { - $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value); - } - - $RSAPrivateKey = implode('', $components); - - if ($num_primes > 2) { - $OtherPrimeInfos = ''; - for ($i = 3; $i <= $num_primes; $i++) { - // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - // - // OtherPrimeInfo ::= SEQUENCE { - // prime INTEGER, -- ri - // exponent INTEGER, -- di - // coefficient INTEGER -- ti - // } - $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); - $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); - } - $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); - } - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - if (!empty($password) || is_string($password)) { - $cipher = self::getEncryptionObject(self::$defaultEncryptionAlgorithm); - $iv = Random::string($cipher->getBlockLength() >> 3); - $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3)); - $cipher->setIV($iv); - $iv = strtoupper(Hex::encode($iv)); - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - "Proc-Type: 4,ENCRYPTED\r\n" . - "DEK-Info: " . self::$defaultEncryptionAlgorithm . ",$iv\r\n" . - "\r\n" . - chunk_split(Base64::encode($cipher->encrypt($RSAPrivateKey)), 64) . - '-----END RSA PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - chunk_split(Base64::encode($RSAPrivateKey), 64) . - '-----END RSA PRIVATE KEY-----'; - } - - return $RSAPrivateKey; - } - - /** - * 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 : - // 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; - } -} diff --git a/src/phpseclib/Crypt/RSA/PKCS8.php b/src/phpseclib/Crypt/RSA/PKCS8.php deleted file mode 100755 index 787c89a5..00000000 --- a/src/phpseclib/Crypt/RSA/PKCS8.php +++ /dev/null @@ -1,209 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use phpseclib\Crypt\DES; -use phpseclib\Crypt\Random; -use phpseclib\Math\BigInteger; - -/** - * PKCS#8 Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class PKCS8 extends PKCS -{ - /** - * 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 = '') - { - $num_primes = count($primes); - $raw = array( - 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi - '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) - ); - - $components = array(); - foreach ($raw as $name => $value) { - $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($value)), $value); - } - - $RSAPrivateKey = implode('', $components); - - if ($num_primes > 2) { - $OtherPrimeInfos = ''; - for ($i = 3; $i <= $num_primes; $i++) { - // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - // - // OtherPrimeInfo ::= SEQUENCE { - // prime INTEGER, -- ri - // exponent INTEGER, -- di - // coefficient INTEGER -- ti - // } - $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, self::_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); - $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); - } - $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); - } - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPrivateKey = pack( - 'Ca*a*Ca*a*', - self::ASN1_INTEGER, - "\01\00", - $rsaOID, - 4, - self::_encodeLength(strlen($RSAPrivateKey)), - $RSAPrivateKey - ); - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - if (!empty($password) || is_string($password)) { - $salt = Random::string(8); - $iterationCount = 2048; - - $crypto = new DES(DES::MODE_CBC); - $crypto->setPassword($password, 'pbkdf1', 'md5', $salt, $iterationCount); - $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); - - $parameters = pack( - 'Ca*a*Ca*N', - self::ASN1_OCTETSTRING, - self::_encodeLength(strlen($salt)), - $salt, - self::ASN1_INTEGER, - self::_encodeLength(4), - $iterationCount - ); - $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; - - $encryptionAlgorithm = pack( - 'Ca*a*Ca*a*', - self::ASN1_OBJECT, - self::_encodeLength(strlen($pbeWithMD5AndDES_CBC)), - $pbeWithMD5AndDES_CBC, - self::ASN1_SEQUENCE, - self::_encodeLength(strlen($parameters)), - $parameters - ); - - $RSAPrivateKey = pack( - 'Ca*a*Ca*a*', - self::ASN1_SEQUENCE, - self::_encodeLength(strlen($encryptionAlgorithm)), - $encryptionAlgorithm, - self::ASN1_OCTETSTRING, - self::_encodeLength(strlen($RSAPrivateKey)), - $RSAPrivateKey - ); - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, self::_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . - chunk_split(Base64::encode($RSAPrivateKey), 64) . - '-----END ENCRYPTED PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . - chunk_split(Base64::encode($RSAPrivateKey), 64) . - '-----END PRIVATE KEY-----'; - } - - return $RSAPrivateKey; - } - - /** - * 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 : - // 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'] - ); - - // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. - $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . self::_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; - - $RSAPublicKey = pack( - 'Ca*a*', - self::ASN1_SEQUENCE, - self::_encodeLength(strlen($rsaOID . $RSAPublicKey)), - $rsaOID . $RSAPublicKey - ); - - $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(Base64::encode($RSAPublicKey), 64) . - '-----END PUBLIC KEY-----'; - - return $RSAPublicKey; - } -} diff --git a/src/phpseclib/Crypt/RSA/PuTTY.php b/src/phpseclib/Crypt/RSA/PuTTY.php deleted file mode 100755 index 04c4ae20..00000000 --- a/src/phpseclib/Crypt/RSA/PuTTY.php +++ /dev/null @@ -1,313 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use ParagonIE\ConstantTime\Hex; -use phpseclib\Crypt\AES; -use phpseclib\Crypt\Hash; -use phpseclib\Math\BigInteger; - -/** - * PuTTY Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class PuTTY -{ - /** - * Default comment - * - * @var string - * @access private - */ - static $comment = 'phpseclib-generated-key'; - - /** - * Sets the default comment - * - * @access public - * @param string $comment - */ - static function setComment($comment) - { - self::$comment = str_replace(array("\r", "\n"), '', $comment); - } - - /** - * Generate a symmetric key for PuTTY keys - * - * @access public - * @param string $password - * @param string $iv - * @param int $length - * @return string - */ - static function generateSymmetricKey($password, $length) - { - $symkey = ''; - $sequence = 0; - while (strlen($symkey) < $length) { - $temp = pack('Na*', $sequence++, $password); - $symkey.= Hex::decode(sha1($temp)); - } - return substr($symkey, 0, $length); - } - - /** - * Break a public or private key down into its constituent components - * - * @access public - * @param string $key - * @param string $password optional - * @return array - */ - static function load($key, $password = '') - { - if (!is_string($key)) { - return false; - } - - static $one; - if (!isset($one)) { - $one = new BigInteger(1); - } - - if (strpos($key, 'BEGIN SSH2 PUBLIC KEY')) { - $data = preg_split('#[\r\n]+#', $key); - $data = array_splice($data, 2, -1); - $data = implode('', $data); - - $components = OpenSSH::load($data); - if ($components === false) { - return false; - } - - if (!preg_match('#Comment: "(.+)"#', $key, $matches)) { - return false; - } - $components['comment'] = str_replace(array('\\\\', '\"'), array('\\', '"'), $matches[1]); - - return $components; - } - - $components = array('isPublicKey' => false); - $key = preg_split('#\r\n|\r|\n#', $key); - $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); - if ($type != 'ssh-rsa') { - return false; - } - $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); - $components['comment'] = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); - - $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); - $public = Base64::decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); - $public = substr($public, 11); - extract(unpack('Nlength', self::_string_shift($public, 4))); - $components['publicExponent'] = new BigInteger(self::_string_shift($public, $length), -256); - extract(unpack('Nlength', self::_string_shift($public, 4))); - $components['modulus'] = new BigInteger(self::_string_shift($public, $length), -256); - - $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); - $private = Base64::decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); - - switch ($encryption) { - case 'aes256-cbc': - $symkey = static::generateSymmetricKey($password, 32); - $crypto = new AES(AES::MODE_CBC); - } - - if ($encryption != 'none') { - $crypto->setKey($symkey); - $crypto->setIV(str_repeat("\0", $crypto->getBlockLength() >> 3)); - $crypto->disablePadding(); - $private = $crypto->decrypt($private); - if ($private === false) { - return false; - } - } - - extract(unpack('Nlength', self::_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['privateExponent'] = new BigInteger(self::_string_shift($private, $length), -256); - extract(unpack('Nlength', self::_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'] = array(1 => new BigInteger(self::_string_shift($private, $length), -256)); - extract(unpack('Nlength', self::_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'][] = new BigInteger(self::_string_shift($private, $length), -256); - - $temp = $components['primes'][1]->subtract($one); - $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); - $temp = $components['primes'][2]->subtract($one); - $components['exponents'][] = $components['publicExponent']->modInverse($temp); - - extract(unpack('Nlength', self::_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['coefficients'] = array(2 => new BigInteger(self::_string_shift($private, $length), -256)); - - return $components; - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - static function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; - } - - /** - * 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("\0", $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; - } - - /** - * 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) - { - $n = $n->toBytes(true); - $e = $e->toBytes(true); - - $key = pack( - 'Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($e), - $e, - strlen($n), - $n - ); - $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" . - 'Comment: "' . str_replace(array('\\', '"'), array('\\\\', '\"'), self::$comment) . "\"\r\n"; - chunk_split(Base64::encode($key), 64) . - '---- END SSH2 PUBLIC KEY ----'; - - return $key; - } -} diff --git a/src/phpseclib/Crypt/RSA/Raw.php b/src/phpseclib/Crypt/RSA/Raw.php deleted file mode 100755 index d3992521..00000000 --- a/src/phpseclib/Crypt/RSA/Raw.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use phpseclib\Math\BigInteger; - -/** - * Raw RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class Raw -{ - /** - * Break a public or private key down into its constituent components - * - * @access public - * @param string $key - * @param string $password optional - * @return array - */ - static function load($key, $password = '') - { - if (!is_array($key)) { - return false; - } - if (isset($key['isPublicKey']) && isset($key['modulus'])) { - if (isset($key['privateExponent']) || isset($key['publicExponent'])) { - if (!isset($key['primes'])) { - return $key; - } - if (isset($key['exponents']) && isset($key['coefficients']) && isset($key['publicExponent']) && isset($key['privateExponent'])) { - return $key; - } - } - } - $components = array('isPublicKey' => true); - switch (true) { - case isset($key['e']): - $components['publicExponent'] = $key['e']; - break; - case isset($key['exponent']): - $components['publicExponent'] = $key['exponent']; - break; - case isset($key['publicExponent']): - $components['publicExponent'] = $key['publicExponent']; - break; - case isset($key[0]): - $components['publicExponent'] = $key[0]; - } - switch (true) { - case isset($key['n']): - $components['modulus'] = $key['n']; - break; - case isset($key['modulo']): - $components['modulus'] = $key['modulo']; - break; - case isset($key['modulus']): - $components['modulus'] = $key['modulus']; - break; - case isset($key[1]): - $components['modulus'] = $key[1]; - } - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; - } - - /** - * 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 array('e' => clone $e, 'n' => clone $n); - } -} diff --git a/src/phpseclib/Crypt/RSA/XML.php b/src/phpseclib/Crypt/RSA/XML.php deleted file mode 100755 index a257033b..00000000 --- a/src/phpseclib/Crypt/RSA/XML.php +++ /dev/null @@ -1,147 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt\RSA; - -use ParagonIE\ConstantTime\Base64; -use phpseclib\Math\BigInteger; - -/** - * XML Formatted RSA Key Handler - * - * @package RSA - * @author Jim Wigginton - * @access public - */ -class XML -{ - /** - * Break a public or private key down into its constituent components - * - * @access public - * @param string $key - * @param string $password optional - * @return array - */ - static function load($key, $password = '') - { - if (!is_string($key)) { - return false; - } - - $components = array( - 'isPublicKey' => false, - 'primes' => array(), - 'exponents' => array(), - 'coefficients' => array() - ); - - $use_errors = libxml_use_internal_errors(true); - - $dom = new \DOMDocument(); - if (!$dom->loadXML('' . $key . '')) { - return false; - } - $xpath = new \DOMXPath($dom); - $keys = array('modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd'); - foreach ($keys as $key) { - // $dom->getElementsByTagName($key) is case-sensitive - $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']"); - if (!$temp->length) { - continue; - } - $value = new BigInteger(Base64::decode($temp->item(0)->nodeValue), 256); - switch ($key) { - case 'modulus': - $components['modulus'] = $value; - break; - case 'exponent': - $components['publicExponent'] = $value; - break; - case 'p': - $components['primes'][1] = $value; - break; - case 'q': - $components['primes'][2] = $value; - break; - case 'dp': - $components['exponents'][1] = $value; - break; - case 'dq': - $components['exponents'][2] = $value; - break; - case 'inverseq': - $components['coefficients'][2] = $value; - break; - case 'd': - $components['privateExponent'] = $value; - } - } - - libxml_use_internal_errors($use_errors); - - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; - } - - /** - * 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; - } - return "\r\n" . - ' ' . Base64::encode($n->toBytes()) . "\r\n" . - ' ' . Base64::encode($e->toBytes()) . "\r\n" . - '

' . Base64::encode($primes[1]->toBytes()) . "

\r\n" . - ' ' . Base64::encode($primes[2]->toBytes()) . "\r\n" . - ' ' . Base64::encode($exponents[1]->toBytes()) . "\r\n" . - ' ' . Base64::encode($exponents[2]->toBytes()) . "\r\n" . - ' ' . Base64::encode($coefficients[2]->toBytes()) . "\r\n" . - ' ' . Base64::encode($d->toBytes()) . "\r\n" . - '
'; - } - - /** - * 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 "\r\n" . - ' ' . Base64::encode($n->toBytes()) . "\r\n" . - ' ' . Base64::encode($e->toBytes()) . "\r\n" . - ''; - } -} diff --git a/src/phpseclib/Crypt/Random.php b/src/phpseclib/Crypt/Random.php index 6bf61468..5df68f55 100755 --- a/src/phpseclib/Crypt/Random.php +++ b/src/phpseclib/Crypt/Random.php @@ -3,21 +3,39 @@ /** * Random Number Generator * - * PHP version 5 + * PHP versions 4 and 5 * * Here's a short example of how to use this library: * * * * + * LICENSE: 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. + * * @category Crypt * @package Random * @author Jim Wigginton - * @copyright 2007 Jim Wigginton + * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ @@ -25,148 +43,182 @@ namespace phpseclib\Crypt; /** - * Pure-PHP Random Number Generator + * "Is Windows" test + * + * @access private + */ +@define('CRYPT_RANDOM_IS_WINDOWS', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); + + +/** + * Generate a random string. + * + * Although microoptimizations are generally discouraged as they impair readability this function is ripe with + * microoptimizations because this function has the potential of being called a huge number of times. + * eg. for RSA key generation. * - * @package Random - * @author Jim Wigginton - * @access public + * @param Integer $length + * @return String + * @access public */ -class Random + +class Random{ + +} + +if(!function_exists("phpseclib\\Crypt\\crypt_random_string")){ +function crypt_random_string($length) { - /** - * Generate a random string. - * - * Although microoptimizations are generally discouraged as they impair readability this function is ripe with - * microoptimizations because this function has the potential of being called a huge number of times. - * eg. for RSA key generation. - * - * @param int $length - * @throws \RuntimeException if a symmetric cipher is needed but not loaded - * @return string - */ - static function string($length) - { - try { - return \random_bytes($length); - } catch (\Exception $e) { - // random_compat will throw an Exception, which in PHP 5 does not implement Throwable - } catch (\Throwable $e) { - // If a sufficient source of randomness is unavailable, random_bytes() will throw an - // object that implements the Throwable interface (Exception, TypeError, Error). - // We don't actually need to do anything here. The string() method should just continue - // as normal. Note, however, that if we don't have a sufficient source of randomness for - // random_bytes(), most of the other calls here will fail too, so we'll end up using - // the PHP implementation. + if (CRYPT_RANDOM_IS_WINDOWS) { + // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. + // ie. class_alias is a function that was introduced in PHP 5.3 + if (function_exists('mcrypt_create_iv') && function_exists('class_alias')) { + return mcrypt_create_iv($length); } - // at this point we have no choice but to use a pure-PHP CSPRNG - - // cascade entropy across multiple PHP instances by fixing the session and collecting all - // environmental variables, including the previous session data and the current session - // data. + // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was, + // to quote , "possible blocking behavior". as of 5.3.4 + // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both + // call php_win32_get_random_bytes(): // - // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) - // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but - // PHP isn't low level to be able to use those as sources and on a web server there's not likely - // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use - // however, a ton of people visiting the website. obviously you don't want to base your seeding - // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled - // by the user and (2) this isn't just looking at the data sent by the current user - it's based - // on the data sent by all users. one user requests the page and a hash of their info is saved. - // another user visits the page and the serialization of their data is utilized along with the - // server envirnment stuff and a hash of the previous http request data (which itself utilizes - // a hash of the session data before that). certainly an attacker should be assumed to have - // full control over his own http requests. he, however, is not going to have control over - // everyone's http requests. - static $crypto = false, $v; - if ($crypto === false) { - // save old session data - $old_session_id = session_id(); - $old_use_cookies = ini_get('session.use_cookies'); - $old_session_cache_limiter = session_cache_limiter(); - $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; - if ($old_session_id != '') { - session_write_close(); - } + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008 + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392 + // + // php_win32_get_random_bytes() is defined thusly: + // + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80 + // + // we're calling it, all the same, in the off chance that the mcrypt extension is not available + if (function_exists('openssl_random_pseudo_bytes') && version_compare(PHP_VERSION, '5.3.4', '>=')) { + return openssl_random_pseudo_bytes($length); + } + } else { + // method 1. the fastest + if (function_exists('openssl_random_pseudo_bytes')) { + return openssl_random_pseudo_bytes($length); + } + // method 2 + static $fp = true; + if ($fp === true) { + // warning's will be output unles the error suppression operator is used. errors such as + // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc. + $fp = @fopen('/dev/urandom', 'rb'); + } + if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() + return fread($fp, $length); + } + // method 3. pretty much does the same thing as method 2 per the following url: + // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391 + // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're + // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir + // restrictions or some such + if (function_exists('mcrypt_create_iv')) { + return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + } + } + // at this point we have no choice but to use a pure-PHP CSPRNG - session_id(1); - ini_set('session.use_cookies', 0); - session_cache_limiter(''); - session_start(); + // cascade entropy across multiple PHP instances by fixing the session and collecting all + // environmental variables, including the previous session data and the current session + // data. + // + // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively) + // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but + // PHP isn't low level to be able to use those as sources and on a web server there's not likely + // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use + // however. a ton of people visiting the website. obviously you don't want to base your seeding + // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled + // by the user and (2) this isn't just looking at the data sent by the current user - it's based + // on the data sent by all users. one user requests the page and a hash of their info is saved. + // another user visits the page and the serialization of their data is utilized along with the + // server envirnment stuff and a hash of the previous http request data (which itself utilizes + // a hash of the session data before that). certainly an attacker should be assumed to have + // full control over his own http requests. he, however, is not going to have control over + // everyone's http requests. + static $crypto = false, $v; + if ($crypto === false) { + // save old session data + $old_session_id = session_id(); + $old_use_cookies = ini_get('session.use_cookies'); + $old_session_cache_limiter = session_cache_limiter(); + $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; + if ($old_session_id != '') { + session_write_close(); + } - $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') . - (isset($_POST) ? self::safe_serialize($_POST) : '') . - (isset($_GET) ? self::safe_serialize($_GET) : '') . - (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') . - self::safe_serialize($GLOBALS) . - self::safe_serialize($_SESSION) . - self::safe_serialize($_OLD_SESSION); - $v = $seed = $_SESSION['seed'] = sha1($v, true); - if (!isset($_SESSION['count'])) { - $_SESSION['count'] = 0; - } - $_SESSION['count']++; + session_id(1); + ini_set('session.use_cookies', 0); + session_cache_limiter(''); + session_start(); - session_write_close(); + $v = $seed = $_SESSION['seed'] = pack('H*', sha1( + serialize($_SERVER) . + serialize($_POST) . + serialize($_GET) . + serialize($_COOKIE) . + serialize($GLOBALS) . + serialize($_SESSION) . + serialize($_OLD_SESSION) + )); + if (!isset($_SESSION['count'])) { + $_SESSION['count'] = 0; + } + $_SESSION['count']++; - // restore old session data - if ($old_session_id != '') { - session_id($old_session_id); - session_start(); - ini_set('session.use_cookies', $old_use_cookies); - session_cache_limiter($old_session_cache_limiter); + session_write_close(); + + // restore old session data + if ($old_session_id != '') { + session_id($old_session_id); + session_start(); + ini_set('session.use_cookies', $old_use_cookies); + session_cache_limiter($old_session_cache_limiter); + } else { + if ($_OLD_SESSION !== false) { + $_SESSION = $_OLD_SESSION; + unset($_OLD_SESSION); } else { - if ($_OLD_SESSION !== false) { - $_SESSION = $_OLD_SESSION; - unset($_OLD_SESSION); - } else { - unset($_SESSION); - } + unset($_SESSION); } + } - // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. - // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. - // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the - // original hash and the current hash. we'll be emulating that. for more info see the following URL: - // - // http://tools.ietf.org/html/rfc4253#section-7.2 - // - // see the is_string($crypto) part for an example of how to expand the keys - $key = sha1($seed . 'A', true); - $iv = sha1($seed . 'C', true); - - // ciphers are used as per the nist.gov link below. also, see this link: - // - // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives - switch (true) { - case class_exists('\phpseclib\Crypt\AES'): - $crypto = new AES(Base::MODE_CTR); - break; - case class_exists('\phpseclib\Crypt\Twofish'): - $crypto = new Twofish(Base::MODE_CTR); - break; - case class_exists('\phpseclib\Crypt\Blowfish'): - $crypto = new Blowfish(Base::MODE_CTR); - break; - case class_exists('\phpseclib\Crypt\TripleDES'): - $crypto = new TripleDES(Base::MODE_CTR); - break; - case class_exists('\phpseclib\Crypt\DES'): - $crypto = new DES(Base::MODE_CTR); - break; - case class_exists('\phpseclib\Crypt\RC4'): - $crypto = new RC4(); - break; - default: - throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded'); - } + // in SSH2 a shared secret and an exchange hash are generated through the key exchange process. + // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C. + // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the + // original hash and the current hash. we'll be emulating that. for more info see the following URL: + // + // http://tools.ietf.org/html/rfc4253#section-7.2 + // + // see the is_string($crypto) part for an example of how to expand the keys + $key = pack('H*', sha1($seed . 'A')); + $iv = pack('H*', sha1($seed . 'C')); - $crypto->setKey(substr($key, 0, $crypto->getKeyLength() >> 3)); - $crypto->setIV(substr($iv, 0, $crypto->getBlockLength() >> 3)); - $crypto->enableContinuousBuffer(); + // ciphers are used as per the nist.gov link below. also, see this link: + // + // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives + switch (true) { + case class_exists('phpseclib\\Crypt\\AES'): + $crypto = new AES(CRYPT_AES_MODE_CTR); + break; + case class_exists('phpseclib\\Crypt\\TripleDES'): + $crypto = new TripleDES(CRYPT_DES_MODE_CTR); + break; + case class_exists('phpseclib\\Crypt\\DES'): + $crypto = new DES(CRYPT_DES_MODE_CTR); + break; + case class_exists('phpseclib\\Crypt\\RC4'): + $crypto = new RC4(); + break; + default: + $crypto = $seed; + return crypt_random_string($length); } - //return $crypto->encrypt(str_repeat("\0", $length)); + $crypto->setKey($key); + $crypto->setIV($iv); + $crypto->enableContinuousBuffer(); + } + if (is_string($crypto)) { // the following is based off of ANSI X9.31: // // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf @@ -175,45 +227,31 @@ static function string($length) // // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c // (do a search for "ANS X9.31 A.2.4") + // + // ANSI X9.31 recommends ciphers be used and phpseclib does use them if they're available (see + // later on in the code) but if they're not we'll use sha1 $result = ''; - while (strlen($result) < $length) { - $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21 - $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20 - $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20 + while (strlen($result) < $length) { // each loop adds 20 bytes + // microtime() isn't packed as "densely" as it could be but then neither is that the idea. + // the idea is simply to ensure that each "block" has a unique element to it. + $i = pack('H*', sha1(microtime())); + $r = pack('H*', sha1($i ^ $v)); + $v = pack('H*', sha1($r ^ $i)); $result.= $r; } return substr($result, 0, $length); } - /** - * Safely serialize variables - * - * If a class has a private __sleep() it'll emit a warning - * - * @param mixed $arr - * @access public - */ - static function safe_serialize(&$arr) - { - if (is_object($arr)) { - return ''; - } - if (!is_array($arr)) { - return serialize($arr); - } - // prevent circular array recursion - if (isset($arr['__phpseclib_marker'])) { - return ''; - } - $safearr = array(); - $arr['__phpseclib_marker'] = true; - foreach (array_keys($arr) as $key) { - // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage - if ($key !== '__phpseclib_marker') { - $safearr[$key] = self::safe_serialize($arr[$key]); - } - } - unset($arr['__phpseclib_marker']); - return serialize($safearr); + //return $crypto->encrypt(str_repeat("\0", $length)); + + $result = ''; + while (strlen($result) < $length) { + $i = $crypto->encrypt(microtime()); + $r = $crypto->encrypt($i ^ $v); + $v = $crypto->encrypt($r ^ $i); + $result.= $r; } + return substr($result, 0, $length); +} + } diff --git a/src/phpseclib/Crypt/Rijndael.php b/src/phpseclib/Crypt/Rijndael.php index c98f02e2..fc1bbcfc 100755 --- a/src/phpseclib/Crypt/Rijndael.php +++ b/src/phpseclib/Crypt/Rijndael.php @@ -5,13 +5,13 @@ * * Uses mcrypt, if available/possible, and an internal implementation, otherwise. * - * PHP version 5 + * PHP versions 4 and 5 * - * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If - * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from - * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's + * If {@link Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If + * {@link Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from + * {@link Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until - * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated. + * {@link Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated. * * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example, * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256. @@ -28,9 +28,9 @@ * Here's a short example of how to use this library: * * setKey('abcdefghijklmnop'); * @@ -44,37 +44,128 @@ * ?> * * + * LICENSE: 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. + * * @category Crypt * @package Rijndael * @author Jim Wigginton - * @copyright 2008 Jim Wigginton + * @copyright MMVIII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/**#@+ + * @access public + * @see Rijndael::encrypt() + * @see Rijndael::decrypt() + */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +@define('CRYPT_RIJNDAEL_MODE_CTR', CRYPT_MODE_CTR); +/** + * Encrypt / decrypt using the Electronic Code Book mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29 + */ +@define('CRYPT_RIJNDAEL_MODE_ECB', CRYPT_MODE_ECB); +/** + * Encrypt / decrypt using the Code Book Chaining mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 + */ +@define('CRYPT_RIJNDAEL_MODE_CBC', CRYPT_MODE_CBC); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 + */ +@define('CRYPT_RIJNDAEL_MODE_CFB', CRYPT_MODE_CFB); +/** + * Encrypt / decrypt using the Cipher Feedback mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29 + */ +@define('CRYPT_RIJNDAEL_MODE_OFB', CRYPT_MODE_OFB); +/**#@-*/ + +/**#@+ + * @access private + * @see Rijndael::Rijndael() + */ +/** + * Toggles the internal implementation + */ +@define('CRYPT_RIJNDAEL_MODE_INTERNAL', CRYPT_MODE_INTERNAL); +/** + * Toggles the mcrypt implementation + */ +@define('CRYPT_RIJNDAEL_MODE_MCRYPT', CRYPT_MODE_MCRYPT); +/**#@-*/ + /** * Pure-PHP implementation of Rijndael. * * @package Rijndael * @author Jim Wigginton + * @version 0.1.0 * @access public */ class Rijndael extends Base { + /** + * The default password key_size used by setPassword() + * + * @see Base::password_key_size + * @see Base::setPassword() + * @var Integer + * @access private + */ + var $password_key_size = 16; + + /** + * The namespace used by the cipher for its constants. + * + * @see Base::const_namespace + * @var String + * @access private + */ + var $const_namespace = 'RIJNDAEL'; + /** * The mcrypt specific name of the cipher * - * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not. - * \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is useable - * or not for the current $block_size/$key_length. - * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly. + * Mcrypt is useable for 128/192/256-bit $block_size/$key_size. For 160/224 not. + * Rijndael determines automatically whether mcrypt is useable + * or not for the current $block_size/$key_size. + * In case of, $cipher_name_mcrypt will be set dynamicaly at run time accordingly. * - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @see \phpseclib\Crypt\Base::engine - * @see self::isValidEngine() - * @var string + * @see Base::cipher_name_mcrypt + * @see Base::engine + * @see _setupEngine() + * @var String * @access private */ var $cipher_name_mcrypt = 'rijndael-128'; @@ -82,18 +173,27 @@ class Rijndael extends Base /** * The default salt used by setPassword() * - * @see \phpseclib\Crypt\Base::password_default_salt - * @see \phpseclib\Crypt\Base::setPassword() - * @var string + * @see Base::password_default_salt + * @see Base::setPassword() + * @var String * @access private */ var $password_default_salt = 'phpseclib'; + /** + * Has the key length explicitly been set or should it be derived from the key, itself? + * + * @see setKeyLength() + * @var Boolean + * @access private + */ + var $explicit_key_length = false; + /** * The Key Schedule * - * @see self::_setup() - * @var array + * @see _setup() + * @var Array * @access private */ var $w; @@ -101,8 +201,8 @@ class Rijndael extends Base /** * The Inverse Key Schedule * - * @see self::_setup() - * @var array + * @see _setup() + * @var Array * @access private */ var $dw; @@ -110,34 +210,35 @@ class Rijndael extends Base /** * The Block Length divided by 32 * - * @see self::setBlockLength() - * @var int + * @see setBlockLength() + * @var Integer * @access private * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once. + * */ var $Nb = 4; /** - * The Key Length (in bytes) + * The Key Length * - * @see self::setKeyLength() - * @var int + * @see setKeyLength() + * @var Integer * @access private * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk - * because the encryption / decryption / key schedule creation requires this number and not $key_length. We could - * derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu + * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could + * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu * of that, we'll just precompute it once. */ - var $key_length = 16; + var $key_size = 16; /** * The Key Length divided by 32 * - * @see self::setKeyLength() - * @var int + * @see setKeyLength() + * @var Integer * @access private * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4 */ @@ -146,7 +247,7 @@ class Rijndael extends Base /** * The Number of Rounds * - * @var int + * @var Integer * @access private * @internal The max value is 14, the min value is 10. */ @@ -155,7 +256,7 @@ class Rijndael extends Base /** * Shift offsets * - * @var array + * @var Array * @access private */ var $c; @@ -163,31 +264,482 @@ class Rijndael extends Base /** * Holds the last used key- and block_size information * - * @var array + * @var Array * @access private */ var $kl; + /** + * Precomputed mixColumns table + * + * According to (section 5.2.1), + * precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so + * those are the names we'll use. + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $t0 = array( + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554, + 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, + 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B, + 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, + 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5, + 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, + 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497, + 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, + 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594, + 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, + 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D, + 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, + 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883, + 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, + 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B, + 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, + 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651, + 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, + 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9, + 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, + 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8, + 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A + ); + + /** + * Precomputed mixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $t1 = array( + 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5, + 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676, + 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0, + 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0, + 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC, + 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515, + 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A, + 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575, + 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0, + 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484, + 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B, + 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF, + 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585, + 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8, + 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5, + 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2, + 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717, + 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373, + 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888, + 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB, + 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C, + 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979, + 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9, + 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808, + 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6, + 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A, + 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E, + 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E, + 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494, + 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF, + 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868, + 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616 + ); + + /** + * Precomputed mixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $t2 = array( + 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5, + 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76, + 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0, + 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0, + 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC, + 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15, + 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A, + 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75, + 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0, + 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384, + 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B, + 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF, + 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185, + 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8, + 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5, + 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2, + 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17, + 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673, + 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88, + 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB, + 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C, + 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279, + 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9, + 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008, + 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6, + 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A, + 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E, + 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E, + 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394, + 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF, + 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068, + 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16 + ); + + /** + * Precomputed mixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $t3 = array( + 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, + 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, + 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, + 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, + 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, + 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, + 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, + 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, + 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, + 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, + 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, + 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, + 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, + 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, + 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, + 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, + 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, + 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, + 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, + 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, + 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, + 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, + 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, + 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, + 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, + 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, + 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, + 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, + 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, + 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, + 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, + 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C + ); + + /** + * Precomputed invMixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $dt0 = array( + 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, 0xACFA58AB, 0x4BE30393, + 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, + 0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, + 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, 0x49E06929, 0x8EC9C844, + 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, + 0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, + 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, 0xE31F8F57, 0x6655AB2A, + 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, + 0x8ACF1C2B, 0xA779B492, 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A, + 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051, + 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, + 0x1998FB24, 0xD6BDE997, 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB, + 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E, + 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, + 0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, + 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8, + 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, + 0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, + 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, 0x0D8652EC, 0x77C1E3D0, + 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, + 0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, + 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, 0x82C3AFF5, + 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, + 0xCD267809, 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6, + 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0, + 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, + 0x764DD68D, 0x43EFB04D, 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F, + 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713, + 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, + 0x9CD2DF59, 0x55F2733F, 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, + 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, 0x283C498B, 0xFF0D9541, + 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742 + ); + + /** + * Precomputed invMixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $dt1 = array( + 0x5051F4A7, 0x537E4165, 0xC31A17A4, 0x963A275E, 0xCB3BAB6B, 0xF11F9D45, 0xABACFA58, 0x934BE303, + 0x552030FA, 0xF6AD766D, 0x9188CC76, 0x25F5024C, 0xFC4FE5D7, 0xD7C52ACB, 0x80263544, 0x8FB562A3, + 0x49DEB15A, 0x6725BA1B, 0x9845EA0E, 0xE15DFEC0, 0x02C32F75, 0x12814CF0, 0xA38D4697, 0xC66BD3F9, + 0xE7038F5F, 0x9515929C, 0xEBBF6D7A, 0xDA955259, 0x2DD4BE83, 0xD3587421, 0x2949E069, 0x448EC9C8, + 0x6A75C289, 0x78F48E79, 0x6B99583E, 0xDD27B971, 0xB6BEE14F, 0x17F088AD, 0x66C920AC, 0xB47DCE3A, + 0x1863DF4A, 0x82E51A31, 0x60975133, 0x4562537F, 0xE0B16477, 0x84BB6BAE, 0x1CFE81A0, 0x94F9082B, + 0x58704868, 0x198F45FD, 0x8794DE6C, 0xB7527BF8, 0x23AB73D3, 0xE2724B02, 0x57E31F8F, 0x2A6655AB, + 0x07B2EB28, 0x032FB5C2, 0x9A86C57B, 0xA5D33708, 0xF2302887, 0xB223BFA5, 0xBA02036A, 0x5CED1682, + 0x2B8ACF1C, 0x92A779B4, 0xF0F307F2, 0xA14E69E2, 0xCD65DAF4, 0xD50605BE, 0x1FD13462, 0x8AC4A6FE, + 0x9D342E53, 0xA0A2F355, 0x32058AE1, 0x75A4F6EB, 0x390B83EC, 0xAA4060EF, 0x065E719F, 0x51BD6E10, + 0xF93E218A, 0x3D96DD06, 0xAEDD3E05, 0x464DE6BD, 0xB591548D, 0x0571C45D, 0x6F0406D4, 0xFF605015, + 0x241998FB, 0x97D6BDE9, 0xCC894043, 0x7767D99E, 0xBDB0E842, 0x8807898B, 0x38E7195B, 0xDB79C8EE, + 0x47A17C0A, 0xE97C420F, 0xC9F8841E, 0x00000000, 0x83098086, 0x48322BED, 0xAC1E1170, 0x4E6C5A72, + 0xFBFD0EFF, 0x560F8538, 0x1E3DAED5, 0x27362D39, 0x640A0FD9, 0x21685CA6, 0xD19B5B54, 0x3A24362E, + 0xB10C0A67, 0x0F9357E7, 0xD2B4EE96, 0x9E1B9B91, 0x4F80C0C5, 0xA261DC20, 0x695A774B, 0x161C121A, + 0x0AE293BA, 0xE5C0A02A, 0x433C22E0, 0x1D121B17, 0x0B0E090D, 0xADF28BC7, 0xB92DB6A8, 0xC8141EA9, + 0x8557F119, 0x4CAF7507, 0xBBEE99DD, 0xFDA37F60, 0x9FF70126, 0xBC5C72F5, 0xC544663B, 0x345BFB7E, + 0x768B4329, 0xDCCB23C6, 0x68B6EDFC, 0x63B8E4F1, 0xCAD731DC, 0x10426385, 0x40139722, 0x2084C611, + 0x7D854A24, 0xF8D2BB3D, 0x11AEF932, 0x6DC729A1, 0x4B1D9E2F, 0xF3DCB230, 0xEC0D8652, 0xD077C1E3, + 0x6C2BB316, 0x99A970B9, 0xFA119448, 0x2247E964, 0xC4A8FC8C, 0x1AA0F03F, 0xD8567D2C, 0xEF223390, + 0xC787494E, 0xC1D938D1, 0xFE8CCAA2, 0x3698D40B, 0xCFA6F581, 0x28A57ADE, 0x26DAB78E, 0xA43FADBF, + 0xE42C3A9D, 0x0D507892, 0x9B6A5FCC, 0x62547E46, 0xC2F68D13, 0xE890D8B8, 0x5E2E39F7, 0xF582C3AF, + 0xBE9F5D80, 0x7C69D093, 0xA96FD52D, 0xB3CF2512, 0x3BC8AC99, 0xA710187D, 0x6EE89C63, 0x7BDB3BBB, + 0x09CD2678, 0xF46E5918, 0x01EC9AB7, 0xA8834F9A, 0x65E6956E, 0x7EAAFFE6, 0x0821BCCF, 0xE6EF15E8, + 0xD9BAE79B, 0xCE4A6F36, 0xD4EA9F09, 0xD629B07C, 0xAF31A4B2, 0x312A3F23, 0x30C6A594, 0xC035A266, + 0x37744EBC, 0xA6FC82CA, 0xB0E090D0, 0x1533A7D8, 0x4AF10498, 0xF741ECDA, 0x0E7FCD50, 0x2F1791F6, + 0x8D764DD6, 0x4D43EFB0, 0x54CCAA4D, 0xDFE49604, 0xE39ED1B5, 0x1B4C6A88, 0xB8C12C1F, 0x7F466551, + 0x049D5EEA, 0x5D018C35, 0x73FA8774, 0x2EFB0B41, 0x5AB3671D, 0x5292DBD2, 0x33E91056, 0x136DD647, + 0x8C9AD761, 0x7A37A10C, 0x8E59F814, 0x89EB133C, 0xEECEA927, 0x35B761C9, 0xEDE11CE5, 0x3C7A47B1, + 0x599CD2DF, 0x3F55F273, 0x791814CE, 0xBF73C737, 0xEA53F7CD, 0x5B5FFDAA, 0x14DF3D6F, 0x867844DB, + 0x81CAAFF3, 0x3EB968C4, 0x2C382434, 0x5FC2A340, 0x72161DC3, 0x0CBCE225, 0x8B283C49, 0x41FF0D95, + 0x7139A801, 0xDE080CB3, 0x9CD8B4E4, 0x906456C1, 0x617BCB84, 0x70D532B6, 0x74486C5C, 0x42D0B857 + ); + + /** + * Precomputed invMixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $dt2 = array( + 0xA75051F4, 0x65537E41, 0xA4C31A17, 0x5E963A27, 0x6BCB3BAB, 0x45F11F9D, 0x58ABACFA, 0x03934BE3, + 0xFA552030, 0x6DF6AD76, 0x769188CC, 0x4C25F502, 0xD7FC4FE5, 0xCBD7C52A, 0x44802635, 0xA38FB562, + 0x5A49DEB1, 0x1B6725BA, 0x0E9845EA, 0xC0E15DFE, 0x7502C32F, 0xF012814C, 0x97A38D46, 0xF9C66BD3, + 0x5FE7038F, 0x9C951592, 0x7AEBBF6D, 0x59DA9552, 0x832DD4BE, 0x21D35874, 0x692949E0, 0xC8448EC9, + 0x896A75C2, 0x7978F48E, 0x3E6B9958, 0x71DD27B9, 0x4FB6BEE1, 0xAD17F088, 0xAC66C920, 0x3AB47DCE, + 0x4A1863DF, 0x3182E51A, 0x33609751, 0x7F456253, 0x77E0B164, 0xAE84BB6B, 0xA01CFE81, 0x2B94F908, + 0x68587048, 0xFD198F45, 0x6C8794DE, 0xF8B7527B, 0xD323AB73, 0x02E2724B, 0x8F57E31F, 0xAB2A6655, + 0x2807B2EB, 0xC2032FB5, 0x7B9A86C5, 0x08A5D337, 0x87F23028, 0xA5B223BF, 0x6ABA0203, 0x825CED16, + 0x1C2B8ACF, 0xB492A779, 0xF2F0F307, 0xE2A14E69, 0xF4CD65DA, 0xBED50605, 0x621FD134, 0xFE8AC4A6, + 0x539D342E, 0x55A0A2F3, 0xE132058A, 0xEB75A4F6, 0xEC390B83, 0xEFAA4060, 0x9F065E71, 0x1051BD6E, + 0x8AF93E21, 0x063D96DD, 0x05AEDD3E, 0xBD464DE6, 0x8DB59154, 0x5D0571C4, 0xD46F0406, 0x15FF6050, + 0xFB241998, 0xE997D6BD, 0x43CC8940, 0x9E7767D9, 0x42BDB0E8, 0x8B880789, 0x5B38E719, 0xEEDB79C8, + 0x0A47A17C, 0x0FE97C42, 0x1EC9F884, 0x00000000, 0x86830980, 0xED48322B, 0x70AC1E11, 0x724E6C5A, + 0xFFFBFD0E, 0x38560F85, 0xD51E3DAE, 0x3927362D, 0xD9640A0F, 0xA621685C, 0x54D19B5B, 0x2E3A2436, + 0x67B10C0A, 0xE70F9357, 0x96D2B4EE, 0x919E1B9B, 0xC54F80C0, 0x20A261DC, 0x4B695A77, 0x1A161C12, + 0xBA0AE293, 0x2AE5C0A0, 0xE0433C22, 0x171D121B, 0x0D0B0E09, 0xC7ADF28B, 0xA8B92DB6, 0xA9C8141E, + 0x198557F1, 0x074CAF75, 0xDDBBEE99, 0x60FDA37F, 0x269FF701, 0xF5BC5C72, 0x3BC54466, 0x7E345BFB, + 0x29768B43, 0xC6DCCB23, 0xFC68B6ED, 0xF163B8E4, 0xDCCAD731, 0x85104263, 0x22401397, 0x112084C6, + 0x247D854A, 0x3DF8D2BB, 0x3211AEF9, 0xA16DC729, 0x2F4B1D9E, 0x30F3DCB2, 0x52EC0D86, 0xE3D077C1, + 0x166C2BB3, 0xB999A970, 0x48FA1194, 0x642247E9, 0x8CC4A8FC, 0x3F1AA0F0, 0x2CD8567D, 0x90EF2233, + 0x4EC78749, 0xD1C1D938, 0xA2FE8CCA, 0x0B3698D4, 0x81CFA6F5, 0xDE28A57A, 0x8E26DAB7, 0xBFA43FAD, + 0x9DE42C3A, 0x920D5078, 0xCC9B6A5F, 0x4662547E, 0x13C2F68D, 0xB8E890D8, 0xF75E2E39, 0xAFF582C3, + 0x80BE9F5D, 0x937C69D0, 0x2DA96FD5, 0x12B3CF25, 0x993BC8AC, 0x7DA71018, 0x636EE89C, 0xBB7BDB3B, + 0x7809CD26, 0x18F46E59, 0xB701EC9A, 0x9AA8834F, 0x6E65E695, 0xE67EAAFF, 0xCF0821BC, 0xE8E6EF15, + 0x9BD9BAE7, 0x36CE4A6F, 0x09D4EA9F, 0x7CD629B0, 0xB2AF31A4, 0x23312A3F, 0x9430C6A5, 0x66C035A2, + 0xBC37744E, 0xCAA6FC82, 0xD0B0E090, 0xD81533A7, 0x984AF104, 0xDAF741EC, 0x500E7FCD, 0xF62F1791, + 0xD68D764D, 0xB04D43EF, 0x4D54CCAA, 0x04DFE496, 0xB5E39ED1, 0x881B4C6A, 0x1FB8C12C, 0x517F4665, + 0xEA049D5E, 0x355D018C, 0x7473FA87, 0x412EFB0B, 0x1D5AB367, 0xD25292DB, 0x5633E910, 0x47136DD6, + 0x618C9AD7, 0x0C7A37A1, 0x148E59F8, 0x3C89EB13, 0x27EECEA9, 0xC935B761, 0xE5EDE11C, 0xB13C7A47, + 0xDF599CD2, 0x733F55F2, 0xCE791814, 0x37BF73C7, 0xCDEA53F7, 0xAA5B5FFD, 0x6F14DF3D, 0xDB867844, + 0xF381CAAF, 0xC43EB968, 0x342C3824, 0x405FC2A3, 0xC372161D, 0x250CBCE2, 0x498B283C, 0x9541FF0D, + 0x017139A8, 0xB3DE080C, 0xE49CD8B4, 0xC1906456, 0x84617BCB, 0xB670D532, 0x5C74486C, 0x5742D0B8 + ); + + /** + * Precomputed invMixColumns table + * + * @see Rijndael:_encryptBlock() + * @see Rijndael:_decryptBlock() + * @var Array + * @access private + */ + var $dt3 = array( + 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, + 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, + 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, + 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, + 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, + 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, + 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, + 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, + 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, + 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, + 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, + 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, + 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, + 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, + 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, + 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, + 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, + 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, + 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, + 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, + 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, + 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, + 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, + 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, + 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, + 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, + 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, + 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, + 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, + 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, + 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, + 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 + ); + + /** + * The SubByte S-Box + * + * @see Rijndael::_encryptBlock() + * @var Array + * @access private + */ + var $sbox = array( + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 + ); + + /** + * The inverse SubByte S-Box + * + * @see Rijndael::_decryptBlock() + * @var Array + * @access private + */ + var $isbox = array( + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D + ); + /** * Default Constructor. * - * @param int $mode + * Determines whether or not the mcrypt extension should be used. + * + * $mode could be: + * + * - CRYPT_RIJNDAEL_MODE_ECB + * + * - CRYPT_RIJNDAEL_MODE_CBC + * + * - CRYPT_RIJNDAEL_MODE_CTR + * + * - CRYPT_RIJNDAEL_MODE_CFB + * + * - CRYPT_RIJNDAEL_MODE_OFB + * + * If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used. + * + * @see Base::Base() + * @param optional Integer $mode * @access public - * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ - function __construct($mode) - { - if ($mode == self::MODE_STREAM) { - throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); - } + function __construct($mode = CRYPT_RIJNDAEL_MODE_CBC) + { parent::__construct($mode); } /** - * Sets the key length. + * Sets the key. + * + * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and + * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length + * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the + * excess bits. + * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * + * Note: 160/224-bit keys must explicitly set by setKeyLength(), otherwise they will be round/pad up to 192/256 bits. + * + * @see Base:setKey() + * @see setKeyLength() + * @access public + * @param String $key + */ + function setKey($key) + { + parent::setKey($key); + + if (!$this->explicit_key_length) { + $length = strlen($key); + switch (true) { + case $length <= 16: + $this->key_size = 16; + break; + case $length <= 24: + $this->key_size = 24; + break; + default: + $this->key_size = 32; + } + $this->_setupEngine(); + } + } + + /** + * Sets the key length * - * Valid key lengths are 128, 160, 192, 224, and 256. + * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. * * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to @@ -197,132 +749,147 @@ function __construct($mode) * you should not setKeyLength(160) or setKeyLength(224). * * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use - * the mcrypt php extension, even if available. + * the mcrypt php extention, even if available. * This results then in slower encryption. * * @access public - * @throws \LengthException if the key length is invalid - * @param int $length + * @param Integer $length */ function setKeyLength($length) { - switch ($length) { - case 128: - case 160: - case 192: - case 224: - case 256: - $this->key_length = $length >> 3; + switch (true) { + case $length == 160: + $this->key_size = 20; + break; + case $length == 224: + $this->key_size = 28; + break; + case $length <= 128: + $this->key_size = 16; + break; + case $length <= 192: + $this->key_size = 24; break; default: - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); + $this->key_size = 32; } - parent::setKeyLength($length); + $this->explicit_key_length = true; + $this->changed = true; + $this->_setupEngine(); } /** - * Sets the key. + * Sets the block length * - * Rijndael supports five different key lengths + * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. * - * @see setKeyLength() * @access public - * @param string $key - * @throws \LengthException if the key length isn't supported + * @param Integer $length */ - function setKey($key) + function setBlockLength($length) { - switch (strlen($key)) { - case 16: - case 20: - case 24: - case 28: - case 32: - break; - default: - throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported'); + $length >>= 5; + if ($length > 8) { + $length = 8; + } else if ($length < 4) { + $length = 4; } - - parent::setKey($key); + $this->Nb = $length; + $this->block_size = $length << 2; + $this->changed = true; + $this->_setupEngine(); } /** - * Sets the block length + * Setup the fastest possible $engine * - * Valid block lengths are 128, 160, 192, 224, and 256. + * Determines if the mcrypt (MODE_MCRYPT) $engine available + * and usable for the current $block_size and $key_size. * - * @access public - * @param int $length + * If not, the slower MODE_INTERNAL $engine will be set. + * + * @see setKey() + * @see setKeyLength() + * @see setBlockLength() + * @access private */ - function setBlockLength($length) + function _setupEngine() { - switch ($length) { - case 128: - case 160: - case 192: - case 224: - case 256: + if (@constant('CRYPT_' . $this->const_namespace . '_MODE') == CRYPT_MODE_INTERNAL) { + // No mcrypt support at all for rijndael + return; + } + + // The required mcrypt module name for the current $block_size of rijndael + $cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); + + // Determining the availibility/usability of $cipher_name_mcrypt + switch (true) { + case $this->key_size % 8: // mcrypt is not usable for 160/224-bit keys, only for 128/192/256-bit keys + case !in_array($cipher_name_mcrypt, mcrypt_list_algorithms()): // $cipher_name_mcrypt is not available for the current $block_size + $engine = CRYPT_MODE_INTERNAL; break; default: - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); + $engine = CRYPT_MODE_MCRYPT; } - $this->Nb = $length >> 5; - $this->block_size = $length >> 3; - $this->changed = true; - $this->_setEngine(); + if ($this->engine == $engine && $this->cipher_name_mcrypt == $cipher_name_mcrypt) { + // allready set, so we not unnecessary close $this->enmcrypt/demcrypt/ecb + return; + } + + // Set the $engine + $this->engine = $engine; + $this->cipher_name_mcrypt = $cipher_name_mcrypt; + + if ($this->enmcrypt) { + // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, + // (re)open them with the module named in $this->cipher_name_mcrypt + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); + $this->enmcrypt = null; + $this->demcrypt = null; + + if ($this->ecb) { + mcrypt_module_close($this->ecb); + $this->ecb = null; + } + } } /** - * Test for engine validity - * - * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() + * Setup the CRYPT_MODE_MCRYPT $engine * - * @see \phpseclib\Crypt\Base::__construct() - * @param int $engine - * @access public - * @return bool + * @see Base::_setupMcrypt() + * @access private */ - function isValidEngine($engine) + function _setupMcrypt() { - switch ($engine) { - case self::ENGINE_OPENSSL: - if ($this->block_size != 16) { - return false; - } - $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb'; - $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode(); - break; - case self::ENGINE_MCRYPT: - $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); - if ($this->key_length % 8) { // is it a 160/224-bit key? - // mcrypt is not usable for them, only for 128/192/256-bit keys - return false; - } - } - - return parent::isValidEngine($engine); + $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0"); + parent::_setupMcrypt(); } /** * Encrypts a block * * @access private - * @param string $in - * @return string + * @param String $in + * @return String */ function _encryptBlock($in) { - static $tables; - if (empty($tables)) { - $tables = &$this->_getTables(); + static $t0, $t1, $t2, $t3, $sbox; + if (!$t0) { + for ($i = 0; $i < 256; ++$i) { + $t0[] = (int)$this->t0[$i]; + $t1[] = (int)$this->t1[$i]; + $t2[] = (int)$this->t2[$i]; + $t3[] = (int)$this->t3[$i]; + $sbox[] = (int)$this->sbox[$i]; + } } - $t0 = $tables[0]; - $t1 = $tables[1]; - $t2 = $tables[2]; - $t3 = $tables[3]; - $sbox = $tables[4]; $state = array(); $words = unpack('N*', $in); @@ -333,9 +900,9 @@ function _encryptBlock($in) $Nr = $this->Nr; // addRoundKey - $wc = $Nb - 1; + $i = -1; foreach ($words as $word) { - $state[] = $word ^ $w[++$wc]; + $state[] = $word ^ $w[0][++$i]; } // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - @@ -358,7 +925,7 @@ function _encryptBlock($in) $t1[$state[$j] >> 16 & 0x000000FF] ^ $t2[$state[$k] >> 8 & 0x000000FF] ^ $t3[$state[$l] & 0x000000FF] ^ - $w[++$wc]; + $w[$round][$i]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -385,7 +952,7 @@ function _encryptBlock($in) ($state[$j] & 0x00FF0000) ^ ($state[$k] & 0x0000FF00) ^ ($state[$l] & 0x000000FF) ^ - $w[$i]; + $w[$Nr][$i]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -410,20 +977,21 @@ function _encryptBlock($in) * Decrypts a block * * @access private - * @param string $in - * @return string + * @param String $in + * @return String */ function _decryptBlock($in) { - static $invtables; - if (empty($invtables)) { - $invtables = &$this->_getInvTables(); + static $dt0, $dt1, $dt2, $dt3, $isbox; + if (!$dt0) { + for ($i = 0; $i < 256; ++$i) { + $dt0[] = (int)$this->dt0[$i]; + $dt1[] = (int)$this->dt1[$i]; + $dt2[] = (int)$this->dt2[$i]; + $dt3[] = (int)$this->dt3[$i]; + $isbox[] = (int)$this->isbox[$i]; + } } - $dt0 = $invtables[0]; - $dt1 = $invtables[1]; - $dt2 = $invtables[2]; - $dt3 = $invtables[3]; - $isbox = $invtables[4]; $state = array(); $words = unpack('N*', $in); @@ -434,9 +1002,9 @@ function _decryptBlock($in) $Nr = $this->Nr; // addRoundKey - $wc = $Nb - 1; + $i = -1; foreach ($words as $word) { - $state[] = $word ^ $dw[++$wc]; + $state[] = $word ^ $dw[$Nr][++$i]; } $temp = array(); @@ -451,7 +1019,7 @@ function _decryptBlock($in) $dt1[$state[$j] >> 16 & 0x000000FF] ^ $dt2[$state[$k] >> 8 & 0x000000FF] ^ $dt3[$state[$l] & 0x000000FF] ^ - $dw[++$wc]; + $dw[$round][$i]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -472,10 +1040,10 @@ function _decryptBlock($in) ($state[$k] & 0x0000FF00) | ($state[$l] & 0x000000FF); - $temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] | - ($isbox[$word >> 8 & 0x000000FF] << 8) | - ($isbox[$word >> 16 & 0x000000FF] << 16) | - ($isbox[$word >> 24 & 0x000000FF] << 24)); + $temp[$i] = $dw[0][$i] ^ ($isbox[$word & 0x000000FF] | + ($isbox[$word >> 8 & 0x000000FF] << 8) | + ($isbox[$word >> 16 & 0x000000FF] << 16) | + ($isbox[$word >> 24 & 0x000000FF] << 24)); ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; @@ -499,7 +1067,7 @@ function _decryptBlock($in) /** * Setup the key (expansion) * - * @see \phpseclib\Crypt\Base::_setupKey() + * @see Base::_setupKey() * @access private */ function _setupKey() @@ -515,13 +1083,15 @@ function _setupKey() 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 ); - if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) { + $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0"); + + if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_size === $this->kl['key_size'] && $this->block_size === $this->kl['block_size']) { // already expanded return; } - $this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size); + $this->kl = array('key' => $this->key, 'key_size' => $this->key_size, 'block_size' => $this->block_size); - $this->Nk = $this->key_length >> 2; + $this->Nk = $this->key_size >> 2; // see Rijndael-ammended.pdf#page=44 $this->Nr = max($this->Nk, $this->Nb) + 6; @@ -554,7 +1124,7 @@ function _setupKey() // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is. $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk]; - } elseif ($this->Nk > 6 && $i % $this->Nk == 4) { + } else if ($this->Nk > 6 && $i % $this->Nk == 4) { $temp = $this->_subWord($temp); } $w[$i] = $w[$i - $this->Nk] ^ $temp; @@ -567,7 +1137,6 @@ function _setupKey() // 1. Apply the Key Expansion. // 2. Apply InvMixColumn to all Round Keys except the first and the last one." // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher" - list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables(); $temp = $this->w = $this->dw = array(); for ($i = $row = $col = 0; $i < $length; $i++, $col++) { if ($col == $this->Nb) { @@ -578,10 +1147,10 @@ function _setupKey() $j = 0; while ($j < $this->Nb) { $dw = $this->_subWord($this->w[$row][$j]); - $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^ - $dt1[$dw >> 16 & 0x000000FF] ^ - $dt2[$dw >> 8 & 0x000000FF] ^ - $dt3[$dw & 0x000000FF]; + $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^ + $this->dt1[$dw >> 16 & 0x000000FF] ^ + $this->dt2[$dw >> 8 & 0x000000FF] ^ + $this->dt3[$dw & 0x000000FF]; $j++; } $this->dw[$row] = $temp; @@ -595,32 +1164,31 @@ function _setupKey() $this->dw[$row] = $this->w[$row]; - // Converting to 1-dim key arrays (both ascending) - $this->dw = array_reverse($this->dw); - $w = array_pop($this->w); - $dw = array_pop($this->dw); - foreach ($this->w as $r => $wr) { - foreach ($wr as $c => $wc) { - $w[] = $wc; - $dw[] = $this->dw[$r][$c]; + // In case of $this->use_inline_crypt === true we have to use 1-dim key arrays (both ascending) + if ($this->use_inline_crypt) { + $this->dw = array_reverse($this->dw); + $w = array_pop($this->w); + $dw = array_pop($this->dw); + foreach ($this->w as $r => $wr) { + foreach ($wr as $c => $wc) { + $w[] = $wc; + $dw[] = $this->dw[$r][$c]; + } } + $this->w = $w; + $this->dw = $dw; } - $this->w = $w; - $this->dw = $dw; } /** * Performs S-Box substitutions * * @access private - * @param int $word + * @param Integer $word */ function _subWord($word) { - static $sbox; - if (empty($sbox)) { - list(, , , , $sbox) = $this->_getTables(); - } + $sbox = $this->sbox; return $sbox[$word & 0x000000FF] | ($sbox[$word >> 8 & 0x000000FF] << 8) | @@ -628,183 +1196,10 @@ function _subWord($word) ($sbox[$word >> 24 & 0x000000FF] << 24); } - /** - * Provides the mixColumns and sboxes tables - * - * @see self::_encryptBlock() - * @see self::_setupInlineCrypt() - * @see self::_subWord() - * @access private - * @return array &$tables - */ - function &_getTables() - { - static $tables; - if (empty($tables)) { - // according to (section 5.2.1), - // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so - // those are the names we'll use. - $t3 = array_map('intval', array( - // with array_map('intval', ...) we ensure we have only int's and not - // some slower floats converted by php automatically on high values - 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, - 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, - 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, - 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, - 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, - 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, - 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, - 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, - 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, - 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, - 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, - 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, - 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, - 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, - 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, - 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, - 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, - 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, - 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, - 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, - 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, - 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, - 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, - 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, - 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, - 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, - 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, - 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, - 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, - 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, - 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, - 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C - )); - - foreach ($t3 as $t3i) { - $t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >> 8) & 0x00FFFFFF); - $t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF); - $t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF); - } - - $tables = array( - // The Precomputed mixColumns tables t0 - t3 - $t0, - $t1, - $t2, - $t3, - // The SubByte S-Box - array( - 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, - 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, - 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, - 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, - 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, - 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, - 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, - 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, - 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, - 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, - 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, - 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, - 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, - 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, - 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, - 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 - ) - ); - } - return $tables; - } - - /** - * Provides the inverse mixColumns and inverse sboxes tables - * - * @see self::_decryptBlock() - * @see self::_setupInlineCrypt() - * @see self::_setupKey() - * @access private - * @return array &$tables - */ - function &_getInvTables() - { - static $tables; - if (empty($tables)) { - $dt3 = array_map('intval', array( - 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, - 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, - 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, - 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, - 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, - 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, - 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, - 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, - 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, - 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, - 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, - 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, - 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, - 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, - 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, - 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, - 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, - 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, - 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, - 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, - 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, - 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, - 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, - 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, - 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, - 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, - 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, - 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, - 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, - 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, - 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, - 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 - )); - - foreach ($dt3 as $dt3i) { - $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF); - $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF); - $dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF); - }; - - $tables = array( - // The Precomputed inverse mixColumns tables dt0 - dt3 - $dt0, - $dt1, - $dt2, - $dt3, - // The inverse SubByte S-Box - array( - 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, - 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, - 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, - 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, - 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, - 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, - 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, - 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, - 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, - 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, - 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, - 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, - 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D - ) - ); - } - return $tables; - } - /** * Setup the performance-optimized function for de/encrypt() * - * @see \phpseclib\Crypt\Base::_setupInlineCrypt() + * @see Base::_setupInlineCrypt() * @access private */ function _setupInlineCrypt() @@ -813,52 +1208,44 @@ function _setupInlineCrypt() // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt(). // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible. - $lambda_functions =& self::_getLambdaFunctions(); - - // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function. - // (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit) - // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one. - $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); - - // Generation of a uniqe hash for our generated code - $code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}"; - if ($gen_hi_opt_code) { - $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); + $lambda_functions =& Rijndael::_getLambdaFunctions(); + + // The first 10 generated $lambda_functions will use the key-words hardcoded for better performance. + // For memory reason we limit those ultra-optimized functions. + // After that, we use pure (extracted) integer vars for the key-words which is faster than accessing them via array. + if (count($lambda_functions) < 10) { + $w = $this->w; + $dw = $this->dw; + $init_encrypt = ''; + $init_decrypt = ''; + } else { + for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { + $w[] = '$w[' . $i . ']'; + $dw[] = '$dw[' . $i . ']'; + } + $init_encrypt = '$w = $self->w;'; + $init_decrypt = '$dw = $self->dw;'; } - if (!isset($lambda_functions[$code_hash])) { - switch (true) { - case $gen_hi_opt_code: - // The hi-optimized $lambda_functions will use the key-words hardcoded for better performance. - $w = $this->w; - $dw = $this->dw; - $init_encrypt = ''; - $init_decrypt = ''; - break; - default: - for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { - $w[] = '$w[' . $i . ']'; - $dw[] = '$dw[' . $i . ']'; - } - $init_encrypt = '$w = $self->w;'; - $init_decrypt = '$dw = $self->dw;'; - } + $code_hash = md5(str_pad("Rijndael, {$this->mode}, {$this->block_size}, ", 32, "\0") . implode(',', $w)); + if (!isset($lambda_functions[$code_hash])) { $Nr = $this->Nr; $Nb = $this->Nb; $c = $this->c; // Generating encrypt code: $init_encrypt.= ' - static $tables; - if (empty($tables)) { - $tables = &$self->_getTables(); + static $t0, $t1, $t2, $t3, $sbox; + if (!$t0) { + for ($i = 0; $i < 256; ++$i) { + $t0[$i] = (int)$self->t0[$i]; + $t1[$i] = (int)$self->t1[$i]; + $t2[$i] = (int)$self->t2[$i]; + $t3[$i] = (int)$self->t3[$i]; + $sbox[$i] = (int)$self->sbox[$i]; + } } - $t0 = $tables[0]; - $t1 = $tables[1]; - $t2 = $tables[2]; - $t3 = $tables[3]; - $sbox = $tables[4]; '; $s = 'e'; @@ -897,25 +1284,26 @@ function _setupInlineCrypt() $encrypt_block .= '$in = pack("N*"'."\n"; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block.= ', - ($'.$e.$i .' & '.((int)0xFF000000).') ^ - ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^ - ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^ - ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^ + ($'.$e.$i .' & 0xFF000000) ^ + ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000) ^ + ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00) ^ + ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF) ^ '.$w[$i]."\n"; } $encrypt_block .= ');'; // Generating decrypt code: $init_decrypt.= ' - static $invtables; - if (empty($invtables)) { - $invtables = &$self->_getInvTables(); + static $dt0, $dt1, $dt2, $dt3, $isbox; + if (!$dt0) { + for ($i = 0; $i < 256; ++$i) { + $dt0[$i] = (int)$self->dt0[$i]; + $dt1[$i] = (int)$self->dt1[$i]; + $dt2[$i] = (int)$self->dt2[$i]; + $dt3[$i] = (int)$self->dt3[$i]; + $isbox[$i] = (int)$self->isbox[$i]; + } } - $dt0 = $invtables[0]; - $dt1 = $invtables[1]; - $dt2 = $invtables[2]; - $dt3 = $invtables[3]; - $isbox = $invtables[4]; '; $s = 'e'; @@ -954,10 +1342,10 @@ function _setupInlineCrypt() $decrypt_block .= '$in = pack("N*"'."\n"; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block.= ', - ($'.$e.$i. ' & '.((int)0xFF000000).') ^ - ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^ - ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^ - ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^ + ($'.$e.$i. ' & 0xFF000000) ^ + ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000) ^ + ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00) ^ + ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF) ^ '.$dw[$i]."\n"; } $decrypt_block .= ');'; diff --git a/src/phpseclib/Crypt/TripleDES.php b/src/phpseclib/Crypt/TripleDES.php index 29c6eb9d..5205dec8 100755 --- a/src/phpseclib/Crypt/TripleDES.php +++ b/src/phpseclib/Crypt/TripleDES.php @@ -5,14 +5,14 @@ * * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt). * - * PHP version 5 + * PHP versions 4 and 5 * * Here's a short example of how to use this library: * * setKey('abcdefghijklmnopqrstuvwx'); * @@ -26,64 +26,99 @@ * ?> * * + * LICENSE: 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. + * * @category Crypt * @package TripleDES * @author Jim Wigginton - * @copyright 2007 Jim Wigginton + * @copyright MMVII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\Crypt; +/** + * Include DES + */ + +/** + * Encrypt / decrypt using inner chaining + * + * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3). + */ +@define('CRYPT_DES_MODE_3CBC', -2); + +/** + * Encrypt / decrypt using outer chaining + * + * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC. + */ +@define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC); + /** * Pure-PHP implementation of Triple DES. * * @package TripleDES * @author Jim Wigginton + * @version 0.1.0 * @access public */ class TripleDES extends DES { /** - * Encrypt / decrypt using inner chaining - * - * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3). - */ - const MODE_3CBC = -2; - - /** - * Encrypt / decrypt using outer chaining + * The default password key_size used by setPassword() * - * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib\Crypt\Base::MODE_CBC. + * @see DES::password_key_size + * @see Base::password_key_size + * @see Base::setPassword() + * @var Integer + * @access private */ - const MODE_CBC3 = Base::MODE_CBC; + var $password_key_size = 24; /** - * Key Length (in bytes) + * The default salt used by setPassword() * - * @see \phpseclib\Crypt\TripleDES::setKeyLength() - * @var int + * @see Base::password_default_salt + * @see Base::setPassword() + * @var String * @access private */ - var $key_length = 24; + var $password_default_salt = 'phpseclib'; /** - * The default salt used by setPassword() + * The namespace used by the cipher for its constants. * - * @see \phpseclib\Crypt\Base::password_default_salt - * @see \phpseclib\Crypt\Base::setPassword() - * @var string + * @see DES::const_namespace + * @see Base::const_namespace + * @var String * @access private */ - var $password_default_salt = 'phpseclib'; + var $const_namespace = 'DES'; /** * The mcrypt specific name of the cipher * - * @see \phpseclib\Crypt\DES::cipher_name_mcrypt - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @var string + * @see DES::cipher_name_mcrypt + * @see Base::cipher_name_mcrypt + * @var String * @access private */ var $cipher_name_mcrypt = 'tripledes'; @@ -91,8 +126,8 @@ class TripleDES extends DES /** * Optimizing value while CFB-encrypting * - * @see \phpseclib\Crypt\Base::cfb_init_len - * @var int + * @see Base::cfb_init_len + * @var Integer * @access private */ var $cfb_init_len = 750; @@ -100,27 +135,27 @@ class TripleDES extends DES /** * max possible size of $key * - * @see self::setKey() - * @see \phpseclib\Crypt\DES::setKey() - * @var string + * @see TripleDES::setKey() + * @see DES::setKey() + * @var String * @access private */ - var $key_length_max = 24; + var $key_size_max = 24; /** - * Internal flag whether using self::MODE_3CBC or not + * Internal flag whether using CRYPT_DES_MODE_3CBC or not * - * @var bool + * @var Boolean * @access private */ var $mode_3cbc; /** - * The \phpseclib\Crypt\DES objects + * The DES objects * * Used only if $mode_3cbc === true * - * @var array + * @var Array * @access private */ var $des; @@ -128,83 +163,65 @@ class TripleDES extends DES /** * Default Constructor. * - * Determines whether or not the mcrypt or OpenSSL extensions should be used. + * Determines whether or not the mcrypt extension should be used. * * $mode could be: * - * - \phpseclib\Crypt\Base::MODE_ECB + * - CRYPT_DES_MODE_ECB + * + * - CRYPT_DES_MODE_CBC * - * - \phpseclib\Crypt\Base::MODE_CBC + * - CRYPT_DES_MODE_CTR * - * - \phpseclib\Crypt\Base::MODE_CTR + * - CRYPT_DES_MODE_CFB * - * - \phpseclib\Crypt\Base::MODE_CFB + * - CRYPT_DES_MODE_OFB * - * - \phpseclib\Crypt\Base::MODE_OFB + * - CRYPT_DES_MODE_3CBC * - * - \phpseclib\Crypt\TripleDES::MODE_3CB + * If not explictly set, CRYPT_DES_MODE_CBC will be used. * - * @see \phpseclib\Crypt\DES::__construct() - * @see \phpseclib\Crypt\Base::__construct() - * @param int $mode + * @see DES::DES() + * @see Base::Base() + * @param optional Integer $mode * @access public */ - function __construct($mode) + function TripleDES($mode = CRYPT_DES_MODE_CBC) { switch ($mode) { - // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC + // In case of CRYPT_DES_MODE_3CBC, we init as CRYPT_DES_MODE_CBC // and additional flag us internally as 3CBC - case self::MODE_3CBC: - parent::__construct(Base::MODE_CBC); + case CRYPT_DES_MODE_3CBC: + parent::DES(CRYPT_DES_MODE_CBC); $this->mode_3cbc = true; // This three $des'es will do the 3CBC work (if $key > 64bits) $this->des = array( - new DES(Base::MODE_CBC), - new DES(Base::MODE_CBC), - new DES(Base::MODE_CBC), + new DES(CRYPT_DES_MODE_CBC), + new DES(CRYPT_DES_MODE_CBC), + new DES(CRYPT_DES_MODE_CBC), ); - // we're going to be doing the padding, ourselves, so disable it in the \phpseclib\Crypt\DES objects + // we're going to be doing the padding, ourselves, so disable it in the DES objects $this->des[0]->disablePadding(); $this->des[1]->disablePadding(); $this->des[2]->disablePadding(); break; // If not 3CBC, we init as usual default: - parent::__construct($mode); - } - } - - /** - * Test for engine validity - * - * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine() - * - * @see \phpseclib\Crypt\Base::__construct() - * @param int $engine - * @access public - * @return bool - */ - function isValidEngine($engine) - { - if ($engine == self::ENGINE_OPENSSL) { - $this->cipher_name_openssl_ecb = 'des-ede3'; - $mode = $this->_openssl_translate_mode(); - $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode; + parent::DES($mode); } - - return parent::isValidEngine($engine); } /** - * Sets the initialization vector. + * Sets the initialization vector. (optional) * - * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. + * SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed + * to be all zero's. * - * @see \phpseclib\Crypt\Base::setIV() + * @see Base::setIV() * @access public - * @param string $iv + * @param String $iv */ function setIV($iv) { @@ -216,66 +233,39 @@ function setIV($iv) } } - /** - * Sets the key length. - * - * Valid key lengths are 128 and 192 bits. - * - * If you want to use a 64-bit key use DES.php - * - * @see \phpseclib\Crypt\Base:setKeyLength() - * @access public - * @throws \LengthException if the key length is invalid - * @param int $length - */ - function setKeyLength($length) - { - switch ($length) { - case 128: - case 192: - break; - default: - throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported'); - } - - parent::setKeyLength($length); - } - /** * Sets the key. * - * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys. + * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or + * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * + * If the key is not explicitly set, it'll be assumed to be all null bytes. + * * @access public - * @see \phpseclib\Crypt\DES::setKey() - * @see \phpseclib\Crypt\Base::setKey() - * @throws \LengthException if the key length is invalid - * @param string $key + * @see DES::setKey() + * @see Base::setKey() + * @param String $key */ function setKey($key) { - if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { - throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); + $length = strlen($key); + if ($length > 8) { + $key = str_pad(substr($key, 0, 24), 24, chr(0)); + // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: + // http://php.net/function.mcrypt-encrypt#47973 + //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); + } else { + $key = str_pad($key, 8, chr(0)); } + parent::setKey($key); - switch (strlen($key)) { - case 16: - $key.= substr($key, 0, 8); - case 24: - break; - default: - throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported'); - } - - // copied from Base::setKey() - $this->key = $key; - $this->key_length = strlen($key); - $this->changed = true; - $this->_setEngine(); - - if ($this->mode_3cbc) { + // And in case of CRYPT_DES_MODE_3CBC: + // if key <= 64bits we not need the 3 $des to work, + // because we will then act as regular DES-CBC with just a <= 64bit key. + // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. + if ($this->mode_3cbc && $length > 8) { $this->des[0]->setKey(substr($key, 0, 8)); $this->des[1]->setKey(substr($key, 8, 8)); $this->des[2]->setKey(substr($key, 16, 8)); @@ -285,25 +275,21 @@ function setKey($key) /** * Encrypts a message. * - * @see \phpseclib\Crypt\Base::encrypt() + * @see Base::encrypt() * @access public - * @param string $plaintext - * @return string $cipertext + * @param String $plaintext + * @return String $cipertext */ function encrypt($plaintext) { // parent::en/decrypt() is able to do all the work for all modes and keylengths, - // except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits + // except for: CRYPT_DES_MODE_3CBC (inner chaining CBC) with a key > 64bits // if the key is smaller then 8, do what we'd normally do if ($this->mode_3cbc && strlen($this->key) > 8) { return $this->des[2]->encrypt( - $this->des[1]->decrypt( - $this->des[0]->encrypt( - $this->_pad($plaintext) - ) - ) - ); + $this->des[1]->decrypt( + $this->des[0]->encrypt($this->_pad($plaintext)))); } return parent::encrypt($plaintext); @@ -312,23 +298,17 @@ function encrypt($plaintext) /** * Decrypts a message. * - * @see \phpseclib\Crypt\Base::decrypt() + * @see Base::decrypt() * @access public - * @param string $ciphertext - * @return string $plaintext + * @param String $ciphertext + * @return String $plaintext */ function decrypt($ciphertext) { if ($this->mode_3cbc && strlen($this->key) > 8) { - return $this->_unpad( - $this->des[0]->decrypt( - $this->des[1]->encrypt( - $this->des[2]->decrypt( - str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0") - ) - ) - ) - ); + return $this->_unpad($this->des[0]->decrypt( + $this->des[1]->encrypt( + $this->des[2]->decrypt(str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0"))))); } return parent::decrypt($ciphertext); @@ -363,13 +343,13 @@ function decrypt($ciphertext) * outputs. The reason is due to the fact that the initialization vector's change after every encryption / * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant. * - * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\DES() object changes after each + * Put another way, when the continuous buffer is enabled, the state of the DES() object changes after each * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them), * however, they are also less intuitive and more likely to cause you problems. * - * @see \phpseclib\Crypt\Base::enableContinuousBuffer() - * @see self::disableContinuousBuffer() + * @see Base::enableContinuousBuffer() + * @see TripleDES::disableContinuousBuffer() * @access public */ function enableContinuousBuffer() @@ -387,8 +367,8 @@ function enableContinuousBuffer() * * The default behavior. * - * @see \phpseclib\Crypt\Base::disableContinuousBuffer() - * @see self::enableContinuousBuffer() + * @see Base::disableContinuousBuffer() + * @see TripleDES::enableContinuousBuffer() * @access public */ function disableContinuousBuffer() @@ -404,8 +384,8 @@ function disableContinuousBuffer() /** * Creates the key schedule * - * @see \phpseclib\Crypt\DES::_setupKey() - * @see \phpseclib\Crypt\Base::_setupKey() + * @see DES::_setupKey() + * @see Base::_setupKey() * @access private */ function _setupKey() @@ -435,24 +415,4 @@ function _setupKey() // setup our key parent::_setupKey(); } - - /** - * Sets the internal crypt engine - * - * @see \phpseclib\Crypt\Base::__construct() - * @see \phpseclib\Crypt\Base::setPreferredEngine() - * @param int $engine - * @access public - * @return int - */ - function setPreferredEngine($engine) - { - if ($this->mode_3cbc) { - $this->des[0]->setPreferredEngine($engine); - $this->des[1]->setPreferredEngine($engine); - $this->des[2]->setPreferredEngine($engine); - } - - return parent::setPreferredEngine($engine); - } } diff --git a/src/phpseclib/Crypt/Twofish.php b/src/phpseclib/Crypt/Twofish.php deleted file mode 100755 index e4d910db..00000000 --- a/src/phpseclib/Crypt/Twofish.php +++ /dev/null @@ -1,846 +0,0 @@ - - * setKey('12345678901234567890123456789012'); - * - * $plaintext = str_repeat('a', 1024); - * - * echo $twofish->decrypt($twofish->encrypt($plaintext)); - * ?> - * - * - * @category Crypt - * @package Twofish - * @author Jim Wigginton - * @author Hans-Juergen Petrich - * @copyright 2007 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Crypt; - -/** - * Pure-PHP implementation of Twofish. - * - * @package Twofish - * @author Jim Wigginton - * @author Hans-Juergen Petrich - * @access public - */ -class Twofish extends Base -{ - /** - * The mcrypt specific name of the cipher - * - * @see \phpseclib\Crypt\Base::cipher_name_mcrypt - * @var string - * @access private - */ - var $cipher_name_mcrypt = 'twofish'; - - /** - * Optimizing value while CFB-encrypting - * - * @see \phpseclib\Crypt\Base::cfb_init_len - * @var int - * @access private - */ - var $cfb_init_len = 800; - - /** - * Q-Table - * - * @var array - * @access private - */ - var $q0 = array( - 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, - 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, - 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, - 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, - 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, - 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, - 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, - 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, - 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, - 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, - 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, - 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, - 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, - 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, - 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, - 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, - 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, - 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, - 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, - 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, - 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, - 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, - 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, - 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, - 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, - 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, - 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, - 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, - 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, - 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, - 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, - 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 - ); - - /** - * Q-Table - * - * @var array - * @access private - */ - var $q1 = array( - 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, - 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, - 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, - 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, - 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, - 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, - 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, - 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, - 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, - 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, - 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, - 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, - 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, - 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, - 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, - 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, - 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, - 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, - 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, - 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, - 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, - 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, - 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, - 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, - 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, - 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, - 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, - 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, - 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, - 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, - 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, - 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 - ); - - /** - * M-Table - * - * @var array - * @access private - */ - var $m0 = array( - 0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8, - 0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B, - 0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1, - 0x24243C30, 0x5151E20F, 0xBABAC6F8, 0x4A4AF31B, 0xBFBF4887, 0x0D0D70FA, 0xB0B0B306, 0x7575DE3F, - 0xD2D2FD5E, 0x7D7D20BA, 0x666631AE, 0x3A3AA35B, 0x59591C8A, 0x00000000, 0xCDCD93BC, 0x1A1AE09D, - 0xAEAE2C6D, 0x7F7FABC1, 0x2B2BC7B1, 0xBEBEB90E, 0xE0E0A080, 0x8A8A105D, 0x3B3B52D2, 0x6464BAD5, - 0xD8D888A0, 0xE7E7A584, 0x5F5FE807, 0x1B1B1114, 0x2C2CC2B5, 0xFCFCB490, 0x3131272C, 0x808065A3, - 0x73732AB2, 0x0C0C8173, 0x79795F4C, 0x6B6B4154, 0x4B4B0292, 0x53536974, 0x94948F36, 0x83831F51, - 0x2A2A3638, 0xC4C49CB0, 0x2222C8BD, 0xD5D5F85A, 0xBDBDC3FC, 0x48487860, 0xFFFFCE62, 0x4C4C0796, - 0x4141776C, 0xC7C7E642, 0xEBEB24F7, 0x1C1C1410, 0x5D5D637C, 0x36362228, 0x6767C027, 0xE9E9AF8C, - 0x4444F913, 0x1414EA95, 0xF5F5BB9C, 0xCFCF18C7, 0x3F3F2D24, 0xC0C0E346, 0x7272DB3B, 0x54546C70, - 0x29294CCA, 0xF0F035E3, 0x0808FE85, 0xC6C617CB, 0xF3F34F11, 0x8C8CE4D0, 0xA4A45993, 0xCACA96B8, - 0x68683BA6, 0xB8B84D83, 0x38382820, 0xE5E52EFF, 0xADAD569F, 0x0B0B8477, 0xC8C81DC3, 0x9999FFCC, - 0x5858ED03, 0x19199A6F, 0x0E0E0A08, 0x95957EBF, 0x70705040, 0xF7F730E7, 0x6E6ECF2B, 0x1F1F6EE2, - 0xB5B53D79, 0x09090F0C, 0x616134AA, 0x57571682, 0x9F9F0B41, 0x9D9D803A, 0x111164EA, 0x2525CDB9, - 0xAFAFDDE4, 0x4545089A, 0xDFDF8DA4, 0xA3A35C97, 0xEAEAD57E, 0x353558DA, 0xEDEDD07A, 0x4343FC17, - 0xF8F8CB66, 0xFBFBB194, 0x3737D3A1, 0xFAFA401D, 0xC2C2683D, 0xB4B4CCF0, 0x32325DDE, 0x9C9C71B3, - 0x5656E70B, 0xE3E3DA72, 0x878760A7, 0x15151B1C, 0xF9F93AEF, 0x6363BFD1, 0x3434A953, 0x9A9A853E, - 0xB1B1428F, 0x7C7CD133, 0x88889B26, 0x3D3DA65F, 0xA1A1D7EC, 0xE4E4DF76, 0x8181942A, 0x91910149, - 0x0F0FFB81, 0xEEEEAA88, 0x161661EE, 0xD7D77321, 0x9797F5C4, 0xA5A5A81A, 0xFEFE3FEB, 0x6D6DB5D9, - 0x7878AEC5, 0xC5C56D39, 0x1D1DE599, 0x7676A4CD, 0x3E3EDCAD, 0xCBCB6731, 0xB6B6478B, 0xEFEF5B01, - 0x12121E18, 0x6060C523, 0x6A6AB0DD, 0x4D4DF61F, 0xCECEE94E, 0xDEDE7C2D, 0x55559DF9, 0x7E7E5A48, - 0x2121B24F, 0x03037AF2, 0xA0A02665, 0x5E5E198E, 0x5A5A6678, 0x65654B5C, 0x62624E58, 0xFDFD4519, - 0x0606F48D, 0x404086E5, 0xF2F2BE98, 0x3333AC57, 0x17179067, 0x05058E7F, 0xE8E85E05, 0x4F4F7D64, - 0x89896AAF, 0x10109563, 0x74742FB6, 0x0A0A75FE, 0x5C5C92F5, 0x9B9B74B7, 0x2D2D333C, 0x3030D6A5, - 0x2E2E49CE, 0x494989E9, 0x46467268, 0x77775544, 0xA8A8D8E0, 0x9696044D, 0x2828BD43, 0xA9A92969, - 0xD9D97929, 0x8686912E, 0xD1D187AC, 0xF4F44A15, 0x8D8D1559, 0xD6D682A8, 0xB9B9BC0A, 0x42420D9E, - 0xF6F6C16E, 0x2F2FB847, 0xDDDD06DF, 0x23233934, 0xCCCC6235, 0xF1F1C46A, 0xC1C112CF, 0x8585EBDC, - 0x8F8F9E22, 0x7171A1C9, 0x9090F0C0, 0xAAAA539B, 0x0101F189, 0x8B8BE1D4, 0x4E4E8CED, 0x8E8E6FAB, - 0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9, - 0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2, - 0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91 - ); - - /** - * M-Table - * - * @var array - * @access private - */ - var $m1 = array( - 0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4, - 0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A, - 0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141, - 0x43BD2828, 0x7532BCBC, 0x37D47B7B, 0x269B8888, 0xFA700D0D, 0x13F94444, 0x94B1FBFB, 0x485A7E7E, - 0xF27A0303, 0xD0E48C8C, 0x8B47B6B6, 0x303C2424, 0x84A5E7E7, 0x54416B6B, 0xDF06DDDD, 0x23C56060, - 0x1945FDFD, 0x5BA33A3A, 0x3D68C2C2, 0x59158D8D, 0xF321ECEC, 0xAE316666, 0xA23E6F6F, 0x82165757, - 0x63951010, 0x015BEFEF, 0x834DB8B8, 0x2E918686, 0xD9B56D6D, 0x511F8383, 0x9B53AAAA, 0x7C635D5D, - 0xA63B6868, 0xEB3FFEFE, 0xA5D63030, 0xBE257A7A, 0x16A7ACAC, 0x0C0F0909, 0xE335F0F0, 0x6123A7A7, - 0xC0F09090, 0x8CAFE9E9, 0x3A809D9D, 0xF5925C5C, 0x73810C0C, 0x2C273131, 0x2576D0D0, 0x0BE75656, - 0xBB7B9292, 0x4EE9CECE, 0x89F10101, 0x6B9F1E1E, 0x53A93434, 0x6AC4F1F1, 0xB499C3C3, 0xF1975B5B, - 0xE1834747, 0xE66B1818, 0xBDC82222, 0x450E9898, 0xE26E1F1F, 0xF4C9B3B3, 0xB62F7474, 0x66CBF8F8, - 0xCCFF9999, 0x95EA1414, 0x03ED5858, 0x56F7DCDC, 0xD4E18B8B, 0x1C1B1515, 0x1EADA2A2, 0xD70CD3D3, - 0xFB2BE2E2, 0xC31DC8C8, 0x8E195E5E, 0xB5C22C2C, 0xE9894949, 0xCF12C1C1, 0xBF7E9595, 0xBA207D7D, - 0xEA641111, 0x77840B0B, 0x396DC5C5, 0xAF6A8989, 0x33D17C7C, 0xC9A17171, 0x62CEFFFF, 0x7137BBBB, - 0x81FB0F0F, 0x793DB5B5, 0x0951E1E1, 0xADDC3E3E, 0x242D3F3F, 0xCDA47676, 0xF99D5555, 0xD8EE8282, - 0xE5864040, 0xC5AE7878, 0xB9CD2525, 0x4D049696, 0x44557777, 0x080A0E0E, 0x86135050, 0xE730F7F7, - 0xA1D33737, 0x1D40FAFA, 0xAA346161, 0xED8C4E4E, 0x06B3B0B0, 0x706C5454, 0xB22A7373, 0xD2523B3B, - 0x410B9F9F, 0x7B8B0202, 0xA088D8D8, 0x114FF3F3, 0x3167CBCB, 0xC2462727, 0x27C06767, 0x90B4FCFC, - 0x20283838, 0xF67F0404, 0x60784848, 0xFF2EE5E5, 0x96074C4C, 0x5C4B6565, 0xB1C72B2B, 0xAB6F8E8E, - 0x9E0D4242, 0x9CBBF5F5, 0x52F2DBDB, 0x1BF34A4A, 0x5FA63D3D, 0x9359A4A4, 0x0ABCB9B9, 0xEF3AF9F9, - 0x91EF1313, 0x85FE0808, 0x49019191, 0xEE611616, 0x2D7CDEDE, 0x4FB22121, 0x8F42B1B1, 0x3BDB7272, - 0x47B82F2F, 0x8748BFBF, 0x6D2CAEAE, 0x46E3C0C0, 0xD6573C3C, 0x3E859A9A, 0x6929A9A9, 0x647D4F4F, - 0x2A948181, 0xCE492E2E, 0xCB17C6C6, 0x2FCA6969, 0xFCC3BDBD, 0x975CA3A3, 0x055EE8E8, 0x7AD0EDED, - 0xAC87D1D1, 0x7F8E0505, 0xD5BA6464, 0x1AA8A5A5, 0x4BB72626, 0x0EB9BEBE, 0xA7608787, 0x5AF8D5D5, - 0x28223636, 0x14111B1B, 0x3FDE7575, 0x2979D9D9, 0x88AAEEEE, 0x3C332D2D, 0x4C5F7979, 0x02B6B7B7, - 0xB896CACA, 0xDA583535, 0xB09CC4C4, 0x17FC4343, 0x551A8484, 0x1FF64D4D, 0x8A1C5959, 0x7D38B2B2, - 0x57AC3333, 0xC718CFCF, 0x8DF40606, 0x74695353, 0xB7749B9B, 0xC4F59797, 0x9F56ADAD, 0x72DAE3E3, - 0x7ED5EAEA, 0x154AF4F4, 0x229E8F8F, 0x12A2ABAB, 0x584E6262, 0x07E85F5F, 0x99E51D1D, 0x34392323, - 0x6EC1F6F6, 0x50446C6C, 0xDE5D3232, 0x68724646, 0x6526A0A0, 0xBC93CDCD, 0xDB03DADA, 0xF8C6BABA, - 0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF, - 0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000, - 0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8 - ); - - /** - * M-Table - * - * @var array - * @access private - */ - var $m2 = array( - 0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA, - 0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7, - 0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783, - 0x2430243C, 0x510F51E2, 0xBAF8BAC6, 0x4A1B4AF3, 0xBF87BF48, 0x0DFA0D70, 0xB006B0B3, 0x753F75DE, - 0xD25ED2FD, 0x7DBA7D20, 0x66AE6631, 0x3A5B3AA3, 0x598A591C, 0x00000000, 0xCDBCCD93, 0x1A9D1AE0, - 0xAE6DAE2C, 0x7FC17FAB, 0x2BB12BC7, 0xBE0EBEB9, 0xE080E0A0, 0x8A5D8A10, 0x3BD23B52, 0x64D564BA, - 0xD8A0D888, 0xE784E7A5, 0x5F075FE8, 0x1B141B11, 0x2CB52CC2, 0xFC90FCB4, 0x312C3127, 0x80A38065, - 0x73B2732A, 0x0C730C81, 0x794C795F, 0x6B546B41, 0x4B924B02, 0x53745369, 0x9436948F, 0x8351831F, - 0x2A382A36, 0xC4B0C49C, 0x22BD22C8, 0xD55AD5F8, 0xBDFCBDC3, 0x48604878, 0xFF62FFCE, 0x4C964C07, - 0x416C4177, 0xC742C7E6, 0xEBF7EB24, 0x1C101C14, 0x5D7C5D63, 0x36283622, 0x672767C0, 0xE98CE9AF, - 0x441344F9, 0x149514EA, 0xF59CF5BB, 0xCFC7CF18, 0x3F243F2D, 0xC046C0E3, 0x723B72DB, 0x5470546C, - 0x29CA294C, 0xF0E3F035, 0x088508FE, 0xC6CBC617, 0xF311F34F, 0x8CD08CE4, 0xA493A459, 0xCAB8CA96, - 0x68A6683B, 0xB883B84D, 0x38203828, 0xE5FFE52E, 0xAD9FAD56, 0x0B770B84, 0xC8C3C81D, 0x99CC99FF, - 0x580358ED, 0x196F199A, 0x0E080E0A, 0x95BF957E, 0x70407050, 0xF7E7F730, 0x6E2B6ECF, 0x1FE21F6E, - 0xB579B53D, 0x090C090F, 0x61AA6134, 0x57825716, 0x9F419F0B, 0x9D3A9D80, 0x11EA1164, 0x25B925CD, - 0xAFE4AFDD, 0x459A4508, 0xDFA4DF8D, 0xA397A35C, 0xEA7EEAD5, 0x35DA3558, 0xED7AEDD0, 0x431743FC, - 0xF866F8CB, 0xFB94FBB1, 0x37A137D3, 0xFA1DFA40, 0xC23DC268, 0xB4F0B4CC, 0x32DE325D, 0x9CB39C71, - 0x560B56E7, 0xE372E3DA, 0x87A78760, 0x151C151B, 0xF9EFF93A, 0x63D163BF, 0x345334A9, 0x9A3E9A85, - 0xB18FB142, 0x7C337CD1, 0x8826889B, 0x3D5F3DA6, 0xA1ECA1D7, 0xE476E4DF, 0x812A8194, 0x91499101, - 0x0F810FFB, 0xEE88EEAA, 0x16EE1661, 0xD721D773, 0x97C497F5, 0xA51AA5A8, 0xFEEBFE3F, 0x6DD96DB5, - 0x78C578AE, 0xC539C56D, 0x1D991DE5, 0x76CD76A4, 0x3EAD3EDC, 0xCB31CB67, 0xB68BB647, 0xEF01EF5B, - 0x1218121E, 0x602360C5, 0x6ADD6AB0, 0x4D1F4DF6, 0xCE4ECEE9, 0xDE2DDE7C, 0x55F9559D, 0x7E487E5A, - 0x214F21B2, 0x03F2037A, 0xA065A026, 0x5E8E5E19, 0x5A785A66, 0x655C654B, 0x6258624E, 0xFD19FD45, - 0x068D06F4, 0x40E54086, 0xF298F2BE, 0x335733AC, 0x17671790, 0x057F058E, 0xE805E85E, 0x4F644F7D, - 0x89AF896A, 0x10631095, 0x74B6742F, 0x0AFE0A75, 0x5CF55C92, 0x9BB79B74, 0x2D3C2D33, 0x30A530D6, - 0x2ECE2E49, 0x49E94989, 0x46684672, 0x77447755, 0xA8E0A8D8, 0x964D9604, 0x284328BD, 0xA969A929, - 0xD929D979, 0x862E8691, 0xD1ACD187, 0xF415F44A, 0x8D598D15, 0xD6A8D682, 0xB90AB9BC, 0x429E420D, - 0xF66EF6C1, 0x2F472FB8, 0xDDDFDD06, 0x23342339, 0xCC35CC62, 0xF16AF1C4, 0xC1CFC112, 0x85DC85EB, - 0x8F228F9E, 0x71C971A1, 0x90C090F0, 0xAA9BAA53, 0x018901F1, 0x8BD48BE1, 0x4EED4E8C, 0x8EAB8E6F, - 0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9, - 0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746, - 0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF - ); - - /** - * M-Table - * - * @var array - * @access private - */ - var $m3 = array( - 0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF, - 0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836, - 0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77, - 0xBD2843BD, 0x32BC7532, 0xD47B37D4, 0x9B88269B, 0x700DFA70, 0xF94413F9, 0xB1FB94B1, 0x5A7E485A, - 0x7A03F27A, 0xE48CD0E4, 0x47B68B47, 0x3C24303C, 0xA5E784A5, 0x416B5441, 0x06DDDF06, 0xC56023C5, - 0x45FD1945, 0xA33A5BA3, 0x68C23D68, 0x158D5915, 0x21ECF321, 0x3166AE31, 0x3E6FA23E, 0x16578216, - 0x95106395, 0x5BEF015B, 0x4DB8834D, 0x91862E91, 0xB56DD9B5, 0x1F83511F, 0x53AA9B53, 0x635D7C63, - 0x3B68A63B, 0x3FFEEB3F, 0xD630A5D6, 0x257ABE25, 0xA7AC16A7, 0x0F090C0F, 0x35F0E335, 0x23A76123, - 0xF090C0F0, 0xAFE98CAF, 0x809D3A80, 0x925CF592, 0x810C7381, 0x27312C27, 0x76D02576, 0xE7560BE7, - 0x7B92BB7B, 0xE9CE4EE9, 0xF10189F1, 0x9F1E6B9F, 0xA93453A9, 0xC4F16AC4, 0x99C3B499, 0x975BF197, - 0x8347E183, 0x6B18E66B, 0xC822BDC8, 0x0E98450E, 0x6E1FE26E, 0xC9B3F4C9, 0x2F74B62F, 0xCBF866CB, - 0xFF99CCFF, 0xEA1495EA, 0xED5803ED, 0xF7DC56F7, 0xE18BD4E1, 0x1B151C1B, 0xADA21EAD, 0x0CD3D70C, - 0x2BE2FB2B, 0x1DC8C31D, 0x195E8E19, 0xC22CB5C2, 0x8949E989, 0x12C1CF12, 0x7E95BF7E, 0x207DBA20, - 0x6411EA64, 0x840B7784, 0x6DC5396D, 0x6A89AF6A, 0xD17C33D1, 0xA171C9A1, 0xCEFF62CE, 0x37BB7137, - 0xFB0F81FB, 0x3DB5793D, 0x51E10951, 0xDC3EADDC, 0x2D3F242D, 0xA476CDA4, 0x9D55F99D, 0xEE82D8EE, - 0x8640E586, 0xAE78C5AE, 0xCD25B9CD, 0x04964D04, 0x55774455, 0x0A0E080A, 0x13508613, 0x30F7E730, - 0xD337A1D3, 0x40FA1D40, 0x3461AA34, 0x8C4EED8C, 0xB3B006B3, 0x6C54706C, 0x2A73B22A, 0x523BD252, - 0x0B9F410B, 0x8B027B8B, 0x88D8A088, 0x4FF3114F, 0x67CB3167, 0x4627C246, 0xC06727C0, 0xB4FC90B4, - 0x28382028, 0x7F04F67F, 0x78486078, 0x2EE5FF2E, 0x074C9607, 0x4B655C4B, 0xC72BB1C7, 0x6F8EAB6F, - 0x0D429E0D, 0xBBF59CBB, 0xF2DB52F2, 0xF34A1BF3, 0xA63D5FA6, 0x59A49359, 0xBCB90ABC, 0x3AF9EF3A, - 0xEF1391EF, 0xFE0885FE, 0x01914901, 0x6116EE61, 0x7CDE2D7C, 0xB2214FB2, 0x42B18F42, 0xDB723BDB, - 0xB82F47B8, 0x48BF8748, 0x2CAE6D2C, 0xE3C046E3, 0x573CD657, 0x859A3E85, 0x29A96929, 0x7D4F647D, - 0x94812A94, 0x492ECE49, 0x17C6CB17, 0xCA692FCA, 0xC3BDFCC3, 0x5CA3975C, 0x5EE8055E, 0xD0ED7AD0, - 0x87D1AC87, 0x8E057F8E, 0xBA64D5BA, 0xA8A51AA8, 0xB7264BB7, 0xB9BE0EB9, 0x6087A760, 0xF8D55AF8, - 0x22362822, 0x111B1411, 0xDE753FDE, 0x79D92979, 0xAAEE88AA, 0x332D3C33, 0x5F794C5F, 0xB6B702B6, - 0x96CAB896, 0x5835DA58, 0x9CC4B09C, 0xFC4317FC, 0x1A84551A, 0xF64D1FF6, 0x1C598A1C, 0x38B27D38, - 0xAC3357AC, 0x18CFC718, 0xF4068DF4, 0x69537469, 0x749BB774, 0xF597C4F5, 0x56AD9F56, 0xDAE372DA, - 0xD5EA7ED5, 0x4AF4154A, 0x9E8F229E, 0xA2AB12A2, 0x4E62584E, 0xE85F07E8, 0xE51D99E5, 0x39233439, - 0xC1F66EC1, 0x446C5044, 0x5D32DE5D, 0x72466872, 0x26A06526, 0x93CDBC93, 0x03DADB03, 0xC6BAF8C6, - 0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D, - 0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000, - 0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8 - ); - - /** - * The Key Schedule Array - * - * @var array - * @access private - */ - var $K = array(); - - /** - * The Key depended S-Table 0 - * - * @var array - * @access private - */ - var $S0 = array(); - - /** - * The Key depended S-Table 1 - * - * @var array - * @access private - */ - var $S1 = array(); - - /** - * The Key depended S-Table 2 - * - * @var array - * @access private - */ - var $S2 = array(); - - /** - * The Key depended S-Table 3 - * - * @var array - * @access private - */ - var $S3 = array(); - - /** - * Holds the last used key - * - * @var array - * @access private - */ - var $kl; - - /** - * The Key Length (in bytes) - * - * @see Crypt_Twofish::setKeyLength() - * @var int - * @access private - */ - var $key_length = 16; - - /** - * Default Constructor. - * - * @param int $mode - * @access public - * @throws \InvalidArgumentException if an invalid / unsupported mode is provided - */ - function __construct($mode) - { - if ($mode == self::MODE_STREAM) { - throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); - } - - parent::__construct($mode); - } - - /** - * Sets the key length. - * - * Valid key lengths are 128, 192 or 256 bits - * - * @access public - * @param int $length - */ - function setKeyLength($length) - { - switch ($length) { - case 128: - case 192: - case 256: - break; - default: - throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); - } - - parent::setKeyLength($length); - } - - /** - * Sets the key. - * - * Rijndael supports five different key lengths - * - * @see setKeyLength() - * @access public - * @param string $key - * @throws \LengthException if the key length isn't supported - */ - function setKey($key) - { - switch (strlen($key)) { - case 16: - case 24: - case 32: - break; - default: - throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); - } - - parent::setKey($key); - } - - /** - * Setup the key (expansion) - * - * @see \phpseclib\Crypt\Base::_setupKey() - * @access private - */ - function _setupKey() - { - if (isset($this->kl['key']) && $this->key === $this->kl['key']) { - // already expanded - return; - } - $this->kl = array('key' => $this->key); - - /* Key expanding and generating the key-depended s-boxes */ - $le_longs = unpack('V*', $this->key); - $key = unpack('C*', $this->key); - $m0 = $this->m0; - $m1 = $this->m1; - $m2 = $this->m2; - $m3 = $this->m3; - $q0 = $this->q0; - $q1 = $this->q1; - - $K = $S0 = $S1 = $S2 = $S3 = array(); - - switch (strlen($this->key)) { - case 16: - list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]); - list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]); - for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { - $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^ - $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ - $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ - $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; - $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ - $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ - $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ - $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; - $B = ($B << 8) | ($B >> 24 & 0xff); - $K[] = $A+= $B; - $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); - } - for ($i = 0; $i < 256; ++$i) { - $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; - $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; - $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; - $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; - } - break; - case 24: - list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]); - list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]); - list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]); - for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { - $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ - $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ - $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ - $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; - $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ - $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ - $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ - $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; - $B = ($B << 8) | ($B >> 24 & 0xff); - $K[] = $A+= $B; - $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); - } - for ($i = 0; $i < 256; ++$i) { - $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; - $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; - $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; - $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; - } - break; - default: // 32 - list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]); - list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]); - list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]); - list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]); - for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { - $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ - $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ - $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ - $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; - $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ - $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ - $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ - $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; - $B = ($B << 8) | ($B >> 24 & 0xff); - $K[] = $A+= $B; - $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); - } - for ($i = 0; $i < 256; ++$i) { - $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; - $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; - $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; - $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; - } - } - - $this->K = $K; - $this->S0 = $S0; - $this->S1 = $S1; - $this->S2 = $S2; - $this->S3 = $S3; - } - - /** - * _mdsrem function using by the twofish cipher algorithm - * - * @access private - * @param string $A - * @param string $B - * @return array - */ - function _mdsrem($A, $B) - { - // No gain by unrolling this loop. - for ($i = 0; $i < 8; ++$i) { - // Get most significant coefficient. - $t = 0xff & ($B >> 24); - - // Shift the others up. - $B = ($B << 8) | (0xff & ($A >> 24)); - $A<<= 8; - - $u = $t << 1; - - // Subtract the modular polynomial on overflow. - if ($t & 0x80) { - $u^= 0x14d; - } - - // Remove t * (a * x^2 + 1). - $B ^= $t ^ ($u << 16); - - // Form u = a*t + t/a = t*(a + 1/a). - $u^= 0x7fffffff & ($t >> 1); - - // Add the modular polynomial on underflow. - if ($t & 0x01) { - $u^= 0xa6 ; - } - - // Remove t * (a + 1/a) * (x^3 + x). - $B^= ($u << 24) | ($u << 8); - } - - return array( - 0xff & $B >> 24, - 0xff & $B >> 16, - 0xff & $B >> 8, - 0xff & $B); - } - - /** - * Encrypts a block - * - * @access private - * @param string $in - * @return string - */ - function _encryptBlock($in) - { - $S0 = $this->S0; - $S1 = $this->S1; - $S2 = $this->S2; - $S3 = $this->S3; - $K = $this->K; - - $in = unpack("V4", $in); - $R0 = $K[0] ^ $in[1]; - $R1 = $K[1] ^ $in[2]; - $R2 = $K[2] ^ $in[3]; - $R3 = $K[3] ^ $in[4]; - - $ki = 7; - while ($ki < 39) { - $t0 = $S0[ $R0 & 0xff] ^ - $S1[($R0 >> 8) & 0xff] ^ - $S2[($R0 >> 16) & 0xff] ^ - $S3[($R0 >> 24) & 0xff]; - $t1 = $S0[($R1 >> 24) & 0xff] ^ - $S1[ $R1 & 0xff] ^ - $S2[($R1 >> 8) & 0xff] ^ - $S3[($R1 >> 16) & 0xff]; - $R2^= $t0 + $t1 + $K[++$ki]; - $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); - $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); - - $t0 = $S0[ $R2 & 0xff] ^ - $S1[($R2 >> 8) & 0xff] ^ - $S2[($R2 >> 16) & 0xff] ^ - $S3[($R2 >> 24) & 0xff]; - $t1 = $S0[($R3 >> 24) & 0xff] ^ - $S1[ $R3 & 0xff] ^ - $S2[($R3 >> 8) & 0xff] ^ - $S3[($R3 >> 16) & 0xff]; - $R0^= ($t0 + $t1 + $K[++$ki]); - $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); - $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); - } - - // @codingStandardsIgnoreStart - return pack("V4", $K[4] ^ $R2, - $K[5] ^ $R3, - $K[6] ^ $R0, - $K[7] ^ $R1); - // @codingStandardsIgnoreEnd - } - - /** - * Decrypts a block - * - * @access private - * @param string $in - * @return string - */ - function _decryptBlock($in) - { - $S0 = $this->S0; - $S1 = $this->S1; - $S2 = $this->S2; - $S3 = $this->S3; - $K = $this->K; - - $in = unpack("V4", $in); - $R0 = $K[4] ^ $in[1]; - $R1 = $K[5] ^ $in[2]; - $R2 = $K[6] ^ $in[3]; - $R3 = $K[7] ^ $in[4]; - - $ki = 40; - while ($ki > 8) { - $t0 = $S0[$R0 & 0xff] ^ - $S1[$R0 >> 8 & 0xff] ^ - $S2[$R0 >> 16 & 0xff] ^ - $S3[$R0 >> 24 & 0xff]; - $t1 = $S0[$R1 >> 24 & 0xff] ^ - $S1[$R1 & 0xff] ^ - $S2[$R1 >> 8 & 0xff] ^ - $S3[$R1 >> 16 & 0xff]; - $R3^= $t0 + ($t1 << 1) + $K[--$ki]; - $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; - $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + $K[--$ki]); - - $t0 = $S0[$R2 & 0xff] ^ - $S1[$R2 >> 8 & 0xff] ^ - $S2[$R2 >> 16 & 0xff] ^ - $S3[$R2 >> 24 & 0xff]; - $t1 = $S0[$R3 >> 24 & 0xff] ^ - $S1[$R3 & 0xff] ^ - $S2[$R3 >> 8 & 0xff] ^ - $S3[$R3 >> 16 & 0xff]; - $R1^= $t0 + ($t1 << 1) + $K[--$ki]; - $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; - $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + $K[--$ki]); - } - - // @codingStandardsIgnoreStart - return pack("V4", $K[0] ^ $R2, - $K[1] ^ $R3, - $K[2] ^ $R0, - $K[3] ^ $R1); - // @codingStandardsIgnoreEnd - } - - /** - * Setup the performance-optimized function for de/encrypt() - * - * @see \phpseclib\Crypt\Base::_setupInlineCrypt() - * @access private - */ - function _setupInlineCrypt() - { - $lambda_functions =& self::_getLambdaFunctions(); - - // Max. 10 Ultra-Hi-optimized inline-crypt functions. After that, we'll (still) create very fast code, but not the ultimate fast one. - // (Currently, for Crypt_Twofish, one generated $lambda_function cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit) - $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); - - // Generation of a uniqe hash for our generated code - $code_hash = "Crypt_Twofish, {$this->mode}"; - if ($gen_hi_opt_code) { - $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); - } - - if (!isset($lambda_functions[$code_hash])) { - switch (true) { - case $gen_hi_opt_code: - $K = $this->K; - $init_crypt = ' - static $S0, $S1, $S2, $S3; - if (!$S0) { - for ($i = 0; $i < 256; ++$i) { - $S0[] = (int)$self->S0[$i]; - $S1[] = (int)$self->S1[$i]; - $S2[] = (int)$self->S2[$i]; - $S3[] = (int)$self->S3[$i]; - } - } - '; - break; - default: - $K = array(); - for ($i = 0; $i < 40; ++$i) { - $K[] = '$K_' . $i; - } - $init_crypt = ' - $S0 = $self->S0; - $S1 = $self->S1; - $S2 = $self->S2; - $S3 = $self->S3; - list(' . implode(',', $K) . ') = $self->K; - '; - } - - // Generating encrypt code: - $encrypt_block = ' - $in = unpack("V4", $in); - $R0 = '.$K[0].' ^ $in[1]; - $R1 = '.$K[1].' ^ $in[2]; - $R2 = '.$K[2].' ^ $in[3]; - $R3 = '.$K[3].' ^ $in[4]; - '; - for ($ki = 7, $i = 0; $i < 8; ++$i) { - $encrypt_block.= ' - $t0 = $S0[ $R0 & 0xff] ^ - $S1[($R0 >> 8) & 0xff] ^ - $S2[($R0 >> 16) & 0xff] ^ - $S3[($R0 >> 24) & 0xff]; - $t1 = $S0[($R1 >> 24) & 0xff] ^ - $S1[ $R1 & 0xff] ^ - $S2[($R1 >> 8) & 0xff] ^ - $S3[($R1 >> 16) & 0xff]; - $R2^= ($t0 + $t1 + '.$K[++$ki].'); - $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); - $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); - - $t0 = $S0[ $R2 & 0xff] ^ - $S1[($R2 >> 8) & 0xff] ^ - $S2[($R2 >> 16) & 0xff] ^ - $S3[($R2 >> 24) & 0xff]; - $t1 = $S0[($R3 >> 24) & 0xff] ^ - $S1[ $R3 & 0xff] ^ - $S2[($R3 >> 8) & 0xff] ^ - $S3[($R3 >> 16) & 0xff]; - $R0^= ($t0 + $t1 + '.$K[++$ki].'); - $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); - $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); - '; - } - $encrypt_block.= ' - $in = pack("V4", '.$K[4].' ^ $R2, - '.$K[5].' ^ $R3, - '.$K[6].' ^ $R0, - '.$K[7].' ^ $R1); - '; - - // Generating decrypt code: - $decrypt_block = ' - $in = unpack("V4", $in); - $R0 = '.$K[4].' ^ $in[1]; - $R1 = '.$K[5].' ^ $in[2]; - $R2 = '.$K[6].' ^ $in[3]; - $R3 = '.$K[7].' ^ $in[4]; - '; - for ($ki = 40, $i = 0; $i < 8; ++$i) { - $decrypt_block.= ' - $t0 = $S0[$R0 & 0xff] ^ - $S1[$R0 >> 8 & 0xff] ^ - $S2[$R0 >> 16 & 0xff] ^ - $S3[$R0 >> 24 & 0xff]; - $t1 = $S0[$R1 >> 24 & 0xff] ^ - $S1[$R1 & 0xff] ^ - $S2[$R1 >> 8 & 0xff] ^ - $S3[$R1 >> 16 & 0xff]; - $R3^= $t0 + ($t1 << 1) + '.$K[--$ki].'; - $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; - $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); - - $t0 = $S0[$R2 & 0xff] ^ - $S1[$R2 >> 8 & 0xff] ^ - $S2[$R2 >> 16 & 0xff] ^ - $S3[$R2 >> 24 & 0xff]; - $t1 = $S0[$R3 >> 24 & 0xff] ^ - $S1[$R3 & 0xff] ^ - $S2[$R3 >> 8 & 0xff] ^ - $S3[$R3 >> 16 & 0xff]; - $R1^= $t0 + ($t1 << 1) + '.$K[--$ki].'; - $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; - $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); - '; - } - $decrypt_block.= ' - $in = pack("V4", '.$K[0].' ^ $R2, - '.$K[1].' ^ $R3, - '.$K[2].' ^ $R0, - '.$K[3].' ^ $R1); - '; - - $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( - array( - 'init_crypt' => $init_crypt, - 'init_encrypt' => '', - 'init_decrypt' => '', - 'encrypt_block' => $encrypt_block, - 'decrypt_block' => $decrypt_block - ) - ); - } - $this->inline_crypt = $lambda_functions[$code_hash]; - } -} diff --git a/src/phpseclib/Exception/BadConfigurationException.php b/src/phpseclib/Exception/BadConfigurationException.php deleted file mode 100755 index 096148a0..00000000 --- a/src/phpseclib/Exception/BadConfigurationException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Exception; - -/** - * BadConfigurationException - * - * @package BadConfigurationException - * @author Jim Wigginton - */ -class BadConfigurationException extends \RuntimeException -{ -} diff --git a/src/phpseclib/Exception/FileNotFoundException.php b/src/phpseclib/Exception/FileNotFoundException.php deleted file mode 100755 index 984edfcc..00000000 --- a/src/phpseclib/Exception/FileNotFoundException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Exception; - -/** - * FileNotFoundException - * - * @package FileNotFoundException - * @author Jim Wigginton - */ -class FileNotFoundException extends \RuntimeException -{ -} diff --git a/src/phpseclib/Exception/NoSupportedAlgorithmsException.php b/src/phpseclib/Exception/NoSupportedAlgorithmsException.php deleted file mode 100755 index bca9a753..00000000 --- a/src/phpseclib/Exception/NoSupportedAlgorithmsException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Exception; - -/** - * NoSupportedAlgorithmsException - * - * @package NoSupportedAlgorithmsException - * @author Jim Wigginton - */ -class NoSupportedAlgorithmsException extends \RuntimeException -{ -} diff --git a/src/phpseclib/Exception/UnsupportedAlgorithmException.php b/src/phpseclib/Exception/UnsupportedAlgorithmException.php deleted file mode 100755 index 47cc41d4..00000000 --- a/src/phpseclib/Exception/UnsupportedAlgorithmException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @copyright 2015 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Exception; - -/** - * UnsupportedAlgorithmException - * - * @package UnsupportedAlgorithmException - * @author Jim Wigginton - */ -class UnsupportedAlgorithmException extends \RuntimeException -{ -} diff --git a/src/phpseclib/File/ANSI.php b/src/phpseclib/File/ANSI.php deleted file mode 100755 index 1f3eecb3..00000000 --- a/src/phpseclib/File/ANSI.php +++ /dev/null @@ -1,574 +0,0 @@ - - * @copyright 2012 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\File; - -/** - * Pure-PHP ANSI Decoder - * - * @package ANSI - * @author Jim Wigginton - * @access public - */ -class ANSI -{ - /** - * Max Width - * - * @var int - * @access private - */ - var $max_x; - - /** - * Max Height - * - * @var int - * @access private - */ - var $max_y; - - /** - * Max History - * - * @var int - * @access private - */ - var $max_history; - - /** - * History - * - * @var array - * @access private - */ - var $history; - - /** - * History Attributes - * - * @var array - * @access private - */ - var $history_attrs; - - /** - * Current Column - * - * @var int - * @access private - */ - var $x; - - /** - * Current Row - * - * @var int - * @access private - */ - var $y; - - /** - * Old Column - * - * @var int - * @access private - */ - var $old_x; - - /** - * Old Row - * - * @var int - * @access private - */ - var $old_y; - - /** - * An empty attribute cell - * - * @var object - * @access private - */ - var $base_attr_cell; - - /** - * The current attribute cell - * - * @var object - * @access private - */ - var $attr_cell; - - /** - * An empty attribute row - * - * @var array - * @access private - */ - var $attr_row; - - /** - * The current screen text - * - * @var array - * @access private - */ - var $screen; - - /** - * The current screen attributes - * - * @var array - * @access private - */ - var $attrs; - - /** - * Current ANSI code - * - * @var string - * @access private - */ - var $ansi; - - /** - * Tokenization - * - * @var array - * @access private - */ - var $tokenization; - - /** - * Default Constructor. - * - * @return \phpseclib\File\ANSI - * @access public - */ - function __construct() - { - $attr_cell = new \stdClass(); - $attr_cell->bold = false; - $attr_cell->underline = false; - $attr_cell->blink = false; - $attr_cell->background = 'black'; - $attr_cell->foreground = 'white'; - $attr_cell->reverse = false; - $this->base_attr_cell = clone $attr_cell; - $this->attr_cell = clone $attr_cell; - - $this->setHistory(200); - $this->setDimensions(80, 24); - } - - /** - * Set terminal width and height - * - * Resets the screen as well - * - * @param int $x - * @param int $y - * @access public - */ - function setDimensions($x, $y) - { - $this->max_x = $x - 1; - $this->max_y = $y - 1; - $this->x = $this->y = 0; - $this->history = $this->history_attrs = array(); - $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell); - $this->screen = array_fill(0, $this->max_y + 1, ''); - $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row); - $this->ansi = ''; - } - - /** - * Set the number of lines that should be logged past the terminal height - * - * @param int $x - * @param int $y - * @access public - */ - function setHistory($history) - { - $this->max_history = $history; - } - - /** - * Load a string - * - * @param string $source - * @access public - */ - function loadString($source) - { - $this->setDimensions($this->max_x + 1, $this->max_y + 1); - $this->appendString($source); - } - - /** - * Appdend a string - * - * @param string $source - * @access public - */ - function appendString($source) - { - $this->tokenization = array(''); - for ($i = 0; $i < strlen($source); $i++) { - if (strlen($this->ansi)) { - $this->ansi.= $source[$i]; - $chr = ord($source[$i]); - // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements - // single character CSI's not currently supported - switch (true) { - case $this->ansi == "\x1B=": - $this->ansi = ''; - continue 2; - case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['): - case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: - break; - default: - continue 2; - } - $this->tokenization[] = $this->ansi; - $this->tokenization[] = ''; - // http://ascii-table.com/ansi-escape-sequences-vt-100.php - switch ($this->ansi) { - case "\x1B[H": // Move cursor to upper left corner - $this->old_x = $this->x; - $this->old_y = $this->y; - $this->x = $this->y = 0; - break; - case "\x1B[J": // Clear screen from cursor down - $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); - $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); - - $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); - $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row)); - - if (count($this->history) == $this->max_history) { - array_shift($this->history); - array_shift($this->history_attrs); - } - case "\x1B[K": // Clear screen from cursor right - $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); - - array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - $this->x - 1, $this->base_attr_cell)); - break; - case "\x1B[2K": // Clear entire line - $this->screen[$this->y] = str_repeat(' ', $this->x); - $this->attrs[$this->y] = $this->attr_row; - break; - case "\x1B[?1h": // set cursor key to application - case "\x1B[?25h": // show the cursor - case "\x1B(B": // set united states g0 character set - break; - case "\x1BE": // Move to next line - $this->_newLine(); - $this->x = 0; - break; - default: - switch (true) { - case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines - $this->old_y = $this->y; - $this->y+= $match[1]; - break; - case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h - $this->old_x = $this->x; - $this->old_y = $this->y; - $this->x = $match[2] - 1; - $this->y = $match[1] - 1; - break; - case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines - $this->old_x = $this->x; - $this->x+= $match[1]; - break; - case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines - $this->old_x = $this->x; - $this->x-= $match[1]; - break; - case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window - break; - case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): // character attributes - $attr_cell = &$this->attr_cell; - $mods = explode(';', $match[1]); - foreach ($mods as $mod) { - switch ($mod) { - case 0: // Turn off character attributes - $attr_cell = clone $this->base_attr_cell; - break; - case 1: // Turn bold mode on - $attr_cell->bold = true; - break; - case 4: // Turn underline mode on - $attr_cell->underline = true; - break; - case 5: // Turn blinking mode on - $attr_cell->blink = true; - break; - case 7: // Turn reverse video on - $attr_cell->reverse = !$attr_cell->reverse; - $temp = $attr_cell->background; - $attr_cell->background = $attr_cell->foreground; - $attr_cell->foreground = $temp; - break; - default: // set colors - //$front = $attr_cell->reverse ? &$attr_cell->background : &$attr_cell->foreground; - $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' }; - //$back = $attr_cell->reverse ? &$attr_cell->foreground : &$attr_cell->background; - $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; - switch ($mod) { - // @codingStandardsIgnoreStart - case 30: $front = 'black'; break; - case 31: $front = 'red'; break; - case 32: $front = 'green'; break; - case 33: $front = 'yellow'; break; - case 34: $front = 'blue'; break; - case 35: $front = 'magenta'; break; - case 36: $front = 'cyan'; break; - case 37: $front = 'white'; break; - - case 40: $back = 'black'; break; - case 41: $back = 'red'; break; - case 42: $back = 'green'; break; - case 43: $back = 'yellow'; break; - case 44: $back = 'blue'; break; - case 45: $back = 'magenta'; break; - case 46: $back = 'cyan'; break; - case 47: $back = 'white'; break; - // @codingStandardsIgnoreEnd - - default: - //user_error('Unsupported attribute: ' . $mod); - $this->ansi = ''; - break 2; - } - } - } - break; - default: - //user_error("{$this->ansi} is unsupported\r\n"); - } - } - $this->ansi = ''; - continue; - } - - $this->tokenization[count($this->tokenization) - 1].= $source[$i]; - switch ($source[$i]) { - case "\r": - $this->x = 0; - break; - case "\n": - $this->_newLine(); - break; - case "\x08": // backspace - if ($this->x) { - $this->x--; - $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell; - $this->screen[$this->y] = substr_replace( - $this->screen[$this->y], - $source[$i], - $this->x, - 1 - ); - } - break; - case "\x0F": // shift - break; - case "\x1B": // start ANSI escape code - $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1); - //if (!strlen($this->tokenization[count($this->tokenization) - 1])) { - // array_pop($this->tokenization); - //} - $this->ansi.= "\x1B"; - break; - default: - $this->attrs[$this->y][$this->x] = clone $this->attr_cell; - if ($this->x > strlen($this->screen[$this->y])) { - $this->screen[$this->y] = str_repeat(' ', $this->x); - } - $this->screen[$this->y] = substr_replace( - $this->screen[$this->y], - $source[$i], - $this->x, - 1 - ); - - if ($this->x > $this->max_x) { - $this->x = 0; - $this->y++; - } else { - $this->x++; - } - } - } - } - - /** - * Add a new line - * - * Also update the $this->screen and $this->history buffers - * - * @access private - */ - function _newLine() - { - //if ($this->y < $this->max_y) { - // $this->y++; - //} - - while ($this->y >= $this->max_y) { - $this->history = array_merge($this->history, array(array_shift($this->screen))); - $this->screen[] = ''; - - $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); - $this->attrs[] = $this->attr_row; - - if (count($this->history) >= $this->max_history) { - array_shift($this->history); - array_shift($this->history_attrs); - } - - $this->y--; - } - $this->y++; - } - - /** - * Returns the current coordinate without preformating - * - * @access private - * @return string - */ - function _processCoordinate($last_attr, $cur_attr, $char) - { - $output = ''; - - if ($last_attr != $cur_attr) { - $close = $open = ''; - if ($last_attr->foreground != $cur_attr->foreground) { - if ($cur_attr->foreground != 'white') { - $open.= ''; - } - if ($last_attr->foreground != 'white') { - $close = '' . $close; - } - } - if ($last_attr->background != $cur_attr->background) { - if ($cur_attr->background != 'black') { - $open.= ''; - } - if ($last_attr->background != 'black') { - $close = '' . $close; - } - } - if ($last_attr->bold != $cur_attr->bold) { - if ($cur_attr->bold) { - $open.= ''; - } else { - $close = '' . $close; - } - } - if ($last_attr->underline != $cur_attr->underline) { - if ($cur_attr->underline) { - $open.= ''; - } else { - $close = '' . $close; - } - } - if ($last_attr->blink != $cur_attr->blink) { - if ($cur_attr->blink) { - $open.= ''; - } else { - $close = '' . $close; - } - } - $output.= $close . $open; - } - - $output.= htmlspecialchars($char); - - return $output; - } - - /** - * Returns the current screen without preformating - * - * @access private - * @return string - */ - function _getScreen() - { - $output = ''; - $last_attr = $this->base_attr_cell; - for ($i = 0; $i <= $this->max_y; $i++) { - for ($j = 0; $j <= $this->max_x; $j++) { - $cur_attr = $this->attrs[$i][$j]; - $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : ''); - $last_attr = $this->attrs[$i][$j]; - } - $output.= "\r\n"; - } - $output = substr($output, 0, -2); - // close any remaining open tags - $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, ''); - return rtrim($output); - } - - /** - * Returns the current screen - * - * @access public - * @return string - */ - function getScreen() - { - return '
' . $this->_getScreen() . '
'; - } - - /** - * Returns the current screen and the x previous lines - * - * @access public - * @return string - */ - function getHistory() - { - $scrollback = ''; - $last_attr = $this->base_attr_cell; - for ($i = 0; $i < count($this->history); $i++) { - for ($j = 0; $j <= $this->max_x + 1; $j++) { - $cur_attr = $this->history_attrs[$i][$j]; - $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : ''); - $last_attr = $this->history_attrs[$i][$j]; - } - $scrollback.= "\r\n"; - } - $base_attr_cell = $this->base_attr_cell; - $this->base_attr_cell = $last_attr; - $scrollback.= $this->_getScreen(); - $this->base_attr_cell = $base_attr_cell; - - return '
' . $scrollback . '
'; - } -} diff --git a/src/phpseclib/File/ASN1.php b/src/phpseclib/File/ASN1.php index b3126b5d..b6ac36b0 100755 --- a/src/phpseclib/File/ASN1.php +++ b/src/phpseclib/File/ASN1.php @@ -3,109 +3,159 @@ /** * Pure-PHP ASN.1 Parser * - * PHP version 5 + * PHP versions 4 and 5 * * ASN.1 provides the semantics for data encoded using various schemes. The most commonly * utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded * DER blobs. * - * \phpseclib\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context. + * ASN1 decodes and encodes DER formatted messages and places them in a semantic context. * * Uses the 1988 ASN.1 syntax. * + * LICENSE: 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. + * * @category File * @package ASN1 * @author Jim Wigginton - * @copyright 2012 Jim Wigginton + * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\File; -use ParagonIE\ConstantTime\Base64; -use phpseclib\File\ASN1\Element; +/**#@+ + * Tag Classes + * + * @access private + * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 + */ use phpseclib\Math\BigInteger; +@define('FILE_ASN1_CLASS_UNIVERSAL', 0); +@define('FILE_ASN1_CLASS_APPLICATION', 1); +@define('FILE_ASN1_CLASS_CONTEXT_SPECIFIC', 2); +@define('FILE_ASN1_CLASS_PRIVATE', 3); +/**#@-*/ + +/**#@+ + * Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node124.html + */ +@define('FILE_ASN1_TYPE_BOOLEAN', 1); +@define('FILE_ASN1_TYPE_INTEGER', 2); +@define('FILE_ASN1_TYPE_BIT_STRING', 3); +@define('FILE_ASN1_TYPE_OCTET_STRING', 4); +@define('FILE_ASN1_TYPE_NULL', 5); +@define('FILE_ASN1_TYPE_OBJECT_IDENTIFIER', 6); +//@define('FILE_ASN1_TYPE_OBJECT_DESCRIPTOR', 7); +//@define('FILE_ASN1_TYPE_INSTANCE_OF', 8); // EXTERNAL +@define('FILE_ASN1_TYPE_REAL', 9); +@define('FILE_ASN1_TYPE_ENUMERATED', 10); +//@define('FILE_ASN1_TYPE_EMBEDDED', 11); +@define('FILE_ASN1_TYPE_UTF8_STRING', 12); +//@define('FILE_ASN1_TYPE_RELATIVE_OID', 13); +@define('FILE_ASN1_TYPE_SEQUENCE', 16); // SEQUENCE OF +@define('FILE_ASN1_TYPE_SET', 17); // SET OF +/**#@-*/ +/**#@+ + * More Tag Classes + * + * @access private + * @link http://www.obj-sys.com/asn1tutorial/node10.html + */ +@define('FILE_ASN1_TYPE_NUMERIC_STRING', 18); +@define('FILE_ASN1_TYPE_PRINTABLE_STRING', 19); +@define('FILE_ASN1_TYPE_TELETEX_STRING', 20); // T61String +@define('FILE_ASN1_TYPE_VIDEOTEX_STRING', 21); +@define('FILE_ASN1_TYPE_IA5_STRING', 22); +@define('FILE_ASN1_TYPE_UTC_TIME', 23); +@define('FILE_ASN1_TYPE_GENERALIZED_TIME', 24); +@define('FILE_ASN1_TYPE_GRAPHIC_STRING', 25); +@define('FILE_ASN1_TYPE_VISIBLE_STRING', 26); // ISO646String +@define('FILE_ASN1_TYPE_GENERAL_STRING', 27); +@define('FILE_ASN1_TYPE_UNIVERSAL_STRING', 28); +//@define('FILE_ASN1_TYPE_CHARACTER_STRING', 29); +@define('FILE_ASN1_TYPE_BMP_STRING', 30); +/**#@-*/ + +/**#@+ + * Tag Aliases + * + * These tags are kinda place holders for other tags. + * + * @access private + */ +@define('FILE_ASN1_TYPE_CHOICE', -1); +@define('FILE_ASN1_TYPE_ANY', -2); +/**#@-*/ + /** - * Pure-PHP ASN.1 Parser + * ASN.1 Element + * + * Bypass normal encoding rules in ASN1::encodeDER() * * @package ASN1 * @author Jim Wigginton + * @version 0.3.0 * @access public */ -class ASN1 +class ASN1_Element { - /**#@+ - * Tag Classes + /** + * Raw element value * + * @var String * @access private - * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12 */ - const CLASS_UNIVERSAL = 0; - const CLASS_APPLICATION = 1; - const CLASS_CONTEXT_SPECIFIC = 2; - const CLASS_PRIVATE = 3; - /**#@-*/ - - /**#@+ - * Tag Classes - * - * @access private - * @link http://www.obj-sys.com/asn1tutorial/node124.html - */ - const TYPE_BOOLEAN = 1; - const TYPE_INTEGER = 2; - const TYPE_BIT_STRING = 3; - const TYPE_OCTET_STRING = 4; - const TYPE_NULL = 5; - const TYPE_OBJECT_IDENTIFIER = 6; - //const TYPE_OBJECT_DESCRIPTOR = 7; - //const TYPE_INSTANCE_OF = 8; // EXTERNAL - const TYPE_REAL = 9; - const TYPE_ENUMERATED = 10; - //const TYPE_EMBEDDED = 11; - const TYPE_UTF8_STRING = 12; - //const TYPE_RELATIVE_OID = 13; - const TYPE_SEQUENCE = 16; // SEQUENCE OF - const TYPE_SET = 17; // SET OF - /**#@-*/ - /**#@+ - * More Tag Classes - * - * @access private - * @link http://www.obj-sys.com/asn1tutorial/node10.html - */ - const TYPE_NUMERIC_STRING = 18; - const TYPE_PRINTABLE_STRING = 19; - const TYPE_TELETEX_STRING = 20; // T61String - const TYPE_VIDEOTEX_STRING = 21; - const TYPE_IA5_STRING = 22; - const TYPE_UTC_TIME = 23; - const TYPE_GENERALIZED_TIME = 24; - const TYPE_GRAPHIC_STRING = 25; - const TYPE_VISIBLE_STRING = 26; // ISO646String - const TYPE_GENERAL_STRING = 27; - const TYPE_UNIVERSAL_STRING = 28; - //const TYPE_CHARACTER_STRING = 29; - const TYPE_BMP_STRING = 30; - /**#@-*/ - - /**#@+ - * Tag Aliases - * - * These tags are kinda place holders for other tags. + var $element; + + /** + * Constructor * - * @access private - */ - const TYPE_CHOICE = -1; - const TYPE_ANY = -2; - /**#@-*/ + * @param String $encoded + * @return ASN1_Element + * @access public + */ + function __construct($encoded) + { + $this->element = $encoded; + } +} +/** + * Pure-PHP ASN.1 Parser + * + * @package ASN1 + * @author Jim Wigginton + * @version 0.3.0 + * @access public + */ +class ASN1 +{ /** * ASN.1 object identifier * - * @var array + * @var Array * @access private * @link http://en.wikipedia.org/wiki/Object_identifier */ @@ -114,19 +164,19 @@ class ASN1 /** * Default date format * - * @var string + * @var String * @access private * @link http://php.net/class.datetime */ - var $format = 'D, d M Y H:i:s O'; + var $format = 'D, d M y H:i:s O'; /** * Default date format * - * @var array + * @var Array * @access private - * @see self::setTimeFormat() - * @see self::asn1map() + * @see ASN1::setTimeFormat() + * @see ASN1::asn1map() * @link http://php.net/class.datetime */ var $encoded; @@ -134,47 +184,47 @@ class ASN1 /** * Filters * - * If the mapping type is self::TYPE_ANY what do we actually encode it as? + * If the mapping type is FILE_ASN1_TYPE_ANY what do we actually encode it as? * - * @var array + * @var Array * @access private - * @see self::_encode_der() + * @see ASN1::_encode_der() */ var $filters; /** * Type mapping table for the ANY type. * - * Structured or unknown types are mapped to a \phpseclib\File\ASN1\Element. + * Structured or unknown types are mapped to a FILE_ASN1_Element. * Unambiguous types get the direct mapping (int/real/bool). * Others are mapped as a choice, with an extra indexing level. * - * @var array + * @var Array * @access public */ var $ANYmap = array( - self::TYPE_BOOLEAN => true, - self::TYPE_INTEGER => true, - self::TYPE_BIT_STRING => 'bitString', - self::TYPE_OCTET_STRING => 'octetString', - self::TYPE_NULL => 'null', - self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', - self::TYPE_REAL => true, - self::TYPE_ENUMERATED => 'enumerated', - self::TYPE_UTF8_STRING => 'utf8String', - self::TYPE_NUMERIC_STRING => 'numericString', - self::TYPE_PRINTABLE_STRING => 'printableString', - self::TYPE_TELETEX_STRING => 'teletexString', - self::TYPE_VIDEOTEX_STRING => 'videotexString', - self::TYPE_IA5_STRING => 'ia5String', - self::TYPE_UTC_TIME => 'utcTime', - self::TYPE_GENERALIZED_TIME => 'generalTime', - self::TYPE_GRAPHIC_STRING => 'graphicString', - self::TYPE_VISIBLE_STRING => 'visibleString', - self::TYPE_GENERAL_STRING => 'generalString', - self::TYPE_UNIVERSAL_STRING => 'universalString', - //self::TYPE_CHARACTER_STRING => 'characterString', - self::TYPE_BMP_STRING => 'bmpString' + FILE_ASN1_TYPE_BOOLEAN => true, + FILE_ASN1_TYPE_INTEGER => true, + FILE_ASN1_TYPE_BIT_STRING => 'bitString', + FILE_ASN1_TYPE_OCTET_STRING => 'octetString', + FILE_ASN1_TYPE_NULL => 'null', + FILE_ASN1_TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', + FILE_ASN1_TYPE_REAL => true, + FILE_ASN1_TYPE_ENUMERATED => 'enumerated', + FILE_ASN1_TYPE_UTF8_STRING => 'utf8String', + FILE_ASN1_TYPE_NUMERIC_STRING => 'numericString', + FILE_ASN1_TYPE_PRINTABLE_STRING => 'printableString', + FILE_ASN1_TYPE_TELETEX_STRING => 'teletexString', + FILE_ASN1_TYPE_VIDEOTEX_STRING => 'videotexString', + FILE_ASN1_TYPE_IA5_STRING => 'ia5String', + FILE_ASN1_TYPE_UTC_TIME => 'utcTime', + FILE_ASN1_TYPE_GENERALIZED_TIME => 'generalTime', + FILE_ASN1_TYPE_GRAPHIC_STRING => 'graphicString', + FILE_ASN1_TYPE_VISIBLE_STRING => 'visibleString', + FILE_ASN1_TYPE_GENERAL_STRING => 'generalString', + FILE_ASN1_TYPE_UNIVERSAL_STRING => 'universalString', + //FILE_ASN1_TYPE_CHARACTER_STRING => 'characterString', + FILE_ASN1_TYPE_BMP_STRING => 'bmpString' ); /** @@ -183,342 +233,308 @@ class ASN1 * Non-convertable types are absent from this table. * size == 0 indicates variable length encoding. * - * @var array + * @var Array * @access public */ var $stringTypeSize = array( - self::TYPE_UTF8_STRING => 0, - self::TYPE_BMP_STRING => 2, - self::TYPE_UNIVERSAL_STRING => 4, - self::TYPE_PRINTABLE_STRING => 1, - self::TYPE_TELETEX_STRING => 1, - self::TYPE_IA5_STRING => 1, - self::TYPE_VISIBLE_STRING => 1, + FILE_ASN1_TYPE_UTF8_STRING => 0, + FILE_ASN1_TYPE_BMP_STRING => 2, + FILE_ASN1_TYPE_UNIVERSAL_STRING => 4, + FILE_ASN1_TYPE_PRINTABLE_STRING => 1, + FILE_ASN1_TYPE_TELETEX_STRING => 1, + FILE_ASN1_TYPE_IA5_STRING => 1, + FILE_ASN1_TYPE_VISIBLE_STRING => 1, ); + /** + * Default Constructor. + * + * @access public + */ + function __construct() + { + static $static_init = null; + if (!$static_init) { + $static_init = true; + } + } + /** * Parse BER-encoding * * Serves a similar purpose to openssl's asn1parse * - * @param string $encoded - * @return array + * @param String $encoded + * @return Array * @access public */ function decodeBER($encoded) { - if ($encoded instanceof Element) { + if (is_object($encoded) && strtolower(get_class($encoded)) == 'file_asn1_element') { $encoded = $encoded->element; } $this->encoded = $encoded; - // encapsulate in an array for BC with the old decodeBER - return array($this->_decode_ber($encoded)); + return $this->_decode_ber($encoded); } /** * Parse BER-encoding (Helper function) * * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode. - * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and - * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used. + * $encoded is passed by reference for the recursive calls done for FILE_ASN1_TYPE_BIT_STRING and + * FILE_ASN1_TYPE_OCTET_STRING. In those cases, the indefinite length is used. * - * @param string $encoded - * @param int $start - * @param int $encoded_pos - * @return array + * @param String $encoded + * @param Integer $start + * @return Array * @access private */ - function _decode_ber($encoded, $start = 0, $encoded_pos = 0) + function _decode_ber(&$encoded, $start = 0) { - $current = array('start' => $start); - - $type = ord($encoded[$encoded_pos++]); - $start++; - - $constructed = ($type >> 5) & 1; - - $tag = $type & 0x1F; - if ($tag == 0x1F) { - $tag = 0; - // process septets (since the eighth bit is ignored, it's not an octet) - do { - $loop = ord($encoded[0]) >> 7; - $tag <<= 7; - $tag |= ord($encoded[$encoded_pos++]) & 0x7F; - $start++; - } while ($loop); - } - - // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 - $length = ord($encoded[$encoded_pos++]); - $start++; - if ($length == 0x80) { // indefinite length - // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all - // immediately available." -- paragraph 8.1.3.2.c - $length = strlen($encoded) - $encoded_pos; - } elseif ($length & 0x80) { // definite length, long form - // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only - // support it up to four. - $length&= 0x7F; - $temp = substr($encoded, $encoded_pos, $length); - $encoded_pos += $length; - // tags of indefinte length don't really have a header length; this length includes the tag - $current+= array('headerlength' => $length + 2); - $start+= $length; - extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); - } else { - $current+= array('headerlength' => 2); - } + $decoded = array(); + + while ( strlen($encoded) ) { + $current = array('start' => $start); + + $type = ord($this->_string_shift($encoded)); + $start++; + + $constructed = ($type >> 5) & 1; + + $tag = $type & 0x1F; + if ($tag == 0x1F) { + $tag = 0; + // process septets (since the eighth bit is ignored, it's not an octet) + do { + $loop = ord($encoded[0]) >> 7; + $tag <<= 7; + $tag |= ord($this->_string_shift($encoded)) & 0x7F; + $start++; + } while ( $loop ); + } - if ($length > (strlen($encoded) - $encoded_pos)) { - return false; - } + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 + $length = ord($this->_string_shift($encoded)); + $start++; + if ( $length == 0x80 ) { // indefinite length + // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all + // immediately available." -- paragraph 8.1.3.2.c + //if ( !$constructed ) { + // return false; + //} + $length = strlen($encoded); + } elseif ( $length & 0x80 ) { // definite length, long form + // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only + // support it up to four. + $length&= 0x7F; + $temp = $this->_string_shift($encoded, $length); + // tags of indefinite length don't really have a header length; this length includes the tag + $current+= array('headerlength' => $length + 2); + $start+= $length; + extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); + } else { + $current+= array('headerlength' => 2); + } - $content = substr($encoded, $encoded_pos, $length); - $content_pos = 0; - - // at this point $length can be overwritten. it's only accurate for definite length things as is - - /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 - built-in types. It defines an application-independent data type that must be distinguishable from all other - data types. The other three classes are user defined. The APPLICATION class distinguishes data types that - have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within - a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the - alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this - data type; the term CONTEXT-SPECIFIC does not appear. - - -- http://www.obj-sys.com/asn1tutorial/node12.html */ - $class = ($type >> 6) & 3; - switch ($class) { - case self::CLASS_APPLICATION: - case self::CLASS_PRIVATE: - case self::CLASS_CONTEXT_SPECIFIC: - if (!$constructed) { - return array( + // End-of-content, see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 + if (!$type && !$length) { + return $decoded; + } + $content = $this->_string_shift($encoded, $length); + + /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1 + built-in types. It defines an application-independent data type that must be distinguishable from all other + data types. The other three classes are user defined. The APPLICATION class distinguishes data types that + have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within + a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the + alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this + data type; the term CONTEXT-SPECIFIC does not appear. + + -- http://www.obj-sys.com/asn1tutorial/node12.html */ + $class = ($type >> 6) & 3; + switch ($class) { + case FILE_ASN1_CLASS_APPLICATION: + case FILE_ASN1_CLASS_PRIVATE: + case FILE_ASN1_CLASS_CONTEXT_SPECIFIC: + $decoded[] = array( 'type' => $class, 'constant' => $tag, - 'content' => $content, + 'content' => $constructed ? $this->_decode_ber($content, $start) : $content, 'length' => $length + $start - $current['start'] - ); - } - - $newcontent = array(); - $remainingLength = $length; - while ($remainingLength > 0) { - $temp = $this->_decode_ber($content, $start); - $length = $temp['length']; - // end-of-content octets - see paragraph 8.1.5 - if (substr($content, $content_pos + $length, 2) == "\0\0") { - $length+= 2; - $start+= $length; - $newcontent[] = $temp; - break; - } + ) + $current; $start+= $length; - $remainingLength-= $length; - $newcontent[] = $temp; - $content_pos += $length; - } - - return array( - 'type' => $class, - 'constant' => $tag, - // the array encapsulation is for BC with the old format - 'content' => $newcontent, - // the only time when $content['headerlength'] isn't defined is when the length is indefinite. - // the absence of $content['headerlength'] is how we know if something is indefinite or not. - // technically, it could be defined to be 2 and then another indicator could be used but whatever. - 'length' => $start - $current['start'] - ) + $current; - } + continue 2; + } - $current+= array('type' => $tag); + $current+= array('type' => $tag); - // decode UNIVERSAL tags - switch ($tag) { - case self::TYPE_BOOLEAN: - // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 - //if (strlen($content) != 1) { - // return false; - //} - $current['content'] = (bool) ord($content[$content_pos]); - break; - case self::TYPE_INTEGER: - case self::TYPE_ENUMERATED: - $current['content'] = new BigInteger(substr($content, $content_pos), -256); - break; - case self::TYPE_REAL: // not currently supported - return false; - case self::TYPE_BIT_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. - if (!$constructed) { - $current['content'] = substr($content, $content_pos); - } else { - $temp = $this->_decode_ber($content, $start, $content_pos); - $length-= (strlen($content) - $content_pos); - $last = count($temp) - 1; - for ($i = 0; $i < $last; $i++) { - // all subtags should be bit strings - //if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { - // return false; - //} - $current['content'].= substr($temp[$i]['content'], 1); - } - // all subtags should be bit strings - //if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { + // decode UNIVERSAL tags + switch ($tag) { + case FILE_ASN1_TYPE_BOOLEAN: + // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 + //if (strlen($content) != 1) { // return false; //} - $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); - } - break; - case self::TYPE_OCTET_STRING: - if (!$constructed) { - $current['content'] = substr($content, $content_pos); - } else { - $current['content'] = ''; - $length = 0; - while (substr($content, $content_pos, 2) != "\0\0") { - $temp = $this->_decode_ber($content, $length + $start, $content_pos); - $content_pos += $temp['length']; - // all subtags should be octet strings - //if ($temp['type'] != self::TYPE_OCTET_STRING) { + $current['content'] = (bool) ord($content[0]); + break; + case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: + $current['content'] = new BigInteger($content, -256); + break; + case FILE_ASN1_TYPE_REAL: // not currently supported + return false; + case FILE_ASN1_TYPE_BIT_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. + if (!$constructed) { + $current['content'] = $content; + } else { + $temp = $this->_decode_ber($content, $start); + $length-= strlen($content); + $last = count($temp) - 1; + for ($i = 0; $i < $last; $i++) { + // all subtags should be bit strings + //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) { + // return false; + //} + $current['content'].= substr($temp[$i]['content'], 1); + } + // all subtags should be bit strings + //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) { // return false; //} - $current['content'].= $temp['content']; - $length+= $temp['length']; - } - if (substr($content, $content_pos, 2) == "\0\0") { - $length+= 2; // +2 for the EOC + $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); } - } - break; - case self::TYPE_NULL: - // "The contents octets shall not contain any octets." -- paragraph 8.8.2 - //if (strlen($content)) { - // return false; - //} - break; - case self::TYPE_SEQUENCE: - case self::TYPE_SET: - $offset = 0; - $current['content'] = array(); - $content_len = strlen($content); - while ($content_pos < $content_len) { - // if indefinite length construction was used and we have an end-of-content string next - // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 - if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") { - $length = $offset + 2; // +2 for the EOC - break 2; + break; + case FILE_ASN1_TYPE_OCTET_STRING: + if (!$constructed) { + $current['content'] = $content; + } else { + $temp = $this->_decode_ber($content, $start); + $length-= strlen($content); + for ($i = 0, $size = count($temp); $i < $size; $i++) { + // all subtags should be octet strings + //if ($temp[$i]['type'] != FILE_ASN1_TYPE_OCTET_STRING) { + // return false; + //} + $current['content'].= $temp[$i]['content']; + } + // $length = } - $temp = $this->_decode_ber($content, $start + $offset, $content_pos); - $content_pos += $temp['length']; - $current['content'][] = $temp; - $offset+= $temp['length']; - } - break; - case self::TYPE_OBJECT_IDENTIFIER: - $temp = ord($content[$content_pos++]); - $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); - $valuen = 0; - // process septets - $content_len = strlen($content); - while ($content_pos < $content_len) { - $temp = ord($content[$content_pos++]); - $valuen <<= 7; - $valuen |= $temp & 0x7F; - if (~$temp & 0x80) { - $current['content'].= ".$valuen"; - $valuen = 0; + break; + case FILE_ASN1_TYPE_NULL: + // "The contents octets shall not contain any octets." -- paragraph 8.8.2 + //if (strlen($content)) { + // return false; + //} + break; + case FILE_ASN1_TYPE_SEQUENCE: + case FILE_ASN1_TYPE_SET: + $current['content'] = $this->_decode_ber($content, $start); + break; + case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + $temp = ord($this->_string_shift($content)); + $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); + $valuen = 0; + // process septets + while (strlen($content)) { + $temp = ord($this->_string_shift($content)); + $valuen <<= 7; + $valuen |= $temp & 0x7F; + if (~$temp & 0x80) { + $current['content'].= ".$valuen"; + $valuen = 0; + } } - } - // the eighth bit of the last byte should not be 1 - //if ($temp >> 7) { - // return false; - //} - break; - /* Each character string type shall be encoded as if it had been declared: - [UNIVERSAL x] IMPLICIT OCTET STRING - - -- X.690-0207.pdf#page=23 (paragraph 8.21.3) - - Per that, we're not going to do any validation. If there are any illegal characters in the string, - we don't really care */ - case self::TYPE_NUMERIC_STRING: - // 0,1,2,3,4,5,6,7,8,9, and space - case self::TYPE_PRINTABLE_STRING: - // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, - // hyphen, full stop, solidus, colon, equal sign, question mark - case self::TYPE_TELETEX_STRING: - // The Teletex character set in CCITT's T61, space, and delete - // see http://en.wikipedia.org/wiki/Teletex#Character_sets - case self::TYPE_VIDEOTEX_STRING: - // The Videotex character set in CCITT's T.100 and T.101, space, and delete - case self::TYPE_VISIBLE_STRING: - // Printing character sets of international ASCII, and space - case self::TYPE_IA5_STRING: - // International Alphabet 5 (International ASCII) - case self::TYPE_GRAPHIC_STRING: - // All registered G sets, and space - case self::TYPE_GENERAL_STRING: - // All registered C and G sets, space and delete - case self::TYPE_UTF8_STRING: - // ???? - case self::TYPE_BMP_STRING: - $current['content'] = substr($content, $content_pos); - break; - case self::TYPE_UTC_TIME: - case self::TYPE_GENERALIZED_TIME: - $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); - default: - } + // the eighth bit of the last byte should not be 1 + //if ($temp >> 7) { + // return false; + //} + break; + /* Each character string type shall be encoded as if it had been declared: + [UNIVERSAL x] IMPLICIT OCTET STRING + + -- X.690-0207.pdf#page=23 (paragraph 8.21.3) + + Per that, we're not going to do any validation. If there are any illegal characters in the string, + we don't really care */ + case FILE_ASN1_TYPE_NUMERIC_STRING: + // 0,1,2,3,4,5,6,7,8,9, and space + case FILE_ASN1_TYPE_PRINTABLE_STRING: + // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma, + // hyphen, full stop, solidus, colon, equal sign, question mark + case FILE_ASN1_TYPE_TELETEX_STRING: + // The Teletex character set in CCITT's T61, space, and delete + // see http://en.wikipedia.org/wiki/Teletex#Character_sets + case FILE_ASN1_TYPE_VIDEOTEX_STRING: + // The Videotex character set in CCITT's T.100 and T.101, space, and delete + case FILE_ASN1_TYPE_VISIBLE_STRING: + // Printing character sets of international ASCII, and space + case FILE_ASN1_TYPE_IA5_STRING: + // International Alphabet 5 (International ASCII) + case FILE_ASN1_TYPE_GRAPHIC_STRING: + // All registered G sets, and space + case FILE_ASN1_TYPE_GENERAL_STRING: + // All registered C and G sets, space and delete + case FILE_ASN1_TYPE_UTF8_STRING: + // ???? + case FILE_ASN1_TYPE_BMP_STRING: + $current['content'] = $content; + break; + case FILE_ASN1_TYPE_UTC_TIME: + case FILE_ASN1_TYPE_GENERALIZED_TIME: + $current['content'] = $this->_decodeTime($content, $tag); + default: + + } - $start+= $length; + $start+= $length; + $decoded[] = $current + array('length' => $start - $current['start']); + } - // ie. length is the length of the full TLV encoding - it's not just the length of the value - return $current + array('length' => $start - $current['start']); + return $decoded; } /** - * ASN.1 Map + * ASN.1 Decode * * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * * "Special" mappings may be applied on a per tag-name basis via $special. * - * @param array $decoded - * @param array $mapping - * @param array $special - * @return array + * @param Array $decoded + * @param Array $mapping + * @param Array $special + * @return Array * @access public */ function asn1map($decoded, $mapping, $special = array()) { - if (isset($mapping['explicit']) && is_array($decoded['content'])) { + if (isset($mapping['explicit'])) { $decoded = $decoded['content'][0]; } switch (true) { - case $mapping['type'] == self::TYPE_ANY: + case $mapping['type'] == FILE_ASN1_TYPE_ANY: $intype = $decoded['type']; if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) { - return new Element(substr($this->encoded, $decoded['start'], $decoded['length'])); + return new ASN1_Element(substr($this->encoded, $decoded['start'], $decoded['length'])); } $inmap = $this->ANYmap[$intype]; if (is_string($inmap)) { return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special)); } break; - case $mapping['type'] == self::TYPE_CHOICE: + case $mapping['type'] == FILE_ASN1_TYPE_CHOICE: foreach ($mapping['children'] as $key => $option) { switch (true) { case isset($option['constant']) && $option['constant'] == $decoded['constant']: case !isset($option['constant']) && $option['type'] == $decoded['type']: $value = $this->asn1map($decoded, $option, $special); break; - case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE: + case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE: $v = $this->asn1map($decoded, $option, $special); if (isset($v)) { $value = $v; @@ -537,15 +553,7 @@ function asn1map($decoded, $mapping, $special = array()) case $decoded['type'] == $mapping['type']: break; default: - // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings, - // let it through - switch (true) { - case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18 - case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30 - case $mapping['type'] < 18: - case $mapping['type'] > 30: - return null; - } + return null; } if (isset($mapping['implicit'])) { @@ -553,7 +561,7 @@ function asn1map($decoded, $mapping, $special = array()) } switch ($decoded['type']) { - case self::TYPE_SEQUENCE: + case FILE_ASN1_TYPE_SEQUENCE: $map = array(); // ignore the min and max @@ -576,18 +584,18 @@ function asn1map($decoded, $mapping, $special = array()) if ($maymatch) { $temp = $decoded['content'][$i]; - if ($child['type'] != self::TYPE_CHOICE) { + if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { // Get the mapping and input class & constant. - $childClass = $tempClass = self::CLASS_UNIVERSAL; + $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; + $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; } if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { - $childClass = self::CLASS_CONTEXT_SPECIFIC; + $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } @@ -596,7 +604,7 @@ function asn1map($decoded, $mapping, $special = array()) $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { // Can only match if no constant expected and type matches or is generic. - $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false; } } } @@ -622,10 +630,10 @@ function asn1map($decoded, $mapping, $special = array()) } // Fail mapping if all input items have not been consumed. - return $i < $n ? null: $map; + return $i < $n? null: $map; // the main diff between sets and sequences is the encapsulation of the foreach in another for loop - case self::TYPE_SET: + case FILE_ASN1_TYPE_SET: $map = array(); // ignore the min and max @@ -642,9 +650,9 @@ function asn1map($decoded, $mapping, $special = array()) for ($i = 0; $i < count($decoded['content']); $i++) { $temp = $decoded['content'][$i]; - $tempClass = self::CLASS_UNIVERSAL; + $tempClass = FILE_ASN1_CLASS_UNIVERSAL; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; + $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; } foreach ($mapping['children'] as $key => $child) { @@ -652,14 +660,14 @@ function asn1map($decoded, $mapping, $special = array()) continue; } $maymatch = true; - if ($child['type'] != self::TYPE_CHOICE) { - $childClass = self::CLASS_UNIVERSAL; + if ($child['type'] != FILE_ASN1_TYPE_CHOICE) { + $childClass = FILE_ASN1_CLASS_UNIVERSAL; $constant = null; if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { - $childClass = self::CLASS_CONTEXT_SPECIFIC; + $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } @@ -668,7 +676,7 @@ function asn1map($decoded, $mapping, $special = array()) $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { // Can only match if no constant expected and type matches or is generic. - $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; + $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false; } } @@ -701,15 +709,15 @@ function asn1map($decoded, $mapping, $special = array()) } } return $map; - case self::TYPE_OBJECT_IDENTIFIER: + case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; - case self::TYPE_UTC_TIME: - case self::TYPE_GENERALIZED_TIME: + case FILE_ASN1_TYPE_UTC_TIME: + case FILE_ASN1_TYPE_GENERALIZED_TIME: if (isset($mapping['implicit'])) { $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); } return @date($this->format, $decoded['content']); - case self::TYPE_BIT_STRING: + case FILE_ASN1_TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $offset = ord($decoded['content'][0]); $size = (strlen($decoded['content']) - 1) * 8 - $offset; @@ -738,26 +746,26 @@ function asn1map($decoded, $mapping, $special = array()) } return $values; } - case self::TYPE_OCTET_STRING: - return Base64::encode($decoded['content']); - case self::TYPE_NULL: + case FILE_ASN1_TYPE_OCTET_STRING: + return base64_encode($decoded['content']); + case FILE_ASN1_TYPE_NULL: return ''; - case self::TYPE_BOOLEAN: + case FILE_ASN1_TYPE_BOOLEAN: return $decoded['content']; - case self::TYPE_NUMERIC_STRING: - case self::TYPE_PRINTABLE_STRING: - case self::TYPE_TELETEX_STRING: - case self::TYPE_VIDEOTEX_STRING: - case self::TYPE_IA5_STRING: - case self::TYPE_GRAPHIC_STRING: - case self::TYPE_VISIBLE_STRING: - case self::TYPE_GENERAL_STRING: - case self::TYPE_UNIVERSAL_STRING: - case self::TYPE_UTF8_STRING: - case self::TYPE_BMP_STRING: + case FILE_ASN1_TYPE_NUMERIC_STRING: + case FILE_ASN1_TYPE_PRINTABLE_STRING: + case FILE_ASN1_TYPE_TELETEX_STRING: + case FILE_ASN1_TYPE_VIDEOTEX_STRING: + case FILE_ASN1_TYPE_IA5_STRING: + case FILE_ASN1_TYPE_GRAPHIC_STRING: + case FILE_ASN1_TYPE_VISIBLE_STRING: + case FILE_ASN1_TYPE_GENERAL_STRING: + case FILE_ASN1_TYPE_UNIVERSAL_STRING: + case FILE_ASN1_TYPE_UTF8_STRING: + case FILE_ASN1_TYPE_BMP_STRING: return $decoded['content']; - case self::TYPE_INTEGER: - case self::TYPE_ENUMERATED: + case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: $temp = $decoded['content']; if (isset($mapping['implicit'])) { $temp = new BigInteger($decoded['content'], -256); @@ -780,10 +788,10 @@ function asn1map($decoded, $mapping, $special = array()) * * "Special" mappings can be applied via $special. * - * @param string $source - * @param string $mapping - * @param int $idx - * @return string + * @param String $source + * @param String $mapping + * @param Integer $idx + * @return String * @access public */ function encodeDER($source, $mapping, $special = array()) @@ -795,16 +803,25 @@ function encodeDER($source, $mapping, $special = array()) /** * ASN.1 Encode (Helper function) * - * @param string $source - * @param string $mapping - * @param int $idx - * @return string - * @throws \RuntimeException if the input has an error in it + * @param String $source + * @param Array $mapping + * @param Integer $idx + * @param Array $special + * @return String + * @access private + */ + /** + * 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) { + if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') { return $source->element; } @@ -823,8 +840,8 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $tag = $mapping['type']; switch ($tag) { - case self::TYPE_SET: // Children order is not important, thus process in sequence. - case self::TYPE_SEQUENCE: + case FILE_ASN1_TYPE_SET: // Children order is not important, thus process in sequence. + case FILE_ASN1_TYPE_SEQUENCE: $tag|= 0x20; // set the constructed bit $value = ''; @@ -843,7 +860,7 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } foreach ($mapping['children'] as $key => $child) { - if (!array_key_exists($key, $source)) { + if (!isset($source[$key])) { if (!isset($child['optional'])) { return false; } @@ -872,18 +889,18 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) 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']); + if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) { + $subtag = chr((FILE_ASN1_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']); + $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } $value.= $temp; } break; - case self::TYPE_CHOICE: + case FILE_ASN1_TYPE_CHOICE: $temp = false; foreach ($mapping['children'] as $key => $child) { @@ -906,11 +923,11 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) // 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']); + if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) { + $subtag = chr((FILE_ASN1_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']); + $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } @@ -925,12 +942,9 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } return $temp; - case self::TYPE_INTEGER: - case self::TYPE_ENUMERATED: + case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_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']); @@ -944,13 +958,13 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $value = chr(0); } break; - case self::TYPE_UTC_TIME: - case self::TYPE_GENERALIZED_TIME: - $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; + case FILE_ASN1_TYPE_UTC_TIME: + case FILE_ASN1_TYPE_GENERALIZED_TIME: + $format = $mapping['type'] == FILE_ASN1_TYPE_UTC_TIME ? 'y' : 'Y'; $format.= 'mdHis'; $value = @gmdate($format, strtotime($source)) . 'Z'; break; - case self::TYPE_BIT_STRING: + case FILE_ASN1_TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $bits = array_fill(0, count($mapping['mapping']), 0); $size = 0; @@ -961,10 +975,6 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } } - if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { - $size = $mapping['min'] - 1; - } - $offset = 8 - (($size + 1) & 7); $offset = $offset !== 8 ? $offset : 0; @@ -982,17 +992,17 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) break; } - case self::TYPE_OCTET_STRING: + case FILE_ASN1_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); + $value = base64_decode($source); break; - case self::TYPE_OBJECT_IDENTIFIER: + case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); if ($oid === false) { - throw new \RuntimeException('Invalid OID'); + user_error('Invalid OID'); return false; } $value = ''; @@ -1012,7 +1022,7 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $value.= $temp; } break; - case self::TYPE_ANY: + case FILE_ASN1_TYPE_ANY: $loc = $this->location; if (isset($idx)) { array_pop($this->location); @@ -1020,21 +1030,21 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) switch (true) { case !isset($source): - return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special); + return $this->_encode_der(null, array('type' => FILE_ASN1_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_object($source) && strtolower(get_class($source)) == 'math_biginteger': + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, null, $special); case is_float($source): - return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special); + return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping, null, $special); case is_bool($source): - return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special); + return $this->_encode_der($source, array('type' => FILE_ASN1_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) { @@ -1045,31 +1055,31 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) $filters = $filters[$part]; } if ($filters === false) { - throw new \RuntimeException('No filters defined for ' . implode('/', $loc)); + user_error('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); - case self::TYPE_NULL: + case FILE_ASN1_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: + case FILE_ASN1_TYPE_NUMERIC_STRING: + case FILE_ASN1_TYPE_TELETEX_STRING: + case FILE_ASN1_TYPE_PRINTABLE_STRING: + case FILE_ASN1_TYPE_UNIVERSAL_STRING: + case FILE_ASN1_TYPE_UTF8_STRING: + case FILE_ASN1_TYPE_BMP_STRING: + case FILE_ASN1_TYPE_IA5_STRING: + case FILE_ASN1_TYPE_VISIBLE_STRING: + case FILE_ASN1_TYPE_VIDEOTEX_STRING: + case FILE_ASN1_TYPE_GRAPHIC_STRING: + case FILE_ASN1_TYPE_GENERAL_STRING: $value = $source; break; - case self::TYPE_BOOLEAN: + case FILE_ASN1_TYPE_BOOLEAN: $value = $source ? "\xFF" : "\x00"; break; default: - throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', $this->location)); + user_error('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } @@ -1078,12 +1088,7 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) } 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']; - } + $tag = ($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']; } return chr($tag) . $this->_encodeLength(strlen($value)) . $value; @@ -1096,8 +1101,8 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. * * @access private - * @param int $length - * @return string + * @param Integer $length + * @return String */ function _encodeLength($length) { @@ -1115,9 +1120,9 @@ function _encodeLength($length) * Called by _decode_ber() and in the case of implicit tags asn1map(). * * @access private - * @param string $content - * @param int $tag - * @return string + * @param String $content + * @param Integer $tag + * @return String */ function _decodeTime($content, $tag) { @@ -1129,7 +1134,7 @@ function _decodeTime($content, $tag) http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://www.obj-sys.com/asn1tutorial/node14.html */ - $pattern = $tag == self::TYPE_UTC_TIME ? + $pattern = $tag == FILE_ASN1_TYPE_UTC_TIME ? '#(..)(..)(..)(..)(..)(..)(.*)#' : '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#'; @@ -1137,7 +1142,7 @@ function _decodeTime($content, $tag) list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches; - if ($tag == self::TYPE_UTC_TIME) { + if ($tag == FILE_ASN1_TYPE_UTC_TIME) { $year = $year >= 50 ? "19$year" : "20$year"; } @@ -1164,7 +1169,7 @@ function _decodeTime($content, $tag) * Sets the time / date format for asn1map(). * * @access public - * @param string $format + * @param String $format */ function setTimeFormat($format) { @@ -1177,7 +1182,7 @@ function setTimeFormat($format) * Load the relevant OIDs for a particular ASN.1 semantic mapping. * * @access public - * @param array $oids + * @param Array $oids */ function loadOIDs($oids) { @@ -1187,10 +1192,10 @@ function loadOIDs($oids) /** * Load filters * - * See \phpseclib\File\X509, etc, for an example. + * See X059, etc, for an example. * * @access public - * @param array $filters + * @param Array $filters */ function loadFilters($filters) { @@ -1202,9 +1207,9 @@ function loadFilters($filters) * * Inspired by array_shift * - * @param string $string - * @param int $index - * @return string + * @param String $string + * @param optional Integer $index + * @return String * @access private */ function _string_shift(&$string, $index = 1) @@ -1220,13 +1225,13 @@ function _string_shift(&$string, $index = 1) * This is a lazy conversion, dealing only with character size. * No real conversion table is used. * - * @param string $in - * @param int $from - * @param int $to - * @return string + * @param String $in + * @param optional Integer $from + * @param optional Integer $to + * @return String * @access public */ - function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING) + function convert($in, $from = FILE_ASN1_TYPE_UTF8_STRING, $to = FILE_ASN1_TYPE_UTF8_STRING) { if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) { return false; diff --git a/src/phpseclib/File/ASN1/Element.php b/src/phpseclib/File/ASN1/Element.php deleted file mode 100755 index 68246e2b..00000000 --- a/src/phpseclib/File/ASN1/Element.php +++ /dev/null @@ -1,47 +0,0 @@ - - * @copyright 2012 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\File\ASN1; - -/** - * ASN.1 Element - * - * Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER() - * - * @package ASN1 - * @author Jim Wigginton - * @access public - */ -class Element -{ - /** - * Raw element value - * - * @var string - * @access private - */ - var $element; - - /** - * Constructor - * - * @param string $encoded - * @return \phpseclib\File\ASN1\Element - * @access public - */ - function __construct($encoded) - { - $this->element = $encoded; - } -} diff --git a/src/phpseclib/File/X509.php b/src/phpseclib/File/X509.php index b49bc01d..55cf8ce4 100755 --- a/src/phpseclib/File/X509.php +++ b/src/phpseclib/File/X509.php @@ -3,7 +3,7 @@ /** * Pure-PHP X.509 Parser * - * PHP version 5 + * PHP versions 4 and 5 * * Encode and decode X.509 certificates. * @@ -16,115 +16,123 @@ * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the * the certificate all together unless the certificate is re-signed. * + * LICENSE: 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. + * * @category File - * @package X509 + * @package X059 * @author Jim Wigginton - * @copyright 2012 Jim Wigginton + * @copyright MMXII Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ namespace phpseclib\File; -use ParagonIE\ConstantTime\Base64; -use ParagonIE\ConstantTime\Hex; +/** + * Flag to only accept signatures signed by certificate authorities + * + * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs + * + * @access public + */ use phpseclib\Crypt\Hash; -use phpseclib\Crypt\Random; use phpseclib\Crypt\RSA; -use phpseclib\Exception\UnsupportedAlgorithmException; -use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; +@define('FILE_X509_VALIDATE_SIGNATURE_BY_CA', 1); + +/**#@+ + * @access public + * @see X059::getDN() + */ +/** + * Return internal array representation + */ +@define('FILE_X509_DN_ARRAY', 0); +/** + * Return string + */ +@define('FILE_X509_DN_STRING', 1); +/** + * Return ASN.1 name string + */ +@define('FILE_X509_DN_ASN1', 2); +/** + * Return OpenSSL compatible array + */ +@define('FILE_X509_DN_OPENSSL', 3); +/** + * Return canonical ASN.1 RDNs string + */ +@define('FILE_X509_DN_CANON', 4); +/** + * Return name hash for file indexing + */ +@define('FILE_X509_DN_HASH', 5); +/**#@-*/ + +/**#@+ + * @access public + * @see X059::saveX509() + * @see X059::saveCSR() + * @see X059::saveCRL() + */ +/** + * Save as PEM + * + * ie. a base64-encoded PEM with a header and a footer + */ +@define('FILE_X509_FORMAT_PEM', 0); +/** + * Save as DER + */ +@define('FILE_X509_FORMAT_DER', 1); +/** + * Save as a SPKAC + * + * Only works on CSRs. Not currently supported. + */ +@define('FILE_X509_FORMAT_SPKAC', 2); +/**#@-*/ + +/** + * Attribute value disposition. + * If disposition is >= 0, this is the index of the target value. + */ +@define('FILE_X509_ATTR_ALL', -1); // All attribute values (array). +@define('FILE_X509_ATTR_APPEND', -2); // Add a value. +@define('FILE_X509_ATTR_REPLACE', -3); // Clear first, then add a value. + /** * Pure-PHP X.509 Parser * - * @package X509 + * @package X059 * @author Jim Wigginton + * @version 0.3.1 * @access public */ -class X509 +class X059 { - /** - * Flag to only accept signatures signed by certificate authorities - * - * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs - * - * @access public - */ - const VALIDATE_SIGNATURE_BY_CA = 1; - - /**#@+ - * @access public - * @see \phpseclib\File\X509::getDN() - */ - /** - * Return internal array representation - */ - const DN_ARRAY = 0; - /** - * Return string - */ - const DN_STRING = 1; - /** - * Return ASN.1 name string - */ - const DN_ASN1 = 2; - /** - * Return OpenSSL compatible array - */ - const DN_OPENSSL = 3; - /** - * Return canonical ASN.1 RDNs string - */ - const DN_CANON = 4; - /** - * Return name hash for file indexing - */ - const DN_HASH = 5; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\File\X509::saveX509() - * @see \phpseclib\File\X509::saveCSR() - * @see \phpseclib\File\X509::saveCRL() - */ - /** - * Save as PEM - * - * ie. a base64-encoded PEM with a header and a footer - */ - const FORMAT_PEM = 0; - /** - * Save as DER - */ - const FORMAT_DER = 1; - /** - * Save as a SPKAC - * - * Only works on CSRs. Not currently supported. - */ - const FORMAT_SPKAC = 2; - /** - * Auto-detect the format - * - * Used only by the load*() functions - */ - const FORMAT_AUTO_DETECT = 3; - /**#@-*/ - - /** - * Attribute value disposition. - * If disposition is >= 0, this is the index of the target value. - */ - const ATTR_ALL = -1; // All attribute values (array). - const ATTR_APPEND = -2; // Add a value. - const ATTR_REPLACE = -3; // Clear first, then add a value. - /** * ASN.1 syntax for X.509 certificates * - * @var array + * @var Array * @access private */ var $Certificate; @@ -147,7 +155,6 @@ class X509 var $CertificatePolicies; var $AuthorityInfoAccessSyntax; var $SubjectAltName; - var $SubjectDirectoryAttributes; var $PrivateKeyUsagePeriod; var $IssuerAltName; var $PolicyMappings; @@ -171,18 +178,10 @@ class X509 var $SignedPublicKeyAndChallenge; /**#@-*/ - /**#@+ - * ASN.1 syntax for various DN attributes - * - * @access private - */ - var $PostalAddress; - /**#@-*/ - /** * ASN.1 syntax for Certificate Signing Requests (RFC2986) * - * @var array + * @var Array * @access private */ var $CertificationRequest; @@ -190,7 +189,7 @@ class X509 /** * ASN.1 syntax for Certificate Revocation Lists (RFC5280) * - * @var array + * @var Array * @access private */ var $CertificateList; @@ -198,7 +197,7 @@ class X509 /** * Distinguished Name * - * @var array + * @var Array * @access private */ var $dn; @@ -206,7 +205,7 @@ class X509 /** * Public key * - * @var string + * @var String * @access private */ var $publicKey; @@ -214,7 +213,7 @@ class X509 /** * Private key * - * @var string + * @var String * @access private */ var $privateKey; @@ -222,7 +221,7 @@ class X509 /** * Object identifiers for X.509 certificates * - * @var array + * @var Array * @access private * @link http://en.wikipedia.org/wiki/Object_identifier */ @@ -231,7 +230,7 @@ class X509 /** * The certificate authorities * - * @var array + * @var Array * @access private */ var $CAs; @@ -239,7 +238,7 @@ class X509 /** * The currently loaded certificate * - * @var array + * @var Array * @access private */ var $currentCert; @@ -247,10 +246,10 @@ class X509 /** * The signature subject * - * There's no guarantee \phpseclib\File\X509 is going to reencode an X.509 cert in the same way it was originally + * There's no guarantee X059 is going to reencode an X.509 cert in the same way it was originally * encoded so we take save the portion of the original cert that the signature would have made for. * - * @var string + * @var String * @access private */ var $signatureSubject; @@ -258,7 +257,7 @@ class X509 /** * Certificate Start Date * - * @var string + * @var String * @access private */ var $startDate; @@ -266,7 +265,7 @@ class X509 /** * Certificate End Date * - * @var string + * @var String * @access private */ var $endDate; @@ -274,7 +273,7 @@ class X509 /** * Serial Number * - * @var string + * @var String * @access private */ var $serialNumber; @@ -285,7 +284,7 @@ class X509 * See {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.1 RFC5280#section-4.2.1.1} and * {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}. * - * @var string + * @var String * @access private */ var $currentKeyIdentifier; @@ -293,55 +292,48 @@ class X509 /** * CA Flag * - * @var bool + * @var Boolean * @access private */ var $caFlag = false; - /** - * SPKAC Challenge - * - * @var string - * @access private - */ - var $challenge; - /** * Default Constructor. * - * @return \phpseclib\File\X509 + * @return X059 * @access public */ function __construct() { + // Explicitly Tagged Module, 1988 Syntax // http://tools.ietf.org/html/rfc5280#appendix-A.1 $this->DirectoryString = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( - 'teletexString' => array('type' => ASN1::TYPE_TELETEX_STRING), - 'printableString' => array('type' => ASN1::TYPE_PRINTABLE_STRING), - 'universalString' => array('type' => ASN1::TYPE_UNIVERSAL_STRING), - 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING), - 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING) + 'teletexString' => array('type' => FILE_ASN1_TYPE_TELETEX_STRING), + 'printableString' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING), + 'universalString' => array('type' => FILE_ASN1_TYPE_UNIVERSAL_STRING), + 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING), + 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING) ) ); $this->PKCS9String = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( - 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), + 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING), 'directoryString' => $this->DirectoryString ) ); - $this->AttributeValue = array('type' => ASN1::TYPE_ANY); + $this->AttributeValue = array('type' => FILE_ASN1_TYPE_ANY); - $AttributeType = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + $AttributeType = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); $AttributeTypeAndValue = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'type' => $AttributeType, 'value'=> $this->AttributeValue @@ -356,7 +348,7 @@ function __construct() - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName */ $this->RelativeDistinguishedName = array( - 'type' => ASN1::TYPE_SET, + 'type' => FILE_ASN1_TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $AttributeTypeAndValue @@ -364,7 +356,7 @@ function __construct() // http://tools.ietf.org/html/rfc5280#section-4.1.2.4 $RDNSequence = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, // RDNSequence does not define a min or a max, which means it doesn't have one 'min' => 0, 'max' => -1, @@ -372,7 +364,7 @@ function __construct() ); $this->Name = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( 'rdnSequence' => $RDNSequence ) @@ -380,11 +372,11 @@ function __construct() // http://tools.ietf.org/html/rfc5280#section-4.1.1.2 $AlgorithmIdentifier = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( - 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'algorithm' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), 'parameters' => array( - 'type' => ASN1::TYPE_ANY, + 'type' => FILE_ASN1_TYPE_ANY, 'optional' => true ) ) @@ -398,20 +390,20 @@ function __construct() http://tools.ietf.org/html/rfc5280#section-4.2 */ $Extension = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( - 'extnId' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'extnId' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), 'critical' => array( - 'type' => ASN1::TYPE_BOOLEAN, + 'type' => FILE_ASN1_TYPE_BOOLEAN, 'optional' => true, 'default' => false ), - 'extnValue' => array('type' => ASN1::TYPE_OCTET_STRING) + 'extnValue' => array('type' => FILE_ASN1_TYPE_OCTET_STRING) ) ); $this->Extensions = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, // technically, it's MAX, but we'll assume anything < 0 is MAX 'max' => -1, @@ -420,42 +412,42 @@ function __construct() ); $SubjectPublicKeyInfo = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'algorithm' => $AlgorithmIdentifier, - 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING) + 'subjectPublicKey' => array('type' => FILE_ASN1_TYPE_BIT_STRING) ) ); - $UniqueIdentifier = array('type' => ASN1::TYPE_BIT_STRING); + $UniqueIdentifier = array('type' => FILE_ASN1_TYPE_BIT_STRING); $Time = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( - 'utcTime' => array('type' => ASN1::TYPE_UTC_TIME), - 'generalTime' => array('type' => ASN1::TYPE_GENERALIZED_TIME) + 'utcTime' => array('type' => FILE_ASN1_TYPE_UTC_TIME), + 'generalTime' => array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME) ) ); // http://tools.ietf.org/html/rfc5280#section-4.1.2.5 $Validity = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'notBefore' => $Time, 'notAfter' => $Time ) ); - $CertificateSerialNumber = array('type' => ASN1::TYPE_INTEGER); + $CertificateSerialNumber = array('type' => FILE_ASN1_TYPE_INTEGER); $Version = array( - 'type' => ASN1::TYPE_INTEGER, + 'type' => FILE_ASN1_TYPE_INTEGER, 'mapping' => array('v1', 'v2', 'v3') ); // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm']) $TBSCertificate = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( // technically, default implies optional, but we'll define it as being optional, none-the-less, just to // reenforce that fact @@ -493,16 +485,16 @@ function __construct() ); $this->Certificate = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'tbsCertificate' => $TBSCertificate, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) ) ); $this->KeyUsage = array( - 'type' => ASN1::TYPE_BIT_STRING, + 'type' => FILE_ASN1_TYPE_BIT_STRING, 'mapping' => array( 'digitalSignature', 'nonRepudiation', @@ -517,52 +509,52 @@ function __construct() ); $this->BasicConstraints = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'cA' => array( - 'type' => ASN1::TYPE_BOOLEAN, + 'type' => FILE_ASN1_TYPE_BOOLEAN, 'optional' => true, 'default' => false ), 'pathLenConstraint' => array( - 'type' => ASN1::TYPE_INTEGER, + 'type' => FILE_ASN1_TYPE_INTEGER, 'optional' => true ) ) ); - $this->KeyIdentifier = array('type' => ASN1::TYPE_OCTET_STRING); + $this->KeyIdentifier = array('type' => FILE_ASN1_TYPE_OCTET_STRING); $OrganizationalUnitNames = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => 4, // ub-organizational-units - 'children' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + 'children' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) ); $PersonalName = array( - 'type' => ASN1::TYPE_SET, + 'type' => FILE_ASN1_TYPE_SET, 'children' => array( 'surname' => array( - 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ), 'given-name' => array( - 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ), 'initials' => array( - 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true ), 'generation-qualifier' => array( - 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, 'constant' => 3, 'optional' => true, 'implicit' => true @@ -570,52 +562,52 @@ function __construct() ) ); - $NumericUserIdentifier = array('type' => ASN1::TYPE_NUMERIC_STRING); + $NumericUserIdentifier = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING); - $OrganizationName = array('type' => ASN1::TYPE_PRINTABLE_STRING); + $OrganizationName = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING); $PrivateDomainName = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( - 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), - 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), + 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) ) ); - $TerminalIdentifier = array('type' => ASN1::TYPE_PRINTABLE_STRING); + $TerminalIdentifier = array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING); - $NetworkAddress = array('type' => ASN1::TYPE_NUMERIC_STRING); + $NetworkAddress = array('type' => FILE_ASN1_TYPE_NUMERIC_STRING); $AdministrationDomainName = array( - 'type' => ASN1::TYPE_CHOICE, - // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or - // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC - 'class' => ASN1::CLASS_APPLICATION, + 'type' => FILE_ASN1_TYPE_CHOICE, + // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or + // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC + 'class' => FILE_ASN1_CLASS_APPLICATION, 'cast' => 2, 'children' => array( - 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), - 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + 'numeric' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), + 'printable' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) ) ); $CountryName = array( - 'type' => ASN1::TYPE_CHOICE, - // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or - // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC - 'class' => ASN1::CLASS_APPLICATION, + 'type' => FILE_ASN1_TYPE_CHOICE, + // if class isn't present it's assumed to be FILE_ASN1_CLASS_UNIVERSAL or + // (if constant is present) FILE_ASN1_CLASS_CONTEXT_SPECIFIC + 'class' => FILE_ASN1_CLASS_APPLICATION, 'cast' => 1, 'children' => array( - 'x121-dcc-code' => array('type' => ASN1::TYPE_NUMERIC_STRING), - 'iso-3166-alpha2-code' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + 'x121-dcc-code' => array('type' => FILE_ASN1_TYPE_NUMERIC_STRING), + 'iso-3166-alpha2-code' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) ) ); $AnotherName = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( - 'type-id' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'type-id' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), 'value' => array( - 'type' => ASN1::TYPE_ANY, + 'type' => FILE_ASN1_TYPE_ANY, 'constant' => 0, 'optional' => true, 'explicit' => true @@ -624,16 +616,16 @@ function __construct() ); $ExtensionAttribute = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'extension-attribute-type' => array( - 'type' => ASN1::TYPE_PRINTABLE_STRING, + 'type' => FILE_ASN1_TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ), 'extension-attribute-value' => array( - 'type' => ASN1::TYPE_ANY, + 'type' => FILE_ASN1_TYPE_ANY, 'constant' => 1, 'optional' => true, 'explicit' => true @@ -642,29 +634,29 @@ function __construct() ); $ExtensionAttributes = array( - 'type' => ASN1::TYPE_SET, + 'type' => FILE_ASN1_TYPE_SET, 'min' => 1, 'max' => 256, // ub-extension-attributes 'children' => $ExtensionAttribute ); $BuiltInDomainDefinedAttribute = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( - 'type' => array('type' => ASN1::TYPE_PRINTABLE_STRING), - 'value' => array('type' => ASN1::TYPE_PRINTABLE_STRING) + 'type' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING), + 'value' => array('type' => FILE_ASN1_TYPE_PRINTABLE_STRING) ) ); $BuiltInDomainDefinedAttributes = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => 4, // ub-domain-defined-attributes 'children' => $BuiltInDomainDefinedAttribute ); $BuiltInStandardAttributes = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'country-name' => array('optional' => true) + $CountryName, 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName, @@ -707,7 +699,7 @@ function __construct() ); $ORAddress = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'built-in-standard-attributes' => $BuiltInStandardAttributes, 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes, @@ -716,14 +708,14 @@ function __construct() ); $EDIPartyName = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'nameAssigner' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $this->DirectoryString, - // partyName is technically required but \phpseclib\File\ASN1 doesn't currently support non-optional constants and + // partyName is technically required but ASN1 doesn't currently support non-optional constants and // setting it to optional gets the job done in any event. 'partyName' => array( 'constant' => 1, @@ -734,7 +726,7 @@ function __construct() ); $GeneralName = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( 'otherName' => array( 'constant' => 0, @@ -742,13 +734,13 @@ function __construct() 'implicit' => true ) + $AnotherName, 'rfc822Name' => array( - 'type' => ASN1::TYPE_IA5_STRING, + 'type' => FILE_ASN1_TYPE_IA5_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ), 'dNSName' => array( - 'type' => ASN1::TYPE_IA5_STRING, + 'type' => FILE_ASN1_TYPE_IA5_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true @@ -769,19 +761,19 @@ function __construct() 'implicit' => true ) + $EDIPartyName, 'uniformResourceIdentifier' => array( - 'type' => ASN1::TYPE_IA5_STRING, + 'type' => FILE_ASN1_TYPE_IA5_STRING, 'constant' => 6, 'optional' => true, 'implicit' => true ), 'iPAddress' => array( - 'type' => ASN1::TYPE_OCTET_STRING, + 'type' => FILE_ASN1_TYPE_OCTET_STRING, 'constant' => 7, 'optional' => true, 'implicit' => true ), 'registeredID' => array( - 'type' => ASN1::TYPE_OBJECT_IDENTIFIER, + 'type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER, 'constant' => 8, 'optional' => true, 'implicit' => true @@ -790,7 +782,7 @@ function __construct() ); $GeneralNames = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $GeneralName @@ -799,7 +791,7 @@ function __construct() $this->IssuerAltName = $GeneralNames; $ReasonFlags = array( - 'type' => ASN1::TYPE_BIT_STRING, + 'type' => FILE_ASN1_TYPE_BIT_STRING, 'mapping' => array( 'unused', 'keyCompromise', @@ -814,7 +806,7 @@ function __construct() ); $DistributionPointName = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( 'fullName' => array( 'constant' => 0, @@ -830,7 +822,7 @@ function __construct() ); $DistributionPoint = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'distributionPoint' => array( 'constant' => 0, @@ -851,14 +843,14 @@ function __construct() ); $this->CRLDistributionPoints = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $DistributionPoint ); $this->AuthorityKeyIdentifier = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'keyIdentifier' => array( 'constant' => 0, @@ -878,24 +870,24 @@ function __construct() ) ); - $PolicyQualifierId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + $PolicyQualifierId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); $PolicyQualifierInfo = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'policyQualifierId' => $PolicyQualifierId, - 'qualifier' => array('type' => ASN1::TYPE_ANY) + 'qualifier' => array('type' => FILE_ASN1_TYPE_ANY) ) ); - $CertPolicyId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + $CertPolicyId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); $PolicyInformation = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'policyIdentifier' => $CertPolicyId, 'policyQualifiers' => array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 0, 'max' => -1, 'optional' => true, @@ -905,18 +897,18 @@ function __construct() ); $this->CertificatePolicies = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $PolicyInformation ); $this->PolicyMappings = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'issuerDomainPolicy' => $CertPolicyId, 'subjectDomainPolicy' => $CertPolicyId @@ -924,25 +916,25 @@ function __construct() ) ); - $KeyPurposeId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + $KeyPurposeId = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); $this->ExtKeyUsageSyntax = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $KeyPurposeId ); $AccessDescription = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( - 'accessMethod' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'accessMethod' => array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER), 'accessLocation' => $GeneralName ) ); $this->AuthorityInfoAccessSyntax = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $AccessDescription @@ -951,25 +943,25 @@ function __construct() $this->SubjectAltName = $GeneralNames; $this->PrivateKeyUsagePeriod = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'notBefore' => array( 'constant' => 0, 'optional' => true, 'implicit' => true, - 'type' => ASN1::TYPE_GENERALIZED_TIME), + 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME), 'notAfter' => array( 'constant' => 1, 'optional' => true, 'implicit' => true, - 'type' => ASN1::TYPE_GENERALIZED_TIME) + 'type' => FILE_ASN1_TYPE_GENERALIZED_TIME) ) ); - $BaseDistance = array('type' => ASN1::TYPE_INTEGER); + $BaseDistance = array('type' => FILE_ASN1_TYPE_INTEGER); $GeneralSubtree = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'base' => $GeneralName, 'minimum' => array( @@ -987,14 +979,14 @@ function __construct() ); $GeneralSubtrees = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $GeneralSubtree ); $this->NameConstraints = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'permittedSubtrees' => array( 'constant' => 0, @@ -1009,33 +1001,33 @@ function __construct() ) ); - $this->CPSuri = array('type' => ASN1::TYPE_IA5_STRING); + $this->CPSuri = array('type' => FILE_ASN1_TYPE_IA5_STRING); $DisplayText = array( - 'type' => ASN1::TYPE_CHOICE, + 'type' => FILE_ASN1_TYPE_CHOICE, 'children' => array( - 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), - 'visibleString' => array('type' => ASN1::TYPE_VISIBLE_STRING), - 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING), - 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING) + 'ia5String' => array('type' => FILE_ASN1_TYPE_IA5_STRING), + 'visibleString' => array('type' => FILE_ASN1_TYPE_VISIBLE_STRING), + 'bmpString' => array('type' => FILE_ASN1_TYPE_BMP_STRING), + 'utf8String' => array('type' => FILE_ASN1_TYPE_UTF8_STRING) ) ); $NoticeReference = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'organization' => $DisplayText, 'noticeNumbers' => array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'min' => 1, 'max' => 200, - 'children' => array('type' => ASN1::TYPE_INTEGER) + 'children' => array('type' => FILE_ASN1_TYPE_INTEGER) ) ) ); $this->UserNotice = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'noticeRef' => array( 'optional' => true, @@ -1050,7 +1042,7 @@ function __construct() // mapping is from $this->netscape_cert_type = array( - 'type' => ASN1::TYPE_BIT_STRING, + 'type' => FILE_ASN1_TYPE_BIT_STRING, 'mapping' => array( 'SSLClient', 'SSLServer', @@ -1063,17 +1055,17 @@ function __construct() ) ); - $this->netscape_comment = array('type' => ASN1::TYPE_IA5_STRING); - $this->netscape_ca_policy_url = array('type' => ASN1::TYPE_IA5_STRING); + $this->netscape_comment = array('type' => FILE_ASN1_TYPE_IA5_STRING); + $this->netscape_ca_policy_url = array('type' => FILE_ASN1_TYPE_IA5_STRING); // attribute is used in RFC2986 but we're using the RFC5280 definition $Attribute = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'type' => $AttributeType, 'value'=> array( - 'type' => ASN1::TYPE_SET, + 'type' => FILE_ASN1_TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $this->AttributeValue @@ -1081,27 +1073,20 @@ function __construct() ) ); - $this->SubjectDirectoryAttributes = array( - 'type' => ASN1::TYPE_SEQUENCE, - 'min' => 1, - 'max' => -1, - 'children' => $Attribute - ); - // adapted from $Attributes = array( - 'type' => ASN1::TYPE_SET, + 'type' => FILE_ASN1_TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $Attribute ); $CertificationRequestInfo = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'version' => array( - 'type' => ASN1::TYPE_INTEGER, + 'type' => FILE_ASN1_TYPE_INTEGER, 'mapping' => array('v1') ), 'subject' => $this->Name, @@ -1115,16 +1100,16 @@ function __construct() ); $this->CertificationRequest = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'certificationRequestInfo' => $CertificationRequestInfo, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) ) ); $RevokedCertificate = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'userCertificate' => $CertificateSerialNumber, 'revocationDate' => $Time, @@ -1135,7 +1120,7 @@ function __construct() ); $TBSCertList = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'version' => array( 'optional' => true, @@ -1148,7 +1133,7 @@ function __construct() 'optional' => true ) + $Time, 'revokedCertificates' => array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'optional' => true, 'min' => 0, 'max' => -1, @@ -1163,17 +1148,17 @@ function __construct() ); $this->CertificateList = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'tbsCertList' => $TBSCertList, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) ) ); - $this->CRLNumber = array('type' => ASN1::TYPE_INTEGER); + $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER); - $this->CRLReason = array('type' => ASN1::TYPE_ENUMERATED, + $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED, 'mapping' => array( 'unspecified', 'keyCompromise', @@ -1189,7 +1174,7 @@ function __construct() ) ); - $this->IssuingDistributionPoint = array('type' => ASN1::TYPE_SEQUENCE, + $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'distributionPoint' => array( 'constant' => 0, @@ -1197,14 +1182,14 @@ function __construct() 'explicit' => true ) + $DistributionPointName, 'onlyContainsUserCerts' => array( - 'type' => ASN1::TYPE_BOOLEAN, + 'type' => FILE_ASN1_TYPE_BOOLEAN, 'constant' => 1, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlyContainsCACerts' => array( - 'type' => ASN1::TYPE_BOOLEAN, + 'type' => FILE_ASN1_TYPE_BOOLEAN, 'constant' => 2, 'optional' => true, 'default' => false, @@ -1216,14 +1201,14 @@ function __construct() 'implicit' => true ) + $ReasonFlags, 'indirectCRL' => array( - 'type' => ASN1::TYPE_BOOLEAN, + 'type' => FILE_ASN1_TYPE_BOOLEAN, 'constant' => 4, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlyContainsAttributeCerts' => array( - 'type' => ASN1::TYPE_BOOLEAN, + 'type' => FILE_ASN1_TYPE_BOOLEAN, 'constant' => 5, 'optional' => true, 'default' => false, @@ -1232,37 +1217,29 @@ function __construct() ) ); - $this->InvalidityDate = array('type' => ASN1::TYPE_GENERALIZED_TIME); + $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME); $this->CertificateIssuer = $GeneralNames; - $this->HoldInstructionCode = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); + $this->HoldInstructionCode = array('type' => FILE_ASN1_TYPE_OBJECT_IDENTIFIER); $PublicKeyAndChallenge = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'spki' => $SubjectPublicKeyInfo, - 'challenge' => array('type' => ASN1::TYPE_IA5_STRING) + 'challenge' => array('type' => FILE_ASN1_TYPE_IA5_STRING) ) ); $this->SignedPublicKeyAndChallenge = array( - 'type' => ASN1::TYPE_SEQUENCE, + 'type' => FILE_ASN1_TYPE_SEQUENCE, 'children' => array( 'publicKeyAndChallenge' => $PublicKeyAndChallenge, 'signatureAlgorithm' => $AlgorithmIdentifier, - 'signature' => array('type' => ASN1::TYPE_BIT_STRING) + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) ) ); - $this->PostalAddress = array( - 'type' => ASN1::TYPE_SEQUENCE, - 'optional' => true, - 'min' => 1, - 'max' => -1, - 'children' => $this->DirectoryString - ); - // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 $this->oids = array( '1.3.6.1.5.5.7' => 'id-pkix', @@ -1297,7 +1274,6 @@ function __construct() '2.5.4.9' => 'id-at-streetAddress', '2.5.4.45' => 'id-at-uniqueIdentifier', '2.5.4.72' => 'id-at-role', - '2.5.4.16' => 'id-at-postalAddress', '0.9.2342.19200300.100.1.25' => 'id-domainComponent', '1.2.840.113549.1.9' => 'pkcs-9', @@ -1435,12 +1411,11 @@ function __construct() * * Returns an associative array describing the X.509 cert or a false if the cert failed to load * - * @param string $cert - * @param int $mode + * @param String $cert * @access public - * @return mixed + * @return Mixed */ - function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) + function loadX509($cert) { if (is_array($cert) && isset($cert['tbsCertificate'])) { unset($this->currentCert); @@ -1461,13 +1436,7 @@ function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) $asn1 = new ASN1(); - if ($mode != self::FORMAT_DER) { - $newcert = $this->_extractBER($cert); - if ($mode == self::FORMAT_PEM && $cert == $newcert) { - return false; - } - $cert = $newcert; - } + $cert = $this->_extractBER($cert); if ($cert === false) { $this->currentCert = false; @@ -1487,11 +1456,7 @@ function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) { - $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); - } - $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); - $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); @@ -1508,12 +1473,12 @@ function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) /** * Save X.509 certificate * - * @param array $cert - * @param int $format optional + * @param Array $cert + * @param Integer $format optional * @access public - * @return string + * @return String */ - function saveX509($cert, $format = self::FORMAT_PEM) + function saveX509($cert, $format = FILE_X509_FORMAT_PEM) { if (!is_array($cert) || !isset($cert['tbsCertificate'])) { return false; @@ -1528,17 +1493,7 @@ function saveX509($cert, $format = self::FORMAT_PEM) switch ($algorithm) { case 'rsaEncryption': $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] - = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); - /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier." - -- https://tools.ietf.org/html/rfc3279#section-2.3.1 - - given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank, - it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever. - */ - $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null; - // https://tools.ietf.org/html/rfc3279#section-2.2.1 - $cert['signatureAlgorithm']['parameters'] = null; - $cert['tbsCertificate']['signature']['parameters'] = null; + = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); } } @@ -1546,7 +1501,7 @@ function saveX509($cert, $format = self::FORMAT_PEM) $asn1->loadOIDs($this->oids); $filters = array(); - $type_utf8_string = array('type' => ASN1::TYPE_UTF8_STRING); + $type_utf8_string = array('type' => FILE_ASN1_TYPE_UTF8_STRING); $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string; $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string; @@ -1558,27 +1513,25 @@ function saveX509($cert, $format = self::FORMAT_PEM) $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string; $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string; - /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib\File\ASN1::TYPE_IA5_STRING. - \phpseclib\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random + /* in the case of policyQualifiers/qualifier, the type has to be FILE_ASN1_TYPE_IA5_STRING. + FILE_ASN1_TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random characters. */ $filters['policyQualifiers']['qualifier'] - = array('type' => ASN1::TYPE_IA5_STRING); + = array('type' => FILE_ASN1_TYPE_IA5_STRING); $asn1->loadFilters($filters); $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1); - $this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1); - $this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1); $cert = $asn1->encodeDER($cert, $this->Certificate); switch ($format) { - case self::FORMAT_DER: + case FILE_X509_FORMAT_DER: return $cert; - // case self::FORMAT_PEM: + // case FILE_X509_FORMAT_PEM: default: - return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(Base64::encode($cert), 64) . '-----END CERTIFICATE-----'; + return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----'; } } @@ -1586,20 +1539,20 @@ function saveX509($cert, $format = self::FORMAT_PEM) * Map extension values from octet string to extension-specific internal * format. * - * @param array ref $root - * @param string $path - * @param object $asn1 + * @param Array ref $root + * @param String $path + * @param Object $asn1 * @access private */ function _mapInExtensions(&$root, $path, $asn1) { - $extensions = &$this->_subArrayUnchecked($root, $path); + $extensions = &$this->_subArray($root, $path); - if ($extensions) { + if (is_array($extensions)) { for ($i = 0; $i < count($extensions); $i++) { $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; - $value = Base64::decode($value); + $value = base64_decode($value); $decoded = $asn1->decodeBER($value); /* [extnValue] contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID */ @@ -1625,8 +1578,8 @@ function _mapInExtensions(&$root, $path, $asn1) } } } - } else { - $value = Base64::encode($value); + } elseif ($map) { + $value = base64_encode($value); } } } @@ -1636,9 +1589,9 @@ function _mapInExtensions(&$root, $path, $asn1) * Map extension values from extension-specific internal format to * octet string. * - * @param array ref $root - * @param string $path - * @param object $asn1 + * @param Array ref $root + * @param String $path + * @param Object $asn1 * @access private */ function _mapOutExtensions(&$root, $path, $asn1) @@ -1648,10 +1601,6 @@ function _mapOutExtensions(&$root, $path, $asn1) if (is_array($extensions)) { $size = count($extensions); for ($i = 0; $i < $size; $i++) { - if ($extensions[$i] instanceof Element) { - continue; - } - $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; @@ -1666,9 +1615,9 @@ function _mapOutExtensions(&$root, $path, $asn1) $map = $this->_getMapping($subid); $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; if ($map !== false) { - // by default \phpseclib\File\ASN1 will try to render qualifier as a \phpseclib\File\ASN1::TYPE_IA5_STRING since it's - // actual type is \phpseclib\File\ASN1::TYPE_ANY - $subvalue = new Element($asn1->encodeDER($subvalue, $map)); + // by default ASN1 will try to render qualifier as a FILE_ASN1_TYPE_IA5_STRING since it's + // actual type is FILE_ASN1_TYPE_ANY + $subvalue = new ASN1_Element($asn1->encodeDER($subvalue, $map)); } } } @@ -1676,8 +1625,8 @@ function _mapOutExtensions(&$root, $path, $asn1) case 'id-ce-authorityKeyIdentifier': // use 00 as the serial number instead of an empty string if (isset($value['authorityCertSerialNumber'])) { if ($value['authorityCertSerialNumber']->toBytes() == '') { - $temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; - $value['authorityCertSerialNumber'] = new Element($temp); + $temp = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; + $value['authorityCertSerialNumber'] = new ASN1_Element($temp); } } } @@ -1687,12 +1636,12 @@ function _mapOutExtensions(&$root, $path, $asn1) $map = $this->_getMapping($id); if (is_bool($map)) { if (!$map) { - //user_error($id . ' is not a currently supported extension'); + user_error($id . ' is not a currently supported extension'); unset($extensions[$i]); } } else { $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); - $value = Base64::encode($temp); + $value = base64_encode($temp); } } } @@ -1702,9 +1651,9 @@ function _mapOutExtensions(&$root, $path, $asn1) * Map attribute values from ANY type to attribute-specific internal * format. * - * @param array ref $root - * @param string $path - * @param object $asn1 + * @param Array ref $root + * @param String $path + * @param Object $asn1 * @access private */ function _mapInAttributes(&$root, $path, $asn1) @@ -1727,11 +1676,11 @@ function _mapInAttributes(&$root, $path, $asn1) if ($mapped !== false) { $values[$j] = $mapped; } - if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { + if ($id == 'pkcs-9-at-extensionRequest') { $this->_mapInExtensions($values, $j, $asn1); } } elseif ($map) { - $values[$j] = Base64::encode($value); + $values[$j] = base64_encode($value); } } } @@ -1743,9 +1692,9 @@ function _mapInAttributes(&$root, $path, $asn1) * Map attribute values from attribute-specific internal format to * ANY type. * - * @param array ref $root - * @param string $path - * @param object $asn1 + * @param Array ref $root + * @param String $path + * @param Object $asn1 * @access private */ function _mapOutAttributes(&$root, $path, $asn1) @@ -1760,7 +1709,7 @@ function _mapOutAttributes(&$root, $path, $asn1) $id = $attributes[$i]['type']; $map = $this->_getMapping($id); if ($map === false) { - //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); unset($attributes[$i]); } elseif (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; @@ -1782,78 +1731,16 @@ function _mapOutAttributes(&$root, $path, $asn1) } } - /** - * Map DN values from ANY type to DN-specific internal - * format. - * - * @param array ref $root - * @param string $path - * @param object $asn1 - * @access private - */ - function _mapInDNs(&$root, $path, $asn1) - { - $dns = &$this->_subArray($root, $path); - - if (is_array($dns)) { - for ($i = 0; $i < count($dns); $i++) { - for ($j = 0; $j < count($dns[$i]); $j++) { - $type = $dns[$i][$j]['type']; - $value = &$dns[$i][$j]['value']; - if (is_object($value) && $value instanceof Element) { - $map = $this->_getMapping($type); - if (!is_bool($map)) { - $decoded = $asn1->decodeBER($value); - $value = $asn1->asn1map($decoded[0], $map); - } - } - } - } - } - } - - /** - * Map DN values from DN-specific internal format to - * ANY type. - * - * @param array ref $root - * @param string $path - * @param object $asn1 - * @access private - */ - function _mapOutDNs(&$root, $path, $asn1) - { - $dns = &$this->_subArray($root, $path); - - if (is_array($dns)) { - $size = count($dns); - for ($i = 0; $i < $size; $i++) { - for ($j = 0; $j < count($dns[$i]); $j++) { - $type = $dns[$i][$j]['type']; - $value = &$dns[$i][$j]['value']; - if (is_object($value) && $value instanceof Element) { - continue; - } - - $map = $this->_getMapping($type); - if (!is_bool($map)) { - $value = new Element($asn1->encodeDER($value, $map)); - } - } - } - } - } - /** * Associate an extension ID to an extension mapping * - * @param string $extnId + * @param String $extnId * @access private - * @return mixed + * @return Mixed */ function _getMapping($extnId) { - if (!is_string($extnId)) { // eg. if it's a \phpseclib\File\ASN1\Element object + if (!is_string($extnId)) { // eg. if it's a ASN1_Element object return true; } @@ -1876,8 +1763,6 @@ function _getMapping($extnId) return $this->AuthorityInfoAccessSyntax; case 'id-ce-subjectAltName': return $this->SubjectAltName; - case 'id-ce-subjectDirectoryAttributes': - return $this->SubjectDirectoryAttributes; case 'id-ce-privateKeyUsagePeriod': return $this->PrivateKeyUsagePeriod; case 'id-ce-issuerAltName': @@ -1937,8 +1822,6 @@ function _getMapping($extnId) return $this->CertificateIssuer; case 'id-ce-holdInstructionCode': return $this->HoldInstructionCode; - case 'id-at-postalAddress': - return $this->PostalAddress; } return false; @@ -1947,9 +1830,9 @@ function _getMapping($extnId) /** * Load an X.509 certificate as a certificate authority * - * @param string $cert + * @param String $cert * @access public - * @return bool + * @return Boolean */ function loadCA($cert) { @@ -2014,9 +1897,9 @@ function loadCA($cert) * component or component fragment. E.g., *.a.com matches foo.a.com but * not bar.foo.a.com. f*.com matches foo.com but not bar.com. * - * @param string $url + * @param String $url * @access public - * @return bool + * @return Boolean */ function validateURL($url) { @@ -2072,7 +1955,7 @@ function validateURL($url) * * If $date isn't defined it is assumed to be the current date. * - * @param int $date optional + * @param Integer $date optional * @access public */ function validateDate($date = null) @@ -2111,9 +1994,9 @@ function validateDate($date = null) * * The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}. * - * @param bool $caonly optional + * @param Boolean $caonly optional * @access public - * @return mixed + * @return Mixed */ function validateSignature($caonly = true) { @@ -2130,16 +2013,14 @@ function validateSignature($caonly = true) switch (true) { case isset($this->currentCert['tbsCertificate']): // self-signed cert - switch (true) { - case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']: - case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING): - $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); - $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); - switch (true) { - case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - $signingCert = $this->currentCert; // working cert - } + if ($this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $this->currentCert; // working cert + } } if (!empty($this->CAs)) { @@ -2147,17 +2028,15 @@ function validateSignature($caonly = true) // even if the cert is a self-signed one we still want to see if it's a CA; // if not, we'll conditionally return an error $ca = $this->CAs[$i]; - switch (true) { - case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']: - case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): - $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); - $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); - switch (true) { - case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - $signingCert = $ca; // working cert - break 3; - } + if ($this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 2; + } } } if (count($this->CAs) == $i && $caonly) { @@ -2170,7 +2049,7 @@ function validateSignature($caonly = true) $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(Base64::decode($this->currentCert['signature']), 1), + substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['certificationRequestInfo']): @@ -2178,7 +2057,7 @@ function validateSignature($caonly = true) $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(Base64::decode($this->currentCert['signature']), 1), + substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['publicKeyAndChallenge']): @@ -2186,24 +2065,22 @@ function validateSignature($caonly = true) $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(Base64::decode($this->currentCert['signature']), 1), + substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['tbsCertList']): if (!empty($this->CAs)) { for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; - switch (true) { - case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']: - case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): - $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); - $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); - switch (true) { - case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - $signingCert = $ca; // working cert - break 3; - } + if ($this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 2; + } } } } @@ -2214,7 +2091,7 @@ function validateSignature($caonly = true) $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(Base64::decode($this->currentCert['signature']), 1), + substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); default: @@ -2225,24 +2102,22 @@ function validateSignature($caonly = true) /** * Validates a signature * - * Returns true if the signature is verified and false if it is not correct. - * If the algorithms are unsupposed an exception is thrown. + * Returns true if the signature is verified, false if it is not correct or null on error * - * @param string $publicKeyAlgorithm - * @param string $publicKey - * @param string $signatureAlgorithm - * @param string $signature - * @param string $signatureSubject + * @param String $publicKeyAlgorithm + * @param String $publicKey + * @param String $signatureAlgorithm + * @param String $signature + * @param String $signatureSubject * @access private - * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported - * @return bool + * @return Integer */ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) { switch ($publicKeyAlgorithm) { case 'rsaEncryption': $rsa = new RSA(); - $rsa->load($publicKey); + $rsa->loadKey($publicKey); switch ($signatureAlgorithm) { case 'md2WithRSAEncryption': @@ -2253,16 +2128,17 @@ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); - if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) { + $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); + if (!@$rsa->verify($signatureSubject, $signature)) { return false; } break; default: - throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); + return null; } break; default: - throw new UnsupportedAlgorithmException('Public key algorithm unsupported'); + return null; } return true; @@ -2273,22 +2149,22 @@ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm * * Reformats a public key to a format supported by phpseclib (if applicable) * - * @param string $algorithm - * @param string $key + * @param String $algorithm + * @param String $key * @access private - * @return string + * @return String */ function _reformatKey($algorithm, $key) { switch ($algorithm) { case 'rsaEncryption': return - "-----BEGIN RSA PUBLIC KEY-----\r\n" . + "-----BEGIN PUBLIC KEY-----\r\n" . // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do. - chunk_split(Base64::encode(substr(Base64::decode($key), 1)), 64) . - '-----END RSA PUBLIC KEY-----'; + chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) . + '-----END PUBLIC KEY-----'; default: return $key; } @@ -2299,13 +2175,15 @@ function _reformatKey($algorithm, $key) * * Takes in a base64 encoded "blob" and returns a human readable IP address * - * @param string $ip + * @param String $ip * @access private - * @return string + * @return String */ function _decodeIP($ip) { - return inet_ntop(Base64::decode($ip)); + $ip = base64_decode($ip); + list(, $ip) = unpack('N', $ip); + return long2ip($ip); } /** @@ -2313,21 +2191,21 @@ function _decodeIP($ip) * * Takes a human readable IP address into a base64-encoded "blob" * - * @param string $ip + * @param String $ip * @access private - * @return string + * @return String */ function _encodeIP($ip) { - return Base64::encode(inet_pton($ip)); + return base64_encode(pack('N', ip2long($ip))); } /** * "Normalizes" a Distinguished Name property * - * @param string $propName + * @param String $propName * @access private - * @return mixed + * @return Mixed */ function _translateDNProp($propName) { @@ -2406,9 +2284,6 @@ function _translateDNProp($propName) case 'uniqueidentifier': case 'x500uniqueidentifier': return 'id-at-uniqueIdentifier'; - case 'postaladdress': - case 'id-at-postaladdress': - return 'id-at-postalAddress'; default: return false; } @@ -2417,11 +2292,11 @@ function _translateDNProp($propName) /** * Set a Distinguished Name property * - * @param string $propName - * @param mixed $propValue - * @param string $type optional + * @param String $propName + * @param Mixed $propValue + * @param String $type optional * @access public - * @return bool + * @return Boolean */ function setDNProp($propName, $propValue, $type = 'utf8String') { @@ -2451,7 +2326,7 @@ function setDNProp($propName, $propValue, $type = 'utf8String') /** * Remove Distinguished Name properties * - * @param string $propName + * @param String $propName * @access public */ function removeDNProp($propName) @@ -2478,10 +2353,10 @@ function removeDNProp($propName) /** * Get Distinguished Name properties * - * @param string $propName - * @param array $dn optional - * @param bool $withType optional - * @return mixed + * @param String $propName + * @param Array $dn optional + * @param Boolean $withType optional + * @return Mixed * @access public */ function getDNProp($propName, $dn = null, $withType = false) @@ -2498,38 +2373,25 @@ function getDNProp($propName, $dn = null, $withType = false) return false; } - $asn1 = new ASN1(); - $asn1->loadOIDs($this->oids); - $filters = array(); - $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); - $asn1->loadFilters($filters); - $this->_mapOutDNs($dn, 'rdnSequence', $asn1); $dn = $dn['rdnSequence']; $result = array(); + $asn1 = new ASN1(); for ($i = 0; $i < count($dn); $i++) { if ($dn[$i][0]['type'] == $propName) { $v = $dn[$i][0]['value']; - if (!$withType) { - if (is_array($v)) { - foreach ($v as $type => $s) { - $type = array_search($type, $asn1->ANYmap, true); - if ($type !== false && isset($asn1->stringTypeSize[$type])) { - $s = $asn1->convert($s, $type); - if ($s !== false) { - $v = $s; - break; - } + if (!$withType && is_array($v)) { + foreach ($v as $type => $s) { + $type = array_search($type, $asn1->ANYmap, true); + if ($type !== false && isset($asn1->stringTypeSize[$type])) { + $s = $asn1->convert($s, $type); + if ($s !== false) { + $v = $s; + break; } } - if (is_array($v)) { - $v = array_pop($v); // Always strip data type. - } - } elseif (is_object($v) && $v instanceof Element) { - $map = $this->_getMapping($propName); - if (!is_bool($map)) { - $decoded = $asn1->decodeBER($v); - $v = $asn1->asn1map($decoded[0], $map); - } + } + if (is_array($v)) { + $v = array_pop($v); // Always strip data type. } } $result[] = $v; @@ -2542,11 +2404,11 @@ function getDNProp($propName, $dn = null, $withType = false) /** * Set a Distinguished Name * - * @param mixed $dn - * @param bool $merge optional - * @param string $type optional + * @param Mixed $dn + * @param Boolean $merge optional + * @param String $type optional * @access public - * @return bool + * @return Boolean */ function setDN($dn, $merge = false, $type = 'utf8String') { @@ -2570,7 +2432,7 @@ function setDN($dn, $merge = false, $type = 'utf8String') } // handles everything else - $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); + $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); for ($i = 1; $i < count($results); $i+=2) { $prop = trim($results[$i], ', =/'); $value = $results[$i + 1]; @@ -2585,42 +2447,55 @@ function setDN($dn, $merge = false, $type = 'utf8String') /** * Get the Distinguished Name for a certificates subject * - * @param mixed $format optional - * @param array $dn optional + * @param Mixed $format optional + * @param Array $dn optional * @access public - * @return bool + * @return Boolean */ - function getDN($format = self::DN_ARRAY, $dn = null) + function getDN($format = FILE_X509_DN_ARRAY, $dn = null) { if (!isset($dn)) { $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; } switch ((int) $format) { - case self::DN_ARRAY: + case FILE_X509_DN_ARRAY: return $dn; - case self::DN_ASN1: + case FILE_X509_DN_ASN1: $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); - $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $filters['rdnSequence']['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING); $asn1->loadFilters($filters); - $this->_mapOutDNs($dn, 'rdnSequence', $asn1); return $asn1->encodeDER($dn, $this->Name); - case self::DN_CANON: + case FILE_X509_DN_OPENSSL: + $dn = $this->getDN(FILE_X509_DN_STRING, $dn); + if ($dn === false) { + return false; + } + $attrs = preg_split('#((?:^|, *|/)[a-z][a-z0-9]*=)#i', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); + $dn = array(); + for ($i = 1; $i < count($attrs); $i += 2) { + $prop = trim($attrs[$i], ', =/'); + $value = $attrs[$i + 1]; + if (!isset($dn[$prop])) { + $dn[$prop] = $value; + } else { + $dn[$prop] = array_merge((array) $dn[$prop], array($value)); + } + } + return $dn; + case FILE_X509_DN_CANON: // No SEQUENCE around RDNs and all string values normalized as - // trimmed lowercase UTF-8 with all spacing as one blank. - // constructed RDNs will not be canonicalized + // trimmed lowercase UTF-8 with all spacing as one blank. $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); - $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); + $filters['value'] = array('type' => FILE_ASN1_TYPE_UTF8_STRING); $asn1->loadFilters($filters); $result = ''; - $this->_mapOutDNs($dn, 'rdnSequence', $asn1); foreach ($dn['rdnSequence'] as $rdn) { - foreach ($rdn as $i => $attr) { - $attr = &$rdn[$i]; + foreach ($rdn as &$attr) { if (is_array($attr['value'])) { foreach ($attr['value'] as $type => $v) { $type = array_search($type, $asn1->ANYmap, true); @@ -2638,26 +2513,18 @@ function getDN($format = self::DN_ARRAY, $dn = null) $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName); } return $result; - case self::DN_HASH: - $dn = $this->getDN(self::DN_CANON, $dn); + case FILE_X509_DN_HASH: + $dn = $this->getDN(FILE_X509_DN_CANON, $dn); $hash = new Hash('sha1'); - $hash = $hash->hash($dn); + $hash = $hash->_hash($dn); extract(unpack('Vhash', $hash)); - return strtolower(Hex::encode(pack('N', $hash))); + return strtolower(bin2hex(pack('N', $hash))); } - // Default is to return a string. + // Defaut is to return a string. $start = true; $output = ''; - - $result = array(); $asn1 = new ASN1(); - $asn1->loadOIDs($this->oids); - $filters = array(); - $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); - $asn1->loadFilters($filters); - $this->_mapOutDNs($dn, 'rdnSequence', $asn1); - foreach ($dn['rdnSequence'] as $field) { $prop = $field[0]['type']; $value = $field[0]['value']; @@ -2665,37 +2532,33 @@ function getDN($format = self::DN_ARRAY, $dn = null) $delim = ', '; switch ($prop) { case 'id-at-countryName': - $desc = 'C'; + $desc = 'C='; break; case 'id-at-stateOrProvinceName': - $desc = 'ST'; + $desc = 'ST='; break; case 'id-at-organizationName': - $desc = 'O'; + $desc = 'O='; break; case 'id-at-organizationalUnitName': - $desc = 'OU'; + $desc = 'OU='; break; case 'id-at-commonName': - $desc = 'CN'; + $desc = 'CN='; break; case 'id-at-localityName': - $desc = 'L'; + $desc = 'L='; break; case 'id-at-surname': - $desc = 'SN'; + $desc = 'SN='; break; case 'id-at-uniqueIdentifier': $delim = '/'; - $desc = 'x500UniqueIdentifier'; - break; - case 'id-at-postalAddress': - $delim = '/'; - $desc = 'postalAddress'; + $desc = 'x500UniqueIdentifier='; break; default: $delim = '/'; - $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop); + $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop) . '='; } if (!$start) { @@ -2715,28 +2578,22 @@ function getDN($format = self::DN_ARRAY, $dn = null) if (is_array($value)) { $value = array_pop($value); // Always strip data type. } - } elseif (is_object($value) && $value instanceof Element) { - $callback = create_function('$x', 'return "\x" . bin2hex($x[0]);'); - $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); } - $output.= $desc . '=' . $value; - $result[$desc] = isset($result[$desc]) ? - array_merge((array) $dn[$prop], array($value)) : - $value; + $output.= $desc . $value; $start = false; } - return $format == self::DN_OPENSSL ? $result : $output; + return $output; } /** * Get the Distinguished Name for a certificate/crl issuer * - * @param int $format optional + * @param Integer $format optional * @access public - * @return mixed + * @return Mixed */ - function getIssuerDN($format = self::DN_ARRAY) + function getIssuerDN($format = FILE_X509_DN_ARRAY) { switch (true) { case !isset($this->currentCert) || !is_array($this->currentCert): @@ -2754,11 +2611,11 @@ function getIssuerDN($format = self::DN_ARRAY) * Get the Distinguished Name for a certificate/csr subject * Alias of getDN() * - * @param int $format optional + * @param Integer $format optional * @access public - * @return mixed + * @return Mixed */ - function getSubjectDN($format = self::DN_ARRAY) + function getSubjectDN($format = FILE_X509_DN_ARRAY) { switch (true) { case !empty($this->dn): @@ -2777,10 +2634,10 @@ function getSubjectDN($format = self::DN_ARRAY) /** * Get an individual Distinguished Name property for a certificate/crl issuer * - * @param string $propName - * @param bool $withType optional + * @param String $propName + * @param Boolean $withType optional * @access public - * @return mixed + * @return Mixed */ function getIssuerDNProp($propName, $withType = false) { @@ -2799,10 +2656,10 @@ function getIssuerDNProp($propName, $withType = false) /** * Get an individual Distinguished Name property for a certificate/csr subject * - * @param string $propName - * @param bool $withType optional + * @param String $propName + * @param Boolean $withType optional * @access public - * @return mixed + * @return Mixed */ function getSubjectDNProp($propName, $withType = false) { @@ -2824,7 +2681,7 @@ function getSubjectDNProp($propName, $withType = false) * Get the certificate chain for the current cert * * @access public - * @return mixed + * @return Mixed */ function getChain() { @@ -2858,8 +2715,8 @@ function getChain() break; } } - foreach ($chain as $key => $value) { - $chain[$key] = new X509(); + foreach ($chain as $key=>$value) { + $chain[$key] = new X059(); $chain[$key]->loadX509($value); } return $chain; @@ -2868,11 +2725,11 @@ function getChain() /** * Set public key * - * Key needs to be a \phpseclib\Crypt\RSA object + * Key needs to be a RSA object * - * @param object $key + * @param Object $key * @access public - * @return bool + * @return Boolean */ function setPublicKey($key) { @@ -2883,9 +2740,9 @@ function setPublicKey($key) /** * Set private key * - * Key needs to be a \phpseclib\Crypt\RSA object + * Key needs to be a RSA object * - * @param object $key + * @param Object $key * @access public */ function setPrivateKey($key) @@ -2893,26 +2750,13 @@ function setPrivateKey($key) $this->privateKey = $key; } - /** - * Set challenge - * - * Used for SPKAC CSR's - * - * @param string $challenge - * @access public - */ - function setChallenge($challenge) - { - $this->challenge = $challenge; - } - /** * Gets the public key * - * Returns a \phpseclib\Crypt\RSA object or a false. + * Returns a RSA object or a false. * * @access public - * @return mixed + * @return Mixed */ function getPublicKey() { @@ -2937,7 +2781,7 @@ function getPublicKey() switch ($keyinfo['algorithm']['algorithm']) { case 'rsaEncryption': $publicKey = new RSA(); - $publicKey->load($key); + $publicKey->loadKey($key); $publicKey->setPublicKey(); break; default: @@ -2950,11 +2794,11 @@ function getPublicKey() /** * Load a Certificate Signing Request * - * @param string $csr + * @param String $csr * @access public - * @return mixed + * @return Mixed */ - function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) + function loadCSR($csr) { if (is_array($csr) && isset($csr['certificationRequestInfo'])) { unset($this->currentCert); @@ -2973,13 +2817,7 @@ function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) $asn1 = new ASN1(); - if ($mode != self::FORMAT_DER) { - $newcsr = $this->_extractBER($csr); - if ($mode == self::FORMAT_PEM && $csr == $newcsr) { - return false; - } - $csr = $newcsr; - } + $csr = $this->_extractBER($csr); $orig = $csr; if ($csr === false) { @@ -3001,10 +2839,8 @@ function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) return false; } - $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1); - $this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); - $this->dn = $csr['certificationRequestInfo']['subject']; + $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1); $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); @@ -3015,7 +2851,7 @@ function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->load($key); + $this->publicKey->loadKey($key); $this->publicKey->setPublicKey(); break; default: @@ -3031,12 +2867,12 @@ function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) /** * Save CSR request * - * @param array $csr - * @param int $format optional + * @param Array $csr + * @param Integer $format optional * @access public - * @return string + * @return String */ - function saveCSR($csr, $format = self::FORMAT_PEM) + function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) { if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) { return false; @@ -3044,16 +2880,13 @@ function saveCSR($csr, $format = self::FORMAT_PEM) switch (true) { case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): - case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']); break; default: switch ($algorithm) { case 'rsaEncryption': $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] - = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); - $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null; - $csr['signatureAlgorithm']['parameters'] = null; - $csr['certificationRequestInfo']['signature']['parameters'] = null; + = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); } } @@ -3063,20 +2896,19 @@ function saveCSR($csr, $format = self::FORMAT_PEM) $filters = array(); $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] - = array('type' => ASN1::TYPE_UTF8_STRING); + = array('type' => FILE_ASN1_TYPE_UTF8_STRING); $asn1->loadFilters($filters); - $this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1); $csr = $asn1->encodeDER($csr, $this->CertificationRequest); switch ($format) { - case self::FORMAT_DER: + case FILE_X509_FORMAT_DER: return $csr; - // case self::FORMAT_PEM: + // case FILE_X509_FORMAT_PEM: default: - return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(Base64::encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; + return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; } } @@ -3087,9 +2919,9 @@ function saveCSR($csr, $format = self::FORMAT_PEM) * * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * - * @param string $csr + * @param String $csr * @access public - * @return mixed + * @return Mixed */ function loadSPKAC($spkac) { @@ -3105,9 +2937,8 @@ function loadSPKAC($spkac) $asn1 = new ASN1(); - // OpenSSL produces SPKAC's that are preceeded by the string SPKAC= - $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; + $temp = preg_replace('#(?:^[^=]+=)|[\r\n\\\]#', '', $spkac); + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; if ($temp != false) { $spkac = $temp; } @@ -3142,7 +2973,7 @@ function loadSPKAC($spkac) switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->load($key); + $this->publicKey->loadKey($key); $this->publicKey->setPublicKey(); break; default: @@ -3155,57 +2986,14 @@ function loadSPKAC($spkac) return $spkac; } - /** - * Save a SPKAC CSR request - * - * @param array $csr - * @param int $format optional - * @access public - * @return string - */ - function saveSPKAC($spkac, $format = self::FORMAT_PEM) - { - if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) { - return false; - } - - $algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm'); - switch (true) { - case !$algorithm: - case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): - break; - default: - switch ($algorithm) { - case 'rsaEncryption': - $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] - = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); - } - } - - $asn1 = new ASN1(); - - $asn1->loadOIDs($this->oids); - $spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge); - - switch ($format) { - case self::FORMAT_DER: - return $spkac; - // case self::FORMAT_PEM: - default: - // OpenSSL's implementation of SPKAC requires the SPKAC be preceeded by SPKAC= and since there are pretty much - // no other SPKAC decoders phpseclib will use that same format - return 'SPKAC=' . Base64::encode($spkac); - } - } - /** * Load a Certificate Revocation List * - * @param string $crl + * @param String $crl * @access public - * @return mixed + * @return Mixed */ - function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) + function loadCRL($crl) { if (is_array($crl) && isset($crl['tbsCertList'])) { $this->currentCert = $crl; @@ -3215,13 +3003,7 @@ function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) $asn1 = new ASN1(); - if ($mode != self::FORMAT_DER) { - $newcrl = $this->_extractBER($crl); - if ($mode == self::FORMAT_PEM && $crl == $newcrl) { - return false; - } - $crl = $newcrl; - } + $crl = $this->_extractBER($crl); $orig = $crl; if ($crl === false) { @@ -3245,19 +3027,11 @@ function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); - if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { - $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); - } - if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { - $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); - if ($rclist_ref) { - $rclist = $crl['tbsCertList']['revokedCertificates']; - foreach ($rclist as $i => $extension) { - if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { - $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1); - } - } + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); } } @@ -3270,12 +3044,12 @@ function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) /** * Save Certificate Revocation List. * - * @param array $crl - * @param int $format optional + * @param Array $crl + * @param Integer $format optional * @access public - * @return string + * @return String */ - function saveCRL($crl, $format = self::FORMAT_PEM) + function saveCRL($crl, $format = FILE_X509_FORMAT_PEM) { if (!is_array($crl) || !isset($crl['tbsCertList'])) { return false; @@ -3287,25 +3061,24 @@ function saveCRL($crl, $format = self::FORMAT_PEM) $filters = array(); $filters['tbsCertList']['issuer']['rdnSequence']['value'] - = array('type' => ASN1::TYPE_UTF8_STRING); + = array('type' => FILE_ASN1_TYPE_UTF8_STRING); $filters['tbsCertList']['signature']['parameters'] - = array('type' => ASN1::TYPE_UTF8_STRING); + = array('type' => FILE_ASN1_TYPE_UTF8_STRING); $filters['signatureAlgorithm']['parameters'] - = array('type' => ASN1::TYPE_UTF8_STRING); + = array('type' => FILE_ASN1_TYPE_UTF8_STRING); if (empty($crl['tbsCertList']['signature']['parameters'])) { $filters['tbsCertList']['signature']['parameters'] - = array('type' => ASN1::TYPE_NULL); + = array('type' => FILE_ASN1_TYPE_NULL); } if (empty($crl['signatureAlgorithm']['parameters'])) { $filters['signatureAlgorithm']['parameters'] - = array('type' => ASN1::TYPE_NULL); + = array('type' => FILE_ASN1_TYPE_NULL); } $asn1->loadFilters($filters); - $this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); if (is_array($rclist)) { @@ -3317,33 +3090,11 @@ function saveCRL($crl, $format = self::FORMAT_PEM) $crl = $asn1->encodeDER($crl, $this->CertificateList); switch ($format) { - case self::FORMAT_DER: + case FILE_X509_FORMAT_DER: return $crl; - // case self::FORMAT_PEM: + // case FILE_X509_FORMAT_PEM: default: - return "-----BEGIN X509 CRL-----\r\n" . chunk_split(Base64::encode($crl), 64) . '-----END X509 CRL-----'; - } - } - - /** - * Helper function to build a time field according to RFC 3280 section - * - 4.1.2.5 Validity - * - 5.1.2.4 This Update - * - 5.1.2.5 Next Update - * - 5.1.2.6 Revoked Certificates - * by choosing utcTime iff year of date given is before 2050 and generalTime else. - * - * @param string $date in format date('D, d M Y H:i:s O') - * @access private - * @return array - */ - function _timeField($date) - { - $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this - if ($year < 2050) { - return array('utcTime' => $date); - } else { - return array('generalTime' => $date); + return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; } } @@ -3354,13 +3105,13 @@ function _timeField($date) * $subject can be either an existing X.509 cert (if you want to resign it), * a CSR or something with the DN and public key explicitly set. * - * @param \phpseclib\File\X509 $issuer - * @param \phpseclib\File\X509 $subject - * @param string $signatureAlgorithm optional + * @param X059 $issuer + * @param X059 $subject + * @param String $signatureAlgorithm optional * @access public - * @return mixed + * @return Mixed */ - function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption') + function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; @@ -3379,10 +3130,12 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; if (!empty($this->startDate)) { - $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate); + $this->currentCert['tbsCertificate']['validity']['notBefore']['generalTime'] = $this->startDate; + unset($this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime']); } if (!empty($this->endDate)) { - $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate); + $this->currentCert['tbsCertificate']['validity']['notAfter']['generalTime'] = $this->endDate; + unset($this->currentCert['tbsCertificate']['validity']['notAfter']['utcTime']); } if (!empty($this->serialNumber)) { $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; @@ -3397,25 +3150,16 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' if (isset($subject->domains)) { $this->removeExtension('id-ce-subjectAltName'); } - } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { + } else if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { return false; } else { if (!isset($subject->publicKey)) { return false; } - $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); - $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year')); - /* "The serial number MUST be a positive integer" - "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." - -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 - - for the integer to be positive the leading bit needs to be 0 hence the - application of a bitmap - */ - $serialNumber = !empty($this->serialNumber) ? - $this->serialNumber : - new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256); + $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O'); + $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M y H:i:s O', strtotime('+1 year')); + $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new BigInteger(); $this->currentCert = array( 'tbsCertificate' => @@ -3425,14 +3169,14 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later 'validity' => array( - 'notBefore' => $this->_timeField($startDate), // $this->setStartDate() - 'notAfter' => $this->_timeField($endDate) // $this->setEndDate() + 'notBefore' => array('generalTime' => $startDate), // $this->setStartDate() + 'notAfter' => array('generalTime' => $endDate) // $this->setEndDate() ), 'subject' => $subject->dn, 'subjectPublicKeyInfo' => $subjectPublicKey ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); // Copy extensions from CSR. @@ -3453,7 +3197,8 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' // ) //), 'keyIdentifier' => $issuer->currentKeyIdentifier - )); + ) + ); //$extensions = &$this->currentCert['tbsCertificate']['extensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; @@ -3468,7 +3213,7 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' $altName = array(); if (isset($subject->domains) && count($subject->domains) > 1) { - $altName = array_map(array('X509', '_dnsName'), $subject->domains); + $altName = array_map(array('X059', '_dnsName'), $subject->domains); } if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { @@ -3496,8 +3241,7 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' $keyUsage = array(); } - $this->setExtension( - 'id-ce-keyUsage', + $this->setExtension('id-ce-keyUsage', array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) ); @@ -3506,19 +3250,16 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' $basicConstraints = array(); } - $this->setExtension( - 'id-ce-basicConstraints', - array_unique(array_merge(array('cA' => true), $basicConstraints)), - true - ); + $this->setExtension('id-ce-basicConstraints', + array_unique(array_merge(array('cA' => true), $basicConstraints)), true); if (!isset($subject->currentKeyIdentifier)) { - $this->setExtension('id-ce-subjectKeyIdentifier', Base64::encode($this->computeKeyIdentifier($this->currentCert)), false, false); + $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); } } // resync $this->signatureSubject - // save $tbsCertificate in case there are any \phpseclib\File\ASN1\Element objects in it + // save $tbsCertificate in case there are any ASN1_Element objects in it $tbsCertificate = $this->currentCert['tbsCertificate']; $this->loadX509($this->saveX509($this->currentCert)); @@ -3535,7 +3276,7 @@ function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption' * Sign a CSR * * @access public - * @return mixed + * @return Mixed */ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') { @@ -3546,7 +3287,7 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); - $this->publicKey->load($this->privateKey->getPublicKey()); + $this->publicKey->loadKey($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); if (!($publicKey = $this->_formatSubjectPublicKey())) { return false; @@ -3570,13 +3311,13 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') 'subject' => $this->dn, 'subjectPKInfo' => $publicKey ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } // resync $this->signatureSubject - // save $certificationRequestInfo in case there are any \phpseclib\File\ASN1\Element objects in it + // save $certificationRequestInfo in case there are any ASN1_Element objects in it $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; $this->loadCSR($this->saveCSR($this->currentCert)); @@ -3589,81 +3330,16 @@ function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') return $result; } - /** - * Sign a SPKAC - * - * @access public - * @return mixed - */ - function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption') - { - if (!is_object($this->privateKey)) { - return false; - } - - $origPublicKey = $this->publicKey; - $class = get_class($this->privateKey); - $this->publicKey = new $class(); - $this->publicKey->load($this->privateKey->getPublicKey()); - $this->publicKey->setPublicKey(); - $publicKey = $this->_formatSubjectPublicKey(); - if (!$publicKey) { - return false; - } - $this->publicKey = $origPublicKey; - - $currentCert = isset($this->currentCert) ? $this->currentCert : null; - $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; - - // re-signing a SPKAC seems silly but since everything else supports re-signing why not? - if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) { - $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; - $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey; - if (!empty($this->challenge)) { - // the bitwise AND ensures that the output is a valid IA5String - $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge)); - } - } else { - $this->currentCert = array( - 'publicKeyAndChallenge' => - array( - 'spki' => $publicKey, - // quoting , - // "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified." - // both Firefox and OpenSSL ("openssl spkac -key private.key") behave this way - // we could alternatively do this instead if we ignored the specs: - // Random::string(8) & str_repeat("\x7F", 8) - 'challenge' => !empty($this->challenge) ? $this->challenge : '' - ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later - ); - } - - // resync $this->signatureSubject - // save $publicKeyAndChallenge in case there are any \phpseclib\File\ASN1\Element objects in it - $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge']; - $this->loadSPKAC($this->saveSPKAC($this->currentCert)); - - $result = $this->_sign($this->privateKey, $signatureAlgorithm); - $result['publicKeyAndChallenge'] = $publicKeyAndChallenge; - - $this->currentCert = $currentCert; - $this->signatureSubject = $signatureSubject; - - return $result; - } - /** * Sign a CRL * * $issuer's private key needs to be loaded. * - * @param \phpseclib\File\X509 $issuer - * @param \phpseclib\File\X509 $crl - * @param string $signatureAlgorithm optional + * @param X059 $issuer + * @param X059 $crl + * @param String $signatureAlgorithm optional * @access public - * @return mixed + * @return Mixed */ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') { @@ -3673,7 +3349,7 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; - $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); + $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M y H:i:s O'); if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { $this->currentCert = $crl->currentCert; @@ -3686,19 +3362,19 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') 'version' => 'v2', 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later - 'thisUpdate' => $this->_timeField($thisUpdate) // $this->setStartDate() + 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate() ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } $tbsCertList = &$this->currentCert['tbsCertList']; $tbsCertList['issuer'] = $issuer->dn; - $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate); + $tbsCertList['thisUpdate'] = array('generalTime' => $thisUpdate); if (!empty($this->endDate)) { - $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate() + $tbsCertList['nextUpdate'] = array('generalTime' => $this->endDate); // $this->setEndDate() } else { unset($tbsCertList['nextUpdate']); } @@ -3707,11 +3383,6 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') $crlNumber = $this->serialNumber; } else { $crlNumber = $this->getExtension('id-ce-cRLNumber'); - // "The CRL number is a non-critical CRL extension that conveys a - // monotonically increasing sequence number for a given CRL scope and - // CRL issuer. This extension allows users to easily determine when a - // particular CRL supersedes another CRL." - // -- https://tools.ietf.org/html/rfc5280#section-5.2.3 $crlNumber = $crlNumber !== false ? $crlNumber->add(new BigInteger(1)) : null; } @@ -3750,7 +3421,8 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') // ) //), 'keyIdentifier' => $issuer->currentKeyIdentifier - )); + ) + ); //$extensions = &$tbsCertList['crlExtensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; @@ -3772,7 +3444,7 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') unset($tbsCertList); // resync $this->signatureSubject - // save $tbsCertList in case there are any \phpseclib\File\ASN1\Element objects in it + // save $tbsCertList in case there are any ASN1_Element objects in it $tbsCertList = $this->currentCert['tbsCertList']; $this->loadCRL($this->saveCRL($this->currentCert)); @@ -3788,51 +3460,50 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') /** * X.509 certificate signing helper function. * - * @param object $key - * @param \phpseclib\File\X509 $subject - * @param string $signatureAlgorithm + * @param Object $key + * @param X059 $subject + * @param String $signatureAlgorithm * @access public - * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported - * @return mixed + * @return Mixed */ function _sign($key, $signatureAlgorithm) { - if ($key instanceof RSA) { - switch ($signatureAlgorithm) { - case 'md2WithRSAEncryption': - case 'md5WithRSAEncryption': - case 'sha1WithRSAEncryption': - case 'sha224WithRSAEncryption': - case 'sha256WithRSAEncryption': - case 'sha384WithRSAEncryption': - case 'sha512WithRSAEncryption': - $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); - - $this->currentCert['signature'] = Base64::encode("\0" . $key->sign($this->signatureSubject, RSA::PADDING_PKCS1)); - return $this->currentCert; - default: - throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); - } - } + switch (strtolower(get_class($key))) { + case 'crypt_rsa': + switch ($signatureAlgorithm) { + case 'md2WithRSAEncryption': + case 'md5WithRSAEncryption': + case 'sha1WithRSAEncryption': + case 'sha224WithRSAEncryption': + case 'sha256WithRSAEncryption': + case 'sha384WithRSAEncryption': + case 'sha512WithRSAEncryption': + $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); + $key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); - throw new UnsupportedAlgorithmException('Unsupported public key algorithm'); + $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); + return $this->currentCert; + } + default: + return false; + } } /** * Set certificate start date * - * @param string $date + * @param String $date * @access public */ function setStartDate($date) { - $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date)); + $this->startDate = @date('D, d M y H:i:s O', @strtotime($date)); } /** * Set certificate end date * - * @param string $date + * @param String $date * @access public */ function setEndDate($date) @@ -3847,17 +3518,17 @@ function setEndDate($date) if (strtolower($date) == 'lifetime') { $temp = '99991231235959Z'; $asn1 = new ASN1(); - $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; - $this->endDate = new Element($temp); + $temp = chr(FILE_ASN1_TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; + $this->endDate = new ASN1_Element($temp); } else { - $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date)); + $this->endDate = @date('D, d M y H:i:s O', @strtotime($date)); } } /** * Set Serial Number * - * @param string $serial + * @param String $serial * @param $base optional * @access public */ @@ -3876,82 +3547,14 @@ function makeCA() $this->caFlag = true; } - /** - * Check for validity of subarray - * - * This is intended for use in conjunction with _subArrayUnchecked(), - * implementing the checks included in _subArray() but without copying - * a potentially large array by passing its reference by-value to is_array(). - * - * @param array $root - * @param string $path - * @return boolean - * @access private - */ - function _isSubArrayValid($root, $path) - { - if (!is_array($root)) { - return false; - } - - foreach (explode('/', $path) as $i) { - if (!is_array($root)) { - return false; - } - - if (!isset($root[$i])) { - return true; - } - - $root = $root[$i]; - } - - return true; - } - - /** - * Get a reference to a subarray - * - * This variant of _subArray() does no is_array() checking, - * so $root should be checked with _isSubArrayValid() first. - * - * This is here for performance reasons: - * Passing a reference (i.e. $root) by-value (i.e. to is_array()) - * creates a copy. If $root is an especially large array, this is expensive. - * - * @param array $root - * @param string $path absolute path with / as component separator - * @param bool $create optional - * @access private - * @return array|false - */ - function &_subArrayUnchecked(&$root, $path, $create = false) - { - $false = false; - - foreach (explode('/', $path) as $i) { - if (!isset($root[$i])) { - if (!$create) { - return $false; - } - - $root[$i] = array(); - } - - $root = &$root[$i]; - } - - return $root; - } - /** * Get a reference to a subarray * * @param array $root - * @param string $path absolute path with / as component separator - * @param bool $create optional + * @param String $path absolute path with / as component separator + * @param Boolean $create optional * @access private - * @return array|false + * @return array item ref or false */ function &_subArray(&$root, $path, $create = false) { @@ -3984,10 +3587,10 @@ function &_subArray(&$root, $path, $create = false) * Get a reference to an extension subarray * * @param array $root - * @param string $path optional absolute path with / as component separator - * @param bool $create optional + * @param String $path optional absolute path with / as component separator + * @param Boolean $create optional * @access private - * @return array|false + * @return array ref or false */ function &_extensions(&$root, $path = null, $create = false) { @@ -4038,10 +3641,10 @@ function &_extensions(&$root, $path = null, $create = false) /** * Remove an Extension * - * @param string $id - * @param string $path optional + * @param String $id + * @param String $path optional * @access private - * @return bool + * @return Boolean */ function _removeExtension($id, $path = null) { @@ -4068,11 +3671,11 @@ function _removeExtension($id, $path = null) * * Returns the extension if it exists and false if not * - * @param string $id - * @param array $cert optional - * @param string $path optional + * @param String $id + * @param Array $cert optional + * @param String $path optional * @access private - * @return mixed + * @return Mixed */ function _getExtension($id, $cert = null, $path = null) { @@ -4095,9 +3698,9 @@ function _getExtension($id, $cert = null, $path = null) * Returns a list of all extensions in use * * @param array $cert optional - * @param string $path optional + * @param String $path optional * @access private - * @return array + * @return Array */ function _getExtensions($cert = null, $path = null) { @@ -4116,13 +3719,13 @@ function _getExtensions($cert = null, $path = null) /** * Set an Extension * - * @param string $id - * @param mixed $value - * @param bool $critical optional - * @param bool $replace optional - * @param string $path optional + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @param String $path optional * @access private - * @return bool + * @return Boolean */ function _setExtension($id, $value, $critical = false, $replace = true, $path = null) { @@ -4152,9 +3755,9 @@ function _setExtension($id, $value, $critical = false, $replace = true, $path = /** * Remove a certificate, CSR or CRL Extension * - * @param string $id + * @param String $id * @access public - * @return bool + * @return Boolean */ function removeExtension($id) { @@ -4166,10 +3769,10 @@ function removeExtension($id) * * Returns the extension if it exists and false if not * - * @param string $id - * @param array $cert optional + * @param String $id + * @param Array $cert optional * @access public - * @return mixed + * @return Mixed */ function getExtension($id, $cert = null) { @@ -4181,7 +3784,7 @@ function getExtension($id, $cert = null) * * @param array $cert optional * @access public - * @return array + * @return Array */ function getExtensions($cert = null) { @@ -4191,12 +3794,12 @@ function getExtensions($cert = null) /** * Set a certificate, CSR or CRL Extension * - * @param string $id - * @param mixed $value - * @param bool $critical optional - * @param bool $replace optional + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional * @access public - * @return bool + * @return Boolean */ function setExtension($id, $value, $critical = false, $replace = true) { @@ -4206,12 +3809,12 @@ function setExtension($id, $value, $critical = false, $replace = true) /** * Remove a CSR attribute. * - * @param string $id - * @param int $disposition optional + * @param String $id + * @param Integer $disposition optional * @access public - * @return bool + * @return Boolean */ - function removeAttribute($id, $disposition = self::ATTR_ALL) + function removeAttribute($id, $disposition = FILE_X509_ATTR_ALL) { $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes'); @@ -4224,13 +3827,13 @@ function removeAttribute($id, $disposition = self::ATTR_ALL) if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { - case $disposition == self::ATTR_APPEND: - case $disposition == self::ATTR_REPLACE: + case $disposition == FILE_X509_ATTR_APPEND: + case $disposition == FILE_X509_ATTR_REPLACE: return false; case $disposition >= $n: $disposition -= $n; break; - case $disposition == self::ATTR_ALL: + case $disposition == FILE_X509_ATTR_ALL: case $n == 1: unset($attributes[$key]); $result = true; @@ -4241,7 +3844,7 @@ function removeAttribute($id, $disposition = self::ATTR_ALL) $result = true; break; } - if ($result && $disposition != self::ATTR_ALL) { + if ($result && $disposition != FILE_X509_ATTR_ALL) { break; } } @@ -4256,13 +3859,13 @@ function removeAttribute($id, $disposition = self::ATTR_ALL) * * Returns the attribute if it exists and false if not * - * @param string $id - * @param int $disposition optional - * @param array $csr optional + * @param String $id + * @param Integer $disposition optional + * @param Array $csr optional * @access public - * @return mixed + * @return Mixed */ - function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) + function getAttribute($id, $disposition = FILE_X509_ATTR_ALL, $csr = null) { if (empty($csr)) { $csr = $this->currentCert; @@ -4278,10 +3881,10 @@ function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { - case $disposition == self::ATTR_APPEND: - case $disposition == self::ATTR_REPLACE: + case $disposition == FILE_X509_ATTR_APPEND: + case $disposition == FILE_X509_ATTR_REPLACE: return false; - case $disposition == self::ATTR_ALL: + case $disposition == FILE_X509_ATTR_ALL: return $attribute['value']; case $disposition >= $n: $disposition -= $n; @@ -4300,7 +3903,7 @@ function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) * * @param array $csr optional * @access public - * @return array + * @return Array */ function getAttributes($csr = null) { @@ -4323,13 +3926,13 @@ function getAttributes($csr = null) /** * Set a CSR attribute * - * @param string $id - * @param mixed $value - * @param bool $disposition optional + * @param String $id + * @param Mixed $value + * @param Boolean $disposition optional * @access public - * @return bool + * @return Boolean */ - function setAttribute($id, $value, $disposition = self::ATTR_ALL) + function setAttribute($id, $value, $disposition = FILE_X509_ATTR_ALL) { $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true); @@ -4338,9 +3941,9 @@ function setAttribute($id, $value, $disposition = self::ATTR_ALL) } switch ($disposition) { - case self::ATTR_REPLACE: - $disposition = self::ATTR_APPEND; - case self::ATTR_ALL: + case FILE_X509_ATTR_REPLACE: + $disposition = FILE_X509_ATTR_APPEND; + case FILE_X509_ATTR_ALL: $this->removeAttribute($id); break; } @@ -4349,10 +3952,10 @@ function setAttribute($id, $value, $disposition = self::ATTR_ALL) if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { - case $disposition == self::ATTR_APPEND: + case $disposition == FILE_X509_ATTR_APPEND: $last = $key; break; - case $disposition >= $n: + case $disposition >= $n; $disposition -= $n; break; default: @@ -4369,7 +3972,7 @@ function setAttribute($id, $value, $disposition = self::ATTR_ALL) $attributes[$last]['value'][] = $value; break; default: - $attributes[] = array('type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value: array($value)); + $attributes[] = array('type' => $id, 'value' => $disposition == FILE_X509_ATTR_ALL ? $value: array($value)); break; } @@ -4381,7 +3984,7 @@ function setAttribute($id, $value, $disposition = self::ATTR_ALL) * * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. * - * @param string $value + * @param String $value * @access public */ function setKeyIdentifier($value) @@ -4389,7 +3992,7 @@ function setKeyIdentifier($value) if (empty($value)) { unset($this->currentKeyIdentifier); } else { - $this->currentKeyIdentifier = Base64::encode($value); + $this->currentKeyIdentifier = base64_encode($value); } } @@ -4401,15 +4004,15 @@ function setKeyIdentifier($value) * recommended methods (4.2.1.2 RFC 3280). * Highly polymorphic: try to accept all possible forms of key: * - Key object - * - \phpseclib\File\X509 object with public or private key defined + * - X059 object with public or private key defined * - Certificate or CSR array - * - \phpseclib\File\ASN1\Element object + * - ASN1_Element object * - PEM or DER string * - * @param mixed $key optional - * @param int $method optional + * @param Mixed $key optional + * @param Integer $method optional * @access public - * @return string binary key identifier + * @return String binary key identifier */ function computeKeyIdentifier($key = null, $method = 1) { @@ -4426,21 +4029,21 @@ function computeKeyIdentifier($key = null, $method = 1) return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); case !is_object($key): return false; - case $key instanceof Element: + case strtolower(get_class($key)) == 'file_asn1_element': // Assume the element is a bitstring-packed key. $asn1 = new ASN1(); $decoded = $asn1->decodeBER($key->element); if (empty($decoded)) { return false; } - $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING)); + $raw = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); if (empty($raw)) { return false; } - $raw = Base64::decode($raw); + $raw = base64_decode($raw); // If the key is private, compute identifier from its corresponding public key. $key = new RSA(); - if (!$key->load($raw)) { + if (!$key->loadKey($raw)) { return false; // Not an unencrypted RSA key. } if ($key->getPrivateKey() !== false) { // If private. @@ -4448,7 +4051,7 @@ function computeKeyIdentifier($key = null, $method = 1) } $key = $raw; // Is a public key. break; - case $key instanceof X509: + case strtolower(get_class($key)) == 'file_x509': if (isset($key->publicKey)) { return $this->computeKeyIdentifier($key->publicKey, $method); } @@ -4459,8 +4062,8 @@ function computeKeyIdentifier($key = null, $method = 1) return $this->computeKeyIdentifier($key->currentCert, $method); } return false; - default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA). - $key = $key->getPublicKey('PKCS1'); + default: // Should be a key object (i.e.: RSA). + $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW); break; } @@ -4469,7 +4072,7 @@ function computeKeyIdentifier($key = null, $method = 1) // Now we have the key string: compute its sha-1 sum. $hash = new Hash('sha1'); - $hash = $hash->hash($key); + $hash = $hash->_hash($key); if ($method == 2) { $hash = substr($hash, -8); @@ -4483,28 +4086,33 @@ function computeKeyIdentifier($key = null, $method = 1) * Format a public key as appropriate * * @access private - * @return array + * @return Array */ function _formatSubjectPublicKey() { - if ($this->publicKey instanceof RSA) { - // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. - // the former is a good example of how to do fuzzing on the public key - //return new Element(Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); - return array( - 'algorithm' => array('algorithm' => 'rsaEncryption'), - 'subjectPublicKey' => $this->publicKey->getPublicKey('PKCS1') - ); + if (!isset($this->publicKey) || !is_object($this->publicKey)) { + return false; } - return false; + switch (strtolower(get_class($this->publicKey))) { + case 'crypt_rsa': + // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. + // the former is a good example of how to do fuzzing on the public key + //return new ASN1_Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); + return array( + 'algorithm' => array('algorithm' => 'rsaEncryption'), + 'subjectPublicKey' => $this->publicKey->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW) + ); + default: + return false; + } } /** * Set the domain name's which the cert is to be valid for * * @access public - * @return array + * @return Array */ function setDomain() { @@ -4517,7 +4125,7 @@ function setDomain() * Set the IP Addresses's which the cert is to be valid for * * @access public - * @param string $ipAddress optional + * @param String $ipAddress optional */ function setIPAddress() { @@ -4534,8 +4142,8 @@ function setIPAddress() * Helper function to build domain array * * @access private - * @param string $domain - * @return array + * @param String $domain + * @return Array */ function _dnsName($domain) { @@ -4548,8 +4156,8 @@ function _dnsName($domain) * (IPv6 is not currently supported) * * @access private - * @param string $address - * @return array + * @param String $address + * @return Array */ function _iPAddress($address) { @@ -4560,10 +4168,10 @@ function _iPAddress($address) * Get the index of a revoked certificate. * * @param array $rclist - * @param string $serial - * @param bool $create optional + * @param String $serial + * @param Boolean $create optional * @access private - * @return int|false + * @return Integer or false */ function _revokedCertificate(&$rclist, $serial, $create = false) { @@ -4581,17 +4189,17 @@ function _revokedCertificate(&$rclist, $serial, $create = false) $i = count($rclist); $rclist[] = array('userCertificate' => $serial, - 'revocationDate' => $this->_timeField(@date('D, d M Y H:i:s O'))); + 'revocationDate' => array('generalTime' => @date('D, d M y H:i:s O'))); return $i; } /** * Revoke a certificate. * - * @param string $serial - * @param string $date optional + * @param String $serial + * @param String $date optional * @access public - * @return bool + * @return Boolean */ function revoke($serial, $date = null) { @@ -4599,8 +4207,9 @@ function revoke($serial, $date = null) if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + if (!empty($date)) { - $rclist[$i]['revocationDate'] = $this->_timeField($date); + $rclist[$i]['revocationDate'] = array('generalTime' => $date); } return true; @@ -4615,9 +4224,9 @@ function revoke($serial, $date = null) /** * Unrevoke a certificate. * - * @param string $serial + * @param String $serial * @access public - * @return bool + * @return Boolean */ function unrevoke($serial) { @@ -4635,9 +4244,9 @@ function unrevoke($serial) /** * Get a revoked certificate. * - * @param string $serial + * @param String $serial * @access public - * @return mixed + * @return Mixed */ function getRevoked($serial) { @@ -4681,10 +4290,10 @@ function listRevoked($crl = null) /** * Remove a Revoked Certificate Extension * - * @param string $serial - * @param string $id + * @param String $serial + * @param String $id * @access public - * @return bool + * @return Boolean */ function removeRevokedCertificateExtension($serial, $id) { @@ -4702,11 +4311,11 @@ function removeRevokedCertificateExtension($serial, $id) * * Returns the extension if it exists and false if not * - * @param string $serial - * @param string $id - * @param array $crl optional + * @param String $serial + * @param String $id + * @param Array $crl optional * @access public - * @return mixed + * @return Mixed */ function getRevokedCertificateExtension($serial, $id, $crl = null) { @@ -4726,10 +4335,10 @@ function getRevokedCertificateExtension($serial, $id, $crl = null) /** * Returns a list of all extensions in use for a given revoked certificate * - * @param string $serial + * @param String $serial * @param array $crl optional * @access public - * @return array + * @return Array */ function getRevokedCertificateExtensions($serial, $crl = null) { @@ -4749,13 +4358,13 @@ function getRevokedCertificateExtensions($serial, $crl = null) /** * Set a Revoked Certificate Extension * - * @param string $serial - * @param string $id - * @param mixed $value - * @param bool $critical optional - * @param bool $replace optional + * @param String $serial + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional * @access public - * @return bool + * @return Boolean */ function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) { @@ -4774,8 +4383,8 @@ function setRevokedCertificateExtension($serial, $id, $value, $critical = false, * Extract raw BER from Base64 encoding * * @access private - * @param string $str - * @return string + * @param String $str + * @return String */ function _extractBER($str) { @@ -4788,39 +4397,12 @@ function _extractBER($str) * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ - $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + $temp = preg_replace('#.*?^-+[^-]+-+#ms', '', $str, 1); // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff $temp = preg_replace('#-+[^-]+-+#', '', $temp); // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; return $temp != false ? $temp : $str; } - - /** - * Returns the OID corresponding to a name - * - * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if - * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version - * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able - * to work from version to version. - * - * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that - * what's being passed to it already is an OID and return that instead. A few examples. - * - * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1' - * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1' - * getOID('zzz') == 'zzz' - * - * @access public - * @return string - */ - function getOID($name) - { - static $reverseMap; - if (!isset($reverseMap)) { - $reverseMap = array_flip($this->oids); - } - return isset($reverseMap[$name]) ? $reverseMap[$name] : $name; - } } diff --git a/src/phpseclib/LICENSE b/src/phpseclib/LICENSE new file mode 100755 index 00000000..7fc2d873 --- /dev/null +++ b/src/phpseclib/LICENSE @@ -0,0 +1,21 @@ +Copyright 2007-2013 TerraFrost and other contributors +http://phpseclib.sourceforge.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. \ No newline at end of file diff --git a/src/phpseclib/Math/BigInteger.php b/src/phpseclib/Math/BigInteger.php index 66fb48e4..f82595d9 100755 --- a/src/phpseclib/Math/BigInteger.php +++ b/src/phpseclib/Math/BigInteger.php @@ -6,10 +6,10 @@ * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available, * and an internal implementation, otherwise. * - * PHP version 5 + * PHP versions 4 and 5 * * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the - * {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode) + * {@link MATH_BIGINTEGER_MODE_INTERNAL MATH_BIGINTEGER_MODE_INTERNAL} mode) * * BigInteger uses base-2**26 to perform operations such as multiplication and division and * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible @@ -19,8 +19,12 @@ * which only supports integers. Although this fact will slow this library down, the fact that such a high * base is being used should more than compensate. * + * When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again, + * allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition / + * subtraction). + * * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie. - * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1) + * (new BigInteger(pow(2, 26)))->value = array(0, 1) * * Useful resources are as follows: * @@ -31,8 +35,10 @@ * Here's an example of how to use this library: * * add($b); * @@ -40,19 +46,130 @@ * ?> * * + * LICENSE: 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. + * * @category Math * @package BigInteger * @author Jim Wigginton - * @copyright 2006 Jim Wigginton + * @copyright MMVI Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://pear.php.net/package/Math_BigInteger + * @link http://pear.php.net/package/BigInteger */ namespace phpseclib\Math; -use ParagonIE\ConstantTime\Base64; -use ParagonIE\ConstantTime\Hex; -use phpseclib\Crypt\Random; +/**#@+ + * Reduction constants + * + * @access private + * @see BigInteger::_reduce() + */ +/** + * @see BigInteger::_montgomery() + * @see BigInteger::_prepMontgomery() + */ +@define('MATH_BIGINTEGER_MONTGOMERY', 0); +/** + * @see BigInteger::_barrett() + */ +@define('MATH_BIGINTEGER_BARRETT', 1); +/** + * @see BigInteger::_mod2() + */ +@define('MATH_BIGINTEGER_POWEROF2', 2); +/** + * @see BigInteger::_remainder() + */ +@define('MATH_BIGINTEGER_CLASSIC', 3); +/** + * @see BigInteger::__clone() + */ +@define('MATH_BIGINTEGER_NONE', 4); +/**#@-*/ + +/**#@+ + * Array constants + * + * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and + * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. + * + * @access private + */ +/** + * $result[MATH_BIGINTEGER_VALUE] contains the value. + */ +@define('MATH_BIGINTEGER_VALUE', 0); +/** + * $result[MATH_BIGINTEGER_SIGN] contains the sign. + */ +@define('MATH_BIGINTEGER_SIGN', 1); +/**#@-*/ + +/**#@+ + * @access private + * @see BigInteger::_montgomery() + * @see BigInteger::_barrett() + */ +/** + * Cache constants + * + * $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid. + */ +@define('MATH_BIGINTEGER_VARIABLE', 0); +/** + * $cache[MATH_BIGINTEGER_DATA] contains the cached data. + */ +@define('MATH_BIGINTEGER_DATA', 1); +/**#@-*/ + +/**#@+ + * Mode constants. + * + * @access private + * @see BigInteger::BigInteger() + */ +/** + * To use the pure-PHP implementation + */ +@define('MATH_BIGINTEGER_MODE_INTERNAL', 1); +/** + * To use the BCMath library + * + * (if enabled; otherwise, the internal implementation will be used) + */ +@define('MATH_BIGINTEGER_MODE_BCMATH', 2); +/** + * To use the GMP library + * + * (if present; otherwise, either the BCMath or the internal implementation will be used) + */ +@define('MATH_BIGINTEGER_MODE_GMP', 3); +/**#@-*/ + +/** + * Karatsuba Cutoff + * + * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? + * + * @access private + */ +@define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25); /** * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256 @@ -60,151 +177,39 @@ * * @package BigInteger * @author Jim Wigginton + * @version 1.0.0RC4 * @access public */ class BigInteger { - /**#@+ - * Reduction constants - * - * @access private - * @see BigInteger::_reduce() - */ - /** - * @see BigInteger::_montgomery() - * @see BigInteger::_prepMontgomery() - */ - const MONTGOMERY = 0; /** - * @see BigInteger::_barrett() - */ - const BARRETT = 1; - /** - * @see BigInteger::_mod2() - */ - const POWEROF2 = 2; - /** - * @see BigInteger::_remainder() - */ - const CLASSIC = 3; - /** - * @see BigInteger::__clone() - */ - const NONE = 4; - /**#@-*/ - - /**#@+ - * Array constants - * - * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and - * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them. - * - * @access private - */ - /** - * $result[self::VALUE] contains the value. - */ - const VALUE = 0; - /** - * $result[self::SIGN] contains the sign. - */ - const SIGN = 1; - /**#@-*/ - - /**#@+ - * @access private - * @see BigInteger::_montgomery() - * @see BigInteger::_barrett() - */ - /** - * Cache constants - * - * $cache[self::VARIABLE] tells us whether or not the cached data is still valid. - */ - const VARIABLE = 0; - /** - * $cache[self::DATA] contains the cached data. - */ - const DATA = 1; - /**#@-*/ - - /**#@+ - * Mode constants. - * - * @access private - * @see BigInteger::__construct() - */ - /** - * To use the pure-PHP implementation - */ - const MODE_INTERNAL = 1; - /** - * To use the BCMath library - * - * (if enabled; otherwise, the internal implementation will be used) - */ - const MODE_BCMATH = 2; - /** - * To use the GMP library - * - * (if present; otherwise, either the BCMath or the internal implementation will be used) - */ - const MODE_GMP = 3; - /**#@-*/ - - /** - * Karatsuba Cutoff - * - * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication? + * Holds the BigInteger's value. * + * @var Array * @access private */ - const KARATSUBA_CUTOFF = 25; - - /**#@+ - * Static properties used by the pure-PHP implementation. - * - * @see __construct() - */ - protected static $base; - protected static $baseFull; - protected static $maxDigit; - protected static $msb; - - /** - * $max10 in greatest $max10Len satisfying - * $max10 = 10**$max10Len <= 2**$base. - */ - protected static $max10; - - /** - * $max10Len in greatest $max10Len satisfying - * $max10 = 10**$max10Len <= 2**$base. - */ - protected static $max10Len; - protected static $maxDigit2; - /**#@-*/ + var $value; /** - * Holds the BigInteger's value. + * Holds the BigInteger's magnitude. * - * @var array + * @var Boolean * @access private */ - var $value; + var $is_negative = false; /** - * Holds the BigInteger's magnitude. + * Random number generator function * - * @var bool + * @see setRandomGenerator() * @access private */ - var $is_negative = false; + var $generator = 'mt_rand'; /** * Precision * - * @see self::setPrecision() + * @see setPrecision() * @access private */ var $precision = -1; @@ -212,7 +217,7 @@ class BigInteger /** * Precision Bitmask * - * @see self::setPrecision() + * @see setPrecision() * @access private */ var $bitmask = false; @@ -224,9 +229,9 @@ class BigInteger * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value, * however, $this->hex is only calculated when $this->__sleep() is called. * - * @see self::__sleep() - * @see self::__wakeup() - * @var string + * @see __sleep() + * @see __wakeup() + * @var String * @access private */ var $hex; @@ -239,76 +244,108 @@ class BigInteger * * Here's an example: * - * toString(); // outputs 50 - * ?> + * ?> * * - * @param $x base-10 number or base-$base number if $base set. - * @param int $base - * @return \phpseclib\Math\BigInteger + * @param optional $x base-10 number or base-$base number if $base set. + * @param optional integer $base + * @return BigInteger * @access public */ function __construct($x = 0, $base = 10) { - if (!defined('MATH_BIGINTEGER_MODE')) { + if ( !defined('MATH_BIGINTEGER_MODE') ) { switch (true) { case extension_loaded('gmp'): - define('MATH_BIGINTEGER_MODE', self::MODE_GMP); + @define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP); break; case extension_loaded('bcmath'): - define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH); + @define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH); break; default: - define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL); + @define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL); } } - if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + if (function_exists('openssl_public_encrypt') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work + ob_start(); + phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + $versions = array(); + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $versions[$matches[1][$i]] = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + } + } + + // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ + switch (true) { + case !isset($versions['Header']): + case !isset($versions['Library']): + case $versions['Header'] == $versions['Library']: + @define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); + break; + default: + @define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); + } } if (!defined('PHP_INT_SIZE')) { - define('PHP_INT_SIZE', 4); + @define('PHP_INT_SIZE', 4); } - if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) { + if (!defined('MATH_BIGINTEGER_BASE') && MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_INTERNAL) { switch (PHP_INT_SIZE) { case 8: // use 64-bit integers if int size is 8 bytes - self::$base = 31; - self::$baseFull = 0x80000000; - self::$maxDigit = 0x7FFFFFFF; - self::$msb = 0x40000000; - self::$max10 = 1000000000; - self::$max10Len = 9; - self::$maxDigit2 = pow(2, 62); + @define('MATH_BIGINTEGER_BASE', 31); + @define('MATH_BIGINTEGER_BASE_FULL', 0x80000000); + @define('MATH_BIGINTEGER_MAX_DIGIT', 0x7FFFFFFF); + @define('MATH_BIGINTEGER_MSB', 0x40000000); + // 10**9 is the closest we can get to 2**31 without passing it + @define('MATH_BIGINTEGER_MAX10', 1000000000); + @define('MATH_BIGINTEGER_MAX10_LEN', 9); + // the largest digit that may be used in addition / subtraction + @define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 62)); break; //case 4: // use 64-bit floats if int size is 4 bytes default: - self::$base = 26; - self::$baseFull = 0x4000000; - self::$maxDigit = 0x3FFFFFF; - self::$msb = 0x2000000; - self::$max10 = 10000000; - self::$max10Len = 7; - self::$maxDigit2 = pow(2, 52); // pow() prevents truncation + @define('MATH_BIGINTEGER_BASE', 26); + @define('MATH_BIGINTEGER_BASE_FULL', 0x4000000); + @define('MATH_BIGINTEGER_MAX_DIGIT', 0x3FFFFFF); + @define('MATH_BIGINTEGER_MSB', 0x2000000); + // 10**7 is the closest to 2**26 without passing it + @define('MATH_BIGINTEGER_MAX10', 10000000); + @define('MATH_BIGINTEGER_MAX10_LEN', 7); + // the largest digit that may be used in addition / subtraction + // we do pow(2, 52) instead of using 4503599627370496 directly because some + // PHP installations will truncate 4503599627370496. + @define('MATH_BIGINTEGER_MAX_DIGIT2', pow(2, 52)); } } switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + case MATH_BIGINTEGER_MODE_GMP: switch (true) { case is_resource($x) && get_resource_type($x) == 'GMP integer': // PHP 5.6 switched GMP from using resources to objects - case $x instanceof \GMP: + case is_object($x) && get_class($x) == 'GMP': $this->value = $x; return; } $this->value = gmp_init(0); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $this->value = '0'; break; default: @@ -327,13 +364,13 @@ function __construct($x = 0, $base = 10) $x = ~$x; $this->is_negative = true; } - case 256: - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + case 256: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: $sign = $this->is_negative ? '-' : ''; - $this->value = gmp_init($sign . '0x' . Hex::encode($x)); + $this->value = gmp_init($sign . '0x' . bin2hex($x)); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: // round $len to the nearest 4 (thanks, DavidMJ!) $len = (strlen($x) + 3) & 0xFFFFFFFC; @@ -352,19 +389,19 @@ function __construct($x = 0, $base = 10) // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb) default: while (strlen($x)) { - $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base)); + $this->value[] = $this->_bytes2int($this->_base256_rshift($x, MATH_BIGINTEGER_BASE)); } } if ($this->is_negative) { - if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) { $this->is_negative = false; } - $temp = $this->add(new static('-1')); + $temp = $this->add(new BigInteger('-1')); $this->value = $temp->value; } break; - case 16: + case 16: case -16: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; @@ -376,70 +413,70 @@ function __construct($x = 0, $base = 10) $is_negative = false; if ($base < 0 && hexdec($x[0]) >= 8) { $this->is_negative = $is_negative = true; - $x = Hex::encode(~Hex::decode($x)); + $x = bin2hex(~pack('H*', $x)); } - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: $temp = $this->is_negative ? '-0x' . $x : '0x' . $x; $this->value = gmp_init($temp); $this->is_negative = false; break; - case self::MODE_BCMATH: - $x = (strlen($x) & 1) ? '0' . $x : $x; - $temp = new static(Hex::decode($x), 256); + case MATH_BIGINTEGER_MODE_BCMATH: + $x = ( strlen($x) & 1 ) ? '0' . $x : $x; + $temp = new BigInteger(pack('H*', $x), 256); $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; $this->is_negative = false; break; default: - $x = (strlen($x) & 1) ? '0' . $x : $x; - $temp = new static(Hex::decode($x), 256); + $x = ( strlen($x) & 1 ) ? '0' . $x : $x; + $temp = new BigInteger(pack('H*', $x), 256); $this->value = $temp->value; } if ($is_negative) { - $temp = $this->add(new static('-1')); + $temp = $this->add(new BigInteger('-1')); $this->value = $temp->value; } break; - case 10: + case 10: case -10: // (?value = gmp_init($x); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different // results then doing it on '-1' does (modInverse does $x[0]) $this->value = $x === '-' ? '0' : (string) $x; break; default: - $temp = new static(); + $temp = new BigInteger(); - $multiplier = new static(); - $multiplier->value = array(self::$max10); + $multiplier = new BigInteger(); + $multiplier->value = array(MATH_BIGINTEGER_MAX10); if ($x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } - $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT); + $x = str_pad($x, strlen($x) + ((MATH_BIGINTEGER_MAX10_LEN - 1) * strlen($x)) % MATH_BIGINTEGER_MAX10_LEN, 0, STR_PAD_LEFT); while (strlen($x)) { $temp = $temp->multiply($multiplier); - $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256)); - $x = substr($x, self::$max10Len); + $temp = $temp->add(new BigInteger($this->_int2bytes(substr($x, 0, MATH_BIGINTEGER_MAX10_LEN)), 256)); + $x = substr($x, MATH_BIGINTEGER_MAX10_LEN); } $this->value = $temp->value; } break; - case 2: // base-2 support originally implemented by Lluis Pamies - thanks! + case 2: // base-2 support originally implemented by Lluis Pamies - thanks! case -2: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; @@ -460,7 +497,7 @@ function __construct($x = 0, $base = 10) $str = '-' . $str; } - $temp = new static($str, 8 * $base); // ie. either -16 or +16 + $temp = new BigInteger($str, 8 * $base); // ie. either -16 or +16 $this->value = $temp->value; $this->is_negative = $temp->is_negative; @@ -479,26 +516,28 @@ function __construct($x = 0, $base = 10) * Here's an example: * * toBytes(); // outputs chr(65) * ?> * * - * @param bool $twos_compliment - * @return string + * @param Boolean $twos_compliment + * @return String * @access public * @internal Converts a base-2**26 number to base-2**8 */ function toBytes($twos_compliment = false) { if ($twos_compliment) { - $comparison = $this->compare(new static()); + $comparison = $this->compare(new BigInteger()); if ($comparison == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $temp = $comparison < 0 ? $this->add(new static(1)) : $this; + $temp = $comparison < 0 ? $this->add(new BigInteger(1)) : $this->copy(); $bytes = $temp->toBytes(); if (empty($bytes)) { // eg. if the number we're trying to convert is -1 @@ -512,20 +551,20 @@ function toBytes($twos_compliment = false) return $comparison < 0 ? ~$bytes : $bytes; } - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: if (gmp_cmp($this->value, gmp_init(0)) == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = gmp_strval(gmp_abs($this->value), 16); - $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; - $temp = Hex::decode($temp); + $temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp; + $temp = pack('H*', $temp); return $this->precision > 0 ? substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : ltrim($temp, chr(0)); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: if ($this->value === '0') { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } @@ -551,11 +590,13 @@ function toBytes($twos_compliment = false) if (!count($this->value)) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $result = self::_int2bytes($this->value[count($this->value) - 1]); + $result = $this->_int2bytes($this->value[count($this->value) - 1]); + + $temp = $this->copy(); - for ($i = count($this->value) - 2; $i >= 0; --$i) { - self::_base256_lshift($result, self::$base); - $result = $result | str_pad(self::_int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); + for ($i = count($temp->value) - 2; $i >= 0; --$i) { + $temp->_base256_lshift($result, MATH_BIGINTEGER_BASE); + $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } return $this->precision > 0 ? @@ -572,20 +613,22 @@ function toBytes($twos_compliment = false) * Here's an example: * * toHex(); // outputs '41' * ?> * * - * @param bool $twos_compliment - * @return string + * @param Boolean $twos_compliment + * @return String * @access public * @internal Converts a base-2**26 number to base-2**8 */ function toHex($twos_compliment = false) { - return Hex::encode($this->toBytes($twos_compliment)); + return bin2hex($this->toBytes($twos_compliment)); } /** @@ -597,14 +640,16 @@ function toHex($twos_compliment = false) * Here's an example: * * toBits(); // outputs '1000001' * ?> * * - * @param bool $twos_compliment - * @return string + * @param Boolean $twos_compliment + * @return String * @access public * @internal Converts a base-2**26 number to base-2**2 */ @@ -620,7 +665,7 @@ function toBits($twos_compliment = false) } $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); - if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { + if ($twos_compliment && $this->compare(new BigInteger()) > 0 && $this->precision <= 0) { return '0' . $result; } @@ -633,22 +678,24 @@ function toBits($twos_compliment = false) * Here's an example: * * toString(); // outputs 50 * ?> * * - * @return string + * @return String * @access public * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10) */ function toString() { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: return gmp_strval($this->value); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: if ($this->value === '0') { return '0'; } @@ -660,15 +707,15 @@ function toString() return '0'; } - $temp = clone $this; + $temp = $this->copy(); $temp->is_negative = false; - $divisor = new static(); - $divisor->value = array(self::$max10); + $divisor = new BigInteger(); + $divisor->value = array(MATH_BIGINTEGER_MAX10); $result = ''; while (count($temp->value)) { list($temp, $mod) = $temp->divide($divisor); - $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result; + $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', MATH_BIGINTEGER_MAX10_LEN, '0', STR_PAD_LEFT) . $result; } $result = ltrim($result, '0'); if (empty($result)) { @@ -682,6 +729,29 @@ function toString() return $result; } + /** + * Copy an object + * + * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee + * that all objects are passed by value, when appropriate. More information can be found here: + * + * {@link http://php.net/language.oop5.basic#51624} + * + * @access public + * @see __clone() + * @return BigInteger + */ + function copy() + { + $temp = new BigInteger(); + $temp->value = $this->value; + $temp->is_negative = $this->is_negative; + $temp->generator = $this->generator; + $temp->precision = $this->precision; + $temp->bitmask = $this->bitmask; + return $temp; + } + /** * __toString() magic method * @@ -696,22 +766,43 @@ function __toString() return $this->toString(); } + /** + * __clone() magic method + * + * Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() + * directly in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5 + * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5, + * call BigInteger::copy(), instead. + * + * @access public + * @see copy() + * @return BigInteger + */ + function __clone() + { + return $this->copy(); + } + /** * __sleep() magic method * * Will be called, automatically, when serialize() is called on a BigInteger object. * - * @see self::__wakeup() + * @see __wakeup() * @access public */ function __sleep() { $this->hex = $this->toHex(true); $vars = array('hex'); + if ($this->generator != 'mt_rand') { + $vars[] = 'generator'; + } if ($this->precision > 0) { $vars[] = 'precision'; } return $vars; + } /** @@ -719,61 +810,31 @@ function __sleep() * * Will be called, automatically, when unserialize() is called on a BigInteger object. * - * @see self::__sleep() + * @see __sleep() * @access public */ function __wakeup() { - $temp = new static($this->hex, -16); + $temp = new BigInteger($this->hex, -16); $this->value = $temp->value; $this->is_negative = $temp->is_negative; + $this->setRandomGenerator($this->generator); if ($this->precision > 0) { // recalculate $this->bitmask $this->setPrecision($this->precision); } } - /** - * __debugInfo() magic method - * - * Will be called, automatically, when print_r() or var_dump() are called - * - * @access public - */ - function __debugInfo() - { - $opts = array(); - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $engine = 'gmp'; - break; - case self::MODE_BCMATH: - $engine = 'bcmath'; - break; - case self::MODE_INTERNAL: - $engine = 'internal'; - $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit'; - } - if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - $opts[] = 'OpenSSL'; - } - if (!empty($opts)) { - $engine.= ' (' . implode($opts, ', ') . ')'; - } - return array( - 'value' => '0x' . $this->toHex(true), - 'engine' => $engine - ); - } - /** * Adds two BigIntegers. * * Here's an example: * * add($b); * @@ -781,31 +842,31 @@ function __debugInfo() * ?> * * - * @param \phpseclib\Math\BigInteger $y - * @return \phpseclib\Math\BigInteger + * @param BigInteger $y + * @return BigInteger * @access public * @internal Performs base-2**52 addition */ - function add(BigInteger $y) + function add($y) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_add($this->value, $y->value); return $this->_normalize($temp); - case self::MODE_BCMATH: - $temp = new static(); + case MATH_BIGINTEGER_MODE_BCMATH: + $temp = new BigInteger(); $temp->value = bcadd($this->value, $y->value, 0); return $this->_normalize($temp); } - $temp = self::_add($this->value, $this->is_negative, $y->value, $y->is_negative); + $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); - $result = new static(); - $result->value = $temp[self::VALUE]; - $result->is_negative = $temp[self::SIGN]; + $result = new BigInteger(); + $result->value = $temp[MATH_BIGINTEGER_VALUE]; + $result->is_negative = $temp[MATH_BIGINTEGER_SIGN]; return $this->_normalize($result); } @@ -813,41 +874,41 @@ function add(BigInteger $y) /** * Performs addition. * - * @param array $x_value - * @param bool $x_negative - * @param array $y_value - * @param bool $y_negative - * @return array + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array * @access private */ - static function _add($x_value, $x_negative, $y_value, $y_negative) + function _add($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return array( - self::VALUE => $y_value, - self::SIGN => $y_negative + MATH_BIGINTEGER_VALUE => $y_value, + MATH_BIGINTEGER_SIGN => $y_negative ); - } elseif ($y_size == 0) { + } else if ($y_size == 0) { return array( - self::VALUE => $x_value, - self::SIGN => $x_negative + MATH_BIGINTEGER_VALUE => $x_value, + MATH_BIGINTEGER_SIGN => $x_negative ); } // subtract, if appropriate - if ($x_negative != $y_negative) { - if ($x_value == $y_value) { + if ( $x_negative != $y_negative ) { + if ( $x_value == $y_value ) { return array( - self::VALUE => array(), - self::SIGN => false + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false ); } - $temp = self::_subtract($x_value, false, $y_value, false); - $temp[self::SIGN] = self::_compare($x_value, false, $y_value, false) > 0 ? + $temp = $this->_subtract($x_value, false, $y_value, false); + $temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? $x_negative : $y_negative; return $temp; @@ -861,37 +922,37 @@ static function _add($x_value, $x_negative, $y_value, $y_negative) $value = $x_value; } - $value[count($value)] = 0; // just in case the carry adds an extra digit + $value[] = 0; // just in case the carry adds an extra digit $carry = 0; for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { - $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry; - $carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 - $sum = $carry ? $sum - self::$maxDigit2 : $sum; + $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] + $y_value[$j] * MATH_BIGINTEGER_BASE_FULL + $y_value[$i] + $carry; + $carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 + $sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT2 : $sum; - $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); + $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); - $value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) + $value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000) $value[$j] = $temp; } if ($j == $size) { // ie. if $y_size is odd $sum = $x_value[$i] + $y_value[$i] + $carry; - $carry = $sum >= self::$baseFull; - $value[$i] = $carry ? $sum - self::$baseFull : $sum; + $carry = $sum >= MATH_BIGINTEGER_BASE_FULL; + $value[$i] = $carry ? $sum - MATH_BIGINTEGER_BASE_FULL : $sum; ++$i; // ie. let $i = $j since we've just done $value[$i] } if ($carry) { - for (; $value[$i] == self::$maxDigit; ++$i) { + for (; $value[$i] == MATH_BIGINTEGER_MAX_DIGIT; ++$i) { $value[$i] = 0; } ++$value[$i]; } return array( - self::VALUE => self::_trim($value), - self::SIGN => $x_negative + MATH_BIGINTEGER_VALUE => $this->_trim($value), + MATH_BIGINTEGER_SIGN => $x_negative ); } @@ -901,8 +962,10 @@ static function _add($x_value, $x_negative, $y_value, $y_negative) * Here's an example: * * subtract($b); * @@ -910,31 +973,31 @@ static function _add($x_value, $x_negative, $y_value, $y_negative) * ?> * * - * @param \phpseclib\Math\BigInteger $y - * @return \phpseclib\Math\BigInteger + * @param BigInteger $y + * @return BigInteger * @access public * @internal Performs base-2**52 subtraction */ - function subtract(BigInteger $y) + function subtract($y) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_sub($this->value, $y->value); return $this->_normalize($temp); - case self::MODE_BCMATH: - $temp = new static(); + case MATH_BIGINTEGER_MODE_BCMATH: + $temp = new BigInteger(); $temp->value = bcsub($this->value, $y->value, 0); return $this->_normalize($temp); } - $temp = self::_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); + $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); - $result = new static(); - $result->value = $temp[self::VALUE]; - $result->is_negative = $temp[self::SIGN]; + $result = new BigInteger(); + $result->value = $temp[MATH_BIGINTEGER_VALUE]; + $result->is_negative = $temp[MATH_BIGINTEGER_SIGN]; return $this->_normalize($result); } @@ -942,49 +1005,49 @@ function subtract(BigInteger $y) /** * Performs subtraction. * - * @param array $x_value - * @param bool $x_negative - * @param array $y_value - * @param bool $y_negative - * @return array + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array * @access private */ - static function _subtract($x_value, $x_negative, $y_value, $y_negative) + function _subtract($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return array( - self::VALUE => $y_value, - self::SIGN => !$y_negative + MATH_BIGINTEGER_VALUE => $y_value, + MATH_BIGINTEGER_SIGN => !$y_negative ); - } elseif ($y_size == 0) { + } else if ($y_size == 0) { return array( - self::VALUE => $x_value, - self::SIGN => $x_negative + MATH_BIGINTEGER_VALUE => $x_value, + MATH_BIGINTEGER_SIGN => $x_negative ); } // add, if appropriate (ie. -$x - +$y or +$x - -$y) - if ($x_negative != $y_negative) { - $temp = self::_add($x_value, false, $y_value, false); - $temp[self::SIGN] = $x_negative; + if ( $x_negative != $y_negative ) { + $temp = $this->_add($x_value, false, $y_value, false); + $temp[MATH_BIGINTEGER_SIGN] = $x_negative; return $temp; } - $diff = self::_compare($x_value, $x_negative, $y_value, $y_negative); + $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); - if (!$diff) { + if ( !$diff ) { return array( - self::VALUE => array(), - self::SIGN => false + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false ); } // switch $x and $y around, if appropriate. - if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { + if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; @@ -999,33 +1062,33 @@ static function _subtract($x_value, $x_negative, $y_value, $y_negative) $carry = 0; for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { - $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry; + $sum = $x_value[$j] * MATH_BIGINTEGER_BASE_FULL + $x_value[$i] - $y_value[$j] * MATH_BIGINTEGER_BASE_FULL - $y_value[$i] - $carry; $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1 - $sum = $carry ? $sum + self::$maxDigit2 : $sum; + $sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT2 : $sum; - $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); + $temp = (int) ($sum / MATH_BIGINTEGER_BASE_FULL); - $x_value[$i] = (int) ($sum - self::$baseFull * $temp); + $x_value[$i] = (int) ($sum - MATH_BIGINTEGER_BASE_FULL * $temp); $x_value[$j] = $temp; } if ($j == $y_size) { // ie. if $y_size is odd $sum = $x_value[$i] - $y_value[$i] - $carry; $carry = $sum < 0; - $x_value[$i] = $carry ? $sum + self::$baseFull : $sum; + $x_value[$i] = $carry ? $sum + MATH_BIGINTEGER_BASE_FULL : $sum; ++$i; } if ($carry) { for (; !$x_value[$i]; ++$i) { - $x_value[$i] = self::$maxDigit; + $x_value[$i] = MATH_BIGINTEGER_MAX_DIGIT; } --$x_value[$i]; } return array( - self::VALUE => self::_trim($x_value), - self::SIGN => $x_negative + MATH_BIGINTEGER_VALUE => $this->_trim($x_value), + MATH_BIGINTEGER_SIGN => $x_negative ); } @@ -1035,8 +1098,10 @@ static function _subtract($x_value, $x_negative, $y_value, $y_negative) * Here's an example: * * multiply($b); * @@ -1044,30 +1109,30 @@ static function _subtract($x_value, $x_negative, $y_value, $y_negative) * ?> * * - * @param \phpseclib\Math\BigInteger $x - * @return \phpseclib\Math\BigInteger + * @param BigInteger $x + * @return BigInteger * @access public */ - function multiply(BigInteger $x) + function multiply($x) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_mul($this->value, $x->value); return $this->_normalize($temp); - case self::MODE_BCMATH: - $temp = new static(); + case MATH_BIGINTEGER_MODE_BCMATH: + $temp = new BigInteger(); $temp->value = bcmul($this->value, $x->value, 0); return $this->_normalize($temp); } - $temp = self::_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); + $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); - $product = new static(); - $product->value = $temp[self::VALUE]; - $product->is_negative = $temp[self::SIGN]; + $product = new BigInteger(); + $product->value = $temp[MATH_BIGINTEGER_VALUE]; + $product->is_negative = $temp[MATH_BIGINTEGER_SIGN]; return $this->_normalize($product); } @@ -1075,37 +1140,37 @@ function multiply(BigInteger $x) /** * Performs multiplication. * - * @param array $x_value - * @param bool $x_negative - * @param array $y_value - * @param bool $y_negative - * @return array + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Array * @access private */ - static function _multiply($x_value, $x_negative, $y_value, $y_negative) + function _multiply($x_value, $x_negative, $y_value, $y_negative) { //if ( $x_value == $y_value ) { // return array( - // self::VALUE => $this->_square($x_value), - // self::SIGN => $x_sign != $y_value + // MATH_BIGINTEGER_VALUE => $this->_square($x_value), + // MATH_BIGINTEGER_SIGN => $x_sign != $y_value // ); //} $x_length = count($x_value); $y_length = count($y_value); - if (!$x_length || !$y_length) { // a 0 is being multiplied + if ( !$x_length || !$y_length ) { // a 0 is being multiplied return array( - self::VALUE => array(), - self::SIGN => false + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false ); } return array( - self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? - self::_trim(self::_regularMultiply($x_value, $y_value)) : - self::_trim(self::_karatsuba($x_value, $y_value)), - self::SIGN => $x_negative != $y_negative + MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ? + $this->_trim($this->_regularMultiply($x_value, $y_value)) : + $this->_trim($this->_karatsuba($x_value, $y_value)), + MATH_BIGINTEGER_SIGN => $x_negative != $y_negative ); } @@ -1114,21 +1179,21 @@ static function _multiply($x_value, $x_negative, $y_value, $y_negative) * * Modeled after 'multiply' in MutableBigInteger.java. * - * @param array $x_value - * @param array $y_value - * @return array + * @param Array $x_value + * @param Array $y_value + * @return Array * @access private */ - static function _regularMultiply($x_value, $y_value) + function _regularMultiply($x_value, $y_value) { $x_length = count($x_value); $y_length = count($y_value); - if (!$x_length || !$y_length) { // a 0 is being multiplied + if ( !$x_length || !$y_length ) { // a 0 is being multiplied return array(); } - if ($x_length < $y_length) { + if ( $x_length < $y_length ) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; @@ -1137,7 +1202,7 @@ static function _regularMultiply($x_value, $y_value) $y_length = count($y_value); } - $product_value = self::_array_repeat(0, $x_length + $y_length); + $product_value = $this->_array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but @@ -1149,8 +1214,8 @@ static function _regularMultiply($x_value, $y_value) for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0 $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $product_value[$j] = (int) ($temp - self::$baseFull * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } $product_value[$j] = $carry; @@ -1162,8 +1227,8 @@ static function _regularMultiply($x_value, $y_value) for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $product_value[$k] = (int) ($temp - self::$baseFull * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } $product_value[$k] = $carry; @@ -1178,17 +1243,17 @@ static function _regularMultiply($x_value, $y_value) * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}. * - * @param array $x_value - * @param array $y_value - * @return array + * @param Array $x_value + * @param Array $y_value + * @return Array * @access private */ - static function _karatsuba($x_value, $y_value) + function _karatsuba($x_value, $y_value) { $m = min(count($x_value) >> 1, count($y_value) >> 1); - if ($m < self::KARATSUBA_CUTOFF) { - return self::_regularMultiply($x_value, $y_value); + if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) { + return $this->_regularMultiply($x_value, $y_value); } $x1 = array_slice($x_value, $m); @@ -1196,36 +1261,36 @@ static function _karatsuba($x_value, $y_value) $y1 = array_slice($y_value, $m); $y0 = array_slice($y_value, 0, $m); - $z2 = self::_karatsuba($x1, $y1); - $z0 = self::_karatsuba($x0, $y0); + $z2 = $this->_karatsuba($x1, $y1); + $z0 = $this->_karatsuba($x0, $y0); - $z1 = self::_add($x1, false, $x0, false); - $temp = self::_add($y1, false, $y0, false); - $z1 = self::_karatsuba($z1[self::VALUE], $temp[self::VALUE]); - $temp = self::_add($z2, false, $z0, false); - $z1 = self::_subtract($z1, false, $temp[self::VALUE], false); + $z1 = $this->_add($x1, false, $x0, false); + $temp = $this->_add($y1, false, $y0, false); + $z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); - $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); + $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]); - $xy = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); - $xy = self::_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); + $xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]); + $xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false); - return $xy[self::VALUE]; + return $xy[MATH_BIGINTEGER_VALUE]; } /** * Performs squaring * - * @param array $x - * @return array + * @param Array $x + * @return Array * @access private */ - static function _square($x = false) + function _square($x = false) { - return count($x) < 2 * self::KARATSUBA_CUTOFF ? - self::_trim(self::_baseSquare($x)) : - self::_trim(self::_karatsubaSquare($x)); + return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ? + $this->_trim($this->_baseSquare($x)) : + $this->_trim($this->_karatsubaSquare($x)); } /** @@ -1235,29 +1300,29 @@ static function _square($x = false) * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} / * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information. * - * @param array $value - * @return array + * @param Array $value + * @return Array * @access private */ - static function _baseSquare($value) + function _baseSquare($value) { - if (empty($value)) { + if ( empty($value) ) { return array(); } - $square_value = self::_array_repeat(0, 2 * count($value)); + $square_value = $this->_array_repeat(0, 2 * count($value)); for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { $i2 = $i << 1; $temp = $square_value[$i2] + $value[$i] * $value[$i]; - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $square_value[$i2] = (int) ($temp - self::$baseFull * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $square_value[$i2] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); // note how we start from $i+1 instead of 0 as we do in multiplication. for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $square_value[$k] = (int) ($temp - self::$baseFull * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $square_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } // the following line can yield values larger 2**15. at this point, PHP should switch @@ -1274,36 +1339,36 @@ static function _baseSquare($value) * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}. * - * @param array $value - * @return array + * @param Array $value + * @return Array * @access private */ - static function _karatsubaSquare($value) + function _karatsubaSquare($value) { $m = count($value) >> 1; - if ($m < self::KARATSUBA_CUTOFF) { - return self::_baseSquare($value); + if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) { + return $this->_baseSquare($value); } $x1 = array_slice($value, $m); $x0 = array_slice($value, 0, $m); - $z2 = self::_karatsubaSquare($x1); - $z0 = self::_karatsubaSquare($x0); + $z2 = $this->_karatsubaSquare($x1); + $z0 = $this->_karatsubaSquare($x0); - $z1 = self::_add($x1, false, $x0, false); - $z1 = self::_karatsubaSquare($z1[self::VALUE]); - $temp = self::_add($z2, false, $z0, false); - $z1 = self::_subtract($z1, false, $temp[self::VALUE], false); + $z1 = $this->_add($x1, false, $x0, false); + $z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]); + $temp = $this->_add($z2, false, $z0, false); + $z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); - $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); + $z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]); - $xx = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); - $xx = self::_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); + $xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]); + $xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false); - return $xx[self::VALUE]; + return $xx[MATH_BIGINTEGER_VALUE]; } /** @@ -1317,8 +1382,10 @@ static function _karatsubaSquare($value) * Here's an example: * * divide($b); * @@ -1328,17 +1395,17 @@ static function _karatsubaSquare($value) * ?> * * - * @param \phpseclib\Math\BigInteger $y - * @return array + * @param BigInteger $y + * @return Array * @access public * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. */ - function divide(BigInteger $y) + function divide($y) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $quotient = new static(); - $remainder = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $quotient = new BigInteger(); + $remainder = new BigInteger(); list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); @@ -1347,9 +1414,9 @@ function divide(BigInteger $y) } return array($this->_normalize($quotient), $this->_normalize($remainder)); - case self::MODE_BCMATH: - $quotient = new static(); - $remainder = new static(); + case MATH_BIGINTEGER_MODE_BCMATH: + $quotient = new BigInteger(); + $remainder = new BigInteger(); $quotient->value = bcdiv($this->value, $y->value, 0); $remainder->value = bcmod($this->value, $y->value); @@ -1363,8 +1430,8 @@ function divide(BigInteger $y) if (count($y->value) == 1) { list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); - $quotient = new static(); - $remainder = new static(); + $quotient = new BigInteger(); + $remainder = new BigInteger(); $quotient->value = $q; $remainder->value = array($r); $quotient->is_negative = $this->is_negative != $y->is_negative; @@ -1372,12 +1439,12 @@ function divide(BigInteger $y) } static $zero; - if (!isset($zero)) { - $zero = new static(); + if ( !isset($zero) ) { + $zero = new BigInteger(); } - $x = clone $this; - $y = clone $y; + $x = $this->copy(); + $y = $y->copy(); $x_sign = $x->is_negative; $y_sign = $y->is_negative; @@ -1386,24 +1453,24 @@ function divide(BigInteger $y) $diff = $x->compare($y); - if (!$diff) { - $temp = new static(); + if ( !$diff ) { + $temp = new BigInteger(); $temp->value = array(1); $temp->is_negative = $x_sign != $y_sign; - return array($this->_normalize($temp), $this->_normalize(new static())); + return array($this->_normalize($temp), $this->_normalize(new BigInteger())); } - if ($diff < 0) { + if ( $diff < 0 ) { // if $x is negative, "add" $y. - if ($x_sign) { + if ( $x_sign ) { $x = $y->subtract($x); } - return array($this->_normalize(new static()), $this->_normalize($x)); + return array($this->_normalize(new BigInteger()), $this->_normalize($x)); } // normalize $x and $y as described in HAC 14.23 / 14.24 $msb = $y->value[count($y->value) - 1]; - for ($shift = 0; !($msb & self::$msb); ++$shift) { + for ($shift = 0; !($msb & MATH_BIGINTEGER_MSB); ++$shift) { $msb <<= 1; } $x->_lshift($shift); @@ -1413,15 +1480,15 @@ function divide(BigInteger $y) $x_max = count($x->value) - 1; $y_max = count($y->value) - 1; - $quotient = new static(); + $quotient = new BigInteger(); $quotient_value = &$quotient->value; $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); static $temp, $lhs, $rhs; if (!isset($temp)) { - $temp = new static(); - $lhs = new static(); - $rhs = new static(); + $temp = new BigInteger(); + $lhs = new BigInteger(); + $rhs = new BigInteger(); } $temp_value = &$temp->value; $rhs_value = &$rhs->value; @@ -1429,7 +1496,7 @@ function divide(BigInteger $y) // $temp = $y << ($x_max - $y_max-1) in base 2**26 $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); - while ($x->compare($temp) >= 0) { + while ( $x->compare($temp) >= 0 ) { // calculate the "common residue" ++$quotient_value[$x_max - $y_max]; $x = $x->subtract($temp); @@ -1445,15 +1512,16 @@ function divide(BigInteger $y) ); $y_window = array( $y_value[$y_max], - ($y_max > 0) ? $y_value[$y_max - 1] : 0 + ( $y_max > 0 ) ? $y_value[$y_max - 1] : 0 ); $q_index = $i - $y_max - 1; if ($x_window[0] == $y_window[0]) { - $quotient_value[$q_index] = self::$maxDigit; + $quotient_value[$q_index] = MATH_BIGINTEGER_MAX_DIGIT; } else { - $quotient_value[$q_index] = $this->_safe_divide( - $x_window[0] * self::$baseFull + $x_window[1], + $quotient_value[$q_index] = (int) ( + ($x_window[0] * MATH_BIGINTEGER_BASE_FULL + $x_window[1]) + / $y_window[0] ); } @@ -1465,7 +1533,7 @@ function divide(BigInteger $y) $rhs_value = array($x_window[2], $x_window[1], $x_window[0]); - while ($lhs->compare($rhs) > 0) { + while ( $lhs->compare($rhs) > 0 ) { --$quotient_value[$q_index]; $lhs->value = array($quotient_value[$q_index]); @@ -1496,7 +1564,7 @@ function divide(BigInteger $y) $quotient->is_negative = $x_sign != $y_sign; // calculate the "common residue", if appropriate - if ($x_sign) { + if ( $x_sign ) { $y->_rshift($shift); $x = $y->subtract($x); } @@ -1509,19 +1577,19 @@ function divide(BigInteger $y) * * abc / x = a00 / x + b0 / x + c / x * - * @param array $dividend - * @param array $divisor - * @return array + * @param Array $dividend + * @param Array $divisor + * @return Array * @access private */ - static function _divide_digit($dividend, $divisor) + function _divide_digit($dividend, $divisor) { $carry = 0; $result = array(); for ($i = count($dividend) - 1; $i >= 0; --$i) { - $temp = self::$baseFull * $carry + $dividend[$i]; - $result[$i] = self::_safe_divide($temp, $divisor); + $temp = MATH_BIGINTEGER_BASE_FULL * $carry + $dividend[$i]; + $result[$i] = (int) ($temp / $divisor); $carry = (int) ($temp - $divisor * $result[$i]); } @@ -1534,9 +1602,11 @@ static function _divide_digit($dividend, $divisor) * Here's an example: * * modPow($b, $c); * @@ -1544,9 +1614,9 @@ static function _divide_digit($dividend, $divisor) * ?> * * - * @param \phpseclib\Math\BigInteger $e - * @param \phpseclib\Math\BigInteger $n - * @return \phpseclib\Math\BigInteger + * @param BigInteger $e + * @param BigInteger $n + * @return BigInteger * @access public * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and * and although the approach involving repeated squaring does vastly better, it, too, is impractical @@ -1568,11 +1638,11 @@ static function _divide_digit($dividend, $divisor) * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. */ - function modPow(BigInteger $e, BigInteger $n) + function modPow($e, $n) { $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); - if ($e->compare(new static()) < 0) { + if ($e->compare(new BigInteger()) < 0) { $e = $e->abs(); $temp = $this->modInverse($n); @@ -1583,14 +1653,14 @@ function modPow(BigInteger $e, BigInteger $n) return $this->_normalize($temp->modPow($e, $n)); } - if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { - $temp = new static(); + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP ) { + $temp = new BigInteger(); $temp->value = gmp_powm($this->value, $e->value, $n->value); return $this->_normalize($temp); } - if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { + if ($this->compare(new BigInteger()) < 0 || $this->compare($n) > 0) { list(, $temp) = $this->divide($n); return $temp->modPow($e, $n); } @@ -1602,81 +1672,70 @@ function modPow(BigInteger $e, BigInteger $n) ); $components = array( - 'modulus' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), - 'publicExponent' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) + 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), + 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) ); - $RSAPublicKey = pack( - 'Ca*a*a*', - 48, - self::_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), - $components['modulus'], - $components['publicExponent'] + $RSAPublicKey = pack('Ca*a*a*', + 48, $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), + $components['modulus'], $components['publicExponent'] ); - $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA + $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . self::_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; + $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; - $encapsulated = pack( - 'Ca*a*', - 48, - self::_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), - $rsaOID . $RSAPublicKey + $encapsulated = pack('Ca*a*', + 48, $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey ); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(Base64::encode($encapsulated)) . + chunk_split(base64_encode($encapsulated)) . '-----END PUBLIC KEY-----'; $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { - return new static($result, 256); + return new BigInteger($result, 256); } } - if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { - $temp = new static(); - $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + $temp = new BigInteger(); + $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); - return $this->_normalize($temp); + return $this->_normalize($temp); } - if (empty($e->value)) { - $temp = new static(); + if ( empty($e->value) ) { + $temp = new BigInteger(); $temp->value = array(1); return $this->_normalize($temp); } - if ($e->value == array(1)) { + if ( $e->value == array(1) ) { list(, $temp) = $this->divide($n); return $this->_normalize($temp); } - if ($e->value == array(2)) { - $temp = new static(); - $temp->value = self::_square($this->value); + if ( $e->value == array(2) ) { + $temp = new BigInteger(); + $temp->value = $this->_square($this->value); list(, $temp) = $temp->divide($n); return $this->_normalize($temp); } - return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); - - // the following code, although not callable, can be run independently of the above code - // although the above code performed better in my benchmarks the following could might - // perform better under different circumstances. in lieu of deleting it it's just been - // made uncallable + return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT)); // is the modulo odd? - if ($n->value[0] & 1) { - return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); + if ( $n->value[0] & 1 ) { + return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY)); } // if it's not, it's even // find the lowest set bit (eg. the max pow of 2 that divides $n) for ($i = 0; $i < count($n->value); ++$i) { - if ($n->value[$i]) { + if ( $n->value[$i] ) { $temp = decbin($n->value[$i]); $j = strlen($temp) - strrpos($temp, '1') - 1; $j+= 26 * $i; @@ -1685,14 +1744,14 @@ function modPow(BigInteger $e, BigInteger $n) } // at this point, 2^$j * $n/(2^$j) == $n - $mod1 = clone $n; + $mod1 = $n->copy(); $mod1->_rshift($j); - $mod2 = new static(); + $mod2 = new BigInteger(); $mod2->value = array(1); $mod2->_lshift($j); - $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); - $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); + $part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new BigInteger(); + $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2); $y1 = $mod2->modInverse($mod1); $y2 = $mod1->modInverse($mod2); @@ -1712,14 +1771,14 @@ function modPow(BigInteger $e, BigInteger $n) /** * Performs modular exponentiation. * - * Alias for modPow(). + * Alias for BigInteger::modPow() * - * @param \phpseclib\Math\BigInteger $e - * @param \phpseclib\Math\BigInteger $n - * @return \phpseclib\Math\BigInteger + * @param BigInteger $e + * @param BigInteger $n + * @return BigInteger * @access public */ - function powMod(BigInteger $e, BigInteger $n) + function powMod($e, $n) { return $this->modPow($e, $n); } @@ -1732,10 +1791,10 @@ function powMod(BigInteger $e, BigInteger $n) * however, this function performs a modular reduction after every multiplication and squaring operation. * As such, this function has the same preconditions that the reductions being used do. * - * @param \phpseclib\Math\BigInteger $e - * @param \phpseclib\Math\BigInteger $n - * @param int $mode - * @return \phpseclib\Math\BigInteger + * @param BigInteger $e + * @param BigInteger $n + * @param Integer $mode + * @return BigInteger * @access private */ function _slidingWindow($e, $n, $mode) @@ -1747,58 +1806,56 @@ function _slidingWindow($e, $n, $mode) $e_length = count($e_value) - 1; $e_bits = decbin($e_value[$e_length]); for ($i = $e_length - 1; $i >= 0; --$i) { - $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT); + $e_bits.= str_pad(decbin($e_value[$i]), MATH_BIGINTEGER_BASE, '0', STR_PAD_LEFT); } $e_length = strlen($e_bits); // calculate the appropriate window size. // $window_size == 3 if $window_ranges is between 25 and 81, for example. - for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { - } + for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i); $n_value = $n->value; // precompute $this^0 through $this^$window_size $powers = array(); - $powers[1] = self::_prepareReduce($this->value, $n_value, $mode); - $powers[2] = self::_squareReduce($powers[1], $n_value, $mode); + $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); + $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end // in a 1. ie. it's supposed to be odd. $temp = 1 << ($window_size - 1); for ($i = 1; $i < $temp; ++$i) { $i2 = $i << 1; - $powers[$i2 + 1] = self::_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); + $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); } $result = array(1); - $result = self::_prepareReduce($result, $n_value, $mode); + $result = $this->_prepareReduce($result, $n_value, $mode); - for ($i = 0; $i < $e_length;) { - if (!$e_bits[$i]) { - $result = self::_squareReduce($result, $n_value, $mode); + for ($i = 0; $i < $e_length; ) { + if ( !$e_bits[$i] ) { + $result = $this->_squareReduce($result, $n_value, $mode); ++$i; } else { for ($j = $window_size - 1; $j > 0; --$j) { - if (!empty($e_bits[$i + $j])) { + if ( !empty($e_bits[$i + $j]) ) { break; } } - // eg. the length of substr($e_bits, $i, $j + 1) - for ($k = 0; $k <= $j; ++$k) { - $result = self::_squareReduce($result, $n_value, $mode); + for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1) + $result = $this->_squareReduce($result, $n_value, $mode); } - $result = self::_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); + $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); - $i += $j + 1; + $i+=$j + 1; } } - $temp = new static(); - $temp->value = self::_reduce($result, $n_value, $mode); + $temp = new BigInteger(); + $temp->value = $this->_reduce($result, $n_value, $mode); return $temp; } @@ -1808,34 +1865,34 @@ function _slidingWindow($e, $n, $mode) * * For most $modes this will return the remainder. * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $n - * @param int $mode - * @return array + * @param Array $x + * @param Array $n + * @param Integer $mode + * @return Array */ - static function _reduce($x, $n, $mode) + function _reduce($x, $n, $mode) { switch ($mode) { - case self::MONTGOMERY: - return self::_montgomery($x, $n); - case self::BARRETT: - return self::_barrett($x, $n); - case self::POWEROF2: - $lhs = new static(); + case MATH_BIGINTEGER_MONTGOMERY: + return $this->_montgomery($x, $n); + case MATH_BIGINTEGER_BARRETT: + return $this->_barrett($x, $n); + case MATH_BIGINTEGER_POWEROF2: + $lhs = new BigInteger(); $lhs->value = $x; - $rhs = new static(); + $rhs = new BigInteger(); $rhs->value = $n; return $x->_mod2($n); - case self::CLASSIC: - $lhs = new static(); + case MATH_BIGINTEGER_CLASSIC: + $lhs = new BigInteger(); $lhs->value = $x; - $rhs = new static(); + $rhs = new BigInteger(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; - case self::NONE: + case MATH_BIGINTEGER_NONE: return $x; default: // an invalid $mode was provided @@ -1845,57 +1902,57 @@ static function _reduce($x, $n, $mode) /** * Modular reduction preperation * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $n - * @param int $mode - * @return array + * @param Array $x + * @param Array $n + * @param Integer $mode + * @return Array */ - static function _prepareReduce($x, $n, $mode) + function _prepareReduce($x, $n, $mode) { - if ($mode == self::MONTGOMERY) { - return self::_prepMontgomery($x, $n); + if ($mode == MATH_BIGINTEGER_MONTGOMERY) { + return $this->_prepMontgomery($x, $n); } - return self::_reduce($x, $n, $mode); + return $this->_reduce($x, $n, $mode); } /** * Modular multiply * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $y - * @param array $n - * @param int $mode - * @return array + * @param Array $x + * @param Array $y + * @param Array $n + * @param Integer $mode + * @return Array */ - static function _multiplyReduce($x, $y, $n, $mode) + function _multiplyReduce($x, $y, $n, $mode) { - if ($mode == self::MONTGOMERY) { - return self::_montgomeryMultiply($x, $y, $n); + if ($mode == MATH_BIGINTEGER_MONTGOMERY) { + return $this->_montgomeryMultiply($x, $y, $n); } - $temp = self::_multiply($x, false, $y, false); - return self::_reduce($temp[self::VALUE], $n, $mode); + $temp = $this->_multiply($x, false, $y, false); + return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode); } /** * Modular square * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $n - * @param int $mode - * @return array + * @param Array $x + * @param Array $n + * @param Integer $mode + * @return Array */ - static function _squareReduce($x, $n, $mode) + function _squareReduce($x, $n, $mode) { - if ($mode == self::MONTGOMERY) { - return self::_montgomeryMultiply($x, $x, $n); + if ($mode == MATH_BIGINTEGER_MONTGOMERY) { + return $this->_montgomeryMultiply($x, $x, $n); } - return self::_reduce(self::_square($x), $n, $mode); + return $this->_reduce($this->_square($x), $n, $mode); } /** @@ -1904,14 +1961,14 @@ static function _squareReduce($x, $n, $mode) * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1), * we'll just use this function as a wrapper for doing that. * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param \phpseclib\Math\BigInteger - * @return \phpseclib\Math\BigInteger + * @param BigInteger + * @return BigInteger */ function _mod2($n) { - $temp = new static(); + $temp = new BigInteger(); $temp->value = array(1); return $this->bitwise_and($n->subtract($temp)); } @@ -1934,25 +1991,25 @@ function _mod2($n) * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line * comments for details. * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param array $n - * @param array $m - * @return array + * @param Array $n + * @param Array $m + * @return Array */ - static function _barrett($n, $m) + function _barrett($n, $m) { static $cache = array( - self::VARIABLE => array(), - self::DATA => array() + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() ); $m_length = count($m); - // if (self::_compare($n, self::_square($m)) >= 0) { + // if ($this->_compare($n, $this->_square($m)) >= 0) { if (count($n) > 2 * $m_length) { - $lhs = new static(); - $rhs = new static(); + $lhs = new BigInteger(); + $rhs = new BigInteger(); $lhs->value = $n; $rhs->value = $m; list(, $temp) = $lhs->divide($rhs); @@ -1961,68 +2018,68 @@ static function _barrett($n, $m) // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced if ($m_length < 5) { - return self::_regularBarrett($n, $m); + return $this->_regularBarrett($n, $m); } // n = 2 * m.length - if (($key = array_search($m, $cache[self::VARIABLE])) === false) { - $key = count($cache[self::VARIABLE]); - $cache[self::VARIABLE][] = $m; + if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $m; - $lhs = new static(); + $lhs = new BigInteger(); $lhs_value = &$lhs->value; - $lhs_value = self::_array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); $lhs_value[] = 1; - $rhs = new static(); + $rhs = new BigInteger(); $rhs->value = $m; list($u, $m1) = $lhs->divide($rhs); $u = $u->value; $m1 = $m1->value; - $cache[self::DATA][] = array( + $cache[MATH_BIGINTEGER_DATA][] = array( 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1) 'm1'=> $m1 // m.length ); } else { - extract($cache[self::DATA][$key]); + extract($cache[MATH_BIGINTEGER_DATA][$key]); } $cutoff = $m_length + ($m_length >> 1); $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) $msd = array_slice($n, $cutoff); // m.length >> 1 - $lsd = self::_trim($lsd); - $temp = self::_multiply($msd, false, $m1, false); - $n = self::_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 + $lsd = $this->_trim($lsd); + $temp = $this->_multiply($msd, false, $m1, false); + $n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1 if ($m_length & 1) { - return self::_regularBarrett($n[self::VALUE], $m); + return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m); } // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 - $temp = array_slice($n[self::VALUE], $m_length - 1); + $temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1); // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 - $temp = self::_multiply($temp, false, $u, false); + $temp = $this->_multiply($temp, false, $u, false); // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) - $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); + $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1); // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) - $temp = self::_multiply($temp, false, $m, false); + $temp = $this->_multiply($temp, false, $m, false); // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop // following this comment would loop a lot (hence our calling _regularBarrett() in that situation). - $result = self::_subtract($n[self::VALUE], false, $temp[self::VALUE], false); + $result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); - while (self::_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { - $result = self::_subtract($result[self::VALUE], $result[self::SIGN], $m, false); + while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) { + $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false); } - return $result[self::VALUE]; + return $result[MATH_BIGINTEGER_VALUE]; } /** @@ -2031,70 +2088,70 @@ static function _barrett($n, $m) * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this * is that this function does not fold the denominator into a smaller form. * - * @see self::_slidingWindow() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $n - * @return array + * @param Array $x + * @param Array $n + * @return Array */ - static function _regularBarrett($x, $n) + function _regularBarrett($x, $n) { static $cache = array( - self::VARIABLE => array(), - self::DATA => array() + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() ); $n_length = count($n); if (count($x) > 2 * $n_length) { - $lhs = new static(); - $rhs = new static(); + $lhs = new BigInteger(); + $rhs = new BigInteger(); $lhs->value = $x; $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } - if (($key = array_search($n, $cache[self::VARIABLE])) === false) { - $key = count($cache[self::VARIABLE]); - $cache[self::VARIABLE][] = $n; - $lhs = new static(); + if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $n; + $lhs = new BigInteger(); $lhs_value = &$lhs->value; - $lhs_value = self::_array_repeat(0, 2 * $n_length); + $lhs_value = $this->_array_repeat(0, 2 * $n_length); $lhs_value[] = 1; - $rhs = new static(); + $rhs = new BigInteger(); $rhs->value = $n; list($temp, ) = $lhs->divide($rhs); // m.length - $cache[self::DATA][] = $temp->value; + $cache[MATH_BIGINTEGER_DATA][] = $temp->value; } // 2 * m.length - (m.length - 1) = m.length + 1 $temp = array_slice($x, $n_length - 1); // (m.length + 1) + m.length = 2 * m.length + 1 - $temp = self::_multiply($temp, false, $cache[self::DATA][$key], false); + $temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false); // (2 * m.length + 1) - (m.length - 1) = m.length + 2 - $temp = array_slice($temp[self::VALUE], $n_length + 1); + $temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1); // m.length + 1 $result = array_slice($x, 0, $n_length + 1); // m.length + 1 - $temp = self::_multiplyLower($temp, false, $n, false, $n_length + 1); - // $temp == array_slice(self::_multiply($temp, false, $n, false)->value, 0, $n_length + 1) + $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); + // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1) - if (self::_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { - $corrector_value = self::_array_repeat(0, $n_length + 1); - $corrector_value[count($corrector_value)] = 1; - $result = self::_add($result, false, $corrector_value, false); - $result = $result[self::VALUE]; + if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) { + $corrector_value = $this->_array_repeat(0, $n_length + 1); + $corrector_value[] = 1; + $result = $this->_add($result, false, $corrector_value, false); + $result = $result[MATH_BIGINTEGER_VALUE]; } // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits - $result = self::_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); - while (self::_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { - $result = self::_subtract($result[self::VALUE], $result[self::SIGN], $n, false); + $result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]); + while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) { + $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false); } - return $result[self::VALUE]; + return $result[MATH_BIGINTEGER_VALUE]; } /** @@ -2102,28 +2159,28 @@ static function _regularBarrett($x, $n) * * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved. * - * @see self::_regularBarrett() - * @param array $x_value - * @param bool $x_negative - * @param array $y_value - * @param bool $y_negative - * @param int $stop - * @return array + * @see _regularBarrett() + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @param Integer $stop + * @return Array * @access private */ - static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) + function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) { $x_length = count($x_value); $y_length = count($y_value); - if (!$x_length || !$y_length) { // a 0 is being multiplied + if ( !$x_length || !$y_length ) { // a 0 is being multiplied return array( - self::VALUE => array(), - self::SIGN => false + MATH_BIGINTEGER_VALUE => array(), + MATH_BIGINTEGER_SIGN => false ); } - if ($x_length < $y_length) { + if ( $x_length < $y_length ) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; @@ -2132,7 +2189,7 @@ static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $st $y_length = count($y_value); } - $product_value = self::_array_repeat(0, $x_length + $y_length); + $product_value = $this->_array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but @@ -2144,8 +2201,8 @@ static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $st for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0 - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $product_value[$j] = (int) ($temp - self::$baseFull * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$j] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } if ($j < $stop) { @@ -2160,8 +2217,8 @@ static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $st for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $product_value[$k] = (int) ($temp - self::$baseFull * $carry); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $product_value[$k] = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * $carry); } if ($k < $stop) { @@ -2170,8 +2227,8 @@ static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $st } return array( - self::VALUE => self::_trim($product_value), - self::SIGN => $x_negative != $y_negative + MATH_BIGINTEGER_VALUE => $this->_trim($product_value), + MATH_BIGINTEGER_SIGN => $x_negative != $y_negative ); } @@ -2183,45 +2240,45 @@ static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $st * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function * to work correctly. * - * @see self::_prepMontgomery() - * @see self::_slidingWindow() + * @see _prepMontgomery() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $n - * @return array + * @param Array $x + * @param Array $n + * @return Array */ - static function _montgomery($x, $n) + function _montgomery($x, $n) { static $cache = array( - self::VARIABLE => array(), - self::DATA => array() + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() ); - if (($key = array_search($n, $cache[self::VARIABLE])) === false) { - $key = count($cache[self::VARIABLE]); - $cache[self::VARIABLE][] = $x; - $cache[self::DATA][] = self::_modInverse67108864($n); + if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $x; + $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n); } $k = count($n); - $result = array(self::VALUE => $x); + $result = array(MATH_BIGINTEGER_VALUE => $x); for ($i = 0; $i < $k; ++$i) { - $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; - $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); - $temp = self::_regularMultiply(array($temp), $n); + $temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key]; + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); + $temp = $this->_regularMultiply(array($temp), $n); $temp = array_merge($this->_array_repeat(0, $i), $temp); - $result = self::_add($result[self::VALUE], false, $temp, false); + $result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false); } - $result[self::VALUE] = array_slice($result[self::VALUE], $k); + $result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k); - if (self::_compare($result, false, $n, false) >= 0) { - $result = self::_subtract($result[self::VALUE], false, $n, false); + if ($this->_compare($result, false, $n, false) >= 0) { + $result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false); } - return $result[self::VALUE]; + return $result[MATH_BIGINTEGER_VALUE]; } /** @@ -2230,70 +2287,65 @@ static function _montgomery($x, $n) * Interleaves the montgomery reduction and long multiplication algorithms together as described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36} * - * @see self::_prepMontgomery() - * @see self::_montgomery() + * @see _prepMontgomery() + * @see _montgomery() * @access private - * @param array $x - * @param array $y - * @param array $m - * @return array + * @param Array $x + * @param Array $y + * @param Array $m + * @return Array */ - static function _montgomeryMultiply($x, $y, $m) + function _montgomeryMultiply($x, $y, $m) { - $temp = self::_multiply($x, false, $y, false); - return self::_montgomery($temp[self::VALUE], $m); - - // the following code, although not callable, can be run independently of the above code - // although the above code performed better in my benchmarks the following could might - // perform better under different circumstances. in lieu of deleting it it's just been - // made uncallable + $temp = $this->_multiply($x, false, $y, false); + return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m); static $cache = array( - self::VARIABLE => array(), - self::DATA => array() + MATH_BIGINTEGER_VARIABLE => array(), + MATH_BIGINTEGER_DATA => array() ); - if (($key = array_search($m, $cache[self::VARIABLE])) === false) { - $key = count($cache[self::VARIABLE]); - $cache[self::VARIABLE][] = $m; - $cache[self::DATA][] = self::_modInverse67108864($m); + if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) { + $key = count($cache[MATH_BIGINTEGER_VARIABLE]); + $cache[MATH_BIGINTEGER_VARIABLE][] = $m; + $cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m); } $n = max(count($x), count($y), count($m)); $x = array_pad($x, $n, 0); $y = array_pad($y, $n, 0); $m = array_pad($m, $n, 0); - $a = array(self::VALUE => self::_array_repeat(0, $n + 1)); + $a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1)); for ($i = 0; $i < $n; ++$i) { - $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; - $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); - $temp = $temp * $cache[self::DATA][$key]; - $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); - $temp = self::_add(self::_regularMultiply(array($x[$i]), $y), false, self::_regularMultiply(array($temp), $m), false); - $a = self::_add($a[self::VALUE], false, $temp[self::VALUE], false); - $a[self::VALUE] = array_slice($a[self::VALUE], 1); + $temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0]; + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); + $temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key]; + $temp = (int) ($temp - MATH_BIGINTEGER_BASE_FULL * ((int) ($temp / MATH_BIGINTEGER_BASE_FULL))); + $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); + $a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false); + $a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1); } - if (self::_compare($a[self::VALUE], false, $m, false) >= 0) { - $a = self::_subtract($a[self::VALUE], false, $m, false); + if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) { + $a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false); } - return $a[self::VALUE]; + return $a[MATH_BIGINTEGER_VALUE]; } /** * Prepare a number for use in Montgomery Modular Reductions * - * @see self::_montgomery() - * @see self::_slidingWindow() + * @see _montgomery() + * @see _slidingWindow() * @access private - * @param array $x - * @param array $n - * @return array + * @param Array $x + * @param Array $n + * @return Array */ - static function _prepMontgomery($x, $n) + function _prepMontgomery($x, $n) { - $lhs = new static(); - $lhs->value = array_merge(self::_array_repeat(0, count($n)), $x); - $rhs = new static(); + $lhs = new BigInteger(); + $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); + $rhs = new BigInteger(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); @@ -2321,10 +2373,10 @@ static function _prepMontgomery($x, $n) * * Thanks to Pedro Gimeno Fortea for input! * - * @see self::_montgomery() + * @see _montgomery() * @access private - * @param array $x - * @return int + * @param Array $x + * @return Integer */ function _modInverse67108864($x) // 2**26 == 67,108,864 { @@ -2333,8 +2385,8 @@ function _modInverse67108864($x) // 2**26 == 67,108,864 $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4 $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8 $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16 - $result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26 - return $result & self::$maxDigit; + $result = fmod($result * (2 - fmod($x * $result, MATH_BIGINTEGER_BASE_FULL)), MATH_BIGINTEGER_BASE_FULL); // x**-1 mod 2**26 + return $result & MATH_BIGINTEGER_MAX_DIGIT; } /** @@ -2345,8 +2397,10 @@ function _modInverse67108864($x) // 2**26 == 67,108,864 * Here's an example: * * modInverse($b); * echo $c->toString(); // outputs 4 @@ -2359,25 +2413,25 @@ function _modInverse67108864($x) // 2**26 == 67,108,864 * ?> * * - * @param \phpseclib\Math\BigInteger $n - * @return \phpseclib\Math\BigInteger|false + * @param BigInteger $n + * @return mixed false, if no modular inverse exists, BigInteger, otherwise. * @access public * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. */ - function modInverse(BigInteger $n) + function modInverse($n) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_invert($this->value, $n->value); - return ($temp->value === false) ? false : $this->_normalize($temp); + return ( $temp->value === false ) ? false : $this->_normalize($temp); } static $zero, $one; if (!isset($zero)) { - $zero = new static(); - $one = new static(1); + $zero = new BigInteger(); + $one = new BigInteger(1); } // $x mod -$n == $x mod $n. @@ -2411,8 +2465,10 @@ function modInverse(BigInteger $n) * Here's an example: * * extendedGCD($b)); * @@ -2421,25 +2477,25 @@ function modInverse(BigInteger $n) * ?> * * - * @param \phpseclib\Math\BigInteger $n - * @return \phpseclib\Math\BigInteger + * @param BigInteger $n + * @return BigInteger * @access public * @internal Calculates the GCD using the binary xGCD algorithim described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, * the more traditional algorithim requires "relatively costly multiple-precision divisions". */ - function extendedGCD(BigInteger $n) + function extendedGCD($n) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: extract(gmp_gcdext($this->value, $n->value)); return array( - 'gcd' => $this->_normalize(new static($g)), - 'x' => $this->_normalize(new static($s)), - 'y' => $this->_normalize(new static($t)) + 'gcd' => $this->_normalize(new BigInteger($g)), + 'x' => $this->_normalize(new BigInteger($s)), + 'y' => $this->_normalize(new BigInteger($t)) ); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, // the basic extended euclidean algorithim is what we're using. @@ -2469,38 +2525,38 @@ function extendedGCD(BigInteger $n) } return array( - 'gcd' => $this->_normalize(new static($u)), - 'x' => $this->_normalize(new static($a)), - 'y' => $this->_normalize(new static($b)) + 'gcd' => $this->_normalize(new BigInteger($u)), + 'x' => $this->_normalize(new BigInteger($a)), + 'y' => $this->_normalize(new BigInteger($b)) ); } - $y = clone $n; - $x = clone $this; - $g = new static(); + $y = $n->copy(); + $x = $this->copy(); + $g = new BigInteger(); $g->value = array(1); - while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) { + while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) { $x->_rshift(1); $y->_rshift(1); $g->_lshift(1); } - $u = clone $x; - $v = clone $y; + $u = $x->copy(); + $v = $y->copy(); - $a = new static(); - $b = new static(); - $c = new static(); - $d = new static(); + $a = new BigInteger(); + $b = new BigInteger(); + $c = new BigInteger(); + $d = new BigInteger(); $a->value = $d->value = $g->value = array(1); $b->value = $c->value = array(); - while (!empty($u->value)) { - while (!($u->value[0] & 1)) { + while ( !empty($u->value) ) { + while ( !($u->value[0] & 1) ) { $u->_rshift(1); - if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) { + if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) { $a = $a->add($y); $b = $b->subtract($x); } @@ -2508,9 +2564,9 @@ function extendedGCD(BigInteger $n) $b->_rshift(1); } - while (!($v->value[0] & 1)) { + while ( !($v->value[0] & 1) ) { $v->_rshift(1); - if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) { + if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) { $c = $c->add($y); $d = $d->subtract($x); } @@ -2544,8 +2600,10 @@ function extendedGCD(BigInteger $n) * Here's an example: * * extendedGCD($b); * @@ -2553,11 +2611,11 @@ function extendedGCD(BigInteger $n) * ?> * * - * @param \phpseclib\Math\BigInteger $n - * @return \phpseclib\Math\BigInteger + * @param BigInteger $n + * @return BigInteger * @access public */ - function gcd(BigInteger $n) + function gcd($n) { extract($this->extendedGCD($n)); return $gcd; @@ -2566,18 +2624,18 @@ function gcd(BigInteger $n) /** * Absolute value. * - * @return \phpseclib\Math\BigInteger + * @return BigInteger * @access public */ function abs() { - $temp = new static(); + $temp = new BigInteger(); - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: $temp->value = gmp_abs($this->value); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; break; default: @@ -2599,45 +2657,45 @@ function abs() * * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * - * @param \phpseclib\Math\BigInteger $y - * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @param BigInteger $y + * @return Integer < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @access public - * @see self::equals() + * @see equals() * @internal Could return $this->subtract($x), but that's not as fast as what we do do. */ - function compare(BigInteger $y) + function compare($y) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: return gmp_cmp($this->value, $y->value); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: return bccomp($this->value, $y->value, 0); } - return self::_compare($this->value, $this->is_negative, $y->value, $y->is_negative); + return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); } /** * Compares two numbers. * - * @param array $x_value - * @param bool $x_negative - * @param array $y_value - * @param bool $y_negative - * @return int - * @see self::compare() + * @param Array $x_value + * @param Boolean $x_negative + * @param Array $y_value + * @param Boolean $y_negative + * @return Integer + * @see compare() * @access private */ - static function _compare($x_value, $x_negative, $y_value, $y_negative) + function _compare($x_value, $x_negative, $y_value, $y_negative) { - if ($x_negative != $y_negative) { - return (!$x_negative && $y_negative) ? 1 : -1; + if ( $x_negative != $y_negative ) { + return ( !$x_negative && $y_negative ) ? 1 : -1; } $result = $x_negative ? -1 : 1; - if (count($x_value) != count($y_value)) { - return (count($x_value) > count($y_value)) ? $result : -$result; + if ( count($x_value) != count($y_value) ) { + return ( count($x_value) > count($y_value) ) ? $result : -$result; } $size = max(count($x_value), count($y_value)); @@ -2646,7 +2704,7 @@ static function _compare($x_value, $x_negative, $y_value, $y_negative) for ($i = count($x_value) - 1; $i >= 0; --$i) { if ($x_value[$i] != $y_value[$i]) { - return ($x_value[$i] > $y_value[$i]) ? $result : -$result; + return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result; } } @@ -2658,15 +2716,15 @@ static function _compare($x_value, $x_negative, $y_value, $y_negative) * * If you need to see if one number is greater than or less than another number, use BigInteger::compare() * - * @param \phpseclib\Math\BigInteger $x - * @return bool + * @param BigInteger $x + * @return Boolean * @access public - * @see self::compare() + * @see compare() */ - function equals(BigInteger $x) + function equals($x) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: return gmp_cmp($this->value, $x->value) == 0; default: return $this->value === $x->value && $this->is_negative == $x->is_negative; @@ -2679,57 +2737,39 @@ function equals(BigInteger $x) * Some bitwise operations give different results depending on the precision being used. Examples include left * shift, not, and rotates. * - * @param int $bits + * @param Integer $bits * @access public */ function setPrecision($bits) { - if ($bits < 1) { - $this->precision = -1; - $this->bitmask = false; - - return; - } $this->precision = $bits; - if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { - $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) { + $this->bitmask = new BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); } else { - $this->bitmask = new static(bcpow('2', $bits, 0)); + $this->bitmask = new BigInteger(bcpow('2', $bits, 0)); } $temp = $this->_normalize($this); $this->value = $temp->value; } - /** - * Get Precision - * - * @return int - * @see self::setPrecision() - * @access public - */ - function getPrecision() - { - return $this->precision; - } - /** * Logical And * - * @param \phpseclib\Math\BigInteger $x + * @param BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return \phpseclib\Math\BigInteger + * @return BigInteger */ - function bitwise_and(BigInteger $x) + function bitwise_and($x) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_and($this->value, $x->value); return $this->_normalize($temp); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); @@ -2738,10 +2778,10 @@ function bitwise_and(BigInteger $x) $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)); + return $this->_normalize(new BigInteger($left & $right, 256)); } - $result = clone $this; + $result = $this->copy(); $length = min(count($x->value), count($this->value)); @@ -2757,20 +2797,20 @@ function bitwise_and(BigInteger $x) /** * Logical Or * - * @param \phpseclib\Math\BigInteger $x + * @param BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return \phpseclib\Math\BigInteger + * @return BigInteger */ - function bitwise_or(BigInteger $x) + function bitwise_or($x) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_or($this->value, $x->value); return $this->_normalize($temp); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); @@ -2779,11 +2819,11 @@ function bitwise_or(BigInteger $x) $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)); + return $this->_normalize(new BigInteger($left | $right, 256)); } $length = max(count($this->value), count($x->value)); - $result = clone $this; + $result = $this->copy(); $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2797,20 +2837,20 @@ function bitwise_or(BigInteger $x) /** * Logical Exclusive-Or * - * @param \phpseclib\Math\BigInteger $x + * @param BigInteger $x * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return \phpseclib\Math\BigInteger + * @return BigInteger */ - function bitwise_xor(BigInteger $x) + function bitwise_xor($x) { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - $temp = new static(); + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + $temp = new BigInteger(); $temp->value = gmp_xor($this->value, $x->value); return $this->_normalize($temp); - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); @@ -2819,11 +2859,11 @@ function bitwise_xor(BigInteger $x) $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)); + return $this->_normalize(new BigInteger($left ^ $right, 256)); } $length = max(count($this->value), count($x->value)); - $result = clone $this; + $result = $this->copy(); $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2839,16 +2879,13 @@ function bitwise_xor(BigInteger $x) * * @access public * @internal Implemented per a request by Lluis Pamies i Juarez - * @return \phpseclib\Math\BigInteger + * @return BigInteger */ function bitwise_not() { // calculuate "not" without regard to $this->precision // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) $temp = $this->toBytes(); - if ($temp == '') { - return ''; - } $pre_msb = decbin(ord($temp[0])); $temp = ~$temp; $msb = decbin(ord($temp[0])); @@ -2861,17 +2898,16 @@ function bitwise_not() $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; $new_bits = $this->precision - $current_bits; if ($new_bits <= 0) { - return $this->_normalize(new static($temp, 256)); + return $this->_normalize(new BigInteger($temp, 256)); } // generate as many leading 1's as we need to. $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); + $this->_base256_lshift($leading_ones, $current_bits); - self::_base256_lshift($leading_ones, $current_bits); - - $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); + $temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT); - return $this->_normalize(new static($leading_ones | $temp, 256)); + return $this->_normalize(new BigInteger($leading_ones | $temp, 256)); } /** @@ -2879,17 +2915,17 @@ function bitwise_not() * * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift. * - * @param int $shift - * @return \phpseclib\Math\BigInteger + * @param Integer $shift + * @return BigInteger * @access public * @internal The only version that yields any speed increases is the internal version. */ function bitwise_rightShift($shift) { - $temp = new static(); + $temp = new BigInteger(); - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: static $two; if (!isset($two)) { @@ -2899,7 +2935,7 @@ function bitwise_rightShift($shift) $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); break; @@ -2917,17 +2953,17 @@ function bitwise_rightShift($shift) * * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift. * - * @param int $shift - * @return \phpseclib\Math\BigInteger + * @param Integer $shift + * @return BigInteger * @access public * @internal The only version that yields any speed increases is the internal version. */ function bitwise_leftShift($shift) { - $temp = new static(); + $temp = new BigInteger(); - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: static $two; if (!isset($two)) { @@ -2937,7 +2973,7 @@ function bitwise_leftShift($shift) $temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); break; @@ -2955,8 +2991,8 @@ function bitwise_leftShift($shift) * * Instead of the top x bits being dropped they're appended to the shifted bit string. * - * @param int $shift - * @return \phpseclib\Math\BigInteger + * @param Integer $shift + * @return BigInteger * @access public */ function bitwise_leftRotate($shift) @@ -2965,16 +3001,15 @@ function bitwise_leftRotate($shift) if ($this->precision > 0) { $precision = $this->precision; - if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { - $mask = $this->bitmask->subtract(new static(1)); + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { + $mask = $this->bitmask->subtract(new BigInteger(1)); $mask = $mask->toBytes(); } else { $mask = $this->bitmask->toBytes(); } } else { $temp = ord($bits[0]); - for ($i = 0; $temp >> $i; ++$i) { - } + for ($i = 0; $temp >> $i; ++$i); $precision = 8 * strlen($bits) - 8 + $i; $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); } @@ -2985,13 +3020,13 @@ function bitwise_leftRotate($shift) $shift%= $precision; if (!$shift) { - return clone $this; + return $this->copy(); } $left = $this->bitwise_leftShift($shift); - $left = $left->bitwise_and(new static($mask, 256)); + $left = $left->bitwise_and(new BigInteger($mask, 256)); $right = $this->bitwise_rightShift($precision - $shift); - $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); + $result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); return $this->_normalize($result); } @@ -3000,8 +3035,8 @@ function bitwise_leftRotate($shift) * * Instead of the bottom x bits being dropped they're prepended to the shifted bit string. * - * @param int $shift - * @return \phpseclib\Math\BigInteger + * @param Integer $shift + * @return BigInteger * @access public */ function bitwise_rightRotate($shift) @@ -3009,19 +3044,32 @@ function bitwise_rightRotate($shift) return $this->bitwise_leftRotate(-$shift); } + /** + * Set random number generator function + * + * This function is deprecated. + * + * @param String $generator + * @access public + */ + function setRandomGenerator($generator) + { + } + /** * Generates a random BigInteger * - * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. + * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not. * - * @param int $length - * @return \phpseclib\Math\BigInteger + * @param Integer $length + * @return BigInteger * @access private */ - static function _random_number_helper($size) + function _random_number_helper($size) { - if (class_exists('\phpseclib\Crypt\Random')) { - $random = Random::string($size); + $crypt_random = function_exists('phpseclib\\Crypt\\crypt_random_string') || (!class_exists('phpseclib\\Crypt\\Random') && function_exists('phpseclib\\Crypt\\crypt_random_string')); + if ($crypt_random) { + $random = \phpseclib\Crypt\crypt_random_string($size); } else { $random = ''; @@ -3036,30 +3084,32 @@ static function _random_number_helper($size) } } - return new static($random, 256); + return new BigInteger($random, 256); } /** * Generate a random number * - * Returns a random number between $min and $max where $min and $max - * can be defined using one of the two methods: - * - * BigInteger::random($min, $max) - * BigInteger::random($max, $min) - * - * @param \phpseclib\Math\BigInteger $arg1 - * @param \phpseclib\Math\BigInteger $arg2 - * @return \phpseclib\Math\BigInteger + * @param optional Integer $min + * @param optional Integer $max + * @return BigInteger * @access public */ - static function random(BigInteger $min, BigInteger $max) + function random($min = false, $max = false) { + if ($min === false) { + $min = new BigInteger(0); + } + + if ($max === false) { + $max = new BigInteger(0x7FFFFFFF); + } + $compare = $max->compare($min); if (!$compare) { return $this->_normalize($min); - } elseif ($compare < 0) { + } else if ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; @@ -3068,7 +3118,7 @@ static function random(BigInteger $min, BigInteger $max) static $one; if (!isset($one)) { - $one = new static(1); + $one = new BigInteger(1); } $max = $max->subtract($min->subtract($one)); @@ -3089,8 +3139,8 @@ static function random(BigInteger $min, BigInteger $max) http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string */ - $random_max = new static(chr(1) . str_repeat("\0", $size), 256); - $random = static::_random_number_helper($size); + $random_max = new BigInteger(chr(1) . str_repeat("\0", $size), 256); + $random = $this->_random_number_helper($size); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); @@ -3099,36 +3149,44 @@ static function random(BigInteger $min, BigInteger $max) $random = $random->subtract($max_multiple); $random_max = $random_max->subtract($max_multiple); $random = $random->bitwise_leftShift(8); - $random = $random->add(self::_random_number_helper(1)); + $random = $random->add($this->_random_number_helper(1)); $random_max = $random_max->bitwise_leftShift(8); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); } list(, $random) = $random->divide($max); - return $random->add($min); + return $this->_normalize($random->add($min)); } /** * 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. + * 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 + * @param optional Integer $min + * @param optional Integer $max + * @param optional Integer $timeout + * @return BigInteger * @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) + function randomPrime($min = false, $max = false, $timeout = false) { + if ($min === false) { + $min = new BigInteger(0); + } + + if ($max === false) { + $max = new BigInteger(0x7FFFFFFF); + } + $compare = $max->compare($min); if (!$compare) { return $min->isPrime() ? $min : false; - } elseif ($compare < 0) { + } else if ($compare < 0) { // if $min is bigger then $max, swap $min and $max $temp = $max; $max = $min; @@ -3137,17 +3195,17 @@ static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) static $one, $two; if (!isset($one)) { - $one = new static(1); - $two = new static(2); + $one = new BigInteger(1); + $two = new BigInteger(2); } $start = time(); - $x = self::random($min, $max); + $x = $this->random($min, $max); // gmp_nextprime() requires PHP 5 >= 5.2.0 per . - if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { - $p = new static(); + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) { + $p = new BigInteger(); $p->value = gmp_nextprime($x->value); if ($p->compare($max) <= 0) { @@ -3158,7 +3216,7 @@ static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) $x = $x->subtract($one); } - return self::randomPrime($min, $x); + return $x->randomPrime($min, $x); } if ($x->equals($two)) { @@ -3171,11 +3229,11 @@ static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) if ($min->equals($max)) { return false; } - $x = clone $min; + $x = $min->copy(); $x->_make_odd(); } - $initial_x = clone $x; + $initial_x = $x->copy(); while (true) { if ($timeout !== false && time() - $start > $timeout) { @@ -3189,7 +3247,7 @@ static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) $x = $x->add($two); if ($x->compare($max) > 0) { - $x = clone $min; + $x = $min->copy(); if ($x->equals($two)) { return $x; } @@ -3207,16 +3265,16 @@ static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) * * If the current number is odd it'll be unchanged. If it's even, one will be added to it. * - * @see self::randomPrime() + * @see randomPrime() * @access private */ function _make_odd() { - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: gmp_setbit($this->value, 0); break; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: if ($this->value[strlen($this->value) - 1] % 2 == 0) { $this->value = bcadd($this->value, '1'); } @@ -3230,11 +3288,11 @@ function _make_odd() * Checks a numer to see if it's prime * * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the - * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads + * $t parameter is distributability. BigInteger::randomPrime() can be distributed accross multiple pageloads * on a website instead of just one. * - * @param \phpseclib\Math\BigInteger $t - * @return bool + * @param optional Integer $t + * @return Boolean * @access public * @internal Uses the * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See @@ -3264,10 +3322,10 @@ function isPrime($t = false) // ie. gmp_testbit($this, 0) // ie. isEven() or !isOdd() - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: return gmp_prob_prime($this->value, $t) != 0; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: if ($this->value === '2') { return true; } @@ -3301,15 +3359,15 @@ function isPrime($t = false) 953, 967, 971, 977, 983, 991, 997 ); - if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) { for ($i = 0; $i < count($primes); ++$i) { - $primes[$i] = new static($primes[$i]); + $primes[$i] = new BigInteger($primes[$i]); } } - $zero = new static(); - $one = new static(1); - $two = new static(2); + $zero = new BigInteger(); + $one = new BigInteger(1); + $two = new BigInteger(2); } if ($this->equals($one)) { @@ -3317,7 +3375,7 @@ function isPrime($t = false) } // see HAC 4.4.1 "Random search for probable primes" - if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { + if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) { foreach ($primes as $prime) { list(, $r) = $this->divide($prime); if ($r->equals($zero)) { @@ -3327,21 +3385,21 @@ function isPrime($t = false) } else { $value = $this->value; foreach ($primes as $prime) { - list(, $r) = self::_divide_digit($value, $prime); + list(, $r) = $this->_divide_digit($value, $prime); if (!$r) { return count($value) == 1 && $value[0] == $prime; } } } - $n = clone $this; + $n = $this->copy(); $n_1 = $n->subtract($one); $n_2 = $n->subtract($two); - $r = clone $n_1; + $r = $n_1->copy(); $r_value = $r->value; // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); - if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { + if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) { $s = 0; // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier while ($r->value[strlen($r->value) - 1] % 2 == 0) { @@ -3351,8 +3409,7 @@ function isPrime($t = false) } else { for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { $temp = ~$r_value[$i] & 0xFFFFFF; - for ($j = 1; ($temp >> $j) & 1; ++$j) { - } + for ($j = 1; ($temp >> $j) & 1; ++$j); if ($j != 25) { break; } @@ -3362,7 +3419,7 @@ function isPrime($t = false) } for ($i = 0; $i < $t; ++$i) { - $a = self::random($two, $n_2); + $a = $this->random($two, $n_2); $y = $a->modPow($r, $n); if (!$y->equals($one) && !$y->equals($n_1)) { @@ -3386,29 +3443,29 @@ function isPrime($t = false) * * Shifts BigInteger's by $shift bits. * - * @param int $shift + * @param Integer $shift * @access private */ function _lshift($shift) { - if ($shift == 0) { + if ( $shift == 0 ) { return; } - $num_digits = (int) ($shift / self::$base); - $shift %= self::$base; + $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); + $shift %= MATH_BIGINTEGER_BASE; $shift = 1 << $shift; $carry = 0; for ($i = 0; $i < count($this->value); ++$i) { $temp = $this->value[$i] * $shift + $carry; - $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); - $this->value[$i] = (int) ($temp - $carry * self::$baseFull); + $carry = (int) ($temp / MATH_BIGINTEGER_BASE_FULL); + $this->value[$i] = (int) ($temp - $carry * MATH_BIGINTEGER_BASE_FULL); } - if ($carry) { - $this->value[count($this->value)] = $carry; + if ( $carry ) { + $this->value[] = $carry; } while ($num_digits--) { @@ -3421,7 +3478,7 @@ function _lshift($shift) * * Shifts BigInteger's by $shift bits. * - * @param int $shift + * @param Integer $shift * @access private */ function _rshift($shift) @@ -3430,12 +3487,12 @@ function _rshift($shift) return; } - $num_digits = (int) ($shift / self::$base); - $shift %= self::$base; - $carry_shift = self::$base - $shift; + $num_digits = (int) ($shift / MATH_BIGINTEGER_BASE); + $shift %= MATH_BIGINTEGER_BASE; + $carry_shift = MATH_BIGINTEGER_BASE - $shift; $carry_mask = (1 << $shift) - 1; - if ($num_digits) { + if ( $num_digits ) { $this->value = array_slice($this->value, $num_digits); } @@ -3455,9 +3512,9 @@ function _rshift($shift) * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * - * @param \phpseclib\Math\BigInteger - * @return \phpseclib\Math\BigInteger - * @see self::_trim() + * @param BigInteger + * @return BigInteger + * @see _trim() * @access private */ function _normalize($result) @@ -3465,14 +3522,14 @@ function _normalize($result) $result->precision = $this->precision; $result->bitmask = $this->bitmask; - switch (MATH_BIGINTEGER_MODE) { - case self::MODE_GMP: - if ($this->bitmask !== false) { + switch ( MATH_BIGINTEGER_MODE ) { + case MATH_BIGINTEGER_MODE_GMP: + if (!empty($result->bitmask->value)) { $result->value = gmp_and($result->value, $result->bitmask->value); } return $result; - case self::MODE_BCMATH: + case MATH_BIGINTEGER_MODE_BCMATH: if (!empty($result->bitmask->value)) { $result->value = bcmod($result->value, $result->bitmask->value); } @@ -3482,7 +3539,7 @@ function _normalize($result) $value = &$result->value; - if (!count($value)) { + if ( !count($value) ) { return $result; } @@ -3505,14 +3562,14 @@ function _normalize($result) * * Removes leading zeros * - * @param array $value - * @return \phpseclib\Math\BigInteger + * @param Array $value + * @return BigInteger * @access private */ - static function _trim($value) + function _trim($value) { for ($i = count($value) - 1; $i >= 0; --$i) { - if ($value[$i]) { + if ( $value[$i] ) { break; } unset($value[$i]); @@ -3526,10 +3583,10 @@ static function _trim($value) * * @param $input Array * @param $multiplier mixed - * @return array + * @return Array * @access private */ - static function _array_repeat($input, $multiplier) + function _array_repeat($input, $multiplier) { return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); } @@ -3541,10 +3598,10 @@ static function _array_repeat($input, $multiplier) * * @param $x String * @param $shift Integer - * @return string + * @return String * @access private */ - static function _base256_lshift(&$x, $shift) + function _base256_lshift(&$x, $shift) { if ($shift == 0) { return; @@ -3570,10 +3627,10 @@ static function _base256_lshift(&$x, $shift) * * @param $x String * @param $shift Integer - * @return string + * @return String * @access private */ - static function _base256_rshift(&$x, $shift) + function _base256_rshift(&$x, $shift) { if ($shift == 0) { $x = ltrim($x, chr(0)); @@ -3610,11 +3667,11 @@ static function _base256_rshift(&$x, $shift) /** * Converts 32-bit integers to bytes. * - * @param int $x - * @return string + * @param Integer $x + * @return String * @access private */ - static function _int2bytes($x) + function _int2bytes($x) { return ltrim(pack('N', $x), chr(0)); } @@ -3622,11 +3679,11 @@ static function _int2bytes($x) /** * Converts bytes to 32-bit integers * - * @param string $x - * @return int + * @param String $x + * @return Integer * @access private */ - static function _bytes2int($x) + function _bytes2int($x) { $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); return $temp['int']; @@ -3637,12 +3694,12 @@ static function _bytes2int($x) * * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL * - * @see self::modPow() + * @see modPow() * @access private - * @param int $length - * @return string + * @param Integer $length + * @return String */ - static function _encodeASN1Length($length) + function _encodeASN1Length($length) { if ($length <= 0x7F) { return chr($length); @@ -3651,27 +3708,4 @@ static function _encodeASN1Length($length) $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } - - /** - * Single digit division - * - * Even if int64 is being used the division operator will return a float64 value - * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't - * have the precision of int64 this is a problem so, when int64 is being used, - * we'll guarantee that the dividend is divisible by first subtracting the remainder. - * - * @access private - * @param int $x - * @param int $y - * @return int - */ - static function _safe_divide($x, $y) - { - if (self::$base === 26) { - return (int) ($x / $y); - } - - // self::$base === 31 - return ($x - ($x % $y)) / $y; - } } diff --git a/src/phpseclib/Net/SCP.php b/src/phpseclib/Net/SCP.php deleted file mode 100755 index 4c28d8b0..00000000 --- a/src/phpseclib/Net/SCP.php +++ /dev/null @@ -1,338 +0,0 @@ - - * login('username', 'password')) { - * exit('bad login'); - * } - * $scp = new \phpseclib\Net\SCP($ssh); - * - * $scp->put('abcd', str_repeat('x', 1024*1024)); - * ?> - * - * - * @category Net - * @package SCP - * @author Jim Wigginton - * @copyright 2010 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Net; - -use phpseclib\Exception\FileNotFoundException; - -/** - * Pure-PHP implementations of SCP. - * - * @package SCP - * @author Jim Wigginton - * @access public - */ -class SCP -{ - /**#@+ - * @access public - * @see \phpseclib\Net\SCP::put() - */ - /** - * Reads data from a local file. - */ - const SOURCE_LOCAL_FILE = 1; - /** - * Reads data from a string. - */ - const SOURCE_STRING = 2; - /**#@-*/ - - /**#@+ - * @access private - * @see \phpseclib\Net\SCP::_send() - * @see \phpseclib\Net\SCP::_receive() - */ - /** - * SSH1 is being used. - */ - const MODE_SSH1 = 1; - /** - * SSH2 is being used. - */ - const MODE_SSH2 = 2; - /**#@-*/ - - /** - * SSH Object - * - * @var object - * @access private - */ - var $ssh; - - /** - * Packet Size - * - * @var int - * @access private - */ - var $packet_size; - - /** - * Mode - * - * @var int - * @access private - */ - var $mode; - - /** - * Default Constructor. - * - * Connects to an SSH server - * - * @param string $host - * @param int $port - * @param int $timeout - * @return \phpseclib\Net\SCP - * @access public - */ - function __construct($ssh) - { - if ($ssh instanceof SSH2) { - $this->mode = self::MODE_SSH2; - } elseif ($ssh instanceof SSH1) { - $this->packet_size = 50000; - $this->mode = self::MODE_SSH1; - } else { - return; - } - - $this->ssh = $ssh; - } - - /** - * Uploads a file to the SCP server. - * - * By default, \phpseclib\Net\SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. - * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes - * long, containing 'filename.ext' as its contents. - * - * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will - * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how - * large $remote_file will be, as well. - * - * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take - * care of that, yourself. - * - * @param string $remote_file - * @param string $data - * @param int $mode - * @param callable $callback - * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist - * @return bool - * @access public - */ - function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null) - { - if (!isset($this->ssh)) { - return false; - } - - if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to - return false; - } - - $temp = $this->_receive(); - if ($temp !== chr(0)) { - return false; - } - - if ($this->mode == self::MODE_SSH2) { - $this->packet_size = $this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4; - } - - $remote_file = basename($remote_file); - - if ($mode == self::SOURCE_STRING) { - $size = strlen($data); - } else { - if (!is_file($data)) { - throw new FileNotFoundException("$data is not a valid file"); - } - - $fp = @fopen($data, 'rb'); - if (!$fp) { - return false; - } - $size = filesize($data); - } - - $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n"); - - $temp = $this->_receive(); - if ($temp !== chr(0)) { - return false; - } - - $sent = 0; - while ($sent < $size) { - $temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size); - $this->_send($temp); - $sent+= strlen($temp); - - if (is_callable($callback)) { - call_user_func($callback, $sent); - } - } - $this->_close(); - - if ($mode != self::SOURCE_STRING) { - fclose($fp); - } - - return true; - } - - /** - * Downloads a file from the SCP server. - * - * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if - * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the - * operation - * - * @param string $remote_file - * @param string $local_file - * @return mixed - * @access public - */ - function get($remote_file, $local_file = false) - { - if (!isset($this->ssh)) { - return false; - } - - if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { // -f = from - return false; - } - - $this->_send("\0"); - - if (!preg_match('#(?[^ ]+) (?\d+) (?.+)#', rtrim($this->_receive()), $info)) { - return false; - } - - $this->_send("\0"); - - $size = 0; - - if ($local_file !== false) { - $fp = @fopen($local_file, 'wb'); - if (!$fp) { - return false; - } - } - - $content = ''; - while ($size < $info['size']) { - $data = $this->_receive(); - // SCP usually seems to split stuff out into 16k chunks - $size+= strlen($data); - - if ($local_file === false) { - $content.= $data; - } else { - fputs($fp, $data); - } - } - - $this->_close(); - - if ($local_file !== false) { - fclose($fp); - return true; - } - - return $content; - } - - /** - * Sends a packet to an SSH server - * - * @param string $data - * @access private - */ - function _send($data) - { - switch ($this->mode) { - case self::MODE_SSH2: - $this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC, $data); - break; - case self::MODE_SSH1: - $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data); - $this->ssh->_send_binary_packet($data); - } - } - - /** - * Receives a packet from an SSH server - * - * @return string - * @throws \UnexpectedValueException on receipt of an unexpected packet - * @access private - */ - function _receive() - { - switch ($this->mode) { - case self::MODE_SSH2: - return $this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true); - case self::MODE_SSH1: - if (!$this->ssh->bitmap) { - return false; - } - while (true) { - $response = $this->ssh->_get_binary_packet(); - switch ($response[SSH1::RESPONSE_TYPE]) { - case NET_SSH1_SMSG_STDOUT_DATA: - extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); - return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); - case NET_SSH1_SMSG_STDERR_DATA: - break; - case NET_SSH1_SMSG_EXITSTATUS: - $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION)); - fclose($this->ssh->fsock); - $this->ssh->bitmap = 0; - return false; - default: - throw new \UnexpectedValueException('Unknown packet received'); - } - } - } - } - - /** - * Closes the connection to an SSH server - * - * @access private - */ - function _close() - { - switch ($this->mode) { - case self::MODE_SSH2: - $this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true); - break; - case self::MODE_SSH1: - $this->ssh->disconnect(); - } - } -} diff --git a/src/phpseclib/Net/SFTP.php b/src/phpseclib/Net/SFTP.php deleted file mode 100755 index 6dc498fc..00000000 --- a/src/phpseclib/Net/SFTP.php +++ /dev/null @@ -1,2947 +0,0 @@ - - * login('username', 'password')) { - * exit('Login Failed'); - * } - * - * echo $sftp->pwd() . "\r\n"; - * $sftp->put('filename.ext', 'hello, world!'); - * print_r($sftp->nlist()); - * ?> - * - * - * @category Net - * @package SFTP - * @author Jim Wigginton - * @copyright 2009 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Net; - -use ParagonIE\ConstantTime\Hex; -use phpseclib\Exception\FileNotFoundException; - -/** - * Pure-PHP implementations of SFTP. - * - * @package SFTP - * @author Jim Wigginton - * @access public - */ -class SFTP extends SSH2 -{ - /** - * SFTP channel constant - * - * \phpseclib\Net\SSH2::exec() uses 0 and \phpseclib\Net\SSH2::read() / \phpseclib\Net\SSH2::write() use 1. - * - * @see \phpseclib\Net\SSH2::_send_channel_packet() - * @see \phpseclib\Net\SSH2::_get_channel_packet() - * @access private - */ - const CHANNEL = 0x100; - - /**#@+ - * @access public - * @see \phpseclib\Net\SFTP::put() - */ - /** - * Reads data from a local file. - */ - const SOURCE_LOCAL_FILE = 1; - /** - * Reads data from a string. - */ - // this value isn't really used anymore but i'm keeping it reserved for historical reasons - const SOURCE_STRING = 2; - /** - * Reads data from callback: - * function callback($length) returns string to proceed, null for EOF - */ - const SOURCE_CALLBACK = 16; - /** - * Resumes an upload - */ - const RESUME = 4; - /** - * Append a local file to an already existing remote file - */ - const RESUME_START = 8; - /**#@-*/ - - /** - * Packet Types - * - * @see self::__construct() - * @var array - * @access private - */ - var $packet_types = array(); - - /** - * Status Codes - * - * @see self::__construct() - * @var array - * @access private - */ - var $status_codes = array(); - - /** - * The Request ID - * - * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support - * concurrent actions, so it's somewhat academic, here. - * - * @var int - * @see self::_send_sftp_packet() - * @access private - */ - var $request_id = false; - - /** - * The Packet Type - * - * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support - * concurrent actions, so it's somewhat academic, here. - * - * @var int - * @see self::_get_sftp_packet() - * @access private - */ - var $packet_type = -1; - - /** - * Packet Buffer - * - * @var string - * @see self::_get_sftp_packet() - * @access private - */ - var $packet_buffer = ''; - - /** - * Extensions supported by the server - * - * @var array - * @see self::_initChannel() - * @access private - */ - var $extensions = array(); - - /** - * Server SFTP version - * - * @var int - * @see self::_initChannel() - * @access private - */ - var $version; - - /** - * Current working directory - * - * @var string - * @see self::_realpath() - * @see self::chdir() - * @access private - */ - var $pwd = false; - - /** - * Packet Type Log - * - * @see self::getLog() - * @var array - * @access private - */ - var $packet_type_log = array(); - - /** - * Packet Log - * - * @see self::getLog() - * @var array - * @access private - */ - var $packet_log = array(); - - /** - * Error information - * - * @see self::getSFTPErrors() - * @see self::getLastSFTPError() - * @var string - * @access private - */ - var $sftp_errors = array(); - - /** - * Stat Cache - * - * Rather than always having to open a directory and close it immediately there after to see if a file is a directory - * we'll cache the results. - * - * @see self::_update_stat_cache() - * @see self::_remove_from_stat_cache() - * @see self::_query_stat_cache() - * @var array - * @access private - */ - var $stat_cache = array(); - - /** - * Max SFTP Packet Size - * - * @see self::__construct() - * @see self::get() - * @var array - * @access private - */ - var $max_sftp_packet; - - /** - * Stat Cache Flag - * - * @see self::disableStatCache() - * @see self::enableStatCache() - * @var bool - * @access private - */ - var $use_stat_cache = true; - - /** - * Sort Options - * - * @see self::_comparator() - * @see self::setListOrder() - * @var array - * @access private - */ - var $sortOptions = array(); - - /** - * Default Constructor. - * - * Connects to an SFTP server - * - * @param string $host - * @param int $port - * @param int $timeout - * @return \phpseclib\Net\SFTP - * @access public - */ - function __construct($host, $port = 22, $timeout = 10) - { - parent::__construct($host, $port, $timeout); - - $this->max_sftp_packet = 1 << 15; - - $this->packet_types = array( - 1 => 'NET_SFTP_INIT', - 2 => 'NET_SFTP_VERSION', - /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+: - SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1 - pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */ - 3 => 'NET_SFTP_OPEN', - 4 => 'NET_SFTP_CLOSE', - 5 => 'NET_SFTP_READ', - 6 => 'NET_SFTP_WRITE', - 7 => 'NET_SFTP_LSTAT', - 9 => 'NET_SFTP_SETSTAT', - 11 => 'NET_SFTP_OPENDIR', - 12 => 'NET_SFTP_READDIR', - 13 => 'NET_SFTP_REMOVE', - 14 => 'NET_SFTP_MKDIR', - 15 => 'NET_SFTP_RMDIR', - 16 => 'NET_SFTP_REALPATH', - 17 => 'NET_SFTP_STAT', - /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+: - SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 - pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */ - 18 => 'NET_SFTP_RENAME', - 19 => 'NET_SFTP_READLINK', - 20 => 'NET_SFTP_SYMLINK', - - 101=> 'NET_SFTP_STATUS', - 102=> 'NET_SFTP_HANDLE', - /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+: - SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4 - pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */ - 103=> 'NET_SFTP_DATA', - 104=> 'NET_SFTP_NAME', - 105=> 'NET_SFTP_ATTRS', - - 200=> 'NET_SFTP_EXTENDED' - ); - $this->status_codes = array( - 0 => 'NET_SFTP_STATUS_OK', - 1 => 'NET_SFTP_STATUS_EOF', - 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', - 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', - 4 => 'NET_SFTP_STATUS_FAILURE', - 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', - 6 => 'NET_SFTP_STATUS_NO_CONNECTION', - 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', - 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', - 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', - 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', - 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', - 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', - 13 => 'NET_SFTP_STATUS_NO_MEDIA', - 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', - 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', - 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', - 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', - 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', - 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', - 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', - 21 => 'NET_SFTP_STATUS_LINK_LOOP', - 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', - 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', - 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', - 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', - 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', - 27 => 'NET_SFTP_STATUS_DELETE_PENDING', - 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', - 29 => 'NET_SFTP_STATUS_OWNER_INVALID', - 30 => 'NET_SFTP_STATUS_GROUP_INVALID', - 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' - ); - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 - // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why - $this->attributes = array( - 0x00000001 => 'NET_SFTP_ATTR_SIZE', - 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ - 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', - 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', - // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers - // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in - // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. - // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. - -1 << 31 => 'NET_SFTP_ATTR_EXTENDED' - ); - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 - // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name - // the array for that $this->open5_flags and similarily alter the constant names. - $this->open_flags = array( - 0x00000001 => 'NET_SFTP_OPEN_READ', - 0x00000002 => 'NET_SFTP_OPEN_WRITE', - 0x00000004 => 'NET_SFTP_OPEN_APPEND', - 0x00000008 => 'NET_SFTP_OPEN_CREATE', - 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', - 0x00000020 => 'NET_SFTP_OPEN_EXCL' - ); - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 - // see \phpseclib\Net\SFTP::_parseLongname() for an explanation - $this->file_types = array( - 1 => 'NET_SFTP_TYPE_REGULAR', - 2 => 'NET_SFTP_TYPE_DIRECTORY', - 3 => 'NET_SFTP_TYPE_SYMLINK', - 4 => 'NET_SFTP_TYPE_SPECIAL', - 5 => 'NET_SFTP_TYPE_UNKNOWN', - // the followin types were first defined for use in SFTPv5+ - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 - 6 => 'NET_SFTP_TYPE_SOCKET', - 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', - 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', - 9 => 'NET_SFTP_TYPE_FIFO' - ); - $this->_define_array( - $this->packet_types, - $this->status_codes, - $this->attributes, - $this->open_flags, - $this->file_types - ); - - if (!defined('NET_SFTP_QUEUE_SIZE')) { - define('NET_SFTP_QUEUE_SIZE', 50); - } - } - - /** - * Login - * - * @param string $username - * @param string $password - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return bool - * @access public - */ - function login($username) - { - $args = func_get_args(); - if (!call_user_func_array(array(&$this, '_login'), $args)) { - return false; - } - - $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; - - $packet = pack( - 'CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, - strlen('session'), - 'session', - self::CHANNEL, - $this->window_size, - 0x4000 - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; - - $response = $this->_get_channel_packet(self::CHANNEL); - if ($response === false) { - return false; - } - - $packet = pack( - 'CNNa*CNa*', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL], - strlen('subsystem'), - 'subsystem', - 1, - strlen('sftp'), - 'sftp' - ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; - - $response = $this->_get_channel_packet(self::CHANNEL); - if ($response === false) { - // from PuTTY's psftp.exe - $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . - "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . - "exec sftp-server"; - // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does - // is redundant - $packet = pack( - 'CNNa*CNa*', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL], - strlen('exec'), - 'exec', - 1, - strlen($command), - $command - ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; - - $response = $this->_get_channel_packet(self::CHANNEL); - if ($response === false) { - return false; - } - } - - $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; - - if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_VERSION) { - throw new \UnexpectedValueException('Expected SSH_FXP_VERSION'); - } - - extract(unpack('Nversion', $this->_string_shift($response, 4))); - $this->version = $version; - while (!empty($response)) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $key = $this->_string_shift($response, $length); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $value = $this->_string_shift($response, $length); - $this->extensions[$key] = $value; - } - - /* - SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', - however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's - not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for - one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that - 'newline@vandyke.com' would. - */ - /* - if (isset($this->extensions['newline@vandyke.com'])) { - $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; - unset($this->extensions['newline@vandyke.com']); - } - */ - - $this->request_id = 1; - - /* - A Note on SFTPv4/5/6 support: - states the following: - - "If the client wishes to interoperate with servers that support noncontiguous version - numbers it SHOULD send '3'" - - Given that the server only sends its version number after the client has already done so, the above - seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the - most popular. - - states the following; - - "If the server did not send the "versions" extension, or the version-from-list was not included, the - server MAY send a status response describing the failure, but MUST then close the channel without - processing any further requests." - - So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and - a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements - v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed - in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the - channel and reopen it with a new and updated SSH_FXP_INIT packet. - */ - switch ($this->version) { - case 2: - case 3: - break; - default: - return false; - } - - $this->pwd = $this->_realpath('.'); - - $this->_update_stat_cache($this->pwd, array()); - - return true; - } - - /** - * Disable the stat cache - * - * @access public - */ - function disableStatCache() - { - $this->use_stat_cache = false; - } - - /** - * Enable the stat cache - * - * @access public - */ - function enableStatCache() - { - $this->use_stat_cache = true; - } - - /** - * Clear the stat cache - * - * @access public - */ - function clearStatCache() - { - $this->stat_cache = array(); - } - - /** - * Returns the current directory name - * - * @return mixed - * @access public - */ - function pwd() - { - return $this->pwd; - } - - /** - * Logs errors - * - * @param string $response - * @param int $status - * @access public - */ - function _logError($response, $status = -1) - { - if ($status == -1) { - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - } - - $error = $this->status_codes[$status]; - - if ($this->version > 2) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); - } else { - $this->sftp_errors[] = $error; - } - } - - /** - * Canonicalize the Server-Side Path Name - * - * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns - * the absolute (canonicalized) path. - * - * @see self::chdir() - * @param string $path - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return mixed - * @access private - */ - function _realpath($path) - { - if ($this->pwd === false) { - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 - if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_NAME: - // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following - // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks - // at is the first part and that part is defined the same in SFTP versions 3 through 6. - $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway - extract(unpack('Nlength', $this->_string_shift($response, 4))); - return $this->_string_shift($response, $length); - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - } - } - - if ($path[0] != '/') { - $path = $this->pwd . '/' . $path; - } - - $path = explode('/', $path); - $new = array(); - foreach ($path as $dir) { - if (!strlen($dir)) { - continue; - } - switch ($dir) { - case '..': - array_pop($new); - case '.': - break; - default: - $new[] = $dir; - } - } - - return '/' . implode('/', $new); - } - - /** - * Changes the current directory - * - * @param string $dir - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return bool - * @access public - */ - function chdir($dir) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - // assume current dir if $dir is empty - if ($dir === '') { - $dir = './'; - // suffix a slash if needed - } elseif ($dir[strlen($dir) - 1] != '/') { - $dir.= '/'; - } - - $dir = $this->_realpath($dir); - - // confirm that $dir is, in fact, a valid directory - if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) { - $this->pwd = $dir; - return true; - } - - // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us - // the currently logged in user has the appropriate permissions or not. maybe you could see if - // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy - // way to get those with SFTP - - if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { - return false; - } - - // see \phpseclib\Net\SFTP::nlist() for a more thorough explanation of the following - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - $handle = substr($response, 4); - break; - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - } - - if (!$this->_close_handle($handle)) { - return false; - } - - $this->_update_stat_cache($dir, array()); - - $this->pwd = $dir; - return true; - } - - /** - * Returns a list of files in the given directory - * - * @param string $dir - * @param bool $recursive - * @return mixed - * @access public - */ - function nlist($dir = '.', $recursive = false) - { - return $this->_nlist_helper($dir, $recursive, ''); - } - - /** - * Helper method for nlist - * - * @param string $dir - * @param bool $recursive - * @param string $relativeDir - * @return mixed - * @access private - */ - function _nlist_helper($dir, $recursive, $relativeDir) - { - $files = $this->_list($dir, false); - - if (!$recursive || $files === false) { - return $files; - } - - $result = array(); - foreach ($files as $value) { - if ($value == '.' || $value == '..') { - if ($relativeDir == '') { - $result[] = $value; - } - continue; - } - if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { - $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); - $result = array_merge($result, $temp); - } else { - $result[] = $relativeDir . $value; - } - } - - return $result; - } - - /** - * Returns a detailed list of files in the given directory - * - * @param string $dir - * @param bool $recursive - * @return mixed - * @access public - */ - function rawlist($dir = '.', $recursive = false) - { - $files = $this->_list($dir, true); - if (!$recursive || $files === false) { - return $files; - } - - static $depth = 0; - - foreach ($files as $key => $value) { - if ($depth != 0 && $key == '..') { - unset($files[$key]); - continue; - } - if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) { - $depth++; - $files[$key] = $this->rawlist($dir . '/' . $key, true); - $depth--; - } else { - $files[$key] = (object) $value; - } - } - - return $files; - } - - /** - * Reads a list, be it detailed or not, of files in the given directory - * - * @param string $dir - * @param bool $raw - * @return mixed - * @throws \UnexpectedValueException on receipt of unexpected packets - * @access private - */ - function _list($dir, $raw = true) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $dir = $this->_realpath($dir . '/'); - if ($dir === false) { - return false; - } - - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2 - if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2 - // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that - // represent the length of the string and leave it at that - $handle = substr($response, 4); - break; - case NET_SFTP_STATUS: - // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - $this->_logError($response); - return false; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - } - - $this->_update_stat_cache($dir, array()); - - $contents = array(); - while (true) { - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2 - // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many - // SSH_MSG_CHANNEL_DATA messages is not known to me. - if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_NAME: - extract(unpack('Ncount', $this->_string_shift($response, 4))); - for ($i = 0; $i < $count; $i++) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $shortname = $this->_string_shift($response, $length); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $longname = $this->_string_shift($response, $length); - $attributes = $this->_parseAttributes($response); - if (!isset($attributes['type'])) { - $fileType = $this->_parseLongname($longname); - if ($fileType) { - $attributes['type'] = $fileType; - } - } - $contents[$shortname] = $attributes + array('filename' => $shortname); - - if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { - $this->_update_stat_cache($dir . '/' . $shortname, array()); - } else { - if ($shortname == '..') { - $temp = $this->_realpath($dir . '/..') . '/.'; - } else { - $temp = $dir . '/' . $shortname; - } - $this->_update_stat_cache($temp, (object) array('lstat' => $attributes)); - } - // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the - // final SSH_FXP_STATUS packet should tell us that, already. - } - break; - case NET_SFTP_STATUS: - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_EOF) { - $this->_logError($response, $status); - return false; - } - break 2; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - } - } - - if (!$this->_close_handle($handle)) { - return false; - } - - if (count($this->sortOptions)) { - uasort($contents, array(&$this, '_comparator')); - } - - return $raw ? $contents : array_keys($contents); - } - - /** - * Compares two rawlist entries using parameters set by setListOrder() - * - * Intended for use with uasort() - * - * @param array $a - * @param array $b - * @return int - * @access private - */ - function _comparator($a, $b) - { - switch (true) { - case $a['filename'] === '.' || $b['filename'] === '.': - if ($a['filename'] === $b['filename']) { - return 0; - } - return $a['filename'] === '.' ? -1 : 1; - case $a['filename'] === '..' || $b['filename'] === '..': - if ($a['filename'] === $b['filename']) { - return 0; - } - return $a['filename'] === '..' ? -1 : 1; - case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY: - if (!isset($b['type'])) { - return 1; - } - if ($b['type'] !== $a['type']) { - return -1; - } - break; - case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY: - return 1; - } - foreach ($this->sortOptions as $sort => $order) { - if (!isset($a[$sort]) || !isset($b[$sort])) { - if (isset($a[$sort])) { - return -1; - } - if (isset($b[$sort])) { - return 1; - } - return 0; - } - switch ($sort) { - case 'filename': - $result = strcasecmp($a['filename'], $b['filename']); - if ($result) { - return $order === SORT_DESC ? -$result : $result; - } - break; - case 'permissions': - case 'mode': - $a[$sort]&= 07777; - $b[$sort]&= 07777; - default: - if ($a[$sort] === $b[$sort]) { - break; - } - return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort]; - } - } - } - - /** - * Defines how nlist() and rawlist() will be sorted - if at all. - * - * If sorting is enabled directories and files will be sorted independently with - * directories appearing before files in the resultant array that is returned. - * - * Any parameter returned by stat is a valid sort parameter for this function. - * Filename comparisons are case insensitive. - * - * Examples: - * - * $sftp->setListOrder('filename', SORT_ASC); - * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC); - * $sftp->setListOrder(true); - * Separates directories from files but doesn't do any sorting beyond that - * $sftp->setListOrder(); - * Don't do any sort of sorting - * - * @access public - */ - function setListOrder() - { - $this->sortOptions = array(); - $args = func_get_args(); - if (empty($args)) { - return; - } - $len = count($args) & 0x7FFFFFFE; - for ($i = 0; $i < $len; $i+=2) { - $this->sortOptions[$args[$i]] = $args[$i + 1]; - } - if (!count($this->sortOptions)) { - $this->sortOptions = array('bogus' => true); - } - } - - /** - * Returns the file size, in bytes, or false, on failure - * - * Files larger than 4GB will show up as being exactly 4GB. - * - * @param string $filename - * @return mixed - * @access public - */ - function size($filename) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $result = $this->stat($filename); - if ($result === false) { - return false; - } - return isset($result['size']) ? $result['size'] : -1; - } - - /** - * Save files / directories to cache - * - * @param string $path - * @param mixed $value - * @access private - */ - function _update_stat_cache($path, $value) - { - if ($this->use_stat_cache === false) { - return; - } - - // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/')) - $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); - - $temp = &$this->stat_cache; - $max = count($dirs) - 1; - foreach ($dirs as $i => $dir) { - // if $temp is an object that means one of two things. - // 1. a file was deleted and changed to a directory behind phpseclib's back - // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to - if (is_object($temp)) { - $temp = array(); - } - if (!isset($temp[$dir])) { - $temp[$dir] = array(); - } - if ($i === $max) { - if (is_object($temp[$dir])) { - if (!isset($value->stat) && isset($temp[$dir]->stat)) { - $value->stat = $temp[$dir]->stat; - } - if (!isset($value->lstat) && isset($temp[$dir]->lstat)) { - $value->lstat = $temp[$dir]->lstat; - } - } - $temp[$dir] = $value; - break; - } - $temp = &$temp[$dir]; - } - } - - /** - * Remove files / directories from cache - * - * @param string $path - * @return bool - * @access private - */ - function _remove_from_stat_cache($path) - { - $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); - - $temp = &$this->stat_cache; - $max = count($dirs) - 1; - foreach ($dirs as $i => $dir) { - if ($i === $max) { - unset($temp[$dir]); - return true; - } - if (!isset($temp[$dir])) { - return false; - } - $temp = &$temp[$dir]; - } - } - - /** - * Checks cache for path - * - * Mainly used by file_exists - * - * @param string $dir - * @return mixed - * @access private - */ - function _query_stat_cache($path) - { - $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); - - $temp = &$this->stat_cache; - foreach ($dirs as $dir) { - if (!isset($temp[$dir])) { - return null; - } - $temp = &$temp[$dir]; - } - return $temp; - } - - /** - * Returns general information about a file. - * - * Returns an array on success and false otherwise. - * - * @param string $filename - * @return mixed - * @access public - */ - function stat($filename) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $filename = $this->_realpath($filename); - if ($filename === false) { - return false; - } - - if ($this->use_stat_cache) { - $result = $this->_query_stat_cache($filename); - if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) { - return $result['.']->stat; - } - if (is_object($result) && isset($result->stat)) { - return $result->stat; - } - } - - $stat = $this->_stat($filename, NET_SFTP_STAT); - if ($stat === false) { - $this->_remove_from_stat_cache($filename); - return false; - } - if (isset($stat['type'])) { - if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { - $filename.= '/.'; - } - $this->_update_stat_cache($filename, (object) array('stat' => $stat)); - return $stat; - } - - $pwd = $this->pwd; - $stat['type'] = $this->chdir($filename) ? - NET_SFTP_TYPE_DIRECTORY : - NET_SFTP_TYPE_REGULAR; - $this->pwd = $pwd; - - if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { - $filename.= '/.'; - } - $this->_update_stat_cache($filename, (object) array('stat' => $stat)); - - return $stat; - } - - /** - * Returns general information about a file or symbolic link. - * - * Returns an array on success and false otherwise. - * - * @param string $filename - * @return mixed - * @access public - */ - function lstat($filename) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $filename = $this->_realpath($filename); - if ($filename === false) { - return false; - } - - if ($this->use_stat_cache) { - $result = $this->_query_stat_cache($filename); - if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) { - return $result['.']->lstat; - } - if (is_object($result) && isset($result->lstat)) { - return $result->lstat; - } - } - - $lstat = $this->_stat($filename, NET_SFTP_LSTAT); - if ($lstat === false) { - $this->_remove_from_stat_cache($filename); - return false; - } - if (isset($lstat['type'])) { - if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { - $filename.= '/.'; - } - $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); - return $lstat; - } - - $stat = $this->_stat($filename, NET_SFTP_STAT); - - if ($lstat != $stat) { - $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); - $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); - return $stat; - } - - $pwd = $this->pwd; - $lstat['type'] = $this->chdir($filename) ? - NET_SFTP_TYPE_DIRECTORY : - NET_SFTP_TYPE_REGULAR; - $this->pwd = $pwd; - - if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { - $filename.= '/.'; - } - $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); - - return $lstat; - } - - /** - * Returns general information about a file or symbolic link - * - * Determines information without calling \phpseclib\Net\SFTP::_realpath(). - * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. - * - * @param string $filename - * @param int $type - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return mixed - * @access private - */ - function _stat($filename, $type) - { - // SFTPv4+ adds an additional 32-bit integer field - flags - to the following: - $packet = pack('Na*', strlen($filename), $filename); - if (!$this->_send_sftp_packet($type, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_ATTRS: - return $this->_parseAttributes($response); - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - } - - throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); - } - - /** - * Truncates a file to a given length - * - * @param string $filename - * @param int $new_size - * @return bool - * @access public - */ - function truncate($filename, $new_size) - { - $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32 - - return $this->_setstat($filename, $attr, false); - } - - /** - * Sets access and modification time of file. - * - * If the file does not exist, it will be created. - * - * @param string $filename - * @param int $time - * @param int $atime - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return bool - * @access public - */ - function touch($filename, $time = null, $atime = null) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $filename = $this->_realpath($filename); - if ($filename === false) { - return false; - } - - if (!isset($time)) { - $time = time(); - } - if (!isset($atime)) { - $atime = $time; - } - - $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL; - $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime); - $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr); - if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - return $this->_close_handle(substr($response, 4)); - case NET_SFTP_STATUS: - $this->_logError($response); - break; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - } - - return $this->_setstat($filename, $attr, false); - } - - /** - * Changes file or directory owner - * - * Returns true on success or false on error. - * - * @param string $filename - * @param int $uid - * @param bool $recursive - * @return bool - * @access public - */ - function chown($filename, $uid, $recursive = false) - { - // quoting from , - // "if the owner or group is specified as -1, then that ID is not changed" - $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1); - - return $this->_setstat($filename, $attr, $recursive); - } - - /** - * Changes file or directory group - * - * Returns true on success or false on error. - * - * @param string $filename - * @param int $gid - * @param bool $recursive - * @return bool - * @access public - */ - function chgrp($filename, $gid, $recursive = false) - { - $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid); - - return $this->_setstat($filename, $attr, $recursive); - } - - /** - * Set permissions on a file. - * - * Returns the new file permissions on success or false on error. - * If $recursive is true than this just returns true or false. - * - * @param int $mode - * @param string $filename - * @param bool $recursive - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return mixed - * @access public - */ - function chmod($mode, $filename, $recursive = false) - { - if (is_string($mode) && is_int($filename)) { - $temp = $mode; - $mode = $filename; - $filename = $temp; - } - - $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); - if (!$this->_setstat($filename, $attr, $recursive)) { - return false; - } - if ($recursive) { - return true; - } - - $filename = $this->_realPath($filename); - // rather than return what the permissions *should* be, we'll return what they actually are. this will also - // tell us if the file actually exists. - // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: - $packet = pack('Na*', strlen($filename), $filename); - if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_ATTRS: - $attrs = $this->_parseAttributes($response); - return $attrs['permissions']; - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - } - - throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); - } - - /** - * Sets information about a file - * - * @param string $filename - * @param string $attr - * @param bool $recursive - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return bool - * @access private - */ - function _setstat($filename, $attr, $recursive) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $filename = $this->_realpath($filename); - if ($filename === false) { - return false; - } - - $this->_remove_from_stat_cache($filename); - - if ($recursive) { - $i = 0; - $result = $this->_setstat_recursive($filename, $attr, $i); - $this->_read_put_responses($i); - return $result; - } - - // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to - // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT. - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { - return false; - } - - /* - "Because some systems must use separate system calls to set various attributes, it is possible that a failure - response will be returned, but yet some of the attributes may be have been successfully modified. If possible, - servers SHOULD avoid this situation; however, clients MUST be aware that this is possible." - - -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6 - */ - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; - } - - /** - * Recursively sets information on directories on the SFTP server - * - * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. - * - * @param string $path - * @param string $attr - * @param int $i - * @return bool - * @access private - */ - function _setstat_recursive($path, $attr, &$i) - { - if (!$this->_read_put_responses($i)) { - return false; - } - $i = 0; - $entries = $this->_list($path, true); - - if ($entries === false) { - return $this->_setstat($path, $attr, false); - } - - // normally $entries would have at least . and .. but it might not if the directories - // permissions didn't allow reading - if (empty($entries)) { - return false; - } - - unset($entries['.'], $entries['..']); - foreach ($entries as $filename => $props) { - if (!isset($props['type'])) { - return false; - } - - $temp = $path . '/' . $filename; - if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { - if (!$this->_setstat_recursive($temp, $attr, $i)) { - return false; - } - } else { - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) { - return false; - } - - $i++; - - if ($i >= NET_SFTP_QUEUE_SIZE) { - if (!$this->_read_put_responses($i)) { - return false; - } - $i = 0; - } - } - } - - if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) { - return false; - } - - $i++; - - if ($i >= NET_SFTP_QUEUE_SIZE) { - if (!$this->_read_put_responses($i)) { - return false; - } - $i = 0; - } - - return true; - } - - /** - * Return the target of a symbolic link - * - * @param string $link - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return mixed - * @access public - */ - function readlink($link) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $link = $this->_realpath($link); - - if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_NAME: - break; - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - } - - extract(unpack('Ncount', $this->_string_shift($response, 4))); - // the file isn't a symlink - if (!$count) { - return false; - } - - extract(unpack('Nlength', $this->_string_shift($response, 4))); - return $this->_string_shift($response, $length); - } - - /** - * Create a symlink - * - * symlink() creates a symbolic link to the existing target with the specified name link. - * - * @param string $target - * @param string $link - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return bool - * @access public - */ - function symlink($target, $link) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $target = $this->_realpath($target); - $link = $this->_realpath($link); - - $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link); - if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; - } - - /** - * Creates a directory. - * - * @param string $dir - * @return bool - * @access public - */ - function mkdir($dir, $mode = -1, $recursive = false) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $dir = $this->_realpath($dir); - // by not providing any permissions, hopefully the server will use the logged in users umask - their - // default permissions. - $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); - - if ($recursive) { - $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); - if (empty($dirs[0])) { - array_shift($dirs); - $dirs[0] = '/' . $dirs[0]; - } - for ($i = 0; $i < count($dirs); $i++) { - $temp = array_slice($dirs, 0, $i + 1); - $temp = implode('/', $temp); - $result = $this->_mkdir_helper($temp, $attr); - } - return $result; - } - - return $this->_mkdir_helper($dir, $attr); - } - - /** - * Helper function for directory creation - * - * @param string $dir - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @access private - */ - function _mkdir_helper($dir, $attr) - { - if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; - } - - /** - * Removes a directory. - * - * @param string $dir - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return bool - * @access public - */ - function rmdir($dir) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $dir = $this->_realpath($dir); - if ($dir === false) { - return false; - } - - if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? - $this->_logError($response, $status); - return false; - } - - $this->_remove_from_stat_cache($dir); - // the following will do a soft delete, which would be useful if you deleted a file - // and then tried to do a stat on the deleted file. the above, in contrast, does - // a hard delete - //$this->_update_stat_cache($dir, false); - - return true; - } - - /** - * Uploads a file to the SFTP server. - * - * By default, \phpseclib\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file. - * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SFTP::get(), you will get a file, twelve bytes - * long, containing 'filename.ext' as its contents. - * - * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will - * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how - * large $remote_file will be, as well. - * - * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number of bytes to return, and returns a string if there is some data or null if there is no more data - * - * If $data is a resource then it'll be used as a resource instead. - * - * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take - * care of that, yourself. - * - * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with - * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: - * - * self::SOURCE_LOCAL_FILE | self::RESUME - * - * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace - * self::RESUME with self::RESUME_START. - * - * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed. - * - * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME - * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle - * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the - * middle of one. - * - * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE. - * - * @param string $remote_file - * @param string|resource $data - * @param int $mode - * @param int $start - * @param int $local_start - * @param callable|null $progressCallback - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid - * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist - * @return bool - * @access public - * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode(). - */ - function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $remote_file = $this->_realpath($remote_file); - if ($remote_file === false) { - return false; - } - - $this->_remove_from_stat_cache($remote_file); - - $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; - // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file." - // in practice, it doesn't seem to do that. - //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE; - - if ($start >= 0) { - $offset = $start; - } elseif ($mode & self::RESUME) { - // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called - $size = $this->size($remote_file); - $offset = $size !== false ? $size : 0; - } else { - $offset = 0; - $flags|= NET_SFTP_OPEN_TRUNCATE; - } - - $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); - if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - $handle = substr($response, 4); - break; - case NET_SFTP_STATUS: - $this->_logError($response); - return false; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - } - - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 - $dataCallback = false; - switch (true) { - case $mode & self::SOURCE_CALLBACK: - if (!is_callable($data)) { - throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); - } - $dataCallback = $data; - // do nothing - break; - case is_resource($data): - $mode = $mode & ~self::SOURCE_LOCAL_FILE; - $fp = $data; - break; - case $mode & self::SOURCE_LOCAL_FILE: - if (!is_file($data)) { - throw new FileNotFoundException("$data is not a valid file"); - } - $fp = @fopen($data, 'rb'); - if (!$fp) { - return false; - } - } - - if (isset($fp)) { - $stat = fstat($fp); - $size = $stat['size']; - - if ($local_start >= 0) { - fseek($fp, $local_start); - $size-= $local_start; - } - } elseif ($dataCallback) { - $size = 0; - } else { - $size = strlen($data); - } - - $sent = 0; - $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; - - $sftp_packet_size = 4096; // PuTTY uses 4096 - // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" - $sftp_packet_size-= strlen($handle) + 25; - $i = 0; - while ($dataCallback || ($size === 0 || $sent < $size)) { - if ($dataCallback) { - $temp = call_user_func($dataCallback, $sftp_packet_size); - if (is_null($temp)) { - break; - } - } else { - $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); - if ($temp === false) { - break; - } - } - - $subtemp = $offset + $sent; - $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); - if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { - if ($mode & self::SOURCE_LOCAL_FILE) { - fclose($fp); - } - return false; - } - $sent+= strlen($temp); - if (is_callable($progressCallback)) { - call_user_func($progressCallback, $sent); - } - - $i++; - - if ($i == NET_SFTP_QUEUE_SIZE) { - if (!$this->_read_put_responses($i)) { - $i = 0; - break; - } - $i = 0; - } - } - - if (!$this->_read_put_responses($i)) { - if ($mode & self::SOURCE_LOCAL_FILE) { - fclose($fp); - } - $this->_close_handle($handle); - return false; - } - - if ($mode & self::SOURCE_LOCAL_FILE) { - fclose($fp); - } - - return $this->_close_handle($handle); - } - - /** - * Reads multiple successive SSH_FXP_WRITE responses - * - * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i - * SSH_FXP_WRITEs, in succession, and then reading $i responses. - * - * @param int $i - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @access private - */ - function _read_put_responses($i) - { - while ($i--) { - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - break; - } - } - - return $i < 0; - } - - /** - * Close handle - * - * @param string $handle - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @access private - */ - function _close_handle($handle) - { - if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { - return false; - } - - // "The client MUST release all resources associated with the handle regardless of the status." - // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3 - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - return true; - } - - /** - * Downloads a file from the SFTP server. - * - * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if - * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the - * operation. - * - * $offset and $length can be used to download files in chunks. - * - * @param string $remote_file - * @param string $local_file - * @param int $offset - * @param int $length - * @throws \UnexpectedValueException on receipt of unexpected packets - * @return mixed - * @access public - */ - function get($remote_file, $local_file = false, $offset = 0, $length = -1) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $remote_file = $this->_realpath($remote_file); - if ($remote_file === false) { - return false; - } - - $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); - if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - $handle = substr($response, 4); - break; - case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - $this->_logError($response); - return false; - default: - throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - } - - if (is_resource($local_file)) { - $fp = $local_file; - $stat = fstat($fp); - $res_offset = $stat['size']; - } else { - $res_offset = 0; - if ($local_file !== false) { - $fp = fopen($local_file, 'wb'); - if (!$fp) { - return false; - } - } else { - $content = ''; - } - } - - $fclose_check = $local_file !== false && !is_resource($local_file); - - $start = $offset; - $read = 0; - while (true) { - $i = 0; - - while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) { - $tempoffset = $start + $read; - - $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; - - $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); - if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { - if ($fclose_check) { - fclose($fp); - } - return false; - } - $packet = null; - $read+= $packet_size; - $i++; - } - - if (!$i) { - break; - } - - $clear_responses = false; - while ($i > 0) { - $i--; - - if ($clear_responses) { - $this->_get_sftp_packet(); - continue; - } else { - $response = $this->_get_sftp_packet(); - } - - switch ($this->packet_type) { - case NET_SFTP_DATA: - $temp = substr($response, 4); - $offset+= strlen($temp); - if ($local_file === false) { - $content.= $temp; - } else { - fputs($fp, $temp); - } - $temp = null; - break; - case NET_SFTP_STATUS: - // could, in theory, return false if !strlen($content) but we'll hold off for the time being - $this->_logError($response); - $clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses - break; - default: - if ($fclose_check) { - fclose($fp); - } - throw new \UnexpectedValueException('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); - } - $response = null; - } - - if ($clear_responses) { - break; - } - } - - if ($length > 0 && $length <= $offset - $start) { - if ($local_file === false) { - $content = substr($content, 0, $length); - } else { - ftruncate($fp, $length + $res_offset); - } - } - - if ($fclose_check) { - fclose($fp); - } - - if (!$this->_close_handle($handle)) { - return false; - } - - // if $content isn't set that means a file was written to - return isset($content) ? $content : true; - } - - /** - * Deletes a file on the SFTP server. - * - * @param string $path - * @param bool $recursive - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @access public - */ - function delete($path, $recursive = true) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $path = $this->_realpath($path); - if ($path === false) { - return false; - } - - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 - if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - if (!$recursive) { - return false; - } - $i = 0; - $result = $this->_delete_recursive($path, $i); - $this->_read_put_responses($i); - return $result; - } - - $this->_remove_from_stat_cache($path); - - return true; - } - - /** - * Recursively deletes directories on the SFTP server - * - * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. - * - * @param string $path - * @param int $i - * @return bool - * @access private - */ - function _delete_recursive($path, &$i) - { - if (!$this->_read_put_responses($i)) { - return false; - } - $i = 0; - $entries = $this->_list($path, true); - - // normally $entries would have at least . and .. but it might not if the directories - // permissions didn't allow reading - if (empty($entries)) { - return false; - } - - unset($entries['.'], $entries['..']); - foreach ($entries as $filename => $props) { - if (!isset($props['type'])) { - return false; - } - - $temp = $path . '/' . $filename; - if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { - if (!$this->_delete_recursive($temp, $i)) { - return false; - } - } else { - if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) { - return false; - } - $this->_remove_from_stat_cache($temp); - - $i++; - - if ($i >= NET_SFTP_QUEUE_SIZE) { - if (!$this->_read_put_responses($i)) { - return false; - } - $i = 0; - } - } - } - - if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) { - return false; - } - $this->_remove_from_stat_cache($path); - - $i++; - - if ($i >= NET_SFTP_QUEUE_SIZE) { - if (!$this->_read_put_responses($i)) { - return false; - } - $i = 0; - } - - return true; - } - - /** - * Checks whether a file or directory exists - * - * @param string $path - * @return bool - * @access public - */ - function file_exists($path) - { - if ($this->use_stat_cache) { - $path = $this->_realpath($path); - - $result = $this->_query_stat_cache($path); - - if (isset($result)) { - // return true if $result is an array or if it's an stdClass object - return $result !== false; - } - } - - return $this->stat($path) !== false; - } - - /** - * Tells whether the filename is a directory - * - * @param string $path - * @return bool - * @access public - */ - function is_dir($path) - { - $result = $this->_get_stat_cache_prop($path, 'type'); - if ($result === false) { - return false; - } - return $result === NET_SFTP_TYPE_DIRECTORY; - } - - /** - * Tells whether the filename is a regular file - * - * @param string $path - * @return bool - * @access public - */ - function is_file($path) - { - $result = $this->_get_stat_cache_prop($path, 'type'); - if ($result === false) { - return false; - } - return $result === NET_SFTP_TYPE_REGULAR; - } - - /** - * Tells whether the filename is a symbolic link - * - * @param string $path - * @return bool - * @access public - */ - function is_link($path) - { - $result = $this->_get_lstat_cache_prop($path, 'type'); - if ($result === false) { - return false; - } - return $result === NET_SFTP_TYPE_SYMLINK; - } - - /** - * Tells whether a file exists and is readable - * - * @param string $path - * @return bool - * @access public - */ - function is_readable($path) - { - $path = $this->_realpath($path); - - $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0); - if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - return true; - case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - return false; - default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; - } - } - - /** - * Tells whether the filename is writable - * - * @param string $path - * @return bool - * @access public - */ - function is_writable($path) - { - $path = $this->_realpath($path); - - $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0); - if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - switch ($this->packet_type) { - case NET_SFTP_HANDLE: - return true; - case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - return false; - default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; - } - } - - /** - * Tells whether the filename is writeable - * - * Alias of is_writable - * - * @param string $path - * @return bool - * @access public - */ - function is_writeable($path) - { - return $this->is_writable($path); - } - - /** - * Gets last access time of file - * - * @param string $path - * @return mixed - * @access public - */ - function fileatime($path) - { - return $this->_get_stat_cache_prop($path, 'atime'); - } - - /** - * Gets file modification time - * - * @param string $path - * @return mixed - * @access public - */ - function filemtime($path) - { - return $this->_get_stat_cache_prop($path, 'mtime'); - } - - /** - * Gets file permissions - * - * @param string $path - * @return mixed - * @access public - */ - function fileperms($path) - { - return $this->_get_stat_cache_prop($path, 'permissions'); - } - - /** - * Gets file owner - * - * @param string $path - * @return mixed - * @access public - */ - function fileowner($path) - { - return $this->_get_stat_cache_prop($path, 'uid'); - } - - /** - * Gets file group - * - * @param string $path - * @return mixed - * @access public - */ - function filegroup($path) - { - return $this->_get_stat_cache_prop($path, 'gid'); - } - - /** - * Gets file size - * - * @param string $path - * @return mixed - * @access public - */ - function filesize($path) - { - return $this->_get_stat_cache_prop($path, 'size'); - } - - /** - * Gets file type - * - * @param string $path - * @return mixed - * @access public - */ - function filetype($path) - { - $type = $this->_get_stat_cache_prop($path, 'type'); - if ($type === false) { - return false; - } - - switch ($type) { - case NET_SFTP_TYPE_BLOCK_DEVICE: - return 'block'; - case NET_SFTP_TYPE_CHAR_DEVICE: - return 'char'; - case NET_SFTP_TYPE_DIRECTORY: - return 'dir'; - case NET_SFTP_TYPE_FIFO: - return 'fifo'; - case NET_SFTP_TYPE_REGULAR: - return 'file'; - case NET_SFTP_TYPE_SYMLINK: - return 'link'; - default: - return false; - } - } - - /** - * Return a stat properity - * - * Uses cache if appropriate. - * - * @param string $path - * @param string $prop - * @return mixed - * @access private - */ - function _get_stat_cache_prop($path, $prop) - { - return $this->_get_xstat_cache_prop($path, $prop, 'stat'); - } - - /** - * Return an lstat properity - * - * Uses cache if appropriate. - * - * @param string $path - * @param string $prop - * @return mixed - * @access private - */ - function _get_lstat_cache_prop($path, $prop) - { - return $this->_get_xstat_cache_prop($path, $prop, 'lstat'); - } - - /** - * Return a stat or lstat properity - * - * Uses cache if appropriate. - * - * @param string $path - * @param string $prop - * @return mixed - * @access private - */ - function _get_xstat_cache_prop($path, $prop, $type) - { - if ($this->use_stat_cache) { - $path = $this->_realpath($path); - - $result = $this->_query_stat_cache($path); - - if (is_object($result) && isset($result->$type)) { - return $result->{$type}[$prop]; - } - } - - $result = $this->$type($path); - - if ($result === false || !isset($result[$prop])) { - return false; - } - - return $result[$prop]; - } - - /** - * Renames a file or a directory on the SFTP server - * - * @param string $oldname - * @param string $newname - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @access public - */ - function rename($oldname, $newname) - { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { - return false; - } - - $oldname = $this->_realpath($oldname); - $newname = $this->_realpath($newname); - if ($oldname === false || $newname === false) { - return false; - } - - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3 - $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); - if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { - return false; - } - - $response = $this->_get_sftp_packet(); - if ($this->packet_type != NET_SFTP_STATUS) { - throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); - } - - // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - extract(unpack('Nstatus', $this->_string_shift($response, 4))); - if ($status != NET_SFTP_STATUS_OK) { - $this->_logError($response, $status); - return false; - } - - // don't move the stat cache entry over since this operation could very well change the - // atime and mtime attributes - //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname)); - $this->_remove_from_stat_cache($oldname); - $this->_remove_from_stat_cache($newname); - - return true; - } - - /** - * Parse Attributes - * - * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info. - * - * @param string $response - * @return array - * @access private - */ - function _parseAttributes(&$response) - { - $attr = array(); - extract(unpack('Nflags', $this->_string_shift($response, 4))); - // SFTPv4+ have a type field (a byte) that follows the above flag field - foreach ($this->attributes as $key => $value) { - switch ($flags & $key) { - case NET_SFTP_ATTR_SIZE: // 0x00000001 - // The size attribute is defined as an unsigned 64-bit integer. - // The following will use floats on 32-bit platforms, if necessary. - // As can be seen in the BigInteger class, floats are generally - // IEEE 754 binary64 "double precision" on such platforms and - // as such can represent integers of at least 2^50 without loss - // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. - $attr['size'] = hexdec(Hex::encode($this->_string_shift($response, 8))); - break; - case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) - $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); - break; - case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 - $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); - // mode == permissions; permissions was the original array key and is retained for bc purposes. - // mode was added because that's the more industry standard terminology - $attr+= array('mode' => $attr['permissions']); - $fileType = $this->_parseMode($attr['permissions']); - if ($fileType !== false) { - $attr+= array('type' => $fileType); - } - break; - case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 - $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); - break; - case NET_SFTP_ATTR_EXTENDED: // 0x80000000 - extract(unpack('Ncount', $this->_string_shift($response, 4))); - for ($i = 0; $i < $count; $i++) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $key = $this->_string_shift($response, $length); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $attr[$key] = $this->_string_shift($response, $length); - } - } - } - return $attr; - } - - /** - * Attempt to identify the file type - * - * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway - * - * @param int $mode - * @return int - * @access private - */ - function _parseMode($mode) - { - // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12 - // see, also, http://linux.die.net/man/2/stat - switch ($mode & 0170000) {// ie. 1111 0000 0000 0000 - case 0000000: // no file type specified - figure out the file type using alternative means - return false; - case 0040000: - return NET_SFTP_TYPE_DIRECTORY; - case 0100000: - return NET_SFTP_TYPE_REGULAR; - case 0120000: - return NET_SFTP_TYPE_SYMLINK; - // new types introduced in SFTPv5+ - // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 - case 0010000: // named pipe (fifo) - return NET_SFTP_TYPE_FIFO; - case 0020000: // character special - return NET_SFTP_TYPE_CHAR_DEVICE; - case 0060000: // block special - return NET_SFTP_TYPE_BLOCK_DEVICE; - case 0140000: // socket - return NET_SFTP_TYPE_SOCKET; - case 0160000: // whiteout - // "SPECIAL should be used for files that are of - // a known type which cannot be expressed in the protocol" - return NET_SFTP_TYPE_SPECIAL; - default: - return NET_SFTP_TYPE_UNKNOWN; - } - } - - /** - * Parse Longname - * - * SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open - * a file as a directory and see if an error is returned or you could try to parse the - * SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does. - * The result is returned using the - * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}. - * - * If the longname is in an unrecognized format bool(false) is returned. - * - * @param string $longname - * @return mixed - * @access private - */ - function _parseLongname($longname) - { - // http://en.wikipedia.org/wiki/Unix_file_types - // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions - if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { - switch ($longname[0]) { - case '-': - return NET_SFTP_TYPE_REGULAR; - case 'd': - return NET_SFTP_TYPE_DIRECTORY; - case 'l': - return NET_SFTP_TYPE_SYMLINK; - default: - return NET_SFTP_TYPE_SPECIAL; - } - } - - return false; - } - - /** - * Sends SFTP Packets - * - * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. - * - * @param int $type - * @param string $data - * @see self::_get_sftp_packet() - * @see self::_send_channel_packet() - * @return bool - * @access private - */ - function _send_sftp_packet($type, $data) - { - $packet = $this->request_id !== false ? - pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : - pack('NCa*', strlen($data) + 1, $type, $data); - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $result = $this->_send_channel_packet(self::CHANNEL, $packet); - $stop = strtok(microtime(), ' ') + strtok(''); - - if (defined('NET_SFTP_LOGGING')) { - $packet_type = '-> ' . $this->packet_types[$type] . - ' (' . round($stop - $start, 4) . 's)'; - if (NET_SFTP_LOGGING == self::LOG_REALTIME) { - echo "
\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n
\r\n"; - flush(); - ob_flush(); - } else { - $this->packet_type_log[] = $packet_type; - if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { - $this->packet_log[] = $data; - } - } - } - - return $result; - } - - /** - * Receives SFTP Packets - * - * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. - * - * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. - * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA - * messages containing one SFTP packet. - * - * @see self::_send_sftp_packet() - * @return string - * @access private - */ - function _get_sftp_packet() - { - $this->curTimeout = false; - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - - // SFTP packet length - while (strlen($this->packet_buffer) < 4) { - $temp = $this->_get_channel_packet(self::CHANNEL); - if (is_bool($temp)) { - $this->packet_type = false; - $this->packet_buffer = ''; - return false; - } - $this->packet_buffer.= $temp; - } - extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); - $tempLength = $length; - $tempLength-= strlen($this->packet_buffer); - - // SFTP packet type and data payload - while ($tempLength > 0) { - $temp = $this->_get_channel_packet(self::CHANNEL); - if (is_bool($temp)) { - $this->packet_type = false; - $this->packet_buffer = ''; - return false; - } - $this->packet_buffer.= $temp; - $tempLength-= strlen($temp); - } - - $stop = strtok(microtime(), ' ') + strtok(''); - - $this->packet_type = ord($this->_string_shift($this->packet_buffer)); - - if ($this->request_id !== false) { - $this->_string_shift($this->packet_buffer, 4); // remove the request id - $length-= 5; // account for the request id and the packet type - } else { - $length-= 1; // account for the packet type - } - - $packet = $this->_string_shift($this->packet_buffer, $length); - - if (defined('NET_SFTP_LOGGING')) { - $packet_type = '<- ' . $this->packet_types[$this->packet_type] . - ' (' . round($stop - $start, 4) . 's)'; - if (NET_SFTP_LOGGING == self::LOG_REALTIME) { - echo "
\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n
\r\n"; - flush(); - ob_flush(); - } else { - $this->packet_type_log[] = $packet_type; - if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { - $this->packet_log[] = $packet; - } - } - } - - return $packet; - } - - /** - * Returns a log of the packets that have been sent and received. - * - * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') - * - * @access public - * @return string or Array - */ - function getSFTPLog() - { - if (!defined('NET_SFTP_LOGGING')) { - return false; - } - - switch (NET_SFTP_LOGGING) { - case self::LOG_COMPLEX: - return $this->_format_log($this->packet_log, $this->packet_type_log); - break; - //case self::LOG_SIMPLE: - default: - return $this->packet_type_log; - } - } - - /** - * Returns all errors - * - * @return string - * @access public - */ - function getSFTPErrors() - { - return $this->sftp_errors; - } - - /** - * Returns the last error - * - * @return string - * @access public - */ - function getLastSFTPError() - { - return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; - } - - /** - * Get supported SFTP versions - * - * @return array - * @access public - */ - function getSupportedVersions() - { - $temp = array('version' => $this->version); - if (isset($this->extensions['versions'])) { - $temp['extensions'] = $this->extensions['versions']; - } - return $temp; - } - - /** - * Disconnect - * - * @param int $reason - * @return bool - * @access private - */ - function _disconnect($reason) - { - $this->pwd = false; - parent::_disconnect($reason); - } -} diff --git a/src/phpseclib/Net/SFTP/Stream.php b/src/phpseclib/Net/SFTP/Stream.php deleted file mode 100755 index d19d08b8..00000000 --- a/src/phpseclib/Net/SFTP/Stream.php +++ /dev/null @@ -1,795 +0,0 @@ - - * @copyright 2013 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Net\SFTP; - -use phpseclib\Crypt\RSA; -use phpseclib\Net\SFTP; -use phpseclib\Net\SSH2; - -/** - * SFTP Stream Wrapper - * - * @package SFTP - * @author Jim Wigginton - * @access public - */ -class Stream -{ - /** - * SFTP instances - * - * Rather than re-create the connection we re-use instances if possible - * - * @var array - */ - static $instances; - - /** - * SFTP instance - * - * @var object - * @access private - */ - var $sftp; - - /** - * Path - * - * @var string - * @access private - */ - var $path; - - /** - * Mode - * - * @var string - * @access private - */ - var $mode; - - /** - * Position - * - * @var int - * @access private - */ - var $pos; - - /** - * Size - * - * @var int - * @access private - */ - var $size; - - /** - * Directory entries - * - * @var array - * @access private - */ - var $entries; - - /** - * EOF flag - * - * @var bool - * @access private - */ - var $eof; - - /** - * Context resource - * - * Technically this needs to be publically accessible so PHP can set it directly - * - * @var resource - * @access public - */ - var $context; - - /** - * Notification callback function - * - * @var callable - * @access public - */ - var $notification; - - /** - * Registers this class as a URL wrapper. - * - * @param string $protocol The wrapper name to be registered. - * @return bool True on success, false otherwise. - * @access public - */ - static function register($protocol = 'sftp') - { - if (in_array($protocol, stream_get_wrappers(), true)) { - return false; - } - return stream_wrapper_register($protocol, get_called_class()); - } - - /** - * The Constructor - * - * @access public - */ - function __construct() - { - if (defined('NET_SFTP_STREAM_LOGGING')) { - echo "__construct()\r\n"; - } - } - - /** - * Path Parser - * - * Extract a path from a URI and actually connect to an SSH server if appropriate - * - * If "notification" is set as a context parameter the message code for successful login is - * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. - * - * @param string $path - * @return string - * @access private - */ - function _parse_path($path) - { - $orig = $path; - extract(parse_url($path) + array('port' => 22)); - if (isset($query)) { - $path.= '?' . $query; - } elseif (preg_match('/(\?|\?#)$/', $orig)) { - $path.= '?'; - } - if (isset($fragment)) { - $path.= '#' . $fragment; - } elseif ($orig[strlen($orig) - 1] == '#') { - $path.= '#'; - } - - if (!isset($host)) { - return false; - } - - if (isset($this->context)) { - $context = stream_context_get_params($this->context); - if (isset($context['notification'])) { - $this->notification = $context['notification']; - } - } - - if (preg_match('/^{[a-z0-9]+}$/i', $host)) { - $host = SSH2::getConnectionByResourceId($host); - if ($host === false) { - return false; - } - $this->sftp = $host; - } else { - if (isset($this->context)) { - $context = stream_context_get_options($this->context); - } - if (isset($context[$scheme]['session'])) { - $sftp = $context[$scheme]['session']; - } - if (isset($context[$scheme]['sftp'])) { - $sftp = $context[$scheme]['sftp']; - } - if (isset($sftp) && $sftp instanceof SFTP) { - $this->sftp = $sftp; - return $path; - } - if (isset($context[$scheme]['username'])) { - $user = $context[$scheme]['username']; - } - if (isset($context[$scheme]['password'])) { - $pass = $context[$scheme]['password']; - } - if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof RSA) { - $pass = $context[$scheme]['privkey']; - } - - if (!isset($user) || !isset($pass)) { - return false; - } - - // casting $pass to a string is necessary in the event that it's a \phpseclib\Crypt\RSA object - if (isset(self::$instances[$host][$port][$user][(string) $pass])) { - $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; - } else { - $this->sftp = new SFTP($host, $port); - $this->sftp->disableStatCache(); - if (isset($this->notification) && is_callable($this->notification)) { - /* if !is_callable($this->notification) we could do this: - - user_error('fopen(): failed to call user notifier', E_USER_WARNING); - - the ftp wrapper gives errors like that when the notifier isn't callable. - i've opted not to do that, however, since the ftp wrapper gives the line - on which the fopen occurred as the line number - not the line that the - user_error is on. - */ - call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); - call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); - if (!$this->sftp->login($user, $pass)) { - call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); - return false; - } - call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); - } else { - if (!$this->sftp->login($user, $pass)) { - return false; - } - } - self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; - } - } - - return $path; - } - - /** - * Opens file or URL - * - * @param string $path - * @param string $mode - * @param int $options - * @param string $opened_path - * @return bool - * @access public - */ - function _stream_open($path, $mode, $options, &$opened_path) - { - $path = $this->_parse_path($path); - - if ($path === false) { - return false; - } - $this->path = $path; - - $this->size = $this->sftp->size($path); - $this->mode = preg_replace('#[bt]$#', '', $mode); - $this->eof = false; - - if ($this->size === false) { - if ($this->mode[0] == 'r') { - return false; - } else { - $this->sftp->touch($path); - $this->size = 0; - } - } else { - switch ($this->mode[0]) { - case 'x': - return false; - case 'w': - $this->sftp->truncate($path, 0); - $this->size = 0; - } - } - - $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; - - return true; - } - - /** - * Read from stream - * - * @param int $count - * @return mixed - * @access public - */ - function _stream_read($count) - { - switch ($this->mode) { - case 'w': - case 'a': - case 'x': - case 'c': - return false; - } - - // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite - //if ($this->pos >= $this->size) { - // $this->eof = true; - // return false; - //} - - $result = $this->sftp->get($this->path, false, $this->pos, $count); - if (isset($this->notification) && is_callable($this->notification)) { - if ($result === false) { - call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); - return 0; - } - // seems that PHP calls stream_read in 8k chunks - call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); - } - - if (empty($result)) { // ie. false or empty string - $this->eof = true; - return false; - } - $this->pos+= strlen($result); - - return $result; - } - - /** - * Write to stream - * - * @param string $data - * @return mixed - * @access public - */ - function _stream_write($data) - { - switch ($this->mode) { - case 'r': - return false; - } - - $result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos); - if (isset($this->notification) && is_callable($this->notification)) { - if (!$result) { - call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); - return 0; - } - // seems that PHP splits up strings into 8k blocks before calling stream_write - call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); - } - - if ($result === false) { - return false; - } - $this->pos+= strlen($data); - if ($this->pos > $this->size) { - $this->size = $this->pos; - } - $this->eof = false; - return strlen($data); - } - - /** - * Retrieve the current position of a stream - * - * @return int - * @access public - */ - function _stream_tell() - { - return $this->pos; - } - - /** - * Tests for end-of-file on a file pointer - * - * In my testing there are four classes functions that normally effect the pointer: - * fseek, fputs / fwrite, fgets / fread and ftruncate. - * - * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof() - * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof() - * will return false. do fread($fp, 1) and feof() will then return true. - * - * @return bool - * @access public - */ - function _stream_eof() - { - return $this->eof; - } - - /** - * Seeks to specific location in a stream - * - * @param int $offset - * @param int $whence - * @return bool - * @access public - */ - function _stream_seek($offset, $whence) - { - switch ($whence) { - case SEEK_SET: - if ($offset >= $this->size || $offset < 0) { - return false; - } - break; - case SEEK_CUR: - $offset+= $this->pos; - break; - case SEEK_END: - $offset+= $this->size; - } - - $this->pos = $offset; - $this->eof = false; - return true; - } - - /** - * Change stream options - * - * @param string $path - * @param int $option - * @param mixed $var - * @return bool - * @access public - */ - function _stream_metadata($path, $option, $var) - { - $path = $this->_parse_path($path); - if ($path === false) { - return false; - } - - // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined - // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246 - // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 - switch ($option) { - case 1: // PHP_STREAM_META_TOUCH - return $this->sftp->touch($path, $var[0], $var[1]); - case 2: // PHP_STREAM_OWNER_NAME - case 3: // PHP_STREAM_GROUP_NAME - return false; - case 4: // PHP_STREAM_META_OWNER - return $this->sftp->chown($path, $var); - case 5: // PHP_STREAM_META_GROUP - return $this->sftp->chgrp($path, $var); - case 6: // PHP_STREAM_META_ACCESS - return $this->sftp->chmod($path, $var) !== false; - } - } - - /** - * Retrieve the underlaying resource - * - * @param int $cast_as - * @return resource - * @access public - */ - function _stream_cast($cast_as) - { - return $this->sftp->fsock; - } - - /** - * Advisory file locking - * - * @param int $operation - * @return bool - * @access public - */ - function _stream_lock($operation) - { - return false; - } - - /** - * Renames a file or directory - * - * Attempts to rename oldname to newname, moving it between directories if necessary. - * If newname exists, it will be overwritten. This is a departure from what \phpseclib\Net\SFTP - * does. - * - * @param string $path_from - * @param string $path_to - * @return bool - * @access public - */ - function _rename($path_from, $path_to) - { - $path1 = parse_url($path_from); - $path2 = parse_url($path_to); - unset($path1['path'], $path2['path']); - if ($path1 != $path2) { - return false; - } - - $path_from = $this->_parse_path($path_from); - $path_to = parse_url($path_to); - if ($path_from === false) { - return false; - } - - $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2 - // "It is an error if there already exists a file with the name specified by newpath." - // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5 - if (!$this->sftp->rename($path_from, $path_to)) { - if ($this->sftp->stat($path_to)) { - return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); - } - return false; - } - - return true; - } - - /** - * Open directory handle - * - * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and - * removed in 5.4 I'm just going to ignore it. - * - * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client - * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting - * the SFTP specs: - * - * The SSH_FXP_NAME response has the following format: - * - * uint32 id - * uint32 count - * repeats count times: - * string filename - * string longname - * ATTRS attrs - * - * @param string $path - * @param int $options - * @return bool - * @access public - */ - function _dir_opendir($path, $options) - { - $path = $this->_parse_path($path); - if ($path === false) { - return false; - } - $this->pos = 0; - $this->entries = $this->sftp->nlist($path); - return $this->entries !== false; - } - - /** - * Read entry from directory handle - * - * @return mixed - * @access public - */ - function _dir_readdir() - { - if (isset($this->entries[$this->pos])) { - return $this->entries[$this->pos++]; - } - return false; - } - - /** - * Rewind directory handle - * - * @return bool - * @access public - */ - function _dir_rewinddir() - { - $this->pos = 0; - return true; - } - - /** - * Close directory handle - * - * @return bool - * @access public - */ - function _dir_closedir() - { - return true; - } - - /** - * Create a directory - * - * Only valid $options is STREAM_MKDIR_RECURSIVE - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - * @access public - */ - function _mkdir($path, $mode, $options) - { - $path = $this->_parse_path($path); - if ($path === false) { - return false; - } - - return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); - } - - /** - * Removes a directory - * - * Only valid $options is STREAM_MKDIR_RECURSIVE per , however, - * does not have a $recursive parameter as mkdir() does so I don't know how - * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as - * $options. What does 8 correspond to? - * - * @param string $path - * @param int $mode - * @param int $options - * @return bool - * @access public - */ - function _rmdir($path, $options) - { - $path = $this->_parse_path($path); - if ($path === false) { - return false; - } - - return $this->sftp->rmdir($path); - } - - /** - * Flushes the output - * - * See . Always returns true because \phpseclib\Net\SFTP doesn't cache stuff before writing - * - * @return bool - * @access public - */ - function _stream_flush() - { - return true; - } - - /** - * Retrieve information about a file resource - * - * @return mixed - * @access public - */ - function _stream_stat() - { - $results = $this->sftp->stat($this->path); - if ($results === false) { - return false; - } - return $results; - } - - /** - * Delete a file - * - * @param string $path - * @return bool - * @access public - */ - function _unlink($path) - { - $path = $this->_parse_path($path); - if ($path === false) { - return false; - } - - return $this->sftp->delete($path, false); - } - - /** - * Retrieve information about a file - * - * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib\Net\SFTP\Stream is quiet by default - * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll - * cross that bridge when and if it's reached - * - * @param string $path - * @param int $flags - * @return mixed - * @access public - */ - function _url_stat($path, $flags) - { - $path = $this->_parse_path($path); - if ($path === false) { - return false; - } - - $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); - if ($results === false) { - return false; - } - - return $results; - } - - /** - * Truncate stream - * - * @param int $new_size - * @return bool - * @access public - */ - function _stream_truncate($new_size) - { - if (!$this->sftp->truncate($this->path, $new_size)) { - return false; - } - - $this->eof = false; - $this->size = $new_size; - - return true; - } - - /** - * Change stream options - * - * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't. - * The other two aren't supported because of limitations in \phpseclib\Net\SFTP. - * - * @param int $option - * @param int $arg1 - * @param int $arg2 - * @return bool - * @access public - */ - function _stream_set_option($option, $arg1, $arg2) - { - return false; - } - - /** - * Close an resource - * - * @access public - */ - function _stream_close() - { - } - - /** - * __call Magic Method - * - * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. - * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function - * lets you figure that out. - * - * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not - * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. - * - * @param string - * @param array - * @return mixed - * @access public - */ - function __call($name, $arguments) - { - if (defined('NET_SFTP_STREAM_LOGGING')) { - echo $name . '('; - $last = count($arguments) - 1; - foreach ($arguments as $i => $argument) { - var_export($argument); - if ($i != $last) { - echo ','; - } - } - echo ")\r\n"; - } - $name = '_' . $name; - if (!method_exists($this, $name)) { - return false; - } - return call_user_func_array(array($this, $name), $arguments); - } -} diff --git a/src/phpseclib/Net/SSH1.php b/src/phpseclib/Net/SSH1.php deleted file mode 100755 index 2ed4a002..00000000 --- a/src/phpseclib/Net/SSH1.php +++ /dev/null @@ -1,1607 +0,0 @@ - - * login('username', 'password')) { - * exit('Login Failed'); - * } - * - * echo $ssh->exec('ls -la'); - * ?> - * - * - * Here's another short example: - * - * login('username', 'password')) { - * exit('Login Failed'); - * } - * - * echo $ssh->read('username@username:~$'); - * $ssh->write("ls -la\n"); - * echo $ssh->read('username@username:~$'); - * ?> - * - * - * More information on the SSHv1 specification can be found by reading - * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}. - * - * @category Net - * @package SSH1 - * @author Jim Wigginton - * @copyright 2007 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Net; - -use ParagonIE\ConstantTime\Hex; -use phpseclib\Crypt\DES; -use phpseclib\Crypt\Random; -use phpseclib\Crypt\TripleDES; -use phpseclib\Math\BigInteger; - -/** - * Pure-PHP implementation of SSHv1. - * - * @package SSH1 - * @author Jim Wigginton - * @access public - */ -class SSH1 -{ - /**#@+ - * Encryption Methods - * - * @see \phpseclib\Net\SSH1::getSupportedCiphers() - * @access public - */ - /** - * No encryption - * - * Not supported. - */ - const CIPHER_NONE = 0; - /** - * IDEA in CFB mode - * - * Not supported. - */ - const CIPHER_IDEA = 1; - /** - * DES in CBC mode - */ - const CIPHER_DES = 2; - /** - * Triple-DES in CBC mode - * - * All implementations are required to support this - */ - const CIPHER_3DES = 3; - /** - * TRI's Simple Stream encryption CBC - * - * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h), - * although it doesn't use it (see cipher.c) - */ - const CIPHER_BROKEN_TSS = 4; - /** - * RC4 - * - * Not supported. - * - * @internal According to the SSH1 specs: - * - * "The first 16 bytes of the session key are used as the key for - * the server to client direction. The remaining 16 bytes are used - * as the key for the client to server direction. This gives - * independent 128-bit keys for each direction." - * - * This library currently only supports encryption when the same key is being used for both directions. This is - * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps). - */ - const CIPHER_RC4 = 5; - /** - * Blowfish - * - * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and - * uses it (see cipher.c) - */ - const CIPHER_BLOWFISH = 6; - /**#@-*/ - - /**#@+ - * Authentication Methods - * - * @see \phpseclib\Net\SSH1::getSupportedAuthentications() - * @access public - */ - /** - * .rhosts or /etc/hosts.equiv - */ - const AUTH_RHOSTS = 1; - /** - * pure RSA authentication - */ - const AUTH_RSA = 2; - /** - * password authentication - * - * This is the only method that is supported by this library. - */ - const AUTH_PASSWORD = 3; - /** - * .rhosts with RSA host authentication - */ - const AUTH_RHOSTS_RSA = 4; - /**#@-*/ - - /**#@+ - * Terminal Modes - * - * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html - * @access private - */ - const TTY_OP_END = 0; - /**#@-*/ - - /** - * The Response Type - * - * @see \phpseclib\Net\SSH1::_get_binary_packet() - * @access private - */ - const RESPONSE_TYPE = 1; - - /** - * The Response Data - * - * @see \phpseclib\Net\SSH1::_get_binary_packet() - * @access private - */ - const RESPONSE_DATA = 2; - - /**#@+ - * Execution Bitmap Masks - * - * @see \phpseclib\Net\SSH1::bitmap - * @access private - */ - const MASK_CONSTRUCTOR = 0x00000001; - const MASK_CONNECTED = 0x00000002; - const MASK_LOGIN = 0x00000004; - const MASK_SHELL = 0x00000008; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\Net\SSH1::getLog() - */ - /** - * Returns the message numbers - */ - const LOG_SIMPLE = 1; - /** - * Returns the message content - */ - const LOG_COMPLEX = 2; - /** - * Outputs the content real-time - */ - const LOG_REALTIME = 3; - /** - * Dumps the content real-time to a file - */ - const LOG_REALTIME_FILE = 4; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\Net\SSH1::read() - */ - /** - * Returns when a string matching $expect exactly is found - */ - const READ_SIMPLE = 1; - /** - * Returns when a string matching the regular expression $expect is found - */ - const READ_REGEX = 2; - /**#@-*/ - - /** - * The SSH identifier - * - * @var string - * @access private - */ - var $identifier = 'SSH-1.5-phpseclib'; - - /** - * The Socket Object - * - * @var object - * @access private - */ - var $fsock; - - /** - * The cryptography object - * - * @var object - * @access private - */ - var $crypto = false; - - /** - * Execution Bitmap - * - * The bits that are set represent functions that have been called already. This is used to determine - * if a requisite function has been successfully executed. If not, an error should be thrown. - * - * @var int - * @access private - */ - var $bitmap = 0; - - /** - * The Server Key Public Exponent - * - * Logged for debug purposes - * - * @see self::getServerKeyPublicExponent() - * @var string - * @access private - */ - var $server_key_public_exponent; - - /** - * The Server Key Public Modulus - * - * Logged for debug purposes - * - * @see self::getServerKeyPublicModulus() - * @var string - * @access private - */ - var $server_key_public_modulus; - - /** - * The Host Key Public Exponent - * - * Logged for debug purposes - * - * @see self::getHostKeyPublicExponent() - * @var string - * @access private - */ - var $host_key_public_exponent; - - /** - * The Host Key Public Modulus - * - * Logged for debug purposes - * - * @see self::getHostKeyPublicModulus() - * @var string - * @access private - */ - var $host_key_public_modulus; - - /** - * Supported Ciphers - * - * Logged for debug purposes - * - * @see self::getSupportedCiphers() - * @var array - * @access private - */ - var $supported_ciphers = array( - self::CIPHER_NONE => 'No encryption', - self::CIPHER_IDEA => 'IDEA in CFB mode', - self::CIPHER_DES => 'DES in CBC mode', - self::CIPHER_3DES => 'Triple-DES in CBC mode', - self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC', - self::CIPHER_RC4 => 'RC4', - self::CIPHER_BLOWFISH => 'Blowfish' - ); - - /** - * Supported Authentications - * - * Logged for debug purposes - * - * @see self::getSupportedAuthentications() - * @var array - * @access private - */ - var $supported_authentications = array( - self::AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv', - self::AUTH_RSA => 'pure RSA authentication', - self::AUTH_PASSWORD => 'password authentication', - self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication' - ); - - /** - * Server Identification - * - * @see self::getServerIdentification() - * @var string - * @access private - */ - var $server_identification = ''; - - /** - * Protocol Flags - * - * @see self::__construct() - * @var array - * @access private - */ - var $protocol_flags = array(); - - /** - * Protocol Flag Log - * - * @see self::getLog() - * @var array - * @access private - */ - var $protocol_flag_log = array(); - - /** - * Message Log - * - * @see self::getLog() - * @var array - * @access private - */ - var $message_log = array(); - - /** - * Real-time log file pointer - * - * @see self::_append_log() - * @var resource - * @access private - */ - var $realtime_log_file; - - /** - * Real-time log file size - * - * @see self::_append_log() - * @var int - * @access private - */ - var $realtime_log_size; - - /** - * Real-time log file wrap boolean - * - * @see self::_append_log() - * @var bool - * @access private - */ - var $realtime_log_wrap; - - /** - * Interactive Buffer - * - * @see self::read() - * @var array - * @access private - */ - var $interactiveBuffer = ''; - - /** - * Timeout - * - * @see self::setTimeout() - * @access private - */ - var $timeout; - - /** - * Current Timeout - * - * @see self::_get_channel_packet() - * @access private - */ - var $curTimeout; - - /** - * Log Boundary - * - * @see self::_format_log() - * @access private - */ - var $log_boundary = ':'; - - /** - * Log Long Width - * - * @see self::_format_log() - * @access private - */ - var $log_long_width = 65; - - /** - * Log Short Width - * - * @see self::_format_log() - * @access private - */ - var $log_short_width = 16; - - /** - * Hostname - * - * @see self::__construct() - * @see self::_connect() - * @var string - * @access private - */ - var $host; - - /** - * Port Number - * - * @see self::__construct() - * @see self::_connect() - * @var int - * @access private - */ - var $port; - - /** - * Timeout for initial connection - * - * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like - * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor, - * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be - * 10 seconds. It is used by fsockopen() in that function. - * - * @see self::__construct() - * @see self::_connect() - * @var int - * @access private - */ - var $connectionTimeout; - - /** - * Default cipher - * - * @see self::__construct() - * @see self::_connect() - * @var int - * @access private - */ - var $cipher; - - /** - * Default Constructor. - * - * Connects to an SSHv1 server - * - * @param string $host - * @param int $port - * @param int $timeout - * @param int $cipher - * @return \phpseclib\Net\SSH1 - * @access public - */ - function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES) - { - $this->protocol_flags = array( - 1 => 'NET_SSH1_MSG_DISCONNECT', - 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', - 3 => 'NET_SSH1_CMSG_SESSION_KEY', - 4 => 'NET_SSH1_CMSG_USER', - 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD', - 10 => 'NET_SSH1_CMSG_REQUEST_PTY', - 12 => 'NET_SSH1_CMSG_EXEC_SHELL', - 13 => 'NET_SSH1_CMSG_EXEC_CMD', - 14 => 'NET_SSH1_SMSG_SUCCESS', - 15 => 'NET_SSH1_SMSG_FAILURE', - 16 => 'NET_SSH1_CMSG_STDIN_DATA', - 17 => 'NET_SSH1_SMSG_STDOUT_DATA', - 18 => 'NET_SSH1_SMSG_STDERR_DATA', - 19 => 'NET_SSH1_CMSG_EOF', - 20 => 'NET_SSH1_SMSG_EXITSTATUS', - 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION' - ); - - $this->_define_array($this->protocol_flags); - - $this->host = $host; - $this->port = $port; - $this->connectionTimeout = $timeout; - $this->cipher = $cipher; - } - - /** - * Connect to an SSHv1 server - * - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @access private - */ - function _connect() - { - $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); - if (!$this->fsock) { - throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); - } - - $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)) { - throw new \RuntimeException('Can only connect to SSH servers'); - } - if ($parts[1][0] != 1) { - throw new \RuntimeException("Cannot connect to $parts[1] servers"); - } - - fputs($this->fsock, $this->identifier."\r\n"); - - $response = $this->_get_binary_packet(); - if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { - throw new \UnexpectedValueException('Expected SSH_SMSG_PUBLIC_KEY'); - } - - $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 = md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie, true); - - $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)) { - throw new \RuntimeException('Error sending SSH_CMSG_SESSION_KEY'); - } - - switch ($cipher) { - //case self::CIPHER_NONE: - // $this->crypto = new \phpseclib\Crypt\Null(); - // break; - case self::CIPHER_DES: - $this->crypto = new DES(DES::MODE_CBC); - $this->crypto->disablePadding(); - $this->crypto->enableContinuousBuffer(); - $this->crypto->setKey(substr($session_key, 0, 8)); - // "The iv (initialization vector) is initialized to all zeroes." - $this->crypto->setIV(str_repeat("\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)); - // "All three initialization vectors are initialized to zero." - $this->crypto->setIV(str_repeat("\0", 8)); - 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) { - throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); - } - - $this->bitmap = self::MASK_CONNECTED; - - return true; - } - - /** - * Login - * - * @param string $username - * @param string $password - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @access public - */ - function login($username, $password = '') - { - if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { - $this->bitmap |= self::MASK_CONSTRUCTOR; - if (!$this->_connect()) { - return false; - } - } - - if (!($this->bitmap & self::MASK_CONNECTED)) { - return false; - } - - $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Error sending SSH_CMSG_USER'); - } - - $response = $this->_get_binary_packet(); - - if ($response === true) { - return false; - } - if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { - $this->bitmap |= self::MASK_LOGIN; - return true; - } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { - throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); - } - - $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Error sending SSH_CMSG_AUTH_PASSWORD'); - } - - // remove the username and password from the last logged packet - if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) { - $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password'); - $this->message_log[count($this->message_log) - 1] = $data; - } - - $response = $this->_get_binary_packet(); - - if ($response === true) { - return false; - } - if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { - $this->bitmap |= self::MASK_LOGIN; - return true; - } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { - return false; - } else { - throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); - } - } - - /** - * Set Timeout - * - * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. - * Setting $timeout to false or 0 will mean there is no timeout. - * - * @param mixed $timeout - */ - function setTimeout($timeout) - { - $this->timeout = $this->curTimeout = $timeout; - } - - /** - * Executes a command on a non-interactive shell, returns the output, and quits. - * - * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2 - * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a - * shell with the -s option, as discussed in the following links: - * - * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html} - * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html} - * - * To execute further commands, a new \phpseclib\Net\SSH1 object will need to be created. - * - * Returns false on failure and the output, otherwise. - * - * @see self::interactiveRead() - * @see self::interactiveWrite() - * @param string $cmd - * @return mixed - * @throws \RuntimeException on error sending command - * @access public - */ - function exec($cmd, $block = true) - { - if (!($this->bitmap & self::MASK_LOGIN)) { - throw new \RuntimeException('Operation disallowed prior to login()'); - } - - $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Error sending SSH_CMSG_EXEC_CMD'); - } - - if (!$block) { - return true; - } - - $output = ''; - $response = $this->_get_binary_packet(); - - if ($response !== false) { - do { - $output.= substr($response[self::RESPONSE_DATA], 4); - $response = $this->_get_binary_packet(); - } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS); - } - - $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); - - // i don't think it's really all that important if this packet gets sent or not. - $this->_send_binary_packet($data); - - fclose($this->fsock); - - // reset the execution bitmap - a new \phpseclib\Net\SSH1 object needs to be created. - $this->bitmap = 0; - - return $output; - } - - /** - * Creates an interactive shell - * - * @see self::interactiveRead() - * @see self::interactiveWrite() - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @access private - */ - function _initShell() - { - // connect using the sample parameters in protocol-1.5.txt. - // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text - // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell. - $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Error sending SSH_CMSG_REQUEST_PTY'); - } - - $response = $this->_get_binary_packet(); - - if ($response === true) { - return false; - } - if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { - throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); - } - - $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Error sending SSH_CMSG_EXEC_SHELL'); - } - - $this->bitmap |= self::MASK_SHELL; - - //stream_set_blocking($this->fsock, 0); - - return true; - } - - /** - * Inputs a command into an interactive shell. - * - * @see self::interactiveWrite() - * @param string $cmd - * @return bool - * @access public - */ - function write($cmd) - { - return $this->interactiveWrite($cmd); - } - - /** - * Returns the output of an interactive shell when there's a match for $expect - * - * $expect can take the form of a string literal or, if $mode == self::READ__REGEX, - * a regular expression. - * - * @see self::write() - * @param string $expect - * @param int $mode - * @return bool - * @throws \RuntimeException on connection error - * @access public - */ - function read($expect, $mode = self::READ__SIMPLE) - { - if (!($this->bitmap & self::MASK_LOGIN)) { - throw new \RuntimeException('Operation disallowed prior to login()'); - } - - if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - throw new \RuntimeException('Unable to initiate an interactive shell session'); - } - - $match = $expect; - while (true) { - if ($mode == self::READ__REGEX) { - preg_match($expect, $this->interactiveBuffer, $matches); - $match = isset($matches[0]) ? $matches[0] : ''; - } - $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; - if ($pos !== false) { - return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); - } - $response = $this->_get_binary_packet(); - - if ($response === true) { - return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); - } - $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4); - } - } - - /** - * Inputs a command into an interactive shell. - * - * @see self::interactiveRead() - * @param string $cmd - * @return bool - * @throws \RuntimeException on connection error - * @access public - */ - function interactiveWrite($cmd) - { - if (!($this->bitmap & self::MASK_LOGIN)) { - throw new \RuntimeException('Operation disallowed prior to login()'); - } - - if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - throw new \RuntimeException('Unable to initiate an interactive shell session'); - } - - $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Error sending SSH_CMSG_STDIN'); - } - - return true; - } - - /** - * Returns the output of an interactive shell when no more output is available. - * - * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like - * "^[[00m", you're seeing ANSI escape codes. According to - * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT - * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user, - * there's not going to be much recourse. - * - * @see self::interactiveRead() - * @return string - * @throws \RuntimeException on connection error - * @access public - */ - function interactiveRead() - { - if (!($this->bitmap & self::MASK_LOGIN)) { - throw new \RuntimeException('Operation disallowed prior to login()'); - } - - if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - throw new \RuntimeException('Unable to initiate an interactive shell session'); - } - - $read = array($this->fsock); - $write = $except = null; - if (stream_select($read, $write, $except, 0)) { - $response = $this->_get_binary_packet(); - return substr($response[self::RESPONSE_DATA], 4); - } else { - return ''; - } - } - - /** - * Disconnect - * - * @access public - */ - function disconnect() - { - $this->_disconnect(); - } - - /** - * Destructor. - * - * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call - * disconnect(). - * - * @access public - */ - function __destruct() - { - $this->_disconnect(); - } - - /** - * Disconnect - * - * @param string $msg - * @access private - */ - function _disconnect($msg = 'Client Quit') - { - if ($this->bitmap) { - $data = pack('C', NET_SSH1_CMSG_EOF); - $this->_send_binary_packet($data); - /* - $response = $this->_get_binary_packet(); - if ($response === true) { - $response = array(self::RESPONSE_TYPE => -1); - } - switch ($response[self::RESPONSE_TYPE]) { - case NET_SSH1_SMSG_EXITSTATUS: - $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); - break; - default: - $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); - } - */ - $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); - - $this->_send_binary_packet($data); - fclose($this->fsock); - $this->bitmap = 0; - } - } - - /** - * Gets Binary Packets - * - * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info. - * - * Also, this function could be improved upon by adding detection for the following exploit: - * http://www.securiteam.com/securitynews/5LP042K3FY.html - * - * @see self::_send_binary_packet() - * @return array - * @access private - */ - function _get_binary_packet() - { - if (feof($this->fsock)) { - //user_error('connection closed prematurely'); - return false; - } - - if ($this->curTimeout) { - $read = array($this->fsock); - $write = $except = null; - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - // on windows this returns a "Warning: Invalid CRT parameters detected" error - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - //$this->_disconnect('Timeout'); - return true; - } - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; - $this->curTimeout-= $elapsed; - } - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $temp = unpack('Nlength', fread($this->fsock, 4)); - - $padding_length = 8 - ($temp['length'] & 7); - $length = $temp['length'] + $padding_length; - $raw = ''; - - while ($length > 0) { - $temp = fread($this->fsock, $length); - $raw.= $temp; - $length-= strlen($temp); - } - $stop = strtok(microtime(), ' ') + strtok(''); - - if (strlen($raw) && $this->crypto !== false) { - $raw = $this->crypto->decrypt($raw); - } - - $padding = substr($raw, 0, $padding_length); - $type = $raw[$padding_length]; - $data = substr($raw, $padding_length + 1, -4); - - $temp = unpack('Ncrc', substr($raw, -4)); - - //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { - // user_error('Bad CRC in packet from server'); - // return false; - //} - - $type = ord($type); - - if (defined('NET_SSH1_LOGGING')) { - $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN'; - $temp = '<- ' . $temp . - ' (' . round($stop - $start, 4) . 's)'; - $this->_append_log($temp, $data); - } - - return array( - self::RESPONSE_TYPE => $type, - self::RESPONSE_DATA => $data - ); - } - - /** - * Sends Binary Packets - * - * Returns true on success, false on failure. - * - * @see self::_get_binary_packet() - * @param string $data - * @return bool - * @access private - */ - function _send_binary_packet($data) - { - if (feof($this->fsock)) { - //user_error('connection closed prematurely'); - return false; - } - - $length = strlen($data) + 4; - - $padding = Random::string(8 - ($length & 7)); - - $orig = $data; - $data = $padding . $data; - $data.= pack('N', $this->_crc($data)); - - if ($this->crypto !== false) { - $data = $this->crypto->encrypt($data); - } - - $packet = pack('Na*', $length, $data); - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $result = strlen($packet) == fputs($this->fsock, $packet); - $stop = strtok(microtime(), ' ') + strtok(''); - - if (defined('NET_SSH1_LOGGING')) { - $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN'; - $temp = '-> ' . $temp . - ' (' . round($stop - $start, 4) . 's)'; - $this->_append_log($temp, $orig); - } - - return $result; - } - - /** - * Cyclic Redundancy Check (CRC) - * - * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so - * we've reimplemented it. A more detailed discussion of the differences can be found after - * $crc_lookup_table's initialization. - * - * @see self::_get_binary_packet() - * @see self::_send_binary_packet() - * @param string $data - * @return int - * @access private - */ - function _crc($data) - { - static $crc_lookup_table = array( - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, - 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, - 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, - 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, - 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, - 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, - 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, - 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, - 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, - 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, - 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, - 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, - 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, - 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, - 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, - 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, - 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, - 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, - 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, - 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, - 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, - 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, - 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, - 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, - 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, - 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, - 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, - 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, - 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, - 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, - 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, - 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, - 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D - ); - - // For this function to yield the same output as PHP's crc32 function, $crc would have to be - // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is. - $crc = 0x00000000; - $length = strlen($data); - - for ($i=0; $i<$length; $i++) { - // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all - // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example, - // yields 0xFF800000 - not 0x00800000. The following link elaborates: - // http://www.php.net/manual/en/language.operators.bitwise.php#57281 - $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])]; - } - - // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with - // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would. - return $crc; - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; - } - - /** - * RSA Encrypt - * - * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e - * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that - * calls this call modexp, instead, but I think this makes things clearer, maybe... - * - * @see self::__construct() - * @param BigInteger $m - * @param array $key - * @return BigInteger - * @access private - */ - function _rsa_crypt($m, $key) - { - /* - $rsa = new RSA(); - $rsa->load($key, 'raw'); - $rsa->setHash('sha1'); - return $rsa->encrypt($m, RSA::PADDING_PKCS1); - */ - - // To quote from protocol-1.5.txt: - // The most significant byte (which is only partial as the value must be - // less than the public modulus, which is never a power of two) is zero. - // - // The next byte contains the value 2 (which stands for public-key - // encrypted data in the PKCS standard [PKCS#1]). Then, there are non- - // zero random bytes to fill any unused space, a zero byte, and the data - // to be encrypted in the least significant bytes, the last byte of the - // data in the least significant byte. - - // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation", - // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL: - // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf - $modulus = $key[1]->toBytes(); - $length = strlen($modulus) - strlen($m) - 3; - $random = ''; - while (strlen($random) != $length) { - $block = Random::string($length - strlen($random)); - $block = str_replace("\x00", '', $block); - $random.= $block; - } - $temp = chr(0) . chr(2) . $random . chr(0) . $m; - - $m = new BigInteger($temp, 256); - $m = $m->modPow($key[0], $key[1]); - - return $m->toBytes(); - } - - /** - * Define Array - * - * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of - * named constants from it, using the value as the name of the constant and the index as the value of the constant. - * If any of the constants that would be defined already exists, none of the constants will be defined. - * - * @param array $array - * @access private - */ - function _define_array() - { - $args = func_get_args(); - foreach ($args as $arg) { - foreach ($arg as $key => $value) { - if (!defined($value)) { - define($value, $key); - } else { - break 2; - } - } - } - } - - /** - * Returns a log of the packets that have been sent and received. - * - * Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING') - * - * @access public - * @return array|false|string - */ - function getLog() - { - if (!defined('NET_SSH1_LOGGING')) { - return false; - } - - switch (NET_SSH1_LOGGING) { - case self::LOG_SIMPLE: - return $this->message_number_log; - break; - case self::LOG_COMPLEX: - return $this->_format_log($this->message_log, $this->protocol_flags_log); - break; - default: - return false; - } - } - - /** - * Formats a log for printing - * - * @param array $message_log - * @param array $message_number_log - * @access private - * @return string - */ - function _format_log($message_log, $message_number_log) - { - $output = ''; - for ($i = 0; $i < count($message_log); $i++) { - $output.= $message_number_log[$i] . "\r\n"; - $current_log = $message_log[$i]; - $j = 0; - do { - if (strlen($current_log)) { - $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; - } - $fragment = $this->_string_shift($current_log, $this->log_short_width); - $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); - // replace non ASCII printable characters with dots - // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters - // also replace < with a . since < messes up the output on web browsers - $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); - $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; - $j++; - } while (strlen($current_log)); - $output.= "\r\n"; - } - - return $output; - } - - /** - * Helper function for _format_log - * - * For use with preg_replace_callback() - * - * @param array $matches - * @access private - * @return string - */ - function _format_log_helper($matches) - { - return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); - } - - /** - * Return the server key public exponent - * - * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, - * the raw bytes. This behavior is similar to PHP's md5() function. - * - * @param bool $raw_output - * @return string - * @access public - */ - function getServerKeyPublicExponent($raw_output = false) - { - return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString(); - } - - /** - * Return the server key public modulus - * - * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, - * the raw bytes. This behavior is similar to PHP's md5() function. - * - * @param bool $raw_output - * @return string - * @access public - */ - function getServerKeyPublicModulus($raw_output = false) - { - return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString(); - } - - /** - * Return the host key public exponent - * - * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, - * the raw bytes. This behavior is similar to PHP's md5() function. - * - * @param bool $raw_output - * @return string - * @access public - */ - function getHostKeyPublicExponent($raw_output = false) - { - return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString(); - } - - /** - * Return the host key public modulus - * - * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead, - * the raw bytes. This behavior is similar to PHP's md5() function. - * - * @param bool $raw_output - * @return string - * @access public - */ - function getHostKeyPublicModulus($raw_output = false) - { - return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString(); - } - - /** - * Return a list of ciphers supported by SSH1 server. - * - * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output - * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll - * get array(self::CIPHER_3DES). - * - * @param bool $raw_output - * @return array - * @access public - */ - function getSupportedCiphers($raw_output = false) - { - return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers); - } - - /** - * Return a list of authentications supported by SSH1 server. - * - * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output - * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll - * get array(self::AUTH_PASSWORD). - * - * @param bool $raw_output - * @return array - * @access public - */ - function getSupportedAuthentications($raw_output = false) - { - return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications); - } - - /** - * Return the server identification. - * - * @return string - * @access public - */ - function getServerIdentification() - { - return rtrim($this->server_identification); - } - - /** - * Logs data packets - * - * Makes sure that only the last 1MB worth of packets will be logged - * - * @param string $data - * @access private - */ - function _append_log($protocol_flags, $message) - { - switch (NET_SSH1_LOGGING) { - // useful for benchmarks - case self::LOG_SIMPLE: - $this->protocol_flags_log[] = $protocol_flags; - break; - // the most useful log for SSH1 - case self::LOG_COMPLEX: - $this->protocol_flags_log[] = $protocol_flags; - $this->_string_shift($message); - $this->log_size+= strlen($message); - $this->message_log[] = $message; - while ($this->log_size > self::LOG_MAX_SIZE) { - $this->log_size-= strlen(array_shift($this->message_log)); - array_shift($this->protocol_flags_log); - } - break; - // dump the output out realtime; packets may be interspersed with non packets, - // passwords won't be filtered out and select other packets may not be correctly - // identified - case self::LOG_REALTIME: - echo "
\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n
\r\n"; - @flush(); - @ob_flush(); - break; - // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE - // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. - // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily - // at the beginning of the file - case self::LOG_REALTIME_FILE: - if (!isset($this->realtime_log_file)) { - // PHP doesn't seem to like using constants in fopen() - $filename = self::LOG_REALTIME_FILE; - $fp = fopen($filename, 'w'); - $this->realtime_log_file = $fp; - } - if (!is_resource($this->realtime_log_file)) { - break; - } - $entry = $this->_format_log(array($message), array($protocol_flags)); - if ($this->realtime_log_wrap) { - $temp = "<<< START >>>\r\n"; - $entry.= $temp; - fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); - } - $this->realtime_log_size+= strlen($entry); - if ($this->realtime_log_size > self::LOG_MAX_SIZE) { - fseek($this->realtime_log_file, 0); - $this->realtime_log_size = strlen($entry); - $this->realtime_log_wrap = true; - } - fputs($this->realtime_log_file, $entry); - } - } -} diff --git a/src/phpseclib/Net/SSH2.php b/src/phpseclib/Net/SSH2.php deleted file mode 100755 index 97a37a0b..00000000 --- a/src/phpseclib/Net/SSH2.php +++ /dev/null @@ -1,4224 +0,0 @@ - - * login('username', 'password')) { - * exit('Login Failed'); - * } - * - * echo $ssh->exec('pwd'); - * echo $ssh->exec('ls -la'); - * ?> - * - * - * - * setPassword('whatever'); - * $key->load(file_get_contents('privatekey')); - * - * $ssh = new \phpseclib\Net\SSH2('www.domain.tld'); - * if (!$ssh->login('username', $key)) { - * exit('Login Failed'); - * } - * - * echo $ssh->read('username@username:~$'); - * $ssh->write("ls -la\n"); - * echo $ssh->read('username@username:~$'); - * ?> - * - * - * @category Net - * @package SSH2 - * @author Jim Wigginton - * @copyright 2007 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - */ - -namespace phpseclib\Net; - -use ParagonIE\ConstantTime\Base64; -use phpseclib\Crypt\Base; -use phpseclib\Crypt\Blowfish; -use phpseclib\Crypt\Hash; -use phpseclib\Crypt\Random; -use phpseclib\Crypt\RC4; -use phpseclib\Crypt\Rijndael; -use phpseclib\Crypt\RSA; -use phpseclib\Crypt\TripleDES; -use phpseclib\Crypt\Twofish; -use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. -use phpseclib\System\SSH\Agent; -use phpseclib\Exception\NoSupportedAlgorithmsException; - -/** - * Pure-PHP implementation of SSHv2. - * - * @package SSH2 - * @author Jim Wigginton - * @access public - */ -class SSH2 -{ - /**#@+ - * Execution Bitmap Masks - * - * @see \phpseclib\Net\SSH2::bitmap - * @access private - */ - const MASK_CONSTRUCTOR = 0x00000001; - const MASK_CONNECTED = 0x00000002; - const MASK_LOGIN_REQ = 0x00000004; - const MASK_LOGIN = 0x00000008; - const MASK_SHELL = 0x00000010; - const MASK_WINDOW_ADJUST = 0x00000020; - /**#@-*/ - - /**#@+ - * Channel constants - * - * RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer - * to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with - * a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a - * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel - * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet: - * The 'recipient channel' is the channel number given in the original - * open request, and 'sender channel' is the channel number allocated by - * the other side. - * - * @see \phpseclib\Net\SSH2::_send_channel_packet() - * @see \phpseclib\Net\SSH2::_get_channel_packet() - * @access private - */ - const CHANNEL_EXEC = 0; // PuTTy uses 0x100 - const CHANNEL_SHELL = 1; - const CHANNEL_SUBSYSTEM = 2; - const CHANNEL_AGENT_FORWARD = 3; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\Net\SSH2::getLog() - */ - /** - * Returns the message numbers - */ - const LOG_SIMPLE = 1; - /** - * Returns the message content - */ - const LOG_COMPLEX = 2; - /** - * Outputs the content real-time - */ - const LOG_REALTIME = 3; - /** - * Dumps the content real-time to a file - */ - const LOG_REALTIME_FILE = 4; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\Net\SSH2::read() - */ - /** - * Returns when a string matching $expect exactly is found - */ - const READ_SIMPLE = 1; - /** - * Returns when a string matching the regular expression $expect is found - */ - const READ_REGEX = 2; - /** - * Make sure that the log never gets larger than this - */ - const LOG_MAX_SIZE = 1048576; // 1024 * 1024 - /**#@-*/ - - /** - * The SSH identifier - * - * @var string - * @access private - */ - var $identifier; - - /** - * The Socket Object - * - * @var object - * @access private - */ - var $fsock; - - /** - * Execution Bitmap - * - * The bits that are set represent functions that have been called already. This is used to determine - * if a requisite function has been successfully executed. If not, an error should be thrown. - * - * @var int - * @access private - */ - var $bitmap = 0; - - /** - * Error information - * - * @see self::getErrors() - * @see self::getLastError() - * @var string - * @access private - */ - var $errors = array(); - - /** - * Server Identifier - * - * @see self::getServerIdentification() - * @var array|false - * @access private - */ - var $server_identifier = false; - - /** - * Key Exchange Algorithms - * - * @see self::getKexAlgorithims() - * @var array|false - * @access private - */ - var $kex_algorithms = false; - - /** - * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods - * - * @see self::_key_exchange() - * @var int - * @access private - */ - var $kex_dh_group_size_min = 1536; - - /** - * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods - * - * @see self::_key_exchange() - * @var int - * @access private - */ - var $kex_dh_group_size_preferred = 2048; - - /** - * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods - * - * @see self::_key_exchange() - * @var int - * @access private - */ - var $kex_dh_group_size_max = 4096; - - /** - * Server Host Key Algorithms - * - * @see self::getServerHostKeyAlgorithms() - * @var array|false - * @access private - */ - var $server_host_key_algorithms = false; - - /** - * Encryption Algorithms: Client to Server - * - * @see self::getEncryptionAlgorithmsClient2Server() - * @var array|false - * @access private - */ - var $encryption_algorithms_client_to_server = false; - - /** - * Encryption Algorithms: Server to Client - * - * @see self::getEncryptionAlgorithmsServer2Client() - * @var array|false - * @access private - */ - var $encryption_algorithms_server_to_client = false; - - /** - * MAC Algorithms: Client to Server - * - * @see self::getMACAlgorithmsClient2Server() - * @var array|false - * @access private - */ - var $mac_algorithms_client_to_server = false; - - /** - * MAC Algorithms: Server to Client - * - * @see self::getMACAlgorithmsServer2Client() - * @var array|false - * @access private - */ - var $mac_algorithms_server_to_client = false; - - /** - * Compression Algorithms: Client to Server - * - * @see self::getCompressionAlgorithmsClient2Server() - * @var array|false - * @access private - */ - var $compression_algorithms_client_to_server = false; - - /** - * Compression Algorithms: Server to Client - * - * @see self::getCompressionAlgorithmsServer2Client() - * @var array|false - * @access private - */ - var $compression_algorithms_server_to_client = false; - - /** - * Languages: Server to Client - * - * @see self::getLanguagesServer2Client() - * @var array|false - * @access private - */ - var $languages_server_to_client = false; - - /** - * Languages: Client to Server - * - * @see self::getLanguagesClient2Server() - * @var array|false - * @access private - */ - var $languages_client_to_server = false; - - /** - * Block Size for Server to Client Encryption - * - * "Note that the length of the concatenation of 'packet_length', - * 'padding_length', 'payload', and 'random padding' MUST be a multiple - * of the cipher block size or 8, whichever is larger. This constraint - * MUST be enforced, even when using stream ciphers." - * - * -- http://tools.ietf.org/html/rfc4253#section-6 - * - * @see self::__construct() - * @see self::_send_binary_packet() - * @var int - * @access private - */ - var $encrypt_block_size = 8; - - /** - * Block Size for Client to Server Encryption - * - * @see self::__construct() - * @see self::_get_binary_packet() - * @var int - * @access private - */ - var $decrypt_block_size = 8; - - /** - * Server to Client Encryption Object - * - * @see self::_get_binary_packet() - * @var object - * @access private - */ - var $decrypt = false; - - /** - * Client to Server Encryption Object - * - * @see self::_send_binary_packet() - * @var object - * @access private - */ - var $encrypt = false; - - /** - * Client to Server HMAC Object - * - * @see self::_send_binary_packet() - * @var object - * @access private - */ - var $hmac_create = false; - - /** - * Server to Client HMAC Object - * - * @see self::_get_binary_packet() - * @var object - * @access private - */ - var $hmac_check = false; - - /** - * Size of server to client HMAC - * - * We need to know how big the HMAC will be for the server to client direction so that we know how many bytes to read. - * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is - * append it. - * - * @see self::_get_binary_packet() - * @var int - * @access private - */ - var $hmac_size = false; - - /** - * Server Public Host Key - * - * @see self::getServerPublicHostKey() - * @var string - * @access private - */ - var $server_public_host_key; - - /** - * Session identifer - * - * "The exchange hash H from the first key exchange is additionally - * used as the session identifier, which is a unique identifier for - * this connection." - * - * -- http://tools.ietf.org/html/rfc4253#section-7.2 - * - * @see self::_key_exchange() - * @var string - * @access private - */ - var $session_id = false; - - /** - * Exchange hash - * - * The current exchange hash - * - * @see self::_key_exchange() - * @var string - * @access private - */ - var $exchange_hash = false; - - /** - * Message Numbers - * - * @see self::__construct() - * @var array - * @access private - */ - var $message_numbers = array(); - - /** - * Disconnection Message 'reason codes' defined in RFC4253 - * - * @see self::__construct() - * @var array - * @access private - */ - var $disconnect_reasons = array(); - - /** - * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 - * - * @see self::__construct() - * @var array - * @access private - */ - var $channel_open_failure_reasons = array(); - - /** - * Terminal Modes - * - * @link http://tools.ietf.org/html/rfc4254#section-8 - * @see self::__construct() - * @var array - * @access private - */ - var $terminal_modes = array(); - - /** - * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes - * - * @link http://tools.ietf.org/html/rfc4254#section-5.2 - * @see self::__construct() - * @var array - * @access private - */ - var $channel_extended_data_type_codes = array(); - - /** - * Send Sequence Number - * - * See 'Section 6.4. Data Integrity' of rfc4253 for more info. - * - * @see self::_send_binary_packet() - * @var int - * @access private - */ - var $send_seq_no = 0; - - /** - * Get Sequence Number - * - * See 'Section 6.4. Data Integrity' of rfc4253 for more info. - * - * @see self::_get_binary_packet() - * @var int - * @access private - */ - var $get_seq_no = 0; - - /** - * Server Channels - * - * Maps client channels to server channels - * - * @see self::_get_channel_packet() - * @see self::exec() - * @var array - * @access private - */ - var $server_channels = array(); - - /** - * Channel Buffers - * - * If a client requests a packet from one channel but receives two packets from another those packets should - * be placed in a buffer - * - * @see self::_get_channel_packet() - * @see self::exec() - * @var array - * @access private - */ - var $channel_buffers = array(); - - /** - * Channel Status - * - * Contains the type of the last sent message - * - * @see self::_get_channel_packet() - * @var array - * @access private - */ - var $channel_status = array(); - - /** - * Packet Size - * - * Maximum packet size indexed by channel - * - * @see self::_send_channel_packet() - * @var array - * @access private - */ - var $packet_size_client_to_server = array(); - - /** - * Message Number Log - * - * @see self::getLog() - * @var array - * @access private - */ - var $message_number_log = array(); - - /** - * Message Log - * - * @see self::getLog() - * @var array - * @access private - */ - var $message_log = array(); - - /** - * The Window Size - * - * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) - * - * @var int - * @see self::_send_channel_packet() - * @see self::exec() - * @access private - */ - var $window_size = 0x7FFFFFFF; - - /** - * Window size, server to client - * - * Window size indexed by channel - * - * @see self::_send_channel_packet() - * @var array - * @access private - */ - var $window_size_server_to_client = array(); - - /** - * Window size, client to server - * - * Window size indexed by channel - * - * @see self::_get_channel_packet() - * @var array - * @access private - */ - var $window_size_client_to_server = array(); - - /** - * Server signature - * - * Verified against $this->session_id - * - * @see self::getServerPublicHostKey() - * @var string - * @access private - */ - var $signature = ''; - - /** - * Server signature format - * - * ssh-rsa or ssh-dss. - * - * @see self::getServerPublicHostKey() - * @var string - * @access private - */ - var $signature_format = ''; - - /** - * Interactive Buffer - * - * @see self::read() - * @var array - * @access private - */ - var $interactiveBuffer = ''; - - /** - * Current log size - * - * Should never exceed self::LOG_MAX_SIZE - * - * @see self::_send_binary_packet() - * @see self::_get_binary_packet() - * @var int - * @access private - */ - var $log_size; - - /** - * Timeout - * - * @see self::setTimeout() - * @access private - */ - var $timeout; - - /** - * Current Timeout - * - * @see self::_get_channel_packet() - * @access private - */ - var $curTimeout; - - /** - * Real-time log file pointer - * - * @see self::_append_log() - * @var resource - * @access private - */ - var $realtime_log_file; - - /** - * Real-time log file size - * - * @see self::_append_log() - * @var int - * @access private - */ - var $realtime_log_size; - - /** - * Has the signature been validated? - * - * @see self::getServerPublicHostKey() - * @var bool - * @access private - */ - var $signature_validated = false; - - /** - * Real-time log file wrap boolean - * - * @see self::_append_log() - * @access private - */ - var $realtime_log_wrap; - - /** - * Flag to suppress stderr from output - * - * @see self::enableQuietMode() - * @access private - */ - var $quiet_mode = false; - - /** - * Time of first network activity - * - * @var int - * @access private - */ - var $last_packet; - - /** - * Exit status returned from ssh if any - * - * @var int - * @access private - */ - var $exit_status; - - /** - * Flag to request a PTY when using exec() - * - * @var bool - * @see self::enablePTY() - * @access private - */ - var $request_pty = false; - - /** - * Flag set while exec() is running when using enablePTY() - * - * @var bool - * @access private - */ - var $in_request_pty_exec = false; - - /** - * Flag set after startSubsystem() is called - * - * @var bool - * @access private - */ - var $in_subsystem; - - /** - * Contents of stdError - * - * @var string - * @access private - */ - var $stdErrorLog; - - /** - * The Last Interactive Response - * - * @see self::_keyboard_interactive_process() - * @var string - * @access private - */ - var $last_interactive_response = ''; - - /** - * Keyboard Interactive Request / Responses - * - * @see self::_keyboard_interactive_process() - * @var array - * @access private - */ - var $keyboard_requests_responses = array(); - - /** - * Banner Message - * - * Quoting from the RFC, "in some jurisdictions, sending a warning message before - * authentication may be relevant for getting legal protection." - * - * @see self::_filter() - * @see self::getBannerMessage() - * @var string - * @access private - */ - var $banner_message = ''; - - /** - * Did read() timeout or return normally? - * - * @see self::isTimeout() - * @var bool - * @access private - */ - var $is_timeout = false; - - /** - * Log Boundary - * - * @see self::_format_log() - * @var string - * @access private - */ - var $log_boundary = ':'; - - /** - * Log Long Width - * - * @see self::_format_log() - * @var int - * @access private - */ - var $log_long_width = 65; - - /** - * Log Short Width - * - * @see self::_format_log() - * @var int - * @access private - */ - var $log_short_width = 16; - - /** - * Hostname - * - * @see self::__construct() - * @see self::_connect() - * @var string - * @access private - */ - var $host; - - /** - * Port Number - * - * @see self::__construct() - * @see self::_connect() - * @var int - * @access private - */ - var $port; - - /** - * Number of columns for terminal window size - * - * @see self::getWindowColumns() - * @see self::setWindowColumns() - * @see self::setWindowSize() - * @var int - * @access private - */ - var $windowColumns = 80; - - /** - * Number of columns for terminal window size - * - * @see self::getWindowRows() - * @see self::setWindowRows() - * @see self::setWindowSize() - * @var int - * @access private - */ - var $windowRows = 24; - - /** - * Crypto Engine - * - * @see self::setCryptoEngine() - * @see self::_key_exchange() - * @var int - * @access private - */ - var $crypto_engine = false; - - /** - * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario - * - * @var System_SSH_Agent - * @access private - */ - var $agent; - - /** - * Connection storage to replicates ssh2 extension functionality: - * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} - * - * @var SSH2[] - */ - static $connections; - - /** - * Default Constructor. - * - * $host can either be a string, representing the host, or a stream resource. - * - * @param mixed $host - * @param int $port - * @param int $timeout - * @see self::login() - * @return \phpseclib\Net\SSH2 - * @access public - */ - function __construct($host, $port = 22, $timeout = 10) - { - $this->message_numbers = array( - 1 => 'NET_SSH2_MSG_DISCONNECT', - 2 => 'NET_SSH2_MSG_IGNORE', - 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', - 4 => 'NET_SSH2_MSG_DEBUG', - 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', - 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', - 20 => 'NET_SSH2_MSG_KEXINIT', - 21 => 'NET_SSH2_MSG_NEWKEYS', - 30 => 'NET_SSH2_MSG_KEXDH_INIT', - 31 => 'NET_SSH2_MSG_KEXDH_REPLY', - 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', - 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', - 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', - 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', - - 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', - 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', - 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', - 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', - 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', - 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', - 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', - 94 => 'NET_SSH2_MSG_CHANNEL_DATA', - 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', - 96 => 'NET_SSH2_MSG_CHANNEL_EOF', - 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', - 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', - 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', - 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' - ); - $this->disconnect_reasons = array( - 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', - 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', - 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', - 4 => 'NET_SSH2_DISCONNECT_RESERVED', - 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', - 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', - 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', - 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', - 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', - 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', - 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', - 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', - 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', - 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', - 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' - ); - $this->channel_open_failure_reasons = array( - 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' - ); - $this->terminal_modes = array( - 0 => 'NET_SSH2_TTY_OP_END' - ); - $this->channel_extended_data_type_codes = array( - 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' - ); - - $this->_define_array( - $this->message_numbers, - $this->disconnect_reasons, - $this->channel_open_failure_reasons, - $this->terminal_modes, - $this->channel_extended_data_type_codes, - array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'), - array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'), - array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', - 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'), - // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} - array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', - 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', - 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', - 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', - 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'), - // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) - array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', - 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') - ); - - self::$connections[$this->getResourceId()] = $this; - - if (is_resource($host)) { - $this->fsock = $host; - return; - } - - if (is_string($host)) { - $this->host = $host; - $this->port = $port; - $this->timeout = $timeout; - } - } - - /** - * Set Crypto Engine Mode - * - * Possible $engine values: - * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT - * - * @param int $engine - * @access private - */ - function setCryptoEngine($engine) - { - $this->crypto_engine = $engine; - } - - /** - * Connect to an SSHv2 server - * - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @access private - */ - function _connect() - { - if ($this->bitmap & self::MASK_CONSTRUCTOR) { - return false; - } - - $this->bitmap |= self::MASK_CONSTRUCTOR; - - $this->curTimeout = $this->timeout; - - $this->last_packet = microtime(true); - - if (!is_resource($this->fsock)) { - $start = microtime(true); - $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout); - if (!$this->fsock) { - $host = $this->host . ':' . $this->port; - throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); - } - $elapsed = microtime(true) - $start; - - $this->curTimeout-= $elapsed; - - if ($this->curTimeout <= 0) { - $this->is_timeout = true; - return false; - } - } - - /* According to the SSH2 specs, - - "The server MAY send other lines of data before sending the version - string. Each line SHOULD be terminated by a Carriage Return and Line - Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded - in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients - MUST be able to process such lines." */ - $data = ''; - while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) { - $line = ''; - while (true) { - if ($this->curTimeout) { - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return false; - } - $read = array($this->fsock); - $write = $except = null; - $start = microtime(true); - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - // on windows this returns a "Warning: Invalid CRT parameters detected" error - // the !count() is done as a workaround for - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->is_timeout = true; - return false; - } - $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; - } - - $temp = stream_get_line($this->fsock, 255, "\n"); - if (strlen($temp) == 255) { - continue; - } - - $line.= "$temp\n"; - if (substr($line, -2) == "\r\n") { - break; - } - } - $data.= $line; - } - - if (feof($this->fsock)) { - throw new \RuntimeException('Connection closed by server'); - } - - $extra = $matches[1]; - - $this->identifier = $this->_generate_identifier(); - - if (defined('NET_SSH2_LOGGING')) { - $this->_append_log('<-', $matches[0]); - $this->_append_log('->', $this->identifier . "\r\n"); - } - - $this->server_identifier = trim($temp, "\r\n"); - if (strlen($extra)) { - $this->errors[] = utf8_decode($data); - } - - if ($matches[3] != '1.99' && $matches[3] != '2.0') { - throw new \RuntimeException("Cannot connect to SSH $matches[1] servers"); - } - - fputs($this->fsock, $this->identifier . "\r\n"); - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { - throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); - } - - if (!$this->_key_exchange($response)) { - return false; - } - - $this->bitmap|= self::MASK_CONNECTED; - - return true; - } - - /** - * Generates the SSH identifier - * - * You should overwrite this method in your own class if you want to use another identifier - * - * @access protected - * @return string - */ - function _generate_identifier() - { - $identifier = 'SSH-2.0-phpseclib_2.0'; - - $ext = array(); - if (extension_loaded('libsodium')) { - $ext[] = 'libsodium'; - } - - if (extension_loaded('openssl')) { - $ext[] = 'openssl'; - } elseif (extension_loaded('mcrypt')) { - $ext[] = 'mcrypt'; - } - - if (extension_loaded('gmp')) { - $ext[] = 'gmp'; - } elseif (extension_loaded('bcmath')) { - $ext[] = 'bcmath'; - } - - if (!empty($ext)) { - $identifier .= ' (' . implode(', ', $ext) . ')'; - } - - return $identifier; - } - - /** - * Key Exchange - * - * @param string $kexinit_payload_server - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible - * @access private - */ - function _key_exchange($kexinit_payload_server) - { - $kex_algorithms = array( - // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using - // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the - // libssh repository for more information. - 'curve25519-sha256@libssh.org', - - // Diffie-Hellman Key Agreement (DH) using integer modulo prime - // groups. - 'diffie-hellman-group1-sha1', // REQUIRED - 'diffie-hellman-group14-sha1', // REQUIRED - 'diffie-hellman-group-exchange-sha1', // RFC 4419 - 'diffie-hellman-group-exchange-sha256', // RFC 4419 - ); - if (!function_exists('\\Sodium\\library_version_major')) { - $kex_algorithms = array_diff( - $kex_algorithms, - array('curve25519-sha256@libssh.org') - ); - } - - $server_host_key_algorithms = array( - 'ssh-rsa', // RECOMMENDED sign Raw RSA Key - 'ssh-dss' // REQUIRED sign Raw DSS Key - ); - - $encryption_algorithms = array( - // from : - 'arcfour256', - 'arcfour128', - - //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key - - // CTR modes from : - 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key - 'aes192-ctr', // RECOMMENDED AES with 192-bit key - 'aes256-ctr', // RECOMMENDED AES with 256-bit key - - 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key - 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key - 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key - - 'aes128-cbc', // RECOMMENDED AES with a 128-bit key - 'aes192-cbc', // OPTIONAL AES with a 192-bit key - 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key - - 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key - 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key - 'twofish256-cbc', - 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" - // (this is being retained for historical reasons) - - 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode - - 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode - - '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode - - '3des-cbc', // REQUIRED three-key 3DES in CBC mode - //'none' // OPTIONAL no encryption; NOT RECOMMENDED - ); - - if (extension_loaded('openssl') && !extension_loaded('mcrypt')) { - // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to - // instances that do not use continuous buffers - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('arcfour256', 'arcfour128', 'arcfour') - ); - } - - if (class_exists('\phpseclib\Crypt\RC4') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('arcfour256', 'arcfour128', 'arcfour') - ); - } - if (class_exists('\phpseclib\Crypt\Rijndael') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc') - ); - } - if (class_exists('\phpseclib\Crypt\Twofish') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc') - ); - } - if (class_exists('\phpseclib\Crypt\Blowfish') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('blowfish-ctr', 'blowfish-cbc') - ); - } - if (class_exists('\phpseclib\Crypt\TripleDES') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('3des-ctr', '3des-cbc') - ); - } - $encryption_algorithms = array_values($encryption_algorithms); - - $mac_algorithms = array( - // from : - 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32) - - 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) - 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) - 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) - 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) - //'none' // OPTIONAL no MAC; NOT RECOMMENDED - ); - - $compression_algorithms = array( - 'none' // REQUIRED no compression - //'zlib' // OPTIONAL ZLIB (LZ77) compression - ); - - // some SSH servers have buggy implementations of some of the above algorithms - switch ($this->server_identifier) { - case 'SSH-2.0-SSHD': - $mac_algorithms = array_values(array_diff( - $mac_algorithms, - array('hmac-sha1-96', 'hmac-md5-96') - )); - } - - $str_kex_algorithms = implode(',', $kex_algorithms); - $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms); - $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms); - $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms); - $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms); - - $client_cookie = Random::string(16); - - $response = $kexinit_payload_server; - $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) - $server_cookie = $this->_string_shift($response, 16); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); - $first_kex_packet_follows = $first_kex_packet_follows != 0; - - // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. - $kexinit_payload_client = pack( - 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', - NET_SSH2_MSG_KEXINIT, - $client_cookie, - strlen($str_kex_algorithms), - $str_kex_algorithms, - strlen($str_server_host_key_algorithms), - $str_server_host_key_algorithms, - strlen($encryption_algorithms_client_to_server), - $encryption_algorithms_client_to_server, - strlen($encryption_algorithms_server_to_client), - $encryption_algorithms_server_to_client, - strlen($mac_algorithms_client_to_server), - $mac_algorithms_client_to_server, - strlen($mac_algorithms_server_to_client), - $mac_algorithms_server_to_client, - strlen($compression_algorithms_client_to_server), - $compression_algorithms_client_to_server, - strlen($compression_algorithms_server_to_client), - $compression_algorithms_server_to_client, - 0, - '', - 0, - '', - 0, - 0 - ); - - if (!$this->_send_binary_packet($kexinit_payload_client)) { - return false; - } - // here ends the second place. - - // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange - - // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the - // diffie-hellman key exchange as fast as possible - $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client); - $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); - if ($decryptKeyLength === null) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); - } - - $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server); - $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); - if ($encryptKeyLength === null) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); - } - - // through diffie-hellman key exchange a symmetric key is obtained - $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); - if ($kex_algorithm === false) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); - } - - // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. - $exchange_hash_rfc4419 = ''; - - if ($kex_algorithm === 'curve25519-sha256@libssh.org') { - $x = Random::string(32); - $eBytes = \Sodium\crypto_box_publickey_from_secretkey($x); - $clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT; - $serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY; - $kexHash = new Hash('sha256'); - } else { - if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) { - $dh_group_sizes_packed = pack( - 'NNN', - $this->kex_dh_group_size_min, - $this->kex_dh_group_size_preferred, - $this->kex_dh_group_size_max - ); - $packet = pack( - 'Ca*', - NET_SSH2_MSG_KEXDH_GEX_REQUEST, - $dh_group_sizes_packed - ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; - } - extract(unpack('Ctype', $this->_string_shift($response, 1))); - if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) { - user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP'); - return false; - } - - extract(unpack('NprimeLength', $this->_string_shift($response, 4))); - $primeBytes = $this->_string_shift($response, $primeLength); - $prime = new BigInteger($primeBytes, -256); - - extract(unpack('NgLength', $this->_string_shift($response, 4))); - $gBytes = $this->_string_shift($response, $gLength); - $g = new BigInteger($gBytes, -256); - - $exchange_hash_rfc4419 = pack( - 'a*Na*Na*', - $dh_group_sizes_packed, - $primeLength, - $primeBytes, - $gLength, - $gBytes - ); - - $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT; - $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY; - } else { - switch ($kex_algorithm) { - // see http://tools.ietf.org/html/rfc2409#section-6.2 and - // http://tools.ietf.org/html/rfc2412, appendex E - case 'diffie-hellman-group1-sha1': - $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . - '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . - '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . - 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; - break; - // see http://tools.ietf.org/html/rfc3526#section-3 - case 'diffie-hellman-group14-sha1': - $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . - '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . - '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . - 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . - '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . - '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . - 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . - '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; - break; - } - // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1 - // the generator field element is 2 (decimal) and the hash function is sha1. - $g = new BigInteger(2); - $prime = new BigInteger($prime, 16); - $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT; - $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY; - } - - switch ($kex_algorithm) { - case 'diffie-hellman-group-exchange-sha256': - $kexHash = new Hash('sha256'); - break; - default: - $kexHash = new Hash('sha1'); - } - - /* To increase the speed of the key exchange, both client and server may - reduce the size of their private exponents. It should be at least - twice as long as the key material that is generated from the shared - secret. For more details, see the paper by van Oorschot and Wiener - [VAN-OORSCHOT]. - - -- http://tools.ietf.org/html/rfc4419#section-6.2 */ - $one = new BigInteger(1); - $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength)); - $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength - $max = $max->subtract($one); - - $x = BigInteger::random($one, $max); - $e = $g->modPow($x, $prime); - - $eBytes = $e->toBytes(true); - } - $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes); - - if (!$this->_send_binary_packet($data)) { - throw new \RuntimeException('Connection closed by server'); - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - if ($type != $serverKexReplyMessage) { - throw new \UnexpectedValueException('Expected SSH_MSG_KEXDH_REPLY'); - } - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); - - $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); - $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $fBytes = $this->_string_shift($response, $temp['length']); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->signature = $this->_string_shift($response, $temp['length']); - - $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); - $this->signature_format = $this->_string_shift($this->signature, $temp['length']); - - if ($kex_algorithm === 'curve25519-sha256@libssh.org') { - if (strlen($fBytes) !== 32) { - user_error('Received curve25519 public key of invalid length.'); - return false; - } - $key = new BigInteger(\Sodium\crypto_scalarmult($x, $fBytes), 256); - \Sodium\memzero($x); - } else { - $f = new BigInteger($fBytes, -256); - $key = $f->modPow($x, $prime); - } - $keyBytes = $key->toBytes(true); - - $this->exchange_hash = pack( - 'Na*Na*Na*Na*Na*a*Na*Na*Na*', - strlen($this->identifier), - $this->identifier, - strlen($this->server_identifier), - $this->server_identifier, - strlen($kexinit_payload_client), - $kexinit_payload_client, - strlen($kexinit_payload_server), - $kexinit_payload_server, - strlen($this->server_public_host_key), - $this->server_public_host_key, - $exchange_hash_rfc4419, - strlen($eBytes), - $eBytes, - strlen($fBytes), - $fBytes, - strlen($keyBytes), - $keyBytes - ); - - $this->exchange_hash = $kexHash->hash($this->exchange_hash); - - if ($this->session_id === false) { - $this->session_id = $this->exchange_hash; - } - - $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); - if ($server_host_key_algorithm === false) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); - } - - if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new \RuntimeException('Server Host Key Algorithm Mismatch'); - } - - $packet = pack( - 'C', - NET_SSH2_MSG_NEWKEYS - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - if ($type != NET_SSH2_MSG_NEWKEYS) { - throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); - } - - $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); - - $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); - if ($this->encrypt) { - if ($this->crypto_engine) { - $this->encrypt->setEngine($this->crypto_engine); - } - if ($this->encrypt->block_size) { - $this->encrypt_block_size = $this->encrypt->block_size; - } - $this->encrypt->enableContinuousBuffer(); - $this->encrypt->disablePadding(); - - if ($this->encrypt->usesIV()) { - $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); - while ($this->encrypt_block_size > strlen($iv)) { - $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); - } - $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); - } - - $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); - while ($encryptKeyLength > strlen($key)) { - $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); - } - $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); - } - - $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); - if ($this->decrypt) { - if ($this->crypto_engine) { - $this->decrypt->setEngine($this->crypto_engine); - } - if ($this->decrypt->block_size) { - $this->decrypt_block_size = $this->decrypt->block_size; - } - $this->decrypt->enableContinuousBuffer(); - $this->decrypt->disablePadding(); - - if ($this->decrypt->usesIV()) { - $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); - while ($this->decrypt_block_size > strlen($iv)) { - $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); - } - $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); - } - - $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); - while ($decryptKeyLength > strlen($key)) { - $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); - } - $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); - } - - /* The "arcfour128" algorithm is the RC4 cipher, as described in - [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream - generated by the cipher MUST be discarded, and the first byte of the - first encrypted packet MUST be encrypted using the 1537th byte of - keystream. - - -- http://tools.ietf.org/html/rfc4345#section-4 */ - if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { - $this->encrypt->encrypt(str_repeat("\0", 1536)); - } - if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { - $this->decrypt->decrypt(str_repeat("\0", 1536)); - } - - $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server); - if ($mac_algorithm === false) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); - } - - $createKeyLength = 0; // ie. $mac_algorithm == 'none' - switch ($mac_algorithm) { - case 'hmac-sha2-256': - $this->hmac_create = new Hash('sha256'); - $createKeyLength = 32; - break; - case 'hmac-sha1': - $this->hmac_create = new Hash('sha1'); - $createKeyLength = 20; - break; - case 'hmac-sha1-96': - $this->hmac_create = new Hash('sha1-96'); - $createKeyLength = 20; - break; - case 'hmac-md5': - $this->hmac_create = new Hash('md5'); - $createKeyLength = 16; - break; - case 'hmac-md5-96': - $this->hmac_create = new Hash('md5-96'); - $createKeyLength = 16; - } - - $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client); - if ($mac_algorithm === false) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); - } - - $checkKeyLength = 0; - $this->hmac_size = 0; - switch ($mac_algorithm) { - case 'hmac-sha2-256': - $this->hmac_check = new Hash('sha256'); - $checkKeyLength = 32; - $this->hmac_size = 32; - break; - case 'hmac-sha1': - $this->hmac_check = new Hash('sha1'); - $checkKeyLength = 20; - $this->hmac_size = 20; - break; - case 'hmac-sha1-96': - $this->hmac_check = new Hash('sha1-96'); - $checkKeyLength = 20; - $this->hmac_size = 12; - break; - case 'hmac-md5': - $this->hmac_check = new Hash('md5'); - $checkKeyLength = 16; - $this->hmac_size = 16; - break; - case 'hmac-md5-96': - $this->hmac_check = new Hash('md5-96'); - $checkKeyLength = 16; - $this->hmac_size = 12; - } - - $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); - while ($createKeyLength > strlen($key)) { - $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); - } - $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); - - $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); - while ($checkKeyLength > strlen($key)) { - $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); - } - $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); - - $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client); - if ($compression_algorithm === false) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); - } - $this->decompress = $compression_algorithm == 'zlib'; - - $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server); - if ($compression_algorithm === false) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); - } - $this->compress = $compression_algorithm == 'zlib'; - - return true; - } - - /** - * Maps an encryption algorithm name to the number of key bytes. - * - * @param string $algorithm Name of the encryption algorithm - * @return int|null Number of bytes as an integer or null for unknown - * @access private - */ - function _encryption_algorithm_to_key_size($algorithm) - { - switch ($algorithm) { - case 'none': - return 0; - case 'aes128-cbc': - case 'aes128-ctr': - case 'arcfour': - case 'arcfour128': - case 'blowfish-cbc': - case 'blowfish-ctr': - case 'twofish128-cbc': - case 'twofish128-ctr': - return 16; - case '3des-cbc': - case '3des-ctr': - case 'aes192-cbc': - case 'aes192-ctr': - case 'twofish192-cbc': - case 'twofish192-ctr': - return 24; - case 'aes256-cbc': - case 'aes256-ctr': - case 'arcfour256': - case 'twofish-cbc': - case 'twofish256-cbc': - case 'twofish256-ctr': - return 32; - } - return null; - } - - /** - * Maps an encryption algorithm name to an instance of a subclass of - * \phpseclib\Crypt\Base. - * - * @param string $algorithm Name of the encryption algorithm - * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown - * @access private - */ - function _encryption_algorithm_to_crypt_instance($algorithm) - { - switch ($algorithm) { - case '3des-cbc': - return new TripleDES(Base::MODE_CBC); - case '3des-ctr': - return new TripleDES(Base::MODE_CTR); - case 'aes256-cbc': - case 'aes192-cbc': - case 'aes128-cbc': - return new Rijndael(Base::MODE_CBC); - case 'aes256-ctr': - case 'aes192-ctr': - case 'aes128-ctr': - return new Rijndael(Base::MODE_CTR); - case 'blowfish-cbc': - return new Blowfish(Base::MODE_CBC); - case 'blowfish-ctr': - return new Blowfish(Base::MODE_CTR); - case 'twofish128-cbc': - case 'twofish192-cbc': - case 'twofish256-cbc': - case 'twofish-cbc': - return new Twofish(Base::MODE_CBC); - case 'twofish128-ctr': - case 'twofish192-ctr': - case 'twofish256-ctr': - return new Twofish(Base::MODE_CTR); - case 'arcfour': - case 'arcfour128': - case 'arcfour256': - return new RC4(); - } - return null; - } - - /** - * Login - * - * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array - * - * @param string $username - * @param mixed $password - * @param mixed $... - * @return bool - * @see self::_login() - * @access public - */ - function login($username) - { - $args = func_get_args(); - return call_user_func_array(array(&$this, '_login'), $args); - } - - /** - * Login Helper - * - * @param string $username - * @param mixed $password - * @param mixed $... - * @return bool - * @see self::_login_helper() - * @access private - */ - function _login($username) - { - if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { - if (!$this->_connect()) { - return false; - } - } - - $args = array_slice(func_get_args(), 1); - if (empty($args)) { - return $this->_login_helper($username); - } - - foreach ($args as $arg) { - if ($this->_login_helper($username, $arg)) { - return true; - } - } - return false; - } - - /** - * Login Helper - * - * @param string $username - * @param string $password - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @access private - * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} - * by sending dummy SSH_MSG_IGNORE messages. - */ - function _login_helper($username, $password = null) - { - if (!($this->bitmap & self::MASK_CONNECTED)) { - return false; - } - - if (!($this->bitmap & self::MASK_LOGIN_REQ)) { - $packet = pack( - 'CNa*', - NET_SSH2_MSG_SERVICE_REQUEST, - strlen('ssh-userauth'), - 'ssh-userauth' - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { - throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); - } - $this->bitmap |= self::MASK_LOGIN_REQ; - } - - if (strlen($this->last_interactive_response)) { - return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); - } - - if ($password instanceof RSA) { - return $this->_privatekey_login($username, $password); - } elseif ($password instanceof Agent) { - return $this->_ssh_agent_login($username, $password); - } - - if (is_array($password)) { - if ($this->_keyboard_interactive_login($username, $password)) { - $this->bitmap |= self::MASK_LOGIN; - return true; - } - return false; - } - - if (!isset($password)) { - $packet = pack( - 'CNa*Na*Na*', - NET_SSH2_MSG_USERAUTH_REQUEST, - strlen($username), - $username, - strlen('ssh-connection'), - 'ssh-connection', - strlen('none'), - 'none' - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - switch ($type) { - case NET_SSH2_MSG_USERAUTH_SUCCESS: - $this->bitmap |= self::MASK_LOGIN; - return true; - //case NET_SSH2_MSG_USERAUTH_FAILURE: - default: - return false; - } - } - - $packet = pack( - 'CNa*Na*Na*CNa*', - NET_SSH2_MSG_USERAUTH_REQUEST, - strlen($username), - $username, - strlen('ssh-connection'), - 'ssh-connection', - strlen('password'), - 'password', - 0, - strlen($password), - $password - ); - - // remove the username and password from the logged packet - if (!defined('NET_SSH2_LOGGING')) { - $logged = null; - } else { - $logged = pack( - 'CNa*Na*Na*CNa*', - NET_SSH2_MSG_USERAUTH_REQUEST, - strlen('username'), - 'username', - strlen('ssh-connection'), - 'ssh-connection', - strlen('password'), - 'password', - 0, - strlen('password'), - 'password' - ); - } - - if (!$this->_send_binary_packet($packet, $logged)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - switch ($type) { - case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed - if (defined('NET_SSH2_LOGGING')) { - $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'; - } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); - return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); - case NET_SSH2_MSG_USERAUTH_FAILURE: - // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees - // multi-factor authentication - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $auth_methods = explode(',', $this->_string_shift($response, $length)); - extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); - $partial_success = $partial_success != 0; - - if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { - if ($this->_keyboard_interactive_login($username, $password)) { - $this->bitmap |= self::MASK_LOGIN; - return true; - } - return false; - } - return false; - case NET_SSH2_MSG_USERAUTH_SUCCESS: - $this->bitmap |= self::MASK_LOGIN; - return true; - } - - return false; - } - - /** - * Login via keyboard-interactive authentication - * - * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. - * - * @param string $username - * @param string $password - * @return bool - * @access private - */ - function _keyboard_interactive_login($username, $password) - { - $packet = pack( - 'CNa*Na*Na*Na*Na*', - NET_SSH2_MSG_USERAUTH_REQUEST, - strlen($username), - $username, - strlen('ssh-connection'), - 'ssh-connection', - strlen('keyboard-interactive'), - 'keyboard-interactive', - 0, - '', - 0, - '' - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - return $this->_keyboard_interactive_process($password); - } - - /** - * Handle the keyboard-interactive requests / responses. - * - * @param string $responses... - * @return bool - * @throws \RuntimeException on connection error - * @access private - */ - function _keyboard_interactive_process() - { - $responses = func_get_args(); - - if (strlen($this->last_interactive_response)) { - $response = $this->last_interactive_response; - } else { - $orig = $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - switch ($type) { - case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->_string_shift($response, $length); // name; may be empty - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->_string_shift($response, $length); // instruction; may be empty - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->_string_shift($response, $length); // language tag; may be empty - extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); - - for ($i = 0; $i < count($responses); $i++) { - if (is_array($responses[$i])) { - foreach ($responses[$i] as $key => $value) { - $this->keyboard_requests_responses[$key] = $value; - } - unset($responses[$i]); - } - } - $responses = array_values($responses); - - if (isset($this->keyboard_requests_responses)) { - for ($i = 0; $i < $num_prompts; $i++) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - // prompt - ie. "Password: "; must not be empty - $prompt = $this->_string_shift($response, $length); - //$echo = $this->_string_shift($response) != chr(0); - foreach ($this->keyboard_requests_responses as $key => $value) { - if (substr($prompt, 0, strlen($key)) == $key) { - $responses[] = $value; - break; - } - } - } - } - - // see http://tools.ietf.org/html/rfc4256#section-3.2 - if (strlen($this->last_interactive_response)) { - $this->last_interactive_response = ''; - } elseif (defined('NET_SSH2_LOGGING')) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', - $this->message_number_log[count($this->message_number_log) - 1] - ); - } - - if (!count($responses) && $num_prompts) { - $this->last_interactive_response = $orig; - return false; - } - - /* - After obtaining the requested information from the user, the client - MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. - */ - // see http://tools.ietf.org/html/rfc4256#section-3.4 - $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); - for ($i = 0; $i < count($responses); $i++) { - $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]); - $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); - } - - if (!$this->_send_binary_packet($packet, $logged)) { - return false; - } - - if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE', - $this->message_number_log[count($this->message_number_log) - 1] - ); - } - - /* - After receiving the response, the server MUST send either an - SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another - SSH_MSG_USERAUTH_INFO_REQUEST message. - */ - // maybe phpseclib should force close the connection after x request / responses? unless something like that is done - // there could be an infinite loop of request / responses. - return $this->_keyboard_interactive_process(); - case NET_SSH2_MSG_USERAUTH_SUCCESS: - return true; - case NET_SSH2_MSG_USERAUTH_FAILURE: - return false; - } - - return false; - } - - /** - * Login with an ssh-agent provided key - * - * @param string $username - * @param \phpseclib\System\SSH\Agent $agent - * @return bool - * @access private - */ - function _ssh_agent_login($username, $agent) - { - $this->agent = $agent; - $keys = $agent->requestIdentities(); - foreach ($keys as $key) { - if ($this->_privatekey_login($username, $key)) { - return true; - } - } - - return false; - } - - /** - * Login with an RSA private key - * - * @param string $username - * @param \phpseclib\Crypt\RSA $password - * @return bool - * @throws \RuntimeException on connection error - * @access private - * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} - * by sending dummy SSH_MSG_IGNORE messages. - */ - function _privatekey_login($username, $privatekey) - { - // see http://tools.ietf.org/html/rfc4253#page-15 - $publickey = $privatekey->getPublicKey('Raw'); - if ($publickey === false) { - return false; - } - - $publickey = array( - 'e' => $publickey['e']->toBytes(true), - 'n' => $publickey['n']->toBytes(true) - ); - $publickey = pack( - 'Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($publickey['e']), - $publickey['e'], - strlen($publickey['n']), - $publickey['n'] - ); - - $part1 = pack( - 'CNa*Na*Na*', - NET_SSH2_MSG_USERAUTH_REQUEST, - strlen($username), - $username, - strlen('ssh-connection'), - 'ssh-connection', - strlen('publickey'), - 'publickey' - ); - $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); - - $packet = $part1 . chr(0) . $part2; - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - switch ($type) { - case NET_SSH2_MSG_USERAUTH_FAILURE: - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); - return false; - case NET_SSH2_MSG_USERAUTH_PK_OK: - // we'll just take it on faith that the public key blob and the public key algorithm name are as - // they should be - if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_PK_OK', - $this->message_number_log[count($this->message_number_log) - 1] - ); - } - } - - $packet = $part1 . chr(1) . $part2; - $privatekey->setHash('sha1'); - $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet), RSA::PADDING_PKCS1); - $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature); - $packet.= pack('Na*', strlen($signature), $signature); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - switch ($type) { - case NET_SSH2_MSG_USERAUTH_FAILURE: - // either the login is bad or the server employs multi-factor authentication - return false; - case NET_SSH2_MSG_USERAUTH_SUCCESS: - $this->bitmap |= self::MASK_LOGIN; - return true; - } - - return false; - } - - /** - * Set Timeout - * - * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. - * Setting $timeout to false or 0 will mean there is no timeout. - * - * @param mixed $timeout - * @access public - */ - function setTimeout($timeout) - { - $this->timeout = $this->curTimeout = $timeout; - } - - /** - * Get the output from stdError - * - * @access public - */ - function getStdError() - { - return $this->stdErrorLog; - } - - /** - * Execute Command - * - * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually. - * In all likelihood, this is not a feature you want to be taking advantage of. - * - * @param string $command - * @param Callback $callback - * @return string - * @throws \RuntimeException on connection error - * @access public - */ - function exec($command, $callback = null) - { - $this->curTimeout = $this->timeout; - $this->is_timeout = false; - $this->stdErrorLog = ''; - - if (!($this->bitmap & self::MASK_LOGIN)) { - return false; - } - - // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to - // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, - // honestly, if you're transfering more than 2GB, you probably shouldn't be using phpseclib, anyway. - // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info - $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; - // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy - // uses 0x4000, that's what will be used here, as well. - $packet_size = 0x4000; - - $packet = pack( - 'CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, - strlen('session'), - 'session', - self::CHANNEL_EXEC, - $this->window_size_server_to_client[self::CHANNEL_EXEC], - $packet_size - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; - - $response = $this->_get_channel_packet(self::CHANNEL_EXEC); - if ($response === false) { - return false; - } - - if ($this->request_pty === true) { - $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); - $packet = pack( - 'CNNa*CNa*N5a*', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL_EXEC], - strlen('pty-req'), - 'pty-req', - 1, - strlen('vt100'), - 'vt100', - $this->windowColumns, - $this->windowRows, - 0, - 0, - strlen($terminal_modes), - $terminal_modes - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - list(, $type) = unpack('C', $this->_string_shift($response, 1)); - - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - break; - case NET_SSH2_MSG_CHANNEL_FAILURE: - default: - $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - throw new \RuntimeException('Unable to request pseudo-terminal'); - } - $this->in_request_pty_exec = true; - } - - // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things - // down. the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &'). - // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then - // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but - // neither will your script. - - // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by - // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the - // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. - $packet = pack( - 'CNNa*CNa*', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL_EXEC], - strlen('exec'), - 'exec', - 1, - strlen($command), - $command - ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; - - $response = $this->_get_channel_packet(self::CHANNEL_EXEC); - if ($response === false) { - return false; - } - - $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; - - if ($callback === false || $this->in_request_pty_exec) { - return true; - } - - $output = ''; - while (true) { - $temp = $this->_get_channel_packet(self::CHANNEL_EXEC); - switch (true) { - case $temp === true: - return is_callable($callback) ? true : $output; - case $temp === false: - return false; - default: - if (is_callable($callback)) { - if (call_user_func($callback, $temp) === true) { - $this->_close_channel(self::CHANNEL_EXEC); - return true; - } - } else { - $output.= $temp; - } - } - } - } - - /** - * Creates an interactive shell - * - * @see self::read() - * @see self::write() - * @return bool - * @throws \UnexpectedValueException on receipt of unexpected packets - * @throws \RuntimeException on other errors - * @access private - */ - function _initShell() - { - if ($this->in_request_pty_exec === true) { - return true; - } - - $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; - $packet_size = 0x4000; - - $packet = pack( - 'CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, - strlen('session'), - 'session', - self::CHANNEL_SHELL, - $this->window_size_server_to_client[self::CHANNEL_SHELL], - $packet_size - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; - - $response = $this->_get_channel_packet(self::CHANNEL_SHELL); - if ($response === false) { - return false; - } - - $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); - $packet = pack( - 'CNNa*CNa*N5a*', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL_SHELL], - strlen('pty-req'), - 'pty-req', - 1, - strlen('vt100'), - 'vt100', - $this->windowColumns, - $this->windowRows, - 0, - 0, - strlen($terminal_modes), - $terminal_modes - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - - list(, $type) = unpack('C', $this->_string_shift($response, 1)); - - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - // if a pty can't be opened maybe commands can still be executed - case NET_SSH2_MSG_CHANNEL_FAILURE: - break; - default: - $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - throw new \UnexpectedValueException('Unable to request pseudo-terminal'); - } - - $packet = pack( - 'CNNa*C', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL_SHELL], - strlen('shell'), - 'shell', - 1 - ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; - - $response = $this->_get_channel_packet(self::CHANNEL_SHELL); - if ($response === false) { - return false; - } - - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; - - $this->bitmap |= self::MASK_SHELL; - - return true; - } - - /** - * Return the channel to be used with read() / write() - * - * @see self::read() - * @see self::write() - * @return int - * @access public - */ - function _get_interactive_channel() - { - switch (true) { - case $this->in_subsystem: - return self::CHANNEL_SUBSYSTEM; - case $this->in_request_pty_exec: - return self::CHANNEL_EXEC; - default: - return self::CHANNEL_SHELL; - } - } - - /** - * Return an available open channel - * - * @return int - * @access public - */ - function _get_open_channel() - { - $channel = self::CHANNEL_EXEC; - do { - if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { - return $channel; - } - } while ($channel++ < self::CHANNEL_SUBSYSTEM); - - return false; - } - - /** - * Returns the output of an interactive shell - * - * Returns when there's a match for $expect, which can take the form of a string literal or, - * if $mode == self::READ_REGEX, a regular expression. - * - * @see self::write() - * @param string $expect - * @param int $mode - * @return string - * @throws \RuntimeException on connection error - * @access public - */ - function read($expect = '', $mode = self::READ_SIMPLE) - { - $this->curTimeout = $this->timeout; - $this->is_timeout = false; - - if (!($this->bitmap & self::MASK_LOGIN)) { - throw new \RuntimeException('Operation disallowed prior to login()'); - } - - if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - throw new \RuntimeException('Unable to initiate an interactive shell session'); - } - - $channel = $this->_get_interactive_channel(); - - $match = $expect; - while (true) { - if ($mode == self::READ_REGEX) { - preg_match($expect, substr($this->interactiveBuffer, -1024), $matches); - $match = isset($matches[0]) ? $matches[0] : ''; - } - $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; - if ($pos !== false) { - return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); - } - $response = $this->_get_channel_packet($channel); - if (is_bool($response)) { - $this->in_request_pty_exec = false; - return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; - } - - $this->interactiveBuffer.= $response; - } - } - - /** - * Inputs a command into an interactive shell. - * - * @see self::read() - * @param string $cmd - * @return bool - * @throws \RuntimeException on connection error - * @access public - */ - function write($cmd) - { - if (!($this->bitmap & self::MASK_LOGIN)) { - throw new \RuntimeException('Operation disallowed prior to login()'); - } - - if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - throw new \RuntimeException('Unable to initiate an interactive shell session'); - } - - return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); - } - - /** - * Start a subsystem. - * - * Right now only one subsystem at a time is supported. To support multiple subsystem's stopSubsystem() could accept - * a string that contained the name of the subsystem, but at that point, only one subsystem of each type could be opened. - * To support multiple subsystem's of the same name maybe it'd be best if startSubsystem() generated a new channel id and - * returns that and then that that was passed into stopSubsystem() but that'll be saved for a future date and implemented - * if there's sufficient demand for such a feature. - * - * @see self::stopSubsystem() - * @param string $subsystem - * @return bool - * @access public - */ - function startSubsystem($subsystem) - { - $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; - - $packet = pack( - 'CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, - strlen('session'), - 'session', - self::CHANNEL_SUBSYSTEM, - $this->window_size, - 0x4000 - ); - - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; - - $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); - if ($response === false) { - return false; - } - - $packet = pack( - 'CNNa*CNa*', - NET_SSH2_MSG_CHANNEL_REQUEST, - $this->server_channels[self::CHANNEL_SUBSYSTEM], - strlen('subsystem'), - 'subsystem', - 1, - strlen($subsystem), - $subsystem - ); - if (!$this->_send_binary_packet($packet)) { - return false; - } - - $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; - - $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); - - if ($response === false) { - return false; - } - - $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; - - $this->bitmap |= self::MASK_SHELL; - $this->in_subsystem = true; - - return true; - } - - /** - * Stops a subsystem. - * - * @see self::startSubsystem() - * @return bool - * @access public - */ - function stopSubsystem() - { - $this->in_subsystem = false; - $this->_close_channel(self::CHANNEL_SUBSYSTEM); - return true; - } - - /** - * Closes a channel - * - * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call - * - * @access public - */ - function reset() - { - $this->_close_channel($this->_get_interactive_channel()); - } - - /** - * Is timeout? - * - * Did exec() or read() return because they timed out or because they encountered the end? - * - * @access public - */ - function isTimeout() - { - return $this->is_timeout; - } - - /** - * Disconnect - * - * @access public - */ - function disconnect() - { - $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { - fclose($this->realtime_log_file); - } - unset(self::$connections[$this->getResourceId()]); - } - - /** - * Destructor. - * - * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call - * disconnect(). - * - * @access public - */ - function __destruct() - { - $this->disconnect(); - } - - /** - * Is the connection still active? - * - * @return bool - * @access public - */ - function isConnected() - { - return (bool) ($this->bitmap & self::MASK_CONNECTED); - } - - /** - * Have you successfully been logged in? - * - * @return bool - * @access public - */ - function isAuthenticated() - { - return (bool) ($this->bitmap & self::MASK_LOGIN); - } - - /** - * Gets Binary Packets - * - * See '6. Binary Packet Protocol' of rfc4253 for more info. - * - * @see self::_send_binary_packet() - * @return string - * @throws \RuntimeException on connection errors - * @access private - */ - function _get_binary_packet() - { - if (!is_resource($this->fsock) || feof($this->fsock)) { - $this->bitmap = 0; - throw new \RuntimeException('Connection closed prematurely'); - } - - $start = microtime(true); - $raw = stream_get_contents($this->fsock, $this->decrypt_block_size); - - if (!strlen($raw)) { - return ''; - } - - if ($this->decrypt !== false) { - $raw = $this->decrypt->decrypt($raw); - } - if ($raw === false) { - throw new \RuntimeException('Unable to decrypt content'); - } - - extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); - - $remaining_length = $packet_length + 4 - $this->decrypt_block_size; - - // quoting , - // "implementations SHOULD check that the packet length is reasonable" - // PuTTY uses 0x9000 as the actual max packet size and so to shall we - if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { - throw new \RuntimeException('Invalid size'); - } - - $buffer = ''; - while ($remaining_length > 0) { - $temp = stream_get_contents($this->fsock, $remaining_length); - if ($temp === false || feof($this->fsock)) { - $this->bitmap = 0; - throw new \RuntimeException('Error reading from socket'); - } - $buffer.= $temp; - $remaining_length-= strlen($temp); - } - $stop = microtime(true); - if (strlen($buffer)) { - $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; - } - - $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1); - $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty - - if ($this->hmac_check !== false) { - $hmac = stream_get_contents($this->fsock, $this->hmac_size); - if ($hmac === false || strlen($hmac) != $this->hmac_size) { - $this->bitmap = 0; - throw new \RuntimeException('Error reading socket'); - } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { - throw new \RuntimeException('Invalid HMAC'); - } - } - - //if ($this->decompress) { - // $payload = gzinflate(substr($payload, 2)); - //} - - $this->get_seq_no++; - - if (defined('NET_SSH2_LOGGING')) { - $current = microtime(true); - $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; - $message_number = '<- ' . $message_number . - ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; - $this->_append_log($message_number, $payload); - $this->last_packet = $current; - } - - return $this->_filter($payload); - } - - /** - * Filter Binary Packets - * - * Because some binary packets need to be ignored... - * - * @see self::_get_binary_packet() - * @return string - * @access private - */ - function _filter($payload) - { - switch (ord($payload[0])) { - case NET_SSH2_MSG_DISCONNECT: - $this->_string_shift($payload, 1); - extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); - $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); - $this->bitmap = 0; - return false; - case NET_SSH2_MSG_IGNORE: - $payload = $this->_get_binary_packet(); - break; - case NET_SSH2_MSG_DEBUG: - $this->_string_shift($payload, 2); - extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length)); - $payload = $this->_get_binary_packet(); - break; - case NET_SSH2_MSG_UNIMPLEMENTED: - return false; - case NET_SSH2_MSG_KEXINIT: - if ($this->session_id !== false) { - if (!$this->_key_exchange($payload)) { - $this->bitmap = 0; - return false; - } - $payload = $this->_get_binary_packet(); - } - } - - // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in - if (($this->bitmap & self::MASK_CONNECTED) && !($this->bitmap & self::MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { - $this->_string_shift($payload, 1); - extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); - $payload = $this->_get_binary_packet(); - } - - // only called when we've already logged in - if (($this->bitmap & self::MASK_CONNECTED) && ($this->bitmap & self::MASK_LOGIN)) { - switch (ord($payload[0])) { - case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 - extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length); - - if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) { - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - } - - $payload = $this->_get_binary_packet(); - break; - case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 - $this->_string_shift($payload, 1); - extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $data = $this->_string_shift($payload, $length); - extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); - switch ($data) { - case 'auth-agent': - case 'auth-agent@openssh.com': - if (isset($this->agent)) { - $new_channel = self::CHANNEL_AGENT_FORWARD; - - extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4))); - extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4))); - - $this->packet_size_client_to_server[$new_channel] = $remote_window_size; - $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size; - $this->window_size_client_to_server[$new_channel] = $this->window_size; - - $packet_size = 0x4000; - - $packet = pack( - 'CN4', - NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, - $server_channel, - $new_channel, - $packet_size, - $packet_size - ); - - $this->server_channels[$new_channel] = $server_channel; - $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; - if (!$this->_send_binary_packet($packet)) { - return false; - } - } - break; - default: - $packet = pack( - 'CN3a*Na*', - NET_SSH2_MSG_REQUEST_FAILURE, - $server_channel, - NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, - 0, - '', - 0, - '' - ); - - if (!$this->_send_binary_packet($packet)) { - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - } - } - $payload = $this->_get_binary_packet(); - break; - case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: - $this->_string_shift($payload, 1); - extract(unpack('Nchannel', $this->_string_shift($payload, 4))); - extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); - $this->window_size_client_to_server[$channel]+= $window_size; - - $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); - } - } - - return $payload; - } - - /** - * Enable Quiet Mode - * - * Suppress stderr from output - * - * @access public - */ - function enableQuietMode() - { - $this->quiet_mode = true; - } - - /** - * Disable Quiet Mode - * - * Show stderr in output - * - * @access public - */ - function disableQuietMode() - { - $this->quiet_mode = false; - } - - /** - * Returns whether Quiet Mode is enabled or not - * - * @see self::enableQuietMode() - * @see self::disableQuietMode() - * @access public - * @return bool - */ - function isQuietModeEnabled() - { - return $this->quiet_mode; - } - - /** - * Enable request-pty when using exec() - * - * @access public - */ - function enablePTY() - { - $this->request_pty = true; - } - - /** - * Disable request-pty when using exec() - * - * @access public - */ - function disablePTY() - { - $this->request_pty = false; - } - - /** - * Returns whether request-pty is enabled or not - * - * @see self::enablePTY() - * @see self::disablePTY() - * @access public - * @return bool - */ - function isPTYEnabled() - { - return $this->request_pty; - } - - /** - * Gets channel data - * - * Returns the data as a string if it's available and false if not. - * - * @param $client_channel - * @return mixed - * @throws \RuntimeException on connection error - * @access private - */ - function _get_channel_packet($client_channel, $skip_extended = false) - { - if (!empty($this->channel_buffers[$client_channel])) { - return array_shift($this->channel_buffers[$client_channel]); - } - - while (true) { - if ($this->curTimeout) { - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return true; - } - - $read = array($this->fsock); - $write = $except = null; - - $start = microtime(true); - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - // on windows this returns a "Warning: Invalid CRT parameters detected" error - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->is_timeout = true; - return true; - } - $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; - } - - $response = $this->_get_binary_packet(); - if ($response === false) { - throw new \RuntimeException('Connection closed by server'); - } - if ($client_channel == -1 && $response === true) { - return true; - } - if (!strlen($response)) { - return ''; - } - - extract(unpack('Ctype', $this->_string_shift($response, 1))); - - if ($type == NET_SSH2_MSG_CHANNEL_OPEN) { - extract(unpack('Nlength', $this->_string_shift($response, 4))); - } else { - extract(unpack('Nchannel', $this->_string_shift($response, 4))); - } - - // will not be setup yet on incoming channel open request - if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) { - $this->window_size_server_to_client[$channel]-= strlen($response); - - // resize the window, if appropriate - if ($this->window_size_server_to_client[$channel] < 0) { - $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size); - if (!$this->_send_binary_packet($packet)) { - return false; - } - $this->window_size_server_to_client[$channel]+= $this->window_size; - } - - switch ($this->channel_status[$channel]) { - case NET_SSH2_MSG_CHANNEL_OPEN: - switch ($type) { - case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: - extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); - $this->server_channels[$channel] = $server_channel; - extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); - if ($window_size < 0) { - $window_size&= 0x7FFFFFFF; - $window_size+= 0x80000000; - } - $this->window_size_client_to_server[$channel] = $window_size; - $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); - $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; - $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); - $this->_on_channel_open(); - return $result; - //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: - default: - $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - throw new \RuntimeException('Unable to open channel'); - } - break; - case NET_SSH2_MSG_CHANNEL_REQUEST: - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - return true; - case NET_SSH2_MSG_CHANNEL_FAILURE: - return false; - default: - $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - throw new \RuntimeException('Unable to fulfill channel request'); - } - case NET_SSH2_MSG_CHANNEL_CLOSE: - return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); - } - } - - // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA - - switch ($type) { - case NET_SSH2_MSG_CHANNEL_DATA: - /* - if ($channel == self::CHANNEL_EXEC) { - // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server - // this actually seems to make things twice as fast. more to the point, the message right after - // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. - // in OpenSSH it slows things down but only by a couple thousandths of a second. - $this->_send_channel_packet($channel, chr(0)); - } - */ - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $data = $this->_string_shift($response, $length); - - if ($channel == self::CHANNEL_AGENT_FORWARD) { - $agent_response = $this->agent->_forward_data($data); - if (!is_bool($agent_response)) { - $this->_send_channel_packet($channel, $agent_response); - } - break; - } - - if ($client_channel == $channel) { - return $data; - } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; - break; - case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - /* - if ($client_channel == self::CHANNEL_EXEC) { - $this->_send_channel_packet($client_channel, chr(0)); - } - */ - // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR - extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); - $data = $this->_string_shift($response, $length); - $this->stdErrorLog.= $data; - if ($skip_extended || $this->quiet_mode) { - break; - } - if ($client_channel == $channel) { - return $data; - } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; - break; - case NET_SSH2_MSG_CHANNEL_REQUEST: - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $value = $this->_string_shift($response, $length); - switch ($value) { - case 'exit-signal': - $this->_string_shift($response, 1); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); - $this->_string_shift($response, 1); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - if ($length) { - $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); - } - - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); - - $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; - - break; - case 'exit-status': - extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); - $this->exit_status = $exit_status; - - // "The client MAY ignore these messages." - // -- http://tools.ietf.org/html/rfc4254#section-6.10 - - break; - default: - // "Some systems may not implement signals, in which case they SHOULD ignore this message." - // -- http://tools.ietf.org/html/rfc4254#section-6.9 - break; - } - break; - case NET_SSH2_MSG_CHANNEL_CLOSE: - $this->curTimeout = 0; - - if ($this->bitmap & self::MASK_SHELL) { - $this->bitmap&= ~self::MASK_SHELL; - } - if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); - } - - $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; - return true; - case NET_SSH2_MSG_CHANNEL_EOF: - break; - default: - $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - throw new \RuntimeException('Error reading channel data'); - } - } - } - - /** - * Sends Binary Packets - * - * See '6. Binary Packet Protocol' of rfc4253 for more info. - * - * @param string $data - * @param string $logged - * @see self::_get_binary_packet() - * @return bool - * @access private - */ - function _send_binary_packet($data, $logged = null) - { - if (!is_resource($this->fsock) || feof($this->fsock)) { - $this->bitmap = 0; - throw new \RuntimeException('Connection closed prematurely'); - } - - //if ($this->compress) { - // // the -4 removes the checksum: - // // http://php.net/function.gzcompress#57710 - // $data = substr(gzcompress($data), 0, -4); - //} - - // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 - $packet_length = strlen($data) + 9; - // round up to the nearest $this->encrypt_block_size - $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; - // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length - $padding_length = $packet_length - strlen($data) - 5; - $padding = Random::string($padding_length); - - // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself - $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); - - $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : ''; - $this->send_seq_no++; - - if ($this->encrypt !== false) { - $packet = $this->encrypt->encrypt($packet); - } - - $packet.= $hmac; - - $start = microtime(true); - $result = strlen($packet) == fputs($this->fsock, $packet); - $stop = microtime(true); - - if (defined('NET_SSH2_LOGGING')) { - $current = microtime(true); - $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')'; - $message_number = '-> ' . $message_number . - ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; - $this->_append_log($message_number, isset($logged) ? $logged : $data); - $this->last_packet = $current; - } - - return $result; - } - - /** - * Logs data packets - * - * Makes sure that only the last 1MB worth of packets will be logged - * - * @param string $data - * @access private - */ - function _append_log($message_number, $message) - { - // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) - if (strlen($message_number) > 2) { - $this->_string_shift($message); - } - - switch (NET_SSH2_LOGGING) { - // useful for benchmarks - case self::LOG_SIMPLE: - $this->message_number_log[] = $message_number; - break; - // the most useful log for SSH2 - case self::LOG_COMPLEX: - $this->message_number_log[] = $message_number; - $this->log_size+= strlen($message); - $this->message_log[] = $message; - while ($this->log_size > self::LOG_MAX_SIZE) { - $this->log_size-= strlen(array_shift($this->message_log)); - array_shift($this->message_number_log); - } - break; - // dump the output out realtime; packets may be interspersed with non packets, - // passwords won't be filtered out and select other packets may not be correctly - // identified - case self::LOG_REALTIME: - switch (PHP_SAPI) { - case 'cli': - $start = $stop = "\r\n"; - break; - default: - $start = '
';
-                        $stop = '
'; - } - echo $start . $this->_format_log(array($message), array($message_number)) . $stop; - @flush(); - @ob_flush(); - break; - // basically the same thing as self::LOG_REALTIME with the caveat that NET_SFTP_LOG_REALTIME_FILENAME - // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. - // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily - // at the beginning of the file - case self::LOG_REALTIME_FILE: - if (!isset($this->realtime_log_file)) { - // PHP doesn't seem to like using constants in fopen() - $filename = NET_SSH2_LOG_REALTIME_FILENAME; - $fp = fopen($filename, 'w'); - $this->realtime_log_file = $fp; - } - if (!is_resource($this->realtime_log_file)) { - break; - } - $entry = $this->_format_log(array($message), array($message_number)); - if ($this->realtime_log_wrap) { - $temp = "<<< START >>>\r\n"; - $entry.= $temp; - fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); - } - $this->realtime_log_size+= strlen($entry); - if ($this->realtime_log_size > self::LOG_MAX_SIZE) { - fseek($this->realtime_log_file, 0); - $this->realtime_log_size = strlen($entry); - $this->realtime_log_wrap = true; - } - fputs($this->realtime_log_file, $entry); - } - } - - /** - * Sends channel data - * - * Spans multiple SSH_MSG_CHANNEL_DATAs if appropriate - * - * @param int $client_channel - * @param string $data - * @return bool - * @access private - */ - function _send_channel_packet($client_channel, $data) - { - while (strlen($data)) { - if (!$this->window_size_client_to_server[$client_channel]) { - $this->bitmap^= self::MASK_WINDOW_ADJUST; - // using an invalid channel will let the buffers be built up for the valid channels - $this->_get_channel_packet(-1); - $this->bitmap^= self::MASK_WINDOW_ADJUST; - } - - /* The maximum amount of data allowed is determined by the maximum - packet size for the channel, and the current window size, whichever - is smaller. - -- http://tools.ietf.org/html/rfc4254#section-5.2 */ - $max_size = min( - $this->packet_size_client_to_server[$client_channel], - $this->window_size_client_to_server[$client_channel] - ); - - $temp = $this->_string_shift($data, $max_size); - $packet = pack( - 'CN2a*', - NET_SSH2_MSG_CHANNEL_DATA, - $this->server_channels[$client_channel], - strlen($temp), - $temp - ); - $this->window_size_client_to_server[$client_channel]-= strlen($temp); - if (!$this->_send_binary_packet($packet)) { - return false; - } - } - - return true; - } - - /** - * Closes and flushes a channel - * - * \phpseclib\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server - * and for SFTP channels are presumably closed when the client disconnects. This functions is intended - * for SCP more than anything. - * - * @param int $client_channel - * @param bool $want_reply - * @return bool - * @access private - */ - function _close_channel($client_channel, $want_reply = false) - { - // see http://tools.ietf.org/html/rfc4254#section-5.3 - - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); - - if (!$want_reply) { - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); - } - - $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; - - $this->curTimeout = 0; - - while (!is_bool($this->_get_channel_packet($client_channel))) { - } - - if ($want_reply) { - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); - } - - if ($this->bitmap & self::MASK_SHELL) { - $this->bitmap&= ~self::MASK_SHELL; - } - } - - /** - * Disconnect - * - * @param int $reason - * @return bool - * @access private - */ - function _disconnect($reason) - { - if ($this->bitmap & self::MASK_CONNECTED) { - $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, ''); - $this->_send_binary_packet($data); - $this->bitmap = 0; - fclose($this->fsock); - return false; - } - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; - } - - /** - * Define Array - * - * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of - * named constants from it, using the value as the name of the constant and the index as the value of the constant. - * If any of the constants that would be defined already exists, none of the constants will be defined. - * - * @param array $array - * @access private - */ - function _define_array() - { - $args = func_get_args(); - foreach ($args as $arg) { - foreach ($arg as $key => $value) { - if (!defined($value)) { - define($value, $key); - } else { - break 2; - } - } - } - } - - /** - * Returns a log of the packets that have been sent and received. - * - * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') - * - * @access public - * @return array|false|string - */ - function getLog() - { - if (!defined('NET_SSH2_LOGGING')) { - return false; - } - - switch (NET_SSH2_LOGGING) { - case self::LOG_SIMPLE: - return $this->message_number_log; - break; - case self::LOG_COMPLEX: - return $this->_format_log($this->message_log, $this->message_number_log); - break; - default: - return false; - } - } - - /** - * Formats a log for printing - * - * @param array $message_log - * @param array $message_number_log - * @access private - * @return string - */ - function _format_log($message_log, $message_number_log) - { - $output = ''; - for ($i = 0; $i < count($message_log); $i++) { - $output.= $message_number_log[$i] . "\r\n"; - $current_log = $message_log[$i]; - $j = 0; - do { - if (strlen($current_log)) { - $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; - } - $fragment = $this->_string_shift($current_log, $this->log_short_width); - $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); - // replace non ASCII printable characters with dots - // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters - // also replace < with a . since < messes up the output on web browsers - $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); - $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; - $j++; - } while (strlen($current_log)); - $output.= "\r\n"; - } - - return $output; - } - - /** - * Helper function for _format_log - * - * For use with preg_replace_callback() - * - * @param array $matches - * @access private - * @return string - */ - function _format_log_helper($matches) - { - return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); - } - - /** - * Helper function for agent->_on_channel_open() - * - * Used when channels are created to inform agent - * of said channel opening. Must be called after - * channel open confirmation received - * - * @access private - */ - function _on_channel_open() - { - if (isset($this->agent)) { - $this->agent->_on_channel_open($this); - } - } - - /** - * Returns the first value of the intersection of two arrays or false if - * the intersection is empty. The order is defined by the first parameter. - * - * @param array $array1 - * @param array $array2 - * @return mixed False if intersection is empty, else intersected value. - * @access private - */ - function _array_intersect_first($array1, $array2) - { - foreach ($array1 as $value) { - if (in_array($value, $array2)) { - return $value; - } - } - return false; - } - - /** - * Returns all errors - * - * @return string[] - * @access public - */ - function getErrors() - { - return $this->errors; - } - - /** - * Returns the last error - * - * @return string - * @access public - */ - function getLastError() - { - $count = count($this->errors); - - if ($count > 0) { - return $this->errors[$count - 1]; - } - } - - /** - * Return the server identification. - * - * @return string - * @access public - */ - function getServerIdentification() - { - $this->_connect(); - - return $this->server_identifier; - } - - /** - * Return a list of the key exchange algorithms the server supports. - * - * @return array - * @access public - */ - function getKexAlgorithms() - { - $this->_connect(); - - return $this->kex_algorithms; - } - - /** - * Return a list of the host key (public key) algorithms the server supports. - * - * @return array - * @access public - */ - function getServerHostKeyAlgorithms() - { - $this->_connect(); - - return $this->server_host_key_algorithms; - } - - /** - * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client. - * - * @return array - * @access public - */ - function getEncryptionAlgorithmsClient2Server() - { - $this->_connect(); - - return $this->encryption_algorithms_client_to_server; - } - - /** - * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client. - * - * @return array - * @access public - */ - function getEncryptionAlgorithmsServer2Client() - { - $this->_connect(); - - return $this->encryption_algorithms_server_to_client; - } - - /** - * Return a list of the MAC algorithms the server supports, when receiving stuff from the client. - * - * @return array - * @access public - */ - function getMACAlgorithmsClient2Server() - { - $this->_connect(); - - return $this->mac_algorithms_client_to_server; - } - - /** - * Return a list of the MAC algorithms the server supports, when sending stuff to the client. - * - * @return array - * @access public - */ - function getMACAlgorithmsServer2Client() - { - $this->_connect(); - - return $this->mac_algorithms_server_to_client; - } - - /** - * Return a list of the compression algorithms the server supports, when receiving stuff from the client. - * - * @return array - * @access public - */ - function getCompressionAlgorithmsClient2Server() - { - $this->_connect(); - - return $this->compression_algorithms_client_to_server; - } - - /** - * Return a list of the compression algorithms the server supports, when sending stuff to the client. - * - * @return array - * @access public - */ - function getCompressionAlgorithmsServer2Client() - { - $this->_connect(); - - return $this->compression_algorithms_server_to_client; - } - - /** - * Return a list of the languages the server supports, when sending stuff to the client. - * - * @return array - * @access public - */ - function getLanguagesServer2Client() - { - $this->_connect(); - - return $this->languages_server_to_client; - } - - /** - * Return a list of the languages the server supports, when receiving stuff from the client. - * - * @return array - * @access public - */ - function getLanguagesClient2Server() - { - $this->_connect(); - - return $this->languages_client_to_server; - } - - /** - * Returns the banner message. - * - * Quoting from the RFC, "in some jurisdictions, sending a warning message before - * authentication may be relevant for getting legal protection." - * - * @return string - * @access public - */ - function getBannerMessage() - { - return $this->banner_message; - } - - /** - * 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 - * @throws \RuntimeException on badly formatted keys - * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format - * @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) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new \RuntimeException('Invalid signature'); - } - - $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: - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new \RuntimeException('Invalid signature'); - } - - $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, "\0")); - - /* - $temp = unpack('Nlength', $this->_string_shift($signature, 4)); - $signature = $this->_string_shift($signature, $temp['length']); - - $rsa = new RSA(); - $rsa->load(array('e' => $e, 'n' => $n), 'raw'); - $rsa->setHash('sha1'); - if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) { - //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) { - $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - throw new \RuntimeException('Invalid signature'); - } - - $s = $s->modPow($e, $n); - $s = $s->toBytes(); - - $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash)); - $h = chr(0x01) . 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: - $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); - throw new NoSupportedAlgorithmsException('Unsupported signature format'); - } - - return $this->signature_format . ' ' . Base64::encode($this->server_public_host_key); - } - - /** - * Returns the exit status of an SSH command or false. - * - * @return false|int - * @access public - */ - function getExitStatus() - { - if (is_null($this->exit_status)) { - return false; - } - return $this->exit_status; - } - - /** - * Returns the number of columns for the terminal window size. - * - * @return int - * @access public - */ - function getWindowColumns() - { - return $this->windowColumns; - } - - /** - * Returns the number of rows for the terminal window size. - * - * @return int - * @access public - */ - function getWindowRows() - { - return $this->windowRows; - } - - /** - * Sets the number of columns for the terminal window size. - * - * @param int $value - * @access public - */ - function setWindowColumns($value) - { - $this->windowColumns = $value; - } - - /** - * Sets the number of rows for the terminal window size. - * - * @param int $value - * @access public - */ - function setWindowRows($value) - { - $this->windowRows = $value; - } - - /** - * Sets the number of columns and rows for the terminal window size. - * - * @param int $columns - * @param int $rows - * @access public - */ - function setWindowSize($columns = 80, $rows = 24) - { - $this->windowColumns = $columns; - $this->windowRows = $rows; - } - - /** - * @return string - */ - function __toString() - { - return $this->getResourceId(); - } - - /** - * We use {} because that symbols should not be in URL according to - * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}. - * It will safe us from any conflicts, because otherwise regexp will - * match all alphanumeric domains. - * - * @return string - */ - function getResourceId() - { - return '{' . spl_object_hash($this) . '}'; - } - - /** - * Return existing connection - * - * @param string $id - * - * @return bool|SSH2 will return false if no such connection - */ - static function getConnectionByResourceId($id) - { - return isset(self::$connections[$id]) ? self::$connections[$id] : false; - } - - /** - * Return all excising connections - * - * @return SSH2[] - */ - static function getConnections() - { - return self::$connections; - } -} diff --git a/src/phpseclib/System/SSH/Agent.php b/src/phpseclib/System/SSH/Agent.php deleted file mode 100755 index 23bf027a..00000000 --- a/src/phpseclib/System/SSH/Agent.php +++ /dev/null @@ -1,313 +0,0 @@ - - * login('username', $agent)) { - * exit('Login Failed'); - * } - * - * echo $ssh->exec('pwd'); - * echo $ssh->exec('ls -la'); - * ?> - * - * - * @category System - * @package SSH\Agent - * @author Jim Wigginton - * @copyright 2014 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - * @internal See http://api.libssh.org/rfc/PROTOCOL.agent - */ - -namespace phpseclib\System\SSH; - -use ParagonIE\ConstantTime\Base64; -use phpseclib\Crypt\RSA; -use phpseclib\Exception\BadConfigurationException; -use phpseclib\System\SSH\Agent\Identity; - -/** - * Pure-PHP ssh-agent client identity factory - * - * requestIdentities() method pumps out \phpseclib\System\SSH\Agent\Identity objects - * - * @package SSH\Agent - * @author Jim Wigginton - * @access internal - */ -class Agent -{ - /**#@+ - * Message numbers - * - * @access private - */ - // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1) - const SSH_AGENTC_REQUEST_IDENTITIES = 11; - // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2). - const SSH_AGENT_IDENTITIES_ANSWER = 12; - // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3) - const SSH_AGENTC_SIGN_REQUEST = 13; - // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4) - const SSH_AGENT_SIGN_RESPONSE = 14; - /**#@-*/ - - /**@+ - * Agent forwarding status - * - * @access private - */ - // no forwarding requested and not active - const FORWARD_NONE = 0; - // request agent forwarding when opportune - const FORWARD_REQUEST = 1; - // forwarding has been request and is active - const FORWARD_ACTIVE = 2; - /**#@-*/ - - /** - * Unused - */ - const SSH_AGENT_FAILURE = 5; - - /** - * Socket Resource - * - * @var resource - * @access private - */ - var $fsock; - - /** - * Agent forwarding status - * - * @access private - */ - var $forward_status = self::FORWARD_NONE; - - /** - * Buffer for accumulating forwarded authentication - * agent data arriving on SSH data channel destined - * for agent unix socket - * - * @access private - */ - var $socket_buffer = ''; - - /** - * Tracking the number of bytes we are expecting - * to arrive for the agent socket on the SSH data - * channel - */ - var $expected_bytes = 0; - - /** - * Default Constructor - * - * @return \phpseclib\System\SSH\Agent - * @throws \phpseclib\Exception\BadConfigurationException if SSH_AUTH_SOCK cannot be found - * @throws \RuntimeException on connection errors - * @access public - */ - function __construct() - { - switch (true) { - case isset($_SERVER['SSH_AUTH_SOCK']): - $address = $_SERVER['SSH_AUTH_SOCK']; - break; - case isset($_ENV['SSH_AUTH_SOCK']): - $address = $_ENV['SSH_AUTH_SOCK']; - break; - default: - throw new BadConfigurationException('SSH_AUTH_SOCK not found'); - } - - $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); - if (!$this->fsock) { - throw new \RuntimeException("Unable to connect to ssh-agent (Error $errno: $errstr)"); - } - } - - /** - * Request Identities - * - * See "2.5.2 Requesting a list of protocol 2 keys" - * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects - * - * @return array - * @throws \RuntimeException on receipt of unexpected packets - * @access public - */ - function requestIdentities() - { - if (!$this->fsock) { - return array(); - } - - $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); - if (strlen($packet) != fputs($this->fsock, $packet)) { - throw new \RuntimeException('Connection closed while requesting identities'); - } - - $length = current(unpack('N', fread($this->fsock, 4))); - $type = ord(fread($this->fsock, 1)); - if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { - throw new \RuntimeException('Unable to request identities'); - } - - $identities = array(); - $keyCount = current(unpack('N', fread($this->fsock, 4))); - for ($i = 0; $i < $keyCount; $i++) { - $length = current(unpack('N', fread($this->fsock, 4))); - $key_blob = fread($this->fsock, $length); - $key_str = 'ssh-rsa ' . Base64::encode($key_blob); - $length = current(unpack('N', fread($this->fsock, 4))); - if ($length) { - $key_str.= ' ' . fread($this->fsock, $length); - } - $length = current(unpack('N', substr($key_blob, 0, 4))); - $key_type = substr($key_blob, 4, $length); - switch ($key_type) { - case 'ssh-rsa': - $key = new RSA(); - $key->load($key_str); - break; - case 'ssh-dss': - // not currently supported - break; - } - // resources are passed by reference by default - if (isset($key)) { - $identity = new Identity($this->fsock); - $identity->setPublicKey($key); - $identity->setPublicKeyBlob($key_blob); - $identities[] = $identity; - unset($key); - } - } - - return $identities; - } - - /** - * Signal that agent forwarding should - * be requested when a channel is opened - * - * @param Net_SSH2 $ssh - * @return bool - * @access public - */ - function startSSHForwarding($ssh) - { - if ($this->forward_status == self::FORWARD_NONE) { - $this->forward_status = self::FORWARD_REQUEST; - } - } - - /** - * Request agent forwarding of remote server - * - * @param Net_SSH2 $ssh - * @return bool - * @access private - */ - function _request_forwarding($ssh) - { - $request_channel = $ssh->_get_open_channel(); - if ($request_channel === false) { - return false; - } - - $packet = pack( - 'CNNa*C', - NET_SSH2_MSG_CHANNEL_REQUEST, - $ssh->server_channels[$request_channel], - strlen('auth-agent-req@openssh.com'), - 'auth-agent-req@openssh.com', - 1 - ); - - $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; - - if (!$ssh->_send_binary_packet($packet)) { - return false; - } - - $response = $ssh->_get_channel_packet($request_channel); - if ($response === false) { - return false; - } - - $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; - $this->forward_status = self::FORWARD_ACTIVE; - - return true; - } - - /** - * On successful channel open - * - * This method is called upon successful channel - * open to give the SSH Agent an opportunity - * to take further action. i.e. request agent forwarding - * - * @param Net_SSH2 $ssh - * @access private - */ - function _on_channel_open($ssh) - { - if ($this->forward_status == self::FORWARD_REQUEST) { - $this->_request_forwarding($ssh); - } - } - - /** - * Forward data to SSH Agent and return data reply - * - * @param string $data - * @return data from SSH Agent - * @throws \RuntimeException on connection errors - * @access private - */ - function _forward_data($data) - { - if ($this->expected_bytes > 0) { - $this->socket_buffer.= $data; - $this->expected_bytes -= strlen($data); - } else { - $agent_data_bytes = current(unpack('N', $data)); - $current_data_bytes = strlen($data); - $this->socket_buffer = $data; - if ($current_data_bytes != $agent_data_bytes + 4) { - $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; - return false; - } - } - - if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { - throw new \RuntimeException('Connection closed attempting to forward data to SSH agent'); - } - - $this->socket_buffer = ''; - $this->expected_bytes = 0; - - $agent_reply_bytes = current(unpack('N', fread($this->fsock, 4))); - - $agent_reply_data = fread($this->fsock, $agent_reply_bytes); - $agent_reply_data = current(unpack('a*', $agent_reply_data)); - - return pack('Na*', $agent_reply_bytes, $agent_reply_data); - } -} diff --git a/src/phpseclib/System/SSH/Agent/Identity.php b/src/phpseclib/System/SSH/Agent/Identity.php deleted file mode 100755 index 612c414e..00000000 --- a/src/phpseclib/System/SSH/Agent/Identity.php +++ /dev/null @@ -1,170 +0,0 @@ - - * @copyright 2009 Jim Wigginton - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://phpseclib.sourceforge.net - * @internal See http://api.libssh.org/rfc/PROTOCOL.agent - */ - -namespace phpseclib\System\SSH\Agent; - -use phpseclib\Crypt\RSA; -use phpseclib\Exception\UnsupportedAlgorithmException; -use phpseclib\System\SSH\Agent; - -/** - * Pure-PHP ssh-agent client identity object - * - * Instantiation should only be performed by \phpseclib\System\SSH\Agent class. - * This could be thought of as implementing an interface that phpseclib\Crypt\RSA - * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something. - * The methods in this interface would be getPublicKey and sign since those are the - * methods phpseclib looks for to perform public key authentication. - * - * @package SSH\Agent - * @author Jim Wigginton - * @access internal - */ -class Identity -{ - /** - * Key Object - * - * @var \phpseclib\Crypt\RSA - * @access private - * @see self::getPublicKey() - */ - var $key; - - /** - * Key Blob - * - * @var string - * @access private - * @see self::sign() - */ - var $key_blob; - - /** - * Socket Resource - * - * @var resource - * @access private - * @see self::sign() - */ - var $fsock; - - /** - * Default Constructor. - * - * @param resource $fsock - * @return \phpseclib\System\SSH\Agent\Identity - * @access private - */ - function __construct($fsock) - { - $this->fsock = $fsock; - } - - /** - * Set Public Key - * - * Called by \phpseclib\System\SSH\Agent::requestIdentities() - * - * @param \phpseclib\Crypt\RSA $key - * @access private - */ - function setPublicKey($key) - { - $this->key = $key; - $this->key->setPublicKey(); - } - - /** - * Set Public Key - * - * Called by \phpseclib\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key - * but this saves a small amount of computation. - * - * @param string $key_blob - * @access private - */ - function setPublicKeyBlob($key_blob) - { - $this->key_blob = $key_blob; - } - - /** - * Get Public Key - * - * Wrapper for $this->key->getPublicKey() - * - * @param int $type optional - * @return mixed - * @access public - */ - function getPublicKey($type = 'PKCS8') - { - return $this->key->getPublicKey($type); - } - - /** - * Sets the hash - * - * ssh-agent only supports signatures with sha1 hashes but to maintain BC with RSA.php this function exists - * - * @param string $hash optional - * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported - * @access public - */ - function setHash($hash = 'sha1') - { - if ($hash != 'sha1') { - throw new UnsupportedAlgorithmException('ssh-agent can only be used with the sha1 hash'); - } - } - - /** - * Create a signature - * - * See "2.6.2 Protocol 2 private key signature request" - * - * @param string $message - * @param int $padding optional - * @return string - * @throws \RuntimeException on connection errors - * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported - * @access public - */ - function sign($message, $padding = RSA::PADDING_PKCS1) - { - if ($padding != RSA::PADDING_PKCS1 && $padding != RSA::PADDING_RELAXED_PKCS1) { - throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures'); - } - - // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE - $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); - $packet = pack('Na*', strlen($packet), $packet); - if (strlen($packet) != fputs($this->fsock, $packet)) { - throw new \RuntimeException('Connection closed during signing'); - } - - $length = current(unpack('N', fread($this->fsock, 4))); - $type = ord(fread($this->fsock, 1)); - if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { - throw new \RuntimeException('Unable to retreive signature'); - } - - $signature_blob = fread($this->fsock, $length - 1); - // the only other signature format defined - ssh-dss - is the same length as ssh-rsa - // the + 12 is for the other various SSH added length fields - return substr($signature_blob, strlen('ssh-rsa') + 12); - } -} diff --git a/src/phpseclib/bootstrap.php b/src/phpseclib/bootstrap.php deleted file mode 100755 index bd4ba0b5..00000000 --- a/src/phpseclib/bootstrap.php +++ /dev/null @@ -1,20 +0,0 @@ -saveDefaultConfig(); $this->saveResource("server-icon.png", false); - $this->saveResource("steve.yml", false); - $this->saveResource("alex.yml", false); $this->reloadConfig(); $this->onlineMode = (bool) $this->getConfig()->get("online-mode"); @@ -71,71 +76,40 @@ public function onEnable(){ if(!$this->getConfig()->exists("motd")){ $this->getLogger()->warning("No motd has been set. The server description will be empty."); - return; } $this->translator = new TranslatorProtocol(); - - /*switch(Info::CURRENT_PROTOCOL){ - case 81: - $this->translator = new TranslatorProtocol(); - break; - default: - $this->getLogger()->critical("Couldn't find a protocol translator for #".Info::CURRENT_PROTOCOL .", disabling plugin"); - $this->getPluginLoader()->disablePlugin($this); - return; - break; - }*/ - $this->rsa = new RSA(); $this->getServer()->getPluginManager()->registerEvents($this, $this); - Achievement::add("openInventory","Taking Inventory"); //this for DesktopPlayer - if($this->onlineMode){ $this->getLogger()->info("Server is being started in the background"); - $this->getLogger()->info("Generating keypair"); - //$this->rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1); - //$this->rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1); - $keys = $this->rsa->createKey(1024); - $this->privateKey = $keys["privatekey"]; - $this->publicKey = $keys["publickey"]; - //$this->rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); - $this->rsa->load($this->privateKey); + $task = new GeneratePrivateKey($this->getServer()->getLogger(), $this->getServer()->getLoader()); + $this->getServer()->getScheduler()->scheduleAsyncTask($task); + }else{ + $this->enableServer(); } - $this->enableServer(); } - protected function enableServer(){ - $this->getLogger()->info("Starting Minecraft: PC server on ".($this->getIp() === "0.0.0.0" ? "*" : $this->getIp()).":".$this->getPort()." version ".MCInfo::VERSION); - - $disable = true; - foreach($this->getServer()->getInterfaces() as $interface){ - if($interface instanceof ProtocolInterface){ - $disable = false; - } - } - if($disable){ - $this->interface = new ProtocolInterface($this, $this->getServer(), $this->translator); - $this->getServer()->addInterface($this->interface); - } - } - - public function getIp(){ - return $this->getConfig()->get("interface"); - } - - public function getPort(){ - return (int) $this->getConfig()->get("port"); - } - - public function getMotd(){ - return (string) $this->getConfig()->get("motd"); + public function receiveCryptoKeys($privateKey, $publicKey){ + $this->privateKey = $privateKey; + $this->publicKey = $publicKey; + $this->rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); + $this->rsa->loadKey($this->privateKey); + $this->enableServer(); } - public function getResourcePackURL(){ - return (string) $this->getConfig()->get("resourcepackurl"); + protected function enableServer(){ + $this->externalQueue = new \Threaded; + $this->internalQueue = new \Threaded; + $port = (int) $this->getConfig()->get("port"); + $interface = $this->getConfig()->get("interface"); + $this->getLogger()->info("Starting Minecraft: PC server on ".($interface === "0.0.0.0" ? "*" : $interface).":$port version ".MCInfo::VERSION); + $this->thread = new ServerThread($this->externalQueue, $this->internalQueue, $this->getServer()->getLogger(), $this->getServer()->getLoader(), $port, $interface, (string) $this->getConfig()->get("motd"), $this->getDataFolder() . "server-icon.png"); + + $this->interface = new ProtocolInterface($this, $this->thread, $this->translator); + $this->getServer()->addInterface($this->interface); } /** @@ -156,6 +130,14 @@ public function decryptBinary($secret){ return $this->rsa->decrypt($secret); } + public function onDisable(){ + //TODO: make it fully /reload compatible (remove from server) + if($this->interface instanceof ProtocolInterface){ + $this->getServer()->removeInterface($this->interface); + $this->thread->join(); + } + } + /** * @param PlayerPreLoginEvent $event * @@ -170,6 +152,7 @@ public function onPreLogin(PlayerPreLoginEvent $event){ } $player->bigBrother_setCompression($threshold); } + } /** @@ -182,32 +165,11 @@ public function onRespawn(PlayerRespawnEvent $event){ if($player instanceof DesktopPlayer){ $pk = new RespawnPacket(); $pk->dimension = 0; - $pk->difficulty = $player->getServer()->getDifficulty(); $pk->gamemode = $player->getGamemode(); + $pk->difficulty = $player->getServer()->getDifficulty(); $pk->levelType = "default"; $player->putRawPacket($pk); } } - public function onTouch(PlayerInteractEvent $event){ - $player = $event->getPlayer(); - if($player instanceof DesktopPlayer){ - $block = $event->getBlock(); - switch($block->getID()){ - case Block::SIGN_POST: - case Block::WALL_SIGN: - $tile = $player->getLevel()->getTile(new Vector3($block->getX(), $block->getY(), $block->getZ())); - if($tile instanceof Sign){ - $text = $tile->getText(); - if($text[0] === "ResourcePack" and $text[1] === "Download" and $this->getResourcePackURL() !== "false"){ - $pk = new ResourcePackSendPacket(); - $pk->url = $this->getResourcePackURL(); - $player->putRawPacket($pk); - } - } - break; - } - } - } - -} \ No newline at end of file +} diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index 14d09f56..fed9a107 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -17,109 +17,139 @@ namespace shoghicp\BigBrother; -use pocketmine\event\Timings; +use pocketmine\event\player\PlayerJoinEvent; +use pocketmine\event\player\PlayerRespawnEvent; +use pocketmine\level\format\anvil\Chunk as AnvilChunk; +use pocketmine\level\format\mcregion\Chunk as McRegionChunk; +use pocketmine\level\format\leveldb\Chunk as LevelDBChunk; +use pocketmine\level\format\generic\EmptyChunkSection; use pocketmine\level\Level; +use pocketmine\level\Position; use pocketmine\network\protocol\Info; use pocketmine\network\protocol\LoginPacket; +use pocketmine\network\protocol\SetEntityMotionPacket; +use pocketmine\network\protocol\SetTimePacket; use pocketmine\network\SourceInterface; use pocketmine\Player; +use pocketmine\scheduler\CallbackTask; use pocketmine\Server; -use pocketmine\tile\Sign; +use pocketmine\tile\Spawnable; +use pocketmine\utils\TextFormat; use pocketmine\utils\Utils; use pocketmine\utils\UUID; -use pocketmine\utils\TextFormat; use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\network\protocol\Login\EncryptionRequestPacket; -use shoghicp\BigBrother\network\protocol\Login\EncryptionResponsePacket; -use shoghicp\BigBrother\network\protocol\Login\LoginSuccessPacket; -use shoghicp\BigBrother\network\protocol\Play\ChunkDataPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerListPacket; -use shoghicp\BigBrother\network\protocol\Play\TitlePacket; +use shoghicp\BigBrother\network\protocol\ChunkDataPacket; +use shoghicp\BigBrother\network\protocol\EncryptionRequestPacket; +use shoghicp\BigBrother\network\protocol\EncryptionResponsePacket; +use shoghicp\BigBrother\network\protocol\EntityMetadataPacket; +use shoghicp\BigBrother\network\protocol\EntityTeleportPacket; +use shoghicp\BigBrother\network\protocol\KeepAlivePacket; +use shoghicp\BigBrother\network\protocol\LoginDisconnectPacket; +use shoghicp\BigBrother\network\protocol\LoginStartPacket; +use shoghicp\BigBrother\network\protocol\LoginSuccessPacket; +use shoghicp\BigBrother\network\protocol\PlayDisconnectPacket; +use shoghicp\BigBrother\network\protocol\SpawnMobPacket; +use shoghicp\BigBrother\network\protocol\SpawnPlayerPacket; use shoghicp\BigBrother\network\ProtocolInterface; +use shoghicp\BigBrother\tasks\AuthenticateOnline; +use shoghicp\BigBrother\tasks\LevelDBToAnvil; +use shoghicp\BigBrother\tasks\McRegionToAnvil; +use shoghicp\BigBrother\tasks\OnlineProfile; use shoghicp\BigBrother\utils\Binary; class DesktopPlayer extends Player{ private $bigBrother_status = 0; //0 = log in, 1 = playing - public $threshold = false; protected $bigBrother_uuid; protected $bigBrother_formatedUUID; protected $bigBrother_properties = []; private $bigBrother_checkToken; private $bigBrother_secret; private $bigBrother_username; - private $bigbrother_clientId; - protected $Settings = []; + protected $bigBrother_titleBarID = null; + protected $bigBrother_titleBarText; + protected $bigBrother_titleBarLevel; /** @var ProtocolInterface */ protected $interface; public function __construct(SourceInterface $interface, $clientID, $address, $port, BigBrother $plugin){ $this->plugin = $plugin; - $this->bigbrother_clientId = $clientID; parent::__construct($interface, $clientID, $address, $port); - $this->setRemoveFormat(false);// Color Code + $this->setRemoveFormat(false); } - public function bigBrother_getStatus(){ - //echo "bigBrother_getStatus: ".$this->bigBrother_status."\n"; - return $this->bigBrother_status; - } - - public function bigBrother_getPeroperties(){ - //echo "bigBrother_getPeroperties"."\n"; - return $this->bigBrother_properties; - } + public function bigBrother_updateTitleBar(){ + if($this->bigBrother_titleBarID === null){ + $this->bigBrother_titleBarID = 2147483647; + + $pk = new SpawnMobPacket(); + $pk->eid = $this->bigBrother_titleBarID; + $pk->type = 63; + $pk->x = $this->x; + $pk->y = 250; + $pk->z = $this->z; + $pk->pitch = 0; + $pk->yaw = 0; + $pk->headPitch = 0; + $pk->velocityX = 0; + $pk->velocityY = 0; + $pk->velocityZ = 0; + $pk->metadata = [ + 0 => ["type" => 0, "value" => 0x20], + 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], + 7 => ["type" => 2, "value" => 0], + 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], + 11 => ["type" => 0, "value" => 1] + ]; + $this->putRawPacket($pk); + $this->tasks[] = $this->getServer()->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_updateTitleBar"]), 5, 20); + }else{ + $pk = new EntityTeleportPacket(); + $pk->eid = $this->bigBrother_titleBarID; + $pk->x = $this->x; + $pk->y = 250; + $pk->z = $this->z; + $pk->yaw = 0; + $pk->pitch = 0; + $this->putRawPacket($pk); - public function bigBrother_getUniqueId(){ - //echo "bigBrother_getUniqueId"."\n"; - return $this->bigBrother_uuid; - } + $pk = new EntityMetadataPacket(); + $pk->eid = $this->bigBrother_titleBarID; + $pk->metadata = [ + 0 => ["type" => 0, "value" => 0x20], + 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], + 7 => ["type" => 2, "value" => 0], + 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], + 11 => ["type" => 0, "value" => 1] + ]; + $this->putRawPacket($pk); - public function getSettings(){ - //echo "getSettings"."\n"; - return $this->Settings; + } } - public function getSetting($settingname = null){ - //echo "getSetting"."\n"; - if(isset($this->Settings[$settingname])){ - return $this->Settings[$settingname]; + public function bigBrother_setTitleBar($text, $level = 100){ + if($level > 100){ + $level = 100; + }elseif($level < 0){ + $level = 0; } - return false; - } - public function setSetting($settings){ - //echo "setSetting"; - $this->Settings = array_merge($this->Settings, $settings); + $this->bigBrother_titleBarText = $text; + $this->bigBrother_titleBarLevel = $level; + $this->bigBrother_updateTitleBar(); } - public function removeSetting($settingname){ - //echo "removeSetting"; - if(isset($this->Settings[$settingname])){ - unset($this->Settings[$settingname]); - } + public function bigBrother_sendKeepAlive(){ + $pk = new KeepAlivePacket(); + $pk->id = mt_rand(); + $this->putRawPacket($pk); } - public function cleanSetting($settingname){ - //echo "cleanSetting"; - unset($this->Settings[$settingname]); + public function bigBrother_getStatus(){ + return $this->bigBrother_status; } - /*public function sendChunk($x, $z, $payload, $ordering = FullChunkDataPacket::ORDER_COLUMNS){ - //echo "sendChunk"; - bigBrother_sendChunk($x, $z, $payload); - }*/ - public function bigBrother_sendChunk($x, $z, $payload){ - - //echo "bigBrother_sendChunk"; - if($this->connected === false){ - return; - } - - $this->usedChunks[Level::chunkHash($x, $z)] = true; - $this->chunkLoadCount++; - $pk = new ChunkDataPacket(); $pk->chunkX = $x; $pk->chunkZ = $z; @@ -128,207 +158,230 @@ public function bigBrother_sendChunk($x, $z, $payload){ $pk->payload = $payload; $pk->primaryBitmap = 0xff; $this->putRawPacket($pk); + } - foreach($this->level->getChunkTiles($x, $z) as $tile){ - if($tile instanceof Sign){ - $tile->spawnTo($this); - } - } + public function sendChunk($x, $z, $payload,$ordering = FullChunkDataPacket::ORDER_COLUMNS){ - if($this->spawned){ - foreach($this->level->getChunkPlayers($x, $z) as $player){ - $player->spawnTo($this); - } - /*foreach($this->level->getChunkEntities($x, $z) as $entity){ - if($entity !== $this and !$entity->closed and $entity->isAlive()){ - $entity->spawnTo($this); - } - }*/ - } } protected function sendNextChunk(){ - //echo "sendNextChunk"; if($this->connected === false){ return; } - Timings::$playerChunkSendTimer->startTiming(); - $count = 0; foreach($this->loadQueue as $index => $distance){ if($count >= $this->chunksPerTick){ break; } - $X = null; - $Z = null; Level::getXZ($index, $X, $Z); - ++$count; - - $this->usedChunks[$index] = false; - $this->level->registerChunkLoader($this, $X, $Z, false); - - if(!$this->level->populateChunk($X, $Z)){ - if($this->spawned and $this->teleportPosition === null){ + if(!$this->level->isChunkPopulated($X, $Z)){ + $this->level->generateChunk($X, $Z); + if($this->spawned){ continue; }else{ break; } } + ++$count; + unset($this->loadQueue[$index]); - $chunk = new DesktopChunk($this, $X, $Z); - $this->bigBrother_sendChunk($X, $Z, $chunk->getData()); - $chunk = null; + $this->usedChunks[$index] = true; + + $this->level->useChunk($X, $Z, $this); + $chunk = $this->level->getChunk($X, $Z); + if($chunk instanceof AnvilChunk){ + $this->kick("Playing on Anvil worlds is not yet implemented"); + //TODO! + /*$pk = new ChunkDataPacket(); + $pk->chunkX = $X; + $pk->chunkZ = $Z; + $pk->groundUp = true; + $ids = ""; + $meta = ""; + $blockLight = ""; + $skyLight = ""; + $biomeIds = $chunk->getBiomeIdArray(); + $bitmap = 0; + for($s = 0; $s < 8; ++$s){ + $section = $chunk->getSection($s); + if(!($section instanceof EmptyChunkSection)){ + $bitmap |= 1 << $s; + }else{ + continue; + } + $ids .= $section->getIdArray(); + $meta .= $section->getDataArray(); + $blockLight .= $section->getLightArray(); + $skyLight .= $section->getSkyLightArray(); + } + + $pk->payload = zlib_encode($ids . $meta . $blockLight . $skyLight . $biomeIds, ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL); + $pk->primaryBitmap = $bitmap; + $this->putRawPacket($pk);*/ + }elseif($chunk instanceof McRegionChunk){ + $task = new McRegionToAnvil($this, $chunk); + $this->server->getScheduler()->scheduleAsyncTask($task); + }elseif($chunk instanceof LevelDBChunk){ + $task = new LevelDBToAnvil($this, $chunk); + $this->server->getScheduler()->scheduleAsyncTask($task); + } + + foreach($chunk->getEntities() as $entity){ + if($entity !== $this){ + $entity->spawnTo($this); + } + } + foreach($chunk->getTiles() as $tile){ + if($tile instanceof Spawnable){ + $tile->spawnTo($this); + } + } } - if($this->chunkLoadCount >= 4 and $this->spawned === false and $this->teleportPosition === null){ - $this->doFirstSpawn(); + if(count($this->usedChunks) >= 4 and $this->spawned === false){ + + $this->bigBrother_setTitleBar(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother.", 0); + + $this->spawned = true; + + $pk = new SetTimePacket(); + $pk->time = $this->level->getTime(); + $pk->started = $this->level->stopTime == false; + $this->dataPacket($pk); + + $pos = $this->level->getSafeSpawn($this); + + $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); + + $this->teleport($ev->getRespawnPosition()); + + $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); + + $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, TextFormat::YELLOW . $this->getName() . " joined the game")); + if(strlen(trim($ev->getJoinMessage())) > 0){ + $this->server->broadcastMessage($ev->getJoinMessage()); + } + + $this->spawnToAll(); + + if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ + $this->server->getUpdater()->showPlayerUpdate($this); + } } + } - Timings::$playerChunkSendTimer->stopTiming(); + public function spawnTo(Player $player){ + if($player instanceof DesktopPlayer){ + if($this !== $player and $this->spawned === true and $player->getLevel() === $this->getLevel() and $player->canSee($this)){ + $this->hasSpawned[$player->getID()] = $player; + $pk = new SpawnPlayerPacket(); + if($player->getRemoveFormat()){ + $pk->name = TextFormat::clean($this->nameTag); + }else{ + $pk->name = $this->nameTag; + } + $pk->eid = $this->getID(); + $pk->uuid = $this->bigBrother_formatedUUID; + $pk->x = $this->x; + $pk->z = $this->y; + $pk->y = $this->z; + $pk->yaw = $this->yaw; + $pk->pitch = $this->pitch; + $pk->item = $this->inventory->getItemInHand()->getID(); + $pk->metadata = $this->getData(); + $pk->data = $this->bigBrother_properties; + $player->putRawPacket($pk); + + $pk = new EntityTeleportPacket(); + $pk->eid = $this->getID(); + $pk->x = $this->x; + $pk->z = $this->y; + $pk->y = $this->z; + $pk->yaw = $this->yaw; + $pk->pitch = $this->pitch; + $player->putRawPacket($pk); + + $pk = new SetEntityMotionPacket(); + $pk->eid = $this->getID(); + $pk->speedX = $this->motionX; + $pk->speedY = $this->motionY; + $pk->speedZ = $this->motionZ; + $player->dataPacket($pk); + + $this->inventory->sendHeldItem($player); + + $this->inventory->sendArmorContents($player); + } + }else{ + parent::spawnTo($player); + } } - public function bigBrother_authenticate($uuid, $onlineModeData = null){ - //echo "bigBrother_authenticate\n"; + public function bigBrother_authenticate($username, $uuid, $onlineModeData = null){ if($this->bigBrother_status === 0){ $this->bigBrother_uuid = $uuid; - $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toString(); + $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toBinary(); $pk = new LoginSuccessPacket(); $pk->uuid = $this->bigBrother_formatedUUID; - $pk->name = $this->bigBrother_username; + $pk->name = $this->username; $this->putRawPacket($pk); - $this->bigBrother_status = 1; - if($onlineModeData !== null and is_array($onlineModeData)){ $this->bigBrother_properties = $onlineModeData; } - foreach($this->bigBrother_properties as $property){ - if($property["name"] === "textures"){ - $skindata = json_decode(base64_decode($property["value"]), true); - if(isset($skindata["textures"]["SKIN"]["url"])){ - $skin = $this->getSkinImage($skindata["textures"]["SKIN"]["url"]); - } - } - } - - $pk = new LoginPacket(); - $pk->username = $this->bigBrother_username; - $pk->clientId = crc32($this->bigbrother_clientId); - $pk->protocol1 = Info::CURRENT_PROTOCOL; - $pk->protocol2 = Info::CURRENT_PROTOCOL; - $pk->clientUUID = UUID::fromString($uuid); - $pk->serverAddress = "127.0.0.1:25565"; - $pk->clientSecret = "BigBrother"; - if($skin === null or $skin === false){ - if($this->plugin->getConfig()->get("skin-slim")){ - $pk->skinname = "Standard_Custom"; - }else{ - $pk->skinname = "Standard_CustomSlim"; - } - $pk->skin = file_get_contents($this->plugin->getDataFolder().$this->plugin->getConfig()->get("skin-yml")); - }else{ - if(!isset($skindata["textures"]["SKIN"]["metadata"]["model"])){ - $pk->skinname = "Standard_Custom"; - }else{ - $pk->skinname = "Standard_CustomSlim"; - } - $pk->skin = $skin; - } - - $this->handleDataPacket($pk); - - $pk = new PlayerListPacket(); - $pk->actionID = PlayerListPacket::TYPE_ADD; - $pk->players[] = [ - $this->bigBrother_uuid, - $this->bigBrother_username, - $this->bigBrother_properties, - $this->getGamemode(), - 0, - false, - ]; - $this->putRawPacket($pk); - - $pk = new TitlePacket(); //Set SubTitle for this - $pk->actionID = TitlePacket::TYPE_SET_TITLE; - $pk->data = TextFormat::toJSON(""); - $this->putRawPacket($pk); - - $pk = new TitlePacket(); - $pk->actionID = TitlePacket::TYPE_SET_SUB_TITLE; - $pk->data = TextFormat::toJSON(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother."); - $this->putRawPacket($pk); - //echo "Done\n"; + $this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); + $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 1); } } public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionResponsePacket $packet){ - //echo "bigBrother_processAuthentication"; $this->bigBrother_secret = $plugin->decryptBinary($packet->sharedSecret); $token = $plugin->decryptBinary($packet->verifyToken); $this->interface->enableEncryption($this, $this->bigBrother_secret); if($token !== $this->bigBrother_checkToken){ - $this->close("", "Invalid check token"); + $this->kick("Invalid check token",false); }else{ - $this->getAuthenticateOnline($this->bigBrother_username, Binary::sha1("".$this->bigBrother_secret.$plugin->getASN1PublicKey())); + $task = new AuthenticateOnline($this->clientID, $this->bigBrother_username, Binary::sha1("" . $this->bigBrother_secret . $plugin->getASN1PublicKey())); + $this->server->getScheduler()->scheduleAsyncTask($task); } } - public function bigBrother_handleAuthentication($plugin, $username, $onlineMode = false){ - //echo "bigBrother_handleAuthentication"."\n"; - if($this->bigBrother_status === 0){ - $this->bigBrother_username = $username; - if($onlineMode === true){ - $pk = new EncryptionRequestPacket(); - $pk->serverID = ""; - $pk->publicKey = $plugin->getASN1PublicKey(); - $pk->verifyToken = $this->bigBrother_checkToken = Utils::getRandomBytes(4, false, true, $pk->publicKey); - //echo "EncryptionRequestPacket\n"; - $this->putRawPacket($pk); - }else{ - //echo "Getting profile...\n"; - $info = $this->getProfile($username); - if(is_array($info)){ - $this->bigBrother_authenticate($info["id"], $info["properties"]); + public function bigBrother_authenticationCallback($username){ + $pk = new LoginPacket(); + $pk->username = $username; + $pk->clientId = crc32($this->clientID); + $pk->protocol = Info::CURRENT_PROTOCOL; + $pk->clientUUID = UUID::fromString($this->bigBrother_uuid); + $pk->serverAddress = "127.0.0.1:25565"; + $pk->clientSecret = "BigBrother"; + + foreach($this->bigBrother_properties as $property){ + if($property["name"] === "textures"){ + $skindata = json_decode(base64_decode($property["value"]), true); + if(isset($skindata["textures"]["SKIN"]["url"])){ + $skin = $this->getSkinImage($skindata["textures"]["SKIN"]["url"]); } } } - } - - public function getProfile($username){ - $profile = json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); - if(!is_array($profile)){ - return false; - } - - $uuid = $profile["id"]; - $info = json_decode(Utils::getURL("https://sessionserver.mojang.com/session/minecraft/profile/".$uuid."", 3), true); - if(!is_array($info)){ - return false; - } - return $info; - } - - public function getAuthenticateOnline($username, $hash){ - $result = json_decode(Utils::getURL("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=".$username."&serverId=".$hash, 5), true); - if(is_array($result) and isset($result["id"])){ - $this->bigBrother_authenticate($result["id"], $result["properties"]); + if(!isset($skindata["textures"]["SKIN"]["metadata"]["model"])){ + $pk->skinId = "Standard_Custom"; }else{ - $this->close("", "User not premium"); + $pk->skinId = "Standard_CustomSlim"; } - } + $pk->skin = $skin; + $this->handleDataPacket($pk); + } public function getSkinImage($url){ if(extension_loaded("gd")){ $image = imagecreatefrompng($url); - if($image !== false){ $width = imagesx($image); $height = imagesy($image); @@ -376,8 +429,49 @@ public function getSkinImage($url){ return false; } + public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $onlineMode){ + if($this->bigBrother_status === 0){ + $this->bigBrother_username = $username; + if($onlineMode === true){ + $pk = new EncryptionRequestPacket(); + $pk->serverID = ""; + $pk->publicKey = $plugin->getASN1PublicKey(); + $pk->verifyToken = $this->bigBrother_checkToken = Utils::getRandomBytes(4, false, true, $pk->publicKey); + $this->putRawPacket($pk); + }else{ + /*$task = new OnlineProfile($this->clientID, $this->bigBrother_username, $this); + $this->server->getScheduler()->scheduleAsyncTask($task);*/ + + $profile = json_decode('{"id":"c96792ac7aea4f16975e535a20a2791a","name":"Hello"}',true);//json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); + if(!is_array($profile)){ + return false; + } + + $uuid = $profile["id"]; + $info = json_decode('{"id":"c96792ac7aea4f16975e535a20a2791a","name":"Hello","properties":[{"name":"textures","value":"eyJ0aW1lc3RhbXAiOjE0Njc1OTk2OTkyODQsInByb2ZpbGVJZCI6ImM5Njc5MmFjN2FlYTRmMTY5NzVlNTM1YTIwYTI3OTFhIiwicHJvZmlsZU5hbWUiOiJIZWxsbyIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9lYzNmMDc2MTliNmFjMzczMGZkYzMxZmExZWMxY2JkMmE4ZjhkZmJkOTdkYzhhYWE4ZTI0NWJhODVhZTlmNzYifX19"}]}', true); + if(!is_array($info)){ + return false; + } + $this->bigBrother_authenticate($this->bigBrother_username, $info["id"], $info["properties"]); + } + } + + } + + public function close($message = "", $reason = "generic reason",$notify = true){ + if($this->bigBrother_status === 0){ + $pk = new LoginDisconnectPacket(); + $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); + $this->putRawPacket($pk); + }else{ + $pk = new PlayDisconnectPacket(); + $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); + $this->putRawPacket($pk); + } + parent::close($message, $reason); + } + public function bigBrother_setCompression($threshold){ - $this->threshold = $threshold; $this->interface->setCompression($this, $threshold); } diff --git a/src/shoghicp/BigBrother/network/Info.php b/src/shoghicp/BigBrother/network/Info.php index 75e6578c..6c13bcc0 100755 --- a/src/shoghicp/BigBrother/network/Info.php +++ b/src/shoghicp/BigBrother/network/Info.php @@ -18,12 +18,6 @@ namespace shoghicp\BigBrother\network; abstract class Info{ - - /** - * Actual Minecraft protocol version - */ - - const VERSION = "1.10.2"; - const PROTOCOL = 210; - + const VERSION = "1.8"; + const PROTOCOL = 47; } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/Packet.php b/src/shoghicp/BigBrother/network/Packet.php index 8834ff53..a5185cca 100755 --- a/src/shoghicp/BigBrother/network/Packet.php +++ b/src/shoghicp/BigBrother/network/Packet.php @@ -17,136 +17,62 @@ namespace shoghicp\BigBrother\network; -use pocketmine\item\Item; +use pocketmine\utils\BinaryStream; +use pocketmine\utils\Utils; use shoghicp\BigBrother\utils\Binary; -abstract class Packet extends \stdClass{ +abstract class Packet extends BinaryStream{ - protected $buffer; - protected $offset = 0; + const NETWORK_ID = 0; - protected function get($len){ - if($len < 0){ - $this->offset = strlen($this->buffer) - 1; + public $isEncoded = false; + private $channel = 0; - return ""; - }elseif($len === true){ - return substr($this->buffer, $this->offset); - } - - $buffer = ""; - for(; $len > 0; --$len, ++$this->offset){ - $buffer .= @$this->buffer{$this->offset}; - } - - return $buffer; - } - - protected function getLong(){ - return Binary::readLong($this->get(8)); - } - - protected function getInt(){ - return Binary::readInt($this->get(4)); + public function pid(){ + return $this::NETWORK_ID; } - protected function getPosition(&$x, &$y, &$z){ - $int1 = $this->getInt(); - $int2 = $this->getInt(); - - $x = $int1 >> 6; - $y = ((($int1 & 0x3F) << 2) | ($int2 & 0xFCFFFFFF) >> 26); - $z = $int2 & 0x3FFFFFF; - - if(PHP_INT_MAX > 0x7FFFFFFF){ - $x = $x << 38 >> 38; - $y = $y << 58 >> 58; - $z = $z << 38 >> 38; - }else{ - $x = $x << 6 >> 6; - $y = $y << 26 >> 26; - $z = $z << 6 >> 6; - } - } + abstract public function encode(); - protected function getFloat(){ - return Binary::readFloat($this->get(4)); - } + abstract public function decode(); - protected function getDouble(){ - return Binary::readDouble($this->get(8)); + public function reset(){ + $this->buffer = chr($this::NETWORK_ID); + $this->offset = 0; } /** - * @return Item + * @deprecated This adds extra overhead on the network, so its usage is now discouraged. It was a test for the viability of this. */ - protected function getSlot(){ - $itemId = $this->getShort(); - if($itemId === 65535){ //Empty - return Item::get(Item::AIR, 0, 0); - }else{ - $count = $this->getByte(); - $damage = $this->getShort(); - $len = $this->getByte(); - $nbt = ""; - if($len > 0){ - $nbt = $this->get($len); - } - return Item::get($itemId, $damage, $count, $nbt); - } - } - - protected function putSlot(Item $item){ - if($item->getID() === 0){ - $this->putShort(-1); - }else{ - $this->putShort($item->getID()); - $this->putByte($item->getCount()); - $this->putShort($item->getDamage()); - $nbt = $item->getCompoundTag(); - $this->putByte(strlen($nbt)); - $this->put($nbt); - } - } - - protected function getShort(){ - return Binary::readShort($this->get(2)); - } - - protected function getTriad(){ - return Binary::readTriad($this->get(3)); - } - - protected function getLTriad(){ - return Binary::readTriad(strrev($this->get(3))); - } - - protected function getByte(){ - return ord($this->buffer{$this->offset++}); - } - - protected function getString(){ - return $this->get($this->getVarInt()); + public function setChannel($channel){ + $this->channel = (int) $channel; + return $this; } - protected function getVarInt(){ - return Binary::readVarInt($this->buffer, $this->offset); + public function getChannel(){ + return $this->channel; } - protected function feof(){ - return !isset($this->buffer{$this->offset}); - } - - protected function put($str){ - $this->buffer .= $str; - } - - protected function putLong($v){ - $this->buffer .= Binary::writeLong($v); - } + public function clean(){ + $this->buffer = null; + $this->isEncoded = false; + $this->offset = 0; + return $this; + } + + public function __debugInfo(){ + $data = []; + foreach($this as $k => $v){ + if($k === "buffer"){ + $data[$k] = bin2hex($v); + }elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){ + $data[$k] = Utils::printable((string) $v); + }else{ + $data[$k] = $v; + } + } - protected function putInt($v){ - $this->buffer .= Binary::writeInt($v); + return $data; } protected function putPosition($x, $y, $z){ @@ -157,56 +83,23 @@ protected function putPosition($x, $y, $z){ $this->buffer .= Binary::writeInt($int1) . Binary::writeInt($int2); } - protected function putFloat($v){ - $this->buffer .= Binary::writeFloat($v); - } - protected function putDouble($v){ $this->buffer .= Binary::writeDouble($v); } - protected function putShort($v){ - $this->buffer .= Binary::writeShort($v); - } - - protected function putTriad($v){ - $this->buffer .= Binary::writeTriad($v); - } - - protected function putLTriad($v){ - $this->buffer .= strrev(Binary::writeTriad($v)); - } - - protected function putByte($v){ - $this->buffer .= chr($v); - } - - protected function putString($v){ - $this->putVarInt(strlen($v)); - $this->put($v); - } - protected function putVarInt($v){ $this->buffer .= Binary::writeVarInt($v); } - public abstract function pid(); - - protected abstract function encode(); - - protected abstract function decode(); - public function write(){ - $this->buffer = ""; - $this->offset = 0; + $this->reset(); $this->encode(); return Binary::writeVarInt($this->pid()) . $this->buffer; } - public function read($buffer, $offset = 0){ $this->buffer = $buffer; $this->offset = $offset; $this->decode(); } - + } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index a6340af0..513a6e05 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -20,32 +20,20 @@ use pocketmine\network\protocol\DataPacket; use pocketmine\network\SourceInterface; use pocketmine\Player; +use pocketmine\utils\TextFormat; use shoghicp\BigBrother\BigBrother; use shoghicp\BigBrother\DesktopPlayer; -use shoghicp\BigBrother\network\Info; //Computer Edition -use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\network\protocol\Login\EncryptionResponsePacket; -use shoghicp\BigBrother\network\protocol\Login\LoginStartPacket; -use shoghicp\BigBrother\network\protocol\Play\AnimatePacket; -use shoghicp\BigBrother\network\protocol\Play\ClientSettingsPacket; -use shoghicp\BigBrother\network\protocol\Play\ClientStatusPacket; -use shoghicp\BigBrother\network\protocol\Play\CreativeInventoryActionPacket; -use shoghicp\BigBrother\network\protocol\Play\CPlayerAbilitiesPacket; -use shoghicp\BigBrother\network\protocol\Play\CTSChatPacket; -use shoghicp\BigBrother\network\protocol\Play\CTSCloseWindowPacket; -use shoghicp\BigBrother\network\protocol\Play\HeldItemChangePacket; -use shoghicp\BigBrother\network\protocol\Play\KeepAlivePacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerArmSwingPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerBlockPlacementPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerDiggingPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerLookPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerPositionAndLookPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerPositionPacket; -use shoghicp\BigBrother\network\protocol\Play\PluginMessagePacket; -use shoghicp\BigBrother\network\protocol\Play\ResourcePackStatusPacket; -use shoghicp\BigBrother\network\protocol\Play\CTabCompletePacket; -use shoghicp\BigBrother\network\protocol\Play\UseEntityPacket; +use shoghicp\BigBrother\network\protocol\ClientStatusPacket; +use shoghicp\BigBrother\network\protocol\CTSChatPacket; +use shoghicp\BigBrother\network\protocol\CTSCloseWindowPacket; +use shoghicp\BigBrother\network\protocol\EncryptionResponsePacket; +use shoghicp\BigBrother\network\protocol\LoginStartPacket; +use shoghicp\BigBrother\network\protocol\PlayerBlockPlacementPacket; +use shoghicp\BigBrother\network\protocol\PlayerDiggingPacket; +use shoghicp\BigBrother\network\protocol\PlayerLookPacket; +use shoghicp\BigBrother\network\protocol\PlayerPositionAndLookPacket; +use shoghicp\BigBrother\network\protocol\PlayerPositionPacket; +use shoghicp\BigBrother\network\protocol\UseEntityPacket; use shoghicp\BigBrother\network\translation\Translator; use shoghicp\BigBrother\utils\Binary; @@ -69,11 +57,10 @@ class ProtocolInterface implements SourceInterface{ protected $identifier = 0; - public function __construct(BigBrother $plugin, $server, Translator $translator){ + public function __construct(BigBrother $plugin, ServerThread $thread, Translator $translator){ $this->plugin = $plugin; - $this->server = $server; $this->translator = $translator; - $this->thread = new ServerThread($server->getLogger(), $server->getLoader(), $plugin->getPort(), $plugin->getIp(), $plugin->getMotd(), $plugin->getDataFolder()."server-icon.png"); + $this->thread = $thread; $this->sessions = new \SplObjectStorage(); } @@ -82,42 +69,31 @@ public function emergencyShutdown(){ } public function shutdown(){ - $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_SHUTDOWN)); + foreach($this->sessionsPlayers as $player){ + $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", $this->plugin->getServer()->getProperty("settings.shutdown-message", "Server closed")); + } + $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_SHUTDOWN)); } public function setName($name){ - $info = $this->plugin->getServer()->getQueryInformation(); - $value = [ - "MaxPlayers" => $info->getMaxPlayerCount(), - "OnlinePlayers" => $info->getPlayerCount(), - ]; - $buffer = chr(ServerManager::PACKET_SET_OPTION).chr(strlen("name"))."name".json_encode($value); - $this->thread->pushMainToThreadPacket($buffer); - } - public function closeSession($identifier){ - if(isset($this->sessionsPlayers[$identifier])){ - $player = $this->sessionsPlayers[$identifier]; - unset($this->sessionsPlayers[$identifier]); - $player->close($player->getLeaveMessage(), "Connection closed"); - } } public function close(Player $player, $reason = "unknown reason"){ if(isset($this->sessions[$player])){ $identifier = $this->sessions[$player]; $this->sessions->detach($player); - unset($this->identifiers[$identifier]); - $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_CLOSE_SESSION) . Binary::writeInt($identifier)); + unset($this->sessionsPlayers[$identifier]); + $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", "Connection closed"); }else{ return; } + $this->thread->pushMainToThreadPacket(chr(ServerManager::PACKET_CLOSE_SESSION) . Binary::writeInt($identifier)); } protected function sendPacket($target, Packet $packet){ $data = chr(ServerManager::PACKET_SEND_PACKET) . Binary::writeInt($target) . $packet->write(); $this->thread->pushMainToThreadPacket($data); - //echo "pushMainToThreadPacket: ".chr(ServerManager::PACKET_SEND_PACKET)."\n"; } public function setCompression(DesktopPlayer $player, $threshold){ @@ -131,7 +107,6 @@ public function setCompression(DesktopPlayer $player, $threshold){ public function enableEncryption(DesktopPlayer $player, $secret){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; - $data = chr(ServerManager::PACKET_ENABLE_ENCRYPTION) . Binary::writeInt($target) . $secret; $this->thread->pushMainToThreadPacket($data); } @@ -140,13 +115,12 @@ public function enableEncryption(DesktopPlayer $player, $secret){ public function putRawPacket(DesktopPlayer $player, Packet $packet){ if(isset($this->sessions[$player])){ $target = $this->sessions[$player]; - //echo "sendPacket\n"; $this->sendPacket($target, $packet); } } public function putPacket(Player $player, DataPacket $packet, $needACK = false, $immediate = true){ - $id = 1; + $id = 0; if($needACK){ $id = $this->identifier++; $this->identifiers[$id] = $player; @@ -181,24 +155,18 @@ protected function receivePacket(DesktopPlayer $player, Packet $packet){ protected function handlePacket(DesktopPlayer $player, $payload){ $pid = ord($payload{0}); - $offset = 1; + $offset = 0; $status = $player->bigBrother_getStatus(); - if($status === 1){ + switch($pid){ - case 0x00: - $pk = new KeepAlivePacket(); - break; case 0x01: $pk = new CTSChatPacket(); break; case 0x02: $pk = new UseEntityPacket(); break; - case 0x03: - $pk = new PlayerPacket(); - break; case 0x04: $pk = new PlayerPositionPacket(); break; @@ -214,62 +182,17 @@ protected function handlePacket(DesktopPlayer $player, $payload){ case 0x08: $pk = new PlayerBlockPlacementPacket(); break; - case 0x09: - $pk = new HeldItemChangePacket(); - break; - case 0x0a: - $pk = new PlayerArmSwingPacket(); - break; - case 0x0b: - $pk = new AnimatePacket(); - break; - /*case 0x0c: - // - break;*/ case 0x0d: $pk = new CTSCloseWindowPacket(); break; - /*case 0x0e: - break; - case 0x0f: - - break;*/ - case 0x10: - $pk = new CreativeInventoryActionPacket(); - break; - /*case 0x11: - - break; - case 0x12: - - break;*/ - case 0x13: - $pk = new CPlayerAbilitiesPacket(); - break; - case 0x14: - $pk = new CTabCompletePacket(); - break; - case 0x15: - $pk = new ClientSettingsPacket(); - break; case 0x16: $pk = new ClientStatusPacket(); break; - case 0x17: - $pk = new PluginMessagePacket(); - break; - /*case 0x18: - // - break;*/ - case 0x19: - $pk = new ResourcePackStatusPacket(); - break; default: - echo "[Receive] 0x".bin2hex(chr($pid))."\n"; //Debug return; } - //echo "Receive\n"; + $pk->read($payload, $offset); $this->receivePacket($player, $pk); }elseif($status === 0){ @@ -282,7 +205,7 @@ protected function handlePacket(DesktopPlayer $player, $payload){ $pk->read($payload, $offset); $player->bigBrother_processAuthentication($this->plugin, $pk); }else{ - $player->close($player->getLeaveMessage(), "Unexpected packet $pid"); + $player->close(TextFormat::YELLOW . $player->getName() . " has left the game", "Unexpected packet $pid"); } } } @@ -297,25 +220,16 @@ public function process(){ while(strlen($buffer = $this->thread->readThreadToMainPacket()) > 0){ $offset = 1; $pid = ord($buffer{0}); - //echo "pid: ".$pid."\n"; if($pid === ServerManager::PACKET_SEND_PACKET){ - //echo "PACKET_SEND_PACKET\n"; $id = Binary::readInt(substr($buffer, $offset, 4)); $offset += 4; if(isset($this->sessionsPlayers[$id])){ $payload = substr($buffer, $offset); try{ $this->handlePacket($this->sessionsPlayers[$id], $payload); - }catch(\Exception $e){ - if(\pocketmine\DEBUG > 1){ - $logger = $this->server->getLogger(); - if($logger instanceof MainLogger){ - $logger->debug("DesktopPacket 0x" . bin2hex($payload)); - $logger->logException($e); - } - } + } } }elseif($pid === ServerManager::PACKET_OPEN_SESSION){ @@ -335,24 +249,17 @@ public function process(){ $this->sessions->attach($player, $id); $this->sessionsPlayers[$id] = $player; $this->plugin->getServer()->addPlayer($identifier, $player); - //echo "PACKET_OPEN_SESSION\n"; }elseif($pid === ServerManager::PACKET_CLOSE_SESSION){ $id = Binary::readInt(substr($buffer, $offset, 4)); - $offset += 4; - $flag = Binary::readInt(substr($buffer, $offset, 4)); - - if(isset($this->sessionsPlayers[$id])){ - if($flag === 0){ - $this->close($this->sessionsPlayers[$id]); - }else{ - $this->closeSession($id); - } + if(!isset($this->sessionsPlayers[$id])){ + continue; } + $this->close($this->sessionsPlayers[$id]); } } return true; - } + } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/ServerManager.php b/src/shoghicp/BigBrother/network/ServerManager.php index 845aed9b..0180090a 100755 --- a/src/shoghicp/BigBrother/network/ServerManager.php +++ b/src/shoghicp/BigBrother/network/ServerManager.php @@ -17,6 +17,7 @@ namespace shoghicp\BigBrother\network; +use pocketmine\utils\TextFormat; use shoghicp\BigBrother\utils\Binary; class ServerManager{ @@ -64,8 +65,6 @@ class ServerManager{ */ const PACKET_SET_COMPRESSION = 0x05; - const PACKET_SET_OPTION = 0x06; - /* * no payload */ @@ -88,14 +87,12 @@ class ServerManager{ protected $logger; protected $shutdown = false; + public $players = 0; + public $maxPlayers = 20; /** @var string[] */ public $sample = []; public $description; public $favicon; - public $serverdata = [ - "MaxPlayers" => 20, - "OnlinePlayers" => 0, - ]; public function __construct(ServerThread $thread, $port, $interface, $description = "", $favicon = null){ $this->thread = $thread; @@ -126,16 +123,6 @@ public function __construct(ServerThread $thread, $port, $interface, $descriptio $this->process(); } - public function getServerData(){ - return $this->serverdata; - } - - public function shutdown(){ - $this->thread->shutdown(); - usleep(50000); //Sleep for 1 tick - //$this->thread->quit(); - } - protected function processPacket(){ @fread($this->fp, 1); if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){ @@ -148,7 +135,7 @@ protected function processPacket(){ $data = substr($buffer, 4); if(!isset($this->sessions[$id])){ - $this->closeSession($id, 0); + $this->closeSession($id); return true; } $this->sessions[$id]->writeRaw($data); @@ -157,7 +144,7 @@ protected function processPacket(){ $secret = substr($buffer, 4); if(!isset($this->sessions[$id])){ - $this->closeSession($id, 0); + $this->closeSession($id); return true; } $this->sessions[$id]->enableEncryption($secret); @@ -166,36 +153,20 @@ protected function processPacket(){ $threshold = Binary::readInt(substr($buffer, 4, 4)); if(!isset($this->sessions[$id])){ - $this->closeSession($id, 0); + $this->closeSession($id); return true; } $this->sessions[$id]->setCompression($threshold); - }elseif($pid === self::PACKET_SET_OPTION){ - $offset = 1; - $len = ord($packet{$offset++}); - $name = substr($packet, $offset, $len); - $offset += $len; - $value = substr($packet, $offset); - switch($name){ - case "name": - $this->serverdata = json_decode($value, true); - break; - } }elseif($pid === self::PACKET_CLOSE_SESSION){ $id = Binary::readInt(substr($buffer, 0, 4)); if(isset($this->sessions[$id])){ $this->close($this->sessions[$id]); - }else{ - $this->closeSession($id, 1); } }elseif($pid === self::PACKET_SHUTDOWN){ + $this->shutdown = true; foreach($this->sessions as $session){ $session->close(); } - - $this->shutdown(); - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); - $this->shutdown = true; }elseif($pid === self::PACKET_EMERGENCY_SHUTDOWN){ $this->shutdown = true; } @@ -215,8 +186,8 @@ public function openSession(Session $session){ $this->thread->pushThreadToMainPacket($data); } - protected function closeSession($id, $flag){ - $this->thread->pushThreadToMainPacket(chr(self::PACKET_CLOSE_SESSION) . Binary::writeInt($id).Binary::writeInt($flag)); + protected function closeSession($id){ + $this->thread->pushThreadToMainPacket(chr(self::PACKET_CLOSE_SESSION) . Binary::writeInt($id)); } private function process(){ @@ -266,7 +237,6 @@ public function close(Session $session){ fclose($this->sockets[$identifier]); unset($this->sockets[$identifier]); unset($this->sessions[$identifier]); - $this->closeSession($identifier, 0); + $this->closeSession($identifier); } - } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/ServerThread.php b/src/shoghicp/BigBrother/network/ServerThread.php index 2fb39208..7c210f7e 100755 --- a/src/shoghicp/BigBrother/network/ServerThread.php +++ b/src/shoghicp/BigBrother/network/ServerThread.php @@ -52,9 +52,7 @@ class ServerThread extends Thread{ * * @throws \Exception */ - public function __construct(\ThreadedLogger $logger, \ClassLoader $loader, $port, $interface = "0.0.0.0", $motd = "Minecraft: PE server", $icon = null){ - $this->externalQueue = new \Threaded; - $this->internalQueue = new \Threaded; + public function __construct(\Threaded $externalQueue, \Threaded $internalQueue, \ThreadedLogger $logger, \ClassLoader $loader, $port, $interface = "0.0.0.0", $motd = "Minecraft: PE server", $icon = null){ $this->port = (int) $port; if($port < 1 or $port > 65536){ throw new \Exception("Invalid port range"); @@ -82,6 +80,9 @@ public function __construct(\ThreadedLogger $logger, \ClassLoader $loader, $port $this->externalSocket = $sockets[1]; stream_set_blocking($this->externalSocket, 0); + $this->externalQueue = $externalQueue; + $this->internalQueue = $internalQueue; + $this->start(); } @@ -104,13 +105,10 @@ public function isShutdown(){ } public function shutdown(){ + $this->lock(); $this->shutdown = true; - } - - public function shutdownHandler(){ - if($this->shutdown !== true){ - $this->getLogger()->emergency("[ServerThread #". \Thread::getCurrentThreadId() ."] ServerThread crashed!"); - } + socket_close($this->internalSocket); + $this->unlock(); } public function getPort(){ @@ -152,8 +150,7 @@ public function pushMainToThreadPacket($str){ } public function readMainToThreadPacket(){ - $var = $this->internalQueue->shift(); - return $var; + return $this->internalQueue->shift(); } public function pushThreadToMainPacket($str){ @@ -172,9 +169,6 @@ public function run(){ } } $this->loader->register(); - - register_shutdown_function([$this, "shutdownHandler"]); - $data = unserialize($this->data); $manager = new ServerManager($this, $this->port, $this->interface, $data["motd"], $data["icon"]); } diff --git a/src/shoghicp/BigBrother/network/Session.php b/src/shoghicp/BigBrother/network/Session.php index de81c57a..9658b2fa 100755 --- a/src/shoghicp/BigBrother/network/Session.php +++ b/src/shoghicp/BigBrother/network/Session.php @@ -18,13 +18,11 @@ namespace shoghicp\BigBrother\network; use pocketmine\utils\TextFormat; -use shoghicp\BigBrother\network\protocol\Login\LoginDisconnectPacket; -use shoghicp\BigBrother\network\protocol\Login\PingPacket; +use shoghicp\BigBrother\network\protocol\LoginDisconnectPacket; +use shoghicp\BigBrother\network\protocol\PingPacket; use shoghicp\BigBrother\utils\AES; use shoghicp\BigBrother\utils\Binary; -use shoghicp\BigBrother\BigBrother; - class Session{ /** @var ServerManager */ private $manager; @@ -172,8 +170,8 @@ public function process(){ "protocol" => Info::PROTOCOL ], "players" => [ - "max" => $this->manager->getServerData()["MaxPlayers"], - "online" => $this->manager->getServerData()["OnlinePlayers"], + "max" => $this->manager->maxPlayers, + "online" => $this->manager->players, "sample" => $sample, ], "description" => json_decode(TextFormat::toJSON($this->manager->description)) @@ -225,6 +223,7 @@ public function process(){ $this->close("Unexpected packet $pid"); } } + } public function getID(){ diff --git a/src/shoghicp/BigBrother/network/protocol/Play/BlockChangePacket.php b/src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/BlockChangePacket.php rename to src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php index dc56cc65..a3e07c30 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/BlockChangePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/BlockChangePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CTSChatPacket.php b/src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/CTSChatPacket.php rename to src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php index af15ae4f..da7496ae 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/CTSChatPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/CTSChatPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CTSCloseWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/CTSCloseWindowPacket.php rename to src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php index 0f1fb04c..812bc5b6 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/CTSCloseWindowPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/CTSCloseWindowPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ChangeGameStatePacket.php b/src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/ChangeGameStatePacket.php rename to src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php index c3a75b82..b6c0f1c3 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/ChangeGameStatePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/ChangeGameStatePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ChunkDataPacket.php b/src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/ChunkDataPacket.php rename to src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php index 0fb7eb11..e7b89299 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/ChunkDataPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/ChunkDataPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ClientStatusPacket.php b/src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php similarity index 90% rename from src/shoghicp/BigBrother/network/protocol/Play/ClientStatusPacket.php rename to src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php index 465d834a..95e4e756 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/ClientStatusPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/ClientStatusPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; @@ -32,6 +32,6 @@ public function encode(){ } public function decode(){ - $this->actionID = $this->getVarInt(); + $this->actionID = $this->getByte(); } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/DestroyEntitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/DestroyEntitiesPacket.php rename to src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php index dd37085a..f9081ec9 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/DestroyEntitiesPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/DestroyEntitiesPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Login/EncryptionRequestPacket.php b/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Login/EncryptionRequestPacket.php rename to src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php index 6aeee924..7f4b17fe 100755 --- a/src/shoghicp/BigBrother/network/protocol/Login/EncryptionRequestPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Login; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Login/EncryptionResponsePacket.php b/src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Login/EncryptionResponsePacket.php rename to src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php index bfe8b93c..506011dc 100755 --- a/src/shoghicp/BigBrother/network/protocol/Login/EncryptionResponsePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EncryptionResponsePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Login; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityHeadLookPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/EntityHeadLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php index 03445d32..df5b3d0c 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/EntityHeadLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EntityHeadLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityMetadataPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/EntityMetadataPacket.php rename to src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php index d8ce5a51..f69ee12e 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/EntityMetadataPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EntityMetadataPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityTeleportPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/EntityTeleportPacket.php rename to src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php index 1174e5de..4f3199aa 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/EntityTeleportPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EntityTeleportPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityVelocityPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php similarity index 91% rename from src/shoghicp/BigBrother/network/protocol/Play/EntityVelocityPacket.php rename to src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php index 1971b6ec..46445b41 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/EntityVelocityPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EntityVelocityPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; @@ -32,7 +32,7 @@ public function pid(){ } public function encode(){ - $this->putVarInt($this->eid); + $this->put(Binary::writeVarInt($this->eid)); $this->putShort($this->velocityX * 8000); $this->putShort($this->velocityY * 8000); $this->putShort($this->velocityZ * 8000); diff --git a/src/shoghicp/BigBrother/network/protocol/Play/JoinGamePacket.php b/src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/JoinGamePacket.php rename to src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php index 71ae1a20..8c4405f2 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/JoinGamePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/JoinGamePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/KeepAlivePacket.php b/src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/KeepAlivePacket.php rename to src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php index d20445cb..78037d2f 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/KeepAlivePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/KeepAlivePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Login/LoginDisconnectPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Login/LoginDisconnectPacket.php rename to src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php index f28b9f73..5a6f24bd 100755 --- a/src/shoghicp/BigBrother/network/protocol/Login/LoginDisconnectPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/LoginDisconnectPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Login; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Login/LoginStartPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Login/LoginStartPacket.php rename to src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php index 347c01d3..4a3f681e 100755 --- a/src/shoghicp/BigBrother/network/protocol/Login/LoginStartPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/LoginStartPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Login; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Login/LoginSuccessPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php similarity index 91% rename from src/shoghicp/BigBrother/network/protocol/Login/LoginSuccessPacket.php rename to src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php index 3ff7c659..2feb7e65 100755 --- a/src/shoghicp/BigBrother/network/protocol/Login/LoginSuccessPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Login; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; @@ -29,7 +29,7 @@ public function pid(){ } public function encode(){ - $this->putString($this->uuid); + $this->put($this->uuid); $this->putString($this->name); } diff --git a/src/shoghicp/BigBrother/network/protocol/Play/OpenWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/Play/OpenWindowPacket.php rename to src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php index 9936c28b..3c8f0d37 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/OpenWindowPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/OpenWindowPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Login/PingPacket.php b/src/shoghicp/BigBrother/network/protocol/PingPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Login/PingPacket.php rename to src/shoghicp/BigBrother/network/protocol/PingPacket.php index 0b72086e..22bc4c64 100755 --- a/src/shoghicp/BigBrother/network/protocol/Login/PingPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PingPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Login; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php deleted file mode 100755 index 2625c9cf..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/AnimatePacket.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class AnimatePacket extends Packet{ - - - - public $eid; - public $action; - public $jump; - - public function pid(){ - return 0x0b; - } - - public function encode(){ - $this->putVarInt($this->eid); - $this->putByte($this->actionID); - } - - public function decode(){ - $this->eid = $this->getVarInt(); - $this->actionID = $this->getVarInt(); - $this->jump = $this->getVarInt(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php deleted file mode 100755 index 3246d724..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/CPlayerAbilitiesPacket.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class CPlayerAbilitiesPacket extends Packet{ - - public $damageDisabled; - public $canFly; - public $isFlying = false; - public $isCreative; - - public $flyingSpeed; - public $walkingSpeed; - - public function pid(){ - return 0x13; - } - - public function encode(){ - } - - public function decode(){ - $flags = base_convert($this->getByte(), 10, 2); - if(strlen($flags) !== 8){ - $flags = str_repeat("0", 8 - strlen($flags)).$flags; - } - $flags = intval($flags); - - if(($flags & 0x08) !== 0){ - $this->damageDisabled = true; - }else{ - $this->damageDisabled = false; - } - - if(($flags & 0x04) !== 0){ - $this->canFly = true; - }else{ - $this->canFly = false; - } - - if(($flags & 0x02) !== 0){ - $this->isFlying = true; - }else{ - $this->isFlying = false; - } - - if(($flags & 0x01) !== 0){ - $this->isCreative = true; - }else{ - $this->isCreative = false; - } - - $this->flyingSpeed = $this->getFloat(); - $this->walkingSpeed = $this->getFloat(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php deleted file mode 100755 index 0feeb390..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/CTabCompletePacket.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class CTabCompletePacket extends Packet{ - - public $text; - public $x; - public $y; - public $z; - - public function pid(){ - return 0x14; - } - - public function encode(){ - } - - public function decode(){ - $this->text = $this->getString(); - $flag = (bool) $this->getByte(); - if($flag){ - $this->getPosition($this->x, $this->y, $this->z); - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php deleted file mode 100755 index cd90c039..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/ClickWindowPacket.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class ClickWindowPacket extends Packet{ - - public $windowID; - public $slot; - public $button; - public $actionID; - public $mode; - public $clickedItem; - - public function pid(){ - return 0x0e; - } - - public function encode(){ - - } - - public function decode(){ - $this->windowID = $this->getByte(); - $this->slot = $this->getShort(); - $this->button = $this->getByte(); - $this->actionID = $this->getShort(); - $this->mode = $this->getByte(); - $this->clickedItem = $this->getSlot(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php deleted file mode 100755 index a8c74557..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/ClientSettingsPacket.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class ClientSettingsPacket extends Packet{ - - public $lang; - public $view; - public $chatmode; - public $chatcolor; - public $skinsetting; - - public function pid(){ - return 0x15; - } - - public function encode(){ - - } - - public function decode(){ - $this->lang = $this->getString(); - $this->view = $this->getByte(); - $this->chatmode = $this->getByte(); - $this->chatcolor = (bool) $this->getByte(); - $this->skinsetting = base_convert($this->getByte(), 10, 2); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php deleted file mode 100755 index baf4cb25..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/CreativeInventoryActionPacket.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class CreativeInventoryActionPacket extends Packet{ - - public $slot; - public $item; - - public function pid(){ - return 0x10; - } - - public function encode(){ - - } - - public function decode(){ - $this->slot = $this->getShort(); - $this->item = $this->getSlot(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php deleted file mode 100755 index 04b08e31..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/EntityEquipmentPacket.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class EntityEquipmentPacket extends Packet{ - - public $eid; - public $slot; - public $item; - - public function pid(){ - return 0x04; - } - - public function encode(){ - $this->putVarInt($this->eid); - $this->putShort($this->slot); - $this->putSlot($this->item); - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php deleted file mode 100755 index 018ef4e8..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/EntityPacket.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class EntityPacket extends Packet{ - - public $eid; - - public function pid(){ - return 0x14; - } - - public function encode(){ - $this->putVarInt($this->eid); - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php deleted file mode 100755 index c5fde031..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/HeldItemChangePacket.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\utils\Binary; - -class HeldItemChangePacket extends Packet{ - - public $selectedSlot; - - public function pid(){ - return 0x09; - } - - public function encode(){ - } - - public function decode(){ - $this->selectedSlot = $this->getShort(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php deleted file mode 100755 index db63950a..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerArmSwingPacket.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class PlayerArmSwingPacket extends Packet{ - - public function pid(){ - return 0x0a; - } - - public function encode(){ - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php deleted file mode 100755 index 889d2f12..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerListPacket.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\utils\Binary; - -class PlayerListPacket extends Packet{ - - const TYPE_ADD = 0; - const TYPE_REMOVE = 4; - - public $actionID; - public $players = []; - - public function pid(){ - return 0x38; - } - - public function clean(){ - $this->players = []; - return parent::clean(); - } - - public function encode(){ - $this->putVarInt($this->actionID); - $this->putVarInt(count($this->players)); - foreach($this->players as $player){ - if($this->actionID === self::TYPE_ADD){//add Player - $this->putLong(substr($player[0], 0, 16));//UUID - $this->putLong(substr($player[0], 16, 16)); - $this->putString($player[1]); //PlayerName - $this->putVarInt(count($player[2])); //Count Peropetry - - foreach($player[2] as $peropetrydata){ - $this->putString($peropetrydata["name"]); //Name - $this->putString($peropetrydata["value"]); //Value - if(isset($peropetrydata["signature"])){ - $this->putByte(1); //Is Signed - $this->putString($peropetrydata["signature"]); //Peropetry - }else{ - $this->putByte(0); //Is Signed - } - } - $this->putVarInt($player[3]); //Gamemode - $this->putVarInt($player[4]); //Ping - $this->putByte($player[5] ? 1 : 0); //has Display name - if($player[5] === true){ - $this->putString($player[6]); //Display name - } - }else{ - $this->putLong(substr($player[0], 0, 16));//UUID - $this->putLong(substr($player[0], 16, 16)); - } - } - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php deleted file mode 100755 index c2ac93bb..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPacket.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class PlayerPacket extends Packet{ - - public $onGround; - - public function pid(){ - return 0x03; - } - - public function encode(){ - - } - - public function decode(){ - $this->onGround = (bool) $this->getByte(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php deleted file mode 100755 index 05bea7e7..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/PluginMessagePacket.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class PluginMessagePacket extends Packet{ - - public $channel; - public $data = []; - - public function pid(){ - return 0x17; - } - - public function encode(){ - } - - public function decode(){ - $this->channel = $this->getString(); - switch($this->channel){ - case "REGISTER": - $channels = bin2hex($this->getString()); - $channels = str_split($channels, 2); - $string = ""; - foreach($channels as $num => $str){ - if($str === "00"){ - $this->data[] = hex2bin($string); - $string = ""; - }else{ - $string .= $str; - if(count($channels) -1 === $num){ - $this->data[] = hex2bin($string); - } - } - } - break; - case "MC|Brand": - $this->data = $this->getString(); - break; - } - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php deleted file mode 100755 index 378c01ff..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackSendPacket.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class ResourcePackSendPacket extends Packet{ - - public $url; - - public function pid(){ - return 0x48; - } - - public function encode(){ - $this->putString($this->url); - $this->putString(sha1($this->url)); - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php deleted file mode 100755 index 4c751bcc..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/ResourcePackStatusPacket.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class ResourcePackStatusPacket extends Packet{ - - public $hash; - public $status; - - public function pid(){ - return 0x19; - } - - public function encode(){ - - } - - public function decode(){ - $this->hash = $this->getString(); - $this->status = $this->getVarInt(); - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php deleted file mode 100755 index e7508022..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/STabCompletePacket.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class STabCompletePacket extends Packet{ - - public $matches = []; - - public function pid(){ - return 0x3a; - } - - public function encode(){ - $this->putVarInt(count($this->matches)); - foreach($this->matches as $match){ - $this->putString($match); - } - } - - public function decode(){ - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php deleted file mode 100755 index 5509e270..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/ScoreboardObjectivePacket.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class ScoreboardObjectivePacket extends Packet{ - - public $ObjectiveName; - public $Mode; - public $ObjectiveValue; - public $Type; - - public function pid(){ - return 0x3b; - } - - public function encode(){ - $this->putString($this->ObjectiveName); - $this->putByte($this->Mode); - $this->putString($this->ObjectiveValue); - $this->putString($this->Type); - } - - public function decode(){ - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php deleted file mode 100755 index fe0b2e57..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/ServerDifficultyPacket.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class ServerDifficultyPacket extends Packet{ - - public $difficulty; - - public function pid(){ - return 0x41; - } - - public function encode(){ - $this->putByte($this->difficulty); - } - - public function decode(){ - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php deleted file mode 100755 index b6c80b41..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/StatisticsPacket.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class StatisticsPacket extends Packet{ - - public $count; - public $statistic = []; - - public function pid(){ - return 0x37; - } - - public function encode(){ - $this->putVarInt($this->count); - foreach($this->statistic as $statistic){ - $this->putString($statistic[0]); - $this->putVarInt($statistic[1]); - } - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php b/src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php deleted file mode 100755 index 957a0e11..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/TitlePacket.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class TitlePacket extends Packet{ - - const TYPE_SET_TITLE = 0; - const TYPE_SET_SUB_TITLE = 1; - const TYPE_SET_SETTINGS = 2; - const TYPE_HIDE = 3; - const TYPE_RESET = 4; - - public $actionID; - public $data = null; - - public function pid(){ - return 0x45; - } - - public function encode(){ - $this->putVarInt($this->actionID); - switch($this->actionID){ - case self::TYPE_SET_TITLE: - case self::TYPE_SET_SUB_TITLE: - $this->putString($this->data); - break; - case self::TYPE_SET_SETTINGS: - $this->putInt($this->data[0]); - $this->putInt($this->data[1]); - $this->putInt($this->data[2]); - break; - } - } - - public function decode(){ - - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php deleted file mode 100755 index fd7f976c..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/UpdateSignPacket.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\utils\Binary; - -class UpdateSignPacket extends Packet{ - - public $x; - public $y; - public $z; - public $line1; - public $line2; - public $line3; - public $line4; - - public function pid(){ - return 0x33; - } - - public function encode(){ - $this->putPosition($this->x, $this->y, $this->z); - $this->putString($this->line1); - $this->putString($this->line2); - $this->putString($this->line3); - $this->putString($this->line4); - } - - public function decode(){ - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php b/src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php deleted file mode 100755 index e11bbad3..00000000 --- a/src/shoghicp/BigBrother/network/protocol/Play/UseBedPacket.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. -*/ - -namespace shoghicp\BigBrother\network\protocol\Play; - -use shoghicp\BigBrother\network\Packet; - -class UseBedPacket extends Packet{ - - public $eid; - public $bedX; - public $bedY; - public $bedZ; - - public function pid(){ - return 0x0a; - } - - public function encode(){ - $this->putVarInt($this->eid); - $this->putPosition($this->bedX, $this->bedY, $this->bedZ); - } - - public function decode(){ - } -} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayDisconnectPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayDisconnectPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php index f95dde31..e83050ac 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayDisconnectPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayDisconnectPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerAbilitiesPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayerAbilitiesPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php index a8356f2e..7242ce7e 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerAbilitiesPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerAbilitiesPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerBlockPlacementPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayerBlockPlacementPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php index 9884810b..e3e46f73 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerBlockPlacementPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerBlockPlacementPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerDiggingPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayerDiggingPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php index 258bc5de..56ce3086 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerDiggingPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerDiggingPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayerLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php index 56b9ac3c..433537f4 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionAndLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php index 5809ff56..613df8ef 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionAndLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionPacket.php rename to src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php index 5cbe6400..97bf8539 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PlayerPositionPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerPositionPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/PositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/Play/PositionAndLookPacket.php rename to src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php index 36bb28b9..e750e49d 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/PositionAndLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PositionAndLookPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/RespawnPacket.php b/src/shoghicp/BigBrother/network/protocol/RespawnPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/RespawnPacket.php rename to src/shoghicp/BigBrother/network/protocol/RespawnPacket.php index e0bb93dd..6c0b1ed3 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/RespawnPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/RespawnPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/STCChatPacket.php b/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/STCChatPacket.php rename to src/shoghicp/BigBrother/network/protocol/STCChatPacket.php index bb0a4a38..a20e3e53 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/STCChatPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/STCCloseWindowPacket.php b/src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/STCCloseWindowPacket.php rename to src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php index 903797d6..303f8be8 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/STCCloseWindowPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/STCCloseWindowPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/SetSlotPacket.php b/src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/SetSlotPacket.php rename to src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php index edb7c14a..f5c8c53e 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/SetSlotPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SetSlotPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/SpawnMobPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/Play/SpawnMobPacket.php rename to src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php index e88f9ade..5494d579 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/SpawnMobPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/SpawnObjectPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php similarity index 96% rename from src/shoghicp/BigBrother/network/protocol/Play/SpawnObjectPacket.php rename to src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php index afd8e577..fa137650 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/SpawnObjectPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SpawnObjectPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/SpawnPlayerPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php similarity index 85% rename from src/shoghicp/BigBrother/network/protocol/Play/SpawnPlayerPacket.php rename to src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php index 5e0e5411..9d77339f 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/SpawnPlayerPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; @@ -38,16 +38,14 @@ public function pid(){ public function encode(){ $this->putVarInt($this->eid); - $this->putLong(substr($this->uuid, 0, 16));//UUID - $this->putLong(substr($this->uuid, 16, 16)); + $this->putString($this->uuid); $this->putInt(intval($this->x * 32)); $this->putInt(intval($this->y * 32)); $this->putInt(intval($this->z * 32)); $this->putByte(($this->yaw / 360) << 8); $this->putByte(($this->pitch / 360) << 8); $this->putShort($this->item); - $meta = Binary::writeMetadata($this->metadata); - $this->put($meta); + $this->put(Binary::writeMetadata($this->metadata)); } public function decode(){ diff --git a/src/shoghicp/BigBrother/network/protocol/Play/SpawnPositionPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/SpawnPositionPacket.php rename to src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php index 11fc78bf..4b71cdac 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/SpawnPositionPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SpawnPositionPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/TimeUpdatePacket.php b/src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/TimeUpdatePacket.php rename to src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php index 099ce867..47d45393 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/TimeUpdatePacket.php +++ b/src/shoghicp/BigBrother/network/protocol/TimeUpdatePacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/UpdateHealthPacket.php b/src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php similarity index 94% rename from src/shoghicp/BigBrother/network/protocol/Play/UpdateHealthPacket.php rename to src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php index d3197649..c0d7372c 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/UpdateHealthPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/UpdateHealthPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/protocol/Play/UseEntityPacket.php b/src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php similarity index 82% rename from src/shoghicp/BigBrother/network/protocol/Play/UseEntityPacket.php rename to src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php index fd1ea4dc..ae252e53 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/UseEntityPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/UseEntityPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; use shoghicp\BigBrother\utils\Binary; @@ -36,10 +36,11 @@ public function encode(){ public function decode(){ $this->target = $this->getVarInt(); $this->type = $this->getVarInt(); - if($this->type === 2){ - $this->targetX = $this->getFloat(); - $this->targetY = $this->getFloat(); - $this->targetZ = $this->getFloat(); + if($this->type){ + //TODO + //$this->targetX = $this->getFloat(); + //$this->targetY = $this->getFloat(); + //$this->targetZ = $this->getFloat(); } } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/Play/WindowItemsPacket.php b/src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php similarity index 95% rename from src/shoghicp/BigBrother/network/protocol/Play/WindowItemsPacket.php rename to src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php index 7cfca2ac..02fb1dd2 100755 --- a/src/shoghicp/BigBrother/network/protocol/Play/WindowItemsPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/WindowItemsPacket.php @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -namespace shoghicp\BigBrother\network\protocol\Play; +namespace shoghicp\BigBrother\network\protocol; use shoghicp\BigBrother\network\Packet; diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index f20de677..a4b9cbe2 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -17,125 +17,51 @@ namespace shoghicp\BigBrother\network\translation; -use pocketmine\Achievement; -use pocketmine\Player; -use pocketmine\entity\Entity; use pocketmine\item\Item; -use pocketmine\Block\Block; -use pocketmine\level\Level; -use pocketmine\network\protocol\AddEntityPacket; -use pocketmine\network\protocol\AddItemEntityPacket; -use pocketmine\network\protocol\AddPaintingPacket; -use pocketmine\network\protocol\AddPlayerPacket; -use pocketmine\network\protocol\AdventureSettingsPacket; -use pocketmine\network\protocol\AnimatePacket; -use pocketmine\network\protocol\BatchPacket; -use pocketmine\network\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\protocol\ContainerClosePacket; -use pocketmine\network\protocol\ContainerOpenPacket; -use pocketmine\network\protocol\ContainerSetContentPacket; -use pocketmine\network\protocol\ContainerSetDataPacket; -use pocketmine\network\protocol\ContainerSetSlotPacket; -use pocketmine\network\protocol\CraftingDataPacket; -use pocketmine\network\protocol\CraftingEventPacket; -use pocketmine\network\protocol\ChangeDimensionPacket; use pocketmine\network\protocol\DataPacket; -use pocketmine\network\protocol\DropItemPacket; -use pocketmine\network\protocol\FullChunkDataPacket; -use pocketmine\network\protocol\ItemFrameDropItemPacket; -use pocketmine\network\protocol\RequestChunkRadiusPacket; -use pocketmine\network\protocol\SetEntityLinkPacket; -use pocketmine\network\protocol\BlockEntityDataPacket; -use pocketmine\network\protocol\EntityEventPacket; -use pocketmine\network\protocol\ExplodePacket; -use pocketmine\network\protocol\HurtArmorPacket; use pocketmine\network\protocol\Info; use pocketmine\network\protocol\InteractPacket; -use pocketmine\network\protocol\LevelEventPacket; -use pocketmine\network\protocol\DisconnectPacket; -use pocketmine\network\protocol\LoginPacket; -use pocketmine\network\protocol\PlayStatusPacket; -use pocketmine\network\protocol\TextPacket; -use pocketmine\network\protocol\MoveEntityPacket; +use pocketmine\network\protocol\MessagePacket; use pocketmine\network\protocol\MovePlayerPacket; -use pocketmine\network\protocol\PlayerActionPacket; -use pocketmine\network\protocol\MobArmorEquipmentPacket; -use pocketmine\network\protocol\MobEquipmentPacket; use pocketmine\network\protocol\RemoveBlockPacket; -use pocketmine\network\protocol\RemoveEntityPacket; -use pocketmine\network\protocol\RemovePlayerPacket; use pocketmine\network\protocol\RespawnPacket; -use pocketmine\network\protocol\SetDifficultyPacket; -use pocketmine\network\protocol\SetEntityDataPacket; -use pocketmine\network\protocol\SetEntityMotionPacket; -use pocketmine\network\protocol\SetHealthPacket; -use pocketmine\network\protocol\SetPlayerGameTypePacket; -use pocketmine\network\protocol\SetSpawnPositionPacket; -use pocketmine\network\protocol\SetTimePacket; -use pocketmine\network\protocol\StartGamePacket; -use pocketmine\network\protocol\TakeItemEntityPacket; -use pocketmine\network\protocol\BlockEventPacket; -use pocketmine\network\protocol\UpdateBlockPacket; use pocketmine\network\protocol\UseItemPacket; -use pocketmine\network\protocol\PlayerInputPacket; -use pocketmine\math\Vector3; -use pocketmine\nbt\NBT; -use pocketmine\tile\Tile; use pocketmine\utils\TextFormat; -use shoghicp\BigBrother\BigBrother; use shoghicp\BigBrother\DesktopPlayer; -use shoghicp\BigBrother\network\Info as CInfo; //Computer Edition use shoghicp\BigBrother\network\Packet; -use shoghicp\BigBrother\network\protocol\Login\LoginDisconnectPacket; -use shoghicp\BigBrother\network\protocol\Play\AnimatePacket as CAnimatePacket; -use shoghicp\BigBrother\network\protocol\Play\BlockChangePacket; -use shoghicp\BigBrother\network\protocol\Play\ChangeGameStatePacket; -use shoghicp\BigBrother\network\protocol\Play\DestroyEntitiesPacket; -use shoghicp\BigBrother\network\protocol\Play\EntityEquipmentPacket; -use shoghicp\BigBrother\network\protocol\Play\EntityHeadLookPacket; -use shoghicp\BigBrother\network\protocol\Play\EntityMetadataPacket; -use shoghicp\BigBrother\network\protocol\Play\EntityTeleportPacket; -use shoghicp\BigBrother\network\protocol\Play\EntityVelocityPacket; -use shoghicp\BigBrother\network\protocol\Play\JoinGamePacket; -use shoghicp\BigBrother\network\protocol\Play\OpenWindowPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayDisconnectPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerAbilitiesPacket; -use shoghicp\BigBrother\network\protocol\Play\PlayerListPacket; -use shoghicp\BigBrother\network\protocol\Play\PositionAndLookPacket; -use shoghicp\BigBrother\network\protocol\Play\STabComletePacket; -use shoghicp\BigBrother\network\protocol\Play\ScoreboardObjectivePacket; -use shoghicp\BigBrother\network\protocol\Play\ServerDifficultyPacket; -use shoghicp\BigBrother\network\protocol\Play\SetSlotPacket; -use shoghicp\BigBrother\network\protocol\Play\SpawnObjectPacket; -use shoghicp\BigBrother\network\protocol\Play\SpawnPlayerPacket; -use shoghicp\BigBrother\network\protocol\Play\SpawnPositionPacket; -use shoghicp\BigBrother\network\protocol\Play\StatisticsPacket; -use shoghicp\BigBrother\network\protocol\Play\RespawnPacket as CRespawnPacket; -use shoghicp\BigBrother\network\protocol\Play\STCChatPacket; -use shoghicp\BigBrother\network\protocol\Play\STCCloseWindowPacket; -use shoghicp\BigBrother\network\protocol\Play\TimeUpdatePacket; -use shoghicp\BigBrother\network\protocol\Play\UpdateHealthPacket; -use shoghicp\BigBrother\network\protocol\Play\UpdateSignPacket; -use shoghicp\BigBrother\network\protocol\Play\UseBedPacket; -use shoghicp\BigBrother\network\protocol\Play\WindowItemsPacket; +use shoghicp\BigBrother\network\protocol\BlockChangePacket; +use shoghicp\BigBrother\network\protocol\ChangeGameStatePacket; +use shoghicp\BigBrother\network\protocol\DestroyEntitiesPacket; +use shoghicp\BigBrother\network\protocol\EntityHeadLookPacket; +use shoghicp\BigBrother\network\protocol\EntityMetadataPacket; +use shoghicp\BigBrother\network\protocol\EntityTeleportPacket; +use shoghicp\BigBrother\network\protocol\EntityVelocityPacket; +use shoghicp\BigBrother\network\protocol\JoinGamePacket; +use shoghicp\BigBrother\network\protocol\OpenWindowPacket; +use shoghicp\BigBrother\network\protocol\PlayerAbilitiesPacket; +use shoghicp\BigBrother\network\protocol\PositionAndLookPacket; +use shoghicp\BigBrother\network\protocol\SetSlotPacket; +use shoghicp\BigBrother\network\protocol\SpawnObjectPacket; +use shoghicp\BigBrother\network\protocol\SpawnPlayerPacket; +use shoghicp\BigBrother\network\protocol\SpawnPositionPacket; +use shoghicp\BigBrother\network\protocol\STCChatPacket; +use shoghicp\BigBrother\network\protocol\STCCloseWindowPacket; +use shoghicp\BigBrother\network\protocol\TimeUpdatePacket; +use shoghicp\BigBrother\network\protocol\UpdateHealthPacket; +use shoghicp\BigBrother\network\protocol\WindowItemsPacket; use shoghicp\BigBrother\utils\Binary; class TranslatorProtocol implements Translator{ + + public function interfaceToServer(DesktopPlayer $player, Packet $packet){ - if($packet->pid() !== 0x00 and $packet->pid() !== 0x03 and $packet->pid() !== 0x04 and $packet->pid() !== 0x05 and $packet->pid() !== 0x06){ - echo "[Receive] 0x".bin2hex(chr($packet->pid()))."\n"; //Debug - } - switch($packet->pid()){ - case 0x00: //KeepAlivePacket - $pk->id = mt_rand(); - $player->putRawPacket($pk); - return null; + // TODO: move to Info - case 0x01: //ChatPacket - $pk = new TextPacket(); - $pk->type = 1;//Chat Type + case 0x01: //CTSChatPacket + $pk = new MessagePacket(); $pk->source = ""; $pk->message = $packet->message; return $pk; @@ -143,17 +69,13 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x02: //UseEntityPacket $pk = new InteractPacket(); $pk->target = $packet->target; - $pk->action = $packet->type; + $pk->action = $packet->mouse; return $pk; - case 0x03: //PlayerPacket - $player->setSetting(["onGround" => $packet->onGround]); - return null; - - case 0x04: //PlayerPositonPacket + case 0x04: //PlayerPositionPacket $pk = new MovePlayerPacket(); $pk->x = $packet->x; - $pk->y = $packet->y + $player->getEyeHeight(); + $pk->y = $packet->y; $pk->z = $packet->z; $pk->yaw = $player->yaw; $pk->bodyYaw = $player->yaw; @@ -163,7 +85,7 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x05: //PlayerLookPacket $pk = new MovePlayerPacket(); $pk->x = $player->x; - $pk->y = $player->y + $player->getEyeHeight(); + $pk->y = $player->y; $pk->z = $player->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; @@ -173,7 +95,7 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x06: //PlayerPositionAndLookPacket $pk = new MovePlayerPacket(); $pk->x = $packet->x; - $pk->y = $packet->y + $player->getEyeHeight(); + $pk->y = $packet->y; $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; @@ -181,334 +103,61 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ return $pk; case 0x07: //PlayerDiggingPacket - switch($packet->status){ - case 0: - if($player->getGamemode() === 1){ - $pk = new RemoveBlockPacket(); - $pk->eid = 0; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - return $pk; - }else{ - $pk = new PlayerActionPacket(); - $pk->eid = 0; - $pk->action = PlayerActionPacket::ACTION_START_BREAK; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->face = $packet->face; - return $pk; - } - break; - case 1: - $pk = new PlayerActionPacket(); - $pk->eid = 0; - $pk->action = PlayerActionPacket::ACTION_ABORT_BREAK; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->face = $packet->face; - return $pk; - break; - case 2: - if($player->getGamemode() !== 1){ - $packets = []; - $pk = new PlayerActionPacket(); - $pk->eid = 0; - $pk->action = PlayerActionPacket::ACTION_STOP_BREAK; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->face = $packet->face; - $packets[] = $pk; - - $pk = new RemoveBlockPacket(); - $pk->eid = 0; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $packets[] = $pk; - return $packets; - }else{ - echo "PlayerDiggingPacket: ".$packet->status."\n"; - } - break; - default: - echo "PlayerDiggingPacket: ".$packet->status."\n"; - break; - } - - return null; - - case 0x08; //PlayerBlockPlacementPacket - echo "PlayerBlockPlacementPacket: ".$packet->direction."\n"; - - if($packet->direction !== 255){ - $pk = new UseItemPacket(); + if($packet->status === 2 or ($player->getGamemode() === 1 and $packet->status === 0)){ //Finished digging + $pk = new RemoveBlockPacket(); + $pk->eid = 0; $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; - $pk->face = $packet->direction; - $pk->item = $packet->heldItem; - $pk->fx = $packet->cursorX / 16; - $pk->fy = $packet->cursorY / 16; - $pk->fz = $packet->cursorZ / 16; - $pk->posX = $player->getX(); - $pk->posY = $player->getY(); - $pk->posZ = $player->getZ(); - return $pk; - }else{ - - } - - return null; - - case 0x09: //HeldItemChangePacket - $item = $player->getInventory()->getItem($packet->selectedSlot); - $olditem = $player->getInventory()->getItem($player->getInventory()->getHeldItemIndex()); - - if($item->getId() !== 0 or $item->getId() === 0 and $olditem->getId() !== 0){ - $pk = new MobEquipmentPacket(); - $pk->eid = 0; - $pk->item = $item; - if($item->getId() === 0){ - $pk->slot = 255; - }else{ - $pk->slot = Item::getCreativeItemIndex($item) + 9; - } - $pk->selectedSlot = $packet->selectedSlot; return $pk; } - return null; - case 0x0a: //PlayerArmSwingPacket - $pk = new AnimatePacket(); - $pk->action = 1; + case 0x08; //PlayerBlockPlacementPacket + $pk = new UseItemPacket(); + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->direction; + $pk->item = $packet->heldItem->getID(); + $pk->meta = $packet->heldItem->getDamage(); $pk->eid = 0; + $pk->fx = $packet->cursorX / 16; + $pk->fy = $packet->cursorY / 16; + $pk->fz = $packet->cursorZ / 16; return $pk; - case 0x0b: //AnimatePacket - switch($packet->actionID){ - case 0: - if(!$player->getSetting("isFlying")){ - $pk = new PlayerActionPacket(); - $pk->eid = $packet->eid; - $pk->action = PlayerActionPacket::ACTION_START_SNEAK; - $pk->x = $player->getX(); - $pk->y = $player->getY(); - $pk->z = $player->getZ(); - $pk->face = 0; - return $pk; - } - break; - case 1: - if(!$player->getSetting("isFlying")){ - $pk = new PlayerActionPacket(); - $pk->eid = $packet->eid; - $pk->action = PlayerActionPacket::ACTION_STOP_SNEAK; - $pk->x = $player->getX(); - $pk->y = $player->getY(); - $pk->z = $player->getZ(); - $pk->face = 0; - return $pk; - } - break; - case 2: - $pk = new PlayerActionPacket(); - $pk->eid = $packet->eid; - $pk->action = PlayerActionPacket::ACTION_STOP_SLEEPING; - $pk->x = $player->getX(); - $pk->y = $player->getY(); - $pk->z = $player->getZ(); - $pk->face = 0; - return $pk; - break; - case 3: - $pk = new PlayerActionPacket(); - $pk->eid = $packet->eid; - $pk->action = PlayerActionPacket::ACTION_START_SPRINT; - $pk->x = $player->getX(); - $pk->y = $player->getY(); - $pk->z = $player->getZ(); - $pk->face = 0; - return $pk; - break; - case 4: - $pk = new PlayerActionPacket(); - $pk->eid = $packet->eid; - $pk->action = PlayerActionPacket::ACTION_STOP_SPRINT; - $pk->x = $player->getX(); - $pk->y = $player->getY(); - $pk->z = $player->getZ(); - $pk->face = 0; - return $pk; - break; - /*case 6: - - break;*/ - default: - echo "[AnimatePacket] ".$packet->actionID."\n";//Debug Code - break; - } - return null; - case 0x0d: //CTSCloseWindowPacket - if($packet->windowID !== 0x00){ - $pk = new ContainerClosePacket(); - $pk->windowid = $packet->windowID; - return $pk; - } - - case 0x10: //CreativeInventoryActionPacket - echo "Slot: ".$packet->slot."\n"; - echo "ItemId: ".$packet->item->getId()." : ".$packet->item->getDamage()."\n"; - - /*if($packet->slot === 65535){ - $pk = new DropItemPacket(); - $pk->type = 0; - $pk->item = $packet->item; - return $pk; - }else{ - $pk = new ContainerSetSlotPacket(); - $pk->windowid = 0; - $pk->slot = $packet->slot; - $pk->item = $packet->item; - return $pk; - }*/ - - return null; - - case 0x13: //CPlayerAbilitiesPacket - $player->setSetting(["isFlying" => $packet->isFlying]); - return null; - - case 0x14: //CTabCompletePacket - /*$pk = new STabComletePacket(); - - foreach($player->getServer()->getCommandMap()->getCommands() as $command){ - if($command->testPermissionSilent($player)){ - $pk->matches[] = $command->getName(); - } - } - - foreach($player->getServer()->getOnlinePlayers() as $packetplayer){ - $pk->matches[] = $packetplayer->getName(); - } - - //TODO - - //echo $packet->text."\n"; - - return $pk;*/ - return null; - - case 0x15: //ClientSettingsPacket - $player->setSetting([ - "Lang" => $packet->lang, - "View" => $packet->view, - "ChatMode" => $packet->chatmode, - "ChatColor" => $packet->chatcolor, - "SkinSettings" => $packet->skinsetting, - ]); - - return null; + $pk = new ContainerClosePacket(); + $pk->windowid = $packet->windowID; + return $pk; case 0x16: //ClientStatusPacket - switch($packet->actionID){ - case 0: - $pk = new PlayerActionPacket(); - $pk->eid = 0; - $pk->action = PlayerActionPacket::ACTION_RESPAWN; - $pk->x = 0; - $pk->y = 0; - $pk->z = 0; - $pk->face = 0; - return $pk; - break; - case 1: - $statistic = []; - $statistic[] = ["achievement.openInventory", 1];// - foreach($player->achievements as $achievement => $count){ - $statistic[] = ["achievement.".$achievement, $count]; - } - - //stat - //https://gist.github.com/thinkofdeath/a1842c21a0cf2e1fb5e0 - - $pk = new StatisticsPacket(); - $pk->count = count($statistic);//TODO stat - $pk->statistic = $statistic; - $player->putRawPacket($pk); - break; - case 2: - //$player->awardAchievement("openInventory"); this for DesktopPlayer - //Achievement::broadcast($player, "openInventory");//Debug - break; - } - return null; - - case 0x17: //PluginMessagePacket - switch($packet->channel){ - case "REGISTER"://Mods Register - $player->setSetting(["Channels" => $packet->data]); - break; - case "MC|Brand": //ServerType - $player->setSetting(["ServerType" => $packet->data]); - break; - default: - echo "PluginChannel: ".$packet->channel."\n"; - break; + if($packet->actionID === 0){ + $pk = new RespawnPacket(); + $pk->eid = 0; + $pk->x = $player->getSpawn()->getX(); + $pk->y = $player->getSpawn()->getX(); + $pk->z = $player->getSpawn()->getX(); + return $pk; } return null; - case 0x19: //ResourcePackStatusPacket - $player->setSetting(["ResourceStatus" => $packet->status, "ResourceHash" => $packet->hash]); - return null; - default: - echo "[Receive] 0x".bin2hex(chr($packet->pid()))."\n"; return null; } } public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ switch($packet->pid()){ - case Info::DISCONNECT_PACKET: - if($player->bigBrother_getStatus() === 0){ - $pk = new LoginDisconnectPacket(); - $pk->reason = TextFormat::toJSON($packet->message === "" ? "You have been disconnected." : $packet->message); - }else{ - $pk = new PlayDisconnectPacket(); - $pk->reason = TextFormat::toJSON($packet->message === "" ? "You have been disconnected." : $packet->message); - } - return $pk; - - case Info::TEXT_PACKET: - - echo $player->getSetting("Lang")."\n"; - - /*if($packet->message === "chat.type.achievement"){ - /*$pk = new ScoreboardObjectivePacket(); - $pk->ObjectiveName = $packet->parameters[0]; - $pk->Mode = 0; - $pk->ObjectiveValue = 3; - return $pk;*/ - /*echo "TextPacket: achievement\n"; - return null; - }else{ - $pk = new STCChatPacket(); - $pk->message = BigBrother::toJSON($packet->message, $packet->type, $packet->parameters); - }*/ - - //return $pk; - return null; - case Info::SET_TIME_PACKET: - $pk = new TimeUpdatePacket(); - $pk->age = $packet->time; - $pk->time = $packet->time; //TODO: calculate offset from MCPE + case Info::UPDATE_BLOCK_PACKET: + $pk = new BlockChangePacket(); + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->blockId = $packet->block; + $pk->blockMeta = $packet->meta; return $pk; case Info::START_GAME_PACKET: @@ -516,19 +165,13 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk = new JoinGamePacket(); $pk->eid = $packet->eid; - $pk->gamemode = $packet->gamemode; + $pk->gamemode = $player->getGamemode(); $pk->dimension = 0; $pk->difficulty = $player->getServer()->getDifficulty(); $pk->maxPlayers = $player->getServer()->getMaxPlayers(); $pk->levelType = "default"; $packets[] = $pk; - $pk = new SpawnPositionPacket(); - $pk->spawnX = $packet->spawnX; - $pk->spawnY = $packet->spawnY; - $pk->spawnZ = $packet->spawnZ; - $packets[] = $pk; - $pk = new PlayerAbilitiesPacket(); $pk->flyingSpeed = 0.05; $pk->walkingSpeed = 0.1; @@ -536,134 +179,71 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; $pk->isFlying = false; $pk->isCreative = ($player->getGamemode() & 0x01) > 0; - $packets[] = $pk; - - $pk = new PositionAndLookPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $player->yaw; - $pk->pitch = $player->pitch; - $packets[] = $pk; + if($player->spawned === true){ + $packets = [$pk]; - return $packets; - - case Info::ADD_PLAYER_PACKET: - $packets = []; - $packetplayer = $player->getServer()->getPlayerExact($packet->username); - - $pk = new PlayerListPacket(); - $pk->actionID = PlayerListPacket::TYPE_ADD; - - $pk->players[] = [ - $packetplayer->getUniqueId()->toBinary(), - $packetplayer->getName(), - [], - $packetplayer->getGamemode(), - 0, - false, - ]; - - if($packetplayer instanceof DesktopPlayer){ - $pk->players[0][2] = $packetplayer->bigBrother_getPeroperties(); + $pk = new ChangeGameStatePacket(); + $pk->reason = 3; + $pk->value = $player->getGamemode(); + $packets[] = $pk; + return $packets; + }else{ + $packets[] = $pk; } - $packets[] = $pk; - $pk = new SpawnPlayerPacket(); - $pk->eid = $packet->eid; - $pk->uuid = $packetplayer->getUniqueId()->toBinary(); - $pk->x = $packet->x; - $pk->z = $packet->z; - $pk->y = $packet->y; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $pk->item = $packetplayer->getInventory()->getItemInHand()->getId(); - $pk->metadata = $packet->metadata; + $pk = new SpawnPositionPacket(); + $pk->spawnX = $packet->spawnX; + $pk->spawnY = $packet->spawnY; + $pk->spawnZ = $packet->spawnZ; $packets[] = $pk; - $pk = new EntityTeleportPacket(); - $pk->eid = $packet->eid; + $pk = new PositionAndLookPacket(); $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; + $pk->yaw = $player->yaw; + $pk->pitch = $player->pitch; + $pk->onGround = $player->isOnGround(); $packets[] = $pk; - return $packets; - /*case Info::ADD_ENTITY_PACKET: - return null;*/ + case Info::SET_HEALTH_PACKET: + $pk = new UpdateHealthPacket(); + $pk->health = $packet->health; + $pk->food = 20; + $pk->saturation = 5; + return $pk; - /*case Info::REMOVE_PLAYER_PACKET: - $pk = new PlayerListPacket(); - $pk->actionID = PlayerListPacket::TYPE_REMOVE; + case Info::TEXT_PACKET: + $pk = new STCChatPacket(); - $pk->players[] = [ - $packet->clientId->toBinary() - ]; - $packets[] = $pk; + $pk->message = TextFormat::toJSON($packet->message); + return $pk; - $pk = new DestroyEntitiesPacket(); - $pk->ids[] = $packet->eid; - $packets[] = $pk; + case Info::SET_TIME_PACKET: + $pk = new TimeUpdatePacket(); + $pk->age = $packet->time; + $pk->time = $packet->time; //TODO: calculate offset from MCPE + return $pk; - return $packets;*/ + case Info::SET_SPAWN_POSITION_PACKET: + $pk = new SpawnPositionPacket(); + $pk->spawnX = $packet->x; + $pk->spawnY = $packet->y; + $pk->spawnZ = $packet->z; + return $pk; case Info::REMOVE_ENTITY_PACKET: + //case Info::REMOVE_PLAYER_PACKET: $pk = new DestroyEntitiesPacket(); $pk->ids[] = $packet->eid; return $pk; - case Info::ADD_ITEM_ENTITY_PACKET: - $packets = []; - $pk = new SpawnObjectPacket(); - $pk->eid = $packet->eid; - $pk->type = 2; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $player->yaw; - $pk->pitch = $player->pitch; - $packets[] = $pk; - - $pk = new EntityMetadataPacket(); - $pk->eid = $packet->eid; - $pk->metadata = [ - 0 => [0 => 0, 1 => 0], - 10 => [0 => 5, 1 => $packet->item], - ]; - $packets[] = $pk; - - return $packets; - - /*case Info::REMOVE_ITEM_ENTITY_PACKET: - return null;*/ - - case Info::MOVE_ENTITY_PACKET: - $packets = []; - foreach($packet->entities as $d){ - $pk = new EntityTeleportPacket(); - $pk->eid = $d[0]; - $pk->x = $d[1]; - $pk->y = $d[2] - $player->getEyeHeight(); - $pk->z = $d[3]; - $pk->yaw = $d[4]; - $pk->pitch = $d[6]; - $packets[] = $pk; - - $pk = new EntityHeadLookPacket(); - $pk->eid = $d[0]; - $pk->yaw = $d[5]; - $packets[] = $pk; - } - return $packets; - case Info::MOVE_PLAYER_PACKET: if($packet->eid === 0){ $pk = new PositionAndLookPacket(); $pk->x = $packet->x; - $pk->y = $packet->y - $player->getEyeHeight(); + $pk->y = $packet->y; $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; @@ -674,7 +254,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk = new EntityTeleportPacket(); $pk->eid = $packet->eid; $pk->x = $packet->x; - $pk->y = $packet->y - $player->getEyeHeight(); + $pk->y = $packet->y; $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; @@ -687,62 +267,25 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return $packets; } - case Info::UPDATE_BLOCK_PACKET://TODO - $pk = new BlockChangePacket(); - $count = count($packet->records) - 1; - $pk->x = $packet->records[$count][0]; - $pk->y = $packet->records[$count][2]; - $pk->z = $packet->records[$count][1]; - $pk->blockId = $packet->records[$count][3]; - $pk->blockMeta = $packet->records[$count][4]; - return $pk; - - case Info::MOB_EQUIPMENT_PACKET: - $pk = new EntityEquipmentPacket(); - $pk->eid = $packet->eid; - $pk->slot = $packet->slot; - $pk->item = $packet->item; - - return $pk; - - case Info::MOB_ARMOR_EQUIPMENT_PACKET: + case Info::MOVE_ENTITY_PACKET: $packets = []; + foreach($packet->entities as $d){ + $pk = new EntityTeleportPacket(); + $pk->eid = $d[0]; + $pk->x = $d[1]; + $pk->y = $d[2]; + $pk->z = $d[3]; + $pk->yaw = $d[4]; + $pk->pitch = $d[5]; + $packets[] = $pk; - foreach($packet->slots as $num => $item){ - $pk = new EntityEquipmentPacket(); - $pk->eid = $packet->eid; - $pk->slot = $num + 1; - $pk->item = $item; + $pk = new EntityHeadLookPacket(); + $pk->eid = $d[0]; + $pk->yaw = $d[4]; $packets[] = $pk; } - return $packets; - case Info::SET_ENTITY_DATA_PACKET: - /*if(isset($packet->metadata[16])){ - if($packet->metadata[16][1] === 2){ - $pk = new UseBedPacket(); //Bug - $pk->eid = $packet->eid; - $bedXYZ = $player->getSetting("BedXYZ"); - $pk->bedX = $bedXYZ[0]; - $pk->bedY = $bedXYZ[1]; - $pk->bedZ = $bedXYZ[2]; - $player->removeSetting("BedXYZ"); - }else{ - $pk = new CAnimatePacket(); - $pk->eid = $packet->eid; - $pk->actionID = 2; - } - return $pk; - }elseif(isset($packet->metadata[17])){ - $player->setSetting(["BedXYZ" => $packet->metadata[17][1]]); - }*/ - - $pk = new EntityMetadataPacket(); - $pk->eid = $packet->eid; - $pk->metadata = $packet->metadata; - return $pk; - case Info::SET_ENTITY_MOTION_PACKET: $packets = []; foreach($packet->entities as $d){ @@ -755,49 +298,13 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ } return $packets; - case Info::SET_HEALTH_PACKET: - $pk = new UpdateHealthPacket(); - $pk->health = $packet->health; - $pk->food = 20; - $pk->saturation = 5; - return $pk; - - case Info::SET_SPAWN_POSITION_PACKET: - $pk = new SpawnPositionPacket(); - $pk->spawnX = $packet->x; - $pk->spawnY = $packet->y; - $pk->spawnZ = $packet->z; - return $pk; - - case Info::ANIMATE_PACKET: - switch($packet->action){ - case 1: - $pk = new CAnimatePacket(); - $pk->actionID = 0; - $pk->eid = $packet->eid; - return $pk; - break; - case 3: //LeaveBed - $pk = new CAnimatePacket(); - $pk->actionID = 2; - $pk->eid = $packet->eid; - return $pk; - break; - default: - echo "AnimatePacket: ".$packet->action."\n"; - break; - } - return null; - - case Info::RESPAWN_PACKET: - $pk = new CRespawnPacket(); - $pk->dimension = 0; - $pk->difficulty = $player->getServer()->getDifficulty(); - $pk->gamemode = $player->getGamemode(); - $pk->levelType = "default"; + /* + case Info::CONTAINER_CLOSE_PACKET: + $pk = new STCCloseWindowPacket(); + $pk->windowID = $packet->windowid; return $pk; - /*case Info::CONTAINER_OPEN_PACKET: + case Info::CONTAINER_OPEN_PACKET: $pk = new OpenWindowPacket(); $pk->windowID = $packet->windowid; $pk->inventoryType = $packet->type; @@ -805,17 +312,11 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->slots = $packet->slots; return $pk; - case Info::CONTAINER_CLOSE_PACKET: - $pk = new STCCloseWindowPacket(); - $pk->windowID = $packet->windowid; - return $pk; - case Info::CONTAINER_SET_SLOT_PACKET: - echo "ContainerSetSlotPacket: 0x".bin2hex(chr($packet->windowid))."\n"; $pk = new SetSlotPacket(); $pk->windowID = $packet->windowid; - if($pk->windowID === 0x00){ - $pk->slot = $packet->slot + 36; + if($pk->windowID === 0){ + $pk->slot = $packet->slot + 9; }elseif($pk->windowID === 0x78){ $pk->windowID = 0; $pk->slot = $packet->slot + 5; @@ -825,10 +326,10 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->item = $packet->item; return $pk; - case Info::CONTAINER_SET_CONTENT_PACKET://Bug - echo "ContainerSetContentPacket: 0x".bin2hex(chr($packet->windowid))."\n"; - if($packet->windowid !== 0x79 and $packet->windowid !== 0x78){ - $pk = new WindowItemsPacket(); + case Info::CONTAINER_SET_CONTENT_PACKET: + $pk = new WindowItemsPacket(); + $pk->windowID = $packet->windowid; + if($pk->windowID === 0 or $pk->windowID === 0x78){ $pk->windowID = 0; for($i = 0; $i < 5; ++$i){ $pk->items[] = Item::get(Item::AIR, 0, 0); @@ -837,83 +338,66 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->items[] = $player->getInventory()->getChestplate(); $pk->items[] = $player->getInventory()->getLeggings(); $pk->items[] = $player->getInventory()->getBoots(); - - if($player->getGamemode() === 0){ - for($i = 9; $i < 36; ++$i){ - $pk->items[] = $player->getInventory()->getItem($i); - } - }else{ - for($i = 0; $i < 27; ++$i){ - $pk->items[] = Item::get(Item::AIR, 0, 0); - } - } - for($i = 0; $i < 9; ++$i){ + $slots = $player->getInventory()->getSize(); + for($i = 0; $i < $slots; ++$i){ $pk->items[] = $player->getInventory()->getItem($i); } - return $pk; - } - return null;*/ - - case Info::CRAFTING_DATA_PACKET: - $player->setSetting(["Recipes" => $packet->entries, "cleanRecipes" => $packet->cleanRecipes]); - return null; - - case Info::BLOCK_ENTITY_DATA_PACKET: - $nbt = new NBT(NBT::LITTLE_ENDIAN); - $nbt->read($packet->namedtag); - $nbt = $nbt->getData(); - if($nbt["id"] !== Tile::SIGN){ - return null; }else{ - $index = Level::chunkHash($packet->x >> 4, $packet->z >> 4); - if(isset($player->usedChunks[$index]) and $player->usedChunks[$index]){ - $pk = new UpdateSignPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->line1 = BigBrother::toJSON($nbt["Text1"]); - $pk->line2 = BigBrother::toJSON($nbt["Text2"]); - $pk->line3 = BigBrother::toJSON($nbt["Text3"]); - $pk->line4 = BigBrother::toJSON($nbt["Text4"]); - return $pk; - } + $pk->items = $packet->slots; } - - return null; - case Info::SET_DIFFICULTY_PACKET: - $pk = new ServerDifficultyPacket(); - $pk->difficulty = $packet->difficulty; return $pk; + */ - case Info::SET_PLAYER_GAMETYPE_PACKET: - $packets = []; - - $pk = new PlayerAbilitiesPacket(); - $pk->flyingSpeed = 0.05; - $pk->walkingSpeed = 0.1; - $pk->canFly = ($player->getGamemode() & 0x01) > 0; - $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; - $pk->isFlying = false; - $pk->isCreative = ($player->getGamemode() & 0x01) > 0; + case Info::ADD_ITEM_ENTITY_PACKET: + $packets = []; + $pk = new SpawnObjectPacket(); + $pk->eid = $packet->eid; + $pk->type = 2; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; $packets[] = $pk; - $pk = new ChangeGameStatePacket(); - $pk->reason = 3; - $pk->value = $player->getGamemode(); + $pk = new EntityMetadataPacket(); + $pk->eid = $packet->eid; + $pk->metadata = $pk->metadata = [ + 0 => ["type" => 0, "value" => 0], + 10 => ["type" => 5, "value" => $packet->item], + ]; $packets[] = $pk; return $packets; - case Info::PLAY_STATUS_PACKET: - case Info::PLAYER_LIST_PACKET: - case Info::ADVENTURE_SETTINGS_PACKET: - case Info::FULL_CHUNK_DATA_PACKET: - case Info::BATCH_PACKET: - return null; + + case Info::ADD_PLAYER_PACKET: + $packets = []; + $pk = new SpawnPlayerPacket(); + $pk->name = $packet->username; + $pk->eid = $packet->eid; + $pk->uuid = Binary::UUIDtoString("00000000000030008000000000000000"); + $pk->x = $packet->x; + $pk->z = $packet->y; + $pk->y = $packet->z; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; + $pk->item = 0; + $pk->metadata = $packet->metadata; + $packets[] = $pk; + + $pk = new EntityTeleportPacket(); + $pk->eid = $packet->eid; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; + $packets[] = $pk; + return $packets; default: - echo "[Send] 0x".bin2hex(chr($packet->pid()))."\n"; return null; } } diff --git a/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php b/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php new file mode 100755 index 00000000..543ef427 --- /dev/null +++ b/src/shoghicp/BigBrother/tasks/AuthenticateOnline.php @@ -0,0 +1,55 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\tasks; + +use pocketmine\scheduler\AsyncTask; +use pocketmine\Server; +use pocketmine\utils\Utils; +use shoghicp\BigBrother\DesktopPlayer; + +class AuthenticateOnline extends AsyncTask{ + + protected $clientID; + protected $username; + protected $hash; + + public function __construct($clientID, $username, $hash){ + $this->clientID = $clientID; + $this->username = $username; + $this->hash = $hash; + } + + public function onRun(){ + $result = Utils::getURL("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=".$this->username."&serverId=".$this->hash, 5); + $this->setResult($result); + } + + public function onCompletion(Server $server){ + foreach($server->getOnlinePlayers() as $clientID => $player){ + if($player instanceof DesktopPlayer and $clientID === $this->clientID){ + $result = json_decode($this->getResult(), true); + if(is_array($result) and isset($result["id"])){ + $player->bigBrother_authenticate($this->username, $result["id"], $result["properties"]); + }else{ + $player->close("", "User not premium"); + } + break; + } + } + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php b/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php new file mode 100755 index 00000000..4f7d86fe --- /dev/null +++ b/src/shoghicp/BigBrother/tasks/GeneratePrivateKey.php @@ -0,0 +1,81 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\tasks; + +use phpseclib\Crypt\RSA; +use pocketmine\scheduler\AsyncTask; +use pocketmine\Server; +use shoghicp\BigBrother\BigBrother; + +class GeneratePrivateKey extends AsyncTask{ + + /** @var \ThreadedLogger */ + protected $logger; + protected $loader; + /** @var array */ + protected $loadPaths; + + public function __construct(\ThreadedLogger $logger, \ClassLoader $loader){ + $this->logger = $logger; + $this->loader = $loader; + $loadPaths = []; + $this->addDependency($loadPaths, new \ReflectionClass($logger)); + $this->addDependency($loadPaths, new \ReflectionClass($loader)); + $this->loadPaths = array_reverse($loadPaths); + } + + protected function addDependency(array &$loadPaths, \ReflectionClass $dep){ + if($dep->getFileName() !== false){ + $loadPaths[$dep->getName()] = $dep->getFileName(); + } + + if($dep->getParentClass() instanceof \ReflectionClass){ + $this->addDependency($loadPaths, $dep->getParentClass()); + } + + foreach($dep->getInterfaces() as $interface){ + $this->addDependency($loadPaths, $interface); + } + } + + public function onRun(){ + foreach($this->loadPaths as $name => $path){ + if(!class_exists($name, false) and !interface_exists($name, false)){ + require($path); + } + } + $this->loader->register(true); + + $rsa = new RSA(); + $this->logger->info("[BigBrother] Generating keypair"); + $rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1); + $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1); + $keys = $rsa->createKey(1024); + $this->setResult($keys); + } + + public function onCompletion(Server $server){ + $plugin = $server->getPluginManager()->getPlugin("BigBrother"); + if($plugin instanceof BigBrother){ + if($plugin->isEnabled()){ + $result = $this->getResult(); + $plugin->receiveCryptoKeys($result["privatekey"], $result["publickey"]); + } + } + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php b/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php new file mode 100755 index 00000000..35b88766 --- /dev/null +++ b/src/shoghicp/BigBrother/tasks/LevelDBToAnvil.php @@ -0,0 +1,122 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\tasks; + +use pocketmine\level\format\leveldb\Chunk; +use pocketmine\level\Level; +use pocketmine\scheduler\AsyncTask; +use pocketmine\Server; +use pocketmine\utils\Binary; +use shoghicp\BigBrother\DesktopPlayer; + +class LevelDBToAnvil extends AsyncTask{ + + protected $playerName; + + protected $chunkX; + protected $chunkZ; + + public $blockIds; + public $blockData; + public $blockSkyLight; + public $blockLight; + + protected $biomeIds; + protected $compressionLevel; + + + public function __construct(DesktopPlayer $player, Chunk $chunk){ + $this->playerName = $player->getName(); + + $this->chunkX = $chunk->getX(); + $this->chunkZ = $chunk->getZ(); + + $this->blockIds = $chunk->getBlockIdArray(); + $this->blockData = $chunk->getBlockDataArray(); + $this->blockSkyLight = $chunk->getBlockSkyLightArray(); + $this->blockLight = $chunk->getBlockLightArray(); + + $this->biomeIds = $chunk->getBiomeIdArray(); + + $this->compressionLevel = Level::$COMPRESSION_LEVEL; + } + + public function onRun(){ + $ids = ["", "", "", "", "", "", "", ""]; + $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; + + //Complexity: O(MG) + for($Y = 0; $Y < 8; ++$Y){ + for($y = 0; $y < 16; ++$y){ + $offset = ($Y << 4) + $y; + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $index = ($x << 11) + ($z << 7) + $offset; + $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); + if(($y & 1) === 0){ + $data = ord($this->blockData[$halfIndex]) & 0x0F; + $bLight = ord($this->blockLight[$halfIndex]) & 0x0F; + //$sLight = ord($this->blockSkyLight[$halfIndex]) & 0x0F; + }else{ + $data = ord($this->blockData[$halfIndex]) >> 4; + $bLight = ord($this->blockLight[$halfIndex]) >> 4; + //$sLight = ord($this->blockSkyLight[$halfIndex]) >> 4; + } + $ids[$Y] .= pack("v", (ord($this->blockIds[$index]) << 4) | $data); + + $blockLight[$Y][] = $bLight; + //$skyLight[$Y][] = $sLight; + } + } + } + } + + foreach($blockLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $blockLight[$Y] = $final; + } + + /* + foreach($skyLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $skyLight[$Y] = $final; + } + */ + + $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; + + $this->setResult(implode($ids) . implode($blockLight) . implode($skyLight) . $this->biomeIds); + } + + public function onCompletion(Server $server){ + $player = $server->getPlayerExact($this->playerName); + if($player instanceof DesktopPlayer){ + if(($payload = $this->getResult()) !== null){ + $player->bigBrother_sendChunk($this->chunkX, $this->chunkZ, $payload); + } + } + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php b/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php new file mode 100755 index 00000000..8d1f1882 --- /dev/null +++ b/src/shoghicp/BigBrother/tasks/McRegionToAnvil.php @@ -0,0 +1,122 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\tasks; + +use pocketmine\level\format\mcregion\Chunk; +use pocketmine\level\Level; +use pocketmine\scheduler\AsyncTask; +use pocketmine\Server; +use pocketmine\utils\Binary; +use shoghicp\BigBrother\DesktopPlayer; + +class McRegionToAnvil extends AsyncTask{ + + protected $playerName; + + protected $chunkX; + protected $chunkZ; + + public $blockIds; + public $blockData; + public $blockSkyLight; + public $blockLight; + + protected $biomeIds; + protected $compressionLevel; + + + public function __construct(DesktopPlayer $player, Chunk $chunk){ + $this->playerName = $player->getName(); + + $this->chunkX = $chunk->getX(); + $this->chunkZ = $chunk->getZ(); + + $this->blockIds = $chunk->getBlockIdArray(); + $this->blockData = $chunk->getBlockDataArray(); + $this->blockSkyLight = $chunk->getBlockSkyLightArray(); + $this->blockLight = $chunk->getBlockLightArray(); + + $this->biomeIds = $chunk->getBiomeIdArray(); + + $this->compressionLevel = Level::$COMPRESSION_LEVEL; + } + + public function onRun(){ + $ids = ["", "", "", "", "", "", "", ""]; + $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; + + //Complexity: O(MG) + for($Y = 0; $Y < 8; ++$Y){ + for($y = 0; $y < 16; ++$y){ + $offset = ($Y << 4) + $y; + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $index = ($x << 11) + ($z << 7) + $offset; + $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); + if(($y & 1) === 0){ + $data = ord($this->blockData[$halfIndex]) & 0x0F; + $bLight = ord($this->blockLight[$halfIndex]) & 0x0F; + //$sLight = ord($this->blockSkyLight[$halfIndex]) & 0x0F; + }else{ + $data = ord($this->blockData[$halfIndex]) >> 4; + $bLight = ord($this->blockLight[$halfIndex]) >> 4; + //$sLight = ord($this->blockSkyLight[$halfIndex]) >> 4; + } + $ids[$Y] .= pack("v", (ord($this->blockIds[$index]) << 4) | $data); + + $blockLight[$Y][] = $bLight; + //$skyLight[$Y][] = $sLight; + } + } + } + } + + foreach($blockLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $blockLight[$Y] = $final; + } + + /* + foreach($skyLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $skyLight[$Y] = $final; + } + */ + + $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; + + $this->setResult(implode($ids) . implode($blockLight) . implode($skyLight) . $this->biomeIds); + } + + public function onCompletion(Server $server){ + $player = $server->getPlayerExact($this->playerName); + if($player instanceof DesktopPlayer){ + if(($payload = $this->getResult()) !== null){ + $player->bigBrother_sendChunk($this->chunkX, $this->chunkZ, $payload); + } + } + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/OnlineProfile.php b/src/shoghicp/BigBrother/tasks/OnlineProfile.php new file mode 100755 index 00000000..cecee5ef --- /dev/null +++ b/src/shoghicp/BigBrother/tasks/OnlineProfile.php @@ -0,0 +1,91 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\tasks; + +use pocketmine\scheduler\AsyncTask; +use pocketmine\Server; +use pocketmine\utils\Utils; +use shoghicp\BigBrother\DesktopPlayer; + +class OnlineProfile extends AsyncTask{ + + protected $clientID; + protected $username; + protected $player; + + public function __construct($clientID, $username, $player){ + $this->clientID = $clientID; + $this->username = $username; + $this->player = $player; + } + + public function onRun(){ + /*$ch = curl_init("https://api.mojang.com/profiles/minecraft"); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); + curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([$this->username])); + curl_setopt($ch, CURLOPT_AUTOREFERER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 PocketMine-MP", "Content-Type: application/json")); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 3); + $ret = json_decode(curl_exec($ch), true); + curl_close($ch); + + if(!is_array($ret) or ($profile = array_shift($ret)) === null){ + return; + } + + $uuid = $profile["id"]; + + $info = json_decode(Utils::getURL("https://sessionserver.mojang.com/session/minecraft/profile/$uuid", 3), true); + + if(!is_array($info)){ + return; + }*/ + $profile = json_decode('{"id":"c96792ac7aea4f16975e535a20a2791a","name":"Hello"}',true);//json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); + if(!is_array($profile)){ + return false; + } + + $uuid = $profile["id"]; + $info = json_decode('{"id":"c96792ac7aea4f16975e535a20a2791a","name":"Hello","properties":[{"name":"textures","value":"eyJ0aW1lc3RhbXAiOjE0Njc1OTk2OTkyODQsInByb2ZpbGVJZCI6ImM5Njc5MmFjN2FlYTRmMTY5NzVlNTM1YTIwYTI3OTFhIiwicHJvZmlsZU5hbWUiOiJIZWxsbyIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9lYzNmMDc2MTliNmFjMzczMGZkYzMxZmExZWMxY2JkMmE4ZjhkZmJkOTdkYzhhYWE4ZTI0NWJhODVhZTlmNzYifX19"}]}', true); + if(!is_array($info)){ + return false; + } + $this->setResult($info); + } + + public function onCompletion(Server $server){ + //foreach($server->getOnlinePlayers() as $clientID => $player){ + //if($player instanceof DesktopPlayer and $clientID === $this->clientID){ + $result = $this->getResult(); + + if(is_array($result) and isset($result["id"])){ + $this->player->bigBrother_authenticate($this->username, $result["id"], $result["properties"]); + }else{ + $this->player->bigBrother_authenticate($this->username, "00000000000040008000000000000000", null); + } + //break; + //} + //} + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/utils/Binary.php b/src/shoghicp/BigBrother/utils/Binary.php index 422e8b98..2dd4fbb5 100755 --- a/src/shoghicp/BigBrother/utils/Binary.php +++ b/src/shoghicp/BigBrother/utils/Binary.php @@ -19,7 +19,6 @@ use phpseclib\Math\BigInteger; use shoghicp\BigBrother\network\Session; -use pocketmine\entity\Entity; class Binary extends \pocketmine\utils\Binary{ @@ -29,48 +28,47 @@ public static function sha1($input){ return ($zero->compare($number) <= 0 ? "":"-") . ltrim($number->toHex(), "0"); } + public static function UUIDtoString($uuid){ + return substr($uuid, 0, 8) ."-". substr($uuid, 8, 4) ."-". substr($uuid, 12, 4) ."-". substr($uuid, 16, 4) ."-". substr($uuid, 20); + } + public static function writeMetadata(array $data){ $m = ""; foreach($data as $bottom => $d){ - if($d[0] !== 6){//6 not use - $m .= chr(($d[0] << 5) | ($bottom & 0x1F)); - switch($d[0]){ - case Entity::DATA_TYPE_BYTE://0 - $m .= self::writeByte($d[1]); - break; - case Entity::DATA_TYPE_SHORT://1 - $m .= self::writeShort($d[1]); - break; - case Entity::DATA_TYPE_INT://2 - $m .= self::writeInt($d[1]); - break; - case Entity::DATA_TYPE_FLOAT://3 - $m .= self::writeFloat($d[1]); - break; - case Entity::DATA_TYPE_STRING://4 - $m .= self::writeVarInt(strlen($d[1])) . $d[1]; - break; - case Entity::DATA_TYPE_SLOT://5 - $item = $d[1]; - if($item->getID() === 0){ - $m .= self::writeShort(-1); - }else{ - $m .= self::writeShort($item->getID()); - $m .= self::writeByte($item->getCount()); - $m .= self::writeShort($item->getDamage()); - $nbt = $item->getCompoundTag(); - $m .= self::writeByte(strlen($nbt)).$nbt; - } - break; - case Entity::DATA_TYPE_ROTATION://7 - $m .= self::writeFloat($d[1][0]); - $m .= self::writeFloat($d[1][1]); - $m .= self::writeFloat($d[1][2]); + $m .= chr(($d["type"] << 5) | ($bottom & 0x1F)); + switch($d["type"]){ + case 0: + $m .= self::writeByte($d["value"]); + break; + case 1: + $m .= self::writeShort($d["value"]); + break; + case 2: + $m .= self::writeInt($d["value"]); + break; + case 3: + $m .= self::writeFloat($d["value"]); + break; + case 4: + $m .= self::writeVarInt(strlen($d["value"])) . $d["value"]; + break; + case 5: + /** @var \pocketmine\item\Item $item */ + $item = $d["value"]; + if($item->getID() === 0){ + $m .= self::writeShort(-1); + }else{ + $m .= self::writeShort($item->getID()); + $m .= self::writeByte($item->getCount()); + $m .= self::writeShort($item->getDamage()); + $m .= self::writeShort(-1); + } + break; + case 6: + $m .= self::writeInt($d["value"][0]); + $m .= self::writeInt($d["value"][1]); + $m .= self::writeInt($d["value"][2]); break; - case Entity::DATA_TYPE_LONG://8 - $m .= self::writeLong($d[1]); - break; - } } } $m .= "\x7f"; From b12cb80a1264a41cb576db0ea28ef779037545c2 Mon Sep 17 00:00:00 2001 From: va2ron1 Date: Tue, 5 Jul 2016 18:17:15 -0400 Subject: [PATCH 07/21] Create README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..26cc3406 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# BigBrother +Allows the connection of Minecraft: PC clients to PocketMine-MP servers. Made for PocketMine-MP + +###Changelog: Update 3: +* Change the Source to shoghicp and rewrite the previous changes. +* Back again to protocol 1.8 (but is giving the same results in 1.10.0) +* I've set a fixed user (Hello) until the plugin works properly again (Remember to change the user to test it) +* Current issue, user logged in and kicked out for being in a different world. + +##Any help to make this plugin working again, will be appreciated. From 2a914a1133d82caeaea05ea6f259b7c70e48b99c Mon Sep 17 00:00:00 2001 From: va2ron1 Date: Tue, 5 Jul 2016 19:43:43 -0400 Subject: [PATCH 08/21] Delete DesktopChunk.php --- src/shoghicp/BigBrother/DesktopChunk.php | 94 ------------------------ 1 file changed, 94 deletions(-) delete mode 100755 src/shoghicp/BigBrother/DesktopChunk.php diff --git a/src/shoghicp/BigBrother/DesktopChunk.php b/src/shoghicp/BigBrother/DesktopChunk.php deleted file mode 100755 index ae7cfec8..00000000 --- a/src/shoghicp/BigBrother/DesktopChunk.php +++ /dev/null @@ -1,94 +0,0 @@ -player = $player; - $this->chunkX = $chunkX; - $this->chunkZ = $chunkZ; - $this->provider = $provider = $player->getLevel()->getProvider(); - $this->data = $this->generateChunk(); - } - - public function generateChunk(){ - $chunk = $this->provider->getChunk($this->chunkX, $this->chunkZ, false); - $chunkblockIds = $chunk->getBlockIdArray(); - $chunkblockData = $chunk->getBlockDataArray(); - $chunkblockSkyLight = $chunk->getBlockSkyLightArray(); - $chunkblockLight = $chunk->getBlockLightArray(); - - $chunkbiomeIds = $chunk->getBiomeIdArray(); - - $compressionLevel = Level::$COMPRESSION_LEVEL; - - $ids = ["", "", "", "", "", "", "", ""]; - $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; - - //Complexity: O(MG) - for($Y = 0; $Y < 8; ++$Y){ - for($y = 0; $y < 16; ++$y){ - $offset = ($Y << 4) + $y; - for($z = 0; $z < 16; ++$z){ - for($x = 0; $x < 16; ++$x){ - $index = ($x << 11) + ($z << 7) + $offset; - $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); - if(($y & 1) === 0){ - $data = ord($chunkblockData[$halfIndex]) & 0x0F; - $bLight = ord($chunkblockLight[$halfIndex]) & 0x0F; - - //$sLight = ord($blockSkyLight[$halfIndex]) & 0x0F; - }else{ - $data = ord($chunkblockData[$halfIndex]) >> 4; - $bLight = ord($chunkblockLight[$halfIndex]) >> 4; - //$sLight = ord($blockSkyLight[$halfIndex]) >> 4; - } - $ids[$Y] .= pack("v", (ord($chunkblockIds[$index]) << 4) | $data); - - $blockLight[$Y][] = $bLight; - //$skyLight[$Y][] = $sLight; - } - } - } - } - - foreach($blockLight as $Y => $data){ - $final = ""; - $len = count($data); - for($i = 0; $i < $len; $i += 2){ - $final .= chr(($data[$i + 1] << 4) | $data[$i]); - } - $blockLight[$Y] = $final; - } - - /* - foreach($skyLight as $Y => $data){ - $final = ""; - $len = count($data); - for($i = 0; $i < $len; $i += 2){ - $final .= chr(($data[$i + 1] << 4) | $data[$i]); - } - $skyLight[$Y] = $final; - } - */ - - $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; - - $payload = implode($ids) . implode($blockLight) . implode($skyLight) . $chunkbiomeIds; - - return $payload; - } - - public function getData(){ - if(isset($this->data)){ - return $this->data; - } - return null; - } - -} \ No newline at end of file From 0d09c97e809f2331e7b31e9081d9764288109bd2 Mon Sep 17 00:00:00 2001 From: va2ron1 Date: Tue, 5 Jul 2016 19:50:46 -0400 Subject: [PATCH 09/21] "close" function has been deleted --- src/shoghicp/BigBrother/DesktopPlayer.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index fed9a107..00df373b 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -458,19 +458,6 @@ public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $ } - public function close($message = "", $reason = "generic reason",$notify = true){ - if($this->bigBrother_status === 0){ - $pk = new LoginDisconnectPacket(); - $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); - $this->putRawPacket($pk); - }else{ - $pk = new PlayDisconnectPacket(); - $pk->reason = TextFormat::toJSON($reason === "" ? "You have been disconnected." : $reason); - $this->putRawPacket($pk); - } - parent::close($message, $reason); - } - public function bigBrother_setCompression($threshold){ $this->interface->setCompression($this, $threshold); } From 832422932f9f109f79bf2dd5b387b67818d8d79b Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Wed, 6 Jul 2016 13:32:16 -0400 Subject: [PATCH 10/21] added iPocketTeam DesktopChunk.php --- src/shoghicp/BigBrother/DesktopChunk.php | 94 +++++++++++++++ src/shoghicp/BigBrother/DesktopPlayer.php | 136 +++++++--------------- 2 files changed, 134 insertions(+), 96 deletions(-) create mode 100755 src/shoghicp/BigBrother/DesktopChunk.php diff --git a/src/shoghicp/BigBrother/DesktopChunk.php b/src/shoghicp/BigBrother/DesktopChunk.php new file mode 100755 index 00000000..ae7cfec8 --- /dev/null +++ b/src/shoghicp/BigBrother/DesktopChunk.php @@ -0,0 +1,94 @@ +player = $player; + $this->chunkX = $chunkX; + $this->chunkZ = $chunkZ; + $this->provider = $provider = $player->getLevel()->getProvider(); + $this->data = $this->generateChunk(); + } + + public function generateChunk(){ + $chunk = $this->provider->getChunk($this->chunkX, $this->chunkZ, false); + $chunkblockIds = $chunk->getBlockIdArray(); + $chunkblockData = $chunk->getBlockDataArray(); + $chunkblockSkyLight = $chunk->getBlockSkyLightArray(); + $chunkblockLight = $chunk->getBlockLightArray(); + + $chunkbiomeIds = $chunk->getBiomeIdArray(); + + $compressionLevel = Level::$COMPRESSION_LEVEL; + + $ids = ["", "", "", "", "", "", "", ""]; + $blockLight = $skyLight = [[], [], [], [], [], [], [], []]; + + //Complexity: O(MG) + for($Y = 0; $Y < 8; ++$Y){ + for($y = 0; $y < 16; ++$y){ + $offset = ($Y << 4) + $y; + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $index = ($x << 11) + ($z << 7) + $offset; + $halfIndex = ($x << 10) + ($z << 6) + ($offset >> 1); + if(($y & 1) === 0){ + $data = ord($chunkblockData[$halfIndex]) & 0x0F; + $bLight = ord($chunkblockLight[$halfIndex]) & 0x0F; + + //$sLight = ord($blockSkyLight[$halfIndex]) & 0x0F; + }else{ + $data = ord($chunkblockData[$halfIndex]) >> 4; + $bLight = ord($chunkblockLight[$halfIndex]) >> 4; + //$sLight = ord($blockSkyLight[$halfIndex]) >> 4; + } + $ids[$Y] .= pack("v", (ord($chunkblockIds[$index]) << 4) | $data); + + $blockLight[$Y][] = $bLight; + //$skyLight[$Y][] = $sLight; + } + } + } + } + + foreach($blockLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $blockLight[$Y] = $final; + } + + /* + foreach($skyLight as $Y => $data){ + $final = ""; + $len = count($data); + for($i = 0; $i < $len; $i += 2){ + $final .= chr(($data[$i + 1] << 4) | $data[$i]); + } + $skyLight[$Y] = $final; + } + */ + + $skyLight = [$half = str_repeat("\xff", 4096), $half, $half, $half, $half, $half, $half, $half]; + + $payload = implode($ids) . implode($blockLight) . implode($skyLight) . $chunkbiomeIds; + + return $payload; + } + + public function getData(){ + if(isset($this->data)){ + return $this->data; + } + return null; + } + +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index 00df373b..ba2b258d 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -17,6 +17,7 @@ namespace shoghicp\BigBrother; +use pocketmine\event\Timings; use pocketmine\event\player\PlayerJoinEvent; use pocketmine\event\player\PlayerRespawnEvent; use pocketmine\level\format\anvil\Chunk as AnvilChunk; @@ -149,130 +150,73 @@ public function bigBrother_getStatus(){ return $this->bigBrother_status; } - public function bigBrother_sendChunk($x, $z, $payload){ + public function sendChunk($x, $z, $payload,$ordering = FullChunkDataPacket::ORDER_COLUMNS){ + + } + + public function bigBrother_sendChunk($x, $z, $payload){ + if($this->connected === false){ + return; + } + $this->usedChunks[Level::chunkHash($x, $z)] = true; + $this->chunkLoadCount++; $pk = new ChunkDataPacket(); $pk->chunkX = $x; $pk->chunkZ = $z; $pk->groundUp = true; - $pk->payload = $payload; $pk->primaryBitmap = 0xff; $this->putRawPacket($pk); + foreach($this->level->getChunkTiles($x, $z) as $tile){ + if($tile instanceof Sign){ + $tile->spawnTo($this); + } + } + if($this->spawned){ + foreach($this->level->getChunkPlayers($x, $z) as $player){ + $player->spawnTo($this); + } + /*foreach($this->level->getChunkEntities($x, $z) as $entity){ + if($entity !== $this and !$entity->closed and $entity->isAlive()){ + $entity->spawnTo($this); + } + }*/ + } } - - public function sendChunk($x, $z, $payload,$ordering = FullChunkDataPacket::ORDER_COLUMNS){ - - } - protected function sendNextChunk(){ if($this->connected === false){ return; } - + Timings::$playerChunkSendTimer->startTiming(); $count = 0; foreach($this->loadQueue as $index => $distance){ if($count >= $this->chunksPerTick){ break; } - + $X = null; + $Z = null; Level::getXZ($index, $X, $Z); - if(!$this->level->isChunkPopulated($X, $Z)){ - $this->level->generateChunk($X, $Z); - if($this->spawned){ + ++$count; + $this->usedChunks[$index] = false; + $this->level->registerChunkLoader($this, $X, $Z, false); + if(!$this->level->populateChunk($X, $Z)){ + if($this->spawned and $this->teleportPosition === null){ continue; }else{ break; } } - - ++$count; - unset($this->loadQueue[$index]); - $this->usedChunks[$index] = true; - - $this->level->useChunk($X, $Z, $this); - $chunk = $this->level->getChunk($X, $Z); - if($chunk instanceof AnvilChunk){ - $this->kick("Playing on Anvil worlds is not yet implemented"); - //TODO! - /*$pk = new ChunkDataPacket(); - $pk->chunkX = $X; - $pk->chunkZ = $Z; - $pk->groundUp = true; - $ids = ""; - $meta = ""; - $blockLight = ""; - $skyLight = ""; - $biomeIds = $chunk->getBiomeIdArray(); - $bitmap = 0; - for($s = 0; $s < 8; ++$s){ - $section = $chunk->getSection($s); - if(!($section instanceof EmptyChunkSection)){ - $bitmap |= 1 << $s; - }else{ - continue; - } - $ids .= $section->getIdArray(); - $meta .= $section->getDataArray(); - $blockLight .= $section->getLightArray(); - $skyLight .= $section->getSkyLightArray(); - } - - $pk->payload = zlib_encode($ids . $meta . $blockLight . $skyLight . $biomeIds, ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL); - $pk->primaryBitmap = $bitmap; - $this->putRawPacket($pk);*/ - }elseif($chunk instanceof McRegionChunk){ - $task = new McRegionToAnvil($this, $chunk); - $this->server->getScheduler()->scheduleAsyncTask($task); - }elseif($chunk instanceof LevelDBChunk){ - $task = new LevelDBToAnvil($this, $chunk); - $this->server->getScheduler()->scheduleAsyncTask($task); - } - - foreach($chunk->getEntities() as $entity){ - if($entity !== $this){ - $entity->spawnTo($this); - } - } - foreach($chunk->getTiles() as $tile){ - if($tile instanceof Spawnable){ - $tile->spawnTo($this); - } - } + $chunk = new DesktopChunk($this, $X, $Z); + $this->bigBrother_sendChunk($X, $Z, $chunk->getData()); + $chunk = null; } - - if(count($this->usedChunks) >= 4 and $this->spawned === false){ - - $this->bigBrother_setTitleBar(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother.", 0); - - $this->spawned = true; - - $pk = new SetTimePacket(); - $pk->time = $this->level->getTime(); - $pk->started = $this->level->stopTime == false; - $this->dataPacket($pk); - - $pos = $this->level->getSafeSpawn($this); - - $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); - - $this->teleport($ev->getRespawnPosition()); - - $this->sendSettings(); + if($this->chunkLoadCount >= 4 and $this->spawned === false and $this->teleportPosition === null){ + $this->doFirstSpawn(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); - - $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, TextFormat::YELLOW . $this->getName() . " joined the game")); - if(strlen(trim($ev->getJoinMessage())) > 0){ - $this->server->broadcastMessage($ev->getJoinMessage()); - } - - $this->spawnToAll(); - - if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ - $this->server->getUpdater()->showPlayerUpdate($this); - } } + Timings::$playerChunkSendTimer->stopTiming(); } public function spawnTo(Player $player){ From d3c611b477dcfc0d9afe979cf941fb5eb07e8562 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Wed, 6 Jul 2016 23:11:08 -0400 Subject: [PATCH 11/21] works again --- config.yml | 24 +++ server-icon.png | Bin 0 -> 1000 bytes src/shoghicp/BigBrother/DesktopPlayer.php | 8 +- src/shoghicp/BigBrother/network/Packet.php | 173 ++++++++++++------ .../network/protocol/LoginSuccessPacket.php | 2 +- 5 files changed, 148 insertions(+), 59 deletions(-) create mode 100644 config.yml create mode 100644 server-icon.png diff --git a/config.yml b/config.yml new file mode 100644 index 00000000..0400b0b7 --- /dev/null +++ b/config.yml @@ -0,0 +1,24 @@ +#Configuration file for BigBrother plugin + +#Do not change the interface if you don't know what you are doing. It's good as it is now +interface: 0.0.0.0 +port: 25565 + +network-compression-threshold: 256 + +#Sets the server description on the server list +motd: "§bPocketMine-MP server using §6§lBigBrother§r§b plugin\n§aConnect to Minecraft: PE servers from PC clients" + +#Use Mojang services to authenticate players +online-mode: false + +#Automatically authenticate players on SimpleAuth that were authenticated via Mojang servers +online-mode-simpleauth: false + +#Action when there is a nick conflict +#kickpe: kicks the Pocket Edition player +#kickpc: kicks the PC player +#kickold: kicks the player that was on the server +#kicknew: kicks the player that is connecting +#none: do nothing, leave it up to PocketMine-MP and other plugins +action-on-conflict: none \ No newline at end of file diff --git a/server-icon.png b/server-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1a5bef5b4f2fa8721ad135edbb5a3c767de6c0b4 GIT binary patch literal 1000 zcmV>P)000McNliru-U0~?FBzSO$m;+A19eG6 zK~#9!?OV%E6hRcNc4ipZO^gBK69IvsxS)Gb1`}ro_yZb|5ED&|iGG2sh+kl$1~qCl z8b3e@i7^;p;ObJHa6fv zKA#s~*i0tzfsYur9c~BPTb%^kLW&wIVu4-yTLjy#O<{j)8T^7(5wK6peF>ytN&y8R z>>5yx^NH>o$Z-ofH?xEsH%Ct+hhs$g;trVZ_bITQ#`EHM?Ai{}gwHtMuIs32BTqz) zQwk^mVb=imi@!S0qF-J`j@pfBu#>H6@dsuW#;|=0a@^WRKs&dJoc;I|5svxTzO;)PG5QiUBHf3a zkKs46k9E=~j8OlWOBl*Ec)m&j1t9Dizz^Em`hxX9oO2o}R$m^YTws?p0{yy|LBH=8 z$j?Vmquj-D+Hb%qSru_z0SbTu5I_UGi5KX1>pCcZ&xrZGa}xdZUFW7afS&#G0Eho9 z;)6p?Xs=;xZ>M_2{?cZ>7y5MtC;$pT01e>#_kIaAz)oJl z{Q!fpxI6v^d!#Kwrvu-2C*KAdAjk4f`&Akbon`+1hMKX0_Xv;Be3b$UK-e{a|6^0< z&0vc%_XV8ecsJPlF)Oz}cM0=*a~T`ZzJ9W)J=l!s{z3g$59?{ZN&y8R>>7}u!#ciC zpMQL(jHBkW!=jiAa9qJ<_9x!?@#$}hc2VBv&ksKm>v_IP0Rid = mt_rand(); $this->putRawPacket($pk); + echo "KeepAlive\n"; } public function bigBrother_getStatus(){ @@ -269,11 +270,11 @@ public function spawnTo(Player $player){ public function bigBrother_authenticate($username, $uuid, $onlineModeData = null){ if($this->bigBrother_status === 0){ $this->bigBrother_uuid = $uuid; - $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toBinary(); + $this->bigBrother_formatedUUID = UUID::fromString($uuid)->toString(); $pk = new LoginSuccessPacket(); $pk->uuid = $this->bigBrother_formatedUUID; - $pk->name = $this->username; + $pk->name = "hello"; $this->putRawPacket($pk); $this->bigBrother_status = 1; if($onlineModeData !== null and is_array($onlineModeData)){ @@ -281,7 +282,7 @@ public function bigBrother_authenticate($username, $uuid, $onlineModeData = null } $this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); - $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 1); + $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], ["hello"]), 2); } } @@ -299,6 +300,7 @@ public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionR public function bigBrother_authenticationCallback($username){ $pk = new LoginPacket(); + echo "User: ".$username."\n"; $pk->username = $username; $pk->clientId = crc32($this->clientID); $pk->protocol = Info::CURRENT_PROTOCOL; diff --git a/src/shoghicp/BigBrother/network/Packet.php b/src/shoghicp/BigBrother/network/Packet.php index a5185cca..42ec4b55 100755 --- a/src/shoghicp/BigBrother/network/Packet.php +++ b/src/shoghicp/BigBrother/network/Packet.php @@ -16,65 +16,109 @@ */ namespace shoghicp\BigBrother\network; - -use pocketmine\utils\BinaryStream; -use pocketmine\utils\Utils; +use pocketmine\item\Item; use shoghicp\BigBrother\utils\Binary; - -abstract class Packet extends BinaryStream{ - - const NETWORK_ID = 0; - - public $isEncoded = false; - private $channel = 0; - - public function pid(){ - return $this::NETWORK_ID; +abstract class Packet extends \stdClass{ + protected $buffer; + protected $offset = 0; + protected function get($len){ + if($len < 0){ + $this->offset = strlen($this->buffer) - 1; + return ""; + }elseif($len === true){ + return substr($this->buffer, $this->offset); + } + $buffer = ""; + for(; $len > 0; --$len, ++$this->offset){ + $buffer .= @$this->buffer{$this->offset}; + } + return $buffer; } - - abstract public function encode(); - - abstract public function decode(); - - public function reset(){ - $this->buffer = chr($this::NETWORK_ID); - $this->offset = 0; + protected function getLong(){ + return Binary::readLong($this->get(8)); } - - /** - * @deprecated This adds extra overhead on the network, so its usage is now discouraged. It was a test for the viability of this. - */ - public function setChannel($channel){ - $this->channel = (int) $channel; - return $this; + protected function getInt(){ + return Binary::readInt($this->get(4)); } - - public function getChannel(){ - return $this->channel; + protected function getPosition(&$x, &$y, &$z){ + $int1 = $this->getInt(); + $int2 = $this->getInt(); + $x = $int1 >> 6; + $y = ((($int1 & 0x3F) << 2) | ($int2 & 0xFCFFFFFF) >> 26); + $z = $int2 & 0x3FFFFFF; + if(PHP_INT_MAX > 0x7FFFFFFF){ + $x = $x << 38 >> 38; + $y = $y << 58 >> 58; + $z = $z << 38 >> 38; + }else{ + $x = $x << 6 >> 6; + $y = $y << 26 >> 26; + $z = $z << 6 >> 6; + } } - - public function clean(){ - $this->buffer = null; - $this->isEncoded = false; - $this->offset = 0; - return $this; + protected function getFloat(){ + return Binary::readFloat($this->get(4)); } - - public function __debugInfo(){ - $data = []; - foreach($this as $k => $v){ - if($k === "buffer"){ - $data[$k] = bin2hex($v); - }elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){ - $data[$k] = Utils::printable((string) $v); - }else{ - $data[$k] = $v; + protected function getDouble(){ + return Binary::readDouble($this->get(8)); + } + /** + * @return Item + */ + protected function getSlot(){ + $itemId = $this->getShort(); + if($itemId === -1){ //Empty + return Item::get(Item::AIR, 0, 0); + }else{ + $count = $this->getByte(); + $damage = $this->getShort(); + $len = $this->getShort(); + if($len > 0){ + $nbt = $this->get($len); } + return Item::get($itemId, $damage, $count); } - - return $data; } - + protected function putSlot(Item $item){ + if($item->getID() === 0){ + $this->putShort(-1); + }else{ + $this->putShort($item->getID()); + $this->putByte($item->getCount()); + $this->putShort($item->getDamage()); + $this->putShort(-1); + } + } + protected function getShort(){ + return Binary::readShort($this->get(2)); + } + protected function getTriad(){ + return Binary::readTriad($this->get(3)); + } + protected function getLTriad(){ + return Binary::readTriad(strrev($this->get(3))); + } + protected function getByte(){ + return ord($this->buffer{$this->offset++}); + } + protected function getString(){ + return $this->get($this->getVarInt()); + } + protected function getVarInt(){ + return Binary::readVarInt($this->buffer, $this->offset); + } + protected function feof(){ + return !isset($this->buffer{$this->offset}); + } + protected function put($str){ + $this->buffer .= $str; + } + protected function putLong($v){ + $this->buffer .= Binary::writeLong($v); + } + protected function putInt($v){ + $this->buffer .= Binary::writeInt($v); + } protected function putPosition($x, $y, $z){ $int2 = ($z & 0x3FFFFFF); //26 bits $int2 |= ($y & 0x3F) << 26; //6 bits @@ -82,17 +126,37 @@ protected function putPosition($x, $y, $z){ $int1 |= ($x & 0x3FFFFFF) << 6; //26 bits $this->buffer .= Binary::writeInt($int1) . Binary::writeInt($int2); } - + protected function putFloat($v){ + $this->buffer .= Binary::writeFloat($v); + } protected function putDouble($v){ $this->buffer .= Binary::writeDouble($v); } - + protected function putShort($v){ + $this->buffer .= Binary::writeShort($v); + } + protected function putTriad($v){ + $this->buffer .= Binary::writeTriad($v); + } + protected function putLTriad($v){ + $this->buffer .= strrev(Binary::writeTriad($v)); + } + protected function putByte($v){ + $this->buffer .= chr($v); + } + protected function putString($v){ + $this->putVarInt(strlen($v)); + $this->put($v); + } protected function putVarInt($v){ $this->buffer .= Binary::writeVarInt($v); } - + public abstract function pid(); + protected abstract function encode(); + protected abstract function decode(); public function write(){ - $this->reset(); + $this->buffer = ""; + $this->offset = 0; $this->encode(); return Binary::writeVarInt($this->pid()) . $this->buffer; } @@ -101,5 +165,4 @@ public function read($buffer, $offset = 0){ $this->offset = $offset; $this->decode(); } - } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php b/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php index 2feb7e65..fa5ef6eb 100755 --- a/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/LoginSuccessPacket.php @@ -29,7 +29,7 @@ public function pid(){ } public function encode(){ - $this->put($this->uuid); + $this->putString($this->uuid); $this->putString($this->name); } From 2b4982453414cd7824063308aa4929809389db02 Mon Sep 17 00:00:00 2001 From: va2ron1 Date: Wed, 6 Jul 2016 23:13:09 -0400 Subject: [PATCH 12/21] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26cc3406..28e55492 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # BigBrother Allows the connection of Minecraft: PC clients to PocketMine-MP servers. Made for PocketMine-MP +###Changelog: Update 4: +* Bring a few changes of iPocketTeam + ###Changelog: Update 3: * Change the Source to shoghicp and rewrite the previous changes. * Back again to protocol 1.8 (but is giving the same results in 1.10.0) * I've set a fixed user (Hello) until the plugin works properly again (Remember to change the user to test it) * Current issue, user logged in and kicked out for being in a different world. -##Any help to make this plugin working again, will be appreciated. +##Works Again (Not properly). From 58b59113b6fbae6000482601818a5ff99ef0f3b7 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 7 Jul 2016 14:53:45 -0400 Subject: [PATCH 13/21] quick fixes --- config.yml | 24 --------- resources/alex.yml | Bin 0 -> 16384 bytes resources/config.yml | 9 +++- resources/steve.yml | Bin 0 -> 16384 bytes server-icon.png | Bin 1000 -> 0 bytes src/shoghicp/BigBrother/DesktopPlayer.php | 47 +++++++++--------- .../BigBrother/network/ProtocolInterface.php | 6 +-- .../protocol/EncryptionRequestPacket.php | 10 ++-- .../BigBrother/tasks/OnlineProfile.php | 1 + 9 files changed, 40 insertions(+), 57 deletions(-) delete mode 100644 config.yml create mode 100755 resources/alex.yml create mode 100755 resources/steve.yml delete mode 100644 server-icon.png diff --git a/config.yml b/config.yml deleted file mode 100644 index 0400b0b7..00000000 --- a/config.yml +++ /dev/null @@ -1,24 +0,0 @@ -#Configuration file for BigBrother plugin - -#Do not change the interface if you don't know what you are doing. It's good as it is now -interface: 0.0.0.0 -port: 25565 - -network-compression-threshold: 256 - -#Sets the server description on the server list -motd: "§bPocketMine-MP server using §6§lBigBrother§r§b plugin\n§aConnect to Minecraft: PE servers from PC clients" - -#Use Mojang services to authenticate players -online-mode: false - -#Automatically authenticate players on SimpleAuth that were authenticated via Mojang servers -online-mode-simpleauth: false - -#Action when there is a nick conflict -#kickpe: kicks the Pocket Edition player -#kickpc: kicks the PC player -#kickold: kicks the player that was on the server -#kicknew: kicks the player that is connecting -#none: do nothing, leave it up to PocketMine-MP and other plugins -action-on-conflict: none \ No newline at end of file diff --git a/resources/alex.yml b/resources/alex.yml new file mode 100755 index 0000000000000000000000000000000000000000..d38f8d4a73638fc44e4d0eee89caf37aebf26106 GIT binary patch literal 16384 zcmeI2&ud*p6vz8dh`QB97cN9m3avyJ#x|rHf)WT#A&p2Oh(RpTNHMx-?JpE`Bk0PF z7Nq{FXiF7bh|-OsXj_$TTCge&WZTU+pOg6x=T2W<-hCw8XL^>3uY&VRy=*Rqrb3tUsvmX_Q8*OR%!9aTRTjPzup_o>?e=x4c?^7;1hPg zLzv-(RdrzesRrPL``wqvD#jSG?q?tTDEY7OyY;~Mt-y~4mj9G<_2Iw#-TRw6|Hbpp zYCn#l2e#SyjhDPD_pbguzkl|ry&2ZO)qSb~<-DwaYG5@R^e3F2>EaZl^_6q)ecRQ7 z&Lgk$<+>@3)A^g$C*NPnvF<;Q7x%68pXbHnx@o4E=I4GoZ`1tc{fL`!yJx2h?En1y znVILYW8jIFk4tTTe#OiW2tOP?r`#KTjj4@?efiRvaOM1mW?3J94#!<;Bz|d(Z#w_t z6pJ`vr48$i8#lsT`|dXO_>@bX!}Cc4#j*xz-3$bO%9+?JFxE7GdHts!xhHtrx^-*t zR?aW`3#aG9p~KVp|6;xaV&dQb=)IwvmFFw_3#aCT?82G1EnoO@UcY_MPTRB7UhBN# zM*|uB_UHiDnECk@upt=IT^<=>to=`+Nhb?LdVF8NPu zU_JP4Em2RYrntYCo_Z)OPCgu-9l77?6-^k&AOHHO*}+d20%pg#r-14!_nsd@}LX z<)1%w!0v;4Km$y}Klu3ykIhfDLQXsJn|A0I?aB`yRE&C(b&K`NzkU1mAXBbi zd~!#?U}Q{j{P6H_#WH`&r6^^YoVxtVb0>bqI`QAW?Y3~|&>cbkiN#T?d-2P|LX5ec<`bX) z_=U=`%!k{X@u&a47>8T&{C`=E{8Pi&`1rWhIyFBtGh?RuC#HELBO_sKY|MF*Nu)>e6fe#RcP@J$r7df5z(Y zYYod&%e2+6@E7Y}b?@hkTe*hYEOII)4K!Y7{SOTd1sSa5er|5gj5cG6<9F`d8AeA( z&1hHFbK>E1}* zoBY;!e?a@&^4jOcYfRq9n&K_5%igF=HBjgMfs##o*q)U+^ohxr_t8|}?~kKH-S-E? zCSLK2m3=K@w!anmEnZ`PccXRj{IommpZhuNxBB-dJRke{et%!w?C%dczupn(#IG1S zbmE6iR_^a>9PeWro!4H^@AuDD|8?FU5MSQIPW$_?t`@7azuhZGzw|3aSd903zw3RRe>b3?_Xpt8I)~Hm;fq^!uo3+K0gzn6O8@`> literal 0 HcmV?d00001 diff --git a/resources/config.yml b/resources/config.yml index 0400b0b7..27439153 100755 --- a/resources/config.yml +++ b/resources/config.yml @@ -21,4 +21,11 @@ online-mode-simpleauth: false #kickold: kicks the player that was on the server #kicknew: kicks the player that is connecting #none: do nothing, leave it up to PocketMine-MP and other plugins -action-on-conflict: none \ No newline at end of file +action-on-conflict: none + +#This is Skin Settings +skin-yml: "steve.yml" +skin-slim: false + +#This is ResourcePackURL Settings +resourcepackurl: "false" \ No newline at end of file diff --git a/resources/steve.yml b/resources/steve.yml new file mode 100755 index 0000000000000000000000000000000000000000..0d40abfcc3f7caf97d2c06eefb493019453a41ff GIT binary patch literal 16384 zcmeI2-)~e!6vy{XDIg^pNr8eE>RK>M1s^EUBFHaeG?r+T8YEz(wlpLSY9cY95}}%? zPb83viHQj@8XgpVF!<$wJLk-FKHoFhAA^5cm6Y=yrC<8=8;Pu{K{htIxz=v%`2UFj`}v&?4x$;qjj=oYO#`Fbj}&)F{@9kX3q*Tx)j8N=A@UI}wDH~es8Xd^v3g+JD*C+y>m ztLJMP?gjQRYp*=nZ9jkdzWsh_GWuUHPTI`WJNDP~l+AqbN;GoFWejuCX0CFWlWWyp z+<0k%Z+HE*Px1XdE3NPGhwbuPLpF2jfDM%&jqG!Cb2jq)wB0^`I;cH5f{P%};^RsrXA3t;YaIEK7Z||{l zhbwmWz;>J5KM+~r{p8i1_Qe}}qR}RY+#kk{#Ie`kdwJehHDpe7RQ=`YBi3&I^!LAc zWY)Zu%VqPH=HGQaJ6rerP$|SpCr&iByCzIJZr-#w4|9b!-#K3&+?x3$`<^d7J8jQS zJ9omcuJn7buA9OSx3dSDKh)pX9xZa~latVl`q%q=peglF_Wx4(ca*0L>wjW2`h!D1 znKvJ!?PEiO(^k50;XfN4wbHd~R=RN`J}>!<rXMERty@;YbX_lBIcZ$u z<#XySzWd=vi^xAVW+jZZXx&u}GIp(p9OdK5SIm3O<8v8Hj>e#!pHFc=&m#HJA+0Zb z(x+a00gM0rtdaGxAGBW9<#}lPwWe*xrF?!a^3!-aN2~eS^J1dyf4}&BU%h;EA`d>_ zPWhNq^Zift_j`bQ-dkjcNygf>e%7hB`jsbNi&g|?Ogu!{$%Zq_kk{O>X&aPf35pNdPKg!<@HMQm7_79Upk08?){*F zxD~%w{=EHB#0v!q1quZU1quZU1quZU1quZU1quZU1quZU1quaH1(tk%$~z#PZ-@3$ z&o|TcwfcI={9>>A2aey@KWNbG|8MB{`g{J+?sR_MVfuHP{@o>QAET}Jy2^|1%=PXZ z4}bODcg_D7NY4pcUC^BQ7t8P8cZ(gLogZ)(1i#LAr2~CBEA!_?(jjMktqw1S-|qp> zUXx#3+5?`SwvW?p^H0>P)000McNliru-U0~?FBzSO$m;+A19eG6 zK~#9!?OV%E6hRcNc4ipZO^gBK69IvsxS)Gb1`}ro_yZb|5ED&|iGG2sh+kl$1~qCl z8b3e@i7^;p;ObJHa6fv zKA#s~*i0tzfsYur9c~BPTb%^kLW&wIVu4-yTLjy#O<{j)8T^7(5wK6peF>ytN&y8R z>>5yx^NH>o$Z-ofH?xEsH%Ct+hhs$g;trVZ_bITQ#`EHM?Ai{}gwHtMuIs32BTqz) zQwk^mVb=imi@!S0qF-J`j@pfBu#>H6@dsuW#;|=0a@^WRKs&dJoc;I|5svxTzO;)PG5QiUBHf3a zkKs46k9E=~j8OlWOBl*Ec)m&j1t9Dizz^Em`hxX9oO2o}R$m^YTws?p0{yy|LBH=8 z$j?Vmquj-D+Hb%qSru_z0SbTu5I_UGi5KX1>pCcZ&xrZGa}xdZUFW7afS&#G0Eho9 z;)6p?Xs=;xZ>M_2{?cZ>7y5MtC;$pT01e>#_kIaAz)oJl z{Q!fpxI6v^d!#Kwrvu-2C*KAdAjk4f`&Akbon`+1hMKX0_Xv;Be3b$UK-e{a|6^0< z&0vc%_XV8ecsJPlF)Oz}cM0=*a~T`ZzJ9W)J=l!s{z3g$59?{ZN&y8R>>7}u!#ciC zpMQL(jHBkW!=jiAa9qJ<_9x!?@#$}hc2VBv&ksKm>v_IP0Rid = mt_rand(); $this->putRawPacket($pk); - echo "KeepAlive\n"; } public function bigBrother_getStatus(){ @@ -155,7 +154,7 @@ public function sendChunk($x, $z, $payload,$ordering = FullChunkDataPacket::ORDE } - public function bigBrother_sendChunk($x, $z, $payload){ + public function bigBrother_sendChunk($x, $z, $payload){ if($this->connected === false){ return; } @@ -274,7 +273,7 @@ public function bigBrother_authenticate($username, $uuid, $onlineModeData = null $pk = new LoginSuccessPacket(); $pk->uuid = $this->bigBrother_formatedUUID; - $pk->name = "hello"; + $pk->name = $username; $this->putRawPacket($pk); $this->bigBrother_status = 1; if($onlineModeData !== null and is_array($onlineModeData)){ @@ -282,7 +281,7 @@ public function bigBrother_authenticate($username, $uuid, $onlineModeData = null } $this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); - $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], ["hello"]), 2); + $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 2); } } @@ -300,28 +299,37 @@ public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionR public function bigBrother_authenticationCallback($username){ $pk = new LoginPacket(); - echo "User: ".$username."\n"; - $pk->username = $username; + $pk->username = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $username); $pk->clientId = crc32($this->clientID); $pk->protocol = Info::CURRENT_PROTOCOL; $pk->clientUUID = UUID::fromString($this->bigBrother_uuid); $pk->serverAddress = "127.0.0.1:25565"; $pk->clientSecret = "BigBrother"; - foreach($this->bigBrother_properties as $property){ + /*foreach($this->bigBrother_properties as $property){ if($property["name"] === "textures"){ $skindata = json_decode(base64_decode($property["value"]), true); if(isset($skindata["textures"]["SKIN"]["url"])){ $skin = $this->getSkinImage($skindata["textures"]["SKIN"]["url"]); } } - } - if(!isset($skindata["textures"]["SKIN"]["metadata"]["model"])){ - $pk->skinId = "Standard_Custom"; + }*/ + + if(!isset($skin)){ + if($this->plugin->getConfig()->get("skin-slim")){ + $pk->skinId = "Standard_Custom"; + }else{ + $pk->skinId = "Standard_CustomSlim"; + } + $pk->skin = file_get_contents($this->plugin->getDataFolder().$this->plugin->getConfig()->get("skin-yml")); }else{ - $pk->skinId = "Standard_CustomSlim"; + if(!isset($skindata["textures"]["SKIN"]["metadata"]["model"])){ + $pk->skinId = "Standard_Custom"; + }else{ + $pk->skinId = "Standard_CustomSlim"; + } + $pk->skin = $skin; } - $pk->skin = $skin; $this->handleDataPacket($pk); } @@ -385,20 +393,11 @@ public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $ $pk->verifyToken = $this->bigBrother_checkToken = Utils::getRandomBytes(4, false, true, $pk->publicKey); $this->putRawPacket($pk); }else{ - /*$task = new OnlineProfile($this->clientID, $this->bigBrother_username, $this); - $this->server->getScheduler()->scheduleAsyncTask($task);*/ - - $profile = json_decode('{"id":"c96792ac7aea4f16975e535a20a2791a","name":"Hello"}',true);//json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); + /*$profile = json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); if(!is_array($profile)){ return false; - } - - $uuid = $profile["id"]; - $info = json_decode('{"id":"c96792ac7aea4f16975e535a20a2791a","name":"Hello","properties":[{"name":"textures","value":"eyJ0aW1lc3RhbXAiOjE0Njc1OTk2OTkyODQsInByb2ZpbGVJZCI6ImM5Njc5MmFjN2FlYTRmMTY5NzVlNTM1YTIwYTI3OTFhIiwicHJvZmlsZU5hbWUiOiJIZWxsbyIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9lYzNmMDc2MTliNmFjMzczMGZkYzMxZmExZWMxY2JkMmE4ZjhkZmJkOTdkYzhhYWE4ZTI0NWJhODVhZTlmNzYifX19"}]}', true); - if(!is_array($info)){ - return false; - } - $this->bigBrother_authenticate($this->bigBrother_username, $info["id"], $info["properties"]); + }*/ + $this->bigBrother_authenticate($this->bigBrother_username, /*$profile["id"]*/"c96792ac7aea4f16975e535a20a2791a", null); } } diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index 513a6e05..35c09667 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -197,9 +197,9 @@ protected function handlePacket(DesktopPlayer $player, $payload){ $this->receivePacket($player, $pk); }elseif($status === 0){ if($pid === 0x00){ - $pk = new LoginStartPacket(); - $pk->read($payload, $offset); - $player->bigBrother_handleAuthentication($this->plugin, $pk->name, $this->plugin->isOnlineMode()); + //$pk = new LoginStartPacket(); + //$pk->read($payload, $offset); + $player->bigBrother_handleAuthentication($this->plugin, $payload, $this->plugin->isOnlineMode()); }elseif($pid === 0x01 and $this->plugin->isOnlineMode()){ $pk = new EncryptionResponsePacket(); $pk->read($payload, $offset); diff --git a/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php b/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php index 7f4b17fe..41849422 100755 --- a/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/EncryptionRequestPacket.php @@ -31,13 +31,13 @@ public function pid(){ public function encode(){ $this->putString($this->serverID); - $this->putVarInt(strlen($this->publicKey)); - $this->put($this->publicKey); - $this->putVarInt(strlen($this->verifyToken)); - $this->put($this->verifyToken); + $this->putString($this->publicKey); + $this->putString($this->verifyToken); } public function decode(){ - + $this->serverID = $this->getString(); + $this->publicKey = $this->get($this->getVarInt()); + $this->verifyToken = $this->get($this->getVarInt()); } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/tasks/OnlineProfile.php b/src/shoghicp/BigBrother/tasks/OnlineProfile.php index cecee5ef..4328853b 100755 --- a/src/shoghicp/BigBrother/tasks/OnlineProfile.php +++ b/src/shoghicp/BigBrother/tasks/OnlineProfile.php @@ -77,6 +77,7 @@ public function onRun(){ public function onCompletion(Server $server){ //foreach($server->getOnlinePlayers() as $clientID => $player){ //if($player instanceof DesktopPlayer and $clientID === $this->clientID){ + echo "Cool\n"; $result = $this->getResult(); if(is_array($result) and isset($result["id"])){ From d172c842d3d62b3bae9886093b6bd20e51c8fea1 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 7 Jul 2016 14:58:33 -0400 Subject: [PATCH 14/21] missing code --- src/shoghicp/BigBrother/BigBrother.php | 2 ++ src/shoghicp/BigBrother/DesktopPlayer.php | 2 +- src/shoghicp/BigBrother/network/ProtocolInterface.php | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/shoghicp/BigBrother/BigBrother.php b/src/shoghicp/BigBrother/BigBrother.php index 824ffe20..1002f8c5 100755 --- a/src/shoghicp/BigBrother/BigBrother.php +++ b/src/shoghicp/BigBrother/BigBrother.php @@ -66,6 +66,8 @@ public function onEnable(){ $this->saveDefaultConfig(); $this->saveResource("server-icon.png", false); + $this->saveResource("alex.yml", false); + $this->saveResource("steve.yml", false); $this->reloadConfig(); $this->onlineMode = (bool) $this->getConfig()->get("online-mode"); diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index df568618..d854ad3f 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -299,7 +299,7 @@ public function bigBrother_processAuthentication(BigBrother $plugin, EncryptionR public function bigBrother_authenticationCallback($username){ $pk = new LoginPacket(); - $pk->username = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $username); + $pk->username = $username; $pk->clientId = crc32($this->clientID); $pk->protocol = Info::CURRENT_PROTOCOL; $pk->clientUUID = UUID::fromString($this->bigBrother_uuid); diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index 35c09667..4bb78bdb 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -199,7 +199,8 @@ protected function handlePacket(DesktopPlayer $player, $payload){ if($pid === 0x00){ //$pk = new LoginStartPacket(); //$pk->read($payload, $offset); - $player->bigBrother_handleAuthentication($this->plugin, $payload, $this->plugin->isOnlineMode()); + + $player->bigBrother_handleAuthentication($this->plugin, preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $payload), $this->plugin->isOnlineMode()); }elseif($pid === 0x01 and $this->plugin->isOnlineMode()){ $pk = new EncryptionResponsePacket(); $pk->read($payload, $offset); From e5f4296f1d7e7353629c78a6249af52f58bb43cd Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 7 Jul 2016 17:26:19 -0400 Subject: [PATCH 15/21] Changelog Update 5 --- README.md | 4 + .../translation/TranslatorProtocol.php | 191 +++++++++++++----- 2 files changed, 142 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 28e55492..00ba82e0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # BigBrother Allows the connection of Minecraft: PC clients to PocketMine-MP servers. Made for PocketMine-MP +###Changelog: Update 5: +* Works Again MCPE 0.15.x and Minecraft PC 1.8.x +* First Step on Protocols + ###Changelog: Update 4: * Bring a few changes of iPocketTeam diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index a4b9cbe2..be685e42 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -57,6 +57,7 @@ class TranslatorProtocol implements Translator{ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ + echo "[Receive] 0x".$packet->pid()."\n"; switch($packet->pid()){ // TODO: move to Info @@ -149,9 +150,144 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ } public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ + echo "[Send] 0x".$packet->pid()."\n"; switch($packet->pid()){ + case Info::START_GAME_PACKET: + echo "START_GAME_PACKET\n"; + $packets = []; + + $pk = new JoinGamePacket(); + $pk->eid = $packet->eid; + $pk->gamemode = $player->getGamemode(); + $pk->dimension = 0; + $pk->difficulty = $player->getServer()->getDifficulty(); + $pk->maxPlayers = $player->getServer()->getMaxPlayers(); + $pk->levelType = "default"; + $packets[] = $pk; + + $pk = new PlayerAbilitiesPacket(); + $pk->flyingSpeed = 0.05; + $pk->walkingSpeed = 0.1; + $pk->canFly = ($player->getGamemode() & 0x01) > 0; + $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; + $pk->isFlying = false; + $pk->isCreative = ($player->getGamemode() & 0x01) > 0; + if($player->spawned === true){ + $packets = [$pk]; - case Info::UPDATE_BLOCK_PACKET: + $pk = new ChangeGameStatePacket(); + $pk->reason = 3; + $pk->value = $player->getGamemode(); + $packets[] = $pk; + return $packets; + }else{ + $packets[] = $pk; + } + + $pk = new SpawnPositionPacket(); + $pk->spawnX = $packet->spawnX; + $pk->spawnY = $packet->spawnY; + $pk->spawnZ = $packet->spawnZ; + $packets[] = $pk; + + $pk = new PositionAndLookPacket(); + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->yaw = $player->yaw; + $pk->pitch = $player->pitch; + $pk->onGround = $player->isOnGround(); + $packets[] = $pk; + return $packets; + + case Info::SET_TIME_PACKET: + $pk = new TimeUpdatePacket(); + $pk->age = $packet->time; + $pk->time = $packet->time; //TODO: calculate offset from MCPE + return $pk; + + case Info::SET_SPAWN_POSITION_PACKET: + $pk = new SpawnPositionPacket(); + $pk->spawnX = $packet->x; + $pk->spawnY = $packet->y; + $pk->spawnZ = $packet->z; + return $pk; + + case Info::SET_HEALTH_PACKET: + $pk = new UpdateHealthPacket(); + $pk->health = $packet->health; + $pk->food = 20; + $pk->saturation = 5; + return $pk; + + case Info::LOGIN_PACKET: echo "LOGIN_PACKET\n";break; + case Info::PLAY_STATUS_PACKET: echo "PLAY_STATUS_PACKET\n";break; + case Info::SERVER_TO_CLIENT_HANDSHAKE_PACKET: echo "SERVER_TO_CLIENT_HANDSHAKE_PACKET\n";break; + case Info::CLIENT_TO_SERVER_HANDSHAKE_PACKET: echo "CLIENT_TO_SERVER_HANDSHAKE_PACKET\n";break; + case Info::DISCONNECT_PACKET: echo "DISCONNECT_PACKET\n";break; + case Info::BATCH_PACKET: echo "BATCH_PACKET\n";break; + case Info::TEXT_PACKET: echo "TEXT_PACKET\n";break; + case Info::SET_TIME_PACKET: echo "SET_TIME_PACKET\n";break; + case Info::ADD_PLAYER_PACKET: echo "ADD_PLAYER_PACKET\n";break; + case Info::ADD_ENTITY_PACKET: echo "ADD_ENTITY_PACKET\n";break; + case Info::REMOVE_ENTITY_PACKET: echo "REMOVE_ENTITY_PACKET\n";break; + case Info::ADD_ITEM_ENTITY_PACKET: echo "ADD_ITEM_ENTITY_PACKET\n";break; + case Info::TAKE_ITEM_ENTITY_PACKET: echo "TAKE_ITEM_ENTITY_PACKET\n";break; + case Info::MOVE_ENTITY_PACKET: echo "MOVE_ENTITY_PACKET\n";break; + case Info::MOVE_PLAYER_PACKET: echo "MOVE_PLAYER_PACKET\n";break; + case Info::RIDER_JUMP_PACKET: echo "RIDER_JUMP_PACKET\n";break; + case Info::REMOVE_BLOCK_PACKET: echo "REMOVE_BLOCK_PACKET\n";break; + case Info::UPDATE_BLOCK_PACKET: echo "UPDATE_BLOCK_PACKET\n";break; + case Info::ADD_PAINTING_PACKET: echo "ADD_PAINTING_PACKET\n";break; + case Info::EXPLODE_PACKET: echo "EXPLODE_PACKET\n";break; + case Info::LEVEL_EVENT_PACKET: echo "LEVEL_EVENT_PACKET\n";break; + case Info::BLOCK_EVENT_PACKET: echo "BLOCK_EVENT_PACKET\n";break; + case Info::ENTITY_EVENT_PACKET: echo "ENTITY_EVENT_PACKET\n";break; + case Info::MOB_EFFECT_PACKET: echo "MOB_EFFECT_PACKET\n";break; + case Info::UPDATE_ATTRIBUTES_PACKET: echo "UPDATE_ATTRIBUTES_PACKET\n";break; + case Info::MOB_EQUIPMENT_PACKET: echo "MOB_EQUIPMENT_PACKET\n";break; + case Info::MOB_ARMOR_EQUIPMENT_PACKET: echo "MOB_ARMOR_EQUIPMENT_PACKET\n";break; + case Info::INTERACT_PACKET: echo "INTERACT_PACKET\n";break; + case Info::USE_ITEM_PACKET: echo "USE_ITEM_PACKET\n";break; + case Info::PLAYER_ACTION_PACKET: echo "PLAYER_ACTION_PACKET\n";break; + case Info::HURT_ARMOR_PACKET: echo "HURT_ARMOR_PACKET\n";break; + case Info::SET_ENTITY_DATA_PACKET: echo "SET_ENTITY_DATA_PACKET\n";break; + case Info::SET_ENTITY_MOTION_PACKET: echo "SET_ENTITY_MOTION_PACKET\n";break; + case Info::SET_ENTITY_LINK_PACKET: echo "SET_ENTITY_LINK_PACKET\n";break; + case Info::SET_HEALTH_PACKET: echo "SET_HEALTH_PACKET\n";break; + case Info::SET_SPAWN_POSITION_PACKET: echo "SET_SPAWN_POSITION_PACKET\n";break; + case Info::ANIMATE_PACKET: echo "ANIMATE_PACKET\n";break; + case Info::RESPAWN_PACKET: echo "RESPAWN_PACKET\n";break; + case Info::DROP_ITEM_PACKET: echo "DROP_ITEM_PACKET\n";break; + case Info::CONTAINER_OPEN_PACKET: echo "CONTAINER_OPEN_PACKET\n";break; + case Info::CONTAINER_CLOSE_PACKET: echo "CONTAINER_CLOSE_PACKET\n";break; + case Info::CONTAINER_SET_SLOT_PACKET: echo "CONTAINER_SET_SLOT_PACKET\n";break; + case Info::CONTAINER_SET_DATA_PACKET: echo "CONTAINER_SET_DATA_PACKET\n";break; + case Info::CONTAINER_SET_CONTENT_PACKET: echo "CONTAINER_SET_CONTENT_PACKET\n";break; + case Info::CRAFTING_DATA_PACKET: echo "CRAFTING_DATA_PACKET\n";break; + case Info::CRAFTING_EVENT_PACKET: echo "CRAFTING_EVENT_PACKET\n";break; + case Info::ADVENTURE_SETTINGS_PACKET: echo "ADVENTURE_SETTINGS_PACKET\n";break; + case Info::BLOCK_ENTITY_DATA_PACKET: echo "BLOCK_ENTITY_DATA_PACKET\n";break; + case Info::PLAYER_INPUT_PACKET: echo "PLAYER_INPUT_PACKET\n";break; + case Info::FULL_CHUNK_DATA_PACKET: echo "FULL_CHUNK_DATA_PACKET\n";break; + case Info::SET_DIFFICULTY_PACKET: echo "SET_DIFFICULTY_PACKET\n";break; + case Info::CHANGE_DIMENSION_PACKET: echo "CHANGE_DIMENSION_PACKET\n";break; + case Info::SET_PLAYER_GAMETYPE_PACKET: echo "SET_PLAYER_GAMETYPE_PACKET\n";break; + case Info::PLAYER_LIST_PACKET: echo "PLAYER_LIST_PACKET\n";break; + case Info::TELEMETRY_EVENT_PACKET: echo "TELEMETRY_EVENT_PACKET\n";break; + case Info::SPAWN_EXPERIENCE_ORB_PACKET: echo "SPAWN_EXPERIENCE_ORB_PACKET\n";break; + case Info::CLIENTBOUND_MAP_ITEM_DATA_PACKET: echo "CLIENTBOUND_MAP_ITEM_DATA_PACKET\n";break; + case Info::MAP_INFO_REQUEST_PACKET: echo "MAP_INFO_REQUEST_PACKET\n";break; + case Info::REQUEST_CHUNK_RADIUS_PACKET: echo "REQUEST_CHUNK_RADIUS_PACKET\n";break; + case Info::CHUNK_RADIUS_UPDATED_PACKET: echo "CHUNK_RADIUS_UPDATED_PACKET\n";break; + case Info::ITEM_FRAME_DROP_ITEM_PACKET: echo "ITEM_FRAME_DROP_ITEM_PACKET\n";break; + case Info::REPLACE_SELECTED_ITEM_PACKET: echo "REPLACE_SELECTED_ITEM_PACKET\n";break; + case Info::ADD_ITEM_PACKET: echo "ADD_ITEM_PACKET\n";break; + //return null; + + + + /*case Info::UPDATE_BLOCK_PACKET: $pk = new BlockChangePacket(); $pk->x = $packet->x; $pk->y = $packet->y; @@ -298,57 +434,6 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ } return $packets; - /* - case Info::CONTAINER_CLOSE_PACKET: - $pk = new STCCloseWindowPacket(); - $pk->windowID = $packet->windowid; - return $pk; - - case Info::CONTAINER_OPEN_PACKET: - $pk = new OpenWindowPacket(); - $pk->windowID = $packet->windowid; - $pk->inventoryType = $packet->type; - $pk->windowTitle = ""; - $pk->slots = $packet->slots; - return $pk; - - case Info::CONTAINER_SET_SLOT_PACKET: - $pk = new SetSlotPacket(); - $pk->windowID = $packet->windowid; - if($pk->windowID === 0){ - $pk->slot = $packet->slot + 9; - }elseif($pk->windowID === 0x78){ - $pk->windowID = 0; - $pk->slot = $packet->slot + 5; - }else{ - $pk->slot = $packet->slot; - } - $pk->item = $packet->item; - return $pk; - - case Info::CONTAINER_SET_CONTENT_PACKET: - $pk = new WindowItemsPacket(); - $pk->windowID = $packet->windowid; - if($pk->windowID === 0 or $pk->windowID === 0x78){ - $pk->windowID = 0; - for($i = 0; $i < 5; ++$i){ - $pk->items[] = Item::get(Item::AIR, 0, 0); - } - $pk->items[] = $player->getInventory()->getHelmet(); - $pk->items[] = $player->getInventory()->getChestplate(); - $pk->items[] = $player->getInventory()->getLeggings(); - $pk->items[] = $player->getInventory()->getBoots(); - $slots = $player->getInventory()->getSize(); - for($i = 0; $i < $slots; ++$i){ - $pk->items[] = $player->getInventory()->getItem($i); - } - }else{ - $pk->items = $packet->slots; - } - - return $pk; - */ - case Info::ADD_ITEM_ENTITY_PACKET: $packets = []; $pk = new SpawnObjectPacket(); @@ -395,7 +480,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; $packets[] = $pk; - return $packets; + return $packets;*/ default: return null; From 6ac06f58aa75951fc7c1c4bf7205c3992c2f9f00 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 7 Jul 2016 18:00:00 -0400 Subject: [PATCH 16/21] more fixes --- .../translation/TranslatorProtocol.php | 281 ++++++------------ 1 file changed, 99 insertions(+), 182 deletions(-) diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index be685e42..87a4a7e6 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -57,7 +57,7 @@ class TranslatorProtocol implements Translator{ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ - echo "[Receive] 0x".$packet->pid()."\n"; + //echo "[Receive] 0x".$packet->pid()."\n"; switch($packet->pid()){ // TODO: move to Info @@ -74,37 +74,36 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ return $pk; case 0x04: //PlayerPositionPacket - $pk = new MovePlayerPacket(); + /*$pk = new MovePlayerPacket(); $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; $pk->yaw = $player->yaw; $pk->bodyYaw = $player->yaw; $pk->pitch = $player->pitch; - return $pk; + return $pk;*/ case 0x05: //PlayerLookPacket - $pk = new MovePlayerPacket(); + /*$pk = new MovePlayerPacket(); $pk->x = $player->x; $pk->y = $player->y; $pk->z = $player->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; $pk->pitch = $packet->pitch; - return $pk; + return $pk;*/ case 0x06: //PlayerPositionAndLookPacket - $pk = new MovePlayerPacket(); + /*$pk = new MovePlayerPacket(); $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; $pk->pitch = $packet->pitch; - return $pk; - + return $pk;*/ case 0x07: //PlayerDiggingPacket - if($packet->status === 2 or ($player->getGamemode() === 1 and $packet->status === 0)){ //Finished digging + /*if($packet->status === 2 or ($player->getGamemode() === 1 and $packet->status === 0)){ //Finished digging $pk = new RemoveBlockPacket(); $pk->eid = 0; $pk->x = $packet->x; @@ -112,10 +111,10 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $pk->z = $packet->z; return $pk; } - return null; + return null;*/ case 0x08; //PlayerBlockPlacementPacket - $pk = new UseItemPacket(); + /*$pk = new UseItemPacket(); $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; @@ -126,15 +125,15 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $pk->fx = $packet->cursorX / 16; $pk->fy = $packet->cursorY / 16; $pk->fz = $packet->cursorZ / 16; - return $pk; + return $pk;*/ case 0x0d: //CTSCloseWindowPacket - $pk = new ContainerClosePacket(); + /*$pk = new ContainerClosePacket(); $pk->windowid = $packet->windowID; - return $pk; + return $pk;*/ case 0x16: //ClientStatusPacket - if($packet->actionID === 0){ + /*if($packet->actionID === 0){ $pk = new RespawnPacket(); $pk->eid = 0; $pk->x = $player->getSpawn()->getX(); @@ -142,7 +141,7 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $pk->z = $player->getSpawn()->getX(); return $pk; } - return null; + return null;*/ default: return null; @@ -184,6 +183,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $packets[] = $pk; } + echo "SpawnX: ".$packet->spawnX." SpawnY: ".$packet->spawnY."SpawnZ: ".$packet->spawnZ."\n"; $pk = new SpawnPositionPacket(); $pk->spawnX = $packet->spawnX; $pk->spawnY = $packet->spawnY; @@ -212,136 +212,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->spawnY = $packet->y; $pk->spawnZ = $packet->z; return $pk; - - case Info::SET_HEALTH_PACKET: - $pk = new UpdateHealthPacket(); - $pk->health = $packet->health; - $pk->food = 20; - $pk->saturation = 5; - return $pk; - - case Info::LOGIN_PACKET: echo "LOGIN_PACKET\n";break; - case Info::PLAY_STATUS_PACKET: echo "PLAY_STATUS_PACKET\n";break; - case Info::SERVER_TO_CLIENT_HANDSHAKE_PACKET: echo "SERVER_TO_CLIENT_HANDSHAKE_PACKET\n";break; - case Info::CLIENT_TO_SERVER_HANDSHAKE_PACKET: echo "CLIENT_TO_SERVER_HANDSHAKE_PACKET\n";break; - case Info::DISCONNECT_PACKET: echo "DISCONNECT_PACKET\n";break; - case Info::BATCH_PACKET: echo "BATCH_PACKET\n";break; - case Info::TEXT_PACKET: echo "TEXT_PACKET\n";break; - case Info::SET_TIME_PACKET: echo "SET_TIME_PACKET\n";break; - case Info::ADD_PLAYER_PACKET: echo "ADD_PLAYER_PACKET\n";break; - case Info::ADD_ENTITY_PACKET: echo "ADD_ENTITY_PACKET\n";break; - case Info::REMOVE_ENTITY_PACKET: echo "REMOVE_ENTITY_PACKET\n";break; - case Info::ADD_ITEM_ENTITY_PACKET: echo "ADD_ITEM_ENTITY_PACKET\n";break; - case Info::TAKE_ITEM_ENTITY_PACKET: echo "TAKE_ITEM_ENTITY_PACKET\n";break; - case Info::MOVE_ENTITY_PACKET: echo "MOVE_ENTITY_PACKET\n";break; - case Info::MOVE_PLAYER_PACKET: echo "MOVE_PLAYER_PACKET\n";break; - case Info::RIDER_JUMP_PACKET: echo "RIDER_JUMP_PACKET\n";break; - case Info::REMOVE_BLOCK_PACKET: echo "REMOVE_BLOCK_PACKET\n";break; - case Info::UPDATE_BLOCK_PACKET: echo "UPDATE_BLOCK_PACKET\n";break; - case Info::ADD_PAINTING_PACKET: echo "ADD_PAINTING_PACKET\n";break; - case Info::EXPLODE_PACKET: echo "EXPLODE_PACKET\n";break; - case Info::LEVEL_EVENT_PACKET: echo "LEVEL_EVENT_PACKET\n";break; - case Info::BLOCK_EVENT_PACKET: echo "BLOCK_EVENT_PACKET\n";break; - case Info::ENTITY_EVENT_PACKET: echo "ENTITY_EVENT_PACKET\n";break; - case Info::MOB_EFFECT_PACKET: echo "MOB_EFFECT_PACKET\n";break; - case Info::UPDATE_ATTRIBUTES_PACKET: echo "UPDATE_ATTRIBUTES_PACKET\n";break; - case Info::MOB_EQUIPMENT_PACKET: echo "MOB_EQUIPMENT_PACKET\n";break; - case Info::MOB_ARMOR_EQUIPMENT_PACKET: echo "MOB_ARMOR_EQUIPMENT_PACKET\n";break; - case Info::INTERACT_PACKET: echo "INTERACT_PACKET\n";break; - case Info::USE_ITEM_PACKET: echo "USE_ITEM_PACKET\n";break; - case Info::PLAYER_ACTION_PACKET: echo "PLAYER_ACTION_PACKET\n";break; - case Info::HURT_ARMOR_PACKET: echo "HURT_ARMOR_PACKET\n";break; - case Info::SET_ENTITY_DATA_PACKET: echo "SET_ENTITY_DATA_PACKET\n";break; - case Info::SET_ENTITY_MOTION_PACKET: echo "SET_ENTITY_MOTION_PACKET\n";break; - case Info::SET_ENTITY_LINK_PACKET: echo "SET_ENTITY_LINK_PACKET\n";break; - case Info::SET_HEALTH_PACKET: echo "SET_HEALTH_PACKET\n";break; - case Info::SET_SPAWN_POSITION_PACKET: echo "SET_SPAWN_POSITION_PACKET\n";break; - case Info::ANIMATE_PACKET: echo "ANIMATE_PACKET\n";break; - case Info::RESPAWN_PACKET: echo "RESPAWN_PACKET\n";break; - case Info::DROP_ITEM_PACKET: echo "DROP_ITEM_PACKET\n";break; - case Info::CONTAINER_OPEN_PACKET: echo "CONTAINER_OPEN_PACKET\n";break; - case Info::CONTAINER_CLOSE_PACKET: echo "CONTAINER_CLOSE_PACKET\n";break; - case Info::CONTAINER_SET_SLOT_PACKET: echo "CONTAINER_SET_SLOT_PACKET\n";break; - case Info::CONTAINER_SET_DATA_PACKET: echo "CONTAINER_SET_DATA_PACKET\n";break; - case Info::CONTAINER_SET_CONTENT_PACKET: echo "CONTAINER_SET_CONTENT_PACKET\n";break; - case Info::CRAFTING_DATA_PACKET: echo "CRAFTING_DATA_PACKET\n";break; - case Info::CRAFTING_EVENT_PACKET: echo "CRAFTING_EVENT_PACKET\n";break; - case Info::ADVENTURE_SETTINGS_PACKET: echo "ADVENTURE_SETTINGS_PACKET\n";break; - case Info::BLOCK_ENTITY_DATA_PACKET: echo "BLOCK_ENTITY_DATA_PACKET\n";break; - case Info::PLAYER_INPUT_PACKET: echo "PLAYER_INPUT_PACKET\n";break; - case Info::FULL_CHUNK_DATA_PACKET: echo "FULL_CHUNK_DATA_PACKET\n";break; - case Info::SET_DIFFICULTY_PACKET: echo "SET_DIFFICULTY_PACKET\n";break; - case Info::CHANGE_DIMENSION_PACKET: echo "CHANGE_DIMENSION_PACKET\n";break; - case Info::SET_PLAYER_GAMETYPE_PACKET: echo "SET_PLAYER_GAMETYPE_PACKET\n";break; - case Info::PLAYER_LIST_PACKET: echo "PLAYER_LIST_PACKET\n";break; - case Info::TELEMETRY_EVENT_PACKET: echo "TELEMETRY_EVENT_PACKET\n";break; - case Info::SPAWN_EXPERIENCE_ORB_PACKET: echo "SPAWN_EXPERIENCE_ORB_PACKET\n";break; - case Info::CLIENTBOUND_MAP_ITEM_DATA_PACKET: echo "CLIENTBOUND_MAP_ITEM_DATA_PACKET\n";break; - case Info::MAP_INFO_REQUEST_PACKET: echo "MAP_INFO_REQUEST_PACKET\n";break; - case Info::REQUEST_CHUNK_RADIUS_PACKET: echo "REQUEST_CHUNK_RADIUS_PACKET\n";break; - case Info::CHUNK_RADIUS_UPDATED_PACKET: echo "CHUNK_RADIUS_UPDATED_PACKET\n";break; - case Info::ITEM_FRAME_DROP_ITEM_PACKET: echo "ITEM_FRAME_DROP_ITEM_PACKET\n";break; - case Info::REPLACE_SELECTED_ITEM_PACKET: echo "REPLACE_SELECTED_ITEM_PACKET\n";break; - case Info::ADD_ITEM_PACKET: echo "ADD_ITEM_PACKET\n";break; - //return null; - - - - /*case Info::UPDATE_BLOCK_PACKET: - $pk = new BlockChangePacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->blockId = $packet->block; - $pk->blockMeta = $packet->meta; - return $pk; - - case Info::START_GAME_PACKET: - $packets = []; - - $pk = new JoinGamePacket(); - $pk->eid = $packet->eid; - $pk->gamemode = $player->getGamemode(); - $pk->dimension = 0; - $pk->difficulty = $player->getServer()->getDifficulty(); - $pk->maxPlayers = $player->getServer()->getMaxPlayers(); - $pk->levelType = "default"; - $packets[] = $pk; - - $pk = new PlayerAbilitiesPacket(); - $pk->flyingSpeed = 0.05; - $pk->walkingSpeed = 0.1; - $pk->canFly = ($player->getGamemode() & 0x01) > 0; - $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; - $pk->isFlying = false; - $pk->isCreative = ($player->getGamemode() & 0x01) > 0; - if($player->spawned === true){ - $packets = [$pk]; - - $pk = new ChangeGameStatePacket(); - $pk->reason = 3; - $pk->value = $player->getGamemode(); - $packets[] = $pk; - return $packets; - }else{ - $packets[] = $pk; - } - - $pk = new SpawnPositionPacket(); - $pk->spawnX = $packet->spawnX; - $pk->spawnY = $packet->spawnY; - $pk->spawnZ = $packet->spawnZ; - $packets[] = $pk; - - $pk = new PositionAndLookPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $player->yaw; - $pk->pitch = $player->pitch; - $pk->onGround = $player->isOnGround(); - $packets[] = $pk; - return $packets; + break; case Info::SET_HEALTH_PACKET: $pk = new UpdateHealthPacket(); @@ -356,25 +227,6 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->message = TextFormat::toJSON($packet->message); return $pk; - case Info::SET_TIME_PACKET: - $pk = new TimeUpdatePacket(); - $pk->age = $packet->time; - $pk->time = $packet->time; //TODO: calculate offset from MCPE - return $pk; - - case Info::SET_SPAWN_POSITION_PACKET: - $pk = new SpawnPositionPacket(); - $pk->spawnX = $packet->x; - $pk->spawnY = $packet->y; - $pk->spawnZ = $packet->z; - return $pk; - - case Info::REMOVE_ENTITY_PACKET: - //case Info::REMOVE_PLAYER_PACKET: - $pk = new DestroyEntitiesPacket(); - $pk->ids[] = $packet->eid; - return $pk; - case Info::MOVE_PLAYER_PACKET: if($packet->eid === 0){ $pk = new PositionAndLookPacket(); @@ -403,24 +255,89 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return $packets; } - case Info::MOVE_ENTITY_PACKET: - $packets = []; - foreach($packet->entities as $d){ - $pk = new EntityTeleportPacket(); - $pk->eid = $d[0]; - $pk->x = $d[1]; - $pk->y = $d[2]; - $pk->z = $d[3]; - $pk->yaw = $d[4]; - $pk->pitch = $d[5]; - $packets[] = $pk; + case Info::LOGIN_PACKET:break; + case Info::PLAY_STATUS_PACKET:break; + case Info::SERVER_TO_CLIENT_HANDSHAKE_PACKET:break; + case Info::CLIENT_TO_SERVER_HANDSHAKE_PACKET:break; + case Info::DISCONNECT_PACKET:break; + case Info::BATCH_PACKET:break; + case Info::TEXT_PACKET:break; + case Info::SET_TIME_PACKET:break; + case Info::ADD_PLAYER_PACKET:break; + case Info::ADD_ENTITY_PACKET:break; + case Info::REMOVE_ENTITY_PACKET:break; + case Info::ADD_ITEM_ENTITY_PACKET:break; + case Info::TAKE_ITEM_ENTITY_PACKET:break; + case Info::MOVE_ENTITY_PACKET:break; + case Info::MOVE_PLAYER_PACKET:break; + case Info::RIDER_JUMP_PACKET:break; + case Info::REMOVE_BLOCK_PACKET:break; + case Info::UPDATE_BLOCK_PACKET:break; + case Info::ADD_PAINTING_PACKET:break; + case Info::EXPLODE_PACKET:break; + case Info::LEVEL_EVENT_PACKET:break; + case Info::BLOCK_EVENT_PACKET:break; + case Info::ENTITY_EVENT_PACKET:break; + case Info::MOB_EFFECT_PACKET:break; + case Info::UPDATE_ATTRIBUTES_PACKET:break; + case Info::MOB_EQUIPMENT_PACKET:break; + case Info::MOB_ARMOR_EQUIPMENT_PACKET:break; + case Info::INTERACT_PACKET:break; + case Info::USE_ITEM_PACKET:break; + case Info::PLAYER_ACTION_PACKET:break; + case Info::HURT_ARMOR_PACKET:break; + case Info::SET_ENTITY_DATA_PACKET:break; + case Info::SET_ENTITY_MOTION_PACKET:break; + case Info::SET_ENTITY_LINK_PACKET:break; + case Info::SET_HEALTH_PACKET:break; + case Info::SET_SPAWN_POSITION_PACKET:break; + case Info::ANIMATE_PACKET:break; + case Info::RESPAWN_PACKET:break; + case Info::DROP_ITEM_PACKET:break; + case Info::CONTAINER_OPEN_PACKET:break; + case Info::CONTAINER_CLOSE_PACKET:break; + case Info::CONTAINER_SET_SLOT_PACKET:break; + case Info::CONTAINER_SET_DATA_PACKET:break; + case Info::CONTAINER_SET_CONTENT_PACKET:break; + case Info::CRAFTING_DATA_PACKET:break; + case Info::CRAFTING_EVENT_PACKET:break; + case Info::ADVENTURE_SETTINGS_PACKET:break; + case Info::BLOCK_ENTITY_DATA_PACKET:break; + case Info::PLAYER_INPUT_PACKET:break; + case Info::FULL_CHUNK_DATA_PACKET:break; + case Info::SET_DIFFICULTY_PACKET:break; + case Info::CHANGE_DIMENSION_PACKET:break; + case Info::SET_PLAYER_GAMETYPE_PACKET:break; + case Info::PLAYER_LIST_PACKET:break; + case Info::TELEMETRY_EVENT_PACKET:break; + case Info::SPAWN_EXPERIENCE_ORB_PACKET:break; + case Info::CLIENTBOUND_MAP_ITEM_DATA_PACKET:break; + case Info::MAP_INFO_REQUEST_PACKET:break; + case Info::REQUEST_CHUNK_RADIUS_PACKET:break; + case Info::CHUNK_RADIUS_UPDATED_PACKET:break; + case Info::ITEM_FRAME_DROP_ITEM_PACKET:break; + case Info::REPLACE_SELECTED_ITEM_PACKET:break; + case Info::ADD_ITEM_PACKET:break; + //return null; - $pk = new EntityHeadLookPacket(); - $pk->eid = $d[0]; - $pk->yaw = $d[4]; - $packets[] = $pk; - } - return $packets; + + + /*case Info::UPDATE_BLOCK_PACKET: + $pk = new BlockChangePacket(); + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->blockId = $packet->block; + $pk->blockMeta = $packet->meta; + return $pk; + + case Info::REMOVE_ENTITY_PACKET: + //case Info::REMOVE_PLAYER_PACKET: + $pk = new DestroyEntitiesPacket(); + $pk->ids[] = $packet->eid; + return $pk; + + case Info::SET_ENTITY_MOTION_PACKET: $packets = []; From d34d1196f6ff070c2d347b7f7bec7b1182673000 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Thu, 7 Jul 2016 20:18:13 -0400 Subject: [PATCH 17/21] improvements --- src/shoghicp/BigBrother/DesktopPlayer.php | 61 -- .../translation/TranslatorProtocol.php | 572 +++++++++++++----- src/shoghicp/BigBrother/utils/Binary.php | 31 +- 3 files changed, 434 insertions(+), 230 deletions(-) diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index d854ad3f..bff566cb 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -79,67 +79,6 @@ public function __construct(SourceInterface $interface, $clientID, $address, $po $this->setRemoveFormat(false); } - public function bigBrother_updateTitleBar(){ - if($this->bigBrother_titleBarID === null){ - $this->bigBrother_titleBarID = 2147483647; - - $pk = new SpawnMobPacket(); - $pk->eid = $this->bigBrother_titleBarID; - $pk->type = 63; - $pk->x = $this->x; - $pk->y = 250; - $pk->z = $this->z; - $pk->pitch = 0; - $pk->yaw = 0; - $pk->headPitch = 0; - $pk->velocityX = 0; - $pk->velocityY = 0; - $pk->velocityZ = 0; - $pk->metadata = [ - 0 => ["type" => 0, "value" => 0x20], - 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], - 7 => ["type" => 2, "value" => 0], - 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], - 11 => ["type" => 0, "value" => 1] - ]; - $this->putRawPacket($pk); - $this->tasks[] = $this->getServer()->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_updateTitleBar"]), 5, 20); - }else{ - $pk = new EntityTeleportPacket(); - $pk->eid = $this->bigBrother_titleBarID; - $pk->x = $this->x; - $pk->y = 250; - $pk->z = $this->z; - $pk->yaw = 0; - $pk->pitch = 0; - $this->putRawPacket($pk); - - $pk = new EntityMetadataPacket(); - $pk->eid = $this->bigBrother_titleBarID; - $pk->metadata = [ - 0 => ["type" => 0, "value" => 0x20], - 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], - 7 => ["type" => 2, "value" => 0], - 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], - 11 => ["type" => 0, "value" => 1] - ]; - $this->putRawPacket($pk); - - } - } - - public function bigBrother_setTitleBar($text, $level = 100){ - if($level > 100){ - $level = 100; - }elseif($level < 0){ - $level = 0; - } - - $this->bigBrother_titleBarText = $text; - $this->bigBrother_titleBarLevel = $level; - $this->bigBrother_updateTitleBar(); - } - public function bigBrother_sendKeepAlive(){ $pk = new KeepAlivePacket(); $pk->id = mt_rand(); diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index 87a4a7e6..ec96deb5 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -57,13 +57,22 @@ class TranslatorProtocol implements Translator{ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ - //echo "[Receive] 0x".$packet->pid()."\n"; + if($packet->pid() === 4 || $packet->pid() === 5||$packet->pid() === 6){ + + }else{ + //echo "[Receive] 0x".$packet->pid()."\n"; + } switch($packet->pid()){ // TODO: move to Info + case 0x00: //KeepAlivePacket + $pk->id = mt_rand(); + $player->putRawPacket($pk); + return null; - case 0x01: //CTSChatPacket - $pk = new MessagePacket(); - $pk->source = ""; + case 0x01: //ChatPacket + $pk = new TextPacket(); + $pk->type = TextPacket::TYPE_CHAT;//Chat Type + //$pk->source = ""; $pk->message = $packet->message; return $pk; @@ -73,75 +82,325 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $pk->action = $packet->mouse; return $pk; - case 0x04: //PlayerPositionPacket - /*$pk = new MovePlayerPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; + case 0x03: //PlayerPacket + $player->setSetting(["onGround" => $packet->onGround]); + return null; + + case 0x04: //PlayerPositonPacket + $pk = new MovePlayerPacket(); + $pk->x = $player->x; + $pk->y = $player->y + $player->getEyeHeight(); + $pk->z = $player->z; $pk->yaw = $player->yaw; $pk->bodyYaw = $player->yaw; $pk->pitch = $player->pitch; - return $pk;*/ + return $pk; case 0x05: //PlayerLookPacket - /*$pk = new MovePlayerPacket(); + $pk = new MovePlayerPacket(); $pk->x = $player->x; - $pk->y = $player->y; + $pk->y = $player->y + $player->getEyeHeight(); $pk->z = $player->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; $pk->pitch = $packet->pitch; - return $pk;*/ + return $pk; case 0x06: //PlayerPositionAndLookPacket - /*$pk = new MovePlayerPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; + $pk = new MovePlayerPacket(); + $pk->x = $player->x; + $pk->y = $player->y + $player->getEyeHeight(); + $pk->z = $player->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; $pk->pitch = $packet->pitch; - return $pk;*/ + return $pk; case 0x07: //PlayerDiggingPacket - /*if($packet->status === 2 or ($player->getGamemode() === 1 and $packet->status === 0)){ //Finished digging - $pk = new RemoveBlockPacket(); - $pk->eid = 0; + switch($packet->status){ + case 0: + if($player->getGamemode() === 1){ + $pk = new RemoveBlockPacket(); + $pk->eid = 0; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + return $pk; + }else{ + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_START_BREAK; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->face; + return $pk; + } + break; + case 1: + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_ABORT_BREAK; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->face; + return $pk; + break; + case 2: + if($player->getGamemode() !== 1){ + $packets = []; + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_STOP_BREAK; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->face = $packet->face; + $packets[] = $pk; + + $pk = new RemoveBlockPacket(); + $pk->eid = 0; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $packets[] = $pk; + return $packets; + }else{ + echo "PlayerDiggingPacket: ".$packet->status."\n"; + } + break; + default: + echo "PlayerDiggingPacket: ".$packet->status."\n"; + break; + } + + return null; + + case 0x08; //PlayerBlockPlacementPacket + echo "PlayerBlockPlacementPacket: ".$packet->direction."\n"; + + if($packet->direction !== 255){ + $pk = new UseItemPacket(); $pk->x = $packet->x; $pk->y = $packet->y; $pk->z = $packet->z; + $pk->face = $packet->direction; + $pk->item = $packet->heldItem; + $pk->fx = $packet->cursorX / 16; + $pk->fy = $packet->cursorY / 16; + $pk->fz = $packet->cursorZ / 16; + $pk->posX = $player->getX(); + $pk->posY = $player->getY(); + $pk->posZ = $player->getZ(); return $pk; + }else{ + } - return null;*/ - case 0x08; //PlayerBlockPlacementPacket - /*$pk = new UseItemPacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->face = $packet->direction; - $pk->item = $packet->heldItem->getID(); - $pk->meta = $packet->heldItem->getDamage(); + return null; + + case 0x09: //HeldItemChangePacket + $item = $player->getInventory()->getItem($packet->selectedSlot); + $olditem = $player->getInventory()->getItem($player->getInventory()->getHeldItemIndex()); + + if($item->getId() !== 0 or $item->getId() === 0 and $olditem->getId() !== 0){ + $pk = new MobEquipmentPacket(); + $pk->eid = 0; + $pk->item = $item; + if($item->getId() === 0){ + $pk->slot = 255; + }else{ + $pk->slot = Item::getCreativeItemIndex($item) + 9; + } + $pk->selectedSlot = $packet->selectedSlot; + return $pk; + } + + return null; + + case 0x0a: //PlayerArmSwingPacket + $pk = new AnimatePacket(); + $pk->action = 1; $pk->eid = 0; - $pk->fx = $packet->cursorX / 16; - $pk->fy = $packet->cursorY / 16; - $pk->fz = $packet->cursorZ / 16; - return $pk;*/ + return $pk; + + case 0x0b: //AnimatePacket + switch($packet->actionID){ + case 0: + if(!$player->getSetting("isFlying")){ + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_START_SNEAK; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + } + break; + case 1: + if(!$player->getSetting("isFlying")){ + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_STOP_SNEAK; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + } + break; + case 2: + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_STOP_SLEEPING; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + break; + case 3: + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_START_SPRINT; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + break; + case 4: + $pk = new PlayerActionPacket(); + $pk->eid = $packet->eid; + $pk->action = PlayerActionPacket::ACTION_STOP_SPRINT; + $pk->x = $player->getX(); + $pk->y = $player->getY(); + $pk->z = $player->getZ(); + $pk->face = 0; + return $pk; + break; + /*case 6: + + break;*/ + default: + echo "[AnimatePacket] ".$packet->actionID."\n";//Debug Code + break; + } + return null; case 0x0d: //CTSCloseWindowPacket - /*$pk = new ContainerClosePacket(); - $pk->windowid = $packet->windowID; + if($packet->windowID !== 0x00){ + $pk = new ContainerClosePacket(); + $pk->windowid = $packet->windowID; + return $pk; + } + + case 0x10: //CreativeInventoryActionPacket + echo "Slot: ".$packet->slot."\n"; + echo "ItemId: ".$packet->item->getId()." : ".$packet->item->getDamage()."\n"; + + /*if($packet->slot === 65535){ + $pk = new DropItemPacket(); + $pk->type = 0; + $pk->item = $packet->item; + return $pk; + }else{ + $pk = new ContainerSetSlotPacket(); + $pk->windowid = 0; + $pk->slot = $packet->slot; + $pk->item = $packet->item; + return $pk; + }*/ + + return null; + + case 0x13: //CPlayerAbilitiesPacket + $player->setSetting(["isFlying" => $packet->isFlying]); + return null; + + case 0x14: //CTabCompletePacket + /*$pk = new STabComletePacket(); + + foreach($player->getServer()->getCommandMap()->getCommands() as $command){ + if($command->testPermissionSilent($player)){ + $pk->matches[] = $command->getName(); + } + } + + foreach($player->getServer()->getOnlinePlayers() as $packetplayer){ + $pk->matches[] = $packetplayer->getName(); + } + + //TODO + + //echo $packet->text."\n"; + return $pk;*/ + return null; + + case 0x15: //ClientSettingsPacket + $player->setSetting([ + "Lang" => $packet->lang, + "View" => $packet->view, + "ChatMode" => $packet->chatmode, + "ChatColor" => $packet->chatcolor, + "SkinSettings" => $packet->skinsetting, + ]); + + return null; case 0x16: //ClientStatusPacket - /*if($packet->actionID === 0){ - $pk = new RespawnPacket(); - $pk->eid = 0; - $pk->x = $player->getSpawn()->getX(); - $pk->y = $player->getSpawn()->getX(); - $pk->z = $player->getSpawn()->getX(); - return $pk; + switch($packet->actionID){ + case 0: + $pk = new PlayerActionPacket(); + $pk->eid = 0; + $pk->action = PlayerActionPacket::ACTION_RESPAWN; + $pk->x = 0; + $pk->y = 0; + $pk->z = 0; + $pk->face = 0; + return $pk; + break; + case 1: + $statistic = []; + $statistic[] = ["achievement.openInventory", 1];// + foreach($player->achievements as $achievement => $count){ + $statistic[] = ["achievement.".$achievement, $count]; + } + + //stat + //https://gist.github.com/thinkofdeath/a1842c21a0cf2e1fb5e0 + + $pk = new StatisticsPacket(); + $pk->count = count($statistic);//TODO stat + $pk->statistic = $statistic; + $player->putRawPacket($pk); + break; + case 2: + //$player->awardAchievement("openInventory"); this for DesktopPlayer + //Achievement::broadcast($player, "openInventory");//Debug + break; } - return null;*/ + return null; + + case 0x17: //PluginMessagePacket + switch($packet->channel){ + case "REGISTER"://Mods Register + $player->setSetting(["Channels" => $packet->data]); + break; + case "MC|Brand": //ServerType + $player->setSetting(["ServerType" => $packet->data]); + break; + default: + echo "PluginChannel: ".$packet->channel."\n"; + break; + } + return null; + + case 0x19: //ResourcePackStatusPacket + $player->setSetting(["ResourceStatus" => $packet->status, "ResourceHash" => $packet->hash]); + return null; default: return null; @@ -149,10 +408,10 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ } public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ - echo "[Send] 0x".$packet->pid()."\n"; + switch($packet->pid()){ case Info::START_GAME_PACKET: - echo "START_GAME_PACKET\n"; + //echo "START_GAME_PACKET\n"; $packets = []; $pk = new JoinGamePacket(); @@ -183,7 +442,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $packets[] = $pk; } - echo "SpawnX: ".$packet->spawnX." SpawnY: ".$packet->spawnY."SpawnZ: ".$packet->spawnZ."\n"; + echo "SpawnX: ".$packet->spawnX." SpawnY: ".$packet->spawnY." SpawnZ: ".$packet->spawnZ."\n"; $pk = new SpawnPositionPacket(); $pk->spawnX = $packet->spawnX; $pk->spawnY = $packet->spawnY; @@ -255,69 +514,113 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return $packets; } - case Info::LOGIN_PACKET:break; - case Info::PLAY_STATUS_PACKET:break; - case Info::SERVER_TO_CLIENT_HANDSHAKE_PACKET:break; - case Info::CLIENT_TO_SERVER_HANDSHAKE_PACKET:break; - case Info::DISCONNECT_PACKET:break; - case Info::BATCH_PACKET:break; - case Info::TEXT_PACKET:break; - case Info::SET_TIME_PACKET:break; - case Info::ADD_PLAYER_PACKET:break; - case Info::ADD_ENTITY_PACKET:break; - case Info::REMOVE_ENTITY_PACKET:break; - case Info::ADD_ITEM_ENTITY_PACKET:break; - case Info::TAKE_ITEM_ENTITY_PACKET:break; - case Info::MOVE_ENTITY_PACKET:break; - case Info::MOVE_PLAYER_PACKET:break; - case Info::RIDER_JUMP_PACKET:break; - case Info::REMOVE_BLOCK_PACKET:break; - case Info::UPDATE_BLOCK_PACKET:break; - case Info::ADD_PAINTING_PACKET:break; - case Info::EXPLODE_PACKET:break; - case Info::LEVEL_EVENT_PACKET:break; - case Info::BLOCK_EVENT_PACKET:break; - case Info::ENTITY_EVENT_PACKET:break; - case Info::MOB_EFFECT_PACKET:break; - case Info::UPDATE_ATTRIBUTES_PACKET:break; - case Info::MOB_EQUIPMENT_PACKET:break; - case Info::MOB_ARMOR_EQUIPMENT_PACKET:break; - case Info::INTERACT_PACKET:break; - case Info::USE_ITEM_PACKET:break; - case Info::PLAYER_ACTION_PACKET:break; - case Info::HURT_ARMOR_PACKET:break; - case Info::SET_ENTITY_DATA_PACKET:break; - case Info::SET_ENTITY_MOTION_PACKET:break; - case Info::SET_ENTITY_LINK_PACKET:break; - case Info::SET_HEALTH_PACKET:break; - case Info::SET_SPAWN_POSITION_PACKET:break; - case Info::ANIMATE_PACKET:break; - case Info::RESPAWN_PACKET:break; - case Info::DROP_ITEM_PACKET:break; - case Info::CONTAINER_OPEN_PACKET:break; - case Info::CONTAINER_CLOSE_PACKET:break; - case Info::CONTAINER_SET_SLOT_PACKET:break; - case Info::CONTAINER_SET_DATA_PACKET:break; - case Info::CONTAINER_SET_CONTENT_PACKET:break; - case Info::CRAFTING_DATA_PACKET:break; - case Info::CRAFTING_EVENT_PACKET:break; - case Info::ADVENTURE_SETTINGS_PACKET:break; - case Info::BLOCK_ENTITY_DATA_PACKET:break; - case Info::PLAYER_INPUT_PACKET:break; - case Info::FULL_CHUNK_DATA_PACKET:break; - case Info::SET_DIFFICULTY_PACKET:break; - case Info::CHANGE_DIMENSION_PACKET:break; - case Info::SET_PLAYER_GAMETYPE_PACKET:break; - case Info::PLAYER_LIST_PACKET:break; - case Info::TELEMETRY_EVENT_PACKET:break; - case Info::SPAWN_EXPERIENCE_ORB_PACKET:break; - case Info::CLIENTBOUND_MAP_ITEM_DATA_PACKET:break; - case Info::MAP_INFO_REQUEST_PACKET:break; - case Info::REQUEST_CHUNK_RADIUS_PACKET:break; - case Info::CHUNK_RADIUS_UPDATED_PACKET:break; - case Info::ITEM_FRAME_DROP_ITEM_PACKET:break; - case Info::REPLACE_SELECTED_ITEM_PACKET:break; - case Info::ADD_ITEM_PACKET:break; + case Info::ADD_PLAYER_PACKET: + $packets = []; + $pk = new SpawnPlayerPacket(); + $pk->name = $packet->username; + $pk->eid = $packet->eid; + $pk->uuid = Binary::UUIDtoString("00000000000030008000000000000000"); + $pk->x = $packet->x; + $pk->z = $packet->y; + $pk->y = $packet->z; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; + $pk->item = 0; + $pk->metadata = $packet->metadata; + $packets[] = $pk; + + $pk = new EntityTeleportPacket(); + $pk->eid = $packet->eid; + $pk->x = $packet->x; + $pk->y = $packet->y; + $pk->z = $packet->z; + $pk->yaw = $packet->yaw; + $pk->pitch = $packet->pitch; + $packets[] = $pk; + return $packets; + + case Info::ADD_ITEM_ENTITY_PACKET: + $packets = []; + $pk = new SpawnObjectPacket(); + $pk->eid = $packet->eid; + $pk->type = 2; + $pk->x = $player->x; + $pk->y = $player->y; + $pk->z = $player->z; + $pk->yaw = $player->yaw; + $pk->pitch = $player->pitch; + $packets[] = $pk; + + $pk = new EntityMetadataPacket(); + $pk->eid = $packet->eid; + $pk->metadata = $pk->metadata = [ + 0 => ["type" => 0, "value" => 0], + 10 => ["type" => 5, "value" => $packet->item], + ]; + $packets[] = $pk; + + return $packets; + + case Info::LOGIN_PACKET: + case Info::PLAY_STATUS_PACKET: + case Info::SERVER_TO_CLIENT_HANDSHAKE_PACKET: + case Info::CLIENT_TO_SERVER_HANDSHAKE_PACKET: + case Info::DISCONNECT_PACKET: + case Info::BATCH_PACKET: + case Info::SET_TIME_PACKET: + case Info::ADD_ENTITY_PACKET: + case Info::REMOVE_ENTITY_PACKET: + case Info::ADD_ITEM_ENTITY_PACKET: + case Info::TAKE_ITEM_ENTITY_PACKET: + case Info::MOVE_ENTITY_PACKET: + case Info::RIDER_JUMP_PACKET: + case Info::REMOVE_BLOCK_PACKET: + case Info::UPDATE_BLOCK_PACKET: + case Info::ADD_PAINTING_PACKET: + case Info::EXPLODE_PACKET: + case Info::LEVEL_EVENT_PACKET: + case Info::BLOCK_EVENT_PACKET: + case Info::ENTITY_EVENT_PACKET: + case Info::MOB_EFFECT_PACKET: + case Info::UPDATE_ATTRIBUTES_PACKET: + case Info::MOB_EQUIPMENT_PACKET: + case Info::MOB_ARMOR_EQUIPMENT_PACKET: + case Info::INTERACT_PACKET: + case Info::USE_ITEM_PACKET: + case Info::PLAYER_ACTION_PACKET: + case Info::HURT_ARMOR_PACKET: + case Info::SET_ENTITY_DATA_PACKET: + case Info::SET_ENTITY_MOTION_PACKET: + case Info::SET_ENTITY_LINK_PACKET: + case Info::SET_HEALTH_PACKET: + case Info::SET_SPAWN_POSITION_PACKET: + case Info::ANIMATE_PACKET: + case Info::RESPAWN_PACKET: + case Info::DROP_ITEM_PACKET: + case Info::CONTAINER_OPEN_PACKET: + case Info::CONTAINER_CLOSE_PACKET: + case Info::CONTAINER_SET_SLOT_PACKET: + case Info::CONTAINER_SET_DATA_PACKET: + case Info::CONTAINER_SET_CONTENT_PACKET: + case Info::CRAFTING_DATA_PACKET: + case Info::CRAFTING_EVENT_PACKET: + case Info::ADVENTURE_SETTINGS_PACKET: + case Info::BLOCK_ENTITY_DATA_PACKET: + case Info::PLAYER_INPUT_PACKET: + case Info::FULL_CHUNK_DATA_PACKET: + case Info::SET_DIFFICULTY_PACKET: + case Info::CHANGE_DIMENSION_PACKET: + case Info::SET_PLAYER_GAMETYPE_PACKET: + case Info::PLAYER_LIST_PACKET: + case Info::TELEMETRY_EVENT_PACKET: + case Info::SPAWN_EXPERIENCE_ORB_PACKET: + case Info::CLIENTBOUND_MAP_ITEM_DATA_PACKET: + case Info::MAP_INFO_REQUEST_PACKET: + case Info::REQUEST_CHUNK_RADIUS_PACKET: + case Info::CHUNK_RADIUS_UPDATED_PACKET: + case Info::ITEM_FRAME_DROP_ITEM_PACKET: + case Info::REPLACE_SELECTED_ITEM_PACKET: + case Info::ADD_ITEM_PACKET: //return null; @@ -351,55 +654,10 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ } return $packets; - case Info::ADD_ITEM_ENTITY_PACKET: - $packets = []; - $pk = new SpawnObjectPacket(); - $pk->eid = $packet->eid; - $pk->type = 2; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $packets[] = $pk; - - $pk = new EntityMetadataPacket(); - $pk->eid = $packet->eid; - $pk->metadata = $pk->metadata = [ - 0 => ["type" => 0, "value" => 0], - 10 => ["type" => 5, "value" => $packet->item], - ]; - $packets[] = $pk; - - return $packets; - - - case Info::ADD_PLAYER_PACKET: - $packets = []; - $pk = new SpawnPlayerPacket(); - $pk->name = $packet->username; - $pk->eid = $packet->eid; - $pk->uuid = Binary::UUIDtoString("00000000000030008000000000000000"); - $pk->x = $packet->x; - $pk->z = $packet->y; - $pk->y = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $pk->item = 0; - $pk->metadata = $packet->metadata; - $packets[] = $pk; - - $pk = new EntityTeleportPacket(); - $pk->eid = $packet->eid; - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->yaw = $packet->yaw; - $pk->pitch = $packet->pitch; - $packets[] = $pk; - return $packets;*/ + */ default: + //echo "[Send] 0x".$packet->pid()."\n"; return null; } } diff --git a/src/shoghicp/BigBrother/utils/Binary.php b/src/shoghicp/BigBrother/utils/Binary.php index 2dd4fbb5..ada21bdd 100755 --- a/src/shoghicp/BigBrother/utils/Binary.php +++ b/src/shoghicp/BigBrother/utils/Binary.php @@ -34,41 +34,48 @@ public static function UUIDtoString($uuid){ public static function writeMetadata(array $data){ $m = ""; + print_r($data); foreach($data as $bottom => $d){ - $m .= chr(($d["type"] << 5) | ($bottom & 0x1F)); - switch($d["type"]){ + $m .= chr(($d[0] << 5) | ($bottom & 0x1F)); + switch($d[0]){ case 0: - $m .= self::writeByte($d["value"]); + $m .= self::writeByte($d[1]); break; case 1: - $m .= self::writeShort($d["value"]); + $m .= self::writeShort($d[1]); break; case 2: - $m .= self::writeInt($d["value"]); + $m .= self::writeInt($d[1]); break; case 3: - $m .= self::writeFloat($d["value"]); + $m .= self::writeFloat($d[1]); break; case 4: - $m .= self::writeVarInt(strlen($d["value"])) . $d["value"]; + $m .= self::writeVarInt(strlen($d[1])) . $d[1]; break; case 5: /** @var \pocketmine\item\Item $item */ - $item = $d["value"]; + $item = $d[1]; if($item->getID() === 0){ $m .= self::writeShort(-1); }else{ $m .= self::writeShort($item->getID()); $m .= self::writeByte($item->getCount()); $m .= self::writeShort($item->getDamage()); - $m .= self::writeShort(-1); + $nbt = $item->getCompoundTag(); + $m .= self::writeByte(strlen($nbt)).$nbt; } break; case 6: - $m .= self::writeInt($d["value"][0]); - $m .= self::writeInt($d["value"][1]); - $m .= self::writeInt($d["value"][2]); + case 7: + $m .= self::writeInt($d[1][0]); + $m .= self::writeInt($d[1][1]); + $m .= self::writeInt($d[1][2]); + break; + case 8: + $m .= self::writeLong($d[1]); break; + } } $m .= "\x7f"; From c6ec3644711a52df26f27b17f6888b96f825ca66 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Wed, 13 Jul 2016 17:13:59 -0400 Subject: [PATCH 18/21] Changelog: Update 6 --- README.md | 5 + src/shoghicp/BigBrother/DesktopPlayer.php | 85 +++++- .../BigBrother/network/ProtocolInterface.php | 62 ++++- .../protocol/EntityEquipmentPacket.php | 32 +++ .../network/protocol/PlayerListPacket.php | 76 ++++++ .../protocol/PlayerPositionAndLookPacket.php | 6 +- .../network/protocol/STCChatPacket.php | 4 +- .../network/protocol/SpawnMobPacket.php | 2 + .../network/protocol/SpawnPlayerPacket.php | 6 +- .../translation/TranslatorProtocol.php | 249 ++++++++++++------ src/shoghicp/BigBrother/utils/Binary.php | 1 - 11 files changed, 424 insertions(+), 104 deletions(-) create mode 100755 src/shoghicp/BigBrother/network/protocol/EntityEquipmentPacket.php create mode 100755 src/shoghicp/BigBrother/network/protocol/PlayerListPacket.php diff --git a/README.md b/README.md index 00ba82e0..d4e22994 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # BigBrother Allows the connection of Minecraft: PC clients to PocketMine-MP servers. Made for PocketMine-MP +##Changelog: Update 6: +* More Fixes +* More Packets (mostly from lolcraftboy14) +* Note: Player from Minecraft PC sometimes lags. + ###Changelog: Update 5: * Works Again MCPE 0.15.x and Minecraft PC 1.8.x * First Step on Protocols diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index bff566cb..db86a242 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -151,13 +151,89 @@ protected function sendNextChunk(){ $chunk = null; } if($this->chunkLoadCount >= 4 and $this->spawned === false and $this->teleportPosition === null){ - $this->doFirstSpawn(); + /*$this->doFirstSpawn(); + $this->inventory->sendContents($this); + $this->inventory->sendArmorContents($this);*/ + //$this->bigBrother_setTitleBar(TextFormat::YELLOW . TextFormat::BOLD . "This is a beta version of BigBrother.", 0); + $this->spawned = true; + $pk = new SetTimePacket(); + $pk->time = $this->level->getTime(); + $pk->started = $this->level->stopTime == false; + $this->dataPacket($pk); + $pos = $this->level->getSafeSpawn($this); + $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); + $this->teleport($ev->getRespawnPosition()); + $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); + $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, TextFormat::YELLOW . $this->getName() . " joined the game")); + if(strlen(trim($ev->getJoinMessage())) > 0){ + $this->server->broadcastMessage($ev->getJoinMessage()); + } + $this->spawnToAll(); + if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ + $this->server->getUpdater()->showPlayerUpdate($this); + } } Timings::$playerChunkSendTimer->stopTiming(); } + public function bigBrother_updateTitleBar(){ + if($this->bigBrother_titleBarID === null){ + $this->bigBrother_titleBarID = 2147483647; + $pk = new SpawnMobPacket(); + $pk->eid = $this->bigBrother_titleBarID; + $pk->type = 63; + $pk->x = $this->x; + $pk->y = 250; + $pk->z = $this->z; + $pk->pitch = 0; + $pk->yaw = 0; + $pk->headPitch = 0; + $pk->velocityX = 0; + $pk->velocityY = 0; + $pk->velocityZ = 0; + $pk->metadata = [ + 0 => ["type" => 0, "value" => 0x20], + 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], + 7 => ["type" => 2, "value" => 0], + 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], + 11 => ["type" => 0, "value" => 1] + ]; + $this->putRawPacket($pk); + $this->tasks[] = $this->getServer()->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_updateTitleBar"]), 5, 20); + }else{ + $pk = new EntityTeleportPacket(); + $pk->eid = $this->bigBrother_titleBarID; + $pk->x = $this->x; + $pk->y = 250; + $pk->z = $this->z; + $pk->yaw = 0; + $pk->pitch = 0; + $this->putRawPacket($pk); + $pk = new EntityMetadataPacket(); + $pk->eid = $this->bigBrother_titleBarID; + $pk->metadata = [ + 0 => ["type" => 0, "value" => 0x20], + 6 => ["type" => 3, "value" => 200 * ($this->bigBrother_titleBarLevel / 100)], + 7 => ["type" => 2, "value" => 0], + 10 => ["type" => 4, "value" => $this->bigBrother_titleBarText], + 11 => ["type" => 0, "value" => 1] + ]; + $this->putRawPacket($pk); + } + } + public function bigBrother_setTitleBar($text, $level = 100){ + if($level > 100){ + $level = 100; + }elseif($level < 0){ + $level = 0; + } + $this->bigBrother_titleBarText = $text; + $this->bigBrother_titleBarLevel = $level; + $this->bigBrother_updateTitleBar(); + } + public function spawnTo(Player $player){ if($player instanceof DesktopPlayer){ if($this !== $player and $this->spawned === true and $player->getLevel() === $this->getLevel() and $player->canSee($this)){ @@ -332,12 +408,9 @@ public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $ $pk->verifyToken = $this->bigBrother_checkToken = Utils::getRandomBytes(4, false, true, $pk->publicKey); $this->putRawPacket($pk); }else{ - /*$profile = json_decode(Utils::getURL("https://api.mojang.com/users/profiles/minecraft/".$username), true); - if(!is_array($profile)){ - return false; - }*/ - $this->bigBrother_authenticate($this->bigBrother_username, /*$profile["id"]*/"c96792ac7aea4f16975e535a20a2791a", null); + $this->bigBrother_authenticate($this->bigBrother_username, UUID::fromRandom()->toString(), null); } + } } diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index 4bb78bdb..da5bd6c5 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -155,18 +155,24 @@ protected function receivePacket(DesktopPlayer $player, Packet $packet){ protected function handlePacket(DesktopPlayer $player, $payload){ $pid = ord($payload{0}); - $offset = 0; + $offset = 1; $status = $player->bigBrother_getStatus(); if($status === 1){ switch($pid){ + case 0x00: + $pk = new KeepAlivePacket(); + break; case 0x01: $pk = new CTSChatPacket(); break; case 0x02: $pk = new UseEntityPacket(); break; + case 0x03: + $pk = new PlayerPacket(); + break; case 0x04: $pk = new PlayerPositionPacket(); break; @@ -182,25 +188,69 @@ protected function handlePacket(DesktopPlayer $player, $payload){ case 0x08: $pk = new PlayerBlockPlacementPacket(); break; + case 0x09: + $pk = new HeldItemChangePacket(); + break; + case 0x0a: + $pk = new PlayerArmSwingPacket(); + break; + case 0x0b: + $pk = new AnimatePacket(); + break; + /*case 0x0c: + // + break;*/ case 0x0d: $pk = new CTSCloseWindowPacket(); break; + /*case 0x0e: + break; + case 0x0f: + + break;*/ + case 0x10: + $pk = new CreativeInventoryActionPacket(); + break; + /*case 0x11: + + break; + case 0x12: + + break;*/ + case 0x13: + $pk = new CPlayerAbilitiesPacket(); + break; + case 0x14: + $pk = new CTabCompletePacket(); + break; + case 0x15: + $pk = new ClientSettingsPacket(); + break; case 0x16: $pk = new ClientStatusPacket(); break; + case 0x17: + $pk = new PluginMessagePacket(); + break; + /*case 0x18: + // + break;*/ + case 0x19: + $pk = new ResourcePackStatusPacket(); + break; default: + //echo "[Receive] 0x".bin2hex(chr($pid))."\n"; return; } - $pk->read($payload, $offset); $this->receivePacket($player, $pk); + }elseif($status === 0){ if($pid === 0x00){ - //$pk = new LoginStartPacket(); - //$pk->read($payload, $offset); - - $player->bigBrother_handleAuthentication($this->plugin, preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $payload), $this->plugin->isOnlineMode()); + $pk = new LoginStartPacket(); + $pk->read($payload, $offset); + $player->bigBrother_handleAuthentication($this->plugin, /*preg_replace('/[\x00-\x1F\x80-\xFF]/', '', )*/$pk->name, $this->plugin->isOnlineMode()); }elseif($pid === 0x01 and $this->plugin->isOnlineMode()){ $pk = new EncryptionResponsePacket(); $pk->read($payload, $offset); diff --git a/src/shoghicp/BigBrother/network/protocol/EntityEquipmentPacket.php b/src/shoghicp/BigBrother/network/protocol/EntityEquipmentPacket.php new file mode 100755 index 00000000..b5112a38 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/EntityEquipmentPacket.php @@ -0,0 +1,32 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ +namespace shoghicp\BigBrother\network\protocol; +use shoghicp\BigBrother\network\Packet; +class EntityEquipmentPacket extends Packet{ + public $eid; + public $slot; + public $item; + public function pid(){ + return 0x04; + } + public function encode(){ + $this->putVarInt($this->eid); + $this->putShort($this->slot); + $this->putSlot($this->item); + } + public function decode(){ + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerListPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerListPacket.php new file mode 100755 index 00000000..b32b0658 --- /dev/null +++ b/src/shoghicp/BigBrother/network/protocol/PlayerListPacket.php @@ -0,0 +1,76 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +namespace shoghicp\BigBrother\network\protocol; + +use shoghicp\BigBrother\network\Packet; +use shoghicp\BigBrother\utils\Binary; + +class PlayerListPacket extends Packet{ + + const TYPE_ADD = 0; + const TYPE_REMOVE = 4; + + public $actionID; + public $players = []; + + public function pid(){ + return 0x38; + } + + public function clean(){ + $this->players = []; + return parent::clean(); + } + + public function encode(){ + $this->putVarInt($this->actionID); + $this->putVarInt(count($this->players)); + foreach($this->players as $player){ + if($this->actionID === self::TYPE_ADD){//add Player + $this->putLong(substr($player[0], 0, 16));//UUID + $this->putLong(substr($player[0], 16, 16)); + $this->putString($player[1]); //PlayerName + $this->putVarInt(count($player[2])); //Count Peropetry + + foreach($player[2] as $peropetrydata){ + $this->putString($peropetrydata["name"]); //Name + $this->putString($peropetrydata["value"]); //Value + if(isset($peropetrydata["signature"])){ + $this->putByte(1); //Is Signed + $this->putString($peropetrydata["signature"]); //Peropetry + }else{ + $this->putByte(0); //Is Signed + } + } + $this->putVarInt($player[3]); //Gamemode + $this->putVarInt($player[4]); //Ping + $this->putByte($player[5] ? 1 : 0); //has Display name + if($player[5] === true){ + $this->putString($player[6]); //Display name + } + }else{ + $this->putLong(substr($player[0], 0, 16));//UUID + $this->putLong(substr($player[0], 16, 16)); + } + } + } + + public function decode(){ + + } +} \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php b/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php index 613df8ef..9e614552 100755 --- a/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/PlayerPositionAndLookPacket.php @@ -27,21 +27,23 @@ class PlayerPositionAndLookPacket extends Packet{ public $yaw; public $pitch; public $onGround; + public $pid; public function pid(){ return 0x06; } public function encode(){ - + } public function decode(){ + //$this->pid = $this->getInt(); $this->x = $this->getDouble(); $this->y = $this->getDouble(); $this->z = $this->getDouble(); $this->yaw = $this->getFloat(); $this->pitch = $this->getFloat(); - $this->onGround = ($this->getByte() > 0); + $this->onGround = $this->getByte() > 0; } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php b/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php index a20e3e53..f6a438f3 100755 --- a/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/STCChatPacket.php @@ -29,12 +29,12 @@ public function pid(){ } public function encode(){ - $this->putString($this->message); $this->putByte($this->position); + $this->putString($this->message); } public function decode(){ - $this->message = $this->getString(); $this->position = $this->getByte(); + $this->message = $this->getString(); } } \ No newline at end of file diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php index 5494d579..00ed90dd 100755 --- a/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SpawnMobPacket.php @@ -23,6 +23,7 @@ class SpawnMobPacket extends Packet{ public $eid; + public $uuid; public $type; public $x; public $y; @@ -41,6 +42,7 @@ public function pid(){ public function encode(){ $this->putVarInt($this->eid); + $this->putString($this->uuid); $this->putByte($this->type); $this->putInt(intval($this->x * 32)); $this->putInt(intval($this->y * 32)); diff --git a/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php b/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php index 9d77339f..d00a546a 100755 --- a/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php +++ b/src/shoghicp/BigBrother/network/protocol/SpawnPlayerPacket.php @@ -38,14 +38,16 @@ public function pid(){ public function encode(){ $this->putVarInt($this->eid); - $this->putString($this->uuid); + $this->putLong(substr($this->uuid, 0, 16));//UUID + $this->putLong(substr($this->uuid, 16, 16)); $this->putInt(intval($this->x * 32)); $this->putInt(intval($this->y * 32)); $this->putInt(intval($this->z * 32)); $this->putByte(($this->yaw / 360) << 8); $this->putByte(($this->pitch / 360) << 8); $this->putShort($this->item); - $this->put(Binary::writeMetadata($this->metadata)); + $meta = Binary::writeMetadata($this->metadata); + $this->put($meta); } public function decode(){ diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index ec96deb5..dcaa124b 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -50,6 +50,8 @@ use shoghicp\BigBrother\network\protocol\TimeUpdatePacket; use shoghicp\BigBrother\network\protocol\UpdateHealthPacket; use shoghicp\BigBrother\network\protocol\WindowItemsPacket; +use shoghicp\BigBrother\network\protocol\PlayerListPacket; +use shoghicp\BigBrother\network\protocol\EntityEquipmentPacket; use shoghicp\BigBrother\utils\Binary; class TranslatorProtocol implements Translator{ @@ -71,8 +73,8 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x01: //ChatPacket $pk = new TextPacket(); - $pk->type = TextPacket::TYPE_CHAT;//Chat Type - //$pk->source = ""; + $pk->type = 1; + $pk->source = ""; $pk->message = $packet->message; return $pk; @@ -88,9 +90,9 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x04: //PlayerPositonPacket $pk = new MovePlayerPacket(); - $pk->x = $player->x; - $pk->y = $player->y + $player->getEyeHeight(); - $pk->z = $player->z; + $pk->x = $packet->x; + $pk->y = $packet->y + $player->getEyeHeight(); + $pk->z = $packet->z; $pk->yaw = $player->yaw; $pk->bodyYaw = $player->yaw; $pk->pitch = $player->pitch; @@ -108,13 +110,14 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ case 0x06: //PlayerPositionAndLookPacket $pk = new MovePlayerPacket(); - $pk->x = $player->x; - $pk->y = $player->y + $player->getEyeHeight(); - $pk->z = $player->z; + $pk->x = $packet->x; + $pk->y = $packet->y + $player->getEyeHeight(); + $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->bodyYaw = $packet->yaw; $pk->pitch = $packet->pitch; return $pk; + case 0x07: //PlayerDiggingPacket switch($packet->status){ case 0: @@ -166,18 +169,18 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $packets[] = $pk; return $packets; }else{ - echo "PlayerDiggingPacket: ".$packet->status."\n"; + //echo "PlayerDiggingPacket: ".$packet->status."\n"; } break; default: - echo "PlayerDiggingPacket: ".$packet->status."\n"; + //echo "PlayerDiggingPacket: ".$packet->status."\n"; break; } return null; case 0x08; //PlayerBlockPlacementPacket - echo "PlayerBlockPlacementPacket: ".$packet->direction."\n"; + //echo "PlayerBlockPlacementPacket: ".$packet->direction."\n"; if($packet->direction !== 255){ $pk = new UseItemPacket(); @@ -280,11 +283,8 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $pk->face = 0; return $pk; break; - /*case 6: - - break;*/ default: - echo "[AnimatePacket] ".$packet->actionID."\n";//Debug Code + ///echo "[AnimatePacket] ".$packet->actionID."\n";//Debug Code break; } return null; @@ -297,8 +297,8 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ } case 0x10: //CreativeInventoryActionPacket - echo "Slot: ".$packet->slot."\n"; - echo "ItemId: ".$packet->item->getId()." : ".$packet->item->getDamage()."\n"; + //echo "Slot: ".$packet->slot."\n"; + //echo "ItemId: ".$packet->item->getId()." : ".$packet->item->getDamage()."\n"; /*if($packet->slot === 65535){ $pk = new DropItemPacket(); @@ -393,7 +393,7 @@ public function interfaceToServer(DesktopPlayer $player, Packet $packet){ $player->setSetting(["ServerType" => $packet->data]); break; default: - echo "PluginChannel: ".$packet->channel."\n"; + //echo "PluginChannel: ".$packet->channel."\n"; break; } return null; @@ -411,7 +411,6 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ switch($packet->pid()){ case Info::START_GAME_PACKET: - //echo "START_GAME_PACKET\n"; $packets = []; $pk = new JoinGamePacket(); @@ -442,7 +441,6 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $packets[] = $pk; } - echo "SpawnX: ".$packet->spawnX." SpawnY: ".$packet->spawnY." SpawnZ: ".$packet->spawnZ."\n"; $pk = new SpawnPositionPacket(); $pk->spawnX = $packet->spawnX; $pk->spawnY = $packet->spawnY; @@ -465,6 +463,22 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->time = $packet->time; //TODO: calculate offset from MCPE return $pk; + case Info::REMOVE_ENTITY_PACKET: + $pk = new DestroyEntitiesPacket(); + $pk->ids[] = $packet->eid; + return $pk; + /*case Info::REMOVE_PLAYER_PACKET: + $pk = new PlayerListPacket(); + $pk->actionID = PlayerListPacket::TYPE_REMOVE; + $pk->players[] = [ + $packet->clientId->toBinary() + ]; + $packets[] = $pk; + $pk = new DestroyEntitiesPacket(); + $pk->ids[] = $packet->eid; + $packets[] = $pk; + return $packets;*/ + case Info::SET_SPAWN_POSITION_PACKET: $pk = new SpawnPositionPacket(); $pk->spawnX = $packet->x; @@ -480,17 +494,12 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk->saturation = 5; return $pk; - case Info::TEXT_PACKET: - $pk = new STCChatPacket(); - - $pk->message = TextFormat::toJSON($packet->message); - return $pk; - + case Info::MOVE_ENTITY_PACKET: case Info::MOVE_PLAYER_PACKET: if($packet->eid === 0){ $pk = new PositionAndLookPacket(); $pk->x = $packet->x; - $pk->y = $packet->y; + $pk->y = $packet->y - $player->getEyeHeight(); $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; @@ -501,7 +510,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ $pk = new EntityTeleportPacket(); $pk->eid = $packet->eid; $pk->x = $packet->x; - $pk->y = $packet->y; + $pk->y = $packet->y - $player->getEyeHeight(); $pk->z = $packet->z; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; @@ -516,19 +525,30 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ case Info::ADD_PLAYER_PACKET: $packets = []; + $packetplayer = $player->getServer()->getPlayerExact($packet->username); + $pk = new PlayerListPacket(); + $pk->actionID = PlayerListPacket::TYPE_ADD; + $pk->players[] = [ + $packetplayer->getUniqueId()->toBinary(), + $packetplayer->getName(), + [], + $packetplayer->getGamemode(), + 0, + false, + ]; + + $packets[] = $pk; $pk = new SpawnPlayerPacket(); - $pk->name = $packet->username; $pk->eid = $packet->eid; - $pk->uuid = Binary::UUIDtoString("00000000000030008000000000000000"); + $pk->uuid = $packetplayer->getUniqueId()->toBinary(); $pk->x = $packet->x; - $pk->z = $packet->y; - $pk->y = $packet->z; + $pk->z = $packet->z; + $pk->y = $packet->y; $pk->yaw = $packet->yaw; $pk->pitch = $packet->pitch; - $pk->item = 0; + $pk->item = $packetplayer->getInventory()->getItemInHand()->getId(); $pk->metadata = $packet->metadata; $packets[] = $pk; - $pk = new EntityTeleportPacket(); $pk->eid = $packet->eid; $pk->x = $packet->x; @@ -561,57 +581,149 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return $packets; - case Info::LOGIN_PACKET: + case Info::UPDATE_BLOCK_PACKET: + $pk = new BlockChangePacket(); + $count = count($packet->records) - 1; + $pk->x = $packet->records[$count][0]; + $pk->y = $packet->records[$count][2]; + $pk->z = $packet->records[$count][1]; + $pk->blockId = $packet->records[$count][3]; + $pk->blockMeta = $packet->records[$count][4]; + return $pk; + + case Info::MOB_EQUIPMENT_PACKET: + $pk = new EntityEquipmentPacket(); + $pk->eid = $packet->eid; + $pk->slot = $packet->slot; + $pk->item = $packet->item; + return $pk; + + + case Info::MOB_ARMOR_EQUIPMENT_PACKET: + $packets = []; + foreach($packet->slots as $num => $item){ + $pk = new EntityEquipmentPacket(); + $pk->eid = $packet->eid; + $pk->slot = $num + 1; + $pk->item = $item; + $packets[] = $pk; + } + return $packets; + + case Info::SET_ENTITY_DATA_PACKET: + $pk = new EntityMetadataPacket(); + $pk->eid = $packet->eid; + $pk->metadata = $packet->metadata; + return $pk; + + case Info::SET_ENTITY_MOTION_PACKET: + $packets = []; + foreach($packet->entities as $d){ + $pk = new EntityVelocityPacket(); + $pk->eid = $d[0]; + $pk->velocityX = $d[1]; + $pk->velocityY = $d[2]; + $pk->velocityZ = $d[3]; + $packets[] = $pk; + } + return $packets; + + case Info::ANIMATE_PACKET: + switch($packet->action){ + case 1: + $pk = new CAnimatePacket(); + $pk->actionID = 0; + $pk->eid = $packet->eid; + return $pk; + break; + case 3: //LeaveBed + $pk = new CAnimatePacket(); + $pk->actionID = 2; + $pk->eid = $packet->eid; + return $pk; + break; + default: + //echo "AnimatePacket: ".$packet->action."\n"; + break; + } + return null; + + case Info::RESPAWN_PACKET: + $pk = new CRespawnPacket(); + $pk->dimension = 0; + $pk->difficulty = $player->getServer()->getDifficulty(); + $pk->gamemode = $player->getGamemode(); + $pk->levelType = "default"; + return $pk; + + case Info::CRAFTING_DATA_PACKET: + $player->setSetting(["Recipes" => $packet->entries, "cleanRecipes" => $packet->cleanRecipes]); + return null; + + /*case Info::SET_DIFFICULTY_PACKET: + $pk = new ServerDifficultyPacket(); + $pk->difficulty = $packet->difficulty; + return $pk;*/ + + case Info::SET_PLAYER_GAMETYPE_PACKET: + $packets = []; + $pk = new PlayerAbilitiesPacket(); + $pk->flyingSpeed = 0.05; + $pk->walkingSpeed = 0.1; + $pk->canFly = ($player->getGamemode() & 0x01) > 0; + $pk->damageDisabled = ($player->getGamemode() & 0x01) > 0; + $pk->isFlying = false; + $pk->isCreative = ($player->getGamemode() & 0x01) > 0; + $packets[] = $pk; + $pk = new ChangeGameStatePacket(); + $pk->reason = 3; + $pk->value = $player->getGamemode(); + $packets[] = $pk; + return $packets; + + case Info::INTERACT_PACKET: case Info::PLAY_STATUS_PACKET: + case Info::BATCH_PACKET: + case Info::TEXT_PACKET: + return null; + + case Info::LOGIN_PACKET: + case Info::EXPLODE_PACKET: + case Info::PLAYER_LIST_PACKET: + case Info::FULL_CHUNK_DATA_PACKET: case Info::SERVER_TO_CLIENT_HANDSHAKE_PACKET: case Info::CLIENT_TO_SERVER_HANDSHAKE_PACKET: case Info::DISCONNECT_PACKET: - case Info::BATCH_PACKET: case Info::SET_TIME_PACKET: case Info::ADD_ENTITY_PACKET: - case Info::REMOVE_ENTITY_PACKET: case Info::ADD_ITEM_ENTITY_PACKET: case Info::TAKE_ITEM_ENTITY_PACKET: - case Info::MOVE_ENTITY_PACKET: case Info::RIDER_JUMP_PACKET: case Info::REMOVE_BLOCK_PACKET: - case Info::UPDATE_BLOCK_PACKET: case Info::ADD_PAINTING_PACKET: - case Info::EXPLODE_PACKET: case Info::LEVEL_EVENT_PACKET: case Info::BLOCK_EVENT_PACKET: case Info::ENTITY_EVENT_PACKET: case Info::MOB_EFFECT_PACKET: case Info::UPDATE_ATTRIBUTES_PACKET: - case Info::MOB_EQUIPMENT_PACKET: - case Info::MOB_ARMOR_EQUIPMENT_PACKET: - case Info::INTERACT_PACKET: case Info::USE_ITEM_PACKET: case Info::PLAYER_ACTION_PACKET: case Info::HURT_ARMOR_PACKET: - case Info::SET_ENTITY_DATA_PACKET: - case Info::SET_ENTITY_MOTION_PACKET: case Info::SET_ENTITY_LINK_PACKET: case Info::SET_HEALTH_PACKET: case Info::SET_SPAWN_POSITION_PACKET: - case Info::ANIMATE_PACKET: - case Info::RESPAWN_PACKET: case Info::DROP_ITEM_PACKET: case Info::CONTAINER_OPEN_PACKET: case Info::CONTAINER_CLOSE_PACKET: case Info::CONTAINER_SET_SLOT_PACKET: case Info::CONTAINER_SET_DATA_PACKET: case Info::CONTAINER_SET_CONTENT_PACKET: - case Info::CRAFTING_DATA_PACKET: case Info::CRAFTING_EVENT_PACKET: case Info::ADVENTURE_SETTINGS_PACKET: case Info::BLOCK_ENTITY_DATA_PACKET: case Info::PLAYER_INPUT_PACKET: - case Info::FULL_CHUNK_DATA_PACKET: case Info::SET_DIFFICULTY_PACKET: case Info::CHANGE_DIMENSION_PACKET: - case Info::SET_PLAYER_GAMETYPE_PACKET: - case Info::PLAYER_LIST_PACKET: case Info::TELEMETRY_EVENT_PACKET: case Info::SPAWN_EXPERIENCE_ORB_PACKET: case Info::CLIENTBOUND_MAP_ITEM_DATA_PACKET: @@ -623,41 +735,8 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ case Info::ADD_ITEM_PACKET: //return null; - - - /*case Info::UPDATE_BLOCK_PACKET: - $pk = new BlockChangePacket(); - $pk->x = $packet->x; - $pk->y = $packet->y; - $pk->z = $packet->z; - $pk->blockId = $packet->block; - $pk->blockMeta = $packet->meta; - return $pk; - - case Info::REMOVE_ENTITY_PACKET: - //case Info::REMOVE_PLAYER_PACKET: - $pk = new DestroyEntitiesPacket(); - $pk->ids[] = $packet->eid; - return $pk; - - - - case Info::SET_ENTITY_MOTION_PACKET: - $packets = []; - foreach($packet->entities as $d){ - $pk = new EntityVelocityPacket(); - $pk->eid = $d[0]; - $pk->velocityX = $d[1]; - $pk->velocityY = $d[2]; - $pk->velocityZ = $d[3]; - $packets[] = $pk; - } - return $packets; - - */ - default: - //echo "[Send] 0x".$packet->pid()."\n"; + echo "[Send] 0x".$packet->pid()."\n"; return null; } } diff --git a/src/shoghicp/BigBrother/utils/Binary.php b/src/shoghicp/BigBrother/utils/Binary.php index ada21bdd..000aad3f 100755 --- a/src/shoghicp/BigBrother/utils/Binary.php +++ b/src/shoghicp/BigBrother/utils/Binary.php @@ -34,7 +34,6 @@ public static function UUIDtoString($uuid){ public static function writeMetadata(array $data){ $m = ""; - print_r($data); foreach($data as $bottom => $d){ $m .= chr(($d[0] << 5) | ($bottom & 0x1F)); switch($d[0]){ From f7861453a527e5710a5a3b67a781a78308870423 Mon Sep 17 00:00:00 2001 From: va2ron1 Date: Wed, 13 Jul 2016 17:46:36 -0400 Subject: [PATCH 19/21] Add License --- LICENSE | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. From ff6f2475ae7af692cac31b7ea99d11ae4b65f42e Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Fri, 15 Jul 2016 18:39:01 -0400 Subject: [PATCH 20/21] fixed PocketMine API --- plugin.yml | 6 ++-- src/shoghicp/BigBrother/BigBrother.php | 5 ++-- src/shoghicp/BigBrother/DesktopPlayer.php | 29 +++++++++++++++++-- .../BigBrother/network/ProtocolInterface.php | 1 + 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/plugin.yml b/plugin.yml index 2026ed24..b9a2bac2 100755 --- a/plugin.yml +++ b/plugin.yml @@ -1,10 +1,8 @@ name: BigBrother -version: 1.5.0-beta -api: 1.10.0 +version: 1.15.0 beta +api: 2.0.0 description: "Allows the connection of Minecraft: PC clients to PocketMine-MP servers" author: shoghicp website: https://github.com/shoghicp/BigBrother load: STARTUP main: shoghicp\BigBrother\BigBrother -softdepend: - - SimpleAuth diff --git a/src/shoghicp/BigBrother/BigBrother.php b/src/shoghicp/BigBrother/BigBrother.php index 1002f8c5..fe3da09b 100755 --- a/src/shoghicp/BigBrother/BigBrother.php +++ b/src/shoghicp/BigBrother/BigBrother.php @@ -109,9 +109,8 @@ protected function enableServer(){ $interface = $this->getConfig()->get("interface"); $this->getLogger()->info("Starting Minecraft: PC server on ".($interface === "0.0.0.0" ? "*" : $interface).":$port version ".MCInfo::VERSION); $this->thread = new ServerThread($this->externalQueue, $this->internalQueue, $this->getServer()->getLogger(), $this->getServer()->getLoader(), $port, $interface, (string) $this->getConfig()->get("motd"), $this->getDataFolder() . "server-icon.png"); - $this->interface = new ProtocolInterface($this, $this->thread, $this->translator); - $this->getServer()->addInterface($this->interface); + $this->getServer()->getNetwork()->registerInterface($this->interface); } /** @@ -135,7 +134,7 @@ public function decryptBinary($secret){ public function onDisable(){ //TODO: make it fully /reload compatible (remove from server) if($this->interface instanceof ProtocolInterface){ - $this->getServer()->removeInterface($this->interface); + $this->getServer()->getNetwork()->unregisterInterface($this->interface); $this->thread->join(); } } diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index db86a242..dfe1f5e6 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -72,6 +72,7 @@ class DesktopPlayer extends Player{ protected $bigBrother_titleBarLevel; /** @var ProtocolInterface */ protected $interface; + protected $Settings = []; public function __construct(SourceInterface $interface, $clientID, $address, $port, BigBrother $plugin){ $this->plugin = $plugin; @@ -290,13 +291,16 @@ public function bigBrother_authenticate($username, $uuid, $onlineModeData = null $pk->uuid = $this->bigBrother_formatedUUID; $pk->name = $username; $this->putRawPacket($pk); + $this->bigBrother_status = 1; if($onlineModeData !== null and is_array($onlineModeData)){ $this->bigBrother_properties = $onlineModeData; } - $this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); - $this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 2); + //$this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); + sleep(1); + $this->bigBrother_authenticationCallback($username); + //$this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 2); } } @@ -415,6 +419,27 @@ public function bigBrother_handleAuthentication(BigBrother $plugin, $username, $ } + public function getSettings(){ + return $this->Settings; + } + public function getSetting($settingname = null){ + if(isset($this->Settings[$settingname])){ + return $this->Settings[$settingname]; + } + return false; + } + public function setSetting($settings){ + $this->Settings = array_merge($this->Settings, $settings); + } + public function removeSetting($settingname){ + if(isset($this->Settings[$settingname])){ + unset($this->Settings[$settingname]); + } + } + public function cleanSetting($settingname){ + unset($this->Settings[$settingname]); + } + public function bigBrother_setCompression($threshold){ $this->interface->setCompression($this, $threshold); } diff --git a/src/shoghicp/BigBrother/network/ProtocolInterface.php b/src/shoghicp/BigBrother/network/ProtocolInterface.php index da5bd6c5..2eb35845 100755 --- a/src/shoghicp/BigBrother/network/ProtocolInterface.php +++ b/src/shoghicp/BigBrother/network/ProtocolInterface.php @@ -120,6 +120,7 @@ public function putRawPacket(DesktopPlayer $player, Packet $packet){ } public function putPacket(Player $player, DataPacket $packet, $needACK = false, $immediate = true){ + $id = 0; if($needACK){ $id = $this->identifier++; From 6703f1de47b796e66651ca44f3545f93f9e22b96 Mon Sep 17 00:00:00 2001 From: CrazyJailHacker Date: Sat, 16 Jul 2016 13:35:38 -0400 Subject: [PATCH 21/21] review --- src/shoghicp/BigBrother/DesktopPlayer.php | 2 +- .../BigBrother/network/translation/TranslatorProtocol.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shoghicp/BigBrother/DesktopPlayer.php b/src/shoghicp/BigBrother/DesktopPlayer.php index dfe1f5e6..7b5312a8 100755 --- a/src/shoghicp/BigBrother/DesktopPlayer.php +++ b/src/shoghicp/BigBrother/DesktopPlayer.php @@ -298,7 +298,7 @@ public function bigBrother_authenticate($username, $uuid, $onlineModeData = null } //$this->tasks[] = $this->server->getScheduler()->scheduleDelayedRepeatingTask(new CallbackTask([$this, "bigBrother_sendKeepAlive"]), 180, 2); - sleep(1); + sleep(2); $this->bigBrother_authenticationCallback($username); //$this->server->getScheduler()->scheduleDelayedTask(new CallbackTask([$this, "bigBrother_authenticationCallback"], [$username]), 2); } diff --git a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php index dcaa124b..f4ff2f8d 100755 --- a/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php +++ b/src/shoghicp/BigBrother/network/translation/TranslatorProtocol.php @@ -25,7 +25,6 @@ use pocketmine\network\protocol\MessagePacket; use pocketmine\network\protocol\MovePlayerPacket; use pocketmine\network\protocol\RemoveBlockPacket; -use pocketmine\network\protocol\RespawnPacket; use pocketmine\network\protocol\UseItemPacket; use pocketmine\utils\TextFormat; use shoghicp\BigBrother\DesktopPlayer; @@ -38,6 +37,7 @@ use shoghicp\BigBrother\network\protocol\EntityTeleportPacket; use shoghicp\BigBrother\network\protocol\EntityVelocityPacket; use shoghicp\BigBrother\network\protocol\JoinGamePacket; +use shoghicp\BigBrother\network\protocol\RespawnPacket; use shoghicp\BigBrother\network\protocol\OpenWindowPacket; use shoghicp\BigBrother\network\protocol\PlayerAbilitiesPacket; use shoghicp\BigBrother\network\protocol\PositionAndLookPacket; @@ -649,7 +649,7 @@ public function serverToInterface(DesktopPlayer $player, DataPacket $packet){ return null; case Info::RESPAWN_PACKET: - $pk = new CRespawnPacket(); + $pk = new RespawnPacket(); $pk->dimension = 0; $pk->difficulty = $player->getServer()->getDifficulty(); $pk->gamemode = $player->getGamemode();