1
0
mirror of https://github.com/moparisthebest/mail synced 2024-11-25 18:32:20 -05:00

update and test forge ~0.1.14

This commit is contained in:
Tankred Hase 2013-08-05 16:45:02 +02:00
parent 9ad79f640f
commit 79b297e624
36 changed files with 4522 additions and 1887 deletions

View File

@ -447,7 +447,7 @@ var expandKey = function(key, decrypt) {
sbox[temp >>> 24] ^ (rcon[iNk] << 24);
iNk++;
}
else if(Nk > 6 && (i % Nk == 4)) {
else if(Nk > 6 && (i % Nk === 4)) {
// temp = SubWord(temp)
temp =
sbox[temp >>> 24] << 24 ^
@ -798,7 +798,7 @@ var _updateBlock = function(w, input, output, decrypt) {
/**
* Creates an AES cipher object. CBC (cipher-block-chaining) mode will be
* used.
* used by default, the other supported modes are: CFB.
*
* The key and iv may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words. If an iv is provided, then
@ -809,29 +809,33 @@ var _updateBlock = function(w, input, output, decrypt) {
* @param iv the initialization vector to start with, null not to start.
* @param output the buffer to write to.
* @param decrypt true for decryption, false for encryption.
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
var _createCipher = function(key, iv, output, decrypt) {
var _createCipher = function(key, iv, output, decrypt, mode) {
var cipher = null;
if(!init) {
initialize();
}
// default to CBC mode
mode = (mode || 'CBC').toUpperCase();
/* Note: The key may be a string of bytes, an array of bytes, a byte
buffer, or an array of 32-bit integers. If the key is in bytes, then
it must be 16, 24, or 32 bytes in length. If it is in 32-bit
integers, it must be 4, 6, or 8 integers long. */
// convert key string into byte buffer
if(key.constructor == String &&
(key.length == 16 || key.length == 24 || key.length == 32)) {
if(typeof key === 'string' &&
(key.length === 16 || key.length === 24 || key.length === 32)) {
key = forge.util.createBuffer(key);
}
// convert key integer array into byte buffer
else if(key.constructor == Array &&
(key.length == 16 || key.length == 24 || key.length == 32)) {
else if(forge.util.isArray(key) &&
(key.length === 16 || key.length === 24 || key.length === 32)) {
var tmp = key;
var key = forge.util.createBuffer();
for(var i = 0; i < tmp.length; ++i) {
@ -840,13 +844,13 @@ var _createCipher = function(key, iv, output, decrypt) {
}
// convert key byte buffer into 32-bit integer array
if(key.constructor != Array) {
if(!forge.util.isArray(key)) {
var tmp = key;
key = [];
// key lengths of 16, 24, 32 bytes allowed
var len = tmp.length();
if(len == 16 || len == 24 || len == 32) {
if(len === 16 || len === 24 || len === 32) {
len = len >>> 2;
for(var i = 0; i < len; ++i) {
key.push(tmp.getInt32());
@ -855,10 +859,19 @@ var _createCipher = function(key, iv, output, decrypt) {
}
// key must be an array of 32-bit integers by now
if(key.constructor == Array &&
(key.length == 4 || key.length == 6 || key.length == 8)) {
if(!forge.util.isArray(key) ||
!(key.length === 4 || key.length === 6 || key.length === 8)) {
return cipher;
}
// (CFB/OFB/CTR always uses encryption)
var alwaysEncrypt = (['CFB', 'OFB', 'CTR'].indexOf(mode) !== -1);
// CBC mode requires padding
var requirePadding = (mode === 'CBC');
// private vars for state
var _w = expandKey(key, decrypt);
var _w = expandKey(key, decrypt && !alwaysEncrypt);
var _blockSize = Nb << 2;
var _input;
var _output;
@ -866,13 +879,32 @@ var _createCipher = function(key, iv, output, decrypt) {
var _outBlock;
var _prev;
var _finish;
var _op;
cipher = {
// output from AES (either encrypted or decrypted bytes)
output: null
};
if(mode === 'CBC') {
_op = cbcOp;
}
else if(mode === 'CFB') {
_op = cfbOp;
}
else if(mode === 'OFB') {
_op = ofbOp;
}
else if(mode === 'CTR') {
_op = ctrOp;
}
else {
throw {
message: 'Unsupported block cipher mode of operation: "' + mode + '"'
}
}
/**
* Updates the next block using CBC mode.
* Updates the next block according to the cipher mode.
*
* @param input the buffer to read from.
*/
@ -882,53 +914,16 @@ var _createCipher = function(key, iv, output, decrypt) {
_input.putBuffer(input);
}
/* In encrypt mode, the threshold for updating a block is the
block size. As soon as enough input is available to update
a block, encryption may occur. In decrypt mode, we wait for
2 blocks to be available or for the finish flag to be set
with only 1 block available. This is done so that the output
buffer will not be populated with padding bytes at the end
of the decryption -- they can be truncated before returning
from finish(). */
var threshold = decrypt && !_finish ? _blockSize << 1 : _blockSize;
while(_input.length() >= threshold) {
// get next block
if(decrypt) {
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _input.getInt32();
}
}
else {
// CBC mode XOR's IV (or previous block) with plaintext
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _prev[i] ^ _input.getInt32();
}
}
// update block
_updateBlock(_w, _inBlock, _outBlock, decrypt);
// write output, save previous ciphered block
if(decrypt) {
// CBC mode XOR's IV (or previous block) with plaintext
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_prev[i] ^ _outBlock[i]);
}
_prev = _inBlock.slice(0);
}
else {
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_outBlock[i]);
}
_prev = _outBlock;
}
// do cipher operation while input contains full blocks or if finishing
while(_input.length() >= _blockSize || (_input.length() > 0 && _finish)) {
_op();
}
};
/**
* Finishes encrypting or decrypting.
*
* @param pad a padding function to use, null for default,
* @param pad a padding function to use in CBC mode, null for default,
* signature(blockSize, buffer, decrypt).
*
* @return true if successful, false on error.
@ -936,14 +931,17 @@ var _createCipher = function(key, iv, output, decrypt) {
cipher.finish = function(pad) {
var rval = true;
// get # of bytes that won't fill a block
var overflow = _input.length() % _blockSize;
if(!decrypt) {
if(pad) {
rval = pad(_blockSize, _input, decrypt);
}
else {
else if(requirePadding) {
// add PKCS#7 padding to block (each pad byte is the
// value of the number of pad bytes)
var padding = (_input.length() == _blockSize) ?
var padding = (_input.length() === _blockSize) ?
_blockSize : (_blockSize - _input.length());
_input.fillWithByte(padding, padding);
}
@ -956,13 +954,15 @@ var _createCipher = function(key, iv, output, decrypt) {
}
if(decrypt) {
if(requirePadding) {
// check for error: input data not a multiple of blockSize
rval = (_input.length() === 0);
rval = (overflow === 0);
}
if(rval) {
if(pad) {
rval = pad(_blockSize, _output, decrypt);
}
else {
else if(requirePadding) {
// ensure padding byte count is valid
var len = _output.length();
var count = _output.at(len - 1);
@ -977,6 +977,11 @@ var _createCipher = function(key, iv, output, decrypt) {
}
}
// handle stream mode truncation if padding not set
if(!requirePadding && !pad && overflow > 0) {
_output.truncate(_blockSize - overflow);
}
return rval;
};
@ -993,7 +998,9 @@ var _createCipher = function(key, iv, output, decrypt) {
*/
cipher.start = function(iv, output) {
// if IV is null, reuse block from previous encryption/decryption
iv = iv || _prev.slice(0);
if(iv === null) {
iv = _prev.slice(0);
}
/* Note: The IV may be a string of bytes, an array of bytes, a
byte buffer, or an array of 32-bit integers. If the IV is in
@ -1001,11 +1008,11 @@ var _createCipher = function(key, iv, output, decrypt) {
32-bit integers, then it must be 4 integers long. */
// convert iv string into byte buffer
if(iv.constructor == String && iv.length == 16) {
if(typeof iv === 'string' && iv.length === 16) {
iv = forge.util.createBuffer(iv);
}
// convert iv byte array into byte buffer
else if(iv.constructor == Array && iv.length == 16) {
else if(forge.util.isArray(iv) && iv.length === 16) {
var tmp = iv;
var iv = forge.util.createBuffer();
for(var i = 0; i < 16; ++i) {
@ -1014,7 +1021,7 @@ var _createCipher = function(key, iv, output, decrypt) {
}
// convert iv byte buffer into 32-bit integer array
if(iv.constructor != Array) {
if(!forge.util.isArray(iv)) {
var tmp = iv;
iv = new Array(4);
iv[0] = tmp.getInt32();
@ -1031,21 +1038,119 @@ var _createCipher = function(key, iv, output, decrypt) {
_outBlock = new Array(Nb);
_finish = false;
cipher.output = _output;
// CFB/OFB/CTR uses IV as first input
if(['CFB', 'OFB', 'CTR'].indexOf(mode) !== -1) {
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _prev[i];
}
_prev = null;
}
};
if(iv !== null) {
cipher.start(iv, output);
}
}
return cipher;
// block cipher mode operations:
function cbcOp() {
// get next block
if(decrypt) {
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _input.getInt32();
}
}
else {
// CBC XOR's IV (or previous block) with plaintext
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _prev[i] ^ _input.getInt32();
}
}
// update block
_updateBlock(_w, _inBlock, _outBlock, decrypt);
// write output, save previous ciphered block
if(decrypt) {
// CBC XOR's IV (or previous block) with ciphertext
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_prev[i] ^ _outBlock[i]);
}
_prev = _inBlock.slice(0);
}
else {
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_outBlock[i]);
}
_prev = _outBlock;
}
}
function cfbOp() {
// update block (CFB always uses encryption mode)
_updateBlock(_w, _inBlock, _outBlock, false);
// get next input
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _input.getInt32();
}
// XOR input with output
for(var i = 0; i < Nb; ++i) {
var result = _inBlock[i] ^ _outBlock[i];
if(!decrypt) {
// update next input block when encrypting
_inBlock[i] = result;
}
_output.putInt32(result);
}
}
function ofbOp() {
// update block (OFB always uses encryption mode)
_updateBlock(_w, _inBlock, _outBlock, false);
// get next input
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _input.getInt32();
}
// XOR input with output and update next input
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_inBlock[i] ^ _outBlock[i]);
_inBlock[i] = _outBlock[i];
}
}
function ctrOp() {
// update block (CTR always uses encryption mode)
_updateBlock(_w, _inBlock, _outBlock, false);
// increment counter (input block)
for(var i = Nb - 1; i >= 0; --i) {
if(_inBlock[i] === 0xFFFFFFFF) {
_inBlock[i] = 0;
}
else {
++_inBlock[i];
break;
}
}
// XOR input with output
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_input.getInt32() ^ _outBlock[i]);
}
}
};
/* AES API */
forge.aes = forge.aes || {};
/**
* Creates an AES cipher object to encrypt data in CBC mode using the
* given symmetric key. The output will be stored in the 'output' member
* of the returned cipher.
* Creates an AES cipher object to encrypt data using the given symmetric key.
* The output will be stored in the 'output' member of the returned cipher.
*
* The key and iv may be given as a string of bytes, an array of bytes,
* a byte buffer, or an array of 32-bit words.
@ -1053,16 +1158,16 @@ forge.aes = forge.aes || {};
* @param key the symmetric key to use.
* @param iv the initialization vector to use.
* @param output the buffer to write to, null to create one.
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
forge.aes.startEncrypting = function(key, iv, output) {
return _createCipher(key, iv, output, false);
forge.aes.startEncrypting = function(key, iv, output, mode) {
return _createCipher(key, iv, output, false, mode);
};
/**
* Creates an AES cipher object to encrypt data in CBC mode using the
* given symmetric key.
* Creates an AES cipher object to encrypt data using the given symmetric key.
*
* The key may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words.
@ -1071,17 +1176,17 @@ forge.aes.startEncrypting = function(key, iv, output) {
* output buffer.
*
* @param key the symmetric key to use.
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
forge.aes.createEncryptionCipher = function(key) {
return _createCipher(key, null, null, false);
forge.aes.createEncryptionCipher = function(key, mode) {
return _createCipher(key, null, null, false, mode);
};
/**
* Creates an AES cipher object to decrypt data in CBC mode using the
* given symmetric key. The output will be stored in the 'output' member
* of the returned cipher.
* Creates an AES cipher object to decrypt data using the given symmetric key.
* The output will be stored in the 'output' member of the returned cipher.
*
* The key and iv may be given as a string of bytes, an array of bytes,
* a byte buffer, or an array of 32-bit words.
@ -1089,16 +1194,16 @@ forge.aes.createEncryptionCipher = function(key) {
* @param key the symmetric key to use.
* @param iv the initialization vector to use.
* @param output the buffer to write to, null to create one.
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
forge.aes.startDecrypting = function(key, iv, output) {
return _createCipher(key, iv, output, true);
forge.aes.startDecrypting = function(key, iv, output, mode) {
return _createCipher(key, iv, output, true, mode);
};
/**
* Creates an AES cipher object to decrypt data in CBC mode using the
* given symmetric key.
* Creates an AES cipher object to decrypt data using the given symmetric key.
*
* The key may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words.
@ -1107,11 +1212,12 @@ forge.aes.startDecrypting = function(key, iv, output) {
* optional output buffer.
*
* @param key the symmetric key to use.
* @param mode the cipher mode to use (default: 'CBC').
*
* @return the cipher.
*/
forge.aes.createDecryptionCipher = function(key) {
return _createCipher(key, null, null, true);
forge.aes.createDecryptionCipher = function(key, mode) {
return _createCipher(key, null, null, true, mode);
};
/**
@ -1143,12 +1249,11 @@ forge.aes._updateBlock = _updateBlock;
/* ########## Begin module wrapper ########## */
var name = 'aes';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -1157,14 +1262,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -1181,6 +1284,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

309
src/lib/aesCipherSuites.js Normal file
View File

@ -0,0 +1,309 @@
/**
* A Javascript implementation of AES Cipher Suites for TLS.
*
* @author Dave Longley
*
* Copyright (c) 2009-2013 Digital Bazaar, Inc.
*
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var tls = forge.tls;
/**
* Supported cipher suites.
*/
tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = {
id: [0x00,0x2f],
name: 'TLS_RSA_WITH_AES_128_CBC_SHA',
initSecurityParameters: function(sp) {
sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
sp.cipher_type = tls.CipherType.block;
sp.enc_key_length = 16;
sp.block_length = 16;
sp.fixed_iv_length = 16;
sp.record_iv_length = 16;
sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;
sp.mac_length = 20;
sp.mac_key_length = 20;
},
initConnectionState: initConnectionState
};
tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = {
id: [0x00,0x35],
name: 'TLS_RSA_WITH_AES_256_CBC_SHA',
initSecurityParameters: function(sp) {
sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
sp.cipher_type = tls.CipherType.block;
sp.enc_key_length = 32;
sp.block_length = 16;
sp.fixed_iv_length = 16;
sp.record_iv_length = 16;
sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;
sp.mac_length = 20;
sp.mac_key_length = 20;
},
initConnectionState: initConnectionState
};
function initConnectionState(state, c, sp) {
var client = (c.entity === forge.tls.ConnectionEnd.client);
// cipher setup
state.read.cipherState = {
init: false,
cipher: forge.aes.createDecryptionCipher(client ?
sp.keys.server_write_key : sp.keys.client_write_key),
iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV
};
state.write.cipherState = {
init: false,
cipher: forge.aes.createEncryptionCipher(client ?
sp.keys.client_write_key : sp.keys.server_write_key),
iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV
};
state.read.cipherFunction = decrypt_aes_cbc_sha1;
state.write.cipherFunction = encrypt_aes_cbc_sha1;
// MAC setup
state.read.macLength = state.write.macLength = sp.mac_length;
state.read.macFunction = state.write.macFunction = tls.hmac_sha1;
};
/**
* Encrypts the TLSCompressed record into a TLSCipherText record using AES
* in CBC mode.
*
* @param record the TLSCompressed record to encrypt.
* @param s the ConnectionState to use.
*
* @return true on success, false on failure.
*/
function encrypt_aes_cbc_sha1(record, s) {
var rval = false;
// append MAC to fragment, update sequence number
var mac = s.macFunction(s.macKey, s.sequenceNumber, record);
record.fragment.putBytes(mac);
s.updateSequenceNumber();
// TLS 1.1 & 1.2 use an explicit IV every time to protect against
// CBC attacks
var iv;
if(record.version.minor > 1) {
iv = forge.random.getBytes(16);
}
else {
// use the pre-generated IV when initializing for TLS 1.0, otherwise use
// the residue from the previous encryption
iv = s.cipherState.init ? null : s.cipherState.iv;
}
s.cipherState.init = true;
// start cipher
var cipher = s.cipherState.cipher;
cipher.start(iv);
// TLS 1.1 & 1.2 write IV into output
if(record.version.minor > 1) {
cipher.output.putBytes(iv);
}
// do encryption (default padding is appropriate)
cipher.update(record.fragment);
if(cipher.finish(encrypt_aes_cbc_sha1_padding)) {
// set record fragment to encrypted output
record.fragment = cipher.output;
record.length = record.fragment.length();
rval = true;
}
return rval;
}
/**
* Handles padding for aes_cbc_sha1 in encrypt mode.
*
* @param blockSize the block size.
* @param input the input buffer.
* @param decrypt true in decrypt mode, false in encrypt mode.
*
* @return true on success, false on failure.
*/
function encrypt_aes_cbc_sha1_padding(blockSize, input, decrypt) {
/* The encrypted data length (TLSCiphertext.length) is one more than the sum
of SecurityParameters.block_length, TLSCompressed.length,
SecurityParameters.mac_length, and padding_length.
The padding may be any length up to 255 bytes long, as long as it results in
the TLSCiphertext.length being an integral multiple of the block length.
Lengths longer than necessary might be desirable to frustrate attacks on a
protocol based on analysis of the lengths of exchanged messages. Each uint8
in the padding data vector must be filled with the padding length value.
The padding length should be such that the total size of the
GenericBlockCipher structure is a multiple of the cipher's block length.
Legal values range from zero to 255, inclusive. This length specifies the
length of the padding field exclusive of the padding_length field itself.
This is slightly different from PKCS#7 because the padding value is 1
less than the actual number of padding bytes if you include the
padding_length uint8 itself as a padding byte. */
if(!decrypt) {
// get the number of padding bytes required to reach the blockSize and
// subtract 1 for the padding value (to make room for the padding_length
// uint8)
var padding = blockSize - (input.length() % blockSize);
input.fillWithByte(padding - 1, padding);
}
return true;
}
/**
* Handles padding for aes_cbc_sha1 in decrypt mode.
*
* @param blockSize the block size.
* @param output the output buffer.
* @param decrypt true in decrypt mode, false in encrypt mode.
*
* @return true on success, false on failure.
*/
function decrypt_aes_cbc_sha1_padding(blockSize, output, decrypt) {
var rval = true;
if(decrypt) {
/* The last byte in the output specifies the number of padding bytes not
including itself. Each of the padding bytes has the same value as that
last byte (known as the padding_length). Here we check all padding
bytes to ensure they have the value of padding_length even if one of
them is bad in order to ward-off timing attacks. */
var len = output.length();
var paddingLength = output.last();
for(var i = len - 1 - paddingLength; i < len - 1; ++i) {
rval = rval && (output.at(i) == paddingLength);
}
if(rval) {
// trim off padding bytes and last padding length byte
output.truncate(paddingLength + 1);
}
}
return rval;
}
/**
* Decrypts a TLSCipherText record into a TLSCompressed record using
* AES in CBC mode.
*
* @param record the TLSCipherText record to decrypt.
* @param s the ConnectionState to use.
*
* @return true on success, false on failure.
*/
function decrypt_aes_cbc_sha1(record, s) {
var rval = false;
// TODO: TLS 1.1 & 1.2 use an explicit IV every time to protect against
// CBC attacks
//var iv = record.fragment.getBytes(16);
// use pre-generated IV when initializing for TLS 1.0, otherwise use the
// residue from the previous decryption
var iv = s.cipherState.init ? null : s.cipherState.iv;
s.cipherState.init = true;
// start cipher
var cipher = s.cipherState.cipher;
cipher.start(iv);
// do decryption
cipher.update(record.fragment);
rval = cipher.finish(decrypt_aes_cbc_sha1_padding);
// even if decryption fails, keep going to minimize timing attacks
// decrypted data:
// first (len - 20) bytes = application data
// last 20 bytes = MAC
var macLen = s.macLength;
// create a zero'd out mac
var mac = '';
for(var i = 0; i < macLen; ++i) {
mac += String.fromCharCode(0);
}
// get fragment and mac
var len = cipher.output.length();
if(len >= macLen) {
record.fragment = cipher.output.getBytes(len - macLen);
mac = cipher.output.getBytes(macLen);
}
// bad data, but get bytes anyway to try to keep timing consistent
else {
record.fragment = cipher.output.getBytes();
}
record.fragment = forge.util.createBuffer(record.fragment);
record.length = record.fragment.length();
// see if data integrity checks out, update sequence number
var mac2 = s.macFunction(s.macKey, s.sequenceNumber, record);
s.updateSequenceNumber();
rval = (mac2 === mac) && rval;
return rval;
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'aesCipherSuites';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './aes', './tls'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -196,7 +196,7 @@ asn1.create = function(tagClass, type, constructed, value) {
according to the ASN.1 data type. */
// remove undefined values
if(value.constructor == Array) {
if(forge.util.isArray(value)) {
var tmp = [];
for(var i = 0; i < value.length; ++i) {
if(value[i] !== undefined) {
@ -210,7 +210,7 @@ asn1.create = function(tagClass, type, constructed, value) {
tagClass: tagClass,
type: type,
constructed: constructed,
composed: constructed || (value.constructor == Array),
composed: constructed || forge.util.isArray(value),
value: value
};
};
@ -226,7 +226,7 @@ asn1.create = function(tagClass, type, constructed, value) {
*/
var _getValueLength = function(b) {
var b2 = b.getByte();
if(b2 == 0x80) {
if(b2 === 0x80) {
return undefined;
}
@ -249,12 +249,18 @@ var _getValueLength = function(b) {
* Parses an asn1 object from a byte buffer in DER format.
*
* @param bytes the byte buffer to parse from.
* @param strict true to be strict when checking value lengths, false to
* allow truncated values (default: true).
*
* @return the parsed asn1 object.
*/
asn1.fromDer = function(bytes) {
asn1.fromDer = function(bytes, strict) {
if(strict === undefined) {
strict = true;
}
// wrap in buffer if needed
if(bytes.constructor == String) {
if(typeof bytes === 'string') {
bytes = forge.util.createBuffer(bytes);
}
@ -280,17 +286,21 @@ asn1.fromDer = function(bytes) {
// ensure there are enough bytes to get the value
if(bytes.length() < length) {
if(strict) {
throw {
message: 'Too few bytes to read ASN.1 value.',
detail: bytes.length() + ' < ' + length
};
}
// Note: be lenient with truncated values
length = bytes.length();
}
// prepare to get value
var value;
// constructed flag is bit 6 (32 = 0x20) of the first byte
var constructed = ((b1 & 0x20) == 0x20);
var constructed = ((b1 & 0x20) === 0x20);
// determine if the value is composed of other ASN.1 objects (if its
// constructed it will be and if its a BITSTRING it may be)
@ -311,8 +321,7 @@ asn1.fromDer = function(bytes) {
// and the length is valid, assume we've got an ASN.1 object
b1 = bytes.getByte();
var tc = (b1 & 0xC0);
if(tc === asn1.Class.UNIVERSAL ||
tc === asn1.Class.CONTEXT_SPECIFIC) {
if(tc === asn1.Class.UNIVERSAL || tc === asn1.Class.CONTEXT_SPECIFIC) {
try {
var len = _getValueLength(bytes);
composed = (len === length - (bytes.read - read));
@ -339,14 +348,14 @@ asn1.fromDer = function(bytes) {
bytes.getBytes(2);
break;
}
value.push(asn1.fromDer(bytes));
value.push(asn1.fromDer(bytes, strict));
}
}
else {
// parsing asn1 object of definite length
var start = bytes.length();
while(length > 0) {
value.push(asn1.fromDer(bytes));
value.push(asn1.fromDer(bytes, strict));
length -= start - bytes.length();
start = bytes.length();
}
@ -520,7 +529,7 @@ asn1.derToOid = function(bytes) {
var oid;
// wrap in buffer if needed
if(bytes.constructor == String) {
if(typeof bytes === 'string') {
bytes = forge.util.createBuffer(bytes);
}
@ -678,7 +687,7 @@ asn1.generalizedTimeToDate = function(gentime) {
var offset = 0;
var isUTC = false;
if(gentime.charAt(gentime.length - 1) == 'Z') {
if(gentime.charAt(gentime.length - 1) === 'Z') {
isUTC = true;
}
@ -701,7 +710,7 @@ asn1.generalizedTimeToDate = function(gentime) {
}
// check for second fraction
if(gentime.charAt(14) == '.') {
if(gentime.charAt(14) === '.') {
fff = parseFloat(gentime.substr(14), 10) * 1000;
}
@ -788,7 +797,7 @@ asn1.validate = function(obj, v, capture, errors) {
rval = true;
// handle sub values
if(v.value && v.value.constructor == Array) {
if(v.value && forge.util.isArray(v.value)) {
var j = 0;
for(var i = 0; rval && i < v.value.length; ++i) {
rval = v.value[i].optional || false;
@ -1012,12 +1021,11 @@ asn1.prettyPrint = function(obj, level, indentation) {
/* ########## Begin module wrapper ########## */
var name = 'asn1';
var deps = ['./util', './oids'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -1026,14 +1034,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -1050,6 +1056,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util', './oids'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -87,12 +87,11 @@ forge.debug.clear = function(cat, name) {
/* ########## Begin module wrapper ########## */
var name = 'debug';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -101,14 +100,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -125,6 +122,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -39,8 +39,6 @@ var spfunction6 = [0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x204
var spfunction7 = [0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002];
var spfunction8 = [0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000];
/**
* Create necessary sub keys.
*
@ -123,9 +121,6 @@ function des_createKeys (key) {
return keys;
}
/**
* Creates an DES cipher object.
*
@ -134,9 +129,8 @@ function des_createKeys (key) {
*
* @return the cipher.
*/
var _createCipher = function(key, encrypt)
{
if(key.constructor == String && (key.length == 8 || key.length == 24)) {
var _createCipher = function(key, encrypt) {
if(typeof key === 'string' && (key.length === 8 || key.length === 24)) {
key = forge.util.createBuffer(key);
}
@ -155,10 +149,10 @@ var _createCipher = function(key, encrypt)
var _finish = false, _input = null, _output = null;
/* Set up the loops for single and triple DES. */
var iterations = keys.length == 32 ? 3 : 9; // single or triple des
var iterations = keys.length === 32 ? 3 : 9; // single or triple des
var looping;
if(iterations == 3) {
if(iterations === 3) {
looping = encrypt
? [0, 32, 2]
: [30, -2, -2];
@ -183,7 +177,7 @@ var _createCipher = function(key, encrypt)
*/
start: function(iv, output) {
if(iv) {
if(key.constructor == String && iv.length == 8) {
if(typeof iv === 'string' && iv.length === 8) {
iv = forge.util.createBuffer(iv);
}
@ -221,7 +215,7 @@ var _createCipher = function(key, encrypt)
var right = _input.getInt32();
//for Cipher Block Chaining mode, xor the message with the previous result
if(mode == 1) {
if(mode === 1) {
if(encrypt) {
left ^= cbcleft;
right ^= cbcright;
@ -275,7 +269,7 @@ var _createCipher = function(key, encrypt)
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
//for Cipher Block Chaining mode, xor the message with the previous result
if(mode == 1) {
if(mode === 1) {
if(encrypt) {
cbcleft = left;
cbcright = right;
@ -307,7 +301,7 @@ var _createCipher = function(key, encrypt)
} else {
// add PKCS#7 padding to block (each pad byte is the
// value of the number of pad bytes)
var padding = (_input.length() == 8) ? 8 : (8 - _input.length());
var padding = (_input.length() === 8) ? 8 : (8 - _input.length());
_input.fillWithByte(padding, padding);
}
}
@ -362,8 +356,7 @@ forge.des = forge.des || {};
*
* @return the cipher.
*/
forge.des.startEncrypting = function(key, iv, output)
{
forge.des.startEncrypting = function(key, iv, output) {
var cipher = _createCipher(key, true);
cipher.start(iv, output);
return cipher;
@ -382,8 +375,7 @@ forge.des.startEncrypting = function(key, iv, output)
*
* @return the cipher.
*/
forge.des.createEncryptionCipher = function(key)
{
forge.des.createEncryptionCipher = function(key) {
return _createCipher(key, true);
};
@ -400,8 +392,7 @@ forge.des.createEncryptionCipher = function(key)
*
* @return the cipher.
*/
forge.des.startDecrypting = function(key, iv, output)
{
forge.des.startDecrypting = function(key, iv, output) {
var cipher = _createCipher(key, false);
cipher.start(iv, output);
return cipher;
@ -420,8 +411,7 @@ forge.des.startDecrypting = function(key, iv, output)
*
* @return the cipher.
*/
forge.des.createDecryptionCipher = function(key)
{
forge.des.createDecryptionCipher = function(key) {
return _createCipher(key, false);
};
@ -429,12 +419,11 @@ forge.des.createDecryptionCipher = function(key)
/* ########## Begin module wrapper ########## */
var name = 'des';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -443,14 +432,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -467,6 +454,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -6,15 +6,71 @@
* Copyright 2011-2013 Digital Bazaar, Inc.
*/
(function() {
var deps = [
var name = 'forge';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
// set to true to disable native code if even it's available
forge = {disableNativeCode: false};
}
return;
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
});
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge;
};
// set to true to disable native code if even it's available
module.exports.disableNativeCode = false;
module.exports(module.exports);
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./aes',
'./aesCipherSuites',
'./asn1',
'./debug',
'./des',
'./hmac',
'./log',
'./pbkdf2',
'./pem',
'./pkcs7',
'./pkcs1',
'./pkcs12',
'./pki',
'./prng',
@ -26,35 +82,7 @@ var deps = [
'./util',
'./md',
'./mgf1'
];
var cjsDefine = null;
if(typeof define !== 'function') {
// CommonJS -> AMD
if(typeof module === 'object' && module.exports) {
cjsDefine = function(ids, factory) {
module.exports = factory.apply(null, ids.map(function(id) {
return require(id);
}));
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(cjsDefine || typeof define === 'function') {
// define module AMD style
(cjsDefine || define)(deps, function() {
var forge = {};
var mods = Array.prototype.slice.call(arguments);
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge;
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
}
})();

View File

@ -45,7 +45,7 @@ hmac.create = function() {
*/
ctx.start = function(md, key) {
if(md !== null) {
if(md.constructor == String) {
if(typeof md === 'string') {
// create builtin message digest
md = md.toLowerCase();
if(md in forge.md.algorithms) {
@ -67,11 +67,11 @@ hmac.create = function() {
}
else {
// convert string into byte buffer
if(key.constructor == String) {
if(typeof key === 'string') {
key = forge.util.createBuffer(key);
}
// convert byte array into byte buffer
else if(key.constructor == Array) {
else if(forge.util.isArray(key)) {
var tmp = key;
key = forge.util.createBuffer();
for(var i = 0; i < tmp.length; ++i) {
@ -153,12 +153,11 @@ hmac.create = function() {
/* ########## Begin module wrapper ########## */
var name = 'hmac';
var deps = ['./md', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -167,14 +166,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -191,6 +188,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './md', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -901,6 +901,9 @@ http.createRequest = function(options) {
// add custom headers
var headers = options.headers || [];
if(!forge.util.isArray(headers)) {
headers = [headers];
}
for(var i = 0; i < headers.length; ++i) {
for(var name in headers[i]) {
request.appendField(name, headers[i][name]);
@ -1202,7 +1205,7 @@ http.createResponse = function() {
if(contentLength !== null && contentLength >= 0) {
response.body = response.body || '';
response.body += b.getBytes(contentLength);
response.bodyReceived = (response.body.length == contentLength);
response.bodyReceived = (response.body.length === contentLength);
}
// read chunked encoding
else if(transferEncoding !== null) {
@ -1372,7 +1375,7 @@ http.withinCookieDomain = function(url, cookie) {
var rval = false;
// cookie may be null, a cookie object, or a domain string
var domain = (cookie === null || cookie.constructor == String) ?
var domain = (cookie === null || typeof cookie === 'string') ?
cookie : cookie.domain;
// any domain will do
@ -1382,7 +1385,7 @@ http.withinCookieDomain = function(url, cookie) {
// ensure domain starts with a '.'
else if(domain.charAt(0) === '.') {
// parse URL as necessary
if(url.constructor == String) {
if(typeof url === 'string') {
url = http.parseUrl(url);
}

View File

@ -267,7 +267,7 @@ function bnCompareTo(a) {
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return r;
if(r != 0) return (this.s<0)?-r:r;
while(--i >= 0) if((r=this.data[i]-a.data[i]) != 0) return r;
return 0;
}
@ -1271,12 +1271,11 @@ forge.jsbn.BigInteger = BigInteger;
/* ########## Begin module wrapper ########## */
var name = 'jsbn';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -1285,14 +1284,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -1309,6 +1306,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -325,12 +325,11 @@ forge.log.consoleLogger = sConsoleLogger;
/* ########## Begin module wrapper ########## */
var name = 'log';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -339,14 +338,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -363,6 +360,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -23,12 +23,11 @@ forge.md.sha256 = forge.sha256;
/* ########## Begin module wrapper ########## */
var name = 'md';
var deps = ['./md5', './sha1', './sha256'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -37,14 +36,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -61,6 +58,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './md5', './sha1', './sha256'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -157,6 +157,8 @@ md5.create = function() {
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
md.messageLength = 0;
@ -167,6 +169,7 @@ md5.create = function() {
h2: 0x98BADCFE,
h3: 0x10325476
};
return md;
};
// start digest automatically for first time
md.start();
@ -178,6 +181,8 @@ md5.create = function() {
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
@ -197,6 +202,8 @@ md5.create = function() {
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
@ -260,12 +267,11 @@ md5.create = function() {
/* ########## Begin module wrapper ########## */
var name = 'md5';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -274,14 +280,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -298,6 +302,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -16,12 +16,11 @@ forge.mgf.mgf1 = forge.mgf1;
/* ########## Begin module wrapper ########## */
var name = 'mgf';
var deps = ['./mgf1'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -30,14 +29,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -54,6 +51,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './mgf1'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -57,12 +57,11 @@ mgf1.create = function(md) {
/* ########## Begin module wrapper ########## */
var name = 'mgf1';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -71,14 +70,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -95,6 +92,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -16,14 +16,14 @@ var oids = forge.pki.oids = forge.oids = forge.oids || {};
oids['1.2.840.113549.1.1.1'] = 'rsaEncryption';
oids['rsaEncryption'] = '1.2.840.113549.1.1.1';
// Note: md2 & md4 not implemented
//oids['1.2.840.113549.1.1.2'] = 'md2withRSAEncryption';
//oids['md2withRSAEncryption'] = '1.2.840.113549.1.1.2';
//oids['1.2.840.113549.1.1.3'] = 'md4withRSAEncryption';
//oids['md4withRSAEncryption'] = '1.2.840.113549.1.1.3';
oids['1.2.840.113549.1.1.4'] = 'md5withRSAEncryption';
oids['md5withRSAEncryption'] = '1.2.840.113549.1.1.4';
oids['1.2.840.113549.1.1.5'] = 'sha1withRSAEncryption';
oids['sha1withRSAEncryption'] = '1.2.840.113549.1.1.5';
//oids['1.2.840.113549.1.1.2'] = 'md2WithRSAEncryption';
//oids['md2WithRSAEncryption'] = '1.2.840.113549.1.1.2';
//oids['1.2.840.113549.1.1.3'] = 'md4WithRSAEncryption';
//oids['md4WithRSAEncryption'] = '1.2.840.113549.1.1.3';
oids['1.2.840.113549.1.1.4'] = 'md5WithRSAEncryption';
oids['md5WithRSAEncryption'] = '1.2.840.113549.1.1.4';
oids['1.2.840.113549.1.1.5'] = 'sha1WithRSAEncryption';
oids['sha1WithRSAEncryption'] = '1.2.840.113549.1.1.5';
oids['1.2.840.113549.1.1.7'] = 'RSAES-OAEP';
oids['RSAES-OAEP'] = '1.2.840.113549.1.1.7';
oids['1.2.840.113549.1.1.8'] = 'mgf1';
@ -65,6 +65,23 @@ oids['1.2.840.113549.1.7.6'] = 'encryptedData';
oids['encryptedData'] = '1.2.840.113549.1.7.6';
// pkcs#9 oids
oids['1.2.840.113549.1.9.1'] = 'emailAddress';
oids['emailAddress'] = '1.2.840.113549.1.9.1';
oids['1.2.840.113549.1.9.2'] = 'unstructuredName';
oids['unstructuredName'] = '1.2.840.113549.1.9.2';
oids['1.2.840.113549.1.9.3'] = 'contentType';
oids['contentType'] = '1.2.840.113549.1.9.3';
oids['1.2.840.113549.1.9.4'] = 'messageDigest';
oids['messageDigest'] = '1.2.840.113549.1.9.4';
oids['1.2.840.113549.1.9.5'] = 'signingTime';
oids['signingTime'] = '1.2.840.113549.1.9.5';
oids['1.2.840.113549.1.9.6'] = 'counterSignature';
oids['counterSignature'] = '1.2.840.113549.1.9.6';
oids['1.2.840.113549.1.9.7'] = 'challengePassword';
oids['challengePassword'] = '1.2.840.113549.1.9.7';
oids['1.2.840.113549.1.9.8'] = 'unstructuredAddress';
oids['unstructuredAddress'] = '1.2.840.113549.1.9.8';
oids['1.2.840.113549.1.9.20'] = 'friendlyName';
oids['friendlyName'] = '1.2.840.113549.1.9.20';
oids['1.2.840.113549.1.9.21'] = 'localKeyId';
@ -72,13 +89,6 @@ oids['localKeyId'] = '1.2.840.113549.1.9.21';
oids['1.2.840.113549.1.9.22.1'] = 'x509Certificate';
oids['x509Certificate'] = '1.2.840.113549.1.9.22.1';
oids['1.2.840.113549.1.9.4'] = 'messageDigest';
oids['messageDigest'] = '1.2.840.113549.1.9.4';
oids['1.2.840.113549.1.9.3'] = 'contentType';
oids['contentType'] = '1.2.840.113549.1.9.3';
oids['1.2.840.113549.1.9.5'] = 'signingTime';
oids['signingTime'] = '1.2.840.113549.1.9.5';
// pkcs#12 safe bags
oids['1.2.840.113549.1.12.10.1.1'] = 'keyBag';
oids['keyBag'] = '1.2.840.113549.1.12.10.1.1';
@ -137,10 +147,10 @@ oids['2.5.4.10'] = 'organizationName';
oids['organizationName'] = '2.5.4.10';
oids['2.5.4.11'] = 'organizationalUnitName';
oids['organizationalUnitName'] = '2.5.4.11';
oids['1.2.840.113549.1.9.1'] = 'emailAddress';
oids['emailAddress'] = '1.2.840.113549.1.9.1';
// X.509 extension OIDs
oids['2.16.840.1.113730.1.1'] = 'nsCertType';
oids['nsCertType'] = '2.16.840.1.113730.1.1';
oids['2.5.29.1'] = 'authorityKeyIdentifier'; // deprecated, use .35
oids['2.5.29.2'] = 'keyAttributes'; // obsolete use .37 or .15
oids['2.5.29.3'] = 'certificatePolicies'; // deprecated, use .32
@ -187,16 +197,27 @@ oids['extKeyUsage'] = '2.5.29.37';
oids['2.5.29.46'] = 'freshestCRL';
oids['2.5.29.54'] = 'inhibitAnyPolicy';
// extKeyUsage purposes
oids['1.3.6.1.5.5.7.3.1'] = 'serverAuth';
oids['serverAuth'] = '1.3.6.1.5.5.7.3.1';
oids['1.3.6.1.5.5.7.3.2'] = 'clientAuth';
oids['clientAuth'] = '1.3.6.1.5.5.7.3.2';
oids['1.3.6.1.5.5.7.3.3'] = 'codeSigning';
oids['codeSigning'] = '1.3.6.1.5.5.7.3.3';
oids['1.3.6.1.5.5.7.3.4'] = 'emailProtection';
oids['emailProtection'] = '1.3.6.1.5.5.7.3.4';
oids['1.3.6.1.5.5.7.3.8'] = 'timeStamping';
oids['timeStamping'] = '1.3.6.1.5.5.7.3.8';
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'oids';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -205,14 +226,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -229,6 +248,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -25,7 +25,7 @@ var pkcs5 = forge.pkcs5 = forge.pkcs5 || {};
*
* @return the derived key, as a string of bytes.
*/
pkcs5.pbkdf2 = function(p, s, c, dkLen, md) {
forge.pbkdf2 = pkcs5.pbkdf2 = function(p, s, c, dkLen, md) {
// default prf to SHA-1
if(typeof(md) === 'undefined' || md === null) {
md = forge.md.sha1.create();
@ -110,12 +110,11 @@ pkcs5.pbkdf2 = function(p, s, c, dkLen, md) {
/* ########## Begin module wrapper ########## */
var name = 'pbkdf2';
var deps = ['./hmac', './md', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -124,14 +123,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -148,6 +145,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './hmac', './md', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

301
src/lib/pem.js Normal file
View File

@ -0,0 +1,301 @@
/**
* Javascript implementation of basic PEM (Privacy Enhanced Mail) algorithms.
*
* See: RFC 1421.
*
* @author Dave Longley
*
* Copyright (c) 2013 Digital Bazaar, Inc.
*
* A Forge PEM object has the following fields:
*
* type: identifies the type of message (eg: "RSA PRIVATE KEY").
*
* procType: identifies the type of processing performed on the message,
* it has two subfields: version and type, eg: 4,ENCRYPTED.
*
* contentDomain: identifies the type of content in the message, typically
* only uses the value: "RFC822".
*
* dekInfo: identifies the message encryption algorithm and mode and includes
* any parameters for the algorithm, it has two subfields: algorithm and
* parameters, eg: DES-CBC,F8143EDE5960C597.
*
* headers: contains all other PEM encapsulated headers -- where order is
* significant (for pairing data like recipient ID + key info).
*
* body: the binary-encoded body.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for pem API
var pem = forge.pem = forge.pem || {};
/**
* Encodes (serializes) the given PEM object.
*
* @param msg the PEM message object to encode.
* @param options the options to use:
* maxline the maximum characters per line for the body, (default: 64).
*
* @return the PEM-formatted string.
*/
pem.encode = function(msg, options) {
options = options || {};
var rval = '-----BEGIN ' + msg.type + '-----\r\n';
// encode special headers
var header;
if(msg.procType) {
header = {
name: 'Proc-Type',
values: [String(msg.procType.version), msg.procType.type]
};
rval += foldHeader(header);
}
if(msg.contentDomain) {
header = {name: 'Content-Domain', values: [msg.contentDomain]};
rval += foldHeader(header);
}
if(msg.dekInfo) {
header = {name: 'DEK-Info', values: [msg.dekInfo.algorithm]};
if(msg.dekInfo.parameters) {
header.values.push(msg.dekInfo.parameters);
}
rval += foldHeader(header);
}
if(msg.headers) {
// encode all other headers
for(var i = 0; i < msg.headers.length; ++i) {
rval += foldHeader(msg.headers[i]);
}
}
// terminate header
if(msg.procType) {
rval += '\r\n';
}
// add body
rval += forge.util.encode64(msg.body, options.maxline || 64) + '\r\n';
rval += '-----END ' + msg.type + '-----\r\n';
return rval;
};
/**
* Decodes (deserializes) all PEM messages found in the given string.
*
* @param str the PEM-formatted string to decode.
*
* @return the PEM message objects in an array.
*/
pem.decode = function(str) {
var rval = [];
// split string into PEM messages
var rMessage = /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g;
var rHeader = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/;
var rCRLF = /\r?\n/;
var match;
while(true) {
match = rMessage.exec(str);
if(!match) {
break;
}
var msg = {
type: match[1],
procType: null,
contentDomain: null,
dekInfo: null,
headers: [],
body: forge.util.decode64(match[3])
};
rval.push(msg);
// no headers
if(!match[2]) {
continue;
}
// parse headers
var lines = match[2].split(rCRLF);
var li = 0;
while(match && li < lines.length) {
// get line, trim any rhs whitespace
var line = lines[li].replace(/\s+$/, '');
// RFC2822 unfold any following folded lines
for(var nl = li + 1; nl < lines.length; ++nl) {
var next = lines[nl];
if(!/\s/.test(next[0])) {
break;
}
line += next;
li = nl;
}
// parse header
match = line.match(rHeader);
if(match) {
var header = {name: match[1], values: []};
var values = match[2].split(',');
for(var vi = 0; vi < values.length; ++vi) {
header.values.push(ltrim(values[vi]));
}
// Proc-Type must be the first header
if(!msg.procType) {
if(header.name !== 'Proc-Type') {
throw {
message: 'Invalid PEM formatted message. The first ' +
'encapsulated header must be "Proc-Type".'
};
}
else if(header.values.length !== 2) {
throw {
message: 'Invalid PEM formatted message. The "Proc-Type" ' +
'header must have two subfields.'
};
}
msg.procType = {version: values[0], type: values[1]};
}
// special-case Content-Domain
else if(!msg.contentDomain && header.name === 'Content-Domain') {
msg.contentDomain = values[0] || '';
}
// special-case DEK-Info
else if(!msg.dekInfo && header.name === 'DEK-Info') {
if(header.values.length === 0) {
throw {
message: 'Invalid PEM formatted message. The "DEK-Info" ' +
'header must have at least one subfield.'
};
}
msg.dekInfo = {algorithm: values[0], parameters: values[1] || null};
}
else {
msg.headers.push(header);
}
}
++li;
}
if(msg.procType === 'ENCRYPTED' && !msg.dekInfo) {
throw {
message: 'Invalid PEM formatted message. The "DEK-Info" ' +
'header must be present if "Proc-Type" is "ENCRYPTED".'
};
}
}
if(rval.length === 0) {
throw {
message: 'Invalid PEM formatted message.'
};
}
return rval;
};
function foldHeader(header) {
var rval = header.name + ': ';
// ensure values with CRLF are folded
var values = [];
for(var i = 0; i < header.values.length; ++i) {
values.push(header.values[i].replace(/^(\S+\r\n)/, function(match, $1) {
return ' ' + $1;
}));
}
rval += values.join(',') + '\r\n';
// do folding
var length = 0;
var candidate = -1;
for(var i = 0; i < rval.length; ++i, ++length) {
if(length > 65 && candidate !== -1) {
var insert = rval[candidate];
if(insert === ',') {
++candidate;
rval = rval.substr(0, candidate) + '\r\n ' + rval.substr(candidate);
}
else {
rval = rval.substr(0, candidate) +
'\r\n' + insert + rval.substr(candidate + 1);
}
length = (i - candidate - 1);
candidate = -1;
++i;
}
else if(rval[i] === ' ' || rval[i] === '\t' || rval[i] === ',') {
candidate = i;
}
}
return rval;
}
function ltrim(str) {
return str.replace(/^\s+/, '');
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pem';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

315
src/lib/pkcs1.js Normal file
View File

@ -0,0 +1,315 @@
/**
* Partial implementation of PKCS#1 v2.2: RSA-OEAP
*
* Modified but based on the following MIT and BSD licensed code:
*
* https://github.com/kjur/jsjws/blob/master/rsa.js:
*
* The 'jsjws'(JSON Web Signature JavaScript Library) License
*
* Copyright (c) 2012 Kenji Urushima
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* http://webrsa.cvs.sourceforge.net/viewvc/webrsa/Client/RSAES-OAEP.js?content-type=text%2Fplain:
*
* RSAES-OAEP.js
* $Id: RSAES-OAEP.js,v 1.1.1.1 2003/03/19 15:37:20 ellispritchard Exp $
* JavaScript Implementation of PKCS #1 v2.1 RSA CRYPTOGRAPHY STANDARD (RSA Laboratories, June 14, 2002)
* Copyright (C) Ellis Pritchard, Guardian Unlimited 2003.
* Contact: ellis@nukinetics.com
* Distributed under the BSD License.
*
* Official documentation: http://www.rsa.com/rsalabs/node.asp?id=2125
*
* @author Evan Jones (http://evanjones.ca/)
* @author Dave Longley
*
* Copyright (c) 2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// shortcut for PKCS#1 API
var pkcs1 = forge.pkcs1 = forge.pkcs1 || {};
/**
* Encode the given RSAES-OAEP message (M) using key, with optional label (L)
* and seed.
*
* This method does not perform RSA encryption, it only encodes the message
* using RSAES-OAEP.
*
* @param key the RSA key to use.
* @param message the message to encode.
* @param options the options to use:
* label an optional label to use.
* seed the seed to use.
* md the message digest object to use, undefined for SHA-1.
*
* @return the encoded message bytes.
*/
pkcs1.encode_rsa_oaep = function(key, message, options) {
// parse arguments
var label = undefined;
var seed = undefined;
var md = undefined;
// legacy args (label, seed, md)
if(typeof options === 'string') {
label = options;
seed = arguments[3] || undefined;
md = arguments[4] || undefined;
}
else if(options) {
label = options.label || undefined;
seed = options.seed || undefined;
md = options.md || undefined;
}
// default to SHA-1 message digest
if(!md) {
md = forge.md.sha1.create();
}
else {
md.start();
}
// compute length in bytes and check output
var keyLength = Math.ceil(key.n.bitLength() / 8);
var maxLength = keyLength - 2 * md.digestLength - 2;
if(message.length > maxLength) {
throw {
message: 'RSAES-OAEP input message length is too long.',
length: message.length,
maxLength: maxLength
};
}
if(!label) {
label = '';
}
md.update(label, 'raw');
var lHash = md.digest();
var PS = '';
var PS_length = maxLength - message.length;
for (var i = 0; i < PS_length; i++) {
PS += '\x00';
}
var DB = lHash.getBytes() + PS + '\x01' + message;
if(!seed) {
seed = forge.random.getBytes(md.digestLength);
}
else if(seed.length !== md.digestLength) {
throw {
message: 'Invalid RSAES-OAEP seed. The seed length must match the ' +
'digest length.',
seedLength: seed.length,
digestLength: md.digestLength
};
}
var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, md);
var maskedDB = forge.util.xorBytes(DB, dbMask, DB.length);
var seedMask = rsa_mgf1(maskedDB, md.digestLength, md);
var maskedSeed = forge.util.xorBytes(seed, seedMask, seed.length);
// return encoded message
return '\x00' + maskedSeed + maskedDB;
};
/**
* Decode the given RSAES-OAEP encoded message (EM) using key, with optional
* label (L).
*
* This method does not perform RSA decryption, it only decodes the message
* using RSAES-OAEP.
*
* @param key the RSA key to use.
* @param em the encoded message to decode.
* @param label an optional label to use.
* @param md the message digest object to use, undefined for SHA-1.
*
* @return the decoded message bytes.
*/
pkcs1.decode_rsa_oaep = function(key, em, options) {
// parse args
var label = undefined;
var md = undefined;
// legacy args
if(typeof options === 'string') {
label = options;
md = arguments[3] || undefined;
}
else if(options) {
label = options.label || undefined;
md = options.md || undefined;
}
// compute length in bytes
var keyLength = Math.ceil(key.n.bitLength() / 8);
if(em.length !== keyLength) {
throw {
message: 'RSAES-OAEP encoded message length is invalid.',
length: em.length,
expectedLength: keyLength
};
}
// default to SHA-1 message digest
if(md === undefined) {
md = forge.md.sha1.create();
}
else {
md.start();
}
if(keyLength < 2 * md.digestLength + 2) {
throw {
message: 'RSAES-OAEP key is too short for the hash function.'
};
}
if(!label) {
label = '';
}
md.update(label, 'raw');
var lHash = md.digest().getBytes();
// split the message into its parts
var y = em.charAt(0);
var maskedSeed = em.substring(1, md.digestLength + 1);
var maskedDB = em.substring(1 + md.digestLength);
var seedMask = rsa_mgf1(maskedDB, md.digestLength, md);
var seed = forge.util.xorBytes(maskedSeed, seedMask, maskedSeed.length);
var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, md);
var db = forge.util.xorBytes(maskedDB, dbMask, maskedDB.length);
var lHashPrime = db.substring(0, md.digestLength);
// constant time check that all values match what is expected
var error = (y !== '\x00');
// constant time check lHash vs lHashPrime
for(var i = 0; i < md.digestLength; ++i) {
error |= (lHash.charAt(i) !== lHashPrime.charAt(i));
}
// "constant time" find the 0x1 byte separating the padding (zeros) from the
// message
// TODO: It must be possible to do this in a better/smarter way?
var in_ps = 1;
var index = md.digestLength;
for(var j = md.digestLength; j < db.length; j++) {
var code = db.charCodeAt(j);
var is_0 = (code & 0x1) ^ 0x1;
// non-zero if not 0 or 1 in the ps section
var error_mask = in_ps ? 0xfffe : 0x0000;
error |= (code & error_mask);
// latch in_ps to zero after we find 0x1
in_ps = in_ps & is_0;
index += in_ps;
}
if(error || db.charCodeAt(index) !== 0x1) {
throw {
message: 'Invalid RSAES-OAEP padding.'
};
}
return db.substring(index + 1);
};
function rsa_mgf1(seed, maskLength, hash) {
var t = '';
var count = Math.ceil(maskLength / hash.digestLength);
for(var i = 0; i < count; ++i) {
var c = String.fromCharCode(
(i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
hash.start();
hash.update(seed + c);
t += hash.digest().getBytes();
}
return t.substring(0, maskLength);
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pkcs1';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util', './random', './sha1'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -265,11 +265,12 @@ var certBagValidator = {
*
* The search can optionally be narrowed by a certain bag type.
*
* @param safeContents The SafeContents structure to search in.
* @param attrName The name of the attribute to compare against.
* @param attrValue The attribute value to search for.
* @param bagType Optional bag type to narrow search by.
* @return Array of matching bags
* @param safeContents the SafeContents structure to search in.
* @param attrName the name of the attribute to compare against.
* @param attrValue the attribute value to search for.
* @param [bagType] bag type to narrow search by.
*
* @return an array of matching bags.
*/
function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) {
var result = [];
@ -295,11 +296,21 @@ function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) {
* Converts a PKCS#12 PFX in ASN.1 notation into a PFX object.
*
* @param obj The PKCS#12 PFX in ASN.1 notation.
* @param {String} password Password to decrypt with (optional)
* @param strict true to use strict DER decoding, false not to (default: true).
* @param {String} password Password to decrypt with (optional).
*
* @return PKCS#12 PFX object.
*/
p12.pkcs12FromAsn1 = function(obj, password) {
p12.pkcs12FromAsn1 = function(obj, strict, password) {
// handle args
if(typeof strict === 'string') {
password = strict;
strict = true;
}
else if(strict === undefined) {
strict = true;
}
// validate PFX and capture data
var capture = {};
var errors = [];
@ -316,27 +327,69 @@ p12.pkcs12FromAsn1 = function(obj, password) {
safeContents: [],
/**
* Get bags with matching friendlyName attribute
* Gets bags with matching attributes.
*
* @param friendlyName The friendly name to search for
* @param bagType Optional bag type to narrow search by
* @return Array of bags with matching friendlyName attribute
* @param filter the attributes to filter by:
* [localKeyId] the localKeyId to search for.
* [localKeyIdHex] the localKeyId in hex to search for.
* [friendlyName] the friendly name to search for.
* [bagType] bag type to narrow each attribute search by.
*
* @return a map of attribute type to an array of matching bags.
*/
getBagsByFriendlyName: function(friendlyName, bagType) {
return _getBagsByAttribute(pfx.safeContents, 'friendlyName',
friendlyName, bagType);
getBags: function(filter) {
var rval = {};
var localKeyId;
if('localKeyId' in filter) {
localKeyId = filter.localKeyId;
}
else if('localKeyIdHex' in filter) {
localKeyId = forge.util.hexToBytes(filter.localKeyIdHex);
}
if(localKeyId !== undefined) {
rval.localKeyId = _getBagsByAttribute(
pfx.safeContents, 'localKeyId',
localKeyId, filter.bagType);
}
if('friendlyName' in filter) {
rval.friendlyName = _getBagsByAttribute(
pfx.safeContents, 'friendlyName',
filter.friendlyName, filter.bagType);
}
return rval;
},
/**
* Get bags with matching localKeyId attribute
* DEPRECATED: use getBags() instead.
*
* @param localKeyId The localKeyId name to search for
* @param bagType Optional bag type to narrow search by
* @return Array of bags with matching localKeyId attribute
* Get bags with matching friendlyName attribute.
*
* @param friendlyName the friendly name to search for.
* @param [bagType] bag type to narrow search by.
*
* @return an array of bags with matching friendlyName attribute.
*/
getBagsByFriendlyName: function(friendlyName, bagType) {
return _getBagsByAttribute(
pfx.safeContents, 'friendlyName', friendlyName, bagType);
},
/**
* DEPRECATED: use getBags() instead.
*
* Get bags with matching localKeyId attribute.
*
* @param localKeyId the localKeyId to search for.
* @param [bagType] bag type to narrow search by.
*
* @return an array of bags with matching localKeyId attribute.
*/
getBagsByLocalKeyId: function(localKeyId, bagType) {
return _getBagsByAttribute(pfx.safeContents, 'localKeyId',
localKeyId, bagType);
return _getBagsByAttribute(
pfx.safeContents, 'localKeyId', localKeyId, bagType);
}
};
@ -412,7 +465,7 @@ p12.pkcs12FromAsn1 = function(obj, password) {
}
}
_decodeAuthenticatedSafe(pfx, data.value, password);
_decodeAuthenticatedSafe(pfx, data.value, strict, password);
return pfx;
};
@ -422,12 +475,12 @@ p12.pkcs12FromAsn1 = function(obj, password) {
* The AuthenticatedSafe is a BER-encoded SEQUENCE OF ContentInfo.
*
* @param pfx The PKCS#12 PFX object to fill.
* @param {String} authSafe BER-encoded AuthenticatedSafe
* @param {String} password Password to decrypt with (optional)
* @return void
* @param {String} authSafe BER-encoded AuthenticatedSafe.
* @param strict true to use strict DER decoding, false not to.
* @param {String} password Password to decrypt with (optional).
*/
function _decodeAuthenticatedSafe(pfx, authSafe, password) {
authSafe = asn1.fromDer(authSafe); /* actually it's BER encoded */
function _decodeAuthenticatedSafe(pfx, authSafe, strict, password) {
authSafe = asn1.fromDer(authSafe, strict); /* actually it's BER encoded */
if(authSafe.tagClass !== asn1.Class.UNIVERSAL ||
authSafe.type !== asn1.Type.SEQUENCE ||
@ -485,7 +538,7 @@ function _decodeAuthenticatedSafe(pfx, authSafe, password) {
};
}
obj.safeBags = _decodeSafeContents(safeContents, password);
obj.safeBags = _decodeSafeContents(safeContents, strict, password);
pfx.safeContents.push(obj);
}
}
@ -537,12 +590,15 @@ function _decryptSafeContents(data, password) {
*
* The safeContents is a BER-encoded SEQUENCE OF SafeBag
*
* @param {String} safeContents BER-encoded safeContents
* @param {String} password Password to decrypt with (optional)
* @param {String} safeContents BER-encoded safeContents.
* @param strict true to use strict DER decoding, false not to.
* @param {String} password Password to decrypt with (optional).
*
* @return {Array} Array of Bag objects.
*/
function _decodeSafeContents(safeContents, password) {
safeContents = asn1.fromDer(safeContents); /* actually it's BER-encoded. */
function _decodeSafeContents(safeContents, strict, password) {
// actually it's BER-encoded
safeContents = asn1.fromDer(safeContents, strict);
if(safeContents.tagClass !== asn1.Class.UNIVERSAL ||
safeContents.type !== asn1.Type.SEQUENCE ||
@ -603,7 +659,7 @@ function _decodeSafeContents(safeContents, password) {
continue; /* Nothing more to do. */
case pki.oids.certBag:
/* A PkCS#12 certBag can wrap both X.509 and sdsi certificates.
/* A PKCS#12 certBag can wrap both X.509 and sdsi certificates.
Therefore put the SafeBag content through another validator to
capture the fields. Afterwards check & store the results. */
validator = certBagValidator;
@ -615,8 +671,9 @@ function _decodeSafeContents(safeContents, password) {
};
}
// true=produce cert hash
bag.cert = pki.certificateFromAsn1(
asn1.fromDer(capture.cert), true);
asn1.fromDer(capture.cert, strict), true);
};
break;
@ -644,16 +701,17 @@ function _decodeSafeContents(safeContents, password) {
}
/**
* Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object
* Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object.
*
* @param attributes SET OF PKCS12Attribute (ASN.1 object)
* @return the decoded attributes
* @param attributes SET OF PKCS12Attribute (ASN.1 object).
*
* @return the decoded attributes.
*/
function _decodeBagAttributes(attributes) {
var decodedAttrs = {};
if(attributes !== undefined) {
for(var i = 0; i < attributes.length; i ++) {
for(var i = 0; i < attributes.length; ++i) {
var capture = {};
var errors = [];
if(!asn1.validate(attributes[i], attributeValidator, capture, errors)) {
@ -670,7 +728,7 @@ function _decodeBagAttributes(attributes) {
}
decodedAttrs[pki.oids[oid]] = [];
for(var j = 0; j < capture.values.length; j ++) {
for(var j = 0; j < capture.values.length; ++j) {
decodedAttrs[pki.oids[oid]].push(capture.values[j].value);
}
}
@ -696,12 +754,13 @@ function _decodeBagAttributes(attributes) {
* to specify a certificate chain).
* @param password the password to use.
* @param options:
* encAlgorithm the encryption algorithm to use
* algorithm the encryption algorithm to use
* ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
* count the iteration count to use.
* saltSize the salt size to use.
* useMac true to include a MAC, false not to, defaults to true.
* localKeyId the local key ID to use, in hex.
* friendlyName the friendly name to use.
* generateLocalKeyId true to generate a random local key ID,
* false not to, defaults to true.
*
@ -712,7 +771,7 @@ p12.toPkcs12Asn1 = function(key, cert, password, options) {
options = options || {};
options.saltSize = options.saltSize || 8;
options.count = options.count || 2048;
options.encAlgorithm = options.encAlgorithm || 'aes128';
options.algorithm = options.algorithm || options.encAlgorithm || 'aes128';
if(!('useMac' in options)) {
options.useMac = true;
}
@ -729,12 +788,27 @@ p12.toPkcs12Asn1 = function(key, cert, password, options) {
localKeyId = forge.util.hexToBytes(localKeyId);
}
else if(options.generateLocalKeyId) {
// set localKeyId and friendlyName (if specified)
// use SHA-1 of paired cert, if available
if(cert) {
var pairedCert = forge.util.isArray(cert) ? cert[0] : cert;
if(typeof pairedCert === 'string') {
pairedCert = pki.certificateFromPem(pairedCert);
}
var sha1 = forge.md.sha1.create();
sha1.update(asn1.toDer(pki.certificateToAsn1(pairedCert)).getBytes());
localKeyId = sha1.digest().getBytes();
}
// FIXME: consider using SHA-1 of public key (which can be generated
// from private key components)
// generate random bytes
else {
localKeyId = forge.random.getBytes(20);
}
}
var attrs = [];
if(localKeyId !== null) {
var attrs = [
attrs.push(
// localKeyID
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// attrId
@ -745,76 +819,34 @@ p12.toPkcs12Asn1 = function(key, cert, password, options) {
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
localKeyId)
])
]));
}
if('friendlyName' in options) {
attrs.push(
// friendlyName
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// attrId
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['friendlyName']).getBytes()),
// attrValues
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BMPSTRING, false,
options.friendlyName)
])
];
]));
}
if(attrs.length > 0) {
bagAttrs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs);
}
// collect contents for AuthenticatedSafe
var contents = [];
// create safe contents for private key
var keyBag = null;
if(key !== null) {
// SafeBag
var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key));
if(password === null) {
// no encryption
keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// bagId
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['keyBag']).getBytes()),
// bagValue
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
// PrivateKeyInfo
pkAsn1
]),
// bagAttributes (OPTIONAL)
bagAttrs
]);
}
else {
// encrypted PrivateKeyInfo
keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// bagId
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['pkcs8ShroudedKeyBag']).getBytes()),
// bagValue
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
// EncryptedPrivateKeyInfo
pki.encryptPrivateKeyInfo(pkAsn1, password, options)
]),
// bagAttributes (OPTIONAL)
bagAttrs
]);
}
// SafeContents
var keySafeContents =
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]);
// ContentInfo
var keyCI =
// PKCS#7 ContentInfo
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// contentType
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
// OID for the content type is 'data'
asn1.oidToDer(pki.oids['data']).getBytes()),
// content
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
asn1.toDer(keySafeContents).getBytes())
])
]);
contents.push(keyCI);
}
// create safe bag(s) for certificate chain
var chain = [];
if(cert !== null) {
if((Array.isArray && Array.isArray(cert)) || cert.constructor === Array) {
if(forge.util.isArray(cert)) {
chain = cert;
}
else {
@ -880,6 +912,64 @@ p12.toPkcs12Asn1 = function(key, cert, password, options) {
contents.push(certCI);
}
// create safe contents for private key
var keyBag = null;
if(key !== null) {
// SafeBag
var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key));
if(password === null) {
// no encryption
keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// bagId
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['keyBag']).getBytes()),
// bagValue
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
// PrivateKeyInfo
pkAsn1
]),
// bagAttributes (OPTIONAL)
bagAttrs
]);
}
else {
// encrypted PrivateKeyInfo
keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// bagId
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['pkcs8ShroudedKeyBag']).getBytes()),
// bagValue
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
// EncryptedPrivateKeyInfo
pki.encryptPrivateKeyInfo(pkAsn1, password, options)
]),
// bagAttributes (OPTIONAL)
bagAttrs
]);
}
// SafeContents
var keySafeContents =
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]);
// ContentInfo
var keyCI =
// PKCS#7 ContentInfo
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// contentType
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
// OID for the content type is 'data'
asn1.oidToDer(pki.oids['data']).getBytes()),
// content
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
asn1.toDer(keySafeContents).getBytes())
])
]);
contents.push(keyCI);
}
// create AuthenticatedSafe by stringing together the contents
var safe = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, contents);
@ -1059,20 +1149,11 @@ p12.generateKey = function(password, salt, id, iter, n, md) {
/* ########## Begin module wrapper ########## */
var name = 'pkcs12';
var deps = [
'./asn1',
'./sha1',
'./pkcs7asn1',
'./pki',
'./util',
'./random',
'./hmac'
];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -1081,14 +1162,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -1105,6 +1184,28 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./asn1',
'./sha1',
'./pkcs7asn1',
'./pki',
'./util',
'./random',
'./hmac'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -33,8 +33,24 @@ var p7 = forge.pkcs7 = forge.pkcs7 || {};
* @return the PKCS#7 message.
*/
p7.messageFromPem = function(pem) {
var der = forge.pki.pemToDer(pem);
var obj = asn1.fromDer(der);
var msg = forge.pem.decode(pem)[0];
if(msg.type !== 'PKCS7') {
throw {
message: 'Could not convert PKCS#7 message from PEM; PEM header type ' +
'is not "PKCS#7".',
headerType: msg.type
};
}
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
throw {
message: 'Could not convert PKCS#7 message from PEM; PEM is encrypted.'
};
}
// convert DER to ASN.1 object
var obj = asn1.fromDer(msg.body);
return p7.messageFromAsn1(obj);
};
@ -47,12 +63,12 @@ p7.messageFromPem = function(pem) {
* @return The PEM-formatted PKCS#7 message.
*/
p7.messageToPem = function(msg, maxline) {
var out = asn1.toDer(msg.toAsn1());
out = forge.util.encode64(out.getBytes(), maxline || 64);
return (
'-----BEGIN PKCS7-----\r\n' +
out +
'\r\n-----END PKCS7-----');
// convert to ASN.1, then DER, then PEM-encode
var pemObj = {
type: 'PKCS7',
body: asn1.toDer(msg.toAsn1()).getBytes()
};
return forge.pem.encode(pemObj, {maxline: maxline});
};
/**
@ -266,7 +282,7 @@ var _fromAsn1 = function(msg, obj, validator) {
if(capture.encContent) {
var content = '';
if(capture.encContent.constructor === Array) {
if(forge.util.isArray(capture.encContent)) {
for(var i = 0; i < capture.encContent.length; ++i) {
if(capture.encContent[i].type !== asn1.Type.OCTETSTRING) {
throw {
@ -289,7 +305,7 @@ var _fromAsn1 = function(msg, obj, validator) {
if(capture.content) {
var content = '';
if(capture.content.constructor === Array) {
if(forge.util.isArray(capture.content)) {
for(var i = 0; i < capture.content.length; ++i) {
if(capture.content[i].type !== asn1.Type.OCTETSTRING) {
throw {
@ -659,20 +675,11 @@ p7.createEnvelopedData = function() {
/* ########## Begin module wrapper ########## */
var name = 'pkcs7';
var deps = [
'./aes',
'./asn1',
'./des',
'./pkcs7asn1',
'./pki',
'./random',
'./util'
];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -681,14 +688,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -705,6 +710,29 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./aes',
'./asn1',
'./des',
'./pem',
'./pkcs7asn1',
'./pki',
'./random',
'./util'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -346,12 +346,11 @@ p7v.recipientInfoValidator = {
/* ########## Begin module wrapper ########## */
var name = 'pkcs7asn1';
var deps = ['./asn1', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -360,14 +359,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -384,6 +381,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './asn1', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

File diff suppressed because it is too large Load Diff

View File

@ -11,9 +11,10 @@
/* ########## Begin module implementation ########## */
function initModule(forge) {
var _nodejs = (typeof module === 'object' && module.exports);
var _nodejs = (
typeof process !== 'undefined' && process.versions && process.versions.node);
var crypto = null;
if(_nodejs) {
if(!forge.disableNativeCode && _nodejs) {
crypto = require('crypto');
}
@ -393,12 +394,11 @@ prng.create = function(plugin) {
/* ########## Begin module wrapper ########## */
var name = 'prng';
var deps = ['./md', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -407,14 +407,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -431,6 +429,19 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './md', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -203,12 +203,11 @@ pss.create = function(hash, mgf, sLen) {
/* ########## Begin module wrapper ########## */
var name = 'pss';
var deps = ['./random', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -217,14 +216,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -241,6 +238,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './random', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -70,9 +70,10 @@ var _ctx = forge.prng.create(prng_aes);
// add other sources of entropy only if window.crypto.getRandomValues is not
// available -- otherwise this source will be automatically used by the prng
var _nodejs = (typeof module === 'object' && module.exports);
if(!_nodejs && !(typeof window !== 'undefined' &&
window.crypto && window.crypto.getRandomValues)) {
var _nodejs = (
typeof process !== 'undefined' && process.versions && process.versions.node);
if(forge.disableNativeCode || (!_nodejs && !(typeof window !== 'undefined' &&
window.crypto && window.crypto.getRandomValues))) {
// if this is a web worker, do not use weak entropy, instead register to
// receive strong entropy asynchronously from the main thread
@ -168,12 +169,11 @@ forge.random.getBytesSync = function(count) {
/* ########## Begin module wrapper ########## */
var name = 'random';
var deps = ['./aes', './md', './prng', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -182,14 +182,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -206,6 +204,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './aes', './md', './prng', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -74,7 +74,7 @@ forge.rc2 = forge.rc2 || {};
* @return the expanded RC2 key (ByteBuffer of 128 bytes)
*/
forge.rc2.expandKey = function(key, effKeyBits) {
if(key.constructor == String) {
if(typeof key === 'string') {
key = forge.util.createBuffer(key);
}
effKeyBits = effKeyBits || 128;
@ -250,7 +250,7 @@ var createCipher = function(key, bits, encrypt)
start: function(iv, output) {
if(iv) {
/* CBC mode */
if(key.constructor == String && iv.length == 8) {
if(typeof key === 'string' && iv.length === 8) {
iv = forge.util.createBuffer(iv);
}
}
@ -302,7 +302,7 @@ var createCipher = function(key, bits, encrypt)
} else {
// add PKCS#7 padding to block (each pad byte is the
// value of the number of pad bytes)
var padding = (_input.length() == 8) ? 8 : (8 - _input.length());
var padding = (_input.length() === 8) ? 8 : (8 - _input.length());
_input.fillWithByte(padding, padding);
}
}
@ -420,12 +420,11 @@ forge.rc2.createDecryptionCipher = function(key, bits) {
/* ########## Begin module wrapper ########## */
var name = 'rc2';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -434,14 +433,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -458,6 +455,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -1,5 +1,5 @@
/**
* Javascript implementation of a basic RSA algorithms.
* Javascript implementation of basic RSA algorithms.
*
* @author Dave Longley
*
@ -9,8 +9,6 @@
function initModule(forge) {
/* ########## Begin module implementation ########## */
var _nodejs = (typeof module === 'object' && module.exports);
if(typeof BigInteger === 'undefined') {
BigInteger = forge.jsbn.BigInteger;
}
@ -42,6 +40,7 @@ var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
* Digest ::= OCTET STRING
*
* @param md the message digest object with the hash to sign.
*
* @return the encoded message (ready for RSA encrytion)
*/
var emsaPkcs1v15encode = function(md) {
@ -206,6 +205,9 @@ var _modPow = function(x, key, pub) {
};
/**
* NOTE: THIS METHOD IS DEPRECATED, use 'sign' on a private key object or
* 'encrypt' on a public key object instead.
*
* Performs RSA encryption.
*
* The parameter bt controls whether to put padding bytes before the
@ -222,69 +224,26 @@ var _modPow = function(x, key, pub) {
* @param key the RSA key to use.
* @param bt for PKCS#1 v1.5 padding, the block type to use
* (0x01 for private key, 0x02 for public),
* to disable padding: true = public key, false = private key
* to disable padding: true = public key, false = private key.
*
* @return the encrypted bytes as a string.
*/
pki.rsa.encrypt = function(m, key, bt) {
var pub = bt;
var eb = forge.util.createBuffer();
var eb;
// get the length of the modulus in bytes
var k = Math.ceil(key.n.bitLength() / 8);
if(bt !== false && bt !== true) {
/* use PKCS#1 v1.5 padding */
if(m.length > (k - 11)) {
throw {
message: 'Message is too long to encrypt.',
length: m.length,
max: (k - 11)
};
}
/* A block type BT, a padding string PS, and the data D shall be
formatted into an octet string EB, the encryption block:
EB = 00 || BT || PS || 00 || D
The block type BT shall be a single octet indicating the structure of
the encryption block. For this version of the document it shall have
value 00, 01, or 02. For a private-key operation, the block type
shall be 00 or 01. For a public-key operation, it shall be 02.
The padding string PS shall consist of k-3-||D|| octets. For block
type 00, the octets shall have value 00; for block type 01, they
shall have value FF; and for block type 02, they shall be
pseudorandomly generated and nonzero. This makes the length of the
encryption block EB equal to k. */
// build the encryption block
eb.putByte(0x00);
eb.putByte(bt);
// create the padding, get key type
var padNum = k - 3 - m.length;
var padByte;
if(bt === 0x00 || bt === 0x01) {
pub = false;
padByte = (bt === 0x00) ? 0x00 : 0xFF;
for(var i = 0; i < padNum; ++i) {
eb.putByte(padByte);
}
// legacy, default to PKCS#1 v1.5 padding
pub = (bt === 0x02);
eb = _encodePkcs1_v1_5(m, key, bt);
}
else {
pub = true;
for(var i = 0; i < padNum; ++i) {
padByte = Math.floor(Math.random() * 255) + 1;
eb.putByte(padByte);
}
}
// zero followed by message
eb.putByte(0x00);
}
eb = forge.util.createBuffer();
eb.putBytes(m);
}
// load encryption block as big integer 'x'
// FIXME: hex conversion inefficient, get BigInteger w/byte strings
@ -308,6 +267,9 @@ pki.rsa.encrypt = function(m, key, bt) {
};
/**
* NOTE: THIS METHOD IS DEPRECATED, use 'decrypt' on a private key object or
* 'verify' on a public key object instead.
*
* Performs RSA decryption.
*
* The parameter ml controls whether to apply PKCS#1 v1.5 padding
@ -318,7 +280,7 @@ pki.rsa.encrypt = function(m, key, bt) {
* @param ed the encrypted data to decrypt in as a byte string.
* @param key the RSA key to use.
* @param pub true for a public key operation, false for private.
* @param ml the message length, if known. false to disable padding.
* @param ml the message length, if known, false to disable padding.
*
* @return the decrypted message as a byte string.
*/
@ -327,7 +289,7 @@ pki.rsa.decrypt = function(ed, key, pub, ml) {
var k = Math.ceil(key.n.bitLength() / 8);
// error if the length of the encrypted data ED is not k
if(ed.length != k) {
if(ed.length !== k) {
throw {
message: 'Encrypted message length is invalid.',
length: ed.length,
@ -339,6 +301,14 @@ pki.rsa.decrypt = function(ed, key, pub, ml) {
// FIXME: hex conversion inefficient, get BigInteger w/byte strings
var y = new BigInteger(forge.util.createBuffer(ed).toHex(), 16);
// y must be less than the modulus or it wasn't the result of
// a previous mod operation (encryption) using that modulus
if(y.compareTo(key.n) >= 0) {
throw {
message: 'Encrypted message is invalid.'
};
}
// do RSA decryption
var x = _modPow(y, key, pub);
@ -355,70 +325,8 @@ pki.rsa.decrypt = function(ed, key, pub, ml) {
eb.putBytes(forge.util.hexToBytes(xhex));
if(ml !== false) {
/* It is an error if any of the following conditions occurs:
1. The encryption block EB cannot be parsed unambiguously.
2. The padding string PS consists of fewer than eight octets
or is inconsisent with the block type BT.
3. The decryption process is a public-key operation and the block
type BT is not 00 or 01, or the decryption process is a
private-key operation and the block type is not 02.
*/
// parse the encryption block
var first = eb.getByte();
var bt = eb.getByte();
if(first !== 0x00 ||
(pub && bt !== 0x00 && bt !== 0x01) ||
(!pub && bt != 0x02) ||
(pub && bt === 0x00 && typeof(ml) === 'undefined')) {
throw {
message: 'Encryption block is invalid.'
};
}
var padNum = 0;
if(bt === 0x00) {
// check all padding bytes for 0x00
padNum = k - 3 - ml;
for(var i = 0; i < padNum; ++i) {
if(eb.getByte() !== 0x00) {
throw {
message: 'Encryption block is invalid.'
};
}
}
}
else if(bt === 0x01) {
// find the first byte that isn't 0xFF, should be after all padding
padNum = 0;
while(eb.length() > 1) {
if(eb.getByte() !== 0xFF) {
--eb.read;
break;
}
++padNum;
}
}
else if(bt === 0x02) {
// look for 0x00 byte
padNum = 0;
while(eb.length() > 1) {
if(eb.getByte() === 0x00) {
--eb.read;
break;
}
++padNum;
}
}
// zero must be 0x00 and padNum must be (k - 3 - message length)
var zero = eb.getByte();
if(zero !== 0x00 || padNum !== (k - 3 - eb.length())) {
throw {
message: 'Encryption block is invalid.'
};
}
// legacy, default to PKCS#1 v1.5 padding
return _decodePkcs1_v1_5(eb.getBytes(), key, pub);
}
// return message
@ -749,24 +657,63 @@ pki.rsa.setPublicKey = function(n, e) {
};
/**
* Encrypts the given data with this public key.
* Encrypts the given data with this public key. Newer applications
* should use the 'RSA-OAEP' decryption scheme, 'RSAES-PKCS1-V1_5' is for
* legacy applications.
*
* @param data the byte string to encrypt.
* @param scheme the encryption scheme to use:
* 'RSAES-PKCS1-V1_5' (default),
* 'RSA-OAEP',
* 'RAW', 'NONE', or null to perform raw RSA encryption.
* @param schemeOptions any scheme-specific options.
*
* @return the encrypted byte string.
*/
key.encrypt = function(data) {
return pki.rsa.encrypt(data, key, 0x02);
key.encrypt = function(data, scheme, schemeOptions) {
if(typeof scheme === 'string') {
scheme = scheme.toUpperCase();
}
else if(scheme === undefined) {
scheme = 'RSAES-PKCS1-V1_5';
}
if(scheme === 'RSAES-PKCS1-V1_5') {
scheme = {
encode: function(m, key, pub) {
return _encodePkcs1_v1_5(m, key, 0x02).getBytes();
}
};
}
else if(scheme === 'RSA-OAEP' || scheme === 'RSAES-OAEP') {
scheme = {
encode: function(m, key) {
return forge.pkcs1.encode_rsa_oaep(key, m, schemeOptions);
}
};
}
else if(['RAW', 'NONE', 'NULL', null].indexOf(scheme) !== -1) {
scheme = { encode: function(e) { return e; } };
}
else {
throw {
message: 'Unsupported encryption scheme: "' + scheme + '".'
};
}
// do scheme-based encoding then rsa encryption
var e = scheme.encode(data, key, true);
return pki.rsa.encrypt(e, key, true);
};
/**
* Verifies the given signature against the given digest.
*
* PKCS#1 supports multiple (currently two) signature schemes:
* RSASSA-PKCS1-v1_5 and RSASSA-PSS.
* RSASSA-PKCS1-V1_5 and RSASSA-PSS.
*
* By default this implementation uses the "old scheme", i.e.
* RSASSA-PKCS1-v1_5, in which case once RSA-decrypted, the
* RSASSA-PKCS1-V1_5, in which case once RSA-decrypted, the
* signature is an OCTET STRING that holds a DigestInfo.
*
* DigestInfo ::= SEQUENCE {
@ -777,29 +724,51 @@ pki.rsa.setPublicKey = function(n, e) {
* Digest ::= OCTET STRING
*
* To perform PSS signature verification, provide an instance
* of Forge PSS object as scheme parameter.
* of Forge PSS object as the scheme parameter.
*
* @param digest the message digest hash to compare against the signature.
* @param signature the signature to verify.
* @param scheme signature scheme to use, undefined for PKCS#1 v1.5
* padding style.
* @param scheme signature verification scheme to use:
* 'RSASSA-PKCS1-V1_5' or undefined for RSASSA PKCS#1 v1.5,
* a Forge PSS object for RSASSA-PSS,
* 'NONE' or null for none, DigestInfo will not be expected, but
* PKCS#1 v1.5 padding will still be used.
*
* @return true if the signature was verified, false if not.
*/
key.verify = function(digest, signature, scheme) {
// do rsa decryption
var ml = scheme === undefined ? undefined : false;
var d = pki.rsa.decrypt(signature, key, true, ml);
if(typeof scheme === 'string') {
scheme = scheme.toUpperCase();
}
else if(scheme === undefined) {
scheme = 'RSASSA-PKCS1-V1_5';
}
if(scheme === undefined) {
if(scheme === 'RSASSA-PKCS1-V1_5') {
scheme = {
verify: function(digest, d) {
// remove padding
d = _decodePkcs1_v1_5(d, key, true);
// d is ASN.1 BER-encoded DigestInfo
var obj = asn1.fromDer(d);
// compare the given digest to the decrypted one
return digest === obj.value[1].value;
}
else {
return scheme.verify(digest, d, key.n.bitLength());
};
}
else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) {
scheme = {
verify: function(digest, d) {
// remove padding
d = _decodePkcs1_v1_5(d, key, true);
return digest === d;
}
};
}
// do rsa decryption w/o any decoding, then verify -- which does decoding
var d = pki.rsa.decrypt(signature, key, true, false);
return scheme.verify(digest, d, key.n.bitLength());
};
return key;
@ -833,39 +802,94 @@ pki.rsa.setPrivateKey = function(n, e, d, p, q, dP, dQ, qInv) {
};
/**
* Decrypts the given data with this private key.
* Decrypts the given data with this private key. The decryption scheme
* must match the one used to encrypt the data.
*
* @param data the byte string to decrypt.
* @param scheme the decryption scheme to use:
* 'RSAES-PKCS1-V1_5' (default),
* 'RSA-OAEP',
* 'RAW', 'NONE', or null to perform raw RSA decryption.
* @param schemeOptions any scheme-specific options.
*
* @return the decrypted byte string.
*/
key.decrypt = function(data) {
return pki.rsa.decrypt(data, key, false);
key.decrypt = function(data, scheme, schemeOptions) {
if(typeof scheme === 'string') {
scheme = scheme.toUpperCase();
}
else if(scheme === undefined) {
scheme = 'RSAES-PKCS1-V1_5';
}
// do rsa decryption w/o any decoding
var d = pki.rsa.decrypt(data, key, false, false);
if(scheme === 'RSAES-PKCS1-V1_5') {
scheme = { decode: _decodePkcs1_v1_5 };
}
else if(scheme === 'RSA-OAEP' || scheme === 'RSAES-OAEP') {
scheme = {
decode: function(d, key) {
return forge.pkcs1.decode_rsa_oaep(key, d, schemeOptions);
}
};
}
else if(['RAW', 'NONE', 'NULL', null].indexOf(scheme) !== -1) {
scheme = { decode: function(d) { return d; } };
}
else {
throw {
message: 'Unsupported encryption scheme: "' + scheme + '".'
};
}
// decode according to scheme
return scheme.decode(d, key, false);
};
/**
* Signs the given digest, producing a signature.
*
* PKCS#1 supports multiple (currently two) signature schemes:
* RSASSA-PKCS1-v1_5 and RSASSA-PSS.
* RSASSA-PKCS1-V1_5 and RSASSA-PSS.
*
* By default this implementation uses the "old scheme", i.e.
* RSASSA-PKCS1-v1_5. In order to generate a PSS signature, provide
* an instance of Forge PSS object as scheme parameter.
* RSASSA-PKCS1-V1_5. In order to generate a PSS signature, provide
* an instance of Forge PSS object as the scheme parameter.
*
* @param md the message digest object with the hash to sign.
* @param scheme signature scheme to use, undefined for PKCS#1 v1.5
* padding style.
* @param scheme the signature scheme to use:
* 'RSASSA-PKCS1-V1_5' or undefined for RSASSA PKCS#1 v1.5,
* a Forge PSS object for RSASSA-PSS,
* 'NONE' or null for none, DigestInfo will not be used but
* PKCS#1 v1.5 padding will still be used.
*
* @return the signature as a byte string.
*/
key.sign = function(md, scheme) {
var bt = false; /* private key operation */
/* Note: The internal implementation of RSA operations is being
transitioned away from a PKCS#1 v1.5 hard-coded scheme. Some legacy
code like the use of an encoding block identifier 'bt' will eventually
be removed. */
if(scheme === undefined) {
// private key operation
var bt = false;
if(typeof scheme === 'string') {
scheme = scheme.toUpperCase();
}
if(scheme === undefined || scheme === 'RSASSA-PKCS1-V1_5') {
scheme = { encode: emsaPkcs1v15encode };
bt = 0x01;
}
else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) {
scheme = { encode: function() { return md; } };
bt = 0x01;
}
// encode and then encrypt
var d = scheme.encode(md, key.n.bitLength());
return pki.rsa.encrypt(d, key, bt);
};
@ -873,6 +897,159 @@ pki.rsa.setPrivateKey = function(n, e, d, p, q, dP, dQ, qInv) {
return key;
};
/**
* Encodes a message using PKCS#1 v1.5 padding.
*
* @param m the message to encode.
* @param key the RSA key to use.
* @param bt the block type to use, i.e. either 0x01 (for signing) or 0x02
* (for encryption).
*
* @return the padded byte buffer.
*/
function _encodePkcs1_v1_5(m, key, bt) {
var eb = forge.util.createBuffer();
// get the length of the modulus in bytes
var k = Math.ceil(key.n.bitLength() / 8);
/* use PKCS#1 v1.5 padding */
if(m.length > (k - 11)) {
throw {
message: 'Message is too long for PKCS#1 v1.5 padding.',
length: m.length,
max: (k - 11)
};
}
/* A block type BT, a padding string PS, and the data D shall be
formatted into an octet string EB, the encryption block:
EB = 00 || BT || PS || 00 || D
The block type BT shall be a single octet indicating the structure of
the encryption block. For this version of the document it shall have
value 00, 01, or 02. For a private-key operation, the block type
shall be 00 or 01. For a public-key operation, it shall be 02.
The padding string PS shall consist of k-3-||D|| octets. For block
type 00, the octets shall have value 00; for block type 01, they
shall have value FF; and for block type 02, they shall be
pseudorandomly generated and nonzero. This makes the length of the
encryption block EB equal to k. */
// build the encryption block
eb.putByte(0x00);
eb.putByte(bt);
// create the padding
var padNum = k - 3 - m.length;
var padByte;
// private key op
if(bt === 0x00 || bt === 0x01) {
padByte = (bt === 0x00) ? 0x00 : 0xFF;
for(var i = 0; i < padNum; ++i) {
eb.putByte(padByte);
}
}
// public key op
else {
for(var i = 0; i < padNum; ++i) {
padByte = Math.floor(Math.random() * 255) + 1;
eb.putByte(padByte);
}
}
// zero followed by message
eb.putByte(0x00);
eb.putBytes(m);
return eb;
}
/**
* Decodes a message using PKCS#1 v1.5 padding.
*
* @param em the message to decode.
* @param key the RSA key to use.
* @param pub true if the key is a public key, false if it is private.
* @param ml the message length, if specified.
*
* @return the decoded bytes.
*/
function _decodePkcs1_v1_5(em, key, pub, ml) {
// get the length of the modulus in bytes
var k = Math.ceil(key.n.bitLength() / 8);
/* It is an error if any of the following conditions occurs:
1. The encryption block EB cannot be parsed unambiguously.
2. The padding string PS consists of fewer than eight octets
or is inconsisent with the block type BT.
3. The decryption process is a public-key operation and the block
type BT is not 00 or 01, or the decryption process is a
private-key operation and the block type is not 02.
*/
// parse the encryption block
var eb = forge.util.createBuffer(em);
var first = eb.getByte();
var bt = eb.getByte();
if(first !== 0x00 ||
(pub && bt !== 0x00 && bt !== 0x01) ||
(!pub && bt != 0x02) ||
(pub && bt === 0x00 && typeof(ml) === 'undefined')) {
throw {
message: 'Encryption block is invalid.'
};
}
var padNum = 0;
if(bt === 0x00) {
// check all padding bytes for 0x00
padNum = k - 3 - ml;
for(var i = 0; i < padNum; ++i) {
if(eb.getByte() !== 0x00) {
throw {
message: 'Encryption block is invalid.'
};
}
}
}
else if(bt === 0x01) {
// find the first byte that isn't 0xFF, should be after all padding
padNum = 0;
while(eb.length() > 1) {
if(eb.getByte() !== 0xFF) {
--eb.read;
break;
}
++padNum;
}
}
else if(bt === 0x02) {
// look for 0x00 byte
padNum = 0;
while(eb.length() > 1) {
if(eb.getByte() === 0x00) {
--eb.read;
break;
}
++padNum;
}
}
// zero must be 0x00 and padNum must be (k - 3 - message length)
var zero = eb.getByte();
if(zero !== 0x00 || padNum !== (k - 3 - eb.length())) {
throw {
message: 'Encryption block is invalid.'
};
}
return eb.getBytes();
}
/**
* Runs the key-generation algorithm asynchronously, either in the background
* via Web Workers, or using the main thread and setImmediate.
@ -1064,12 +1241,11 @@ function _generateKeyPair(state, options, callback) {
/* ########## Begin module wrapper ########## */
var name = 'rsa';
var deps = ['./asn1', './oids', './random', './util', './jsbn'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -1078,14 +1254,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -1102,6 +1276,27 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./asn1',
'./oids',
'./random',
'./util',
'./jsbn',
'./pkcs1'
], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -174,6 +174,8 @@ sha1.create = function() {
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
md.messageLength = 0;
@ -185,6 +187,7 @@ sha1.create = function() {
h3: 0x10325476,
h4: 0xC3D2E1F0
};
return md;
};
// start digest automatically for first time
md.start();
@ -196,6 +199,8 @@ sha1.create = function() {
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
@ -215,6 +220,8 @@ sha1.create = function() {
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
@ -280,12 +287,11 @@ sha1.create = function() {
/* ########## Begin module wrapper ########## */
var name = 'sha1';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -294,14 +300,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -318,6 +322,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -175,6 +175,8 @@ sha256.create = function() {
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
md.messageLength = 0;
@ -189,6 +191,7 @@ sha256.create = function() {
h6: 0x1F83D9AB,
h7: 0x5BE0CD19
};
return md;
};
// start digest automatically for first time
md.start();
@ -200,6 +203,8 @@ sha256.create = function() {
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
@ -219,6 +224,8 @@ sha256.create = function() {
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
@ -290,12 +297,11 @@ sha256.create = function() {
/* ########## Begin module wrapper ########## */
var name = 'sha256';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -304,14 +310,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -328,6 +332,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -292,12 +292,11 @@ net.createSocket = function(options) {
/* ########## Begin module wrapper ########## */
var name = 'net';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -306,14 +305,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -330,6 +327,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -258,7 +258,7 @@ Task.prototype.next = function(name, subrun) {
*/
Task.prototype.parallel = function(name, subrun) {
// juggle parameters if it looks like no name is given
if(name.constructor == Array) {
if(forge.util.isArray(name)) {
subrun = name;
// inherit parent's name
@ -738,12 +738,11 @@ forge.task.createCondition = function() {
/* ########## Begin module wrapper ########## */
var name = 'task';
var deps = ['./debug', './log', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -752,14 +751,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -776,6 +773,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './debug', './log', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -439,182 +439,6 @@ var inflate = function(c, record, s) {
return rval;
};
/**
* Encrypts the TLSCompressed record into a TLSCipherText record using AES-128
* in CBC mode.
*
* @param record the TLSCompressed record to encrypt.
* @param s the ConnectionState to use.
*
* @return true on success, false on failure.
*/
var encrypt_aes_128_cbc_sha1 = function(record, s) {
var rval = false;
// append MAC to fragment, update sequence number
var mac = s.macFunction(s.macKey, s.sequenceNumber, record);
record.fragment.putBytes(mac);
s.updateSequenceNumber();
// TODO: TLS 1.1 & 1.2 use an explicit IV every time to protect against
// CBC attacks
// var iv = forge.random.getBytes(16);
// use the pre-generated IV when initializing for TLS 1.0, otherwise use the
// residue from the previous encryption
var iv = s.cipherState.init ? null : s.cipherState.iv;
s.cipherState.init = true;
// start cipher
var cipher = s.cipherState.cipher;
cipher.start(iv);
// TODO: TLS 1.1 & 1.2 write IV into output
//cipher.output.putBytes(iv);
// do encryption (default padding is appropriate)
cipher.update(record.fragment);
if(cipher.finish(encrypt_aes_128_cbc_sha1_padding)) {
// set record fragment to encrypted output
record.fragment = cipher.output;
record.length = record.fragment.length();
rval = true;
}
return rval;
};
/**
* Handles padding for aes_128_cbc_sha1 in encrypt mode.
*
* @param blockSize the block size.
* @param input the input buffer.
* @param decrypt true in decrypt mode, false in encrypt mode.
*
* @return true on success, false on failure.
*/
var encrypt_aes_128_cbc_sha1_padding = function(blockSize, input, decrypt) {
/* The encrypted data length (TLSCiphertext.length) is one more than the sum
of SecurityParameters.block_length, TLSCompressed.length,
SecurityParameters.mac_length, and padding_length.
The padding may be any length up to 255 bytes long, as long as it results in
the TLSCiphertext.length being an integral multiple of the block length.
Lengths longer than necessary might be desirable to frustrate attacks on a
protocol based on analysis of the lengths of exchanged messages. Each uint8
in the padding data vector must be filled with the padding length value.
The padding length should be such that the total size of the
GenericBlockCipher structure is a multiple of the cipher's block length.
Legal values range from zero to 255, inclusive. This length specifies the
length of the padding field exclusive of the padding_length field itself.
This is slightly different from PKCS#7 because the padding value is 1
less than the actual number of padding bytes if you include the
padding_length uint8 itself as a padding byte. */
if(!decrypt) {
// get the number of padding bytes required to reach the blockSize and
// subtract 1 to make room for the padding_length uint8 but add it during
// fillWithByte
var padding = (input.length() == blockSize) ?
(blockSize - 1) : (blockSize - input.length() - 1);
input.fillWithByte(padding, padding + 1);
}
return true;
};
/**
* Handles padding for aes_128_cbc_sha1 in decrypt mode.
*
* @param blockSize the block size.
* @param output the output buffer.
* @param decrypt true in decrypt mode, false in encrypt mode.
*
* @return true on success, false on failure.
*/
var decrypt_aes_128_cbc_sha1_padding = function(blockSize, output, decrypt) {
var rval = true;
if(decrypt) {
/* The last byte in the output specifies the number of padding bytes not
including itself. Each of the padding bytes has the same value as that
last byte (known as the padding_length). Here we check all padding
bytes to ensure they have the value of padding_length even if one of
them is bad in order to ward-off timing attacks. */
var len = output.length();
var paddingLength = output.last();
for(var i = len - 1 - paddingLength; i < len - 1; ++i) {
rval = rval && (output.at(i) == paddingLength);
}
if(rval) {
// trim off padding bytes and last padding length byte
output.truncate(paddingLength + 1);
}
}
return rval;
};
/**
* Decrypts a TLSCipherText record into a TLSCompressed record using
* AES-128 in CBC mode.
*
* @param record the TLSCipherText record to decrypt.
* @param s the ConnectionState to use.
*
* @return true on success, false on failure.
*/
var decrypt_aes_128_cbc_sha1 = function(record, s) {
var rval = false;
// TODO: TLS 1.1 & 1.2 use an explicit IV every time to protect against
// CBC attacks
//var iv = record.fragment.getBytes(16);
// use pre-generated IV when initializing for TLS 1.0, otherwise use the
// residue from the previous decryption
var iv = s.cipherState.init ? null : s.cipherState.iv;
s.cipherState.init = true;
// start cipher
var cipher = s.cipherState.cipher;
cipher.start(iv);
// do decryption
cipher.update(record.fragment);
rval = cipher.finish(decrypt_aes_128_cbc_sha1_padding);
// even if decryption fails, keep going to minimize timing attacks
// decrypted data:
// first (len - 20) bytes = application data
// last 20 bytes = MAC
var macLen = s.macLength;
// create a zero'd out mac
var mac = '';
for(var i = 0; i < macLen; ++i) {
mac += String.fromCharCode(0);
}
// get fragment and mac
var len = cipher.output.length();
if(len >= macLen) {
record.fragment = cipher.output.getBytes(len - macLen);
mac = cipher.output.getBytes(macLen);
}
// bad data, but get bytes anyway to try to keep timing consistent
else {
record.fragment = cipher.output.getBytes();
}
record.fragment = forge.util.createBuffer(record.fragment);
record.length = record.fragment.length();
// see if data integrity checks out, update sequence number
var mac2 = s.macFunction(s.macKey, s.sequenceNumber, record);
s.updateSequenceNumber();
rval = (mac2 === mac) && rval;
return rval;
};
/**
* Reads a TLS variable-length vector from a byte buffer.
*
@ -857,16 +681,11 @@ tls.Alert.Description = {
/**
* Supported cipher suites.
*
* TODO: Make cipher suites modular.
*/
tls.CipherSuites = {
TLS_RSA_WITH_AES_128_CBC_SHA: [0x00,0x2f],
TLS_RSA_WITH_AES_256_CBC_SHA: [0x00,0x35]
};
tls.CipherSuites = {};
/**
* Gets a supported cipher suite from 2 bytes.
* Gets a supported cipher suite from its 2 byte ID.
*
* @param twoBytes two bytes in a string.
*
@ -876,8 +695,8 @@ tls.getCipherSuite = function(twoBytes) {
var rval = null;
for(var key in tls.CipherSuites) {
var cs = tls.CipherSuites[key];
if(cs[0] === twoBytes.charCodeAt(0) &&
cs[1] === twoBytes.charCodeAt(1)) {
if(cs.id[0] === twoBytes.charCodeAt(0) &&
cs.id[1] === twoBytes.charCodeAt(1)) {
rval = cs;
break;
}
@ -939,7 +758,7 @@ tls.handleHelloRequest = function(c, record, length) {
tls.parseHelloMessage = function(c, record, length) {
var msg = null;
var client = (c.entity == tls.ConnectionEnd.client);
var client = (c.entity === tls.ConnectionEnd.client);
// minimum of 38 bytes in message
if(length < 38) {
@ -1050,7 +869,7 @@ tls.parseHelloMessage = function(c, record, length) {
// cipher suite not supported
if(c.session.cipherSuite === null) {
c.error(c, {
return c.error(c, {
message: 'No cipher suites in common.',
send: true,
alert: {
@ -1087,18 +906,6 @@ tls.createSecurityParameters = function(c, msg) {
// TODO: handle other options from server when more supported
// only AES CBC is presently supported, so just change the key length based
// on the chosen cipher suite
var keyLength;
switch(c.session.cipherSuite) {
case tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA:
keyLength = 16;
break;
case tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA:
keyLength = 32;
break;
}
// get client and server randoms
var client = (c.entity === tls.ConnectionEnd.client);
var msgRandom = msg.random.bytes();
@ -1109,15 +916,15 @@ tls.createSecurityParameters = function(c, msg) {
c.session.sp = {
entity: c.entity,
prf_algorithm: tls.PRFAlgorithm.tls_prf_sha256,
bulk_cipher_algorithm: tls.BulkCipherAlgorithm.aes,
cipher_type: tls.CipherType.block,
enc_key_length: keyLength,
block_length: 16,
fixed_iv_length: 16,
record_iv_length: 16,
mac_algorithm: tls.MACAlgorithm.hmac_sha1,
mac_length: 20,
mac_key_length: 20,
bulk_cipher_algorithm: null,
cipher_type: null,
enc_key_length: null,
block_length: null,
fixed_iv_length: null,
record_iv_length: null,
mac_algorithm: null,
mac_length: null,
mac_key_length: null,
compression_algorithm: c.session.compressionMethod,
pre_master_secret: null,
master_secret: null,
@ -1277,6 +1084,7 @@ tls.handleClientHello = function(c, record, length) {
data: tls.createCertificate(c)
}));
if(!c.fail) {
// queue server key exchange
tls.queue(c, tls.createRecord({
type: tls.ContentType.handshake,
@ -1298,6 +1106,7 @@ tls.handleClientHello = function(c, record, length) {
data: tls.createServerHelloDone(c)
}));
}
}
// send records
tls.flush(c);
@ -1517,7 +1326,7 @@ tls.handleClientKeyExchange = function(c, record, length) {
}
else {
var b = record.fragment;
msg = {
var msg = {
enc_pre_master_secret: readVector(b, 2).getBytes()
};
@ -1678,7 +1487,7 @@ tls.handleCertificateVerify = function(c, record, length) {
var msgBytes = b.bytes();
b.read += 4;
msg = {
var msg = {
signature: readVector(b, 2).getBytes()
};
@ -1692,9 +1501,10 @@ tls.handleCertificateVerify = function(c, record, length) {
try {
var cert = c.session.clientCertificate;
b = forge.pki.rsa.decrypt(
/*b = forge.pki.rsa.decrypt(
msg.signature, cert.publicKey, true, verify.length);
if(b !== verify) {
if(b !== verify) {*/
if(!cert.publicKey.verify(verify, msg.signature, 'NONE')) {
throw {
message: 'CertificateVerify signature does not match.'
};
@ -1781,7 +1591,7 @@ tls.handleServerHelloDone = function(c, record, length) {
// check for custom alert info
if(ret || ret === 0) {
// set custom message and alert description
if(ret.constructor == Object) {
if(typeof ret === 'object' && !forge.util.isArray(ret)) {
if(ret.message) {
error.message = ret.message;
}
@ -1789,7 +1599,7 @@ tls.handleServerHelloDone = function(c, record, length) {
error.alert.description = ret.alert;
}
}
else if(ret.constructor == Number) {
else if(typeof ret === 'number') {
// set custom alert description
error.alert.description = ret;
}
@ -1879,7 +1689,7 @@ tls.handleServerHelloDone = function(c, record, length) {
* @param record the record.
*/
tls.handleChangeCipherSpec = function(c, record) {
if(record.fragment.getByte() != 0x01) {
if(record.fragment.getByte() !== 0x01) {
c.error(c, {
message: 'Invalid ChangeCipherSpec message received.',
send: true,
@ -2336,7 +2146,7 @@ var F3 = tls.handleApplicationData;
var ctTable = [];
ctTable[tls.ConnectionEnd.client] = [
// CC,AL,HS,AD
/*SHE*/[__,__,F2,__],
/*SHE*/[__,F1,F2,__],
/*SCE*/[__,F1,F2,__],
/*SKE*/[__,F1,F2,__],
/*SCR*/[__,F1,F2,__],
@ -2350,7 +2160,7 @@ ctTable[tls.ConnectionEnd.client] = [
// map server current expect state and content type to function
ctTable[tls.ConnectionEnd.server] = [
// CC,AL,HS,AD
/*CHE*/[__,__,F2,__],
/*CHE*/[__,F1,F2,__],
/*CCE*/[__,F1,F2,__],
/*CKE*/[__,F1,F2,__],
/*CCV*/[__,F1,F2,__],
@ -2476,7 +2286,7 @@ hsTable[tls.ConnectionEnd.server] = [
*/
tls.generateKeys = function(c, sp) {
// TLS_RSA_WITH_AES_128_CBC_SHA (required to be compliant with TLS 1.2) &
// TLS_RSA_WITH_AES_128_CBC_SHA are the only cipher suites implemented
// TLS_RSA_WITH_AES_256_CBC_SHA are the only cipher suites implemented
// at present
// TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA is required to be compliant with
@ -2601,7 +2411,7 @@ tls.createConnectionState = function(c) {
compressionState: null,
compressFunction: function(record){return true;},
updateSequenceNumber: function() {
if(mode.sequenceNumber[1] == 0xFFFFFFFF) {
if(mode.sequenceNumber[1] === 0xFFFFFFFF) {
mode.sequenceNumber[1] = 0;
++mode.sequenceNumber[0];
}
@ -2676,57 +2486,18 @@ tls.createConnectionState = function(c) {
// handle security parameters
if(c.session) {
// generate keys
var sp = c.session.sp;
sp.keys = tls.generateKeys(c, sp);
c.session.cipherSuite.initSecurityParameters(sp);
// mac setup
// generate keys
sp.keys = tls.generateKeys(c, sp);
state.read.macKey = client ?
sp.keys.server_write_MAC_key : sp.keys.client_write_MAC_key;
state.write.macKey = client ?
sp.keys.client_write_MAC_key : sp.keys.server_write_MAC_key;
state.read.macLength = state.write.macLength = sp.mac_length;
switch(sp.mac_algorithm) {
case tls.MACAlgorithm.hmac_sha1:
state.read.macFunction = state.write.macFunction = hmac_sha1;
break;
default:
throw {
message: 'Unsupported MAC algorithm.'
};
}
// cipher setup
switch(sp.bulk_cipher_algorithm) {
case tls.BulkCipherAlgorithm.aes:
state.read.cipherState = {
init: false,
cipher: forge.aes.createDecryptionCipher(client ?
sp.keys.server_write_key : sp.keys.client_write_key),
iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV
};
state.write.cipherState = {
init: false,
cipher: forge.aes.createEncryptionCipher(client ?
sp.keys.client_write_key : sp.keys.server_write_key),
iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV
};
state.read.cipherFunction = decrypt_aes_128_cbc_sha1;
state.write.cipherFunction = encrypt_aes_128_cbc_sha1;
break;
default:
throw {
message: 'Unsupported cipher algorithm.'
};
}
switch(sp.cipher_type) {
case tls.CipherType.block:
break;
default:
throw {
message: 'Unsupported cipher type.'
};
}
// cipher suite setup
c.session.cipherSuite.initConnectionState(state, c, sp);
// compression setup
switch(sp.compression_algorithm) {
@ -2787,6 +2558,9 @@ tls.createRandom = function() {
* @return the created record.
*/
tls.createRecord = function(options) {
if(!options.data) {
return null;
}
var record = {
type: options.type,
version: {
@ -2889,8 +2663,8 @@ tls.createClientHello = function(c) {
var cipherSuites = forge.util.createBuffer();
for(var i = 0; i < c.cipherSuites.length; ++i) {
var cs = c.cipherSuites[i];
cipherSuites.putByte(cs[0]);
cipherSuites.putByte(cs[1]);
cipherSuites.putByte(cs.id[0]);
cipherSuites.putByte(cs.id[1]);
}
var cSuites = cipherSuites.length();
@ -3007,8 +2781,8 @@ tls.createServerHello = function(c) {
rval.putByte(tls.Version.minor); // minor version
rval.putBytes(c.session.sp.server_random); // random time + bytes
writeVector(rval, 1, forge.util.createBuffer(sessionId));
rval.putByte(c.session.cipherSuite[0]);
rval.putByte(c.session.cipherSuite[1]);
rval.putByte(c.session.cipherSuite.id[0]);
rval.putByte(c.session.cipherSuite.id[1]);
rval.putByte(c.session.compressionMethod);
return rval;
};
@ -3051,15 +2825,31 @@ tls.createCertificate = function(c) {
if(cert !== null) {
try {
// normalize cert to a chain of certificates
if((Array.isArray && !Array.isArray(cert)) ||
cert.constructor !== Array) {
if(!forge.util.isArray(cert)) {
cert = [cert];
}
var asn1 = null;
for(var i = 0; i < cert.length; ++i) {
var der = forge.pki.pemToDer(cert);
var msg = forge.pem.decode(cert[i])[0];
if(msg.type !== 'CERTIFICATE' &&
msg.type !== 'X509 CERTIFICATE' &&
msg.type !== 'TRUSTED CERTIFICATE') {
throw {
message: 'Could not convert certificate from PEM; PEM header ' +
'type is not "CERTIFICATE", "X509 CERTIFICATE", or ' +
'"TRUSTED CERTIFICATE".',
headerType: msg.type
};
}
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
throw {
message: 'Could not convert certificate from PEM; PEM is encrypted.'
};
}
var der = forge.util.createBuffer(msg.body);
if(asn1 === null) {
asn1 = forge.asn1.fromDer(der.bytes());
asn1 = forge.asn1.fromDer(der.bytes(), false);
}
// certificate entry is itself a vector with 3 length bytes
@ -3080,7 +2870,7 @@ tls.createCertificate = function(c) {
}
}
catch(ex) {
c.error(c, {
return c.error(c, {
message: 'Could not send certificate list.',
cause: ex,
send: true,
@ -3259,7 +3049,7 @@ tls.getClientSignature = function(c, callback) {
});
}
else {
b = forge.pki.rsa.encrypt(b, privateKey, 0x01);
b = privateKey.sign(b, null);
}
callback(c, b);
};
@ -3479,6 +3269,11 @@ tls.createFinished = function(c) {
* @param record the record to queue.
*/
tls.queue = function(c, record) {
// error during record creation
if(!record) {
return;
}
// if the record is a handshake record, update handshake hashes
if(record.type === tls.ContentType.handshake) {
var bytes = record.fragment.bytes();
@ -3624,7 +3419,7 @@ tls.verifyCertificateChain = function(c, chain) {
// call application callback
var ret = c.verify(c, vfd, depth, chain);
if(ret !== true) {
if(ret.constructor === Object) {
if(typeof ret === 'object' && !forge.util.isArray(ret)) {
// throw custom error
var error = {
message: 'The application rejected the certificate.',
@ -3654,7 +3449,7 @@ tls.verifyCertificateChain = function(c, chain) {
}
catch(ex) {
// build tls error if not already customized
if(ex.constructor !== Object) {
if(typeof ex !== 'object' || forge.util.isArray(ex)) {
ex = {
send: true,
alert: {
@ -3771,8 +3566,7 @@ tls.createConnection = function(options) {
var caStore = null;
if(options.caStore) {
// if CA store is an array, convert it to a CA store object
if((Array.isArray && Array.isArray(options.caStore)) ||
options.caStore.constructor == Array) {
if(forge.util.isArray(options.caStore)) {
caStore = forge.pki.createCaStore(options.caStore);
}
else {
@ -3788,8 +3582,9 @@ tls.createConnection = function(options) {
var cipherSuites = options.cipherSuites || null;
if(cipherSuites === null) {
cipherSuites = [];
cipherSuites.push(tls.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA);
cipherSuites.push(tls.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA);
for(var key in tls.CipherSuites) {
cipherSuites.push(tls.CipherSuites[key]);
}
}
// set default entity
@ -3934,8 +3729,8 @@ tls.createConnection = function(options) {
};
// check record version
if(c.record.version.major != tls.Version.major ||
c.record.version.minor != tls.Version.minor) {
if(c.record.version.major !== tls.Version.major ||
c.record.version.minor !== tls.Version.minor) {
c.error(c, {
message: 'Incompatible TLS version.',
send: true,
@ -4198,14 +3993,18 @@ tls.createConnection = function(options) {
/* TLS API */
forge.tls = forge.tls || {};
// expose non-functions
for(var key in tls) {
if(typeof tls[key] !== 'function') {
forge.tls[key] = tls[key];
}
}
// expose prf_tls1 for testing
forge.tls.prf_tls1 = prf_TLS1;
// expose TLS alerts
forge.tls.Alert = tls.Alert;
// expose cipher suites
forge.tls.CipherSuites = tls.CipherSuites;
// expost sha1 hmac method
forge.tls.hmac_sha1 = hmac_sha1;
// expose session cache creation
forge.tls.createSessionCache = tls.createSessionCache;
@ -4313,20 +4112,11 @@ forge.tls.createConnection = tls.createConnection;
/* ########## Begin module wrapper ########## */
var name = 'tls';
var deps = [
'./aes',
'./asn1',
'./hmac',
'./md',
'./pki',
'./random',
'./util'
];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -4335,14 +4125,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -4359,6 +4147,27 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define([
'require',
'module',
'./asn1',
'./hmac',
'./md',
'./pem',
'./pki',
'./random',
'./util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -254,12 +254,11 @@ forge.tls.wrapSocket = function(options) {
/* ########## Begin module wrapper ########## */
var name = 'tlssocket';
var deps = ['./tls'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -268,14 +267,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -292,6 +289,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module', './tls'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -37,6 +37,11 @@ else {
}
}
// define isArray
util.isArray = Array.isArray || function(x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
/**
* Constructor for a byte buffer.
*
@ -64,16 +69,19 @@ util.ByteBuffer.prototype.length = function() {
* @return true if this buffer is empty, false if not.
*/
util.ByteBuffer.prototype.isEmpty = function() {
return (this.data.length - this.read) === 0;
return this.length() <= 0;
};
/**
* Puts a byte in this buffer.
*
* @param b the byte to put.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putByte = function(b) {
this.data += String.fromCharCode(b);
return this;
};
/**
@ -81,6 +89,8 @@ util.ByteBuffer.prototype.putByte = function(b) {
*
* @param b the byte to put.
* @param n the number of bytes of value b to put.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.fillWithByte = function(b, n) {
b = String.fromCharCode(b);
@ -95,53 +105,68 @@ util.ByteBuffer.prototype.fillWithByte = function(b, n) {
}
}
this.data = d;
return this;
};
/**
* Puts bytes in this buffer.
*
* @param bytes the bytes (as a UTF-8 encoded string) to put.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putBytes = function(bytes) {
this.data += bytes;
return this;
};
/**
* Puts a UTF-16 encoded string into this buffer.
*
* @param str the string to put.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putString = function(str) {
this.data += util.encodeUtf8(str);
return this;
};
/**
* Puts a 16-bit integer in this buffer in big-endian order.
*
* @param i the 16-bit integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt16 = function(i) {
this.data +=
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF);
return this;
};
/**
* Puts a 24-bit integer in this buffer in big-endian order.
*
* @param i the 24-bit integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt24 = function(i) {
this.data +=
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF);
return this;
};
/**
* Puts a 32-bit integer in this buffer in big-endian order.
*
* @param i the 32-bit integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt32 = function(i) {
this.data +=
@ -149,35 +174,44 @@ util.ByteBuffer.prototype.putInt32 = function(i) {
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF);
return this;
};
/**
* Puts a 16-bit integer in this buffer in little-endian order.
*
* @param i the 16-bit integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt16Le = function(i) {
this.data +=
String.fromCharCode(i & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF);
return this;
};
/**
* Puts a 24-bit integer in this buffer in little-endian order.
*
* @param i the 24-bit integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt24Le = function(i) {
this.data +=
String.fromCharCode(i & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i >> 16 & 0xFF);
return this;
};
/**
* Puts a 32-bit integer in this buffer in little-endian order.
*
* @param i the 32-bit integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt32Le = function(i) {
this.data +=
@ -185,6 +219,7 @@ util.ByteBuffer.prototype.putInt32Le = function(i) {
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 24 & 0xFF);
return this;
};
/**
@ -192,6 +227,8 @@ util.ByteBuffer.prototype.putInt32Le = function(i) {
*
* @param i the n-bit integer.
* @param n the number of bits in the integer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putInt = function(i, n) {
do {
@ -199,15 +236,19 @@ util.ByteBuffer.prototype.putInt = function(i, n) {
this.data += String.fromCharCode((i >> n) & 0xFF);
}
while(n > 0);
return this;
};
/**
* Puts the given buffer into this buffer.
*
* @param buffer the buffer to put into this one.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.putBuffer = function(buffer) {
this.data += buffer.getBytes();
return this;
};
/**
@ -320,7 +361,7 @@ util.ByteBuffer.prototype.getInt32Le = function() {
util.ByteBuffer.prototype.getInt = function(n) {
var rval = 0;
do {
rval = (rval << n) + this.data.charCodeAt(this.read++);
rval = (rval << 8) + this.data.charCodeAt(this.read++);
n -= 8;
}
while(n > 0);
@ -383,11 +424,14 @@ util.ByteBuffer.prototype.at = function(i) {
*
* @param i the byte index.
* @param b the byte to put.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.setAt = function(i, b) {
this.data = this.data.substr(0, this.read + i) +
String.fromCharCode(b) +
this.data.substr(this.read + i + 1);
return this;
};
/**
@ -412,31 +456,40 @@ util.ByteBuffer.prototype.copy = function() {
/**
* Compacts this buffer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.compact = function() {
if(this.read > 0) {
this.data = this.data.slice(this.read);
this.read = 0;
}
return this;
};
/**
* Clears this buffer.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.clear = function() {
this.data = '';
this.read = 0;
return this;
};
/**
* Shortens this buffer by triming bytes off of the end of this buffer.
*
* @param count the number of bytes to trim off.
*
* @return this buffer.
*/
util.ByteBuffer.prototype.truncate = function(count) {
var len = Math.max(0, this.length() - count);
this.data = this.data.substr(this.read, len);
this.read = 0;
return this;
};
/**
@ -1152,7 +1205,8 @@ util.getQueryVariables = function(query) {
if(!(key in rval)) {
rval[key] = [];
}
if(val !== null) {
// disallow overriding object prototype keys
if(!(key in Object.prototype) && val !== null) {
rval[key].push(unescape(val));
}
}
@ -1535,12 +1589,11 @@ util.formatSize = function(size) {
/* ########## Begin module wrapper ########## */
var name = 'util';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
}
@ -1549,14 +1602,12 @@ if(typeof define !== 'function') {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
return initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
function(require, module) {
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
@ -1573,6 +1624,18 @@ if(nodeDefine || typeof define === 'function') {
}
return forge[name];
};
});
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(['require', 'module'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();

View File

@ -235,7 +235,7 @@ xhrApi.getCookie = function(name, path, domain) {
if(rval === null) {
rval = cookie;
}
else if(rval.constructor != Array) {
else if(!forge.util.isArray(rval)) {
rval = [rval, cookie];
}
else {
@ -689,8 +689,8 @@ xhrApi.create = function(options) {
_state.response = null;
// 6. if state is DONE or UNSENT, or if OPENED and send flag is false
if(xhr.readyState == DONE || xhr.readyState == UNSENT ||
(xhr.readyState == OPENED && !_state.sendFlag)) {
if(xhr.readyState === DONE || xhr.readyState === UNSENT ||
(xhr.readyState === OPENED && !_state.sendFlag)) {
// 7. set ready state to unsent
xhr.readyState = UNSENT;
}
@ -741,7 +741,7 @@ xhrApi.create = function(options) {
if(_state.response !== null) {
if(header in _state.response.fields) {
rval = _state.response.fields[header];
if(rval.constructor == Array) {
if(forge.util.isArray(rval)) {
rval = rval.join();
}
}