<?php

function getPassphrase() {
    $passphrase = trim(shell_exec("/usr/bin/hostname")).trim(shell_exec("/usr/bin/cat /sys/class/net/*/address"));
    return $passphrase;
}

function ensureKey() {
    if (file_exists("/var/www/usergen/secret/private.key") && file_exists("/var/www/usergen/secret/public.key")) {
        return;
    }
    $passphrase = getPassphrase();
    $config = array(
        "digest_alg" => "sha256",
        "private_key_bits" => 4096,
        "private_key_type" => OPENSSL_KEYTYPE_RSA,
        "encrypt_key" => true,
        "encrypt_key_cipher" => OPENSSL_CIPHER_AES_256_CBC
    );
    $res = openssl_pkey_new($config);
    openssl_pkey_export($res, $privkey, $passphrase);
    $oldMask = umask(0007);
    file_put_contents("/var/www/usergen/secret/private.key", $privkey);
    $pubkey = openssl_pkey_get_details($res);
    umask($oldMask);
    file_put_contents("/var/www/usergen/secret/public.key", $pubkey["key"]);
}

function getPublic() {
    ensureKey();
    $public = file_get_contents("/var/www/usergen/secret/public.key");
    return $public;
}

function getFingerprint() {
    ensureKey();
    $fingerprint = shell_exec("/usr/bin/openssl pkey -pubin -in /var/www/usergen/secret/public.key -outform DER | /usr/bin/openssl dgst -sha256 -c | /usr/bin/sed -e 's/^.* //' | /usr/bin/sed -e 's/://g'");
    return $fingerprint;
}

// Object -> JSON -> Base84 -> Split -> Encrypt -> Combined -> re-base64 -> Sent

function encryptPayload($input){
    $holdingArray = str_split(base64_encode($input), 64);
    $holdingArray = array_map(function($value){
        return encrypt($value);
    }, $holdingArray);
    $holdingArray = implode("%", $holdingArray);
    return $holdingArray;
}

function decryptPayload($input){
    $holdingArray = explode("%", $input);
    $holdingArray = array_map(function($value){
        return decrypt($value);
    }, $holdingArray);
    $holdingArray = implode("", $holdingArray);
    return base64_decode($holdingArray);
}

function encrypt($input){
    // Encrypt with public key
    ensureKey();
    $public = getPublic();
    $public = openssl_get_publickey($public);
    openssl_public_encrypt($input, $encrypted, $public);
    return base64_encode($encrypted);
}

function decrypt($input){
    // Decrypt with private key
    ensureKey();
    openssl_private_decrypt(
        base64_decode($input),
        $decrypted,
        openssl_get_privatekey(
            file_get_contents("/var/www/usergen/secret/private.key"),
            getPassphrase()
        )
    );
    return $decrypted;
}

function buildEncToken($AuthToken, $UserID, $UserIP, $UserAgent ){
    // Token Data:
    //  - HTown AuthToken
    //  - UserID
    //  - UserIP
    //  - UserAgent
    //  - Timestamp
    $TokenData = array(
        "AuthToken" => $AuthToken,
        "UserID" => $UserID,
        "UserIP" => $UserIP,
        "UserAgent" => $UserAgent,
        "Timestamp" => time() // Unix Time in Seconds
    );
    $TokenData = json_encode($TokenData);
    $EncTokenData = encryptPayload($TokenData);
    return $EncTokenData;
}

function verifyEncToken($EncTokenData){
    $TokenData = decryptPayload($EncTokenData);
    $TokenData = json_decode($TokenData);
    if ($TokenData != null && isset($TokenData->AuthToken) && isset($TokenData->UserID) && isset($TokenData->UserIP) && isset($TokenData->UserAgent) && isset($TokenData->Timestamp)) {
        // Valid Token
        if (time() - $TokenData->Timestamp > 900) { // 15-minute max login session
            // Token Expired
            return "Login Expired";
        }
        if ($TokenData->UserIP != $_SERVER["REMOTE_ADDR"]) {
            // IP Mismatch
            return "IP Mismatch";
        }
        if ($TokenData->UserAgent != $_SERVER["HTTP_USER_AGENT"]) {
            // User Agent Mismatch
            return "UserAgent Mismatch";
        }
        $credentialResults = verifyCredentials($TokenData->AuthToken);
        if (gettype($credentialResults) == "string") {
            // Invalid AuthToken
            return "Invalid Mastodon Account";
        }
        return array(
            "AuthToken" => $TokenData->AuthToken,
            "UserID" => $TokenData->UserID,
            "MastodonData" => $credentialResults
        );
    }else{
        // Invalid Token
        return "Invalid Token";
    }
}

?>