# huge php refactoring to OOP (not yet working)

This commit is contained in:
cbonar 2008-06-30 22:54:09 +00:00
parent 317aee4844
commit f80a92aa10
9 changed files with 1004 additions and 811 deletions

View file

@ -1,159 +0,0 @@
<?php
/**
* This is the PHP server side library for Ciform
* @package ciform
*/
// constants exportable to included scripts
define("CIFORM_SESSION","CIFORM");
define("CIFORM_PROTOCOL_VERSION",0);
define("CIFORM_REQUEST_PREFIX","ciform:");
define("CIFORM_SESSION_KEYPAIR","KEYPAIR"); // TODO : move to ciform_rsa.php
if ( ! defined("CIFORM_DEBUG") ) define("CIFORM_DEBUG",FALSE);
require_once("ciform_rsa.php");
// private constants
define("CIFORM_REQUEST_PROTOCOL","protocol");
define("CIFORM_KEYTYPE",CIFORM_RSA_KEYTYPE); // choose the desired encryption module here
if ( ! defined("CIFORM_AUTODECRYPT") ) define("CIFORM_AUTODECRYPT", TRUE );
// TODO : embed the key in the data (e.g. ciform:rsa:keyId:0x12345:0xdd33be2b17813b396d63dd1be9c72e9756bbd8ae5d5555b93a7f4b4fd5a8c80d:salt24325234)
// TODO : use the responsibility chain to rewrite this function
function ciform_decode( $data, $keyPair, $base=1 )
{
if ( CIFORM_DEBUG ) echo "ciform_decrypt($data,keyPair,$base)<br>";
// $newData is going to be decoded by one of the following filters
// then, it'll be encoded to the destination base
$newData = $data;
// this flag means this parameter is handled by this library
if ( eregi('^'.CIFORM_REQUEST_PREFIX.'(.*)$',$data,$matches) > 0 )
{
$newData = ciform_decode($matches[1],$keyPair);
}
// this is just salt that adds randomness to the string : it can be removed safely
else if ( eregi('^salt[^:]*:(.*)$',$data,$matches) > 0 )
{
$newData = ciform_decode($matches[1],$keyPair);
}
// this is an hexadecimal string
else if ( eregi('^(hex:|0x)(.*)$',$data,$matches) > 0 )
{
$tmpData = ciform_decode($matches[2],$keyPair);
$newData = pack("H*",$tmpData);
}
// this a base64 encoded string
else if ( eregi('^(base64|b64):(.*)$',$data,$matches) > 0 )
{
$tmpData = ciform_decode($matches[2],$keyPair);
if ( $base == 64 )
{
// we're already in the right radix, don't go further
// (same can be done with other bases too, but right now we only need this one)
return $tmpData;
}
$newData = base64_decode($tmpData);
}
// this is an encrypted message
else if ( eregi('^'.CIFORM_KEYTYPE.':(.*)(:.+)?$',$data,$matches) > 0 )
{
$tmpData = ciform_decode($matches[1],$keyPair,64);
// decrypts the data using the configured module
$func = "ciform_".CIFORM_KEYTYPE."_decrypt";
$newData = ciform_decode( $func($tmpData,$keyPair), $keyPair );
}
// FIXME : do better recursion : each case should return ciform_decode($tmpData) except if no encoding is detected (which then ends the recursion)
// FIXME : put each case in a different 'decoder' class
// encodes the data into the desired base
switch( $base )
{
case 64:
return base64_encode($newData);
default:
return $newData;
}
}
function ciform_decryptParam( $data, $keyPair )
{
if ( gettype($data) == "string" && eregi('^'.CIFORM_REQUEST_PREFIX.'(.*)$',$data,$matches) > 0 )
{
return ciform_decode($matches[1],$keyPair);
}
return $data;
}
function ciform_decryptParams( $request, $keyPair )
{
$decoded = array();
// accepts encrypted data from the client
foreach ( $request as $key => $value )
{
$newValue = ciform_decryptParam($value,$keyPair);
$decoded[$key] = $newValue;
}
return $decoded;
}
// returns the protocol of this script
if ( isset($_REQUEST[CIFORM_REQUEST_PROTOCOL]) )
{
$func = "ciform_".CIFORM_KEYTYPE."_getProtocol";
$name = $_REQUEST[CIFORM_REQUEST_PROTOCOL];
header("Content-type: text/plain");
// if a name was given, use it to name the variable :
// the result may be used as a full (java)script
if ( trim($name) != "" ) {
echo "var $name = " . $func() . ";";
}
// if no name was given, just print the JSON value :
// the result may be used as an Ajax response
else {
echo $func();
}
exit;
}
// makes sure the key is accessible
// TODO : instanciate the given crypto class,
// which stores itself the key and all other specific data
// then, move the following code to the correct sub-script
if ( !isset($_SESSION[CIFORM_SESSION]) )
{
$_SESSION[CIFORM_SESSION] = array();
}
if ( !isset($_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR]) )
{
// the encryption module's name is given by the defined key type
$func = "ciform_".CIFORM_KEYTYPE."_getKeyPair";
$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR] = $func();
}
if ( CIFORM_AUTODECRYPT )
{
$_REQUEST = ciform_decryptParams($_REQUEST,$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR]);
}
?>

View file

