mirror of
https://github.com/nicolabs/ciform.git
synced 2026-05-21 12:13:25 +02:00
+ a batch of fixes
This commit is contained in:
parent
aea146ce22
commit
b36e5c5012
|
|
@ -4,37 +4,74 @@
|
|||
This library provides specifications and basic functions to add encryption functionalities to an HTML form,
|
||||
therefore called "Ciform".
|
||||
|
||||
@requires base64.js {@link www.haneWIN.de}
|
||||
@requires hex.js {@link www.haneWIN.de}
|
||||
@requires sha1.js {@link www.haneWIN.de}
|
||||
@requires rsa.js {@link www.haneWIN.de}
|
||||
@requires base64.js (http://www.hanewin.net/encrypt/rsa/base64.js)
|
||||
@requires hex.js (http://www.hanewin.net/encrypt/rsa/hex.js)
|
||||
@requires sha1.js (http://pajhome.org.uk/crypt/md5/sha1.js)
|
||||
@requires rsa.js (http://www.hanewin.net/encrypt/rsa/rsa.js)
|
||||
@author cbonar at users dot sf dot net
|
||||
*/
|
||||
// Data from the server is available in a literal object named 'CIFORM'
|
||||
// e.g. { 'serverURL':"login.php", 'pubKey':{'type':'rsa', 'e':10000,'m':24} }
|
||||
|
||||
// TODO : define the Ciform protocol using constants that can change from one server to another
|
||||
// This protocol would be retrieved from the server (either with a <script src> or using Ajax) so nothing is hard coded
|
||||
|
||||
// defines a namespace for this project
|
||||
ciform = {}; // end of ns:ciform
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// DEFAULT ENCODER
|
||||
// NAMESPACE : ciform.*
|
||||
//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Implementations of an encoder must re-define all the defined method.
|
||||
Defines a namespace for this project.
|
||||
*/
|
||||
ciform = {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This namespace contains the constants required for the client to communicate with the server.<br>
|
||||
|
||||
<p>Data from the server is served as a literal object indexed with some of those constants.<br>
|
||||
e.g. <code>{ 'serverURL':"login.php", 'pubKey':{'type':'rsa', 'e':10000,'pq':24} }</code>.</p>
|
||||
|
||||
<p>The normal behavior is to retrieve the protocol from the server and to compare it to the one of this library,
|
||||
in order to know if they're compatible.</p>
|
||||
|
||||
<p>FIXME : jsdoc doesn't print this class correctly : look at the source code (sorry)</p>
|
||||
*/
|
||||
ciform.protocol = {
|
||||
|
||||
/** Version of the protocol (should be used when the protocol changes) */
|
||||
VERSION: 0,
|
||||
|
||||
/**
|
||||
Prefix to use for a ciform 'packet' to decode :
|
||||
HTTP request parameters beginning with this String will be considered as 'Ciform-encoded' values.
|
||||
*/
|
||||
PACKET_PREFIX: "ciform:"
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// NAMESPACE : ciform.encoders.*
|
||||
//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This namespace contains the encoders used by Ciform.<br>
|
||||
|
||||
<p>All encoders must respect the interface defined as {@link ciform.encoders.Encoder}.</p>
|
||||
*/
|
||||
ciform.encoders = {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@class An encoder provides a way to encode/encrypt a message.
|
||||
Implementations of an encoder must re-define all of the methods of this class.
|
||||
@constructor
|
||||
*/
|
||||
ciform.Encoder = function()
|
||||
{
|
||||
}
|
||||
ciform.encoders.Encoder = function(){};
|
||||
|
||||
|
||||
|
||||
|
|
@ -43,11 +80,12 @@ ciform.Encoder = function()
|
|||
|
||||
@param {String} message The text to encrypt
|
||||
@return the encrypted message (ciphertext) : the result depends on the encoder
|
||||
@type String
|
||||
*/
|
||||
ciform.Encoder.prototype.encode = function( message )
|
||||
ciform.encoders.Encoder.prototype.encode = function( message )
|
||||
{
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -59,25 +97,25 @@ ciform.Encoder.prototype.encode = function( message )
|
|||
|
||||
/**
|
||||
@param {Object} options Default values :
|
||||
- 'preamble' = false (don't add meta-data in the beginning of the ciphertext)
|
||||
<ul><li>'preamble' = false (don't add meta-data in the beginning of the ciphertext)</li></ul>
|
||||
@constructor
|
||||
*/
|
||||
ciform.SHA1Encoder = function( options )
|
||||
ciform.encoders.SHA1Encoder = function( options )
|
||||
{
|
||||
this.options = options ? options : {'preamble':false};
|
||||
}
|
||||
ciform.SHA1Encoder.prototype = new ciform.Encoder();
|
||||
this.extend(options);
|
||||
};
|
||||
ciform.encoders.SHA1Encoder.prototype = new ciform.encoders.Encoder();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@see ciform#Encoder#encode
|
||||
@return the sha-1 of the message, base64 encoded, with meta-data about the encoding if this.options['preamble'] is true
|
||||
@see ciform.encoders.Encoder#encode
|
||||
@return the sha-1 of the message, base64 encoded, with meta-data about the encoding if this.options.preamble is true
|
||||
*/
|
||||
ciform.SHA1Encoder.prototype.encode = function( message )
|
||||
ciform.encoders.SHA1Encoder.prototype.encode = function( message )
|
||||
{
|
||||
console.debug(this,"encode(",message,")");
|
||||
return (this.options['preamble'] ? "sha1:b64:" : "") + b64_sha1(message);
|
||||
return (this['preamble'] ? "sha1:b64:" : "") + b64_sha1(message);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -89,26 +127,64 @@ ciform.SHA1Encoder.prototype.encode = function( message )
|
|||
|
||||
|
||||
/**
|
||||
@param {Object} pubKey The public key to use for encryption : a dictionary with the following fields :
|
||||
- 'type' : must be 'rsa'
|
||||
- 'pq' : modulo of the RSA key
|
||||
- 'e' : exponent of the RSA key
|
||||
- 'mpi' (optional) : the RSA key as a base64 encoded mutli-precision integer
|
||||
@class
|
||||
<p>Formal description of a public key.</p>
|
||||
|
||||
<p>FIXME : Sorry, jsdoc doesn't print this class correctly : look at the source code</p>
|
||||
*/
|
||||
ciform.encoders.PublicKey = {
|
||||
/** Type of the key */
|
||||
'type': String
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@class
|
||||
<p>Formal description of a public RSA key.</p>
|
||||
|
||||
<p>FIXME : Sorry, jsdoc doesn't print this class correctly : look at the source code</p>
|
||||
*/
|
||||
ciform.encoders.RSAPublicKey = {
|
||||
/** Must be "rsa" */
|
||||
'type': "rsa",
|
||||
/** (not used) Size of the key, in bits */
|
||||
'size': Number,
|
||||
/** Public exponent as an array of 28 bits integers */
|
||||
'e': Array(Number),
|
||||
/** (not used) Prime factor p, as an array of 28 bits integers */
|
||||
'p':Array(Number),
|
||||
/** (not used) Prime factor q, as an array of 28 bits integers */
|
||||
'q':Array(Number),
|
||||
/** Modulus, as an array of 28 bits integers */
|
||||
'pq': Array(Number),
|
||||
/** (not used) e + modulus, encoded into a base64 <b>M</b>ulti-<b>P</b>recision <b>I</b>nteger string */
|
||||
'mpi': Array(Number)
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@param {ciform.encoders.RSAPublicKey} pubKey The public key to use for encryption
|
||||
@param {Object} options Default values :
|
||||
- 'preamble' = false (don't add meta-data in the beginning of the ciphertext)
|
||||
- 'salt' = false (don't add salt in the beginning of the ciphertext : WARNING : without salt, the same message encoded with the same key will always give the same ciphertext)
|
||||
- 'checkPadding' = true (check that the padding scheme is correct : does not apply if salt is added)
|
||||
<ul>
|
||||
<li>'preamble' = false (don't add meta-data in the beginning of the ciphertext)
|
||||
<li>'salt' = false (don't add salt in the beginning of the ciphertext : WARNING : without salt, the same message encoded with the same key will always give the same ciphertext)
|
||||
<li>'checkPadding' = true (check that the padding scheme is correct : does not apply if salt is added)
|
||||
</ul>
|
||||
@throw TypeError if the public key is not correct
|
||||
@constructor
|
||||
*/
|
||||
ciform.RSAEncoder = function( pubKey, options )
|
||||
ciform.encoders.RSAEncoder = function( pubKey, options )
|
||||
{
|
||||
/** @private */
|
||||
this.options = options ? options : {'preamble':false,'salt':false,'nopadding':false};
|
||||
|
||||
if ( pubKey['type'] == "rsa" )
|
||||
{
|
||||
if ( pubKey['pq'] && pubKey['e'] )
|
||||
{
|
||||
/** @private */
|
||||
this.pubKey = pubKey;
|
||||
}
|
||||
else
|
||||
|
|
@ -121,16 +197,20 @@ ciform.RSAEncoder = function( pubKey, options )
|
|||
throw new TypeError("Type of public key must be 'rsa'");
|
||||
}
|
||||
};
|
||||
ciform.RSAEncoder.prototype = new ciform.Encoder();
|
||||
ciform.encoders.RSAEncoder.prototype = new ciform.encoders.Encoder();
|
||||
|
||||
|
||||
|
||||
ciform.RSAEncoder.prototype.SALT_MAX = 9999;
|
||||
ciform.RSAEncoder.prototype.SALT_MIN = 1;
|
||||
/** @final @type Integer */
|
||||
ciform.encoders.RSAEncoder.prototype.SALT_MAX = 9999;
|
||||
/** @final @type Integer */
|
||||
ciform.encoders.RSAEncoder.prototype.SALT_MIN = 1;
|
||||
|
||||
|
||||
|
||||
ciform.RSAEncoder.prototype._getMPI = function()
|
||||
/**
|
||||
@private
|
||||
@type Array[Number]
|
||||
*/
|
||||
ciform.encoders.RSAEncoder.prototype._getMPI = function()
|
||||
{
|
||||
// this function can be called several times so we don't compute the following each time
|
||||
if ( ! this.pubKey['mpi'] )
|
||||
|
|
@ -139,17 +219,19 @@ ciform.RSAEncoder.prototype._getMPI = function()
|
|||
}
|
||||
|
||||
return this.pubKey['mpi'];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@private
|
||||
@return a random number between this.SALT_MIN and this.SALT_MAX
|
||||
@type Number
|
||||
*/
|
||||
ciform.RSAEncoder.prototype._getSalt = function()
|
||||
ciform.encoders.RSAEncoder.prototype._getSalt = function()
|
||||
{
|
||||
return Math.floor(Math.random() * (this.SALT_MAX - this.SALT_MIN + 1) + this.SALT_MIN);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -160,9 +242,10 @@ ciform.RSAEncoder.prototype._getSalt = function()
|
|||
@return the max. length for a message to be encoded.
|
||||
In case salt is added to the ciphertext, the real max. length might be longer,
|
||||
because the salt has a variable length
|
||||
@see ciform.RSAEncoder#_getSalt
|
||||
@type Number
|
||||
@see ciform.encoders.RSAEncoder#_getSalt
|
||||
*/
|
||||
ciform.RSAEncoder.prototype.maxLength = function()
|
||||
ciform.encoders.RSAEncoder.prototype.maxLength = function()
|
||||
{
|
||||
var s = r2s(this._getMPI());
|
||||
var l = Math.floor((s.charCodeAt(0)*256 + s.charCodeAt(1)+7)/8);
|
||||
|
|
@ -176,16 +259,16 @@ ciform.RSAEncoder.prototype.maxLength = function()
|
|||
|
||||
console.debug(this,".maxLength()=",lmax);
|
||||
return lmax;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@see ciform.Encoder#encode
|
||||
@see ciform.encoders.Encoder#encode
|
||||
@return the ciphertext of the message, encoded with the public RSA key of this encoder, with meta-data about the encoding if this.options['preamble'] is true
|
||||
@throws RangeError if the message is too long to be secure for the current public key (ignored if either 'salt' or 'nopadding' is true)
|
||||
*/
|
||||
ciform.RSAEncoder.prototype.encode = function( message )
|
||||
ciform.encoders.RSAEncoder.prototype.encode = function( message )
|
||||
{
|
||||
console.debug(this,"encode(",message,")");
|
||||
|
||||
|
|
@ -235,19 +318,20 @@ ciform.RSAEncoder.prototype.encode = function( message )
|
|||
|
||||
|
||||
/**
|
||||
This encoder simply combine encoders in a chain.
|
||||
For instance, the message will be first hashed through SHA-1, and then encrypted with a RSA key.
|
||||
@param {Array[ciform.Encoder]} encoders A list with the instances of encoders to use (The chain starts with index 0)
|
||||
@class This encoder simply combine encoders into a chain.
|
||||
For instance, the message will be first hashed through SHA-1, and then encrypted with a RSA key.
|
||||
@param {Array[ciform.encoders.Encoder]} encoders A list with the instances of encoders to use (The chain starts with index 0)
|
||||
*/
|
||||
ciform.ChainEncoder = function( encoders )
|
||||
ciform.encoders.ChainEncoder = function( encoders )
|
||||
{
|
||||
/** @private */
|
||||
this.encoders = encoders;
|
||||
};
|
||||
ciform.ChainEncoder.prototype = new ciform.Encoder();
|
||||
ciform.encoders.ChainEncoder.prototype = new ciform.encoders.Encoder();
|
||||
|
||||
|
||||
|
||||
ciform.ChainEncoder.prototype.encode = function( message )
|
||||
ciform.encoders.ChainEncoder.prototype.encode = function( message )
|
||||
{
|
||||
var ciphertext = message;
|
||||
|
||||
|
|
@ -257,95 +341,211 @@ ciform.ChainEncoder.prototype.encode = function( message )
|
|||
}
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CIFORM HANDLER
|
||||
// CIFORM ENCODER
|
||||
//
|
||||
|
||||
|
||||
|
||||
ciform.Ciform = function( form, pubKey )
|
||||
{
|
||||
this.form = form;
|
||||
this.pubKey = pubKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This function should be called when the 'onsubmit' event happens.
|
||||
Its job is to encrypt 'input' fiels into 'output' fields.
|
||||
|
||||
@param {Object} fields An array with the inputs and outputs, like [ {'inputName1':'outputName1'}, {'inputName2':'outputName2'}, ... ].
|
||||
It can be either the DOM nodes, their id or name (in the latter, the second parameter must be set).
|
||||
Input and output can be the same : the input value will be replaced with the ciphertext.
|
||||
@param {HTMLFormElement} form The object containing the fields (optional)
|
||||
This encoder makes sure the ciphertext will be a ciform packet.
|
||||
@see ciform.protocol
|
||||
*/
|
||||
ciform.Ciform.prototype.encryptFields = function( fields, onError )
|
||||
ciform.encoders.CiformEncoder = function() {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Adds {@link ciform.protocol#PACKET_PREFIX} to the message, if necessary.
|
||||
*/
|
||||
ciform.encoders.CiformEncoder.prototype.encode = function( message )
|
||||
{
|
||||
console.debug("encryptFields(",fields,onError,")");
|
||||
// only if it doesn't start with the prefix already
|
||||
return new RegExp("^"+ciform.protocol.PACKET_PREFIX+"(.*)").test(message) ? message : ciform.protocol.PACKET_PREFIX + message;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CIFORM CLASS
|
||||
//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@class
|
||||
This class handles encryption of a form's fields (and parameters of an HTTP request)
|
||||
@constructor
|
||||
@param {Object} arg1 : (optional) either a fixed target to encrypt or an option object.
|
||||
@param {Object} arg2 (and next arguments) : (optional) options :<ul>
|
||||
<li>'pubKey' : the {@link ciform.encoders.RSAPublicKey} to use for encryption
|
||||
<li>'form' : (optional) a form (or an container object) to work on : the fields to encrypt will be retrieved from it
|
||||
<li>'encoder' : (optional) the encoder to use (defaults to ciform.encoders.RSAEncoder)
|
||||
<li>'onerror' : (optional) a function that will act as an error handler : ciform errors will be passed to it
|
||||
<lI>'showWait' : (optional) whether or not to show a message when the enryption is going on (because it can take time)
|
||||
</ul>
|
||||
Several arguments like this one can be provided (the latter overrides the sooner).
|
||||
@throws Error if at least one of the required options is missing
|
||||
*/
|
||||
ciform.Ciform = function( arg1, arg2 )
|
||||
{
|
||||
var firstArg = 0;
|
||||
|
||||
if ( arg1 )
|
||||
{
|
||||
targetForm = this._getForm($(arg1));
|
||||
targetField = this._getField(arg1,'in',arg1['form']);
|
||||
targetFields = this._getFields(arg1);
|
||||
if ( targetForm ) {
|
||||
this['form'] = targetForm;
|
||||
firstArg = 1;
|
||||
} else if ( targetFields ) {
|
||||
this['fields'] = targetFields;
|
||||
firstArg = 1;
|
||||
} else if ( targetField ) {
|
||||
this['fields'] = [targetField];
|
||||
firstArg = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for ( var a=firstArg ; a<arguments.length ; a++ ) {
|
||||
this.extend(arguments[a]);
|
||||
}
|
||||
|
||||
if ( this['pubKey'] == null ) {
|
||||
throw new Error("The public key is required !");
|
||||
}
|
||||
|
||||
if ( this['encoder'] == null ) {
|
||||
this['encoder'] = new ciform.encoders.ChainEncoder( [new ciform.encoders.RSAEncoder(this['pubKey'],{'preamble':true,'salt':true}), new ciform.encoders.CiformEncoder()] );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This function is called just before the encryption of an object starts.<br>
|
||||
|
||||
<p>This default implementation just prints a message in the window's status bar.
|
||||
It should be overridden to match specific needs.</p>
|
||||
|
||||
@param target The object that's going to be encrypted
|
||||
@param options (optional) The parameters of the current context, if any
|
||||
*/
|
||||
ciform.Ciform.prototype.onEncryptionStart = function( target, options )
|
||||
{
|
||||
if ( this['showWait'] ) {
|
||||
window.status = "Starting to encode " + target;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This function is called once the encryption of an object has ended.<br>
|
||||
|
||||
<p>This default implementation just prints a message in the window's status bar.
|
||||
It should be overridden to match specific needs.</p>
|
||||
|
||||
@param target The object that has just been be encrypted
|
||||
@param options (optional) The parameters of the current context, if any
|
||||
*/
|
||||
ciform.Ciform.prototype.onEncryptionEnd = function( target, options )
|
||||
{
|
||||
if ( this['showWait'] ) {
|
||||
window.status = "Finished encoding " + target;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Encrypts a text using the current configuration.
|
||||
|
||||
@param {String} text The message to encode as plain text
|
||||
@param {Object} options (optional) :<ul>
|
||||
<li>'encoder': {ciform.encoder.Encoder} An optional encoder (use the current encoder if not set)
|
||||
</ul>
|
||||
*/
|
||||
ciform.Ciform.prototype.encryptText = function( text, options )
|
||||
{
|
||||
var localOptions = merge(this,options);
|
||||
var e = localOptions['encoder'];
|
||||
// makes sure the result is 'ciform-encoded' by adding a CiformEncoder to the end
|
||||
if ( !(e instanceof ciform.encoders.ChainEncoder) || !(e.encoders[e.encoders.length-1] instanceof ciform.encoders.CiformEncoder) ) {
|
||||
e = new ciform.encoders.ChainEncoder([encoder,new ciform.encoders.CiformEncoder()]);
|
||||
}
|
||||
return e.encode(text); // may throw an exception
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Encrypts HTML fields.<br>
|
||||
|
||||
<p>If there is an error during the encryption, none of the field is changed.</p>
|
||||
|
||||
@param {Object} fields An array with the fields to encrypt.
|
||||
Each entry can be either the element itself or a literal object with the following keys :<ul>
|
||||
<li>'in' : the name / id of the input field
|
||||
<li>'out' (optional) : the name / id of the output field. If not set, the output field is the input field itself.
|
||||
<li>'sha1' : this field will be encrypted through SHA-1 before this object encrypts it
|
||||
<li>'ciform-sha1' : same as 'sha1' but with informative meta-data (also called 'preamble')
|
||||
</ul>
|
||||
As an alternative,'sha1' and 'ciform-sha1' options can be set in the 'class' attribute of output fields.
|
||||
@param {Object} options (optional) Global options (to be applied to each field encryption)
|
||||
@return false if there was an error, true if not
|
||||
@type Boolean
|
||||
*/
|
||||
ciform.Ciform.prototype.encryptFields = function( fields, options )
|
||||
{
|
||||
console.debug("encryptFields(",fields,options,")");
|
||||
|
||||
var done = [];
|
||||
|
||||
for ( var f=0 ; f< fields.length ; f++ )
|
||||
{
|
||||
/*ciform.encryptFields([
|
||||
{'in':"f1",'out':"f2",'encoding':["hex","rsa","base64","sha1"]},
|
||||
{'in':"f3"},
|
||||
"f4"
|
||||
]);
|
||||
}*/
|
||||
// gets the nodes from either id, names or nodes
|
||||
var localOptions = merge(fields[f],options,this);
|
||||
var form = merge(this,options)['form'];
|
||||
// gets the nodes from either id, names or nodes directly
|
||||
var kIn = fields[f]['in'] ? fields[f]['in'] : fields[f];
|
||||
var kOut = fields[f]['out'] ? fields[f]['out'] : kIn;
|
||||
var nodIn = this.form ? this.form[kIn] : document.getElementById(kIn) ? document.getElementById(kIn) : kIn;
|
||||
var nodOut = this.form ? this.form[kOut] : document.getElementById(kOut) ? document.getElementById(kOut) : kOut;
|
||||
|
||||
//var message = nodIn.value;
|
||||
|
||||
var encoders = [];
|
||||
// takes care of one-level sha-1 encoding for such fields
|
||||
if ( /sha1/.test(nodOut.className) ) // FIXME : a more accurate filter
|
||||
{
|
||||
//message = b64_sha1(nodIn.value);
|
||||
if ( /ciform-sha1/.test(nodOut.className) )
|
||||
{
|
||||
// for this class we add meta-data
|
||||
// NOTE : message must not contain the ":" separator so it can be extracted later
|
||||
//message = "salt" + salt + ":b64:sha1:" + message;
|
||||
encoders.push( new ciform.SHA1Encoder({'preamble':true}) );
|
||||
}
|
||||
// fields to be encrypted are replaced with something else
|
||||
// because they're not supposed to be transmitted in clear
|
||||
// FIXME ? must respect character set and length of the original field
|
||||
//input_replacement = "";
|
||||
else
|
||||
{
|
||||
encoders.push( new ciform.SHA1Encoder() );
|
||||
}
|
||||
}
|
||||
encoders.push( new ciform.RSAEncoder(this.pubKey,{'preamble':true,'salt':true}) );
|
||||
var encoder = new ciform.ChainEncoder(encoders);
|
||||
|
||||
// encrypts the output field using the server's public key
|
||||
//var key = CIFORM[CIFORM_ID_PUBKEY];
|
||||
//var ciphertext = "ciform:rsa:0x" + ciform_encryptMessage(message,key);
|
||||
var nodIn = form && form[kIn] ? form[kIn] : $(kIn);
|
||||
var nodOut = form && form[kOut] ? form[kOut] : $(kOut);
|
||||
var text = nodIn.value;
|
||||
|
||||
try
|
||||
{
|
||||
var ciphertext = "ciform:" + encoder.encode(nodIn.value);
|
||||
done.push({'field':nodOut,'value':ciphertext});
|
||||
this.onEncryptionStart(nodIn,options);
|
||||
|
||||
// handles a one-level sha-1 encoding for such fields
|
||||
if ( localOptions['sha1'] || /sha1/.test(nodOut.className) ) // FIXME : a more accurate filter
|
||||
{
|
||||
text = new ciform.encoders.SHA1Encoder().encode(text);
|
||||
}
|
||||
// same as sha1, but adds meta-data
|
||||
else if ( localOptions['ciform-sha1'] || /ciform-sha1/.test(nodOut.className) )
|
||||
{
|
||||
text = new ciform.encoders.SHA1Encoder({'preamble':true}).encode(text);
|
||||
}
|
||||
|
||||
// the encryption is here !
|
||||
var ciphertext = this.encryptText(text,localOptions); // may throw an exception
|
||||
|
||||
// records the 'in' and 'out' values for later (out of this loop)
|
||||
done.push({'field':nodIn,'value':""});
|
||||
done.push({'field':nodOut,'value':ciphertext});
|
||||
}
|
||||
catch ( e )
|
||||
{
|
||||
// calls back the error handler if defined
|
||||
if ( onError )
|
||||
if ( localOptions['onerror'] )
|
||||
{
|
||||
onError.apply(null,[e]);
|
||||
localOptions['onerror'].apply(null,[e]);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
|
@ -353,24 +553,16 @@ ciform.Ciform.prototype.encryptFields = function( fields, onError )
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
/*// if everything went fine, stores the replacement values for later
|
||||
if ( ciphertext.length && ciphertext.length > 0 )
|
||||
finally
|
||||
{
|
||||
// TODO : lighter formalism : don't need to use formal object here
|
||||
done.push({'field':nodOut,'value':ciphertext});
|
||||
if ( input_replacement != null )
|
||||
{
|
||||
done.push({'field':input,'value':input_replacement});
|
||||
}
|
||||
this.onEncryptionEnd(nodIn,localOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
// could happen if the text's length doesn't fit with the key's length
|
||||
return false;
|
||||
}*/
|
||||
} // iterating over fields is over
|
||||
|
||||
// replaces the values of the fiels (in the end, so that nothing is done if there's any error)
|
||||
// fields to be encrypted are replaced with something else
|
||||
// because they're not supposed to be transmitted in clear
|
||||
// FIXME ? must respect character set and length of the original field
|
||||
// transaction-like : if an error was catched before, this block would not be reached
|
||||
for ( var f=0 ; f<done.length ; f++ )
|
||||
{
|
||||
done[f]['field'].value = done[f]['value'];
|
||||
|
|
@ -378,3 +570,285 @@ ciform.Ciform.prototype.encryptFields = function( fields, onError )
|
|||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A simple shortcut when there's only one field to encrypt.
|
||||
@see ciform.Ciform#encryptFields
|
||||
*/
|
||||
ciform.Ciform.prototype.encryptField = function( field, options )
|
||||
{
|
||||
return this.encryptFields([field],options);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Encrypts fields of a form using the current configuration.<br>
|
||||
By default, all fields are encrypted.<br>
|
||||
|
||||
<p>For instance, this encrypts only inputs of type 'password' in a form : cif.encryptForm(myForm,{'tags':"password"})</p>
|
||||
|
||||
@param {String} form The form to encode
|
||||
@param {Object} options (optional) Contains filters to select the fields that should be encrypted :
|
||||
<ul>
|
||||
<li>'tags' : the list of tag names (or type of input fields) this function is allowed to encrypt.
|
||||
If not set, all tags are allowed.
|
||||
<li>'fields' : the list of the fields (their name, id or themselves) to encrypt.
|
||||
If not set, all fields are allowed to be encrypted.
|
||||
</ul>
|
||||
*/
|
||||
ciform.Ciform.prototype.encryptForm = function( form, options )
|
||||
{
|
||||
// 1. prepares parameters : instanciates the given elements from ther id, name, ...
|
||||
var localOptions = merge(options,this);
|
||||
var $form = $(form);
|
||||
var oktags = localOptions['tags'];
|
||||
var okfields = [];
|
||||
for ( var f=0 ; f<localOptions['fields'].length ; f++ ) {
|
||||
var node = this._getField( localOptions['fields'][f], 'in', form );
|
||||
okfields.push( merge( localOptions['fields'][f], {'in':node} ) );
|
||||
}
|
||||
|
||||
// 2. keeps only the fields to be encrypted
|
||||
var fields = [];
|
||||
for ( var e=0 ; e<$form.length ; e++ )
|
||||
{
|
||||
var el = $form[e];
|
||||
// a filter is applied to the form control type
|
||||
if ( !oktags || oktags.containsNoCase(el.tagName) || (el.tagName == "INPUT" && el.type && oktags.containsNoCase(el.type)) )
|
||||
{
|
||||
// ... and to the field name and id
|
||||
for ( var f=0 ; f<okfields.length ; f++ )
|
||||
{
|
||||
if ( okfields[f]['in'] == el && !fields.contains(el) )
|
||||
{
|
||||
fields.push(okfields[f]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. encrypts
|
||||
return this.encryptFields( fields, merge(localOptions,{'form':$form}) );
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Encrypts the parameters in an URL.
|
||||
|
||||
@param {String} url The full URL to encrypt (e.g. http://plugnauth.sf.net/ciform/demo.php?password=mysecret)
|
||||
@param {Object} options (optional) Options :<ul>
|
||||
<li>'fields' : an array containing the name of the fields that must be encrypted (if not present, all fields are encrypted)
|
||||
</ul>
|
||||
@return the same URL, with the fields encrypted.
|
||||
E.g. http://plugnauth.sf.net/ciform/demo.php?password=mysecret could become http://plugnauth.sf.net/ciform/demo.php?password=ciform:rsa:0x2137230230234832423723
|
||||
*/
|
||||
ciform.Ciform.prototype.encryptURL = function( url, options )
|
||||
{
|
||||
var cipherurl = url;
|
||||
|
||||
var query = /([^\?]*)\?(.*)/.exec(url);
|
||||
if ( query && query.length >= 3 )
|
||||
{
|
||||
cipherurl = query[1] + "?";
|
||||
var args = query[2].split(/&/);
|
||||
for ( var a=0 ; a<args.length ; a++ )
|
||||
{
|
||||
var keyval = /([^=]*)=?(.*)/.exec(args[a]);
|
||||
if ( keyval[1] )
|
||||
{
|
||||
cipherurl += keyval[1];
|
||||
|
||||
if ( keyval[2] )
|
||||
{
|
||||
// to be encrypted, either no field at all must be specified in the options
|
||||
// or this field must be present in the list
|
||||
var encryptMe = !options['fields'];
|
||||
if ( !encryptMe )
|
||||
{
|
||||
for ( var f=0 ; f<options['fields'] ; f++ )
|
||||
{
|
||||
if ( options['fields'] == keyval[1] )
|
||||
{
|
||||
encryptMe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
options['fields'][keyval[1]]
|
||||
}
|
||||
|
||||
cipherurl += "=" + (encryptMe ? this.encryptText(keyval[2]) : keyval[2]);
|
||||
}
|
||||
}
|
||||
if ( a+1<args.length )
|
||||
{
|
||||
cipherurl += "&";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cipherurl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@private
|
||||
*/
|
||||
ciform.Ciform.prototype._getForm = function( node )
|
||||
{
|
||||
return node instanceof HTMLFormElement ? node : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@private
|
||||
@param key can be either the node itself, its name or id, or an object with a property designating the field
|
||||
@param id the name of the property designating the field if key is a literal object
|
||||
@param form if defined, the form the field belongs to
|
||||
@return the corresponding Element or false if not found
|
||||
*/
|
||||
ciform.Ciform.prototype._getField = function( key, id, form )
|
||||
{
|
||||
if ( form && form[key] )
|
||||
{
|
||||
return form[key];
|
||||
} else {
|
||||
var node = $(key) ? $(key) : key;
|
||||
if ( typeof node == "object" && node.isFormInput() ) {
|
||||
return node;
|
||||
} else if ( $defined(id) && typeof node == "object" ) {
|
||||
return node[id] && $(node[id]) ? $(node[id]) : false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@private
|
||||
*/
|
||||
ciform.Ciform.prototype._getFields = function( node )
|
||||
{
|
||||
return node instanceof Array ? node : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This function tries to automatically determine the target to encrypt given versatile arguments.<br>
|
||||
|
||||
<p>It can be called in three ways :<ul>
|
||||
<li>2 arguments : target is the first argument, options the second
|
||||
<li>1 argument : can be either the target or options (determined from its type)
|
||||
<li>0 argument : target and options will be guessed from the current object
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
@param arg1 (optional) Either a form, a form field or an list of fields to encrypt / or options
|
||||
@param arg2 (optional) Options to pass to the encoding method
|
||||
@see ciform.Ciform#encryptForm
|
||||
@see ciform.Ciform#encryptField
|
||||
@see ciform.Ciform#encryptFields
|
||||
@return The returned valued depends on the target (see the definition of the corresponding function)
|
||||
@throws SyntaxError if a target could not be determined
|
||||
*/
|
||||
ciform.Ciform.prototype.encrypt = function( arg1, arg2 )
|
||||
{
|
||||
var targetForm = false;
|
||||
var targetField = false;
|
||||
var targetFields = false;
|
||||
|
||||
var options = arg2 ? arg2 : {};
|
||||
|
||||
// 1. determines if the first argument is the target
|
||||
if ( arg1 )
|
||||
{
|
||||
targetForm = this._getForm($(arg1));
|
||||
targetField = this._getField($(arg1));
|
||||
targetFields = this._getFields(arg1);
|
||||
}
|
||||
|
||||
// 2. target couldn't be determined from the first argument : try with the current context
|
||||
if ( !targetForm && !targetField && !targetFields )
|
||||
{
|
||||
options = arg1 ? arg1 : {};
|
||||
targetFields = $defined(this['fields']) && this['fields'] instanceof Array ? this['fields'] : false;
|
||||
targetField = targetFields && targetFields.length == 1 ? targetFields[0] : false;
|
||||
targetForm = $defined(this['form']) && this['form'] instanceof HTMLFormElement ? this['form'] : false;
|
||||
}
|
||||
|
||||
// 3. executes the correct encryption method
|
||||
if ( targetForm ) {
|
||||
return this.encryptForm($(targetForm),options);
|
||||
}
|
||||
else if ( targetField ) {
|
||||
return this.encryptField($(targetField),options);
|
||||
}
|
||||
else if ( targetFields ) {
|
||||
return this.encryptFields(targetFields,options);
|
||||
}
|
||||
|
||||
throw new SyntaxError("A target couldn't be determined for encryption from the current context and arguments !");
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@return true if this object is a form input (including select and textarea)
|
||||
@addon
|
||||
*/
|
||||
Object.prototype.isFormInput = function()
|
||||
{
|
||||
return this instanceof HTMLInputElement
|
||||
|| this instanceof HTMLSelectElement
|
||||
|| this instanceof HTMLTextAreaElement;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A shortcut to encode different sources.<br>
|
||||
|
||||
This function merely does the following :
|
||||
<ol>
|
||||
<li>detects the type of the target
|
||||
<li>instanciates a ciform.Ciform
|
||||
<li>calls the appropriate encoding method on the target
|
||||
</ol>
|
||||
|
||||
@param target Either a form, a form field or an URL to encrypt
|
||||
@param initOptions (optional) Options to pass to the constructor of ciform.Ciform and to the encoding method
|
||||
@param encodeOptions (optional) Options to add to options1, to pass to the encoding method (content overrides the one of 'options1')
|
||||
@see ciform.Ciform#encryptURL
|
||||
@see ciform.Ciform#encryptField
|
||||
@see ciform.Ciform#encryptForm
|
||||
@return The returned valued depends on the target (see the definition of the corresponding function)
|
||||
@member ciform
|
||||
*/
|
||||
/*ciform.encrypt = function( target, initOptions, encodeOptions )
|
||||
{
|
||||
var targ = $(target);
|
||||
var url = !targ && typeof target == "string" ? target : false;
|
||||
var field = targ.value ? targ : false;
|
||||
var form = targ ? targ : target;
|
||||
|
||||
var cif = new ciform.Ciform( merge(initOptions,{'url':}) );
|
||||
|
||||
if ( url ) {
|
||||
return cif.encryptURL(url,merge(initOptions,encodeOptions));
|
||||
}
|
||||
else if ( field ) {
|
||||
return cif.encryptField(field,merge(initOptions,encodeOptions));
|
||||
}
|
||||
else {
|
||||
return cif.encryptForm(form,merge(initOptions,encodeOptions));
|
||||
}
|
||||
};*/
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
<?php
|
||||
// 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_PREFIX","ciform:");
|
||||
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 );
|
||||
|
||||
|
|
@ -105,6 +107,28 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
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.js");
|
||||
define("CIFORM_RSA_KEYFILE_JS",CIFORM_RSA_KEYSTORE."/key-rsa.pub.json");
|
||||
define("CIFORM_RSA_REQUEST_GENKEY","ciform-genkey");
|
||||
|
||||
|
||||
|
|
@ -48,20 +48,13 @@
|
|||
$pq = ciform_rsa_bigInt2Json($math,$pubKey->getModulus());
|
||||
//$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 ; not required (=p*q)
|
||||
//.","
|
||||
//."'mpi':'$mpi'" // e + modulus, encoded into a base64 MPI string
|
||||
."}";
|
||||
."'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;
|
||||
}
|
||||
|
||||
|
|
@ -97,10 +90,10 @@
|
|||
{
|
||||
@mkdir(dirname($jsFilename),0777,TRUE);
|
||||
// FIXME : serverURL must be absolute, so scripts can call it from other servers
|
||||
$serverURL = $_SERVER['PHP_SELF'];
|
||||
//$serverURL = $_SERVER['PHP_SELF'];
|
||||
$pubKey = ciform_rsa_pubKey2Json($keyPair);
|
||||
$jsContents .= "\nvar CIFORM = {'serverURL':'".str_replace("'","\\'",$serverURL)."', 'pubKey':$pubKey};";
|
||||
@file_put_contents($jsFilename,$jsContents);
|
||||
//$jsContents = "\nvar CIFORM = {'serverURL':'".str_replace("'","\\'",$serverURL)."', 'pubKey':$pubKey};";
|
||||
@file_put_contents($jsFilename,$pubKey);
|
||||
}
|
||||
|
||||
// returns the newly created key
|
||||
|
|
@ -120,6 +113,24 @@
|
|||
|
||||
|
||||
|
||||
function ciform_rsa_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':".ciform_rsa_pubKey2Json($keyPair)
|
||||
."}";
|
||||
if ( CIFORM_DEBUG ) print_r($protocol);
|
||||
return $protocol;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ciform_rsa_decrypt( $data, $keyPair )
|
||||
{
|
||||
if ( CIFORM_DEBUG ) echo "ciform_rsa_decrypt($data,keyPair)<br>";
|
||||
|
|
|
|||
Loading…
Reference in a new issue