<?php

namespace AuthStack\EncryptedLicense;

use AuthStack\EncryptedLicense\Exception\EncryptedLicenseException;

class Encrypter
{
    /**
     * @param License $license
     * @param string $private_key
     * @param string|null $passphrase
     * @return string
     * @throws EncryptedLicenseException
     */
    public static function encrypt(License $license, string $private_key, string $passphrase = null): string
    {
        $private = openssl_pkey_get_private($private_key, $passphrase);

        if(false === $private)
        {
            throw new EncryptedLicenseException('Invalid private key specified');
        }

        $payload = json_encode($license);
        $method = 'aes-128-cbc';
        $key = str_random(32);
        $iv = str_random(16);

        $encrypted = openssl_encrypt($payload, $method, $key, OPENSSL_RAW_DATA, $iv);

        // Encrypt the symmetric key
        openssl_private_encrypt($key, $encrypted_key, $private_key);

        // Sign the data
        openssl_sign($encrypted, $signature, $private, OPENSSL_ALGO_SHA256);

        $result = [
            'payload'   => base64_encode($encrypted),
            'iv'        => $iv,
            'key'       => base64_encode($encrypted_key),
            'signature' => base64_encode($signature)
        ];

        return base64_encode(json_encode($result));
    }

    /**
     * @param $payload
     * @param string $public_key
     * @return License
     * @throws EncryptedLicenseException
     */
    public static function decrypt($payload, string $public_key): License
    {
        $public = openssl_get_publickey($public_key);

        $data = @json_decode(base64_decode($payload), true);

        $method = 'aes-128-cbc';

        if(!json_last_error() === JSON_ERROR_NONE)
        {
            throw new EncryptedLicenseException('Invalid license payload');
        }

        // Validate signature
        $valid = openssl_verify(base64_decode($data['payload']), base64_decode($data['signature']), $public, OPENSSL_ALGO_SHA256);

        if(1 !== $valid)
        {
            // Error
            if(-1 === $valid)
            {
                throw new EncryptedLicenseException(openssl_error_string());
            }

            // Invalid signature
            if(0 === $valid)
            {
                throw new EncryptedLicenseException('Invalid signature, license was tampered with. ' . openssl_error_string());
            }
        }

        // Signature is valid, decrypt the key
        openssl_public_decrypt(base64_decode($data['key']), $key, $public);

        // Decrypt the payload, this contains JSON-encoded array with license data
        $decrypted = openssl_decrypt(base64_decode($data['payload']), $method, $key, OPENSSL_RAW_DATA, $data['iv']);

        // Decode
        $decoded = @json_decode($decrypted, true);

        if(!json_last_error() === JSON_ERROR_NONE)
        {
            throw new EncryptedLicenseException('Invalid JSON specified');
        }

        return new License($decoded);
    }
}