@ -0,0 +1,176 @@
<?php
//
// Ciform
//
// Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
//
/**
* This is the PHP server side library for Ciform
* @package ciform
*/
// TODO : load all files in ciform/schemes
require_once "ciform/schemes/core.php";
require_once "ciform/schemes/rsa.php";
//
// CONSTANTS DEFINITIONS
//
// Comment out the following line to enable debug traces from this script
//define("CIFORM_DEBUG",TRUE);
/** Prefix for the session variables for this library */
define("CIFORM_SESSION","CIFORM");
/**
* Version number of the Ciform protocol implemented here
* @type string
*/
define("CIFORM_PROTOCOL_VERSION","0");
/** If the request contains a parameter with this name, this script will print out the Ciform protocol rather than doing decryption */
define("CIFORM_REQUEST_PROTOCOL","protocol");
if ( ! defined("CIFORM_AUTODECRYPT") )
/** Define this constant to TRUE in caller script to enable transparent decryption of the request */
define("CIFORM_AUTODECRYPT", TRUE );
/**
* List of the schemes taken in account for auto decryption.<br>
*
* Comment out the schemes you don't use in the following list.<br>
* Add each (custom) scheme you want to the list to handle more schemes.<br>
*/
define("CIFORM_SCHEMES_DEFAULT",array(
new Ciform_schemes_Ciform(),
new Ciform_schemes_Base16(),
new Ciform_schemes_Base64(),
new Ciform_schemes_RSA()
);
//
// CLASS 'ciform_Server'
//
/**
* This class handles everything about Ciform on the server side.
*/
class ciform_Server
{
/**
* @var Ciform_CipherChain Holds the decoders to use on the request parameters
* @access private
*/
var $codec;
function ciform_Server( $schemes )
{
// registers all known schemes into a decoder chain
$this->codec = new Ciform_CipherChain($schemes);
}
/**
* Decrypts all recognized parameters from the given request
*
* @param $request An associative array containing the request parameters (use $_REQUEST)
* @return array A copy of the input array, with values for all handled parameters decrypted
*/
function decodeParams( $request )
{
$decoded = array();
// accepts encrypted data from the client
foreach ( $request as $key => $value )
{
$decoded[$key] = $this->codec->decode($value);
}
return $decoded;
}
/**
* @return array the current Ciform protocol
*/
function getProtocol()
{
// FIXME : serverURL must be absolute, so scripts can call it from other servers
$serverURL = $_SERVER['PHP_SELF'];
$schemes = array();
foreach ( $this->codec->ciphers as $cipher )
{
$schemes[$cipher->getName()] = $cipher->getParameters();
}
$protocol = array(
'VERSION' => CIFORM_PROTOCOL_VERSION,
'PACKET_PREFIX' => CIFORM_REQUEST_PREFIX,
'serverURL' => str_replace("'","\\'",$serverURL),
'schemes' => $schemes
);
if ( CIFORM_DEBUG ) echo "ciform_rsa_getProtocol() = ".print_r($protocol,TRUE)."<br>";
return $protocol;
}
}
//
// CASE 'PROTOCOL' : Called with the CIFORM_REQUEST_PROTOCOL parameter,
// this script is used to print the Ciform protocol parameters
//
if ( isset($_REQUEST[CIFORM_REQUEST_PROTOCOL]) )
{
header("Content-type: text/plain");
// instanciates a default Ciform server handler
$ciform = new ciform_Server(CIFORM_SCHEMES_DEFAULT);
// if a name was given, use it to name the variable :
// the result may be used as a full (java)script
if ( trim($name) != "" ) {
echo "var $name = " . json_encode($ciform->getProtocol()) . ";";
}
// if no name was given, just print the JSON value :
// the result may be used as an Ajax response
else {
echo json_encode($ciform->getProtocol());
}
exit;
}
//
// CASE 'AUTODECRYPT' : If set, this script will try to automatically decrypt all parameters of the current $_REQUEST,
// so the decryption phase is transparent to the next server scripts
//
if ( CIFORM_AUTODECRYPT )
{
// instanciates a default Ciform server handler
$ciform = new ciform_Server(CIFORM_SCHEMES_DEFAULT);
// and replaces the request with the unencrypted one
$_REQUEST = $ciform->decryptParams($_REQUEST);
}
?>

View file

@ -0,0 +1,411 @@
<?php
//
// Ciform
//
// Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
//
/**
* Core decoders/encoders classes for ciform messages.<br>
*
* <p>NOTE : In order to keep compatibility with PHP 4, only the basic object oriented features of PHP
* are used in this script. This may lead programmers familiar with OOP to misunderstand the classes tree sometimes.
* In particular, some classes are meant to be interfaces, but since there's only one possible extend per class,
* they were made real classes, and then the tree does not conform exactly to what it would be if multiple
* inheritance was allowed.</p>
*
* @package ciform
* @subpackage ciphers
* @link http://plugnauth.sourceforge.net/ciform
* @author Nicolas BONARDELLE <http://nicobo.net/contact>
*/
require_once "crypto/ciphers/core.php";
// Comment out the following line to enable debug traces from this script
//define("CIFORM_DEBUG",TRUE);
/** The prefix to any Ciform packet */
define("CIFORM_SCHEME_NAME","ciform");
/** The regular expression that extracts the body from any Ciform packet */
define("CIFORM_SCHEME_EXTRACT_BODY",'^'.CIFORM_SCHEME_NAME.':(.*)$');
/**
* This class specifies the interface of a scheme handler in the Ciform protocol.<br>
* @abstract
*/
class Ciform_Scheme extends crypto_Cipher
{
/**
* @access private
*/
var $name;
Ciform_Scheme( $name )
{
if ( defined($name) ) {
$this->name = $name;
}
}
/**
* Used to identify this scheme.
*/
function getName()
{
return $this->name;
}
/**
* Builds a packet with full scheme from a message.
* @param string $packet The message to pack
* @abstract
*/
function pack( $packet ) { throw new Exception("This method is not implemented."); }
/**
* @param string $packet A 'ciphertext', including the full scheme
* @return array|FALSE FALSE if this does not matches this scheme.
* Otherwise, an array containing unpacked informations,
* with at least the body of the given packet indexed on the key <kbd>body</kbd>.
* For instance,<kbd>rsa:0x21372A02302FD83242A723</kbd> would return array("body"=&gt;<kbd>0x21372A02302FD83242A723</kbd>)
* if this class handles <kbd>rsa:</kbd> prefixed packets.
* @abstract
*/
function unpack( $packet ) { throw new Exception("This method is not implemented."); }
}
/**
* This pseudo-cipher handles the recursive aspects of Ciform's scheme by holding a list of ciphers to use for enc/decoding.<br>
*
* <p>As a decoder, it acts like a <b>chain of responsibility</b>, invoking each of its internal decoders
* in turn until one of them can decode the message.</p>
*
* <p>NOTE : The encoder part is not implemented.</p>
*/
class Ciform_CipherChain extends Ciform_Scheme
{
/**
* @var array<crypto_Cipher>
* @access private
*/
var $ciphers;
/**
* @param array $ciphers The list of the internal {@link crypto_Cipher}s in this chain
*/
function Ciform_CipherChain( $ciphers, $name="schemes" )
{
parent::Ciform_Scheme($name);
$this->ciphers = $ciphers;
}
/**
* Does one loop in the chain : tries each cipher in turn (lowest index first) on the given ciphertext.<br>
*
* @return string The first result not FALSE, or FALSE if no cipher matched
* @access private
*/
function chainDecode( $packet )
{
$message = $ciphertext;
foreach ( $this->ciphers as $decoder )
{
// The first decoder accepting the ciphertext is the good one, we don't need to go further.
if ( $message = $decoder->decode($message,$this) )
{
return $message;
}
}
// no decoder has accepted the ciphertext
return FALSE;
}
/**
* @return string The result of the call to the {@link crypto_Cipher#decode} method
* of the first decoder in the chain that accepted the ciphertext,
* or the original message if none was found (never returns FALSE)
*/
function decode( $packet )
{
// this outer loop tells to continue while the message is still encoded and handled by one of the decoders
for ( $message1 = $packet ; $message2 = $this->chainDecode($message1) ; $message1 = $message2 );
return $message1;
}
}
/**
* This class defines a skeleton to write a Ciform enc/decoder.
* @abstract
*/
class Ciform_Codec extends Ciform_Scheme
{
/**
* @var Ciform_Scheme Used as a default cipher chain
* @access private
* @see #decode
*/
var $chain;
/**
* @var boolean
* @access private
*/
var $unpackOnly;
/**
* @param boolean $unpackOnly If TRUE, this codec will not do the final decoding on the packet,
* but will rather return the body as is.
*/
function Ciform_Codec( $name, $unpackOnly=FALSE )
{
parent::Ciform_Scheme($name);
$this->unpackOnly = $unpackOnly;
}
/**
* @return crypto_Cipher|FALSE A cipher able to decode the body of the given packet,
* or FALSE if this scheme does not handle this kind of packet.<br>
* <br>
* Note that the prefered way to check if a codec does handle some packet
* is not to call this method, but rather to compare the result of the {@link unpack} method with FALSE.
* @access protected
* @abstract
*/
function getDecoder( $packet ) { throw new Exception("This method is not implemented."); }
/**
* @todo Describe this method
*/
function encode( $text ) { throw new Exception("This method is not implemented."); }
/**
* Decrypts and unpack in chain a Ciform packet.<br>
*
* <p>There can be several levels of encoding in one ciphertext.<br>
* For instance, a message could first be encoded into hexadecimal form, then through RSA.<br>
* The decryption would then take place in two steps : first, decoding the RSA message will give the hexadecimal form;
* then, transcoding this message to the original charset (e.g. ASCII) will give back the original text.<br>
* An object implementing this class is used for each step in this <b>decoding chain</b>.</p>
*
* @param string $ciphertext The message to decode
* @param Ciform_CipherChain $chain A decoder that may be used to decrypt inner parts of the ciphertext.<br>
* It should be a {@link Ciform_CipherChain}, but all that is required is that it respects the {@link Ciform_Scheme} interface.<br>
* It may be used to decode some part of the message, e.g. when the decoder allows the body of the message itself to be encoded.<br>
* For instance : with <kbd>rsa:hex:0x1232423248</kbd>, a {@link Ciform_ciphers_RSADecoder} will call the chain on <kbd>hex:0x1232423248</kbd>
* in order to get the raw value to decrypt with a RSA private key.
* @return string|FALSE The decoded message or FALSE if this class cannot decode the given ciphertext.
*/
function decode( $packet, $chain=FALSE )
{
if ( CIFORM_DEBUG ) { echo print_r($this,TRUE),"->decode($packet,",print_r($chain,TRUE),")"."\n"; }
// uses the internal chain (if any) if the chain was not given as an argument
$myChain = $chain ? $chain : ($this->chain ? $this->chain : FALSE);
if ( CIFORM_DEBUG ) { echo "myChain=",print_r($myChain,TRUE)."\n"; }
// 1. First extract the body from the packet
if ( ($unpacked = $this->unpack($packet)) && isset($unpacked['body']) )
{
if ( CIFORM_DEBUG ) { echo "unpacked=$unpacked"."\n"; }
$body = $unpacked['body'];
// 2. and resursively decode the body if it is itself a packet
if ( $myChain && $cleartext = $myChain->decode($body) )
{
if ( CIFORM_DEBUG ) { echo "cleartext=$cleartext"."\n"; }
$body = $cleartext;
}
// 3. finally decode the body using the decoder of this cipher
if ( $this->unpackOnly )
{
return $body;
}
else
{
// 3.a get a decoder on the given packet
$decoder = $this->getDecoder($packet);
if ( CIFORM_DEBUG ) { echo "decoder=".print_r($decoder,TRUE)."\n"; }
// 3.b then use the decoder to return a decoded text
return $decoder->decode($body);
}
}
if ( CIFORM_DEBUG ) { echo "unpacked=".print_r($this->unpack($packet),TRUE)."\n"; }
// if no decoder was found, this object does not handle this scheme
return FALSE;
}
}
/**
* A simple scheme packer/unpacker based on a regular expression.<br>
*
* <p>Such a simple scheme unpacker could be used "as is" if the operation only consist to remove a part of the packet (a prefix for instance).<br>
* This is sometimes usefull to unmark a packet or remove unused information from a message.</p>
*
* <p>NOTE : The unpacker part is not implemented.</p>
*/
class Ciform_SimpleScheme extends Ciform_Codec
{
/**
* @var string
* @access private
*/
var $regex;
/**
* @param string $regex The regular expression to use to extract the message body from packets.
* It will be passed as the first argument to {@link preg_match}.
* It must isolate the message into its first capturing group (<kbd>$matches[1]</kbd>).
* @param boolean $unpackOnly See Ciform_Codec#Ciform_Codec()
*/
function Ciform_SimpleScheme( $name, $regex, $unpackOnly=FALSE )
{
parent::Ciform_Codec($name,$unpackOnly);
$this->regex = $regex;
}
/**
* Extracts the body of the message using its registered regular expression
*/
function unpack( $packet )
{
if ( CIFORM_DEBUG ) { echo print_r($this,TRUE)."->unpack($packet)"."\n"; }
if ( preg_match($this->regex,$packet,$matches) )
{
if ( CIFORM_DEBUG ) { echo "matches=".print_r($matches,TRUE)."\n"; }
return array( 'body' => $matches[1] );
}
if ( CIFORM_DEBUG ) { echo "preg_match(".$this->regex.",$packet,$matches)=".preg_match($this->regex,$packet,$matches)."\n"; }
return FALSE;
}
}
/**
* A simple decoder that removes salt from a message.
*
* <p>Salt is used to add randomness to a message : it can be removed safely.</p>
*/
class Ciform_schemes_Salt extends Ciform_SimpleScheme
{
/**
* Only this constructor needs to be defined, in order to pass the superclass
* the right regex to extract the salt from the message.
*/
function Ciform_schemes_Salt()
{
parent::Ciform_SimpleScheme("salt",'/^salt[^:]*:(.*)$/i');
}
}
/**
* This decoder transforms an hexadecimal string into its binary representation.
*/
class Ciform_schemes_Base16 extends Ciform_SimpleScheme
{
/**
* @param boolean $unpackOnly If FALSE, the {@link decode()} method will not decode the ciphertext,
* but just return the hexadecimal encoded value without the <kbd>hex:</kbd> preamble
*/
function Ciform_schemes_Base16( $unpackOnly=FALSE )
{
parent::Ciform_SimpleScheme("base16",'/^(?:hex:|0x)(.*)$/i',$unpackOnly);
}
function getDecoder( $packet )
{
if ( CIFORM_DEBUG ) { echo print_r($this,TRUE)."->getDecoder($packet)"."\n"; }
if ( $this->unpack($packet) )
{
return new crypto_ciphers_Base16();
}
return FALSE;
}
}
/**
* Decodes base64 encoded texts
*/
class Ciform_schemes_Base64 extends Ciform_SimpleScheme
{
/**
* @param boolean $unpackOnly If FALSE, the {@link #decode} method will not decode the ciphertext,
* but just return the base64 encoded value without the <kbd>base64:</kbd> preamble
*/
function Ciform_schemes_Base64( $unpackOnly=FALSE )
{
parent::Ciform_SimpleScheme("base64",'/^(?:base64|b64):(.*)$/',$unpackOnly);
}
function getDecoder( $packet )
{
if ( CIFORM_DEBUG ) { echo print_r($this,TRUE)."->getDecoder($packet)"."\n"; }
if ( $this->unpack($packet) )
{
return new crypto_ciphers_Base64();
}
return FALSE;
}
}
/**
* Simply unpacks a ciform packet
*/
class Ciform_schemes_Ciform extends Ciform_SimpleScheme
{
function Ciform_schemes_Ciform()
{
parent::Ciform_SimpleScheme(CIFORM_SCHEME_NAME,CIFORM_SCHEME_EXTRACT_BODY,TRUE);
}
}
?>

View file

@ -1,422 +0,0 @@
<?php
/**
* Core decoders for ciform messages
*
* @package ciform
* @subpackage ciphers
*/
// constants exportable to included scripts
define("CIFORM_SESSION","CIFORM");
define("CIFORM_PROTOCOL_VERSION",0);
/**
* This prefix just means that the corresponding message can be handled by this library.
* @see {@link Depacketizer}
* @static
* @final
*/
define("CIFORM_REQUEST_PREFIX","ciform:");
define("CIFORM_PACKET_EXTRACT_MESSAGE",'^'.CIFORM_REQUEST_PREFIX.'(.*)$');
define("CIFORM_SESSION_KEYPAIR","KEYPAIR"); // TODO : move to ciform_rsa.php
if ( ! defined("CIFORM_DEBUG") ) define("CIFORM_DEBUG",FALSE);
require_once("ciform_rsa.php");
// private constants
define("CIFORM_REQUEST_PROTOCOL","protocol");
define("CIFORM_KEYTYPE",CIFORM_RSA_KEYTYPE); // choose the desired encryption module here
if ( ! defined("CIFORM_AUTODECRYPT") ) define("CIFORM_AUTODECRYPT", TRUE );
/**
* This class defines the interface of a scheme in the Ciform protocol.<br>
*/
class Scheme
{
/**
* @return the body of the given packet, or FALSE if this does not matches this scheme.
* For instance,<code>rsa:0x21372A02302FD83242A723</code> would return <code>0x21372A02302FD83242A723</code>
* if this class handles <code>rsa:</code> prefixed packets.
*/
function getBody( $packet ) { return FALSE; }
}
/**
* This class defines the interface decoders have to implement in order to decrypt in chain Ciform messages.<br>
*
* <p>There can be several levels of encoding in one ciphertext.<br>
* For instance, a message could first be encoded into hexadecimal form, then through RSA.<br>
* The decryption would then take place in two steps : first, decoding the RSA message will give the hexadecimal form;
* then, transcoding this message to the original charset (e.g. ASCII) will give back the original text.<br>
*
* An object implementing this class is used for each step in this <em>decoding chain</em>.</p>
*
* @abstract
*/
class ChainDecoder extends Scheme
{
// /**
// * @param string $ciphertext The message to decode
// * @return boolean TRUE if this decoder can handle the given message, FALSE if not.
// * @abstract
// */
// function canDecode( $ciphertext ) { return FALSE };
/**
* {@link ::canDecode()} must be called before to call this method.
*
* @param string $ciphertext The message to decode
* @param ChainDecoder $chain The chain decoder to invoke next.<br>
* It must <b>not</b> be used when the decoder cannot handle the ciphertext (in this case, it must return FALSE).<br>
* It may be used to decode the rest of the message, when the decoder allows the body of the message to be encoded itself.<br>
* For instance : with <code>rsa:hex:0x1232423248</code>, a {@link RSADecoder} will call the chain on <code>hex:0x1232423248</code>
* in order to get the raw value to decrypt with a RSA private key.
* @return string|FALSE The decoded message or FALSE if an error happened.
* @abstract
*/
function decode( $ciphertext, $chain ) { return FALSE; }
}
/**
* This decoder acts like a <em>chain of responsibility</em> : it invokes each of its internal decoders
* in turn until one of them can decode the message.<br>
*
* <p>Its role is not to be a single link in the chain, but to make the chain start.
* However, it respects the {@link ChainDecoder} interface.</p>
*/
class ChainDecoderChain extends ChainDecoder
{
/** @access private */
var $decoders;
/**
* @param array $decoders The list of the internal decoders of this chain
*/
function ChainDecoderChain( $decoders )
{
$this->$decoders = $decoders;
}
function canDecode( $ciphertext )
{
foreach ( $this->$decoders as $decoder )
{
if ( $decoder->canDecode($ciphertext) ) {
return TRUE;
}
}
return FALSE;
}
/**
* NOTE : the $chain parameter is not used here
* @return TRUE if at least one decoder in the chain could decode some part of the ciphertext, FALSE else
*/
function decode( $ciphertext, $chain )
{
if ( ! $this->canDecode($ciphertext) ) {
return FALSE;
}
$message = $ciphertext;
// this outer loop tells to continue while the message is still encoded and handled by one of the decoders
while ( $this->canDecode($message) )
{
// this inner loop tells to try each decoder in turn
foreach ( $this->$decoders as $decoder )
{
if ( $decoder->canDecode($message) ) {
$message = $decoder->decode($message,$this);
// The first decoder accepting the ciphertext is the good one, we don't need to go further.
// This break is not necessary since there is no real priority order in the chain,
// but it helps to show the logic of the loop.
break;
}
}
}
return $message;
}
}
/**
* A simple de-packetizer of (for instance) "ciform:" prepended messages.<br>
*
* <p>Such a simple decoder could exist just to unmark a packet or remove unused information from a message.</p>
*/
class Depacketizer extends ChainDecoder
{
/**
* @var array Matches of the last message passed to {@link ::canDecode()}
* @access private
*/
var $matches;
/**
* @var string
* @access private
*/
var $regex;
/**
* @param string $regex The regular expression to use to extract core the message from packets.
* It will be passed as the first argument to {@link eregi}.
* It must isolate the message into its first capturing group (<code>$matches[1]</code>).
* Defaults to {@link CIFORM_PACKET_EXTRACT_MESSAGE}
*/
function Depacketizer( $regex=CIFORM_PACKET_EXTRACT_MESSAGE )
{
$this->$regex = $regex;
}
/** @return TRUE if the the ciphertext matches the regular expression, giving at least one result */
function canDecode( $ciphertext )
{
return eregi($this->$regex,$message,$this->$matches) > 0;
}
/** Extracts the core message using its regular expression, and calls the chain to return a totally decoded message */
function decode( $ciphertext, $chain )
{
if ( defined($this->$matches) )
{
$cleartext = $chain->decode($matches[1]);
delete $this->$matches;
return $cleartext;
}
// canDecode() *must* be called before decode()
return FALSE;
}
}
/**
* A simple decoder that removes salt from a message.
* Salt is used to add randomness to a message : it can be removed safely
*/
class Desalter extends Depacketizer
{
/**
* Only this constructor needs to be defined, in order to pass the superclass
* the right regex to extract the salt from the message.
*/
function Desalter()
{
parent::Depacketizer('^salt[^:]*:(.*)$');
}
}
/**
* This decoder transforms an hexadecimal string into its binary representation
*/
class HexDecoder extends Depacketizer
{
function HexDecoder()
{
parent::Depacketizer('^(?:hex:|0x)(.*)$');
}
function decode( $ciphertext, $chain )
{
// first step : let the base class extract the wanted message
$message = parent::decode($ciphertext,$chain);
if ( $message )
{
// second step : do hexadecimal-to-text conversion
return pack("H*",$message);
}
return FALSE;
}
}
/**
* Decodes base64 encoded texts
*/
class Base64Decoder extends Depacketizer
{
function Base64Decoder()
{
parent::Depacketizer('^(?:base64|b64):(.*)$');
}
function decode( $ciphertext, $chain )
{
// first step : let the base class extract the wanted message
$message = parent::decode($ciphertext,$chain);
if ( $message )
{
// second step : pass the message to the available decoders to get the raw value to work on
$cleartext = $chain->decode($message);
// final step : do hexadecimal-to-text conversion
return pack("H*",$cleartext);
if ( $base == 64 )
{
// we're already in the right radix, don't go further
// (same can be done with other bases too, but right now we only need this one)
return $tmpData;
}
$newData = base64_decode($tmpData);
}
return FALSE;
}
}
// TODO : embed the key in the data (e.g. ciform:rsa:keyId:0x12345:0xdd33be2b17813b396d63dd1be9c72e9756bbd8ae5d5555b93a7f4b4fd5a8c80d:salt24325234)
// TODO : use the responsibility chain to rewrite this function
function ciform_decode( $data, $keyPair, $base=1 )
{
if ( CIFORM_DEBUG ) echo "ciform_decrypt($data,keyPair,$base)<br>";
// $newData is going to be decoded by one of the following filters
// then, it'll be encoded to the destination base
$newData = $data;
// this flag means this parameter is handled by this library
if ( eregi('^'.CIFORM_REQUEST_PREFIX.'(.*)$',$data,$matches) > 0 )
{
$newData = ciform_decode($matches[1],$keyPair);
}
// this is just salt that adds randomness to the string : it can be removed safely
else if ( eregi('^salt[^:]*:(.*)$',$data,$matches) > 0 )
{
$newData = ciform_decode($matches[1],$keyPair);
}
// this is an hexadecimal string
else if ( eregi('^(hex:|0x)(.*)$',$data,$matches) > 0 )
{
$tmpData = ciform_decode($matches[2],$keyPair);
$newData = pack("H*",$tmpData);
}
// this a base64 encoded string
else if ( eregi('^(base64|b64):(.*)$',$data,$matches) > 0 )
{
$tmpData = ciform_decode($matches[2],$keyPair);
if ( $base == 64 )
{
// we're already in the right radix, don't go further
// (same can be done with other bases too, but right now we only need this one)
return $tmpData;
}
$newData = base64_decode($tmpData);
}
// this is an encrypted message
else if ( eregi('^'.CIFORM_KEYTYPE.':(.*)(:.+)?$',$data,$matches) > 0 )
{
$tmpData = ciform_decode($matches[1],$keyPair,64);
// decrypts the data using the configured module
$func = "ciform_".CIFORM_KEYTYPE."_decrypt";
$newData = ciform_decode( $func($tmpData,$keyPair), $keyPair );
}
// FIXME : do better recursion : each case should return ciform_decode($tmpData) except if no encoding is detected (which then ends the recursion)
// FIXME : put each case in a different 'decoder' class
// encodes the data into the desired base
switch( $base )
{
case 64:
return base64_encode($newData);
default:
return $newData;
}
}
function ciform_decryptParam( $data, $keyPair )
{
if ( gettype($data) == "string" && eregi('^'.CIFORM_REQUEST_PREFIX.'(.*)$',$data,$matches) > 0 )
{
return ciform_decode($matches[1],$keyPair);
}
return $data;
}
function ciform_decryptParams( $request, $keyPair )
{
$decoded = array();
// accepts encrypted data from the client
foreach ( $request as $key => $value )
{
$newValue = ciform_decryptParam($value,$keyPair);
$decoded[$key] = $newValue;
}
return $decoded;
}
// returns the protocol of this script
if ( isset($_REQUEST[CIFORM_REQUEST_PROTOCOL]) )
{
$func = "ciform_".CIFORM_KEYTYPE."_getProtocol";
$name = $_REQUEST[CIFORM_REQUEST_PROTOCOL];
header("Content-type: text/plain");
// if a name was given, use it to name the variable :
// the result may be used as a full (java)script
if ( trim($name) != "" ) {
echo "var $name = " . $func() . ";";
}
// if no name was given, just print the JSON value :
// the result may be used as an Ajax response
else {
echo $func();
}
exit;
}
// makes sure the key is accessible
// TODO : instanciate the given crypto class,
// which stores itself the key and all other specific data
// then, move the following code to the correct sub-script
if ( !isset($_SESSION[CIFORM_SESSION]) )
{
$_SESSION[CIFORM_SESSION] = array();
}
if ( !isset($_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR]) )
{
// the encryption module's name is given by the defined key type
$func = "ciform_".CIFORM_KEYTYPE."_getKeyPair";
$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR] = $func();
}
if ( CIFORM_AUTODECRYPT )
{
$_REQUEST = ciform_decryptParams($_REQUEST,$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR]);
}
?>

