Vigenère

Source code of vigenere.class.php

<?php
/**
 * @class  Vigenere
 *
 * Crypt messages via Vigenère
 *
 * @author      Frank Bo"es <info@3960.org>
 * @copyright   CC-BY <http://creativecommons.org/licenses/by/3.0/>
 */
class Vigenere
{
    protected $key = array();
    protected $replacement = array(
        '.' => 'X',
        '!' => 'X',
        '?' => 'Q',
        '@' => 'AT',
        '1' => 'ONE',
        '2' => 'TWO',
        '3' => 'THREE',
        '4' => 'FOUR',
        '5' => 'FIVE',
        '6' => 'SIX',
        '7' => 'SEVEN',
        '8' => 'EIGHT',
        '9' => 'NINE',
        '0' => 'ZERO',
        'Ä' => 'AE',
        'Ö' => 'OE',
        'Ü' => 'UE',
    );
    const ALPHABET_LENGTH = 26;
    
    /**
     * Set key for coding.
     *
     * @param   string  $key    May only consist of latin letters
     */
    public function __construct ($key)
    {
        $key = mb_strtoupper($key, mb_detect_encoding($key));
        $key = str_replace(
            array_keys($this->replacement),
            $this->replacement,
            $key
        );
        $key = preg_replace('#[^A-Za-z]#','',$key);
        $this->key = str_split(strtoupper($key));
    }
    
    /**
     * Show current key. This key may have been altered, but is only
     * needed for internal encoding.
     *
     * @return  string
     */
    public function showKey ()
    {
        return implode($this->key);
    }

    /**
     * Encrypt message via $this->key.
     *
     * @param   string  $message    plain message
     * @return  string  crypted message
     */
    public function encryptMessage ($message)
    {
        $message = mb_strtoupper($message, mb_detect_encoding($message));
        $message = preg_replace('#[!\?\.]$#','',$message);
        $message = str_replace(
            array_keys($this->replacement),
            $this->replacement,
            $message
        );
        $message = preg_replace('#[^A-Za-z]#','',$message);
        $encryptedMessage = $this->workRotor($message);
        $encryptedMessage = implode(" ",str_split($encryptedMessage, 4));
        return $encryptedMessage;
    }

    /**
     * Decrypt message via $this->key.
     *
     * @param   string  $message    encrypted message
     * @return  string  decrypted message
     */
    public function decryptMessage ($encryptedMessage)
    {
        $encryptedMessage = preg_replace('#[^A-Za-z]#','',$encryptedMessage);
        $message = $this->workRotor($encryptedMessage, TRUE);
        $message = mb_strtolower($message, mb_detect_encoding($message));
        return ucwords($message);
    }
    
    /**
     * Move all characters of $in through rotor with $this->keys.
     *
     * @param   string  $in
     * @param   bool    $reverese   Use rotor the other way round.
     *      Optional, defaults to FALSE.
     * @return  string  out
     */
    public function workRotor ($in, $reverse = FALSE)
    {
        $posAsciiA = ord('A');
        $rotor = 0;                      
        $in = str_split($in);
        $out = NULL;
        foreach ($in as $char)
        {
            $rotorValue = ord($this->key[$rotor]) - $posAsciiA;
            $charValue  = ord($char) - $posAsciiA;
            if ($reverse)
            {
                $charValue -= $rotorValue;
            }
            else
            {
                $charValue += $rotorValue;
            }
            while ($charValue >= self::ALPHABET_LENGTH)
            {
                $charValue -= self::ALPHABET_LENGTH;
            }
            while ($charValue < 0)
            {
                $charValue += self::ALPHABET_LENGTH;
            }
            $out .= chr($charValue + $posAsciiA);
            $rotor = $this->moveRotor($rotor);
        }
        return $out;
    }
    
    /**
     * Turn rotor. If rotor is smaller than 0 or bigger than $max, hop
     * to new position of rotor;
     *
     * @param   int $oldPosition    of rotor
     * @param   int $by how to increment rotor. you may use negative values.
     *  Optional, defaults to 1
     * @param   int $max    Max lentgh of rotor. Optional, defaults to
     *  length of $this->keys
     * @return  int new position of rotor
     */
    protected function moveRotor ($oldPosition, $by = 1, $max = NULL)
    {
        if (empty($max))
        {
            $max = count($this->key);
        }
        $newPosition = $oldPosition + $by;
        while ($newPosition >= $max)
        {
            $newPosition -= $max;
        }
        while ($newPosition < 0)
        {
            $newPosition += $max;
        }
        return $newPosition;
    }
}
?>

Source code of vigenere.js

Vigenere = {    
    replacement : {
        '.' : 'X',
        '!' : 'X',
        '?' : 'Q',
        '@' : 'AT',
        '1' : 'ONE',
        '2' : 'TWO',
        '3' : 'THREE',
        '4' : 'FOUR',
        '5' : 'FIVE',
        '6' : 'SIX',
        '7' : 'SEVEN',
        '8' : 'EIGHT',
        '9' : 'NINE',
        '0' : 'ZERO',
        'Ä' : 'AE',
        'Ö' : 'OE',
        'Ü' : 'UE',
    },
    alphabetLength : 26,
    key : '',
    
    init: function (key) {
        key = key.toUpperCase();
        for (var r in this.replacement) {
            key = key.replace(new RegExp(r.replace(/(\?|\.)/, '\\$1'),'g'), this.replacement[r]);
        }
        key = key.replace(/[^A-Za-z]/g,'');
        if (key.length > 0) {
            this.key = key;
        }
        return this.key;
    },

    encryptMessage: function (message) {
        message = message.toUpperCase();
        message = message.replace(/[!\?\.]$/g,'');
        for (var r in this.replacement) {
            message = message.replace(new RegExp(r.replace(/(\?|\.)/, '\\$1'),'g'), this.replacement[r]);
        }
        message = message.replace(/[^A-Za-z]/g,'');
        var encryptedMessage = this.workRotor(message);
        encryptedMessage = encryptedMessage.replace(/([^\s]{4})/g, '$1 ');
        return encryptedMessage;
    },

    decryptMessage: function (encryptedMessage) {
        encryptedMessage = encryptedMessage.toUpperCase();
        encryptedMessage = encryptedMessage.replace(/[^A-Za-z]/g,'');
        message = this.workRotor(encryptedMessage, true);
        message = message.toLowerCase();
        return message;
    },
    
    workRotor: function (input, reverse) {
        if (this.key.length < 1) {
            return '';
        }
        if (!reverse) {
            reverse = false;
        }
        var posAsciiA = 'A'.charCodeAt(0);
        rotor = 0;
        out = '';
        for (var i=0; i < input.length; i++) {
            rotorValue = this.key.charCodeAt(rotor) - posAsciiA;
            charValue  = input.charCodeAt(i) - posAsciiA;
            if (reverse) {
                charValue -= rotorValue;
            }
            else {
                charValue += rotorValue;
            }
            while (charValue >= this.alphabetLength) {
                charValue -= this.alphabetLength;
            }
            while (charValue < 0) {
                charValue += this.alphabetLength;
            }
            out = out + String.fromCharCode(charValue + posAsciiA);
            rotor = this.moveRotor(rotor);
        }
        return out;
    },
    
    moveRotor: function (oldPosition, by, max) {
        if (!by) {
            by = 1;
        }
        if (!max) {
            max = this.key.length;
        }
        newPosition = oldPosition + by;
        while (newPosition >= max) {
            newPosition -= max;
        }
        while (newPosition < 0) {
            newPosition += max;
        }
        return newPosition;
    },

    end : 0
}