<?php

/*
 * This file is part of the AuthStack project.
 *
 * (c) Buckhill Ltd <development@buckhill.co.uk>
 *
 * This source file is subject to the Buckhill license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Buckhill\License\Encryption;

use Buckhill\License\Exceptions\EncryptionException;
use Buckhill\License\Exceptions\PrivateKeyException;
use Buckhill\License\Model\EncryptedLicense;
use Buckhill\License\Model\Seal;

class Encrypter
{
    protected $privateKey;

    protected $publicKey;

    /**
     * @param $private
     * @param string $passphrase
     * @return Encrypter
     */
    public static function createFromString($private, $passphrase = '')
    {
        $me = new static();

        return $me->createPrivateKey($private, $passphrase);
    }

    /**
     * @param EncryptedLicense $license
     * @return string
     */
    public function seal(EncryptedLicense $license)
    {
        // Retrieve encryption metadata (key, alg, iv) as JSON
        $metadata       = $license->getEncryptionMetadata(true);

        // Retrieve encrypted license string (it's encrypted with the above metadata)
        $payload        = $license->getEncryptedPayload();

        // Encrypt metadata and return it base64-encoded (2nd parameter encodes it)
        $enc_metadata   = $this->encrypt($metadata, true);

        // Concatenate encrypted metadata and payload, calculate MAC using PublicKey digest (sha256 of public key pem value)
        $signature      = hash_hmac('sha256', sprintf("%s.%s", $enc_metadata, $payload), $this->getPublicKeyFingerprint(), true);
        $signature      = base64_encode($signature);

        $seal           = new Seal($enc_metadata, $payload, $signature);

        return $seal;
    }

    /**
     * @param $payload
     * @param bool $encode
     * @return string
     * @throws EncryptionException
     */
    public function encrypt($payload, $encode = false)
    {
        $result = openssl_private_encrypt($payload, $encrypted, $this->privateKey);

        if(false === $result)
        {
            throw new EncryptionException(openssl_error_string());
        }

        if($encode)
        {
            return base64_encode($encrypted);
        }

        return $encrypted;
    }

    /**
     * @return resource
     */
    public function getPrivateKey()
    {
        return $this->privateKey;
    }

    /**
     * @return string
     */
    public function getPublicKeyFingerprint()
    {
        return hash('sha256', $this->getPublicKey());
    }

    /**
     * @return string
     */
    public function getPublicKey()
    {
        if(is_null($this->publicKey))
        {
            $details = openssl_pkey_get_details($this->getPrivateKey());

            $this->publicKey = $details['key'];
        }

        return $this->publicKey;
    }


    /**
     * @param $string
     * @param string $passphrase
     * @return Encrypter
     * @throws PrivateKeyException
     */
    protected function createPrivateKey($string, $passphrase = '')
    {
        $key = @openssl_pkey_get_private($string, $passphrase);

        if(false === $key)
        {
            throw new PrivateKeyException(openssl_error_string());
        }

        $this->privateKey = $key;

        return $this;
    }


}