View file

@ -1,230 +0,0 @@
<?php
require_once("Crypt/RSA.php");
define("CIFORM_RSA_KEYTYPE","rsa");
define("CIFORM_RSA_KEYSIZE",768);
define("CIFORM_RSA_KEYSTORE","keys");
define("CIFORM_RSA_KEYFILE_PEM",CIFORM_RSA_KEYSTORE."/protected/key-rsa.pem");
define("CIFORM_RSA_KEYFILE_JS",CIFORM_RSA_KEYSTORE."/key-rsa.pub.json");
define("CIFORM_RSA_REQUEST_GENKEY","ciform-genkey");
/**
* This class extends Crypt_RSA_KeyPair by adding conversion functions to Javascript and to the Ciform protocol
*/
class Ciform_RSA_KeyPair extends Crypt_RSA_KeyPair
{
/**
* Transforms a big integer value into a JSON array of 28 bits integers
*
* @param string $binValue The raw, binary string value of the big integer
* @param Crypt_RSA_Math_* $math Optional math wrapper to use to manipulate large integers. Will use the current one if not specified.
* @see Crypt_RSA_KeyPair::$_math_obj
* @return string The number as a JSON structure
* @access private
*/
function bigInt2Json( $binValue, $math=$this->_math_obj )
{
$json = "[";
$intValue = $math->bin2int($binValue);
$szBits = $math->bitLen($intValue); // total length, in bits
for ( $b=0 ; $b<$szBits ; )
{
$l = min(28,$szBits-$b);
$json .= $math->subint($intValue, $b, $l);
$b += $l;
if ( $b<$szBits )
{
$json .= ",";
}
}
return $json."]";
}
/**
* Transforms an RSA public key into a JSON structure
*
* @param Crypt_RSA_KeyPair $keyPair Optional RSA key pair holding the public key (use this if called from the class context).
* @return string The public key as a JSON structure
* @access private
*/
function pubKey2Json( $keyPair=$this )
{
$pubKey = $keyPair->getPublicKey();
$math = $keyPair->_math_obj;
$p = Ciform_RSA_KeyPair::bigInt2Json($keyPair->_attrs['p'],$math);
$q = Ciform_RSA_KeyPair::bigInt2Json($keyPair->_attrs['q'],$math);
$e = Ciform_RSA_KeyPair::bigInt2Json($pubKey->getExponent(),$math);
$pq = Ciform_RSA_KeyPair::bigInt2Json($pubKey->getModulus(),$math);
//$mpi = base64_encode($math->bin2int($pubKey->getModulus())+$math->bin2int($pubKey->getExponent()));
$json = "{"
."'type':'".CIFORM_RSA_KEYTYPE."',"
."'size':".$pubKey->getKeyLength()."," // size of the key, in bits
."'p':$p," // prime factor p, as an array of 28 bits integers
."'q':$q," // prime factor q, as an array of 28 bits integers
."'e':$e," // public exponent as an array of 28 bits integers
."'pq':$pq}"; // modulus, as an array of 28 bits integers
//."'mpi':'$mpi'" // e + modulus, encoded into a base64 MPI string
return $json;
}
/**
* Clones an existing key pair by copying its intrisinc fields into this one,
*
* @param Crypt_RSA_KeyPair $keyPair The key pair to copy
* @return $this
* @todo This implementation depends totally on the version of the superclass :
* if a field is added or removed, this method can very possibly fail to do its job
* @private
*/
function copy( $keyPair )
{
$this->$_math_obj = $keyPair->$_math_obj;
$this->$_key_len = $keyPair->$_key_len;
$this->$_public_key = $keyPair->$_public_key;
$this->$_private_key = $keyPair->$_private_key;
$this->$_random_generator = $keyPair->$_random_generator;
$this->$_attrs = $keyPair->$_attrs;
return $this;
}
/**
* This method is overriden to instanciate an object from the same class as the current object
* @see Crypt_RSA_KeyPair::fromPEMString
* @todo This should be done in the superclass : it should dynamically return a new instance of the current class
*/
function &fromPEMString($str, $wrapper_name = 'default', $error_handler = '')
{
$keyPair1 = parent::fromPEMString($str,$wrapper_name,$error_handler);
$keyPair2 = new Crypt_RSA_KeyPair();
$keyPair2->copy($keyPair1);
return $keyPair2;
}
/**
* Uses an existing key pair stored in a file or generates a new one.
*
* If the PEM file exist, the key pair will be read from it. If it doesn't, the key pair will be generated.<br>
* Upon generation of a new key pair, both a PEM and a Javascript file will be created if they don't exist already.
*
* @param int $keySize Size of the key to generate, in bits
* @param string $pemFilename Name of the file where a PEM formated keypair may be found / stored
* @param string $jsFilename Name of the file where to store the key upon generation as a Javascript script
* @param boolean $force Forces generation of both the PEM and the Javascript file. If false, the files will not be overwritten if they exist already.
* @access private
* @static
*/
function genKeyPair( $keySize, $pemFilename, $jsFilename, $force=FALSE )
{
// if the key has been stored to a file, get it from there
if ( $contents = @file_get_contents($pemFilename) )
{
return Ciform_RSA_KeyPair::fromPEMString($contents);
}
// else, generate a new key and try to store it to a file
else
{
// generates the key
$keyPair = new Ciform_RSA_KeyPair($keySize);
// stores as PEM
if ( $force || !@file_exists($pemFilename) )
{
@mkdir(dirname($pemFilename),0777,TRUE);
@file_put_contents($pemFilename,$keyPair->toPEMString());
}
// store some Javascript variables, including the public key
// FIXME : if file_put_contents fails, no notification but the file is never written
if ( $force || !@file_exists($jsFilename) )
{
@mkdir(dirname($jsFilename),0777,TRUE);
// FIXME : serverURL must be absolute, so scripts can call it from other servers
//$serverURL = $_SERVER['PHP_SELF'];
$pubKey = $keyPair->pubKey2Json();
//$jsContents = "\nvar CIFORM = {'serverURL':'".str_replace("'","\\'",$serverURL)."', 'pubKey':$pubKey};";
@file_put_contents($jsFilename,$pubKey);
}
// returns the newly created key
return $keyPair;
}
}
}
/**
* Ciform handler using the RSA cipher
*/
class Ciform_RSA
{
/**
* @return Ciform_RSA_KeyPair the current key pair (or a new one if none defined)
*/
function getKeyPair()
{
if ( CIFORM_DEBUG ) echo "ciform_rsa_getKeyPair() = ";
$keyPair = Ciform_RSA_KeyPair::genKeyPair(CIFORM_RSA_KEYSIZE, CIFORM_RSA_KEYFILE_PEM, CIFORM_RSA_KEYFILE_JS);
if ( CIFORM_DEBUG ) print_r($keyPair);
return $keyPair;
}
/**
* @return string the current Ciform protocol
*/
function getProtocol()
{
if ( CIFORM_DEBUG ) echo "ciform_rsa_getProtocol() = ";
// FIXME : serverURL must be absolute, so scripts can call it from other servers
$serverURL = $_SERVER['PHP_SELF'];
$keyPair = Ciform_RSA::getKeyPair();
$protocol = "{
'VERSION':".CIFORM_PROTOCOL_VERSION.",
'PACKET_PREFIX':'".CIFORM_REQUEST_PREFIX."',
'serverURL':'".str_replace("'","\\'",$serverURL)."',
'pubKey':".$keyPair->pubKey2Json()
."}";
if ( CIFORM_DEBUG ) print_r($protocol);
return $protocol;
}
/**
*
*/
function decrypt( $data, $keyPair )
{
if ( CIFORM_DEBUG ) echo "ciform_rsa_decrypt($data,keyPair)<br>";
$privateKey = $keyPair->getPrivateKey();
// TODO make the math object configurable, because there are big differences between them and to offer better compatibility
$rsa = new Crypt_RSA($privateKey->getKeyLength(),'BCMath');
return $rsa->decrypt($data,$privateKey);
}
}
// keypair generation is forced if this parameter is set
if ( isset($_SESSION[CIFORM_RSA_REQUEST_GENKEY]) )
{
$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR] = Ciform_RSA_KeyPair::genKeyPair(CIFORM_RSA_KEYSIZE, CIFORM_RSA_KEYFILE_PEM, CIFORM_RSA_KEYFILE_JS, TRUE);
}
?>

