| 
<?phpnamespace ParagonIE\EasyRSA;
 
 // PHPSecLib:
 use ParagonIE\EasyRSA\Exception\InvalidKeyException;
 use \phpseclib\Crypt\RSA;
 // defuse/php-encryption:
 use \ParagonIE\ConstantTime\Base64;
 use \Defuse\Crypto\Key;
 use \Defuse\Crypto\Crypto;
 // Typed Exceptions:
 use \ParagonIE\EasyRSA\Exception\InvalidChecksumException;
 use \ParagonIE\EasyRSA\Exception\InvalidCiphertextException;
 
 class EasyRSA implements EasyRSAInterface
 {
 const SEPARATOR = '$';
 const VERSION_TAG = "EzR2";
 
 static private $rsa;
 
 /**
 * Set RSA to use in between calls
 *
 * @param RSA|null $rsa
 */
 public static function setRsa(RSA $rsa = null)
 {
 self::$rsa = $rsa;
 }
 
 /**
 * Get RSA
 *
 * @param int $mode
 *
 * @return RSA
 */
 public static function getRsa($mode)
 {
 if (self::$rsa) {
 $rsa = self::$rsa;
 } else {
 $rsa = new RSA();
 $rsa->setMGFHash('sha256');
 }
 
 $rsa->setSignatureMode($mode);
 
 return $rsa;
 }
 
 /**
 * KEM+DEM approach to RSA encryption.
 *
 * @param string $plaintext
 * @param PublicKey $rsaPublicKey
 *
 * @return string
 */
 public static function encrypt($plaintext, PublicKey $rsaPublicKey)
 {
 // Random encryption key
 $random_key = random_bytes(32);
 
 // Use RSA to encrypt the random key
 $rsaOut = self::rsaEncrypt($random_key, $rsaPublicKey);
 
 // Generate a symmetric key from the RSA output and plaintext
 $symmetricKey = hash_hmac(
 'sha256',
 $rsaOut,
 $random_key,
 true
 );
 $ephemeral = self::defuseKey(
 $symmetricKey
 );
 
 // Now we encrypt the actual message
 $symmetric = Base64::encode(
 Crypto::encrypt($plaintext, $ephemeral, true)
 );
 
 $packaged = \implode(self::SEPARATOR,
 array(
 self::VERSION_TAG,
 Base64::encode($rsaOut),
 $symmetric
 )
 );
 
 $checksum = \substr(
 \hash('sha256', $packaged),
 0,
 16
 );
 
 // Return the ciphertext
 return $packaged . self::SEPARATOR . $checksum;
 }
 
 /**
 * KEM+DEM approach to RSA encryption.
 *
 * @param string $ciphertext
 * @param PrivateKey $rsaPrivateKey
 *
 * @return string
 * @throws InvalidCiphertextException
 * @throws InvalidChecksumException
 */
 public static function decrypt($ciphertext, PrivateKey $rsaPrivateKey)
 {
 $split = explode(self::SEPARATOR, $ciphertext);
 if (\count($split) !== 4) {
 throw new InvalidCiphertextException('Invalid ciphertext message');
 }
 if (!\hash_equals($split[0], self::VERSION_TAG)) {
 throw new InvalidCiphertextException('Invalid version tag');
 }
 $checksum = \substr(
 \hash('sha256', implode('$', array_slice($split, 0, 3))),
 0,
 16
 );
 if (!\hash_equals($split[3], $checksum)) {
 throw new InvalidChecksumException('Invalid checksum');
 }
 
 $rsaCipher = Base64::decode($split[1]);
 $rsaPlain = self::rsaDecrypt(
 $rsaCipher,
 $rsaPrivateKey
 );
 $symmetricKey = hash_hmac(
 'sha256',
 $rsaCipher,
 $rsaPlain,
 true
 );
 
 $key = self::defuseKey($symmetricKey);
 return Crypto::decrypt(
 Base64::decode($split[2]),
 $key,
 true
 );
 }
 
 /**
 * Sign with RSASS-PSS + MGF1+SHA256
 *
 * @param string $message
 * @param PrivateKey $rsaPrivateKey
 * @return string
 */
 public static function sign($message, PrivateKey $rsaPrivateKey)
 {
 $rsa = self::getRsa(RSA::SIGNATURE_PSS);
 
 $return = $rsa->loadKey($rsaPrivateKey->getKey());
 if ($return === false) {
 throw new InvalidKeyException('Signing failed due to invalid key');
 }
 
 return $rsa->sign($message);
 }
 
 /**
 * Verify with RSASS-PSS + MGF1+SHA256
 *
 * @param string $message
 * @param string $signature
 * @param PublicKey $rsaPublicKey
 * @return bool
 */
 public static function verify($message, $signature, PublicKey $rsaPublicKey)
 {
 $rsa = self::getRsa(RSA::SIGNATURE_PSS);
 
 $return = $rsa->loadKey($rsaPublicKey->getKey());
 if ($return === false) {
 throw new InvalidKeyException('Verification failed due to invalid key');
 }
 
 return $rsa->verify($message, $signature);
 }
 
 /**
 * Decrypt with RSAES-OAEP + MGF1+SHA256
 *
 * @param string $plaintext
 * @param PublicKey $rsaPublicKey
 * @return string
 * @throws InvalidCiphertextException
 */
 protected static function rsaEncrypt($plaintext, PublicKey $rsaPublicKey)
 {
 $rsa = self::getRsa(RSA::ENCRYPTION_OAEP);
 
 $return = $rsa->loadKey($rsaPublicKey->getKey());
 if ($return === false) {
 throw new InvalidKeyException('Ecryption failed due to invalid key');
 }
 
 return $rsa->encrypt($plaintext);
 }
 
 /**
 * Decrypt with RSAES-OAEP + MGF1+SHA256
 *
 * @param string $ciphertext
 * @param PrivateKey $rsaPrivateKey
 * @return string
 * @throws InvalidCiphertextException
 */
 protected static function rsaDecrypt($ciphertext, PrivateKey $rsaPrivateKey)
 {
 $rsa = self::getRsa(RSA::ENCRYPTION_OAEP);
 
 $return = $rsa->loadKey($rsaPrivateKey->getKey());
 if ($return === false) {
 throw new InvalidKeyException('Decryption failed due to invalid key');
 }
 
 $return = @$rsa->decrypt($ciphertext);
 if ($return === false) {
 throw new InvalidCiphertextException('Decryption failed');
 }
 return $return;
 }
 
 /**
 * Use an internally generated key in a Defuse context
 *
 * @param string $randomBytes
 * @return Key
 */
 protected static function defuseKey($randomBytes)
 {
 $kludege = new Kludge();
 return $kludege->defuseKey($randomBytes);
 }
 }
 
 |