<?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\Model\Seal;
use Buckhill\License\Model\EncryptedLicense;
use Buckhill\License\Exceptions\PublicKeyException;
use Buckhill\License\Exceptions\DecryptionException;
use Buckhill\License\Exceptions\InvalidSignatureException;

class Decrypter
{
    protected $publicKey;
    protected $fingerprint;

    /**
     * @param $publicKey
     * @return Decrypter
     */
    public static function createFromString($publicKey)
    {
        $me = new static();

        // Make sure that we don't have Windows style line endings because we don't like how Windows do stuff.
        $pkey =  \Illuminate\Support\Str::replace("\r", "", $publicKey);

        $me->fingerprint = hash('sha256', $pkey);

        return $me->createPublicKey($pkey);
    }

    /**
     * @param $sealed
     * @return EncryptedLicense
     * @throws InvalidSignatureException
     */
    public function unseal($sealed)
    {
        $seal       = Seal::fromString($sealed);

        if(!$seal->verifySignatureWith($this->getPublicKeyFingerprint()))
        {
            throw new InvalidSignatureException("Invalid seal signature.");
        }

        $metadata   = $this->decrypt($seal->getMetadata(true));
        $metadata   = json_decode($metadata, true);

        $payload    = $seal->getPayload();

        return new EncryptedLicense($payload, $metadata);
    }

    /**
     * @param $payload
     * @return mixed
     * @throws DecryptionException
     */
    public function decrypt($payload)
    {
        $result = openssl_public_decrypt($payload, $decrypted, $this->getPublicKey());

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

        return $decrypted;
    }

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

    /**
     * @return string
     */
    public function getPublicKeyFingerprint()
    {
        return $this->fingerprint;
    }

    /**
     * @param $publicKey
     * @return Decrypter
     * @throws PublicKeyException
     */
    protected function createPublicKey($publicKey)
    {
        $key = @openssl_get_publickey($publicKey);

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

        $this->publicKey = $key;

        return $this;
    }

}