View file

@ -0,0 +1,49 @@
<?php
//
// Ciform
//
// Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
//
/**
* Core ciphers for ciform messages.<br>
*
* COPYRIGHT NOTICE :<pre>
*
* Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
*
*</pre>
*
* <p>Most of the ciphers here are only partially implemented, since this server-side library doesn't use encryption yet.</p>
*
* @package ciform
* @subpackage ciphers
* @link http://plugnauth.sourceforge.net/ciform
* @author Nicolas BONARDELLE <http://nicobo.net/contact>
*/
/**
* This class specifies the interface of a cipher : both the encoding and the decoding parts.<br>
*
* @abstract
*/
class crypto_Cipher
{
/**
* @param string $cleartext The message to encode
* @return string The encoded message (= ciphertext)
* @abstract
*/
function encode( $cleartext ) { throw new Exception("This method is not implemented."); }
/**
* @param string $ciphertext The encoded message to decode
* @return string|FALSE The decoded message or FALSE if this object cannot decode the given ciphertext
* @abstract
*/
function decode( $ciphertext ) { throw new Exception("This method is not implemented."); }
}
?>

View file

@ -0,0 +1,60 @@
<?php
//
// Ciform
//
// Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
//
/**
* A few common ciphers.<br>
*
* COPYRIGHT NOTICE :<pre>
*
* Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
*
*</pre>
*
* <p>Most of the ciphers here are only partially implemented, since this server-side library doesn't use encryption yet.</p>
*
* @package crypto
* @subpackage ciphers
* @link http://plugnauth.sourceforge.net/ciform
* @author Nicolas BONARDELLE <http://nicobo.net/contact>
*/
require_once "crypto/Cipher.class.php";
// Comment out the following line to enable debug traces from this script
//define("CRYPTO_DEBUG",TRUE);
/**
* This cipher transforms an text into its hexadecimal representation
*/
class crypto_ciphers_Base16 extends crypto_Cipher
{
function decode( $base16msg )
{
if ( CRYPTO_DEBUG) { echo print_r($this,TRUE)."->decode($base16msg)"."\n"; }
return pack("H*",$base16msg);
}
}
/**
* Encodes/decodes text into/from a base64 string
*/
class crypto_ciphers_Base64 extends crypto_Cipher
{
function decode( $base64msg )
{
if ( CRYPTO_DEBUG) { echo print_r($this,TRUE)."->decode($base64msg)"."\n"; }
return base64_decode($base64msg);
}
}
?>

