<?php
/**
 * Class WDevs\CustomerOrderExport\Clients\DataEncryption
 *
 * Based on Google\Site_Kit\Core\Storage\Data_Encryption
 * @package   Google\Site_Kit
 * @copyright 2021 Google LLC
 * @license   https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link      https://sitekit.withgoogle.com
 * 
 * Adapted for WDevs Customer Order Export plugin
 * @package   WDevs\CustomerOrderExport
 * @since     1.0.0
 */

namespace WDevs\CustomerOrderExport\Clients;

/**
 * Class responsible for encrypting and decrypting data.
 *
 * @since 1.0.0
 */
final class DataEncryption {
	/**
	 * Key to use for encryption.
	 *
	 * @since 1.0.0
	 * @var string
	 */
	private $key;

	/**
	 * Salt to use for encryption.
	 *
	 * @since 1.0.0
	 * @var string
	 */
	private $salt;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->key  = $this->get_default_key();
		$this->salt = $this->get_default_salt();
	}

	/**
	 * Encrypts a value.
	 *
	 * If a user-based key is set, that key is used. Otherwise the default key is used.
	 *
	 * @since 1.0.0
	 *
	 * @param string $value Value to encrypt.
	 * @return string|bool Encrypted value, or false on failure.
	 */
	public function encrypt( $value ) {
		if ( ! extension_loaded( 'openssl' ) || empty( $this->key ) || empty( $this->salt ) ) {
			return false;
		}

		$method = 'aes-256-gcm';
		$ivlen  = openssl_cipher_iv_length( $method );
		try {
			$iv = random_bytes( $ivlen );
		} catch ( \Throwable $e ) {
			return false;
		}
		$tag    = '';
		$key    = hash( 'sha256', $this->key, true );

		$raw_value = openssl_encrypt( $value, $method, $key, OPENSSL_RAW_DATA, $iv, $tag, $this->salt );
		if ( false === $raw_value || empty( $tag ) ) {
			return false;
		}

		return base64_encode( $iv . $tag . $raw_value );
	}

	/**
	 * Decrypts a value.
	 *
	 * If a user-based key is set, that key is used. Otherwise the default key is used.
	 *
	 * @since 1.0.0
	 *
	 * @param string $raw_value Value to decrypt.
	 * @return string|bool Decrypted value, or false on failure.
	 */
	public function decrypt( $raw_value ) {
		if ( ! extension_loaded( 'openssl' ) || empty( $this->key ) || empty( $this->salt ) || ! is_string( $raw_value ) ) {
			return false;
		}

		$decoded_value = base64_decode( $raw_value, true );
		if ( false === $decoded_value ) {
			return false;
		}

		$method = 'aes-256-gcm';
		$ivlen  = openssl_cipher_iv_length( $method );
		$taglen = 16;
		if ( strlen( $decoded_value ) < $ivlen + $taglen ) {
			return false;
		}

		$iv         = substr( $decoded_value, 0, $ivlen );
		$tag        = substr( $decoded_value, $ivlen, $taglen );
		$ciphertext = substr( $decoded_value, $ivlen + $taglen );
		$key        = hash( 'sha256', $this->key, true );

		return openssl_decrypt( $ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv, $tag, $this->salt );
	}

	/**
	 * Gets the default encryption key to use.
	 *
	 * @since 1.0.0
	 *
	 * @return string Default (not user-based) encryption key.
	 */
	private function get_default_key() {
		if ( defined( 'WDEVS_COE_ENCRYPTION_KEY' ) && '' !== WDEVS_COE_ENCRYPTION_KEY ) {
			return WDEVS_COE_ENCRYPTION_KEY;
		}

		if ( defined( 'LOGGED_IN_KEY' ) && '' !== LOGGED_IN_KEY ) {
			return LOGGED_IN_KEY;
		}

		return '';
	}

	/**
	 * Gets the default encryption salt to use.
	 *
	 * @since 1.0.0
	 *
	 * @return string Encryption salt.
	 */
	private function get_default_salt() {
		if ( defined( 'WDEVS_COE_ENCRYPTION_SALT' ) && '' !== WDEVS_COE_ENCRYPTION_SALT ) {
			return WDEVS_COE_ENCRYPTION_SALT;
		}

		if ( defined( 'LOGGED_IN_SALT' ) && '' !== LOGGED_IN_SALT ) {
			return LOGGED_IN_SALT;
		}

		return '';
	}

}
