<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2019 Lifestyle Webconsulting GmbH
 * @link       https://www.life-style.de
 */

namespace SimpleSAML\Module\lifestyle\Webservice\Cyphering;

use SimpleSAML\Module\lifestyle\Webservice\Api\Configuration\Cyphering;

/**
 * Class Encoder
 *
 * @copyright  2019 Lifestyle Webconsulting GmbH
 * @link       https://www.life-style.de
 * @package SimpleSAML\Module\lifestyle\Webservice\Cyphering
 */
class Encoder
{
    /**
     * @var Cyphering
     */
    private $configuration;

    /**
     * \SimpleSAML\Module\lifestyle\Webservice\Cyphering\Decoder constructor.
     * @param Cyphering $configuration
     */
    public function __construct(Cyphering $configuration)
    {
        $this->configuration = $configuration;
    }

    /**
     * @param string $decryptedString
     * @param string $encryptKey
     * @return string
     */
    public function encode($decryptedString, $encryptKey)
    {
        // Combine static and flexible keys
        $mixedKey = $this->configuration->keyMixer($encryptKey);
        $key = substr(hash('sha256', $mixedKey), 0, 32);

        // Serialize data to encrypt
        $encrypt = serialize($decryptedString);

        return Manager::useOpenSsl() ?
            $this->encryptOpenSsl($encrypt, $key) :
            $this->encryptMcrypt($encrypt, $key);
    }

    /**
     * @param string $encrypt
     * @param string $key
     * @return string
     */
    private function encryptMcrypt($encrypt, $key)
    {
        // Pack the key
        $key = pack('H*', $key);
        $mac = hash_hmac('sha256', $encrypt, substr(bin2hex($key), -32));

        $mcryptCipherName = $this->configuration->getMcryptCipherName();
        $mcryptModeName = $this->configuration->getMcryptModeName();
        $mcryptIV = $this->configuration->getMcryptitializationVector();
        $passcrypt = mcrypt_encrypt(constant($mcryptCipherName), $key, $encrypt . $mac, constant($mcryptModeName), $mcryptIV);
        return base64_encode($passcrypt) . '#|#' . base64_encode($mcryptIV);
    }

    /**
     * Encrypt message with open ssl
     *
     * As we are using GCM the cipher should be one of these
     * - aes-128-gcm
     * - aes-192-gcm
     * - aes-256-gcm
     * - id-aes128-GCM
     * - id-aes192-GCM
     * - id-aes256-GCM
     * Make sure the cypher is supported by your installation!
     *
     * @param string $encrypt
     * @param string $key
     * @return string
     */
    private function encryptOpenSsl($encrypt, $key)
    {
        // Key should be in hex format
        $key = pack('H*', $key);

        $openSslCipherName = $this->configuration->getOpenSslCipherName();
        $openSslIV = $this->configuration->getOpenSslInitializationVector();

        $ciphertext = openssl_encrypt($encrypt, $openSslCipherName, $key, 0, $openSslIV, $openSslTag);
        return implode('#|#', [base64_encode($ciphertext), base64_encode($openSslIV), base64_encode($openSslTag)]);
    }
}
