<?php if (!defined('PmWiki')) exit();
# vim: set ts=4 sw=4 et:
##
##        File: WikiShCrypt.php
##     Version: 2015-06-06
##      SVN ID: $Id: WikiShCrypt.php 106 2008-08-08 15:40:00Z pbowers $
##      Author: Peter Bowers
## Create Date: April 29, 2008
##   Copyright: 2008, Peter Bowers
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License, Version 2, as
## published by the Free Software Foundation.
## http://www.gnu.org/copyleft/gpl.html
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## This module is dependent on (called by) WikiSh.php
## The function WikiShStdErr() from WikiSh.php is used several times here.
## Other functions used from WikiSh.php include ShReadPage() and WikiShWrite().
## The PHP extension MCRYPT must be installed
##

$RecipeInfo['WikiShCrypt']['Version'] = '2015-06-06';

if (!function_exists('mcrypt_generic')) {
   echo "ERROR: WikiShCrypt.php depends on the PHP extension MCRYPT which is not installed.  Exiting.<br>\n";
   exit();
}

define(CRYPTPREFIX, 'ENCRYPTED--');
define(CRYPTSUFFIX, '--ENCRYPTED');
SDV($WikiShEditCrypting, false);
SDV($WikiShVars['CRYPT_ALGORITHM'], MCRYPT_DES);
SDV($WikiShVars['CRYPT_ALGORITHM_DIR'], '');
SDV($WikiShVars['CRYPT_MODE'], MCRYPT_MODE_ECB);
SDV($WikiShVars['CRYPT_MODE_DIR'], '');
SDV($WikiShVars['CRYPT_IV'], '');
# The following can be set to a page or TEXTFILE--file.  
# You must have read/write privs on this file
SDV($WikiShVars['CRYPT_IV_FILE'], 'WikiSh.IV'); 

function WikiShEncrypt($pagename, $opt, $cleartext)
{
    $func = 'WikiShEncrypt()';
    wdbg(4,"$func: Entering: $cleartext");
    if (substr($crypttext, 0, strlen(CRYPTPREFIX)) == CRYPTPREFIX &&
        substr($crypttext, -strlen(CRYPTSUFFIX)) == CRYPTSUFFIX) {
        WikiShStdErr($pagename, $opt, "ERROR: $func: Attempt to encrypt already-encrypted text.  Encryption aborted.");
        return($cleartext);
    }
    if (!($td = WikiShCryptInit($pagename, $opt))) return(false);
    $crypttext = CRYPTPREFIX . base64_encode(mcrypt_generic($td, $cleartext)) . CRYPTSUFFIX;
    WikiShCryptDeinit($td);
    wdbg(2,"$func: returning Encrypted: $crypttext");
    return($crypttext);
}
function WikiShDecrypt($pagename, $opt, $crypttext)
{
    $func = 'WikiShDecrypt()';
    wdbg(4,"$func: Entering: $crypttext");
    if (substr($crypttext, 0, strlen(CRYPTPREFIX)) != CRYPTPREFIX ||
        substr($crypttext, -strlen(CRYPTSUFFIX)) != CRYPTSUFFIX) {
        WikiShStdErr($pagename, $opt, "ERROR: $func: Attempt to decrypt clear text.  Decryption aborted.");
        return($crypttext);
    } else {
        $crypttext = substr(substr($crypttext, 0, -strlen(CRYPTSUFFIX)), strlen(CRYPTPREFIX));
    }
    if (!($td = WikiShCryptInit($pagename, $opt))) return(false);
    wdbg(1,"$func: Init completed");
    $cleartext = mdecrypt_generic($td, base64_decode($crypttext));
    wdbg(1,"$func: decrypt completed");
    $cleartext = rtrim($cleartext, CHR(0));
    wdbg(1,"$func: rtrim completed");
    WikiShCryptDeinit($td);
    wdbg(2,"$func: returning Decrypted: $cleartext");
    return($cleartext);
}