View file

@ -0,0 +1,308 @@
<?php
//
// Ciform
//
// Copyright © 2008 Nicolas BONARDELLE <http://nicobo.net/contact>
//
require_once "crypto/ciphers/core.php";
require_once("Crypt/RSA.php");
define("CIFORM_RSA_KEYTYPE","rsa");
define("CIFORM_RSA_KEYSIZE",768);
define("CIFORM_RSA_KEYSTORE","keys");
define("CIFORM_RSA_KEYFILE_PEM",CIFORM_RSA_KEYSTORE."/protected/key-rsa.pem");
define("CIFORM_RSA_KEYFILE_JS",CIFORM_RSA_KEYSTORE."/key-rsa.pub.json");
define("CIFORM_RSA_REQUEST_GENKEY","ciform-genkey");
define("CIFORM_SESSION_KEYPAIR","KEYPAIR"); // TODO : move to ciform_rsa.php
/** */
define("CIFORM_KEYTYPE",CIFORM_RSA_KEYTYPE); // choose the desired encryption module here
// TODO : embed the key in the data (e.g. ciform:rsa:keyId:0x12345:0xdd33be2b17813b396d63dd1be9c72e9756bbd8ae5d5555b93a7f4b4fd5a8c80d:salt24325234)
//
// RSA SPECIAL HANDLING : Since this is the default encryption,
// the script will retrieve the keys or generated them if not found.
//
// TODO : instanciate the given crypto class,
// which stores itself the key and all other specific data
// then, move the following code to the correct sub-script
if ( !isset($_SESSION[CIFORM_SESSION]) )
{
$_SESSION[CIFORM_SESSION] = array();
}
if ( !isset($_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR]) )
{
// the encryption module's name is given by the defined key type
$func = "ciform_".CIFORM_KEYTYPE."_getKeyPair";
$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR] = $func();
}
/**
* This class extends Crypt_RSA_KeyPair by adding conversion functions to Javascript and to the Ciform protocol
*/
class Ciform_ciphers_rsa_KeyPair extends Crypt_RSA_KeyPair
{
/**
* Transforms a big integer value into a JSON array of 28 bits integers
*
* @param string $binValue The raw, binary string value of the big integer
* @param Crypt_RSA_Math_* $math Optional math wrapper to use to manipulate large integers. Will use the current one if not specified.
* @see Crypt_RSA_KeyPair::$_math_obj
* @return string The number as a JSON structure
* @access private
*/
function bigInt2Json( $binValue, $math=$this->_math_obj )
{
$json = "[";
$intValue = $math->bin2int($binValue);
$szBits = $math->bitLen($intValue); // total length, in bits
for ( $b=0 ; $b<$szBits ; )
{
$l = min(28,$szBits-$b);
$json .= $math->subint($intValue, $b, $l);
$b += $l;
if ( $b<$szBits )
{
$json .= ",";
}
}
return $json."]";
}
/**
* Exports a RSA public key into an associative array, ready for JSON transformation
*
* @param Crypt_RSA_KeyPair $keyPair Optional RSA key pair holding the public key (use this if called from the class context).
* @return array The public key as an associative array
* @access private
*/
function exportPubKey( $keyPair=$this )
{
$pubKey = $keyPair->getPublicKey();
$math = $keyPair->_math_obj;
$p = Ciform_RSA_KeyPair::bigInt2Json($keyPair->_attrs['p'],$math);
$q = Ciform_RSA_KeyPair::bigInt2Json($keyPair->_attrs['q'],$math);
$e = Ciform_RSA_KeyPair::bigInt2Json($pubKey->getExponent(),$math);
$pq = Ciform_RSA_KeyPair::bigInt2Json($pubKey->getModulus(),$math);
//$mpi = base64_encode($math->bin2int($pubKey->getModulus())+$math->bin2int($pubKey->getExponent()));
$export = array(
'type' => CIFORM_RSA_KEYTYPE,
'size' => $pubKey->getKeyLength(), // size of the key, in bits
'p' => $p, // prime factor p, as an array of 28 bits integers
'q' => $q, // prime factor q, as an array of 28 bits integers
'e' => $e, // public exponent as an array of 28 bits integers
'pq' => $pq; // modulus, as an array of 28 bits integers
//'mpi' => $mpi // e + modulus, encoded into a base64 MPI string
);
return $export;
}
/**
* Clones an existing key pair by copying its intrisinc fields into this one,
*
* @param Crypt_RSA_KeyPair $keyPair The key pair to copy
* @return $this
* @todo This implementation depends totally on the version of the superclass :
* if a field is added or removed, this method can very possibly fail to do its job
* @private
*/
function copy( $keyPair )
{
$this->$_math_obj = $keyPair->$_math_obj;
$this->$_key_len = $keyPair->$_key_len;
$this->$_public_key = $keyPair->$_public_key;
$this->$_private_key = $keyPair->$_private_key;
$this->$_random_generator = $keyPair->$_random_generator;
$this->$_attrs = $keyPair->$_attrs;
return $this;
}
/**
* This method is overriden to instanciate an object from the same class as the current object
* @see Crypt_RSA_KeyPair::fromPEMString
* @todo This should be done in the superclass : it should dynamically return a new instance of the current class
*/
function &fromPEMString($str, $wrapper_name = 'default', $error_handler = '')
{
$keyPair1 = parent::fromPEMString($str,$wrapper_name,$error_handler);
$keyPair2 = new Crypt_RSA_KeyPair();
$keyPair2->copy($keyPair1);
return $keyPair2;
}
/**
* Uses an existing key pair stored in a file or generates a new one.
*
* If the PEM file exist, the key pair will be read from it. If it doesn't, the key pair will be generated.<br>
* Upon generation of a new key pair, both a PEM and a Javascript file will be created if they don't exist already.
*
* @param int $keySize Size of the key to generate, in bits
* @param string $pemFilename Name of the file where a PEM formated keypair may be found / stored
* @param string $jsFilename Name of the file where to store the key upon generation as a Javascript script
* @param boolean $force Forces generation of both the PEM and the Javascript file. If false, the files will not be overwritten if they exist already.
* @access private
* @static
*/
function genKeyPair( $keySize, $pemFilename, $jsFilename, $force=FALSE )
{
// if the key has been stored to a file, get it from there
if ( $contents = @file_get_contents($pemFilename) )
{
return Ciform_RSA_KeyPair::fromPEMString($contents);
}
// else, generate a new key and try to store it to a file
else
{
// generates the key
$keyPair = new Ciform_RSA_KeyPair($keySize);
// stores as PEM
if ( $force || !@file_exists($pemFilename) )
{
@mkdir(dirname($pemFilename),0777,TRUE);
@file_put_contents($pemFilename,$keyPair->toPEMString());
}
// store some Javascript variables, including the public key
// FIXME : if file_put_contents fails, no notification but the file is never written
if ( $force || !@file_exists($jsFilename) )
{
@mkdir(dirname($jsFilename),0777,TRUE);
// FIXME : serverURL must be absolute, so scripts can call it from other servers
//$serverURL = $_SERVER['PHP_SELF'];
$pubKey = $keyPair->pubKey2Json();
//$jsContents = "\nvar CIFORM = {'serverURL':'".str_replace("'","\\'",$serverURL)."', 'pubKey':$pubKey};";
@file_put_contents($jsFilename,$pubKey);
}
// returns the newly created key
return $keyPair;
}
}
}
/**
* Ciform handler using the RSA cipher
*/
class Ciform_RSA extends Ciform_
{
/**
* @return Ciform_RSA_KeyPair the current key pair (or a new one if none defined)
*/
function getKeyPair()
{
if ( CIFORM_DEBUG ) echo "ciform_rsa_getKeyPair() = ";
$keyPair = Ciform_RSA_KeyPair::genKeyPair(CIFORM_RSA_KEYSIZE, CIFORM_RSA_KEYFILE_PEM, CIFORM_RSA_KEYFILE_JS);
if ( CIFORM_DEBUG ) print_r($keyPair);
return $keyPair;
}
/**
* @return string the current Ciform protocol
*/
function getProtocol()
{
'pubKey' => $keyPair->exportPubKey(),
if ( CIFORM_DEBUG ) echo "ciform_rsa_getProtocol() = ";
// FIXME : serverURL must be absolute, so scripts can call it from other servers
$serverURL = $_SERVER['PHP_SELF'];
$keyPair = Ciform_RSA::getKeyPair();
$protocol = "{
'VERSION':".CIFORM_PROTOCOL_VERSION.",
'PACKET_PREFIX':'".CIFORM_REQUEST_PREFIX."',
'serverURL':'".str_replace("'","\\'",$serverURL)."',
'pubKey':".$keyPair->pubKey2Json()
."}";
if ( CIFORM_DEBUG ) print_r($protocol);
return $protocol;
}
/**
*
*/
function decrypt( $data, $keyPair )
{
if ( CIFORM_DEBUG ) echo "ciform_rsa_decrypt($data,keyPair)<br>";
$privateKey = $keyPair->getPrivateKey();
// TODO make the math object configurable, because there are big differences between them and to offer better compatibility
$rsa = new Crypt_RSA($privateKey->getKeyLength(),'BCMath');
return $rsa->decrypt($data,$privateKey);
}
}
class Ciform_schemes_RSA extends Ciform_SimpleScheme
{
function Ciform_schemes_RSA()
{
parent::Ciform_SimpleScheme("rsa",'/^rsa:(.*)(:.+)?$/i');
}
function export()
{
return array( 'pubKey' => $keyPair->exportPubKey() );
}
function getDecoder( $packet )
{
if ( CIFORM_DEBUG ) { echo print_r($this,TRUE)."->getDecoder($packet)"."\n"; }
if ( $this->unpack($packet) )
{
return new crypto_ciphers_Base64();
}
return FALSE;
}
}
// keypair generation is forced if this parameter is set
if ( isset($_SESSION[CIFORM_RSA_REQUEST_GENKEY]) )
{
$_SESSION[CIFORM_SESSION][CIFORM_SESSION_KEYPAIR] = Ciform_RSA_KeyPair::genKeyPair(CIFORM_RSA_KEYSIZE, CIFORM_RSA_KEYFILE_PEM, CIFORM_RSA_KEYFILE_JS, TRUE);
}
?>