<?php
/**
 * Created by PhpStorm.
 * User: eater
 * Date: 4/4/16
 * Time: 9:25 PM
 */

namespace Eater\Glim\Service;


use Eater\Glim\Core;

class CA extends Main
{
    public function getPrivateKey()
    {
        /** @var Core $core */
        $core = $this->get('core');
        
        return file_get_contents($core->getBaseDir() . '/storage/ca/ca.key');
    }

    /**
     * @return string
     */
    public function getOpenSslError()
    {
        $error = "";
        while ($msg = openssl_error_string()) {
            $error .= $msg . "\n";
        }

        return $error;
    }

    /**
     * Signs a client certificate and returns the signed certificate
     * @param string $csr
     * @return string
     * @throws \Exception
     */
    public function signClientCsr($csr)
    {
        /** @var Core $core */
        $core = $this->get('core');

        $csrPath = tempnam(sys_get_temp_dir(), '0.');
        $crtPath = tempnam(sys_get_temp_dir(), '0.');

        file_put_contents($csrPath, $csr);

        exec(escapeshellcmd($core->getBaseDir() . '/bin/sign-client-csr') . ' ' . escapeshellarg($csrPath) . ' ' .  escapeshellarg($crtPath) . ' 2>&1', $output, $exitCode);

        if ($exitCode !== 0) {
            throw new \Exception("Failed signing CSR: " . implode("\n", $output));
        }

        $crt = file_get_contents($crtPath);

        unlink($crtPath);
        unlink($csrPath);

        return $crt;
    }

    /**
     * Signs a client certificate and returns the signed certificate
     * @param string $csr
     * @return string
     * @throws \Exception
     */
    public function signServerKey($csr)
    {
        /** @var Core $core */
        $core = $this->get('core');

        $csrPath = tempnam(sys_get_temp_dir(), '0.');
        $crtPath = tempnam(sys_get_temp_dir(), '0.');

        file_put_contents($csrPath, $csr);

        exec(escapeshellcmd($core->getBaseDir() . '/bin/sign-server-csr') . ' ' . escapeshellarg($csrPath) . ' ' .  escapeshellarg($crtPath) . ' 2>&1', $output, $exitCode);

        if ($exitCode !== 0) {
            throw new \Exception("Failed signing CSR: " . implode("\n", $output));
        }

        $crt = file_get_contents($crtPath);

        unlink($crtPath);
        unlink($csrPath);

        return $crt;
    }

    /**
     * @param string $csr
     * @throws \Exception
     */
    public function getCommonNameFromCsr($csr)
    {
        $subject = openssl_csr_get_subject($csr);

        if ($subject === false) {
            throw new \Exception("Failed to read CSR: " . $this->getOpenSslError());
        }

        return $subject["CN"];
    }

    /**
     * @param string $certificate
     * @throws \Exception
     */
    public function revoke($certificate)
    {
        /** @var Core $core */
        $core = $this->get('core');

        $crtPath = tempnam(sys_get_temp_dir(), '0.');

        file_put_contents($crtPath, $certificate);

        exec(escapeshellcmd($core->getBaseDir() . '/bin/revoke-cert') . ' ' . escapeshellarg($crtPath) . ' 2>&1', $output, $exitCode);

        if ($exitCode !== 0) {
            throw new \Exception("Failed signing CSR: " . implode("\n", $output));
        }

        unlink($crtPath);
    }

    /**
     * @param string $pubKey
     * @return string
     * @throws \Exception
     */
    public function getFingerprintFromPublicKey($pubKey)
    {
        /** @var Core $core */
        $core = $this->get('core');

        $pubPath = tempnam(sys_get_temp_dir(), '0.');

        file_put_contents($pubPath, $pubKey);

        exec(escapeshellcmd($core->getBaseDir() . '/bin/get-fingerprint') . ' ' . escapeshellarg($pubPath) . ' 2>&1', $output, $exitCode);

        if ($exitCode !== 0) {
            throw new \Exception("Failed getting fingerprint from public key: " . implode("\n", $output));
        }

        unlink($pubPath);

        return $output[0];
    }

    public function verify($data, $signature, $publicKey)
    {
        $publicKeyResource = openssl_pkey_get_public($publicKey);

        $result = openssl_verify($data, $signature, $publicKeyResource);

        if ($result === 1) {
            return true;
        } else if ($result === 0) {
            return false;
        }

        throw new \Exception("Verify failed: " . $this->getOpenSslError());
    }

    public function signWithCA($data) {
        $privateKeyPlain = $this->getPrivateKey();
        $privateKey = openssl_get_privatekey($privateKeyPlain);

        $result = openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);

        if ($result) {
            return $signature;
        }

        throw new \Exception("Signing failed: " . $this->getOpenSslError());
    }
}