Markup_e('WikiShCrypt', '<{(',
  '/('.CRYPTPREFIX.'.*'.CRYPTSUFFIX.')/',
  "MarkupDecrypt(\$pagename, PSS(\$m[1]))");
function MarkupDecrypt($pagename, $crypttext)
{
    if ($_REQUEST['passwd'] && $_REQUEST['decrypt']) {
        $cleartext = WikiShDecrypt($pagename, array('passwd'=>$_REQUEST['passwd']), $crypttext);
        #echo "ENCRYPTED: >>$crypttext<<<br>\nDECRYPTED: $cleartext<br>\n";
        return($cleartext);
    } else {
        $rtn  = "(:input form method=POST:)";
        $rtn .= '(:input hidden n {*$FullName}:)' . "\n";
        $rtn .= "--ENCRYPTED TEXT--\\\\\nPassword: ";
        $rtn .= '(:input password passwd:)';
        $rtn .= '(:input submit decrypt Decrypt:)';
        $rtn .= "\\\\\n''(If you decrypt with the wrong password you will need to re-load the page before you can try again.)''";
        return($rtn);
    }
}

function WikiShCryptInit($pagename, $opt)
{
    global $WikiShVars;
    $func = 'WikiShCryptInit()';

    # Get password from $opt['passwd'] or ${CRYPT_PASSWD} or else return failure
    if (isset($opt['passwd']))
        $passwd = $opt['passwd'];
    elseif (isset($WikiShVars['CRYPT_PASSWD']))
        $passwd = $WikiShVars['CRYPT_PASSWD'];
    else {
        wdbg(2,"$func: No password.");
        WikiShStdErr($pagename, $opt, "ERROR: No passphrase.  Cryption aborted.");
        return(false);
    }
    wdbg(1,"$func: pass=$passwd");

    # Make sure we have the algorithm and mode and dirs
    if (!isset($WikiShVars['CRYPT_ALGORITHM'])) {
        WikiShStdErr($pagename, $opt, "ERROR: Crypt: \${CRYPT_ALGORITHM} not set.  Crypt operation aborted.");
        return(false);
    }
    if (!isset($WikiShVars['CRYPT_MODE'])) {
        WikiShStdErr($pagename, $opt, "ERROR: Crypt: \${CRYPT_MODE} not set.  Crypt operation aborted.");
        return(false);
    }
    if (!isset($WikiShVars['CRYPT_ALGORITHM_DIR'])) 
        $WikiShVars['CRYPT_ALGORITHM_DIR'] = '';
    if (!isset($WikiShVars['CRYPT_MODE_DIR'])) 
        $WikiShVars['CRYPT_MODE_DIR'] = '';

    if (!isset($WikiShVars['CRYPT_IV']) || !$WikiShVars['CRYPT_IV']) {
        wdbg(2,"$func: Obtaining IV");
        # For some reason ECB gives me an error on iv even tho docs say diff
        if (false && $WikiShVars['CRYPT_MODE'] == MCRYPT_MODE_ECB)
            $WikiShVars['CRYPT_IV'] = '';   // Not needed
        else {
            if (!isset($WikiShVars['CRYPT_IV_FILE']) || !$WikiShVars['CRYPT_IV_FILE']) {
                WikiShStdErr($pagename, $opt, "ERROR: $func: Must set \${CRYPT_IV_FILE}.  Cryption aborted.");
                return(false);
            }
            wdbg(1,"$func: Reading page $WikiShVars[CRYPT_IV_FILE]");
            if (!isawikipage('', $WikiShVars['CRYPT_IV_FILE']) && 
                !isatextfile('', $WikiShVars['CRYPT_IV_FILE']))
                $WikiShVars['CRYPT_IV_FILE'] = WIKIPAGEID . $WikiShVars['CRYPT_IV_FILE'];
            if (isset($opt['decrypt'])) unset($opt['decrypt']); // no recurse
            $page = ShReadPage($pagename, $opt, $WikiShVars['CRYPT_IV_FILE']);
            wdbg(1,"$func: Text read: >>$page[text]<<");
            if (isabadfile($page) || !$page['text']) {
                wdbg(1,"$func: Read was unsuccessful. Creating IV from zip");

                # Create the IV
                $td = mcrypt_module_open($WikiShVars['CRYPT_ALGORITHM'], $WikiShVars['CRYPT_ALGORITHM_DIR'], $WikiShVars['CRYPT_MODE'], $WikiShVars['CRYPT_MODE_DIR']);
                $size = mcrypt_enc_get_iv_size($td);
                # I would like to use MCRYPT_DEV_RAND but it causes problems
                # with different OSes.  As long as I'm properly seeded I think
                # the MCRYPT_RAND is acceptable, particularly since this is
                # a potentially public portion of the key
                srand((double) microtime() * 1000000); //seed for MCRYPT_RAND
                $iv = mcrypt_create_iv($size, MCRYPT_RAND);
                mcrypt_module_close($td);
                
                # Write it to the CRYPT_IV_FILE
                wdbg(1,"$func: Writing IV($iv) to file");
                if (isabadfile($page)) {
                    if (isatextfile('', $WikiShVars['CRYPT_IV_FILE']))
                        $page['type'] = 'text';
                    else
                        $page['type'] = 'wiki';
                }
                if (isset($opt['encrypt'])) unset($opt['encrypt']); //no recurse
                if (!WikiShWrite($pagename, $opt, $WikiShVars['CRYPT_IV_FILE'], $page['type'], $iv, array())) {
                    WikiShStdErr($pagename, $opt, "ERROR: $func: Unable to write IV to page/file $WikiShVars[CRYPT_IV_FILE].  Cryption aborted.");
                    return(false);
                }

                # Now read it back in
                $page = ShReadPage($pagename, $opt, $WikiShVars['CRYPT_IV_FILE']);
                if (isabadfile($page)) {
                    WikiShStdErr($pagename, $opt, "ERROR: $func: Unable to read IV from just-written $WikiShVars[CRYPT_IV_FILE].  Cryption aborted.");
                    return(false);
                }
            }
            $WikiShVars['CRYPT_IV'] = $page['text'];
        }
    }

    # Prep the encryption
    $td = mcrypt_module_open($WikiShVars['CRYPT_ALGORITHM'], $WikiShVars['CRYPT_ALGORITHM_DIR'], $WikiShVars['CRYPT_MODE'], $WikiShVars['CRYPT_MODE_DIR']);
    $size = mcrypt_enc_get_key_size($td);
    if (strlen($passwd) > $size)
        $passwd = substr($passwd, 0, $size);
    mcrypt_generic_init($td, $passwd, $WikiShVars['CRYPT_IV']);
    wdbg(2,"$func: set IV: $WikiShVars[CRYPT_IV]");
    return($td);
}

function WikiShCryptDeinit($td)
{
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
}

$MarkupExpr["encrypt_edittext"] = 'EncryptEditText($pagename, @$argp, @$args)'; 
function EncryptEditText($pagename, $opt, $args)
{
    global $InputValues, $FmtV, $WikiShEditCrypting;
    $WikiShEditCrypting = true;
    $InputValues['text'] = WikiShEncrypt($page, array('passwd'=>$InputValues['e_cryptpass']), stripmagic($InputValues['text']));
    $FmtV['$EditText'] = $InputValues['text'];
    $WikiShEditCrypting = false;
}
$MarkupExpr["decrypt_edittext"] = 'DecryptEditText($pagename, @$argp, @$args)'; 
function DecryptEditText($pagename, $opt, $args)
{
    global $InputValues, $FmtV, $WikiShEditCrypting;
    $WikiShEditCrypting = true;
    $InputValues['text'] = WikiShDecrypt($page, array('passwd'=>$InputValues['e_cryptpass']), $InputValues['text']);
    $FmtV['$EditText'] = $InputValues['text'];
    $WikiShEditCrypting = false;
}