<?php
/**
 * Created by PhpStorm.
 * User: damien
 * Date: 2019-03-04
 * Time: 15:50
 */

namespace Actigraph\ActipageBaseBundle\Services;


class DataEncryptionService
{
    private $encryptPhpFunc,$encryptConfig,$moduleIdsEnabled;
    public function __construct($params)
    {
        if(isset($params["dataEncryption"]) && isset($params["dataEncryption"]["encryptFunction"]) && isset($params["dataEncryption"]["enabled"])){
            $this->encryptPhpFunc = "sodium";
            if($params["dataEncryption"]["enabled"] == false)
                $this->encryptPhpFunc = "none";
            else{
                if(isset($params["dataEncryption"]["enabled"]))
                    $this->encryptConfig = $params["dataEncryption"]["encryptConfig"];
                if(isset($params["dataEncryption"]["modules"]) && is_array($params["dataEncryption"]["modules"])){
                    $this->moduleIdsEnabled = $params["dataEncryption"]["modules"];
                }
            }
        }else{
            $this->encryptPhpFunc = "none";
        }
    }

    /**
     * @param string $data
     * @param int $moduleId
     * @return string
     * @throws \Exception
     */
    public function encyptIfNeeded($data, $moduleId){
        if(!$this->isModuleDataEncryptNeeded($moduleId))
            return $data;


        if($this->encryptPhpFunc =="sodium"){
            return $this->sodiumEncrypt($data,$this->encryptConfig["key"]);
        }
        throw new \Exception('Methode encryption('.$this->encryptPhpFunc.') non géré');
    }

    /**
     * @param string $data
     * @param int $moduleId
     * @return string
     * @throws \Exception
     */
    public function decryptIfNeeded($data, $moduleId){
        if(!$this->isModuleDataEncryptNeeded($moduleId))
            return $data;

        if($this->encryptPhpFunc =="sodium"){
            return $this->sodiumDecrypt($data,$this->encryptConfig["key"]);
        }
        throw new \Exception('Methode encryption('.$this->encryptPhpFunc.') non géré');
    }

    public function isModuleDataEncryptNeeded($moduleId){
        if($this->encryptPhpFunc == "none")
            return false;
        if(in_array($moduleId,$this->moduleIdsEnabled))
            return true;
        return false;
    }

    /**
     * @param $dataToEncrypt
     * @param $key
     * @return string
     * @throws \Exception
     */
    private function sodiumEncrypt($dataToEncrypt, $key){
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $ciphertext = sodium_crypto_secretbox($dataToEncrypt, $nonce, base64_decode($key));
        return base64_encode($nonce.$ciphertext);
    }

    /**
     * @param $encryptedData
     * @return bool|string
     * @throws \Exception
     */
    private function sodiumDecrypt($encryptedData,$key){
        $decoded = base64_decode($encryptedData);
        if ($decoded === false) {
            throw new \Exception('Decodage base64 impossible');
        }
        if (mb_strlen($decoded, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
            throw new \Exception('Data incomplet');
        }
        $nonceDecoded = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
        $ciphertextDecoded = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
        $plain = sodium_crypto_secretbox_open(
            $ciphertextDecoded,
            $nonceDecoded,
            base64_decode($key)
        );
        if ($plain === false) {
            throw new \Exception('Decodage impossible, alteration ou mauvaise clés');
        }
        return $plain;
    }
}