mirror of
https://github.com/moparisthebest/mail
synced 2024-11-29 20:32:15 -05:00
cleanup src/lib, delete all npm copied files and build everythiny with grunt
This commit is contained in:
parent
7b532f780c
commit
55abbc00cd
@ -121,6 +121,7 @@ module.exports = function(grunt) {
|
|||||||
flatten: true,
|
flatten: true,
|
||||||
cwd: 'node_modules/',
|
cwd: 'node_modules/',
|
||||||
src: [
|
src: [
|
||||||
|
'requirejs/require.js',
|
||||||
'crypto-lib/node_modules/node-forge/js/*.js',
|
'crypto-lib/node_modules/node-forge/js/*.js',
|
||||||
'imap-client/src/*.js',
|
'imap-client/src/*.js',
|
||||||
'imap-client/node_modules/inbox/src/*.js',
|
'imap-client/node_modules/inbox/src/*.js',
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/master",
|
"crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/master",
|
||||||
"imap-client": "git+ssh://git@github.com:whiteout-io/imap-client.git#amd",
|
"imap-client": "git+ssh://git@github.com:whiteout-io/imap-client.git#amd",
|
||||||
"smtp-client": "git+ssh://git@github.com:whiteout-io/smtp-client.git#master"
|
"smtp-client": "git+ssh://git@github.com:whiteout-io/smtp-client.git#master",
|
||||||
|
"requirejs": "2.1.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "0.4.1",
|
"grunt": "0.4.1",
|
||||||
|
1301
src/lib/aes.js
1301
src/lib/aes.js
File diff suppressed because it is too large
Load Diff
@ -1,309 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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));
|
|
||||||
});
|
|
||||||
})();
|
|
1073
src/lib/asn1.js
1073
src/lib/asn1.js
File diff suppressed because it is too large
Load Diff
4
src/lib/backbone-1.0.0.min.js
vendored
4
src/lib/backbone-1.0.0.min.js
vendored
File diff suppressed because one or more lines are too long
139
src/lib/debug.js
139
src/lib/debug.js
@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* Debugging support for web applications.
|
|
||||||
*
|
|
||||||
* @author David I. Lehn <dlehn@digitalbazaar.com>
|
|
||||||
*
|
|
||||||
* Copyright 2008-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
/* DEBUG API */
|
|
||||||
forge.debug = forge.debug || {};
|
|
||||||
|
|
||||||
// Private storage for debugging.
|
|
||||||
// Useful to expose data that is otherwise unviewable behind closures.
|
|
||||||
// NOTE: remember that this can hold references to data and cause leaks!
|
|
||||||
// format is "forge._debug.<modulename>.<dataname> = data"
|
|
||||||
// Example:
|
|
||||||
// (function() {
|
|
||||||
// var cat = 'forge.test.Test'; // debugging category
|
|
||||||
// var sState = {...}; // local state
|
|
||||||
// forge.debug.set(cat, 'sState', sState);
|
|
||||||
// })();
|
|
||||||
forge.debug.storage = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets debug data. Omit name for all cat data Omit name and cat for
|
|
||||||
* all data.
|
|
||||||
*
|
|
||||||
* @param cat name of debugging category.
|
|
||||||
* @param name name of data to get (optional).
|
|
||||||
* @return object with requested debug data or undefined.
|
|
||||||
*/
|
|
||||||
forge.debug.get = function(cat, name) {
|
|
||||||
var rval;
|
|
||||||
if(typeof(cat) === 'undefined') {
|
|
||||||
rval = forge.debug.storage;
|
|
||||||
}
|
|
||||||
else if(cat in forge.debug.storage) {
|
|
||||||
if(typeof(name) === 'undefined') {
|
|
||||||
rval = forge.debug.storage[cat];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rval = forge.debug.storage[cat][name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets debug data.
|
|
||||||
*
|
|
||||||
* @param cat name of debugging category.
|
|
||||||
* @param name name of data to set.
|
|
||||||
* @param data data to set.
|
|
||||||
*/
|
|
||||||
forge.debug.set = function(cat, name, data) {
|
|
||||||
if(!(cat in forge.debug.storage)) {
|
|
||||||
forge.debug.storage[cat] = {};
|
|
||||||
}
|
|
||||||
forge.debug.storage[cat][name] = data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears debug data. Omit name for all cat data. Omit name and cat for
|
|
||||||
* all data.
|
|
||||||
*
|
|
||||||
* @param cat name of debugging category.
|
|
||||||
* @param name name of data to clear or omit to clear entire category.
|
|
||||||
*/
|
|
||||||
forge.debug.clear = function(cat, name) {
|
|
||||||
if(typeof(cat) === 'undefined') {
|
|
||||||
forge.debug.storage = {};
|
|
||||||
}
|
|
||||||
else if(cat in forge.debug.storage) {
|
|
||||||
if(typeof(name) === 'undefined') {
|
|
||||||
delete forge.debug.storage[cat];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
delete forge.debug.storage[cat][name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'debug';
|
|
||||||
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'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
471
src/lib/des.js
471
src/lib/des.js
@ -1,471 +0,0 @@
|
|||||||
/**
|
|
||||||
* DES (Data Encryption Standard) implementation.
|
|
||||||
*
|
|
||||||
* This implementation supports DES as well as 3DES-EDE in ECB and CBC mode.
|
|
||||||
* It is based on the BSD-licensed implementation by Paul Tero:
|
|
||||||
*
|
|
||||||
* Paul Tero, July 2001
|
|
||||||
* http://www.tero.co.uk/des/
|
|
||||||
*
|
|
||||||
* Optimised for performance with large blocks by Michael Hayworth, November 2001
|
|
||||||
* http://www.netdealing.com
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var spfunction1 = [0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004];
|
|
||||||
var spfunction2 = [-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000];
|
|
||||||
var spfunction3 = [0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200];
|
|
||||||
var spfunction4 = [0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080];
|
|
||||||
var spfunction5 = [0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100];
|
|
||||||
var spfunction6 = [0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010];
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* @param key The 64-bit or 192-bit key
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function des_createKeys (key) {
|
|
||||||
var pc2bytes0 = [0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204],
|
|
||||||
pc2bytes1 = [0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101],
|
|
||||||
pc2bytes2 = [0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808],
|
|
||||||
pc2bytes3 = [0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000],
|
|
||||||
pc2bytes4 = [0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010],
|
|
||||||
pc2bytes5 = [0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420],
|
|
||||||
pc2bytes6 = [0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002],
|
|
||||||
pc2bytes7 = [0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800],
|
|
||||||
pc2bytes8 = [0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002],
|
|
||||||
pc2bytes9 = [0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408],
|
|
||||||
pc2bytes10 = [0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020],
|
|
||||||
pc2bytes11 = [0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200],
|
|
||||||
pc2bytes12 = [0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010],
|
|
||||||
pc2bytes13 = [0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105];
|
|
||||||
|
|
||||||
//how many iterations (1 for des, 3 for triple des)
|
|
||||||
var iterations = key.length() > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
|
|
||||||
|
|
||||||
//stores the return keys
|
|
||||||
var keys = [];
|
|
||||||
|
|
||||||
//now define the left shifts which need to be done
|
|
||||||
var shifts = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0];
|
|
||||||
|
|
||||||
var n = 0, temp;
|
|
||||||
for(var j = 0; j < iterations; j ++) {
|
|
||||||
var left = key.getInt32();
|
|
||||||
var right = key.getInt32();
|
|
||||||
|
|
||||||
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
|
|
||||||
temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
|
|
||||||
temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2);
|
|
||||||
temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
|
|
||||||
temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
|
|
||||||
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
|
|
||||||
temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
|
|
||||||
|
|
||||||
//the right side needs to be shifted and to get the last four bits of the left side
|
|
||||||
temp = (left << 8) | ((right >>> 20) & 0x000000f0);
|
|
||||||
|
|
||||||
//left needs to be put upside down
|
|
||||||
left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
|
|
||||||
right = temp;
|
|
||||||
|
|
||||||
//now go through and perform these shifts on the left and right keys
|
|
||||||
for(var i=0; i < shifts.length; i++) {
|
|
||||||
//shift the keys either one or two bits to the left
|
|
||||||
if(shifts[i]) {
|
|
||||||
left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);
|
|
||||||
} else {
|
|
||||||
left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);
|
|
||||||
}
|
|
||||||
left &= -0xf; right &= -0xf;
|
|
||||||
|
|
||||||
//now apply PC-2, in such a way that E is easier when encrypting or decrypting
|
|
||||||
//this conversion will look like PC-2 except only the last 6 bits of each byte are used
|
|
||||||
//rather than 48 consecutive bits and the order of lines will be according to
|
|
||||||
//how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
|
|
||||||
var lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf]
|
|
||||||
| pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf]
|
|
||||||
| pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf]
|
|
||||||
| pc2bytes6[(left >>> 4) & 0xf];
|
|
||||||
var righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf]
|
|
||||||
| pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf]
|
|
||||||
| pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf]
|
|
||||||
| pc2bytes13[(right >>> 4) & 0xf];
|
|
||||||
temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff;
|
|
||||||
keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an DES cipher object.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use (64 or 192 bits).
|
|
||||||
* @param encrypt false for decryption, true for encryption.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
var _createCipher = function(key, encrypt) {
|
|
||||||
if(typeof key === 'string' && (key.length === 8 || key.length === 24)) {
|
|
||||||
key = forge.util.createBuffer(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the 16 or 48 subkeys we will need. */
|
|
||||||
var keys = des_createKeys (key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mode of encryption.
|
|
||||||
*
|
|
||||||
* 0: ECB (Electronic Codebook)
|
|
||||||
* 1: CBC (Cipher Block Chaining)
|
|
||||||
*/
|
|
||||||
var mode = 1;
|
|
||||||
|
|
||||||
var cbcleft = 0, cbcleft2 = 0, cbcright = 0, cbcright2 = 0;
|
|
||||||
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 looping;
|
|
||||||
|
|
||||||
if(iterations === 3) {
|
|
||||||
looping = encrypt
|
|
||||||
? [0, 32, 2]
|
|
||||||
: [30, -2, -2];
|
|
||||||
} else {
|
|
||||||
looping = encrypt
|
|
||||||
? [0, 32, 2, 62, 30, -2, 64, 96, 2]
|
|
||||||
: [94, 62, -2, 32, 64, 2, 30, -2, -2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create cipher object
|
|
||||||
var cipher = null;
|
|
||||||
cipher = {
|
|
||||||
/**
|
|
||||||
* Starts or restarts the encryption or decryption process, whichever
|
|
||||||
* was previously configured.
|
|
||||||
*
|
|
||||||
* To use the cipher in CBC mode, iv may be given either as a string
|
|
||||||
* of bytes, or as a byte buffer. For ECB mode, give null as iv.
|
|
||||||
*
|
|
||||||
* @param iv the initialization vector to use, null for ECB mode.
|
|
||||||
* @param output the output the buffer to write to, null to create one.
|
|
||||||
*/
|
|
||||||
start: function(iv, output) {
|
|
||||||
if(iv) {
|
|
||||||
if(typeof iv === 'string' && iv.length === 8) {
|
|
||||||
iv = forge.util.createBuffer(iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = 1; // CBC mode
|
|
||||||
cbcleft = iv.getInt32();
|
|
||||||
cbcright = iv.getInt32();
|
|
||||||
} else {
|
|
||||||
mode = 0; // ECB mode
|
|
||||||
}
|
|
||||||
|
|
||||||
//store the result here
|
|
||||||
_finish = false;
|
|
||||||
|
|
||||||
_input = forge.util.createBuffer();
|
|
||||||
_output = output || forge.util.createBuffer();
|
|
||||||
|
|
||||||
cipher.output = _output;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the next block.
|
|
||||||
*
|
|
||||||
* @param input the buffer to read from.
|
|
||||||
*/
|
|
||||||
update: function(input) {
|
|
||||||
if(!_finish) {
|
|
||||||
// not finishing, so fill the input buffer with more input
|
|
||||||
_input.putBuffer(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
while(_input.length() >= 8) {
|
|
||||||
var temp;
|
|
||||||
|
|
||||||
var left = _input.getInt32();
|
|
||||||
var right = _input.getInt32();
|
|
||||||
|
|
||||||
//for Cipher Block Chaining mode, xor the message with the previous result
|
|
||||||
if(mode === 1) {
|
|
||||||
if(encrypt) {
|
|
||||||
left ^= cbcleft;
|
|
||||||
right ^= cbcright;
|
|
||||||
} else {
|
|
||||||
cbcleft2 = cbcleft;
|
|
||||||
cbcright2 = cbcright;
|
|
||||||
cbcleft = left;
|
|
||||||
cbcright = right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//first each 64 bit chunk of the message must be permuted according to IP
|
|
||||||
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
|
|
||||||
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
|
|
||||||
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
|
|
||||||
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
|
|
||||||
temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
|
|
||||||
|
|
||||||
left = ((left << 1) | (left >>> 31));
|
|
||||||
right = ((right << 1) | (right >>> 31));
|
|
||||||
|
|
||||||
for(var j = 0; j < iterations; j += 3) {
|
|
||||||
var endloop = looping[j+1];
|
|
||||||
var loopinc = looping[j+2];
|
|
||||||
|
|
||||||
//now go through and perform the encryption or decryption
|
|
||||||
for(var i = looping[j]; i != endloop; i += loopinc) {
|
|
||||||
var right1 = right ^ keys[i];
|
|
||||||
var right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1];
|
|
||||||
|
|
||||||
//the result is attained by passing these bytes through the S selection functions
|
|
||||||
temp = left;
|
|
||||||
left = right;
|
|
||||||
right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f]
|
|
||||||
| spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f]
|
|
||||||
| spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f]
|
|
||||||
| spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]);
|
|
||||||
}
|
|
||||||
temp = left; left = right; right = temp; //unreverse left and right
|
|
||||||
}
|
|
||||||
|
|
||||||
//move then each one bit to the right
|
|
||||||
left = ((left >>> 1) | (left << 31));
|
|
||||||
right = ((right >>> 1) | (right << 31));
|
|
||||||
|
|
||||||
//now perform IP-1, which is IP in the opposite direction
|
|
||||||
temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
|
|
||||||
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
|
|
||||||
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
|
|
||||||
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
|
|
||||||
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(encrypt) {
|
|
||||||
cbcleft = left;
|
|
||||||
cbcright = right;
|
|
||||||
} else {
|
|
||||||
left ^= cbcleft2;
|
|
||||||
right ^= cbcright2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_output.putInt32(left);
|
|
||||||
_output.putInt32(right);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finishes encrypting or decrypting.
|
|
||||||
*
|
|
||||||
* @param pad a padding function to use, null for PKCS#7 padding,
|
|
||||||
* signature(blockSize, buffer, decrypt).
|
|
||||||
*
|
|
||||||
* @return true if successful, false on error.
|
|
||||||
*/
|
|
||||||
finish: function(pad) {
|
|
||||||
var rval = true;
|
|
||||||
|
|
||||||
if(encrypt) {
|
|
||||||
if(pad) {
|
|
||||||
rval = pad(8, _input, !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());
|
|
||||||
_input.fillWithByte(padding, padding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rval) {
|
|
||||||
// do final update
|
|
||||||
_finish = true;
|
|
||||||
cipher.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!encrypt) {
|
|
||||||
// check for error: input data not a multiple of block size
|
|
||||||
rval = (_input.length() === 0);
|
|
||||||
if(rval) {
|
|
||||||
if(pad) {
|
|
||||||
rval = pad(8, _output, !encrypt);
|
|
||||||
} else {
|
|
||||||
// ensure padding byte count is valid
|
|
||||||
var len = _output.length();
|
|
||||||
var count = _output.at(len - 1);
|
|
||||||
|
|
||||||
if(count > len) {
|
|
||||||
rval = false;
|
|
||||||
} else {
|
|
||||||
// trim off padding bytes
|
|
||||||
_output.truncate(count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* DES API */
|
|
||||||
forge.des = forge.des || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DES cipher object to encrypt data in ECB or CBC mode 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 or as a byte buffer.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use.
|
|
||||||
* @param iv the initialization vector to use, null for ECB mode.
|
|
||||||
* @param output the buffer to write to, null to create one.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.des.startEncrypting = function(key, iv, output) {
|
|
||||||
var cipher = _createCipher(key, true);
|
|
||||||
cipher.start(iv, output);
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DES cipher object to encrypt data in ECB or CBC mode using the
|
|
||||||
* given symmetric key.
|
|
||||||
*
|
|
||||||
* The key may be given as a string of bytes, or as a byte buffer.
|
|
||||||
*
|
|
||||||
* To start encrypting call start() on the cipher with an iv and optional
|
|
||||||
* output buffer.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.des.createEncryptionCipher = function(key) {
|
|
||||||
return _createCipher(key, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DES cipher object to decrypt data in ECB or CBC mode 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, or as a byte buffer.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use.
|
|
||||||
* @param iv the initialization vector to use, null for ECB mode.
|
|
||||||
* @param output the buffer to write to, null to create one.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.des.startDecrypting = function(key, iv, output) {
|
|
||||||
var cipher = _createCipher(key, false);
|
|
||||||
cipher.start(iv, output);
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DES cipher object to decrypt data in ECB or CBC mode using the
|
|
||||||
* given symmetric key.
|
|
||||||
*
|
|
||||||
* The key may be given as a string of bytes, or as a byte buffer.
|
|
||||||
*
|
|
||||||
* To start decrypting call start() on the cipher with an iv and
|
|
||||||
* optional output buffer.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.des.createDecryptionCipher = function(key) {
|
|
||||||
return _createCipher(key, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'des';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,88 +0,0 @@
|
|||||||
/**
|
|
||||||
* Node.js module for Forge.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright 2011-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
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',
|
|
||||||
'./pss',
|
|
||||||
'./random',
|
|
||||||
'./rc2',
|
|
||||||
'./task',
|
|
||||||
'./tls',
|
|
||||||
'./util',
|
|
||||||
'./md',
|
|
||||||
'./mgf1'
|
|
||||||
], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
1
src/lib/forge.min.js
vendored
1
src/lib/forge.min.js
vendored
File diff suppressed because one or more lines are too long
162
src/lib/form.js
162
src/lib/form.js
@ -1,162 +0,0 @@
|
|||||||
/**
|
|
||||||
* Functions for manipulating web forms.
|
|
||||||
*
|
|
||||||
* @author David I. Lehn <dlehn@digitalbazaar.com>
|
|
||||||
* @author Dave Longley
|
|
||||||
* @author Mike Johnson
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011-2012 Digital Bazaar, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
(function($) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The form namespace.
|
|
||||||
*/
|
|
||||||
var form = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Regex for parsing a single name property (handles array brackets).
|
|
||||||
*/
|
|
||||||
var _regex = /(.*?)\[(.*?)\]/g;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a single name property into an array with the name and any
|
|
||||||
* array indices.
|
|
||||||
*
|
|
||||||
* @param name the name to parse.
|
|
||||||
*
|
|
||||||
* @return the array of the name and its array indices in order.
|
|
||||||
*/
|
|
||||||
var _parseName = function(name) {
|
|
||||||
var rval = [];
|
|
||||||
|
|
||||||
var matches;
|
|
||||||
while(!!(matches = _regex.exec(name))) {
|
|
||||||
if(matches[1].length > 0) {
|
|
||||||
rval.push(matches[1]);
|
|
||||||
}
|
|
||||||
if(matches.length >= 2) {
|
|
||||||
rval.push(matches[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rval.length === 0) {
|
|
||||||
rval.push(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a field from the given form to the given object.
|
|
||||||
*
|
|
||||||
* @param obj the object.
|
|
||||||
* @param names the field as an array of object property names.
|
|
||||||
* @param value the value of the field.
|
|
||||||
* @param dict a dictionary of names to replace.
|
|
||||||
*/
|
|
||||||
var _addField = function(obj, names, value, dict) {
|
|
||||||
// combine array names that fall within square brackets
|
|
||||||
var tmp = [];
|
|
||||||
for(var i = 0; i < names.length; ++i) {
|
|
||||||
// check name for starting square bracket but no ending one
|
|
||||||
var name = names[i];
|
|
||||||
if(name.indexOf('[') !== -1 && name.indexOf(']') === -1 &&
|
|
||||||
i < names.length - 1) {
|
|
||||||
do {
|
|
||||||
name += '.' + names[++i];
|
|
||||||
}
|
|
||||||
while(i < names.length - 1 && names[i].indexOf(']') === -1);
|
|
||||||
}
|
|
||||||
tmp.push(name);
|
|
||||||
}
|
|
||||||
names = tmp;
|
|
||||||
|
|
||||||
// split out array indexes
|
|
||||||
var tmp = [];
|
|
||||||
$.each(names, function(n, name) {
|
|
||||||
tmp = tmp.concat(_parseName(name));
|
|
||||||
});
|
|
||||||
names = tmp;
|
|
||||||
|
|
||||||
// iterate over object property names until value is set
|
|
||||||
$.each(names, function(n, name) {
|
|
||||||
// do dictionary name replacement
|
|
||||||
if(dict && name.length !== 0 && name in dict) {
|
|
||||||
name = dict[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
// blank name indicates appending to an array, set name to
|
|
||||||
// new last index of array
|
|
||||||
if(name.length === 0) {
|
|
||||||
name = obj.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// value already exists, append value
|
|
||||||
if(obj[name]) {
|
|
||||||
// last name in the field
|
|
||||||
if(n == names.length - 1) {
|
|
||||||
// more than one value, so convert into an array
|
|
||||||
if(!$.isArray(obj[name])) {
|
|
||||||
obj[name] = [obj[name]];
|
|
||||||
}
|
|
||||||
obj[name].push(value);
|
|
||||||
}
|
|
||||||
// not last name, go deeper into object
|
|
||||||
else {
|
|
||||||
obj = obj[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// new value, last name in the field, set value
|
|
||||||
else if(n == names.length - 1) {
|
|
||||||
obj[name] = value;
|
|
||||||
}
|
|
||||||
// new value, not last name, go deeper
|
|
||||||
else {
|
|
||||||
// get next name
|
|
||||||
var next = names[n + 1];
|
|
||||||
|
|
||||||
// blank next value indicates array-appending, so create array
|
|
||||||
if(next.length === 0) {
|
|
||||||
obj[name] = [];
|
|
||||||
}
|
|
||||||
// if next name is a number create an array, otherwise a map
|
|
||||||
else {
|
|
||||||
var isNum = ((next - 0) == next && next.length > 0);
|
|
||||||
obj[name] = isNum ? [] : {};
|
|
||||||
}
|
|
||||||
obj = obj[name];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes a form to a JSON object. Object properties will be separated
|
|
||||||
* using the given separator (defaults to '.') and by square brackets.
|
|
||||||
*
|
|
||||||
* @param input the jquery form to serialize.
|
|
||||||
* @param sep the object-property separator (defaults to '.').
|
|
||||||
* @param dict a dictionary of names to replace (name=replace).
|
|
||||||
*
|
|
||||||
* @return the JSON-serialized form.
|
|
||||||
*/
|
|
||||||
form.serialize = function(input, sep, dict) {
|
|
||||||
var rval = {};
|
|
||||||
|
|
||||||
// add all fields in the form to the object
|
|
||||||
sep = sep || '.';
|
|
||||||
$.each(input.serializeArray(), function() {
|
|
||||||
_addField(rval, this.name.split(sep), this.value || '', dict);
|
|
||||||
});
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The forge namespace and form API.
|
|
||||||
*/
|
|
||||||
if(typeof forge === 'undefined') {
|
|
||||||
forge = {};
|
|
||||||
}
|
|
||||||
forge.form = form;
|
|
||||||
|
|
||||||
})(jQuery);
|
|
205
src/lib/hmac.js
205
src/lib/hmac.js
@ -1,205 +0,0 @@
|
|||||||
/**
|
|
||||||
* Hash-based Message Authentication Code implementation. Requires a message
|
|
||||||
* digest object that can be obtained, for example, from forge.md.sha1 or
|
|
||||||
* forge.md.md5.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
/* HMAC API */
|
|
||||||
var hmac = forge.hmac = forge.hmac || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an HMAC object that uses the given message digest object.
|
|
||||||
*
|
|
||||||
* @return an HMAC object.
|
|
||||||
*/
|
|
||||||
hmac.create = function() {
|
|
||||||
// the hmac key to use
|
|
||||||
var _key = null;
|
|
||||||
|
|
||||||
// the message digest to use
|
|
||||||
var _md = null;
|
|
||||||
|
|
||||||
// the inner padding
|
|
||||||
var _ipadding = null;
|
|
||||||
|
|
||||||
// the outer padding
|
|
||||||
var _opadding = null;
|
|
||||||
|
|
||||||
// hmac context
|
|
||||||
var ctx = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts or restarts the HMAC with the given key and message digest.
|
|
||||||
*
|
|
||||||
* @param md the message digest to use, null to reuse the previous one,
|
|
||||||
* a string to use builtin 'sha1', 'md5', 'sha256'.
|
|
||||||
* @param key the key to use as a string, array of bytes, byte buffer,
|
|
||||||
* or null to reuse the previous key.
|
|
||||||
*/
|
|
||||||
ctx.start = function(md, key) {
|
|
||||||
if(md !== null) {
|
|
||||||
if(typeof md === 'string') {
|
|
||||||
// create builtin message digest
|
|
||||||
md = md.toLowerCase();
|
|
||||||
if(md in forge.md.algorithms) {
|
|
||||||
_md = forge.md.algorithms[md].create();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw 'Unknown hash algorithm "' + md + '"';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// store message digest
|
|
||||||
_md = md;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(key === null) {
|
|
||||||
// reuse previous key
|
|
||||||
key = _key;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// convert string into byte buffer
|
|
||||||
if(typeof key === 'string') {
|
|
||||||
key = forge.util.createBuffer(key);
|
|
||||||
}
|
|
||||||
// convert byte array into byte buffer
|
|
||||||
else if(forge.util.isArray(key)) {
|
|
||||||
var tmp = key;
|
|
||||||
key = forge.util.createBuffer();
|
|
||||||
for(var i = 0; i < tmp.length; ++i) {
|
|
||||||
key.putByte(tmp[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if key is longer than blocksize, hash it
|
|
||||||
var keylen = key.length();
|
|
||||||
if(keylen > _md.blockLength) {
|
|
||||||
_md.start();
|
|
||||||
_md.update(key.bytes());
|
|
||||||
key = _md.digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
// mix key into inner and outer padding
|
|
||||||
// ipadding = [0x36 * blocksize] ^ key
|
|
||||||
// opadding = [0x5C * blocksize] ^ key
|
|
||||||
_ipadding = forge.util.createBuffer();
|
|
||||||
_opadding = forge.util.createBuffer();
|
|
||||||
keylen = key.length();
|
|
||||||
for(var i = 0; i < keylen; ++i) {
|
|
||||||
var tmp = key.at(i);
|
|
||||||
_ipadding.putByte(0x36 ^ tmp);
|
|
||||||
_opadding.putByte(0x5C ^ tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if key is shorter than blocksize, add additional padding
|
|
||||||
if(keylen < _md.blockLength) {
|
|
||||||
var tmp = _md.blockLength - keylen;
|
|
||||||
for(var i = 0; i < tmp; ++i) {
|
|
||||||
_ipadding.putByte(0x36);
|
|
||||||
_opadding.putByte(0x5C);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_key = key;
|
|
||||||
_ipadding = _ipadding.bytes();
|
|
||||||
_opadding = _opadding.bytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// digest is done like so: hash(opadding | hash(ipadding | message))
|
|
||||||
|
|
||||||
// prepare to do inner hash
|
|
||||||
// hash(ipadding | message)
|
|
||||||
_md.start();
|
|
||||||
_md.update(_ipadding);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the HMAC with the given message bytes.
|
|
||||||
*
|
|
||||||
* @param bytes the bytes to update with.
|
|
||||||
*/
|
|
||||||
ctx.update = function(bytes) {
|
|
||||||
_md.update(bytes);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the Message Authentication Code (MAC).
|
|
||||||
*
|
|
||||||
* @return a byte buffer containing the digest value.
|
|
||||||
*/
|
|
||||||
ctx.getMac = function() {
|
|
||||||
// digest is done like so: hash(opadding | hash(ipadding | message))
|
|
||||||
// here we do the outer hashing
|
|
||||||
var inner = _md.digest().bytes();
|
|
||||||
_md.start();
|
|
||||||
_md.update(_opadding);
|
|
||||||
_md.update(inner);
|
|
||||||
return _md.digest();
|
|
||||||
};
|
|
||||||
// alias for getMac
|
|
||||||
ctx.digest = ctx.getMac;
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'hmac';
|
|
||||||
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', './md', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
1413
src/lib/http.js
1413
src/lib/http.js
File diff suppressed because it is too large
Load Diff
2
src/lib/jquery-1.8.2.min.js
vendored
2
src/lib/jquery-1.8.2.min.js
vendored
File diff suppressed because one or more lines are too long
6
src/lib/jquery/jquery-2.0.3.min.js
vendored
Normal file
6
src/lib/jquery/jquery-2.0.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1323
src/lib/jsbn.js
1323
src/lib/jsbn.js
File diff suppressed because it is too large
Load Diff
377
src/lib/log.js
377
src/lib/log.js
@ -1,377 +0,0 @@
|
|||||||
/**
|
|
||||||
* Cross-browser support for logging in a web application.
|
|
||||||
*
|
|
||||||
* @author David I. Lehn <dlehn@digitalbazaar.com>
|
|
||||||
*
|
|
||||||
* Copyright (c) 2008-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
/* LOG API */
|
|
||||||
forge.log = forge.log || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application logging system.
|
|
||||||
*
|
|
||||||
* Each logger level available as it's own function of the form:
|
|
||||||
* forge.log.level(category, args...)
|
|
||||||
* The category is an arbitrary string, and the args are the same as
|
|
||||||
* Firebug's console.log API. By default the call will be output as:
|
|
||||||
* 'LEVEL [category] <args[0]>, args[1], ...'
|
|
||||||
* This enables proper % formatting via the first argument.
|
|
||||||
* Each category is enabled by default but can be enabled or disabled with
|
|
||||||
* the setCategoryEnabled() function.
|
|
||||||
*/
|
|
||||||
// list of known levels
|
|
||||||
forge.log.levels = [
|
|
||||||
'none', 'error', 'warning', 'info', 'debug', 'verbose', 'max'];
|
|
||||||
// info on the levels indexed by name:
|
|
||||||
// index: level index
|
|
||||||
// name: uppercased display name
|
|
||||||
var sLevelInfo = {};
|
|
||||||
// list of loggers
|
|
||||||
var sLoggers = [];
|
|
||||||
/**
|
|
||||||
* Standard console logger. If no console support is enabled this will
|
|
||||||
* remain null. Check before using.
|
|
||||||
*/
|
|
||||||
var sConsoleLogger = null;
|
|
||||||
|
|
||||||
// logger flags
|
|
||||||
/**
|
|
||||||
* Lock the level at the current value. Used in cases where user config may
|
|
||||||
* set the level such that only critical messages are seen but more verbose
|
|
||||||
* messages are needed for debugging or other purposes.
|
|
||||||
*/
|
|
||||||
forge.log.LEVEL_LOCKED = (1 << 1);
|
|
||||||
/**
|
|
||||||
* Always call log function. By default, the logging system will check the
|
|
||||||
* message level against logger.level before calling the log function. This
|
|
||||||
* flag allows the function to do its own check.
|
|
||||||
*/
|
|
||||||
forge.log.NO_LEVEL_CHECK = (1 << 2);
|
|
||||||
/**
|
|
||||||
* Perform message interpolation with the passed arguments. "%" style
|
|
||||||
* fields in log messages will be replaced by arguments as needed. Some
|
|
||||||
* loggers, such as Firebug, may do this automatically. The original log
|
|
||||||
* message will be available as 'message' and the interpolated version will
|
|
||||||
* be available as 'fullMessage'.
|
|
||||||
*/
|
|
||||||
forge.log.INTERPOLATE = (1 << 3);
|
|
||||||
|
|
||||||
// setup each log level
|
|
||||||
for(var i = 0; i < forge.log.levels.length; ++i) {
|
|
||||||
var level = forge.log.levels[i];
|
|
||||||
sLevelInfo[level] = {
|
|
||||||
index: i,
|
|
||||||
name: level.toUpperCase()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message logger. Will dispatch a message to registered loggers as needed.
|
|
||||||
*
|
|
||||||
* @param message message object
|
|
||||||
*/
|
|
||||||
forge.log.logMessage = function(message) {
|
|
||||||
var messageLevelIndex = sLevelInfo[message.level].index;
|
|
||||||
for(var i = 0; i < sLoggers.length; ++i) {
|
|
||||||
var logger = sLoggers[i];
|
|
||||||
if(logger.flags & forge.log.NO_LEVEL_CHECK) {
|
|
||||||
logger.f(message);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// get logger level
|
|
||||||
var loggerLevelIndex = sLevelInfo[logger.level].index;
|
|
||||||
// check level
|
|
||||||
if(messageLevelIndex <= loggerLevelIndex) {
|
|
||||||
// message critical enough, call logger
|
|
||||||
logger.f(logger, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the 'standard' key on a message object to:
|
|
||||||
* "LEVEL [category] " + message
|
|
||||||
*
|
|
||||||
* @param message a message log object
|
|
||||||
*/
|
|
||||||
forge.log.prepareStandard = function(message) {
|
|
||||||
if(!('standard' in message)) {
|
|
||||||
message.standard =
|
|
||||||
sLevelInfo[message.level].name +
|
|
||||||
//' ' + +message.timestamp +
|
|
||||||
' [' + message.category + '] ' +
|
|
||||||
message.message;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the 'full' key on a message object to the original message
|
|
||||||
* interpolated via % formatting with the message arguments.
|
|
||||||
*
|
|
||||||
* @param message a message log object.
|
|
||||||
*/
|
|
||||||
forge.log.prepareFull = function(message) {
|
|
||||||
if(!('full' in message)) {
|
|
||||||
// copy args and insert message at the front
|
|
||||||
var args = [message.message];
|
|
||||||
args = args.concat([] || message['arguments']);
|
|
||||||
// format the message
|
|
||||||
message.full = forge.util.format.apply(this, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies both preparseStandard() and prepareFull() to a message object and
|
|
||||||
* store result in 'standardFull'.
|
|
||||||
*
|
|
||||||
* @param message a message log object.
|
|
||||||
*/
|
|
||||||
forge.log.prepareStandardFull = function(message) {
|
|
||||||
if(!('standardFull' in message)) {
|
|
||||||
// FIXME implement 'standardFull' logging
|
|
||||||
forge.log.prepareStandard(message);
|
|
||||||
message.standardFull = message.standard;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// create log level functions
|
|
||||||
if(true) {
|
|
||||||
// levels for which we want functions
|
|
||||||
var levels = ['error', 'warning', 'info', 'debug', 'verbose'];
|
|
||||||
for(var i = 0; i < levels.length; ++i) {
|
|
||||||
// wrap in a function to ensure proper level var is passed
|
|
||||||
(function(level) {
|
|
||||||
// create function for this level
|
|
||||||
forge.log[level] = function(category, message/*, args...*/) {
|
|
||||||
// convert arguments to real array, remove category and message
|
|
||||||
var args = Array.prototype.slice.call(arguments).slice(2);
|
|
||||||
// create message object
|
|
||||||
// Note: interpolation and standard formatting is done lazily
|
|
||||||
var msg = {
|
|
||||||
timestamp: new Date(),
|
|
||||||
level: level,
|
|
||||||
category: category,
|
|
||||||
message: message,
|
|
||||||
'arguments': args
|
|
||||||
/*standard*/
|
|
||||||
/*full*/
|
|
||||||
/*fullMessage*/
|
|
||||||
};
|
|
||||||
// process this message
|
|
||||||
forge.log.logMessage(msg);
|
|
||||||
};
|
|
||||||
})(levels[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new logger with specified custom logging function.
|
|
||||||
*
|
|
||||||
* The logging function has a signature of:
|
|
||||||
* function(logger, message)
|
|
||||||
* logger: current logger
|
|
||||||
* message: object:
|
|
||||||
* level: level id
|
|
||||||
* category: category
|
|
||||||
* message: string message
|
|
||||||
* arguments: Array of extra arguments
|
|
||||||
* fullMessage: interpolated message and arguments if INTERPOLATE flag set
|
|
||||||
*
|
|
||||||
* @param logFunction a logging function which takes a log message object
|
|
||||||
* as a parameter.
|
|
||||||
*
|
|
||||||
* @return a logger object.
|
|
||||||
*/
|
|
||||||
forge.log.makeLogger = function(logFunction) {
|
|
||||||
var logger = {
|
|
||||||
flags: 0,
|
|
||||||
f: logFunction
|
|
||||||
};
|
|
||||||
forge.log.setLevel(logger, 'none');
|
|
||||||
return logger;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current log level on a logger.
|
|
||||||
*
|
|
||||||
* @param logger the target logger.
|
|
||||||
* @param level the new maximum log level as a string.
|
|
||||||
*
|
|
||||||
* @return true if set, false if not.
|
|
||||||
*/
|
|
||||||
forge.log.setLevel = function(logger, level) {
|
|
||||||
var rval = false;
|
|
||||||
if(logger && !(logger.flags & forge.log.LEVEL_LOCKED)) {
|
|
||||||
for(var i = 0; i < forge.log.levels.length; ++i) {
|
|
||||||
var aValidLevel = forge.log.levels[i];
|
|
||||||
if(level == aValidLevel) {
|
|
||||||
// set level
|
|
||||||
logger.level = level;
|
|
||||||
rval = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locks the log level at its current value.
|
|
||||||
*
|
|
||||||
* @param logger the target logger.
|
|
||||||
* @param lock boolean lock value, default to true.
|
|
||||||
*/
|
|
||||||
forge.log.lock = function(logger, lock) {
|
|
||||||
if(typeof lock === 'undefined' || lock) {
|
|
||||||
logger.flags |= forge.log.LEVEL_LOCKED;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger.flags &= ~forge.log.LEVEL_LOCKED;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a logger.
|
|
||||||
*
|
|
||||||
* @param logger the logger object.
|
|
||||||
*/
|
|
||||||
forge.log.addLogger = function(logger) {
|
|
||||||
sLoggers.push(logger);
|
|
||||||
};
|
|
||||||
|
|
||||||
// setup the console logger if possible, else create fake console.log
|
|
||||||
if(typeof(console) !== 'undefined' && 'log' in console) {
|
|
||||||
var logger;
|
|
||||||
if(console.error && console.warn && console.info && console.debug) {
|
|
||||||
// looks like Firebug-style logging is available
|
|
||||||
// level handlers map
|
|
||||||
var levelHandlers = {
|
|
||||||
error: console.error,
|
|
||||||
warning: console.warn,
|
|
||||||
info: console.info,
|
|
||||||
debug: console.debug,
|
|
||||||
verbose: console.debug
|
|
||||||
};
|
|
||||||
var f = function(logger, message) {
|
|
||||||
forge.log.prepareStandard(message);
|
|
||||||
var handler = levelHandlers[message.level];
|
|
||||||
// prepend standard message and concat args
|
|
||||||
var args = [message.standard];
|
|
||||||
args = args.concat(message['arguments'].slice());
|
|
||||||
// apply to low-level console function
|
|
||||||
handler.apply(console, args);
|
|
||||||
};
|
|
||||||
logger = forge.log.makeLogger(f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// only appear to have basic console.log
|
|
||||||
var f = function(logger, message) {
|
|
||||||
forge.log.prepareStandardFull(message);
|
|
||||||
console.log(message.standardFull);
|
|
||||||
};
|
|
||||||
logger = forge.log.makeLogger(f);
|
|
||||||
}
|
|
||||||
forge.log.setLevel(logger, 'debug');
|
|
||||||
forge.log.addLogger(logger);
|
|
||||||
sConsoleLogger = logger;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// define fake console.log to avoid potential script errors on
|
|
||||||
// browsers that do not have console logging
|
|
||||||
console = {
|
|
||||||
log: function() {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for logging control query vars.
|
|
||||||
*
|
|
||||||
* console.level=<level-name>
|
|
||||||
* Set's the console log level by name. Useful to override defaults and
|
|
||||||
* allow more verbose logging before a user config is loaded.
|
|
||||||
*
|
|
||||||
* console.lock=<true|false>
|
|
||||||
* Lock the console log level at whatever level it is set at. This is run
|
|
||||||
* after console.level is processed. Useful to force a level of verbosity
|
|
||||||
* that could otherwise be limited by a user config.
|
|
||||||
*/
|
|
||||||
if(sConsoleLogger !== null) {
|
|
||||||
var query = forge.util.getQueryVariables();
|
|
||||||
if('console.level' in query) {
|
|
||||||
// set with last value
|
|
||||||
forge.log.setLevel(
|
|
||||||
sConsoleLogger, query['console.level'].slice(-1)[0]);
|
|
||||||
}
|
|
||||||
if('console.lock' in query) {
|
|
||||||
// set with last value
|
|
||||||
var lock = query['console.lock'].slice(-1)[0];
|
|
||||||
if(lock == 'true') {
|
|
||||||
forge.log.lock(sConsoleLogger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// provide public access to console logger
|
|
||||||
forge.log.consoleLogger = sConsoleLogger;
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'log';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,75 +0,0 @@
|
|||||||
/**
|
|
||||||
* Node.js module for Forge message digests.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright 2011-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
forge.md = forge.md || {};
|
|
||||||
forge.md.algorithms = {
|
|
||||||
md5: forge.md5,
|
|
||||||
sha1: forge.sha1,
|
|
||||||
sha256: forge.sha256
|
|
||||||
};
|
|
||||||
forge.md.md5 = forge.md5;
|
|
||||||
forge.md.sha1 = forge.sha1;
|
|
||||||
forge.md.sha256 = forge.sha256;
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'md';
|
|
||||||
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', './md5', './sha1', './sha256'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
319
src/lib/md5.js
319
src/lib/md5.js
@ -1,319 +0,0 @@
|
|||||||
/**
|
|
||||||
* Message Digest Algorithm 5 with 128-bit digest (MD5) implementation.
|
|
||||||
*
|
|
||||||
* This implementation is currently limited to message lengths (in bytes) that
|
|
||||||
* are up to 32-bits in size.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var md5 = forge.md5 = forge.md5 || {};
|
|
||||||
forge.md = forge.md || {};
|
|
||||||
forge.md.algorithms = forge.md.algorithms || {};
|
|
||||||
forge.md.md5 = forge.md.algorithms['md5'] = md5;
|
|
||||||
|
|
||||||
// padding, constant tables for calculating md5
|
|
||||||
var _padding = null;
|
|
||||||
var _g = null;
|
|
||||||
var _r = null;
|
|
||||||
var _k = null;
|
|
||||||
var _initialized = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the constant tables.
|
|
||||||
*/
|
|
||||||
var _init = function() {
|
|
||||||
// create padding
|
|
||||||
_padding = String.fromCharCode(128);
|
|
||||||
_padding += forge.util.fillString(String.fromCharCode(0x00), 64);
|
|
||||||
|
|
||||||
// g values
|
|
||||||
_g = [
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,
|
|
||||||
5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,
|
|
||||||
0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9];
|
|
||||||
|
|
||||||
// rounds table
|
|
||||||
_r = [
|
|
||||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
|
||||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
|
||||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
|
||||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];
|
|
||||||
|
|
||||||
// get the result of abs(sin(i + 1)) as a 32-bit integer
|
|
||||||
_k = new Array(64);
|
|
||||||
for(var i = 0; i < 64; ++i) {
|
|
||||||
_k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now initialized
|
|
||||||
_initialized = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an MD5 state with the given byte buffer.
|
|
||||||
*
|
|
||||||
* @param s the MD5 state to update.
|
|
||||||
* @param w the array to use to store words.
|
|
||||||
* @param bytes the byte buffer to update with.
|
|
||||||
*/
|
|
||||||
var _update = function(s, w, bytes) {
|
|
||||||
// consume 512 bit (64 byte) chunks
|
|
||||||
var t, a, b, c, d, f, r, i;
|
|
||||||
var len = bytes.length();
|
|
||||||
while(len >= 64) {
|
|
||||||
// initialize hash value for this chunk
|
|
||||||
a = s.h0;
|
|
||||||
b = s.h1;
|
|
||||||
c = s.h2;
|
|
||||||
d = s.h3;
|
|
||||||
|
|
||||||
// round 1
|
|
||||||
for(i = 0; i < 16; ++i) {
|
|
||||||
w[i] = bytes.getInt32Le();
|
|
||||||
f = d ^ (b & (c ^ d));
|
|
||||||
t = (a + f + _k[i] + w[i]);
|
|
||||||
r = _r[i];
|
|
||||||
a = d;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b += (t << r) | (t >>> (32 - r));
|
|
||||||
}
|
|
||||||
// round 2
|
|
||||||
for(; i < 32; ++i) {
|
|
||||||
f = c ^ (d & (b ^ c));
|
|
||||||
t = (a + f + _k[i] + w[_g[i]]);
|
|
||||||
r = _r[i];
|
|
||||||
a = d;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b += (t << r) | (t >>> (32 - r));
|
|
||||||
}
|
|
||||||
// round 3
|
|
||||||
for(; i < 48; ++i) {
|
|
||||||
f = b ^ c ^ d;
|
|
||||||
t = (a + f + _k[i] + w[_g[i]]);
|
|
||||||
r = _r[i];
|
|
||||||
a = d;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b += (t << r) | (t >>> (32 - r));
|
|
||||||
}
|
|
||||||
// round 4
|
|
||||||
for(; i < 64; ++i) {
|
|
||||||
f = c ^ (b | ~d);
|
|
||||||
t = (a + f + _k[i] + w[_g[i]]);
|
|
||||||
r = _r[i];
|
|
||||||
a = d;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b += (t << r) | (t >>> (32 - r));
|
|
||||||
}
|
|
||||||
|
|
||||||
// update hash state
|
|
||||||
s.h0 = (s.h0 + a) & 0xFFFFFFFF;
|
|
||||||
s.h1 = (s.h1 + b) & 0xFFFFFFFF;
|
|
||||||
s.h2 = (s.h2 + c) & 0xFFFFFFFF;
|
|
||||||
s.h3 = (s.h3 + d) & 0xFFFFFFFF;
|
|
||||||
|
|
||||||
len -= 64;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an MD5 message digest object.
|
|
||||||
*
|
|
||||||
* @return a message digest object.
|
|
||||||
*/
|
|
||||||
md5.create = function() {
|
|
||||||
// do initialization as necessary
|
|
||||||
if(!_initialized) {
|
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// MD5 state contains four 32-bit integers
|
|
||||||
var _state = null;
|
|
||||||
|
|
||||||
// input buffer
|
|
||||||
var _input = forge.util.createBuffer();
|
|
||||||
|
|
||||||
// used for word storage
|
|
||||||
var _w = new Array(16);
|
|
||||||
|
|
||||||
// message digest object
|
|
||||||
var md = {
|
|
||||||
algorithm: 'md5',
|
|
||||||
blockLength: 64,
|
|
||||||
digestLength: 16,
|
|
||||||
// length of message so far (does not including padding)
|
|
||||||
messageLength: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the digest.
|
|
||||||
*
|
|
||||||
* @return this digest object.
|
|
||||||
*/
|
|
||||||
md.start = function() {
|
|
||||||
md.messageLength = 0;
|
|
||||||
_input = forge.util.createBuffer();
|
|
||||||
_state = {
|
|
||||||
h0: 0x67452301,
|
|
||||||
h1: 0xEFCDAB89,
|
|
||||||
h2: 0x98BADCFE,
|
|
||||||
h3: 0x10325476
|
|
||||||
};
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
// start digest automatically for first time
|
|
||||||
md.start();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the digest with the given message input. The given input can
|
|
||||||
* treated as raw input (no encoding will be applied) or an encoding of
|
|
||||||
* 'utf8' maybe given to encode the input using UTF-8.
|
|
||||||
*
|
|
||||||
* @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') {
|
|
||||||
msg = forge.util.encodeUtf8(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update message length
|
|
||||||
md.messageLength += msg.length;
|
|
||||||
|
|
||||||
// add bytes to input buffer
|
|
||||||
_input.putBytes(msg);
|
|
||||||
|
|
||||||
// process bytes
|
|
||||||
_update(_state, _w, _input);
|
|
||||||
|
|
||||||
// compact input buffer every 2K or if empty
|
|
||||||
if(_input.read > 2048 || _input.length() === 0) {
|
|
||||||
_input.compact();
|
|
||||||
}
|
|
||||||
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the digest.
|
|
||||||
*
|
|
||||||
* @return a byte buffer containing the digest value.
|
|
||||||
*/
|
|
||||||
md.digest = function() {
|
|
||||||
/* Note: Here we copy the remaining bytes in the input buffer and
|
|
||||||
add the appropriate MD5 padding. Then we do the final update
|
|
||||||
on a copy of the state so that if the user wants to get
|
|
||||||
intermediate digests they can do so. */
|
|
||||||
|
|
||||||
/* Determine the number of bytes that must be added to the message
|
|
||||||
to ensure its length is congruent to 448 mod 512. In other words,
|
|
||||||
a 64-bit integer that gives the length of the message will be
|
|
||||||
appended to the message and whatever the length of the message is
|
|
||||||
plus 64 bits must be a multiple of 512. So the length of the
|
|
||||||
message must be congruent to 448 mod 512 because 512 - 64 = 448.
|
|
||||||
|
|
||||||
In order to fill up the message length it must be filled with
|
|
||||||
padding that begins with 1 bit followed by all 0 bits. Padding
|
|
||||||
must *always* be present, so if the message length is already
|
|
||||||
congruent to 448 mod 512, then 512 padding bits must be added. */
|
|
||||||
|
|
||||||
// 512 bits == 64 bytes, 448 bits == 56 bytes, 64 bits = 8 bytes
|
|
||||||
// _padding starts with 1 byte with first bit is set in it which
|
|
||||||
// is byte value 128, then there may be up to 63 other pad bytes
|
|
||||||
var len = md.messageLength;
|
|
||||||
var padBytes = forge.util.createBuffer();
|
|
||||||
padBytes.putBytes(_input.bytes());
|
|
||||||
padBytes.putBytes(_padding.substr(0, 64 - ((len + 8) % 64)));
|
|
||||||
|
|
||||||
/* Now append length of the message. The length is appended in bits
|
|
||||||
as a 64-bit number in little-endian format. Since we store the
|
|
||||||
length in bytes, we must multiply it by 8 (or left shift by 3). So
|
|
||||||
here store the high 3 bits in the high end of the second 32-bits of
|
|
||||||
the 64-bit number and the lower 5 bits in the low end of the
|
|
||||||
second 32-bits. */
|
|
||||||
padBytes.putInt32Le((len << 3) & 0xFFFFFFFF);
|
|
||||||
padBytes.putInt32Le((len >>> 29) & 0xFF);
|
|
||||||
var s2 = {
|
|
||||||
h0: _state.h0,
|
|
||||||
h1: _state.h1,
|
|
||||||
h2: _state.h2,
|
|
||||||
h3: _state.h3
|
|
||||||
};
|
|
||||||
_update(s2, _w, padBytes);
|
|
||||||
var rval = forge.util.createBuffer();
|
|
||||||
rval.putInt32Le(s2.h0);
|
|
||||||
rval.putInt32Le(s2.h1);
|
|
||||||
rval.putInt32Le(s2.h2);
|
|
||||||
rval.putInt32Le(s2.h3);
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'md5';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,68 +0,0 @@
|
|||||||
/**
|
|
||||||
* Node.js module for Forge mask generation functions.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
forge.mgf = forge.mgf || {};
|
|
||||||
forge.mgf.mgf1 = forge.mgf1;
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'mgf';
|
|
||||||
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', './mgf1'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
109
src/lib/mgf1.js
109
src/lib/mgf1.js
@ -1,109 +0,0 @@
|
|||||||
/**
|
|
||||||
* Javascript implementation of mask generation function MGF1.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
forge.mgf = forge.mgf || {};
|
|
||||||
var mgf1 = forge.mgf.mgf1 = forge.mgf1 = forge.mgf1 || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a MGF1 mask generation function object.
|
|
||||||
*
|
|
||||||
* @return a mask generation function object.
|
|
||||||
*/
|
|
||||||
mgf1.create = function(md) {
|
|
||||||
var mgf = {
|
|
||||||
/**
|
|
||||||
* Generate mask of specified length.
|
|
||||||
*
|
|
||||||
* @param {String} seed The seed for mask generation.
|
|
||||||
* @param maskLen Number of bytes to generate.
|
|
||||||
* @return {String} The generated mask.
|
|
||||||
*/
|
|
||||||
generate: function(seed, maskLen) {
|
|
||||||
/* 2. Let T be the empty octet string. */
|
|
||||||
var t = new forge.util.ByteBuffer();
|
|
||||||
|
|
||||||
/* 3. For counter from 0 to ceil(maskLen / hLen), do the following: */
|
|
||||||
var len = Math.ceil(maskLen / md.digestLength);
|
|
||||||
for(var i = 0; i < len; i++) {
|
|
||||||
/* a. Convert counter to an octet string C of length 4 octets */
|
|
||||||
var c = new forge.util.ByteBuffer();
|
|
||||||
c.putInt32(i);
|
|
||||||
|
|
||||||
/* b. Concatenate the hash of the seed mgfSeed and C to the octet
|
|
||||||
* string T: */
|
|
||||||
md.start();
|
|
||||||
md.update(seed + c.getBytes());
|
|
||||||
t.putBuffer(md.digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Output the leading maskLen octets of T as the octet string mask. */
|
|
||||||
t.truncate(t.length() - maskLen);
|
|
||||||
return t.getBytes();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return mgf;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'mgf1';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
265
src/lib/oids.js
265
src/lib/oids.js
@ -1,265 +0,0 @@
|
|||||||
/**
|
|
||||||
* Object IDs for ASN.1.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
forge.pki = forge.pki || {};
|
|
||||||
var oids = forge.pki.oids = forge.oids = forge.oids || {};
|
|
||||||
|
|
||||||
// algorithm 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.7'] = 'RSAES-OAEP';
|
|
||||||
oids['RSAES-OAEP'] = '1.2.840.113549.1.1.7';
|
|
||||||
oids['1.2.840.113549.1.1.8'] = 'mgf1';
|
|
||||||
oids['mgf1'] = '1.2.840.113549.1.1.8';
|
|
||||||
oids['1.2.840.113549.1.1.9'] = 'pSpecified';
|
|
||||||
oids['pSpecified'] = '1.2.840.113549.1.1.9';
|
|
||||||
oids['1.2.840.113549.1.1.10'] = 'RSASSA-PSS';
|
|
||||||
oids['RSASSA-PSS'] = '1.2.840.113549.1.1.10';
|
|
||||||
oids['1.2.840.113549.1.1.11'] = 'sha256WithRSAEncryption';
|
|
||||||
oids['sha256WithRSAEncryption'] = '1.2.840.113549.1.1.11';
|
|
||||||
oids['1.2.840.113549.1.1.12'] = 'sha384WithRSAEncryption';
|
|
||||||
oids['sha384WithRSAEncryption'] = '1.2.840.113549.1.1.12';
|
|
||||||
oids['1.2.840.113549.1.1.13'] = 'sha512WithRSAEncryption';
|
|
||||||
oids['sha512WithRSAEncryption'] = '1.2.840.113549.1.1.13';
|
|
||||||
|
|
||||||
oids['1.3.14.3.2.26'] = 'sha1';
|
|
||||||
oids['sha1'] = '1.3.14.3.2.26';
|
|
||||||
oids['2.16.840.1.101.3.4.2.1'] = 'sha256';
|
|
||||||
oids['sha256'] = '2.16.840.1.101.3.4.2.1';
|
|
||||||
oids['2.16.840.1.101.3.4.2.2'] = 'sha384';
|
|
||||||
oids['sha384'] = '2.16.840.1.101.3.4.2.2';
|
|
||||||
oids['2.16.840.1.101.3.4.2.3'] = 'sha512';
|
|
||||||
oids['sha512'] = '2.16.840.1.101.3.4.2.3';
|
|
||||||
oids['1.2.840.113549.2.5'] = 'md5';
|
|
||||||
oids['md5'] = '1.2.840.113549.2.5';
|
|
||||||
|
|
||||||
// pkcs#7 content types
|
|
||||||
oids['1.2.840.113549.1.7.1'] = 'data';
|
|
||||||
oids['data'] = '1.2.840.113549.1.7.1';
|
|
||||||
oids['1.2.840.113549.1.7.2'] = 'signedData';
|
|
||||||
oids['signedData'] = '1.2.840.113549.1.7.2';
|
|
||||||
oids['1.2.840.113549.1.7.3'] = 'envelopedData';
|
|
||||||
oids['envelopedData'] = '1.2.840.113549.1.7.3';
|
|
||||||
oids['1.2.840.113549.1.7.4'] = 'signedAndEnvelopedData';
|
|
||||||
oids['signedAndEnvelopedData'] = '1.2.840.113549.1.7.4';
|
|
||||||
oids['1.2.840.113549.1.7.5'] = 'digestedData';
|
|
||||||
oids['digestedData'] = '1.2.840.113549.1.7.5';
|
|
||||||
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';
|
|
||||||
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';
|
|
||||||
|
|
||||||
// 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';
|
|
||||||
oids['1.2.840.113549.1.12.10.1.2'] = 'pkcs8ShroudedKeyBag';
|
|
||||||
oids['pkcs8ShroudedKeyBag'] = '1.2.840.113549.1.12.10.1.2';
|
|
||||||
oids['1.2.840.113549.1.12.10.1.3'] = 'certBag';
|
|
||||||
oids['certBag'] = '1.2.840.113549.1.12.10.1.3';
|
|
||||||
oids['1.2.840.113549.1.12.10.1.4'] = 'crlBag';
|
|
||||||
oids['crlBag'] = '1.2.840.113549.1.12.10.1.4';
|
|
||||||
oids['1.2.840.113549.1.12.10.1.5'] = 'secretBag';
|
|
||||||
oids['secretBag'] = '1.2.840.113549.1.12.10.1.5';
|
|
||||||
oids['1.2.840.113549.1.12.10.1.6'] = 'safeContentsBag';
|
|
||||||
oids['safeContentsBag'] = '1.2.840.113549.1.12.10.1.6';
|
|
||||||
|
|
||||||
// password-based-encryption for pkcs#12
|
|
||||||
oids['1.2.840.113549.1.5.13'] = 'pkcs5PBES2';
|
|
||||||
oids['pkcs5PBES2'] = '1.2.840.113549.1.5.13';
|
|
||||||
oids['1.2.840.113549.1.5.12'] = 'pkcs5PBKDF2';
|
|
||||||
oids['pkcs5PBKDF2'] = '1.2.840.113549.1.5.12';
|
|
||||||
|
|
||||||
oids['1.2.840.113549.1.12.1.1'] = 'pbeWithSHAAnd128BitRC4';
|
|
||||||
oids['pbeWithSHAAnd128BitRC4'] = '1.2.840.113549.1.12.1.1';
|
|
||||||
oids['1.2.840.113549.1.12.1.2'] = 'pbeWithSHAAnd40BitRC4';
|
|
||||||
oids['pbeWithSHAAnd40BitRC4'] = '1.2.840.113549.1.12.1.2';
|
|
||||||
oids['1.2.840.113549.1.12.1.3'] = 'pbeWithSHAAnd3-KeyTripleDES-CBC';
|
|
||||||
oids['pbeWithSHAAnd3-KeyTripleDES-CBC'] = '1.2.840.113549.1.12.1.3';
|
|
||||||
oids['1.2.840.113549.1.12.1.4'] = 'pbeWithSHAAnd2-KeyTripleDES-CBC';
|
|
||||||
oids['pbeWithSHAAnd2-KeyTripleDES-CBC'] = '1.2.840.113549.1.12.1.4';
|
|
||||||
oids['1.2.840.113549.1.12.1.5'] = 'pbeWithSHAAnd128BitRC2-CBC';
|
|
||||||
oids['pbeWithSHAAnd128BitRC2-CBC'] = '1.2.840.113549.1.12.1.5';
|
|
||||||
oids['1.2.840.113549.1.12.1.6'] = 'pbewithSHAAnd40BitRC2-CBC';
|
|
||||||
oids['pbewithSHAAnd40BitRC2-CBC'] = '1.2.840.113549.1.12.1.6';
|
|
||||||
|
|
||||||
// symmetric key algorithm oids
|
|
||||||
oids['1.2.840.113549.3.7'] = 'des-EDE3-CBC';
|
|
||||||
oids['des-EDE3-CBC'] = '1.2.840.113549.3.7';
|
|
||||||
oids['2.16.840.1.101.3.4.1.2'] = 'aes128-CBC';
|
|
||||||
oids['aes128-CBC'] = '2.16.840.1.101.3.4.1.2';
|
|
||||||
oids['2.16.840.1.101.3.4.1.22'] = 'aes192-CBC';
|
|
||||||
oids['aes192-CBC'] = '2.16.840.1.101.3.4.1.22';
|
|
||||||
oids['2.16.840.1.101.3.4.1.42'] = 'aes256-CBC';
|
|
||||||
oids['aes256-CBC'] = '2.16.840.1.101.3.4.1.42';
|
|
||||||
|
|
||||||
// certificate issuer/subject OIDs
|
|
||||||
oids['2.5.4.3'] = 'commonName';
|
|
||||||
oids['commonName'] = '2.5.4.3';
|
|
||||||
oids['2.5.4.5'] = 'serialName';
|
|
||||||
oids['serialName'] = '2.5.4.5';
|
|
||||||
oids['2.5.4.6'] = 'countryName';
|
|
||||||
oids['countryName'] = '2.5.4.6';
|
|
||||||
oids['2.5.4.7'] = 'localityName';
|
|
||||||
oids['localityName'] = '2.5.4.7';
|
|
||||||
oids['2.5.4.8'] = 'stateOrProvinceName';
|
|
||||||
oids['stateOrProvinceName'] = '2.5.4.8';
|
|
||||||
oids['2.5.4.10'] = 'organizationName';
|
|
||||||
oids['organizationName'] = '2.5.4.10';
|
|
||||||
oids['2.5.4.11'] = 'organizationalUnitName';
|
|
||||||
oids['organizationalUnitName'] = '2.5.4.11';
|
|
||||||
|
|
||||||
// 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
|
|
||||||
oids['2.5.29.4'] = 'keyUsageRestriction'; // obsolete use .37 or .15
|
|
||||||
oids['2.5.29.5'] = 'policyMapping'; // deprecated use .33
|
|
||||||
oids['2.5.29.6'] = 'subtreesConstraint'; // obsolete use .30
|
|
||||||
oids['2.5.29.7'] = 'subjectAltName'; // deprecated use .17
|
|
||||||
oids['2.5.29.8'] = 'issuerAltName'; // deprecated use .18
|
|
||||||
oids['2.5.29.9'] = 'subjectDirectoryAttributes';
|
|
||||||
oids['2.5.29.10'] = 'basicConstraints'; // deprecated use .19
|
|
||||||
oids['2.5.29.11'] = 'nameConstraints'; // deprecated use .30
|
|
||||||
oids['2.5.29.12'] = 'policyConstraints'; // deprecated use .36
|
|
||||||
oids['2.5.29.13'] = 'basicConstraints'; // deprecated use .19
|
|
||||||
oids['2.5.29.14'] = 'subjectKeyIdentifier';
|
|
||||||
oids['subjectKeyIdentifier'] = '2.5.29.14';
|
|
||||||
oids['2.5.29.15'] = 'keyUsage';
|
|
||||||
oids['keyUsage'] = '2.5.29.15';
|
|
||||||
oids['2.5.29.16'] = 'privateKeyUsagePeriod';
|
|
||||||
oids['2.5.29.17'] = 'subjectAltName';
|
|
||||||
oids['subjectAltName'] = '2.5.29.17';
|
|
||||||
oids['2.5.29.18'] = 'issuerAltName';
|
|
||||||
oids['issuerAltName'] = '2.5.29.18';
|
|
||||||
oids['2.5.29.19'] = 'basicConstraints';
|
|
||||||
oids['basicConstraints'] = '2.5.29.19';
|
|
||||||
oids['2.5.29.20'] = 'cRLNumber';
|
|
||||||
oids['2.5.29.21'] = 'cRLReason';
|
|
||||||
oids['2.5.29.22'] = 'expirationDate';
|
|
||||||
oids['2.5.29.23'] = 'instructionCode';
|
|
||||||
oids['2.5.29.24'] = 'invalidityDate';
|
|
||||||
oids['2.5.29.25'] = 'cRLDistributionPoints'; // deprecated use .31
|
|
||||||
oids['2.5.29.26'] = 'issuingDistributionPoint'; // deprecated use .28
|
|
||||||
oids['2.5.29.27'] = 'deltaCRLIndicator';
|
|
||||||
oids['2.5.29.28'] = 'issuingDistributionPoint';
|
|
||||||
oids['2.5.29.29'] = 'certificateIssuer';
|
|
||||||
oids['2.5.29.30'] = 'nameConstraints';
|
|
||||||
oids['2.5.29.31'] = 'cRLDistributionPoints';
|
|
||||||
oids['2.5.29.32'] = 'certificatePolicies';
|
|
||||||
oids['2.5.29.33'] = 'policyMappings';
|
|
||||||
oids['2.5.29.34'] = 'policyConstraints'; // deprecated use .36
|
|
||||||
oids['2.5.29.35'] = 'authorityKeyIdentifier';
|
|
||||||
oids['2.5.29.36'] = 'policyConstraints';
|
|
||||||
oids['2.5.29.37'] = 'extKeyUsage';
|
|
||||||
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';
|
|
||||||
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'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
943
src/lib/pbe.js
943
src/lib/pbe.js
@ -1,943 +0,0 @@
|
|||||||
/**
|
|
||||||
* Password-based encryption functions.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
* @author Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*
|
|
||||||
* An EncryptedPrivateKeyInfo:
|
|
||||||
*
|
|
||||||
* EncryptedPrivateKeyInfo ::= SEQUENCE {
|
|
||||||
* encryptionAlgorithm EncryptionAlgorithmIdentifier,
|
|
||||||
* encryptedData EncryptedData }
|
|
||||||
*
|
|
||||||
* EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
|
||||||
*
|
|
||||||
* EncryptedData ::= OCTET STRING
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
if(typeof BigInteger === 'undefined') {
|
|
||||||
var BigInteger = forge.jsbn.BigInteger;
|
|
||||||
}
|
|
||||||
|
|
||||||
// shortcut for asn.1 API
|
|
||||||
var asn1 = forge.asn1;
|
|
||||||
|
|
||||||
/* Password-based encryption implementation. */
|
|
||||||
var pki = forge.pki = forge.pki || {};
|
|
||||||
pki.pbe = forge.pbe = forge.pbe || {};
|
|
||||||
var oids = pki.oids;
|
|
||||||
|
|
||||||
// validator for an EncryptedPrivateKeyInfo structure
|
|
||||||
// Note: Currently only works w/algorithm params
|
|
||||||
var encryptedPrivateKeyValidator = {
|
|
||||||
name: 'EncryptedPrivateKeyInfo',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'EncryptedPrivateKeyInfo.encryptionAlgorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'AlgorithmIdentifier.algorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encryptionOid'
|
|
||||||
}, {
|
|
||||||
name: 'AlgorithmIdentifier.parameters',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
captureAsn1: 'encryptionParams'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
// encryptedData
|
|
||||||
name: 'EncryptedPrivateKeyInfo.encryptedData',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OCTETSTRING,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encryptedData'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
// validator for a PBES2Algorithms structure
|
|
||||||
// Note: Currently only works w/PBKDF2 + AES encryption schemes
|
|
||||||
var PBES2AlgorithmsValidator = {
|
|
||||||
name: 'PBES2Algorithms',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'PBES2Algorithms.keyDerivationFunc',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'PBES2Algorithms.keyDerivationFunc.oid',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'kdfOid'
|
|
||||||
}, {
|
|
||||||
name: 'PBES2Algorithms.params',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'PBES2Algorithms.params.salt',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OCTETSTRING,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'kdfSalt'
|
|
||||||
}, {
|
|
||||||
name: 'PBES2Algorithms.params.iterationCount',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
onstructed: true,
|
|
||||||
capture: 'kdfIterationCount'
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: 'PBES2Algorithms.encryptionScheme',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'PBES2Algorithms.encryptionScheme.oid',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encOid'
|
|
||||||
}, {
|
|
||||||
name: 'PBES2Algorithms.encryptionScheme.iv',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OCTETSTRING,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encIv'
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
var pkcs12PbeParamsValidator = {
|
|
||||||
name: 'pkcs-12PbeParams',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'pkcs-12PbeParams.salt',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OCTETSTRING,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'salt'
|
|
||||||
}, {
|
|
||||||
name: 'pkcs-12PbeParams.iterations',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'iterations'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a ASN.1 PrivateKeyInfo object, producing an EncryptedPrivateKeyInfo.
|
|
||||||
*
|
|
||||||
* PBES2Algorithms ALGORITHM-IDENTIFIER ::=
|
|
||||||
* { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
|
|
||||||
*
|
|
||||||
* id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
|
|
||||||
*
|
|
||||||
* PBES2-params ::= SEQUENCE {
|
|
||||||
* keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
|
|
||||||
* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* PBES2-KDFs ALGORITHM-IDENTIFIER ::=
|
|
||||||
* { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
|
|
||||||
*
|
|
||||||
* PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
|
|
||||||
*
|
|
||||||
* PBKDF2-params ::= SEQUENCE {
|
|
||||||
* salt CHOICE {
|
|
||||||
* specified OCTET STRING,
|
|
||||||
* otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
|
|
||||||
* },
|
|
||||||
* iterationCount INTEGER (1..MAX),
|
|
||||||
* keyLength INTEGER (1..MAX) OPTIONAL,
|
|
||||||
* prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param obj the ASN.1 PrivateKeyInfo object.
|
|
||||||
* @param password the password to encrypt with.
|
|
||||||
* @param options:
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @return the ASN.1 EncryptedPrivateKeyInfo.
|
|
||||||
*/
|
|
||||||
pki.encryptPrivateKeyInfo = function(obj, password, options) {
|
|
||||||
// set default options
|
|
||||||
options = options || {};
|
|
||||||
options.saltSize = options.saltSize || 8;
|
|
||||||
options.count = options.count || 2048;
|
|
||||||
options.algorithm = options.algorithm || 'aes128';
|
|
||||||
|
|
||||||
// generate PBE params
|
|
||||||
var salt = forge.random.getBytes(options.saltSize);
|
|
||||||
var count = options.count;
|
|
||||||
var countBytes = forge.util.createBuffer();
|
|
||||||
countBytes.putInt16(count);
|
|
||||||
var dkLen;
|
|
||||||
var encryptionAlgorithm;
|
|
||||||
var encryptedData;
|
|
||||||
if(options.algorithm.indexOf('aes') === 0) {
|
|
||||||
// Do PBES2
|
|
||||||
var encOid;
|
|
||||||
if(options.algorithm === 'aes128') {
|
|
||||||
dkLen = 16;
|
|
||||||
encOid = oids['aes128-CBC'];
|
|
||||||
}
|
|
||||||
else if(options.algorithm === 'aes192') {
|
|
||||||
dkLen = 24;
|
|
||||||
encOid = oids['aes192-CBC'];
|
|
||||||
}
|
|
||||||
else if(options.algorithm === 'aes256') {
|
|
||||||
dkLen = 32;
|
|
||||||
encOid = oids['aes256-CBC'];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot encrypt private key. Unknown encryption algorithm.',
|
|
||||||
algorithm: options.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// encrypt private key using pbe SHA-1 and AES
|
|
||||||
var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen);
|
|
||||||
var iv = forge.random.getBytes(16);
|
|
||||||
var cipher = forge.aes.createEncryptionCipher(dk);
|
|
||||||
cipher.start(iv);
|
|
||||||
cipher.update(asn1.toDer(obj));
|
|
||||||
cipher.finish();
|
|
||||||
encryptedData = cipher.output.getBytes();
|
|
||||||
|
|
||||||
encryptionAlgorithm = asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(oids['pkcs5PBES2']).getBytes()),
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// keyDerivationFunc
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(oids['pkcs5PBKDF2']).getBytes()),
|
|
||||||
// PBKDF2-params
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// salt
|
|
||||||
asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
|
|
||||||
// iteration count
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
|
||||||
countBytes.getBytes())
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
// encryptionScheme
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(encOid).getBytes()),
|
|
||||||
// iv
|
|
||||||
asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, iv)
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else if(options.algorithm === '3des') {
|
|
||||||
// Do PKCS12 PBE
|
|
||||||
dkLen = 24;
|
|
||||||
|
|
||||||
var saltBytes = new forge.util.ByteBuffer(salt);
|
|
||||||
var dk = pki.pbe.generatePkcs12Key(password, saltBytes, 1, count, dkLen);
|
|
||||||
var iv = pki.pbe.generatePkcs12Key(password, saltBytes, 2, count, dkLen);
|
|
||||||
var cipher = forge.des.createEncryptionCipher(dk);
|
|
||||||
cipher.start(iv);
|
|
||||||
cipher.update(asn1.toDer(obj));
|
|
||||||
cipher.finish();
|
|
||||||
encryptedData = cipher.output.getBytes();
|
|
||||||
|
|
||||||
encryptionAlgorithm = asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(oids['pbeWithSHAAnd3-KeyTripleDES-CBC']).getBytes()),
|
|
||||||
// pkcs-12PbeParams
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// salt
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
|
|
||||||
// iteration count
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
|
||||||
countBytes.getBytes())
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot encrypt private key. Unknown encryption algorithm.',
|
|
||||||
algorithm: options.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptedPrivateKeyInfo
|
|
||||||
var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// encryptionAlgorithm
|
|
||||||
encryptionAlgorithm,
|
|
||||||
// encryptedData
|
|
||||||
asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, encryptedData)
|
|
||||||
]);
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a ASN.1 PrivateKeyInfo object.
|
|
||||||
*
|
|
||||||
* @param obj the ASN.1 EncryptedPrivateKeyInfo object.
|
|
||||||
* @param password the password to decrypt with.
|
|
||||||
*
|
|
||||||
* @return the ASN.1 PrivateKeyInfo on success, null on failure.
|
|
||||||
*/
|
|
||||||
pki.decryptPrivateKeyInfo = function(obj, password) {
|
|
||||||
var rval = null;
|
|
||||||
|
|
||||||
// get PBE params
|
|
||||||
var capture = {};
|
|
||||||
var errors = [];
|
|
||||||
if(!asn1.validate(obj, encryptedPrivateKeyValidator, capture, errors)) {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read encrypted private key. ' +
|
|
||||||
'ASN.1 object is not a supported EncryptedPrivateKeyInfo.',
|
|
||||||
errors: errors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// get cipher
|
|
||||||
var oid = asn1.derToOid(capture.encryptionOid);
|
|
||||||
var cipher = pki.pbe.getCipher(oid, capture.encryptionParams, password);
|
|
||||||
|
|
||||||
// get encrypted data
|
|
||||||
var encrypted = forge.util.createBuffer(capture.encryptedData);
|
|
||||||
|
|
||||||
cipher.update(encrypted);
|
|
||||||
if(cipher.finish()) {
|
|
||||||
rval = asn1.fromDer(cipher.output);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a EncryptedPrivateKeyInfo to PEM format.
|
|
||||||
*
|
|
||||||
* @param epki the EncryptedPrivateKeyInfo.
|
|
||||||
* @param maxline the maximum characters per line, defaults to 64.
|
|
||||||
*
|
|
||||||
* @return the PEM-formatted encrypted private key.
|
|
||||||
*/
|
|
||||||
pki.encryptedPrivateKeyToPem = function(epki, maxline) {
|
|
||||||
// convert to DER, then PEM-encode
|
|
||||||
var msg = {
|
|
||||||
type: 'ENCRYPTED PRIVATE KEY',
|
|
||||||
body: asn1.toDer(epki).getBytes()
|
|
||||||
};
|
|
||||||
return forge.pem.encode(msg, {maxline: maxline});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format. Decryption
|
|
||||||
* is not performed.
|
|
||||||
*
|
|
||||||
* @param pem the EncryptedPrivateKeyInfo in PEM-format.
|
|
||||||
*
|
|
||||||
* @return the ASN.1 EncryptedPrivateKeyInfo.
|
|
||||||
*/
|
|
||||||
pki.encryptedPrivateKeyFromPem = function(pem) {
|
|
||||||
var msg = forge.pem.decode(pem)[0];
|
|
||||||
|
|
||||||
if(msg.type !== 'ENCRYPTED PRIVATE KEY') {
|
|
||||||
throw {
|
|
||||||
message: 'Could not convert encrypted private key from PEM; PEM header ' +
|
|
||||||
'type is "ENCRYPTED PRIVATE KEY".',
|
|
||||||
headerType: msg.type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
|
|
||||||
throw {
|
|
||||||
message: 'Could not convert encrypted private key from PEM; ' +
|
|
||||||
'PEM is encrypted.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert DER to ASN.1 object
|
|
||||||
return asn1.fromDer(msg.body);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts an RSA private key. By default, the key will be wrapped in
|
|
||||||
* a PrivateKeyInfo and encrypted to produce a PKCS#8 EncryptedPrivateKeyInfo.
|
|
||||||
* This is the standard, preferred way to encrypt a private key.
|
|
||||||
*
|
|
||||||
* To produce a non-standard PEM-encrypted private key that uses encapsulated
|
|
||||||
* headers to indicate the encryption algorithm (old-style non-PKCS#8 OpenSSL
|
|
||||||
* private key encryption), set the 'legacy' option to true. Note: Using this
|
|
||||||
* option will cause the iteration count to be forced to 1.
|
|
||||||
*
|
|
||||||
* @param rsaKey the RSA key to encrypt.
|
|
||||||
* @param password the password to use.
|
|
||||||
* @param options:
|
|
||||||
* algorithm: the encryption algorithm to use
|
|
||||||
* ('aes128', 'aes192', 'aes256', '3des').
|
|
||||||
* count: the iteration count to use.
|
|
||||||
* saltSize: the salt size to use.
|
|
||||||
* legacy: output an old non-PKCS#8 PEM-encrypted+encapsulated
|
|
||||||
* headers (DEK-Info) private key.
|
|
||||||
*
|
|
||||||
* @return the PEM-encoded ASN.1 EncryptedPrivateKeyInfo.
|
|
||||||
*/
|
|
||||||
pki.encryptRsaPrivateKey = function(rsaKey, password, options) {
|
|
||||||
// standard PKCS#8
|
|
||||||
options = options || {};
|
|
||||||
if(!options.legacy) {
|
|
||||||
// encrypt PrivateKeyInfo
|
|
||||||
var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));
|
|
||||||
rval = pki.encryptPrivateKeyInfo(rval, password, options);
|
|
||||||
return pki.encryptedPrivateKeyToPem(rval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// legacy non-PKCS#8
|
|
||||||
var algorithm;
|
|
||||||
var iv;
|
|
||||||
var dkLen;
|
|
||||||
var cipherFn;
|
|
||||||
switch(options.algorithm) {
|
|
||||||
case 'aes128':
|
|
||||||
algorithm = 'AES-128-CBC';
|
|
||||||
dkLen = 16;
|
|
||||||
iv = forge.random.getBytes(16);
|
|
||||||
cipherFn = forge.aes.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
case 'aes192':
|
|
||||||
algorithm = 'AES-192-CBC';
|
|
||||||
dkLen = 24;
|
|
||||||
iv = forge.random.getBytes(16);
|
|
||||||
cipherFn = forge.aes.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
case 'aes256':
|
|
||||||
algorithm = 'AES-256-CBC';
|
|
||||||
dkLen = 32;
|
|
||||||
iv = forge.random.getBytes(16);
|
|
||||||
cipherFn = forge.aes.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
case '3des':
|
|
||||||
algorithm = 'DES-EDE3-CBC';
|
|
||||||
dkLen = 24;
|
|
||||||
iv = forge.random.getBytes(8);
|
|
||||||
cipherFn = forge.des.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Could not encrypt RSA private key; unsupported encryption ' +
|
|
||||||
'algorithm "' + options.algorithm + '".',
|
|
||||||
algorithm: options.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// encrypt private key using OpenSSL legacy key derivation
|
|
||||||
var dk = evpBytesToKey(password, iv.substr(0, 8), dkLen);
|
|
||||||
var cipher = cipherFn(dk);
|
|
||||||
cipher.start(iv);
|
|
||||||
cipher.update(asn1.toDer(pki.privateKeyToAsn1(rsaKey)));
|
|
||||||
cipher.finish();
|
|
||||||
|
|
||||||
var msg = {
|
|
||||||
type: 'RSA PRIVATE KEY',
|
|
||||||
procType: {
|
|
||||||
version: '4',
|
|
||||||
type: 'ENCRYPTED'
|
|
||||||
},
|
|
||||||
dekInfo: {
|
|
||||||
algorithm: algorithm,
|
|
||||||
parameters: forge.util.bytesToHex(iv).toUpperCase()
|
|
||||||
},
|
|
||||||
body: cipher.output.getBytes()
|
|
||||||
};
|
|
||||||
return forge.pem.encode(msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts an RSA private key.
|
|
||||||
*
|
|
||||||
* @param pem the PEM-formatted EncryptedPrivateKeyInfo to decrypt.
|
|
||||||
* @param password the password to use.
|
|
||||||
*
|
|
||||||
* @return the RSA key on success, null on failure.
|
|
||||||
*/
|
|
||||||
pki.decryptRsaPrivateKey = function(pem, password) {
|
|
||||||
var rval = null;
|
|
||||||
|
|
||||||
var msg = forge.pem.decode(pem)[0];
|
|
||||||
|
|
||||||
if(msg.type !== 'ENCRYPTED PRIVATE KEY' &&
|
|
||||||
msg.type !== 'PRIVATE KEY' &&
|
|
||||||
msg.type !== 'RSA PRIVATE KEY') {
|
|
||||||
throw {
|
|
||||||
message: 'Could not convert private key from PEM; PEM header type is ' +
|
|
||||||
'not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".',
|
|
||||||
headerType: msg.type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
|
|
||||||
var dkLen;
|
|
||||||
var cipherFn;
|
|
||||||
switch(msg.dekInfo.algorithm) {
|
|
||||||
case 'DES-EDE3-CBC':
|
|
||||||
dkLen = 24;
|
|
||||||
cipherFn = forge.des.createDecryptionCipher;
|
|
||||||
break;
|
|
||||||
case 'AES-128-CBC':
|
|
||||||
dkLen = 16;
|
|
||||||
cipherFn = forge.aes.createDecryptionCipher;
|
|
||||||
break;
|
|
||||||
case 'AES-192-CBC':
|
|
||||||
dkLen = 24;
|
|
||||||
cipherFn = forge.aes.createDecryptionCipher;
|
|
||||||
break;
|
|
||||||
case 'AES-256-CBC':
|
|
||||||
dkLen = 32;
|
|
||||||
cipherFn = forge.aes.createDecryptionCipher;
|
|
||||||
break;
|
|
||||||
case 'RC2-40-CBC':
|
|
||||||
dkLen = 5;
|
|
||||||
cipherFn = function(key) {
|
|
||||||
return forge.rc2.createDecryptionCipher(key, 40);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'RC2-64-CBC':
|
|
||||||
dkLen = 8;
|
|
||||||
cipherFn = function(key) {
|
|
||||||
return forge.rc2.createDecryptionCipher(key, 64);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'RC2-128-CBC':
|
|
||||||
dkLen = 16;
|
|
||||||
cipherFn = function(key) {
|
|
||||||
return forge.rc2.createDecryptionCipher(key, 128);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Could not decrypt private key; unsupported encryption ' +
|
|
||||||
'algorithm "' + msg.dekInfo.algorithm + '".',
|
|
||||||
algorithm: msg.dekInfo.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// use OpenSSL legacy key derivation
|
|
||||||
var iv = forge.util.hexToBytes(msg.dekInfo.parameters);
|
|
||||||
var dk = evpBytesToKey(password, iv.substr(0, 8), dkLen);
|
|
||||||
var cipher = cipherFn(dk);
|
|
||||||
cipher.start(iv);
|
|
||||||
cipher.update(forge.util.createBuffer(msg.body));
|
|
||||||
if(cipher.finish()) {
|
|
||||||
rval = cipher.output.getBytes();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rval = msg.body;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(msg.type === 'ENCRYPTED PRIVATE KEY') {
|
|
||||||
rval = pki.decryptPrivateKeyInfo(asn1.fromDer(rval), password);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// decryption already performed above
|
|
||||||
rval = asn1.fromDer(rval);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rval !== null) {
|
|
||||||
rval = pki.privateKeyFromAsn1(rval);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a PKCS#12 key.
|
|
||||||
*
|
|
||||||
* @param password the password to derive the key material from.
|
|
||||||
* @param salt the salt, as a ByteBuffer, to use.
|
|
||||||
* @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
|
|
||||||
* @param iter the iteration count.
|
|
||||||
* @param n the number of bytes to derive from the password.
|
|
||||||
* @param md the message digest to use, defaults to SHA-1.
|
|
||||||
*
|
|
||||||
* @return a ByteBuffer with the bytes derived from the password.
|
|
||||||
*/
|
|
||||||
pki.pbe.generatePkcs12Key = function(password, salt, id, iter, n, md) {
|
|
||||||
var j, l;
|
|
||||||
|
|
||||||
if(typeof md === 'undefined' || md === null) {
|
|
||||||
md = forge.md.sha1.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
var u = md.digestLength;
|
|
||||||
var v = md.blockLength;
|
|
||||||
var result = new forge.util.ByteBuffer();
|
|
||||||
|
|
||||||
/* Convert password to Unicode byte buffer + trailing 0-byte. */
|
|
||||||
var passBuf = new forge.util.ByteBuffer();
|
|
||||||
for(l = 0; l < password.length; l++) {
|
|
||||||
passBuf.putInt16(password.charCodeAt(l));
|
|
||||||
}
|
|
||||||
passBuf.putInt16(0);
|
|
||||||
|
|
||||||
/* Length of salt and password in BYTES. */
|
|
||||||
var p = passBuf.length();
|
|
||||||
var s = salt.length();
|
|
||||||
|
|
||||||
/* 1. Construct a string, D (the "diversifier"), by concatenating
|
|
||||||
v copies of ID. */
|
|
||||||
var D = new forge.util.ByteBuffer();
|
|
||||||
D.fillWithByte(id, v);
|
|
||||||
|
|
||||||
/* 2. Concatenate copies of the salt together to create a string S of length
|
|
||||||
v * ceil(s / v) bytes (the final copy of the salt may be trunacted
|
|
||||||
to create S).
|
|
||||||
Note that if the salt is the empty string, then so is S. */
|
|
||||||
var Slen = v * Math.ceil(s / v);
|
|
||||||
var S = new forge.util.ByteBuffer();
|
|
||||||
for(l = 0; l < Slen; l ++) {
|
|
||||||
S.putByte(salt.at(l % s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 3. Concatenate copies of the password together to create a string P of
|
|
||||||
length v * ceil(p / v) bytes (the final copy of the password may be
|
|
||||||
truncated to create P).
|
|
||||||
Note that if the password is the empty string, then so is P. */
|
|
||||||
var Plen = v * Math.ceil(p / v);
|
|
||||||
var P = new forge.util.ByteBuffer();
|
|
||||||
for(l = 0; l < Plen; l ++) {
|
|
||||||
P.putByte(passBuf.at(l % p));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. Set I=S||P to be the concatenation of S and P. */
|
|
||||||
var I = S;
|
|
||||||
I.putBuffer(P);
|
|
||||||
|
|
||||||
/* 5. Set c=ceil(n / u). */
|
|
||||||
var c = Math.ceil(n / u);
|
|
||||||
|
|
||||||
/* 6. For i=1, 2, ..., c, do the following: */
|
|
||||||
for(var i = 1; i <= c; i ++) {
|
|
||||||
/* a) Set Ai=H^r(D||I). (l.e. the rth hash of D||I, H(H(H(...H(D||I)))) */
|
|
||||||
var buf = new forge.util.ByteBuffer();
|
|
||||||
buf.putBytes(D.bytes());
|
|
||||||
buf.putBytes(I.bytes());
|
|
||||||
for(var round = 0; round < iter; round ++) {
|
|
||||||
md.start();
|
|
||||||
md.update(buf.getBytes());
|
|
||||||
buf = md.digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* b) Concatenate copies of Ai to create a string B of length v bytes (the
|
|
||||||
final copy of Ai may be truncated to create B). */
|
|
||||||
var B = new forge.util.ByteBuffer();
|
|
||||||
for(l = 0; l < v; l ++) {
|
|
||||||
B.putByte(buf.at(l % u));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* c) Treating I as a concatenation I0, I1, ..., Ik-1 of v-byte blocks,
|
|
||||||
where k=ceil(s / v) + ceil(p / v), modify I by setting
|
|
||||||
Ij=(Ij+B+1) mod 2v for each j. */
|
|
||||||
var k = Math.ceil(s / v) + Math.ceil(p / v);
|
|
||||||
var Inew = new forge.util.ByteBuffer();
|
|
||||||
for(j = 0; j < k; j ++) {
|
|
||||||
var chunk = new forge.util.ByteBuffer(I.getBytes(v));
|
|
||||||
var x = 0x1ff;
|
|
||||||
for(l = B.length() - 1; l >= 0; l --) {
|
|
||||||
x = x >> 8;
|
|
||||||
x += B.at(l) + chunk.at(l);
|
|
||||||
chunk.setAt(l, x & 0xff);
|
|
||||||
}
|
|
||||||
Inew.putBuffer(chunk);
|
|
||||||
}
|
|
||||||
I = Inew;
|
|
||||||
|
|
||||||
/* Add Ai to A. */
|
|
||||||
result.putBuffer(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.truncate(result.length() - n);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get new Forge cipher object instance.
|
|
||||||
*
|
|
||||||
* @param oid the OID (in string notation).
|
|
||||||
* @param params the ASN.1 params object.
|
|
||||||
* @param password the password to decrypt with.
|
|
||||||
*
|
|
||||||
* @return new cipher object instance.
|
|
||||||
*/
|
|
||||||
pki.pbe.getCipher = function(oid, params, password) {
|
|
||||||
switch(oid) {
|
|
||||||
case pki.oids['pkcs5PBES2']:
|
|
||||||
return pki.pbe.getCipherForPBES2(oid, params, password);
|
|
||||||
|
|
||||||
case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
|
|
||||||
case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
|
|
||||||
return pki.pbe.getCipherForPKCS12PBE(oid, params, password);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read encrypted PBE data block. Unsupported OID.',
|
|
||||||
oid: oid,
|
|
||||||
supportedOids: [
|
|
||||||
'pkcs5PBES2',
|
|
||||||
'pbeWithSHAAnd3-KeyTripleDES-CBC',
|
|
||||||
'pbewithSHAAnd40BitRC2-CBC'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get new Forge cipher object instance according to PBES2 params block.
|
|
||||||
*
|
|
||||||
* The returned cipher instance is already started using the IV
|
|
||||||
* from PBES2 parameter block.
|
|
||||||
*
|
|
||||||
* @param oid the PKCS#5 PBKDF2 OID (in string notation).
|
|
||||||
* @param params the ASN.1 PBES2-params object.
|
|
||||||
* @param password the password to decrypt with.
|
|
||||||
*
|
|
||||||
* @return new cipher object instance.
|
|
||||||
*/
|
|
||||||
pki.pbe.getCipherForPBES2 = function(oid, params, password) {
|
|
||||||
// get PBE params
|
|
||||||
var capture = {};
|
|
||||||
var errors = [];
|
|
||||||
if(!asn1.validate(params, PBES2AlgorithmsValidator, capture, errors)) {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read password-based-encryption algorithm ' +
|
|
||||||
'parameters. ASN.1 object is not a supported ' +
|
|
||||||
'EncryptedPrivateKeyInfo.',
|
|
||||||
errors: errors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// check oids
|
|
||||||
oid = asn1.derToOid(capture.kdfOid);
|
|
||||||
if(oid !== pki.oids['pkcs5PBKDF2']) {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read encrypted private key. ' +
|
|
||||||
'Unsupported key derivation function OID.',
|
|
||||||
oid: oid,
|
|
||||||
supportedOids: ['pkcs5PBKDF2']
|
|
||||||
};
|
|
||||||
}
|
|
||||||
oid = asn1.derToOid(capture.encOid);
|
|
||||||
if(oid !== pki.oids['aes128-CBC'] &&
|
|
||||||
oid !== pki.oids['aes192-CBC'] &&
|
|
||||||
oid !== pki.oids['aes256-CBC']) {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read encrypted private key. ' +
|
|
||||||
'Unsupported encryption scheme OID.',
|
|
||||||
oid: oid,
|
|
||||||
supportedOids: ['aes128-CBC', 'aes192-CBC', 'aes256-CBC']
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// set PBE params
|
|
||||||
var salt = capture.kdfSalt;
|
|
||||||
var count = forge.util.createBuffer(capture.kdfIterationCount);
|
|
||||||
count = count.getInt(count.length() << 3);
|
|
||||||
var dkLen;
|
|
||||||
if(oid === pki.oids['aes128-CBC']) {
|
|
||||||
dkLen = 16;
|
|
||||||
}
|
|
||||||
else if(oid === pki.oids['aes192-CBC']) {
|
|
||||||
dkLen = 24;
|
|
||||||
}
|
|
||||||
else if(oid === pki.oids['aes256-CBC']) {
|
|
||||||
dkLen = 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt private key using pbe SHA-1 and AES
|
|
||||||
var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen);
|
|
||||||
var iv = capture.encIv;
|
|
||||||
var cipher = forge.aes.createDecryptionCipher(dk);
|
|
||||||
cipher.start(iv);
|
|
||||||
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get new Forge cipher object instance for PKCS#12 PBE.
|
|
||||||
*
|
|
||||||
* The returned cipher instance is already started using the key & IV
|
|
||||||
* derived from the provided password and PKCS#12 PBE salt.
|
|
||||||
*
|
|
||||||
* @param oid The PKCS#12 PBE OID (in string notation).
|
|
||||||
* @param params The ASN.1 PKCS#12 PBE-params object.
|
|
||||||
* @param password The password to decrypt with.
|
|
||||||
* @return New cipher object instance.
|
|
||||||
*/
|
|
||||||
pki.pbe.getCipherForPKCS12PBE = function(oid, params, password) {
|
|
||||||
// get PBE params
|
|
||||||
var capture = {};
|
|
||||||
var errors = [];
|
|
||||||
if(!asn1.validate(params, pkcs12PbeParamsValidator, capture, errors)) {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read password-based-encryption algorithm ' +
|
|
||||||
'parameters. ASN.1 object is not a supported ' +
|
|
||||||
'EncryptedPrivateKeyInfo.',
|
|
||||||
errors: errors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var salt = forge.util.createBuffer(capture.salt);
|
|
||||||
var count = forge.util.createBuffer(capture.iterations);
|
|
||||||
count = count.getInt(count.length() << 3);
|
|
||||||
|
|
||||||
var dkLen, dIvLen, cipherFn;
|
|
||||||
switch(oid) {
|
|
||||||
case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
|
|
||||||
dkLen = 24;
|
|
||||||
dIvLen = 8;
|
|
||||||
cipherFn = forge.des.startDecrypting;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
|
|
||||||
dkLen = 5;
|
|
||||||
dIvLen = 8;
|
|
||||||
cipherFn = function(key, iv) {
|
|
||||||
var cipher = forge.rc2.createDecryptionCipher(key, 40);
|
|
||||||
cipher.start(iv, null);
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read PKCS #12 PBE data block. Unsupported OID.',
|
|
||||||
oid: oid
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = pki.pbe.generatePkcs12Key(password, salt, 1, count, dkLen);
|
|
||||||
var iv = pki.pbe.generatePkcs12Key(password, salt, 2, count, dIvLen);
|
|
||||||
|
|
||||||
return cipherFn(key, iv);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OpenSSL's legacy key derivation function.
|
|
||||||
*
|
|
||||||
* See: http://www.openssl.org/docs/crypto/EVP_BytesToKey.html
|
|
||||||
*
|
|
||||||
* @param password the password to derive the key from.
|
|
||||||
* @param salt the salt to use.
|
|
||||||
* @param dkLen the number of bytes needed for the derived key.
|
|
||||||
*/
|
|
||||||
function evpBytesToKey(password, salt, dkLen) {
|
|
||||||
var digests = [md5(password + salt)];
|
|
||||||
for(var length = 16, i = 1; length < dkLen; ++i, length += 16) {
|
|
||||||
digests.push(md5(digests[i - 1] + password + salt));
|
|
||||||
}
|
|
||||||
return digests.join('').substr(0, dkLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function md5(bytes) {
|
|
||||||
return forge.md.md5.create().update(bytes).digest().getBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'pbe';
|
|
||||||
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',
|
|
||||||
'./asn1',
|
|
||||||
'./des',
|
|
||||||
'./md',
|
|
||||||
'./oids',
|
|
||||||
'./pem',
|
|
||||||
'./pbkdf2',
|
|
||||||
'./random',
|
|
||||||
'./rc2',
|
|
||||||
'./rsa',
|
|
||||||
'./util'
|
|
||||||
], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,163 +0,0 @@
|
|||||||
/**
|
|
||||||
* Password-Based Key-Derivation Function #2 implementation.
|
|
||||||
*
|
|
||||||
* See RFC 2898 for details.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var pkcs5 = forge.pkcs5 = forge.pkcs5 || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a key from a password.
|
|
||||||
*
|
|
||||||
* @param p the password as a string of bytes.
|
|
||||||
* @param s the salt as a string of bytes.
|
|
||||||
* @param c the iteration count, a positive integer.
|
|
||||||
* @param dkLen the intended length, in bytes, of the derived key,
|
|
||||||
* (max: 2^32 - 1) * hash length of the PRF.
|
|
||||||
* @param md the message digest to use in the PRF, defaults to SHA-1.
|
|
||||||
*
|
|
||||||
* @return the derived key, as a string of bytes.
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
var hLen = md.digestLength;
|
|
||||||
|
|
||||||
/* 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and
|
|
||||||
stop. */
|
|
||||||
if(dkLen > (0xFFFFFFFF * hLen)) {
|
|
||||||
throw {
|
|
||||||
message: 'Derived key is too long.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. Let len be the number of hLen-octet blocks in the derived key,
|
|
||||||
rounding up, and let r be the number of octets in the last
|
|
||||||
block:
|
|
||||||
|
|
||||||
len = CEIL(dkLen / hLen),
|
|
||||||
r = dkLen - (len - 1) * hLen. */
|
|
||||||
var len = Math.ceil(dkLen / hLen);
|
|
||||||
var r = dkLen - (len - 1) * hLen;
|
|
||||||
|
|
||||||
/* 3. For each block of the derived key apply the function F defined
|
|
||||||
below to the password P, the salt S, the iteration count c, and
|
|
||||||
the block index to compute the block:
|
|
||||||
|
|
||||||
T_1 = F(P, S, c, 1),
|
|
||||||
T_2 = F(P, S, c, 2),
|
|
||||||
...
|
|
||||||
T_len = F(P, S, c, len),
|
|
||||||
|
|
||||||
where the function F is defined as the exclusive-or sum of the
|
|
||||||
first c iterates of the underlying pseudorandom function PRF
|
|
||||||
applied to the password P and the concatenation of the salt S
|
|
||||||
and the block index i:
|
|
||||||
|
|
||||||
F(P, S, c, i) = u_1 XOR u_2 XOR ... XOR u_c
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
u_1 = PRF(P, S || INT(i)),
|
|
||||||
u_2 = PRF(P, u_1),
|
|
||||||
...
|
|
||||||
u_c = PRF(P, u_{c-1}).
|
|
||||||
|
|
||||||
Here, INT(i) is a four-octet encoding of the integer i, most
|
|
||||||
significant octet first. */
|
|
||||||
var prf = forge.hmac.create();
|
|
||||||
prf.start(md, p);
|
|
||||||
var dk = '';
|
|
||||||
var xor, u_c, u_c1;
|
|
||||||
for(var i = 1; i <= len; ++i) {
|
|
||||||
// PRF(P, S || INT(i)) (first iteration)
|
|
||||||
prf.start(null, null);
|
|
||||||
prf.update(s);
|
|
||||||
prf.update(forge.util.int32ToBytes(i));
|
|
||||||
xor = u_c1 = prf.digest().getBytes();
|
|
||||||
|
|
||||||
// PRF(P, u_{c-1}) (other iterations)
|
|
||||||
for(var j = 2; j <= c; ++j) {
|
|
||||||
prf.start(null, null);
|
|
||||||
prf.update(u_c1);
|
|
||||||
u_c = prf.digest().getBytes();
|
|
||||||
// F(p, s, c, i)
|
|
||||||
xor = forge.util.xorBytes(xor, u_c, hLen);
|
|
||||||
u_c1 = u_c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. Concatenate the blocks and extract the first dkLen octets to
|
|
||||||
produce a derived key DK:
|
|
||||||
|
|
||||||
DK = T_1 || T_2 || ... || T_len<0..r-1> */
|
|
||||||
dk += (i < len) ? xor : xor.substr(0, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 5. Output the derived key DK. */
|
|
||||||
return dk;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'pbkdf2';
|
|
||||||
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', './hmac', './md', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
301
src/lib/pem.js
301
src/lib/pem.js
@ -1,301 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
315
src/lib/pkcs1.js
@ -1,315 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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));
|
|
||||||
});
|
|
||||||
})();
|
|
1118
src/lib/pkcs12.js
1118
src/lib/pkcs12.js
File diff suppressed because it is too large
Load Diff
874
src/lib/pkcs7.js
874
src/lib/pkcs7.js
@ -1,874 +0,0 @@
|
|||||||
/**
|
|
||||||
* Javascript implementation of PKCS#7 v1.5. Currently only certain parts of
|
|
||||||
* PKCS#7 are implemented, especially the enveloped-data content type.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*
|
|
||||||
* Currently this implementation only supports ContentType of either
|
|
||||||
* EnvelopedData or EncryptedData on root level. The top level elements may
|
|
||||||
* contain only a ContentInfo of ContentType Data, i.e. plain data. Further
|
|
||||||
* nesting is not (yet) supported.
|
|
||||||
*
|
|
||||||
* The Forge validators for PKCS #7's ASN.1 structures are available from
|
|
||||||
* a seperate file pkcs7asn1.js, since those are referenced from other
|
|
||||||
* PKCS standards like PKCS #12.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// shortcut for ASN.1 API
|
|
||||||
var asn1 = forge.asn1;
|
|
||||||
|
|
||||||
// shortcut for PKCS#7 API
|
|
||||||
var p7 = forge.pkcs7 = forge.pkcs7 || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a PKCS#7 message from PEM format.
|
|
||||||
*
|
|
||||||
* @param pem the PEM-formatted PKCS#7 message.
|
|
||||||
*
|
|
||||||
* @return the PKCS#7 message.
|
|
||||||
*/
|
|
||||||
p7.messageFromPem = function(pem) {
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a PKCS#7 message to PEM format.
|
|
||||||
*
|
|
||||||
* @param msg The PKCS#7 message object
|
|
||||||
* @param maxline The maximum characters per line, defaults to 64.
|
|
||||||
*
|
|
||||||
* @return The PEM-formatted PKCS#7 message.
|
|
||||||
*/
|
|
||||||
p7.messageToPem = function(msg, maxline) {
|
|
||||||
// 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});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a PKCS#7 message from an ASN.1 object.
|
|
||||||
*
|
|
||||||
* @param obj the ASN.1 representation of a ContentInfo.
|
|
||||||
*
|
|
||||||
* @return the PKCS#7 message.
|
|
||||||
*/
|
|
||||||
p7.messageFromAsn1 = function(obj) {
|
|
||||||
// validate root level ContentInfo and capture data
|
|
||||||
var capture = {};
|
|
||||||
var errors = [];
|
|
||||||
if(!asn1.validate(obj, p7.asn1.contentInfoValidator, capture, errors))
|
|
||||||
{
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read PKCS#7 message. ' +
|
|
||||||
'ASN.1 object is not an PKCS#7 ContentInfo.',
|
|
||||||
errors: errors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var contentType = asn1.derToOid(capture.contentType);
|
|
||||||
var msg;
|
|
||||||
|
|
||||||
switch(contentType) {
|
|
||||||
case forge.pki.oids.envelopedData:
|
|
||||||
msg = p7.createEnvelopedData();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case forge.pki.oids.encryptedData:
|
|
||||||
msg = p7.createEncryptedData();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case forge.pki.oids.signedData:
|
|
||||||
msg = p7.createSignedData();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read PKCS#7 message. ContentType with OID ' +
|
|
||||||
contentType + ' is not (yet) supported.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.fromAsn1(capture.content.value[0]);
|
|
||||||
return msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a single RecipientInfo from an ASN.1 object.
|
|
||||||
*
|
|
||||||
* @param obj The ASN.1 representation of a RecipientInfo.
|
|
||||||
*
|
|
||||||
* @return The recipientInfo object.
|
|
||||||
*/
|
|
||||||
var _recipientInfoFromAsn1 = function(obj) {
|
|
||||||
// Validate EnvelopedData content block and capture data.
|
|
||||||
var capture = {};
|
|
||||||
var errors = [];
|
|
||||||
if(!asn1.validate(obj, p7.asn1.recipientInfoValidator, capture, errors))
|
|
||||||
{
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read PKCS#7 message. ' +
|
|
||||||
'ASN.1 object is not an PKCS#7 EnvelopedData.',
|
|
||||||
errors: errors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
version: capture.version.charCodeAt(0),
|
|
||||||
issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
|
|
||||||
serialNumber: forge.util.createBuffer(capture.serial).toHex(),
|
|
||||||
encryptedContent: {
|
|
||||||
algorithm: asn1.derToOid(capture.encAlgorithm),
|
|
||||||
parameter: capture.encParameter.value,
|
|
||||||
content: capture.encKey
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a single recipientInfo object to an ASN.1 object.
|
|
||||||
*
|
|
||||||
* @param obj The recipientInfo object.
|
|
||||||
*
|
|
||||||
* @return The ASN.1 representation of a RecipientInfo.
|
|
||||||
*/
|
|
||||||
var _recipientInfoToAsn1 = function(obj) {
|
|
||||||
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// Version
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
|
||||||
String.fromCharCode(obj.version)),
|
|
||||||
// IssuerAndSerialNumber
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// Name
|
|
||||||
forge.pki.distinguishedNameToAsn1({ attributes: obj.issuer }),
|
|
||||||
// Serial
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
|
||||||
forge.util.hexToBytes(obj.serialNumber))
|
|
||||||
]),
|
|
||||||
// KeyEncryptionAlgorithmIdentifier
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// Algorithm
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),
|
|
||||||
// Parameter, force NULL, only RSA supported for now.
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
|
|
||||||
]),
|
|
||||||
// EncryptedKey
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
|
|
||||||
obj.encryptedContent.content)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map a set of RecipientInfo ASN.1 objects to recipientInfo objects.
|
|
||||||
*
|
|
||||||
* @param objArr Array of ASN.1 representations RecipientInfo (i.e. SET OF).
|
|
||||||
*
|
|
||||||
* @return array of recipientInfo objects.
|
|
||||||
*/
|
|
||||||
var _recipientInfosFromAsn1 = function(objArr) {
|
|
||||||
var ret = [];
|
|
||||||
for(var i = 0; i < objArr.length; i ++) {
|
|
||||||
ret.push(_recipientInfoFromAsn1(objArr[i]));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map an array of recipientInfo objects to ASN.1 objects.
|
|
||||||
*
|
|
||||||
* @param recipientsArr Array of recipientInfo objects.
|
|
||||||
*
|
|
||||||
* @return Array of ASN.1 representations RecipientInfo.
|
|
||||||
*/
|
|
||||||
var _recipientInfosToAsn1 = function(recipientsArr) {
|
|
||||||
var ret = [];
|
|
||||||
for(var i = 0; i < recipientsArr.length; i ++) {
|
|
||||||
ret.push(_recipientInfoToAsn1(recipientsArr[i]));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map messages encrypted content to ASN.1 objects.
|
|
||||||
*
|
|
||||||
* @param ec The encryptedContent object of the message.
|
|
||||||
*
|
|
||||||
* @return ASN.1 representation of the encryptedContent object (SEQUENCE).
|
|
||||||
*/
|
|
||||||
var _encryptedContentToAsn1 = function(ec) {
|
|
||||||
return [
|
|
||||||
// ContentType, always Data for the moment
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(forge.pki.oids.data).getBytes()),
|
|
||||||
// ContentEncryptionAlgorithmIdentifier
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// Algorithm
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(ec.algorithm).getBytes()),
|
|
||||||
// Parameters (IV)
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
|
|
||||||
ec.parameter.getBytes())
|
|
||||||
]),
|
|
||||||
// [0] EncryptedContent
|
|
||||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
|
|
||||||
ec.content.getBytes())
|
|
||||||
])
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the "common part" of an PKCS#7 content block (in ASN.1 format)
|
|
||||||
*
|
|
||||||
* This function reads the "common part" of the PKCS#7 content blocks
|
|
||||||
* EncryptedData and EnvelopedData, i.e. version number and symmetrically
|
|
||||||
* encrypted content block.
|
|
||||||
*
|
|
||||||
* The result of the ASN.1 validate and capture process is returned
|
|
||||||
* to allow the caller to extract further data, e.g. the list of recipients
|
|
||||||
* in case of a EnvelopedData object.
|
|
||||||
*
|
|
||||||
* @param msg the PKCS#7 object to read the data to.
|
|
||||||
* @param obj the ASN.1 representation of the content block.
|
|
||||||
* @param validator the ASN.1 structure validator object to use.
|
|
||||||
*
|
|
||||||
* @return the value map captured by validator object.
|
|
||||||
*/
|
|
||||||
var _fromAsn1 = function(msg, obj, validator) {
|
|
||||||
var capture = {};
|
|
||||||
var errors = [];
|
|
||||||
if(!asn1.validate(obj, validator, capture, errors)) {
|
|
||||||
throw {
|
|
||||||
message: 'Cannot read PKCS#7 message. ' +
|
|
||||||
'ASN.1 object is not a supported PKCS#7 message.',
|
|
||||||
errors: errors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check contentType, so far we only support (raw) Data.
|
|
||||||
var contentType = asn1.derToOid(capture.contentType);
|
|
||||||
if(contentType !== forge.pki.oids.data) {
|
|
||||||
throw {
|
|
||||||
message: 'Unsupported PKCS#7 message. ' +
|
|
||||||
'Only wrapped ContentType Data supported.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(capture.encryptedContent) {
|
|
||||||
var content = '';
|
|
||||||
if(forge.util.isArray(capture.encryptedContent)) {
|
|
||||||
for(var i = 0; i < capture.encryptedContent.length; ++i) {
|
|
||||||
if(capture.encryptedContent[i].type !== asn1.Type.OCTETSTRING) {
|
|
||||||
throw {
|
|
||||||
message: 'Malformed PKCS#7 message, expecting encrypted ' +
|
|
||||||
'content constructed of only OCTET STRING objects.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
content += capture.encryptedContent[i].value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
content = capture.encryptedContent;
|
|
||||||
}
|
|
||||||
msg.encryptedContent = {
|
|
||||||
algorithm: asn1.derToOid(capture.encAlgorithm),
|
|
||||||
parameter: forge.util.createBuffer(capture.encParameter.value),
|
|
||||||
content: forge.util.createBuffer(content)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(capture.content) {
|
|
||||||
var content = '';
|
|
||||||
if(forge.util.isArray(capture.content)) {
|
|
||||||
for(var i = 0; i < capture.content.length; ++i) {
|
|
||||||
if(capture.content[i].type !== asn1.Type.OCTETSTRING) {
|
|
||||||
throw {
|
|
||||||
message: 'Malformed PKCS#7 message, expecting ' +
|
|
||||||
'content constructed of only OCTET STRING objects.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
content += capture.content[i].value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
content = capture.content;
|
|
||||||
}
|
|
||||||
msg.content = forge.util.createBuffer(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.version = capture.version.charCodeAt(0);
|
|
||||||
msg.rawCapture = capture;
|
|
||||||
|
|
||||||
return capture;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt the symmetrically encrypted content block of the PKCS#7 message.
|
|
||||||
*
|
|
||||||
* Decryption is skipped in case the PKCS#7 message object already has a
|
|
||||||
* (decrypted) content attribute. The algorithm, key and cipher parameters
|
|
||||||
* (probably the iv) are taken from the encryptedContent attribute of the
|
|
||||||
* message object.
|
|
||||||
*
|
|
||||||
* @param The PKCS#7 message object.
|
|
||||||
*/
|
|
||||||
var _decryptContent = function (msg) {
|
|
||||||
if(msg.encryptedContent.key === undefined) {
|
|
||||||
throw {
|
|
||||||
message: 'Symmetric key not available.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(msg.content === undefined) {
|
|
||||||
var ciph;
|
|
||||||
|
|
||||||
switch(msg.encryptedContent.algorithm) {
|
|
||||||
case forge.pki.oids['aes128-CBC']:
|
|
||||||
case forge.pki.oids['aes192-CBC']:
|
|
||||||
case forge.pki.oids['aes256-CBC']:
|
|
||||||
ciph = forge.aes.createDecryptionCipher(msg.encryptedContent.key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case forge.pki.oids['des-EDE3-CBC']:
|
|
||||||
ciph = forge.des.createDecryptionCipher(msg.encryptedContent.key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Unsupported symmetric cipher, OID ' +
|
|
||||||
msg.encryptedContent.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ciph.start(msg.encryptedContent.parameter);
|
|
||||||
ciph.update(msg.encryptedContent.content);
|
|
||||||
|
|
||||||
if(!ciph.finish()) {
|
|
||||||
throw {
|
|
||||||
message: 'Symmetric decryption failed.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.content = ciph.output;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
p7.createSignedData = function() {
|
|
||||||
var msg = null;
|
|
||||||
msg = {
|
|
||||||
type: forge.pki.oids.signedData,
|
|
||||||
version: 1,
|
|
||||||
certificates: [],
|
|
||||||
crls: [],
|
|
||||||
// populated during sign()
|
|
||||||
digestAlgorithmIdentifiers: [],
|
|
||||||
contentInfo: null,
|
|
||||||
signerInfos: [],
|
|
||||||
|
|
||||||
fromAsn1: function(obj) {
|
|
||||||
// validate SignedData content block and capture data.
|
|
||||||
_fromAsn1(msg, obj, p7.asn1.signedDataValidator);
|
|
||||||
msg.certificates = [];
|
|
||||||
msg.crls = [];
|
|
||||||
msg.digestAlgorithmIdentifiers = [];
|
|
||||||
msg.contentInfo = null;
|
|
||||||
msg.signerInfos = [];
|
|
||||||
|
|
||||||
var certs = msg.rawCapture.certificates.value;
|
|
||||||
for(var i = 0; i < certs.length; ++i) {
|
|
||||||
msg.certificates.push(forge.pki.certificateFromAsn1(certs[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: parse crls
|
|
||||||
},
|
|
||||||
|
|
||||||
toAsn1: function() {
|
|
||||||
// TODO: add support for more data types here
|
|
||||||
if('content' in msg) {
|
|
||||||
throw 'Signing PKCS#7 content not yet implemented.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// degenerate case with no content
|
|
||||||
if(!msg.contentInfo) {
|
|
||||||
msg.sign();
|
|
||||||
}
|
|
||||||
|
|
||||||
var certs = [];
|
|
||||||
for(var i = 0; i < msg.certificates.length; ++i) {
|
|
||||||
certs.push(forge.pki.certificateToAsn1(msg.certificates[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
var crls = [];
|
|
||||||
// TODO: implement CRLs
|
|
||||||
|
|
||||||
// ContentInfo
|
|
||||||
return asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// ContentType
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(msg.type).getBytes()),
|
|
||||||
// [0] SignedData
|
|
||||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// Version
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
|
||||||
String.fromCharCode(msg.version)),
|
|
||||||
// DigestAlgorithmIdentifiers
|
|
||||||
asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.SET, true,
|
|
||||||
msg.digestAlgorithmIdentifiers),
|
|
||||||
// ContentInfo
|
|
||||||
msg.contentInfo,
|
|
||||||
// [0] IMPLICIT ExtendedCertificatesAndCertificates
|
|
||||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, certs),
|
|
||||||
// [1] IMPLICIT CertificateRevocationLists
|
|
||||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, crls),
|
|
||||||
// SignerInfos
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
|
|
||||||
msg.signerInfos)
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs the content.
|
|
||||||
*
|
|
||||||
* @param signer the signer (or array of signers) to sign as, for each:
|
|
||||||
* key the private key to sign with.
|
|
||||||
* [md] the message digest to use, defaults to sha-1.
|
|
||||||
*/
|
|
||||||
sign: function(signer) {
|
|
||||||
if('content' in msg) {
|
|
||||||
throw 'PKCS#7 signing not yet implemented.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof msg.content !== 'object') {
|
|
||||||
// use Data ContentInfo
|
|
||||||
msg.contentInfo = asn1.create(
|
|
||||||
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// ContentType
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(forge.pki.oids.data).getBytes())
|
|
||||||
]);
|
|
||||||
|
|
||||||
// add actual content, if present
|
|
||||||
if('content' in msg) {
|
|
||||||
msg.contentInfo.value.push(
|
|
||||||
// [0] EXPLICIT content
|
|
||||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
|
|
||||||
msg.content)
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: generate digest algorithm identifiers
|
|
||||||
|
|
||||||
// TODO: generate signerInfos
|
|
||||||
},
|
|
||||||
|
|
||||||
verify: function() {
|
|
||||||
throw 'PKCS#7 signature verification not yet implemented.';
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a certificate.
|
|
||||||
*
|
|
||||||
* @param cert the certificate to add.
|
|
||||||
*/
|
|
||||||
addCertificate: function(cert) {
|
|
||||||
// convert from PEM
|
|
||||||
if(typeof cert === 'string') {
|
|
||||||
cert = forge.pki.certificateFromPem(cert);
|
|
||||||
}
|
|
||||||
msg.certificates.push(cert);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a certificate revokation list.
|
|
||||||
*
|
|
||||||
* @param crl the certificate revokation list to add.
|
|
||||||
*/
|
|
||||||
addCertificateRevokationList: function(crl) {
|
|
||||||
throw 'PKCS#7 CRL support not yet implemented.';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an empty PKCS#7 message of type EncryptedData.
|
|
||||||
*
|
|
||||||
* @return the message.
|
|
||||||
*/
|
|
||||||
p7.createEncryptedData = function() {
|
|
||||||
var msg = null;
|
|
||||||
msg = {
|
|
||||||
type: forge.pki.oids.encryptedData,
|
|
||||||
version: 0,
|
|
||||||
encryptedContent: {
|
|
||||||
algorithm: forge.pki.oids['aes256-CBC']
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an EncryptedData content block (in ASN.1 format)
|
|
||||||
*
|
|
||||||
* @param obj The ASN.1 representation of the EncryptedData content block
|
|
||||||
*/
|
|
||||||
fromAsn1: function(obj) {
|
|
||||||
// Validate EncryptedData content block and capture data.
|
|
||||||
_fromAsn1(msg, obj, p7.asn1.encryptedDataValidator);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt encrypted content
|
|
||||||
*
|
|
||||||
* @param key The (symmetric) key as a byte buffer
|
|
||||||
*/
|
|
||||||
decrypt: function(key) {
|
|
||||||
if(key !== undefined) {
|
|
||||||
msg.encryptedContent.key = key;
|
|
||||||
}
|
|
||||||
_decryptContent(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an empty PKCS#7 message of type EnvelopedData.
|
|
||||||
*
|
|
||||||
* @return the message.
|
|
||||||
*/
|
|
||||||
p7.createEnvelopedData = function() {
|
|
||||||
var msg = null;
|
|
||||||
msg = {
|
|
||||||
type: forge.pki.oids.envelopedData,
|
|
||||||
version: 0,
|
|
||||||
recipients: [],
|
|
||||||
encryptedContent: {
|
|
||||||
algorithm: forge.pki.oids['aes256-CBC']
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an EnvelopedData content block (in ASN.1 format)
|
|
||||||
*
|
|
||||||
* @param obj the ASN.1 representation of the EnvelopedData content block.
|
|
||||||
*/
|
|
||||||
fromAsn1: function(obj) {
|
|
||||||
// validate EnvelopedData content block and capture data
|
|
||||||
var capture = _fromAsn1(msg, obj, p7.asn1.envelopedDataValidator);
|
|
||||||
msg.recipients = _recipientInfosFromAsn1(capture.recipientInfos.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
toAsn1: function() {
|
|
||||||
// ContentInfo
|
|
||||||
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// ContentType
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
|
||||||
asn1.oidToDer(msg.type).getBytes()),
|
|
||||||
// [0] EnvelopedData
|
|
||||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
|
||||||
// Version
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
|
||||||
String.fromCharCode(msg.version)),
|
|
||||||
// RecipientInfos
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
|
|
||||||
_recipientInfosToAsn1(msg.recipients)),
|
|
||||||
// EncryptedContentInfo
|
|
||||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true,
|
|
||||||
_encryptedContentToAsn1(msg.encryptedContent))
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find recipient by X.509 certificate's issuer.
|
|
||||||
*
|
|
||||||
* @param cert the certificate with the issuer to look for.
|
|
||||||
*
|
|
||||||
* @return the recipient object.
|
|
||||||
*/
|
|
||||||
findRecipient: function(cert) {
|
|
||||||
var sAttr = cert.issuer.attributes;
|
|
||||||
|
|
||||||
for(var i = 0; i < msg.recipients.length; ++i) {
|
|
||||||
var r = msg.recipients[i];
|
|
||||||
var rAttr = r.issuer;
|
|
||||||
|
|
||||||
if(r.serialNumber !== cert.serialNumber) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rAttr.length !== sAttr.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var match = true;
|
|
||||||
for(var j = 0; j < sAttr.length; ++j) {
|
|
||||||
if(rAttr[j].type !== sAttr[j].type ||
|
|
||||||
rAttr[j].value !== sAttr[j].value) {
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(match) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt enveloped content
|
|
||||||
*
|
|
||||||
* @param recipient The recipient object related to the private key
|
|
||||||
* @param privKey The (RSA) private key object
|
|
||||||
*/
|
|
||||||
decrypt: function(recipient, privKey) {
|
|
||||||
if(msg.encryptedContent.key === undefined && recipient !== undefined
|
|
||||||
&& privKey !== undefined) {
|
|
||||||
switch(recipient.encryptedContent.algorithm) {
|
|
||||||
case forge.pki.oids.rsaEncryption:
|
|
||||||
var key = privKey.decrypt(recipient.encryptedContent.content);
|
|
||||||
msg.encryptedContent.key = forge.util.createBuffer(key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Unsupported asymmetric cipher, '
|
|
||||||
+ 'OID ' + recipient.encryptedContent.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_decryptContent(msg);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add (another) entity to list of recipients.
|
|
||||||
*
|
|
||||||
* @param cert The certificate of the entity to add.
|
|
||||||
*/
|
|
||||||
addRecipient: function(cert) {
|
|
||||||
msg.recipients.push({
|
|
||||||
version: 0,
|
|
||||||
issuer: cert.subject.attributes,
|
|
||||||
serialNumber: cert.serialNumber,
|
|
||||||
encryptedContent: {
|
|
||||||
// We simply assume rsaEncryption here, since forge.pki only
|
|
||||||
// supports RSA so far. If the PKI module supports other
|
|
||||||
// ciphers one day, we need to modify this one as well.
|
|
||||||
algorithm: forge.pki.oids.rsaEncryption,
|
|
||||||
key: cert.publicKey
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt enveloped content.
|
|
||||||
*
|
|
||||||
* This function supports two optional arguments, cipher and key, which
|
|
||||||
* can be used to influence symmetric encryption. Unless cipher is
|
|
||||||
* provided, the cipher specified in encryptedContent.algorithm is used
|
|
||||||
* (defaults to AES-256-CBC). If no key is provided, encryptedContent.key
|
|
||||||
* is (re-)used. If that one's not set, a random key will be generated
|
|
||||||
* automatically.
|
|
||||||
*
|
|
||||||
* @param [key] The key to be used for symmetric encryption.
|
|
||||||
* @param [cipher] The OID of the symmetric cipher to use.
|
|
||||||
*/
|
|
||||||
encrypt: function(key, cipher) {
|
|
||||||
// Part 1: Symmetric encryption
|
|
||||||
if(msg.encryptedContent.content === undefined) {
|
|
||||||
cipher = cipher || msg.encryptedContent.algorithm;
|
|
||||||
key = key || msg.encryptedContent.key;
|
|
||||||
|
|
||||||
var keyLen, ivLen, ciphFn;
|
|
||||||
switch(cipher) {
|
|
||||||
case forge.pki.oids['aes128-CBC']:
|
|
||||||
keyLen = 16;
|
|
||||||
ivLen = 16;
|
|
||||||
ciphFn = forge.aes.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case forge.pki.oids['aes192-CBC']:
|
|
||||||
keyLen = 24;
|
|
||||||
ivLen = 16;
|
|
||||||
ciphFn = forge.aes.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case forge.pki.oids['aes256-CBC']:
|
|
||||||
keyLen = 32;
|
|
||||||
ivLen = 16;
|
|
||||||
ciphFn = forge.aes.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case forge.pki.oids['des-EDE3-CBC']:
|
|
||||||
keyLen = 24;
|
|
||||||
ivLen = 8;
|
|
||||||
ciphFn = forge.des.createEncryptionCipher;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Unsupported symmetric cipher, OID ' + cipher
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if(key === undefined) {
|
|
||||||
key = forge.util.createBuffer(forge.random.getBytes(keyLen));
|
|
||||||
} else if(key.length() != keyLen) {
|
|
||||||
throw {
|
|
||||||
message: 'Symmetric key has wrong length, '
|
|
||||||
+ 'got ' + key.length() + ' bytes, expected ' + keyLen
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep a copy of the key & IV in the object, so the caller can
|
|
||||||
// use it for whatever reason.
|
|
||||||
msg.encryptedContent.algorithm = cipher;
|
|
||||||
msg.encryptedContent.key = key;
|
|
||||||
msg.encryptedContent.parameter = forge.util.createBuffer(
|
|
||||||
forge.random.getBytes(ivLen));
|
|
||||||
|
|
||||||
var ciph = ciphFn(key);
|
|
||||||
ciph.start(msg.encryptedContent.parameter.copy());
|
|
||||||
ciph.update(msg.content);
|
|
||||||
|
|
||||||
// The finish function does PKCS#7 padding by default, therefore
|
|
||||||
// no action required by us.
|
|
||||||
if(!ciph.finish()) {
|
|
||||||
throw {
|
|
||||||
message: 'Symmetric encryption failed.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.encryptedContent.content = ciph.output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Part 2: asymmetric encryption for each recipient
|
|
||||||
for(var i = 0; i < msg.recipients.length; i ++) {
|
|
||||||
var recipient = msg.recipients[i];
|
|
||||||
|
|
||||||
// Nothing to do, encryption already done.
|
|
||||||
if(recipient.encryptedContent.content !== undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(recipient.encryptedContent.algorithm) {
|
|
||||||
case forge.pki.oids.rsaEncryption:
|
|
||||||
recipient.encryptedContent.content =
|
|
||||||
recipient.encryptedContent.key.encrypt(
|
|
||||||
msg.encryptedContent.key.data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw {
|
|
||||||
message: 'Unsupported asymmetric cipher, OID ' +
|
|
||||||
recipient.encryptedContent.algorithm
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'pkcs7';
|
|
||||||
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',
|
|
||||||
'./asn1',
|
|
||||||
'./des',
|
|
||||||
'./oids',
|
|
||||||
'./pem',
|
|
||||||
'./pkcs7asn1',
|
|
||||||
'./random',
|
|
||||||
'./util',
|
|
||||||
'./x509'
|
|
||||||
], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,399 +0,0 @@
|
|||||||
/**
|
|
||||||
* Javascript implementation of PKCS#7 v1.5. Currently only certain parts of
|
|
||||||
* PKCS#7 are implemented, especially the enveloped-data content type.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*
|
|
||||||
* The ASN.1 representation of PKCS#7 is as follows
|
|
||||||
* (see RFC #2315 for details, http://www.ietf.org/rfc/rfc2315.txt):
|
|
||||||
*
|
|
||||||
* A PKCS#7 message consists of a ContentInfo on root level, which may
|
|
||||||
* contain any number of further ContentInfo nested into it.
|
|
||||||
*
|
|
||||||
* ContentInfo ::= SEQUENCE {
|
|
||||||
* contentType ContentType,
|
|
||||||
* content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ContentType ::= OBJECT IDENTIFIER
|
|
||||||
*
|
|
||||||
* EnvelopedData ::= SEQUENCE {
|
|
||||||
* version Version,
|
|
||||||
* recipientInfos RecipientInfos,
|
|
||||||
* encryptedContentInfo EncryptedContentInfo
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* EncryptedData ::= SEQUENCE {
|
|
||||||
* version Version,
|
|
||||||
* encryptedContentInfo EncryptedContentInfo
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Version ::= INTEGER
|
|
||||||
*
|
|
||||||
* RecipientInfos ::= SET OF RecipientInfo
|
|
||||||
*
|
|
||||||
* EncryptedContentInfo ::= SEQUENCE {
|
|
||||||
* contentType ContentType,
|
|
||||||
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
|
|
||||||
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
|
||||||
*
|
|
||||||
* The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
|
|
||||||
* for the algorithm, if any. In the case of AES and DES3, there is only one,
|
|
||||||
* the IV.
|
|
||||||
*
|
|
||||||
* AlgorithmIdentifer ::= SEQUENCE {
|
|
||||||
* algorithm OBJECT IDENTIFIER,
|
|
||||||
* parameters ANY DEFINED BY algorithm OPTIONAL
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* EncryptedContent ::= OCTET STRING
|
|
||||||
*
|
|
||||||
* RecipientInfo ::= SEQUENCE {
|
|
||||||
* version Version,
|
|
||||||
* issuerAndSerialNumber IssuerAndSerialNumber,
|
|
||||||
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
|
|
||||||
* encryptedKey EncryptedKey
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* IssuerAndSerialNumber ::= SEQUENCE {
|
|
||||||
* issuer Name,
|
|
||||||
* serialNumber CertificateSerialNumber
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* CertificateSerialNumber ::= INTEGER
|
|
||||||
*
|
|
||||||
* KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
|
||||||
*
|
|
||||||
* EncryptedKey ::= OCTET STRING
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// shortcut for ASN.1 API
|
|
||||||
var asn1 = forge.asn1;
|
|
||||||
|
|
||||||
// shortcut for PKCS#7 API
|
|
||||||
var p7v = forge.pkcs7asn1 = forge.pkcs7asn1 || {};
|
|
||||||
forge.pkcs7 = forge.pkcs7 || {};
|
|
||||||
forge.pkcs7.asn1 = p7v;
|
|
||||||
|
|
||||||
var contentInfoValidator = {
|
|
||||||
name: 'ContentInfo',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'ContentInfo.ContentType',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'contentType'
|
|
||||||
}, {
|
|
||||||
name: 'ContentInfo.content',
|
|
||||||
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
||||||
type: 0,
|
|
||||||
constructed: true,
|
|
||||||
optional: true,
|
|
||||||
captureAsn1: 'content'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
p7v.contentInfoValidator = contentInfoValidator;
|
|
||||||
|
|
||||||
var encryptedContentInfoValidator = {
|
|
||||||
name: 'EncryptedContentInfo',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'EncryptedContentInfo.contentType',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'contentType'
|
|
||||||
}, {
|
|
||||||
name: 'EncryptedContentInfo.contentEncryptionAlgorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'EncryptedContentInfo.contentEncryptionAlgorithm.algorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encAlgorithm'
|
|
||||||
}, {
|
|
||||||
name: 'EncryptedContentInfo.contentEncryptionAlgorithm.parameter',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
captureAsn1: 'encParameter'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: 'EncryptedContentInfo.encryptedContent',
|
|
||||||
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
||||||
type: 0,
|
|
||||||
/* The PKCS#7 structure output by OpenSSL somewhat differs from what
|
|
||||||
* other implementations do generate.
|
|
||||||
*
|
|
||||||
* OpenSSL generates a structure like this:
|
|
||||||
* SEQUENCE {
|
|
||||||
* ...
|
|
||||||
* [0]
|
|
||||||
* 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
|
|
||||||
* C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Whereas other implementations (and this PKCS#7 module) generate:
|
|
||||||
* SEQUENCE {
|
|
||||||
* ...
|
|
||||||
* [0] {
|
|
||||||
* OCTET STRING
|
|
||||||
* 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
|
|
||||||
* C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* In order to support both, we just capture the context specific
|
|
||||||
* field here. The OCTET STRING bit is removed below.
|
|
||||||
*/
|
|
||||||
capture: 'encryptedContent'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
p7v.envelopedDataValidator = {
|
|
||||||
name: 'EnvelopedData',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'EnvelopedData.Version',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'version'
|
|
||||||
}, {
|
|
||||||
name: 'EnvelopedData.RecipientInfos',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SET,
|
|
||||||
constructed: true,
|
|
||||||
captureAsn1: 'recipientInfos'
|
|
||||||
}].concat(encryptedContentInfoValidator)
|
|
||||||
};
|
|
||||||
|
|
||||||
p7v.encryptedDataValidator = {
|
|
||||||
name: 'EncryptedData',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'EncryptedData.Version',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'version'
|
|
||||||
}].concat(encryptedContentInfoValidator)
|
|
||||||
};
|
|
||||||
|
|
||||||
var signerValidator = {
|
|
||||||
name: 'SignerInfo',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'SignerInfo.Version',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false
|
|
||||||
}, {
|
|
||||||
name: 'SignerInfo.IssuerAndSerialNumber',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true
|
|
||||||
}, {
|
|
||||||
name: 'SignerInfo.DigestAlgorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true
|
|
||||||
}, {
|
|
||||||
name: 'SignerInfo.AuthenticatedAttributes',
|
|
||||||
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
||||||
type: 0,
|
|
||||||
constructed: true,
|
|
||||||
optional: true,
|
|
||||||
capture: 'authenticatedAttributes'
|
|
||||||
}, {
|
|
||||||
name: 'SignerInfo.DigestEncryptionAlgorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true
|
|
||||||
}, {
|
|
||||||
name: 'SignerInfo.EncryptedDigest',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OCTETSTRING,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'signature'
|
|
||||||
}, {
|
|
||||||
name: 'SignerInfo.UnauthenticatedAttributes',
|
|
||||||
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
||||||
type: 1,
|
|
||||||
constructed: true,
|
|
||||||
optional: true
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
p7v.signedDataValidator = {
|
|
||||||
name: 'SignedData',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'SignedData.Version',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'version'
|
|
||||||
}, {
|
|
||||||
name: 'SignedData.DigestAlgorithms',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SET,
|
|
||||||
constructed: true,
|
|
||||||
captureAsn1: 'digestAlgorithms'
|
|
||||||
},
|
|
||||||
contentInfoValidator,
|
|
||||||
{
|
|
||||||
name: 'SignedData.Certificates',
|
|
||||||
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
||||||
type: 0,
|
|
||||||
optional: true,
|
|
||||||
captureAsn1: 'certificates'
|
|
||||||
}, {
|
|
||||||
name: 'SignedData.CertificateRevocationLists',
|
|
||||||
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
||||||
type: 1,
|
|
||||||
optional: true,
|
|
||||||
captureAsn1: 'crls'
|
|
||||||
}, {
|
|
||||||
name: 'SignedData.SignerInfos',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SET,
|
|
||||||
capture: 'signerInfos',
|
|
||||||
optional: true,
|
|
||||||
value: [signerValidator]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
p7v.recipientInfoValidator = {
|
|
||||||
name: 'RecipientInfo',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'RecipientInfo.version',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'version'
|
|
||||||
}, {
|
|
||||||
name: 'RecipientInfo.issuerAndSerial',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'RecipientInfo.issuerAndSerial.issuer',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
captureAsn1: 'issuer'
|
|
||||||
}, {
|
|
||||||
name: 'RecipientInfo.issuerAndSerial.serialNumber',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.INTEGER,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'serial'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: 'RecipientInfo.keyEncryptionAlgorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.SEQUENCE,
|
|
||||||
constructed: true,
|
|
||||||
value: [{
|
|
||||||
name: 'RecipientInfo.keyEncryptionAlgorithm.algorithm',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OID,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encAlgorithm'
|
|
||||||
}, {
|
|
||||||
name: 'RecipientInfo.keyEncryptionAlgorithm.parameter',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
constructed: false,
|
|
||||||
captureAsn1: 'encParameter'
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
name: 'RecipientInfo.encryptedKey',
|
|
||||||
tagClass: asn1.Class.UNIVERSAL,
|
|
||||||
type: asn1.Type.OCTETSTRING,
|
|
||||||
constructed: false,
|
|
||||||
capture: 'encKey'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'pkcs7asn1';
|
|
||||||
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', './asn1', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
150
src/lib/pki.js
150
src/lib/pki.js
@ -1,150 +0,0 @@
|
|||||||
/**
|
|
||||||
* Javascript implementation of a basic Public Key Infrastructure, including
|
|
||||||
* support for RSA public and private keys.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// shortcut for asn.1 API
|
|
||||||
var asn1 = forge.asn1;
|
|
||||||
|
|
||||||
/* Public Key Infrastructure (PKI) implementation. */
|
|
||||||
var pki = forge.pki = forge.pki || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: THIS METHOD IS DEPRECATED. Use pem.decode() instead.
|
|
||||||
*
|
|
||||||
* Converts PEM-formatted data to DER.
|
|
||||||
*
|
|
||||||
* @param pem the PEM-formatted data.
|
|
||||||
*
|
|
||||||
* @return the DER-formatted data.
|
|
||||||
*/
|
|
||||||
pki.pemToDer = function(pem) {
|
|
||||||
var msg = forge.pem.decode(pem)[0];
|
|
||||||
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
|
|
||||||
throw {
|
|
||||||
message: 'Could not convert PEM to DER; PEM is encrypted.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return forge.util.createBuffer(msg.body);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts an RSA private key from PEM format.
|
|
||||||
*
|
|
||||||
* @param pem the PEM-formatted private key.
|
|
||||||
*
|
|
||||||
* @return the private key.
|
|
||||||
*/
|
|
||||||
pki.privateKeyFromPem = function(pem) {
|
|
||||||
var msg = forge.pem.decode(pem)[0];
|
|
||||||
|
|
||||||
if(msg.type !== 'PRIVATE KEY' && msg.type !== 'RSA PRIVATE KEY') {
|
|
||||||
throw {
|
|
||||||
message: 'Could not convert private key from PEM; PEM header type is ' +
|
|
||||||
'not "PRIVATE KEY" or "RSA PRIVATE KEY".',
|
|
||||||
headerType: msg.type
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if(msg.procType && msg.procType.type === 'ENCRYPTED') {
|
|
||||||
throw {
|
|
||||||
message: 'Could not convert private key from PEM; PEM is encrypted.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert DER to ASN.1 object
|
|
||||||
var obj = asn1.fromDer(msg.body);
|
|
||||||
|
|
||||||
return pki.privateKeyFromAsn1(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts an RSA private key to PEM format.
|
|
||||||
*
|
|
||||||
* @param key the private key.
|
|
||||||
* @param maxline the maximum characters per line, defaults to 64.
|
|
||||||
*
|
|
||||||
* @return the PEM-formatted private key.
|
|
||||||
*/
|
|
||||||
pki.privateKeyToPem = function(key, maxline) {
|
|
||||||
// convert to ASN.1, then DER, then PEM-encode
|
|
||||||
var msg = {
|
|
||||||
type: 'RSA PRIVATE KEY',
|
|
||||||
body: asn1.toDer(pki.privateKeyToAsn1(key)).getBytes()
|
|
||||||
};
|
|
||||||
return forge.pem.encode(msg, {maxline: maxline});
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'pki';
|
|
||||||
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',
|
|
||||||
'./asn1',
|
|
||||||
'./oids',
|
|
||||||
'./pbe',
|
|
||||||
'./pem',
|
|
||||||
'./pbkdf2',
|
|
||||||
'./pkcs12',
|
|
||||||
'./pss',
|
|
||||||
'./rsa',
|
|
||||||
'./util',
|
|
||||||
'./x509'
|
|
||||||
], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,128 +0,0 @@
|
|||||||
/**
|
|
||||||
* RSA Key Generation Worker.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
importScripts('jsbn.js');
|
|
||||||
|
|
||||||
// prime constants
|
|
||||||
var LOW_PRIMES = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
|
|
||||||
var LP_LIMIT = (1 << 26) / LOW_PRIMES[LOW_PRIMES.length - 1];
|
|
||||||
|
|
||||||
var BigInteger = forge.jsbn.BigInteger;
|
|
||||||
var BIG_TWO = new BigInteger(null);
|
|
||||||
BIG_TWO.fromInt(2);
|
|
||||||
|
|
||||||
self.addEventListener('message', function(e) {
|
|
||||||
var result = findPrime(e.data);
|
|
||||||
self.postMessage(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
// start receiving ranges to check
|
|
||||||
self.postMessage({found: false});
|
|
||||||
|
|
||||||
// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
|
|
||||||
var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
|
|
||||||
|
|
||||||
function findPrime(data) {
|
|
||||||
// create BigInteger from given random bytes
|
|
||||||
var num = new BigInteger(data.hex, 16);
|
|
||||||
|
|
||||||
/* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The
|
|
||||||
number we are given is always aligned at 30k + 1. Each time the number is
|
|
||||||
determined not to be prime we add to get to the next 'i', eg: if the number
|
|
||||||
was at 30k + 1 we add 6. */
|
|
||||||
var deltaIdx = 0;
|
|
||||||
|
|
||||||
// find nearest prime
|
|
||||||
var workLoad = data.workLoad;
|
|
||||||
var e = new BigInteger(null);
|
|
||||||
e.fromInt(data.e);
|
|
||||||
for(var i = 0; i < workLoad; ++i) {
|
|
||||||
// do primality test
|
|
||||||
if(isProbablePrime(num, 1)) {
|
|
||||||
// ensure number is coprime with e
|
|
||||||
if(num.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE) === 0 &&
|
|
||||||
isProbablePrime(num, 10)) {
|
|
||||||
return {found: true, prime: num.toString(16)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get next potential prime
|
|
||||||
num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {found: false};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isProbablePrime(n, k) {
|
|
||||||
// divide by low primes, ignore even checks, etc (n alread aligned properly)
|
|
||||||
var i = 1;
|
|
||||||
while(i < LOW_PRIMES.length) {
|
|
||||||
var m = LOW_PRIMES[i];
|
|
||||||
var j = i + 1;
|
|
||||||
while(j < LOW_PRIMES.length && m < LP_LIMIT) {
|
|
||||||
m *= LOW_PRIMES[j++];
|
|
||||||
}
|
|
||||||
m = n.modInt(m);
|
|
||||||
while(i < j) {
|
|
||||||
if(m % LOW_PRIMES[i++] == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return runMillerRabin(n, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
// HAC 4.24, Miller-Rabin
|
|
||||||
function runMillerRabin(n, k) {
|
|
||||||
// n1 = n - 1
|
|
||||||
var n1 = n.subtract(BigInteger.ONE);
|
|
||||||
|
|
||||||
// get s and d such that n1 = 2^s * d
|
|
||||||
var s = n1.getLowestSetBit();
|
|
||||||
if(s <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var d = n1.shiftRight(s);
|
|
||||||
|
|
||||||
var a = new BigInteger(null);
|
|
||||||
for(var i = 0; i < k; ++i) {
|
|
||||||
// 'a' should be selected at random, but lower primes are picked for speed
|
|
||||||
a.fromInt(LOW_PRIMES[i]);
|
|
||||||
|
|
||||||
/* See if 'a' is a composite witness. */
|
|
||||||
|
|
||||||
// x = a^d mod n
|
|
||||||
var x = a.modPow(d, n);
|
|
||||||
|
|
||||||
// probably prime
|
|
||||||
if(x.compareTo(BigInteger.ONE) === 0 || x.compareTo(n1) === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var j = s;
|
|
||||||
while(--j) {
|
|
||||||
// x = x^2 mod a
|
|
||||||
x = x.modPowInt(2, n);
|
|
||||||
|
|
||||||
// 'n' is composite because no previous x == -1 mod n
|
|
||||||
if(x.compareTo(BigInteger.ONE) === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// x == -1 mod n, so probably prime
|
|
||||||
if(x.compareTo(n1) === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'x' is first_x^(n1/2) and is not +/- 1, so 'n' is not prime
|
|
||||||
if(j === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
447
src/lib/prng.js
447
src/lib/prng.js
@ -1,447 +0,0 @@
|
|||||||
/**
|
|
||||||
* A javascript implementation of a cryptographically-secure
|
|
||||||
* Pseudo Random Number Generator (PRNG). The Fortuna algorithm is mostly
|
|
||||||
* followed here. SHA-1 is used instead of SHA-256.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var _nodejs = (
|
|
||||||
typeof process !== 'undefined' && process.versions && process.versions.node);
|
|
||||||
var crypto = null;
|
|
||||||
if(!forge.disableNativeCode && _nodejs) {
|
|
||||||
crypto = require('crypto');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PRNG API */
|
|
||||||
var prng = forge.prng = forge.prng || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new PRNG context.
|
|
||||||
*
|
|
||||||
* A PRNG plugin must be passed in that will provide:
|
|
||||||
*
|
|
||||||
* 1. A function that initializes the key and seed of a PRNG context. It
|
|
||||||
* will be given a 16 byte key and a 16 byte seed. Any key expansion
|
|
||||||
* or transformation of the seed from a byte string into an array of
|
|
||||||
* integers (or similar) should be performed.
|
|
||||||
* 2. The cryptographic function used by the generator. It takes a key and
|
|
||||||
* a seed.
|
|
||||||
* 3. A seed increment function. It takes the seed and return seed + 1.
|
|
||||||
* 4. An api to create a message digest.
|
|
||||||
*
|
|
||||||
* For an example, see random.js.
|
|
||||||
*
|
|
||||||
* @param plugin the PRNG plugin to use.
|
|
||||||
*/
|
|
||||||
prng.create = function(plugin) {
|
|
||||||
var ctx = {
|
|
||||||
plugin: plugin,
|
|
||||||
key: null,
|
|
||||||
seed: null,
|
|
||||||
time: null,
|
|
||||||
// number of reseeds so far
|
|
||||||
reseeds: 0,
|
|
||||||
// amount of data generated so far
|
|
||||||
generated: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// create 32 entropy pools (each is a message digest)
|
|
||||||
var md = plugin.md;
|
|
||||||
var pools = new Array(32);
|
|
||||||
for(var i = 0; i < 32; ++i) {
|
|
||||||
pools[i] = md.create();
|
|
||||||
}
|
|
||||||
ctx.pools = pools;
|
|
||||||
|
|
||||||
// entropy pools are written to cyclically, starting at index 0
|
|
||||||
ctx.pool = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates random bytes. The bytes may be generated synchronously or
|
|
||||||
* asynchronously. Web workers must use the asynchronous interface or
|
|
||||||
* else the behavior is undefined.
|
|
||||||
*
|
|
||||||
* @param count the number of random bytes to generate.
|
|
||||||
* @param [callback(err, bytes)] called once the operation completes.
|
|
||||||
*
|
|
||||||
* @return count random bytes as a string.
|
|
||||||
*/
|
|
||||||
ctx.generate = function(count, callback) {
|
|
||||||
// do synchronously
|
|
||||||
if(!callback) {
|
|
||||||
return ctx.generateSync(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
// simple generator using counter-based CBC
|
|
||||||
var cipher = ctx.plugin.cipher;
|
|
||||||
var increment = ctx.plugin.increment;
|
|
||||||
var formatKey = ctx.plugin.formatKey;
|
|
||||||
var formatSeed = ctx.plugin.formatSeed;
|
|
||||||
var b = forge.util.createBuffer();
|
|
||||||
|
|
||||||
generate();
|
|
||||||
|
|
||||||
function generate(err) {
|
|
||||||
if(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sufficient bytes generated
|
|
||||||
if(b.length() >= count) {
|
|
||||||
return callback(null, b.getBytes(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
// if amount of data generated is greater than 1 MiB, trigger reseed
|
|
||||||
if(ctx.generated >= 1048576) {
|
|
||||||
// only do reseed at most every 100 ms
|
|
||||||
var now = +new Date();
|
|
||||||
if(ctx.time === null || (now - ctx.time > 100)) {
|
|
||||||
ctx.key = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ctx.key === null) {
|
|
||||||
return _reseed(generate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate the random bytes
|
|
||||||
var bytes = cipher(ctx.key, ctx.seed);
|
|
||||||
ctx.generated += bytes.length;
|
|
||||||
b.putBytes(bytes);
|
|
||||||
|
|
||||||
// generate bytes for a new key and seed
|
|
||||||
ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
|
|
||||||
ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
|
|
||||||
|
|
||||||
forge.util.setImmediate(generate);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates random bytes synchronously.
|
|
||||||
*
|
|
||||||
* @param count the number of random bytes to generate.
|
|
||||||
*
|
|
||||||
* @return count random bytes as a string.
|
|
||||||
*/
|
|
||||||
ctx.generateSync = function(count) {
|
|
||||||
// simple generator using counter-based CBC
|
|
||||||
var cipher = ctx.plugin.cipher;
|
|
||||||
var increment = ctx.plugin.increment;
|
|
||||||
var formatKey = ctx.plugin.formatKey;
|
|
||||||
var formatSeed = ctx.plugin.formatSeed;
|
|
||||||
var b = forge.util.createBuffer();
|
|
||||||
while(b.length() < count) {
|
|
||||||
// if amount of data generated is greater than 1 MiB, trigger reseed
|
|
||||||
if(ctx.generated >= 1048576) {
|
|
||||||
// only do reseed at most every 100 ms
|
|
||||||
var now = +new Date();
|
|
||||||
if(ctx.time === null || (now - ctx.time > 100)) {
|
|
||||||
ctx.key = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ctx.key === null) {
|
|
||||||
_reseedSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate the random bytes
|
|
||||||
var bytes = cipher(ctx.key, ctx.seed);
|
|
||||||
ctx.generated += bytes.length;
|
|
||||||
b.putBytes(bytes);
|
|
||||||
|
|
||||||
// generate bytes for a new key and seed
|
|
||||||
ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
|
|
||||||
ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.getBytes(count);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private function that asynchronously reseeds a generator.
|
|
||||||
*
|
|
||||||
* @param callback(err) called once the operation completes.
|
|
||||||
*/
|
|
||||||
function _reseed(callback) {
|
|
||||||
if(ctx.pools[0].messageLength >= 32) {
|
|
||||||
_seed();
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
// not enough seed data...
|
|
||||||
var needed = (32 - ctx.pools[0].messageLength) << 5;
|
|
||||||
ctx.seedFile(needed, function(err, bytes) {
|
|
||||||
if(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
ctx.collect(bytes);
|
|
||||||
_seed();
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private function that synchronously reseeds a generator.
|
|
||||||
*/
|
|
||||||
function _reseedSync() {
|
|
||||||
if(ctx.pools[0].messageLength >= 32) {
|
|
||||||
return _seed();
|
|
||||||
}
|
|
||||||
// not enough seed data...
|
|
||||||
var needed = (32 - ctx.pools[0].messageLength) << 5;
|
|
||||||
ctx.collect(ctx.seedFileSync(needed));
|
|
||||||
_seed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private function that seeds a generator once enough bytes are available.
|
|
||||||
*/
|
|
||||||
function _seed() {
|
|
||||||
// create a SHA-1 message digest
|
|
||||||
var md = forge.md.sha1.create();
|
|
||||||
|
|
||||||
// digest pool 0's entropy and restart it
|
|
||||||
md.update(ctx.pools[0].digest().getBytes());
|
|
||||||
ctx.pools[0].start();
|
|
||||||
|
|
||||||
// digest the entropy of other pools whose index k meet the
|
|
||||||
// condition '2^k mod n == 0' where n is the number of reseeds
|
|
||||||
var k = 1;
|
|
||||||
for(var i = 1; i < 32; ++i) {
|
|
||||||
// prevent signed numbers from being used
|
|
||||||
k = (k === 31) ? 0x80000000 : (k << 2);
|
|
||||||
if(k % ctx.reseeds === 0) {
|
|
||||||
md.update(ctx.pools[i].digest().getBytes());
|
|
||||||
ctx.pools[i].start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get digest for key bytes and iterate again for seed bytes
|
|
||||||
var keyBytes = md.digest().getBytes();
|
|
||||||
md.start();
|
|
||||||
md.update(keyBytes);
|
|
||||||
var seedBytes = md.digest().getBytes();
|
|
||||||
|
|
||||||
// update
|
|
||||||
ctx.key = ctx.plugin.formatKey(keyBytes);
|
|
||||||
ctx.seed = ctx.plugin.formatSeed(seedBytes);
|
|
||||||
++ctx.reseeds;
|
|
||||||
ctx.generated = 0;
|
|
||||||
ctx.time = +new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The built-in default seedFile. This seedFile is used when entropy
|
|
||||||
* is needed immediately.
|
|
||||||
*
|
|
||||||
* @param needed the number of bytes that are needed.
|
|
||||||
*
|
|
||||||
* @return the random bytes.
|
|
||||||
*/
|
|
||||||
function defaultSeedFile(needed) {
|
|
||||||
// use window.crypto.getRandomValues strong source of entropy if
|
|
||||||
// available
|
|
||||||
var b = forge.util.createBuffer();
|
|
||||||
if(typeof window !== 'undefined' &&
|
|
||||||
window.crypto && window.crypto.getRandomValues) {
|
|
||||||
var entropy = new Uint32Array(needed / 4);
|
|
||||||
try {
|
|
||||||
window.crypto.getRandomValues(entropy);
|
|
||||||
for(var i = 0; i < entropy.length; ++i) {
|
|
||||||
b.putInt32(entropy[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
/* Mozilla claims getRandomValues can throw QuotaExceededError, so
|
|
||||||
ignore errors. In this case, weak entropy will be added, but
|
|
||||||
hopefully this never happens.
|
|
||||||
https://developer.mozilla.org/en-US/docs/DOM/window.crypto.getRandomValues
|
|
||||||
However I've never observed this exception --@evanj */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// be sad and add some weak random data
|
|
||||||
if(b.length() < needed) {
|
|
||||||
/* Draws from Park-Miller "minimal standard" 31 bit PRNG,
|
|
||||||
implemented with David G. Carta's optimization: with 32 bit math
|
|
||||||
and without division (Public Domain). */
|
|
||||||
var hi, lo, next;
|
|
||||||
var seed = Math.floor(Math.random() * 0xFFFF);
|
|
||||||
while(b.length() < needed) {
|
|
||||||
lo = 16807 * (seed & 0xFFFF);
|
|
||||||
hi = 16807 * (seed >> 16);
|
|
||||||
lo += (hi & 0x7FFF) << 16;
|
|
||||||
lo += hi >> 15;
|
|
||||||
lo = (lo & 0x7FFFFFFF) + (lo >> 31);
|
|
||||||
seed = lo & 0xFFFFFFFF;
|
|
||||||
|
|
||||||
// consume lower 3 bytes of seed
|
|
||||||
for(var i = 0; i < 3; ++i) {
|
|
||||||
// throw in more pseudo random
|
|
||||||
next = seed >>> (i << 3);
|
|
||||||
next ^= Math.floor(Math.random() * 0xFF);
|
|
||||||
b.putByte(String.fromCharCode(next & 0xFF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.getBytes();
|
|
||||||
}
|
|
||||||
// initialize seed file APIs
|
|
||||||
if(crypto) {
|
|
||||||
// use nodejs async API
|
|
||||||
ctx.seedFile = function(needed, callback) {
|
|
||||||
crypto.randomBytes(needed, function(err, bytes) {
|
|
||||||
if(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
callback(null, bytes.toString());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// use nodejs sync API
|
|
||||||
ctx.seedFileSync = function(needed) {
|
|
||||||
return crypto.randomBytes(needed).toString();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ctx.seedFile = function(needed, callback) {
|
|
||||||
try {
|
|
||||||
callback(null, defaultSeedFile(needed));
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
callback(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ctx.seedFileSync = defaultSeedFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds entropy to a prng ctx's accumulator.
|
|
||||||
*
|
|
||||||
* @param bytes the bytes of entropy as a string.
|
|
||||||
*/
|
|
||||||
ctx.collect = function(bytes) {
|
|
||||||
// iterate over pools distributing entropy cyclically
|
|
||||||
var count = bytes.length;
|
|
||||||
for(var i = 0; i < count; ++i) {
|
|
||||||
ctx.pools[ctx.pool].update(bytes.substr(i, 1));
|
|
||||||
ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects an integer of n bits.
|
|
||||||
*
|
|
||||||
* @param i the integer entropy.
|
|
||||||
* @param n the number of bits in the integer.
|
|
||||||
*/
|
|
||||||
ctx.collectInt = function(i, n) {
|
|
||||||
var bytes = '';
|
|
||||||
for(var x = 0; x < n; x += 8) {
|
|
||||||
bytes += String.fromCharCode((i >> x) & 0xFF);
|
|
||||||
}
|
|
||||||
ctx.collect(bytes);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a Web Worker to receive immediate entropy from the main thread.
|
|
||||||
* This method is required until Web Workers can access the native crypto
|
|
||||||
* API. This method should be called twice for each created worker, once in
|
|
||||||
* the main thread, and once in the worker itself.
|
|
||||||
*
|
|
||||||
* @param worker the worker to register.
|
|
||||||
*/
|
|
||||||
ctx.registerWorker = function(worker) {
|
|
||||||
// worker receives random bytes
|
|
||||||
if(worker === self) {
|
|
||||||
ctx.seedFile = function(needed, callback) {
|
|
||||||
function listener(e) {
|
|
||||||
var data = e.data;
|
|
||||||
if(data.forge && data.forge.prng) {
|
|
||||||
self.removeEventListener('message', listener);
|
|
||||||
callback(data.forge.prng.err, data.forge.prng.bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.addEventListener('message', listener);
|
|
||||||
self.postMessage({forge: {prng: {needed: needed}}});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// main thread sends random bytes upon request
|
|
||||||
else {
|
|
||||||
function listener(e) {
|
|
||||||
var data = e.data;
|
|
||||||
if(data.forge && data.forge.prng) {
|
|
||||||
ctx.seedFile(data.forge.prng.needed, function(err, bytes) {
|
|
||||||
worker.postMessage({forge: {prng: {err: err, bytes: bytes}}});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: do we need to remove the event listener when the worker dies?
|
|
||||||
worker.addEventListener('message', listener);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'prng';
|
|
||||||
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', './md', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
|
|
||||||
})();
|
|
255
src/lib/pss.js
255
src/lib/pss.js
@ -1,255 +0,0 @@
|
|||||||
/**
|
|
||||||
* Javascript implementation of PKCS#1 PSS signature padding.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// shortcut for PSS API
|
|
||||||
var pss = forge.pss = forge.pss || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a PSS signature scheme object.
|
|
||||||
*
|
|
||||||
* @param hash hash function to use, a Forge md instance
|
|
||||||
* @param mgf mask generation function to use, a Forge mgf instance
|
|
||||||
* @param sLen length of the salt in octets
|
|
||||||
* @return a signature scheme object.
|
|
||||||
*/
|
|
||||||
pss.create = function(hash, mgf, sLen) {
|
|
||||||
var hLen = hash.digestLength;
|
|
||||||
var pssobj = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify PSS signature
|
|
||||||
*
|
|
||||||
* This function implements EMSA-PSS-VERIFY as per RFC 3447, section 9.1.2
|
|
||||||
*
|
|
||||||
* @param {String} mHash The message digest hash to compare against
|
|
||||||
* the signature.
|
|
||||||
* @param {String} em The encoded message (RSA decryption result).
|
|
||||||
* @param modsBits Length of the RSA modulus in bits.
|
|
||||||
* @return true if the signature was verified, false if not.
|
|
||||||
*/
|
|
||||||
pssobj.verify = function(mHash, em, modBits) {
|
|
||||||
var i;
|
|
||||||
var emBits = modBits - 1;
|
|
||||||
var emLen = Math.ceil(emBits / 8);
|
|
||||||
|
|
||||||
/* c. Convert the message representative m to an encoded message EM
|
|
||||||
* of length emLen = ceil((modBits - 1) / 8) octets, where modBits
|
|
||||||
* is the length in bits of the RSA modulus n */
|
|
||||||
em = em.substr(-emLen);
|
|
||||||
|
|
||||||
/* 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. */
|
|
||||||
if(emLen < hLen + sLen + 2) {
|
|
||||||
throw {
|
|
||||||
message: 'Inconsistent parameters to PSS signature verification.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. If the rightmost octet of EM does not have hexadecimal value
|
|
||||||
* 0xbc, output "inconsistent" and stop. */
|
|
||||||
if(em.charCodeAt(emLen - 1) !== 0xbc) {
|
|
||||||
throw {
|
|
||||||
message: 'Encoded message does not end in 0xBC.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
|
|
||||||
* let H be the next hLen octets. */
|
|
||||||
var maskLen = emLen - hLen - 1;
|
|
||||||
var maskedDB = em.substr(0, maskLen);
|
|
||||||
var h = em.substr(maskLen, hLen);
|
|
||||||
|
|
||||||
/* 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
|
|
||||||
* maskedDB are not all equal to zero, output "inconsistent" and stop. */
|
|
||||||
var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
|
|
||||||
if((maskedDB.charCodeAt(0) & mask) !== 0) {
|
|
||||||
throw {
|
|
||||||
message: 'Bits beyond keysize not zero as expected.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 7. Let dbMask = MGF(H, emLen - hLen - 1). */
|
|
||||||
var dbMask = mgf.generate(h, maskLen);
|
|
||||||
|
|
||||||
/* 8. Let DB = maskedDB \xor dbMask. */
|
|
||||||
var db = '';
|
|
||||||
for(i = 0; i < maskLen; i ++) {
|
|
||||||
db += String.fromCharCode(maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 9. Set the leftmost 8emLen - emBits bits of the leftmost octet
|
|
||||||
* in DB to zero. */
|
|
||||||
db = String.fromCharCode(db.charCodeAt(0) & ~mask) + db.substr(1);
|
|
||||||
|
|
||||||
/* 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
|
|
||||||
* or if the octet at position emLen - hLen - sLen - 1 (the leftmost
|
|
||||||
* position is "position 1") does not have hexadecimal value 0x01,
|
|
||||||
* output "inconsistent" and stop. */
|
|
||||||
var checkLen = emLen - hLen - sLen - 2;
|
|
||||||
for(i = 0; i < checkLen; i ++) {
|
|
||||||
if(db.charCodeAt(i) !== 0x00) {
|
|
||||||
throw {
|
|
||||||
message: 'Leftmost octets not zero as expected'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(db.charCodeAt(checkLen) !== 0x01) {
|
|
||||||
throw {
|
|
||||||
message: 'Inconsistent PSS signature, 0x01 marker not found'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 11. Let salt be the last sLen octets of DB. */
|
|
||||||
var salt = db.substr(-sLen);
|
|
||||||
|
|
||||||
/* 12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
|
|
||||||
var m_ = new forge.util.ByteBuffer();
|
|
||||||
m_.fillWithByte(0, 8);
|
|
||||||
m_.putBytes(mHash);
|
|
||||||
m_.putBytes(salt);
|
|
||||||
|
|
||||||
/* 13. Let H' = Hash(M'), an octet string of length hLen. */
|
|
||||||
hash.start();
|
|
||||||
hash.update(m_.getBytes());
|
|
||||||
var h_ = hash.digest().getBytes();
|
|
||||||
|
|
||||||
/* 14. If H = H', output "consistent." Otherwise, output "inconsistent." */
|
|
||||||
return h === h_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode PSS signature.
|
|
||||||
*
|
|
||||||
* This function implements EMSA-PSS-ENCODE as per RFC 3447, section 9.1.1
|
|
||||||
*
|
|
||||||
* @param md the message digest object with the hash to sign.
|
|
||||||
* @param modsBits Length of the RSA modulus in bits.
|
|
||||||
* @return the encoded message, string of length ceil((modBits - 1) / 8)
|
|
||||||
*/
|
|
||||||
pssobj.encode = function(md, modBits) {
|
|
||||||
var i;
|
|
||||||
var emBits = modBits - 1;
|
|
||||||
var emLen = Math.ceil(emBits / 8);
|
|
||||||
|
|
||||||
/* 2. Let mHash = Hash(M), an octet string of length hLen. */
|
|
||||||
var mHash = md.digest().getBytes();
|
|
||||||
|
|
||||||
/* 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. */
|
|
||||||
if(emLen < hLen + sLen + 2) {
|
|
||||||
throw {
|
|
||||||
message: 'Message is too long to encrypt'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. Generate a random octet string salt of length sLen; if sLen = 0,
|
|
||||||
* then salt is the empty string. */
|
|
||||||
var salt = forge.random.getBytes(sLen);
|
|
||||||
|
|
||||||
/* 5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; */
|
|
||||||
var m_ = new forge.util.ByteBuffer();
|
|
||||||
m_.fillWithByte(0, 8);
|
|
||||||
m_.putBytes(mHash);
|
|
||||||
m_.putBytes(salt);
|
|
||||||
|
|
||||||
/* 6. Let H = Hash(M'), an octet string of length hLen. */
|
|
||||||
hash.start();
|
|
||||||
hash.update(m_.getBytes());
|
|
||||||
var h = hash.digest().getBytes();
|
|
||||||
|
|
||||||
/* 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
|
|
||||||
* zero octets. The length of PS may be 0. */
|
|
||||||
var ps = new forge.util.ByteBuffer();
|
|
||||||
ps.fillWithByte(0, emLen - sLen - hLen - 2);
|
|
||||||
|
|
||||||
/* 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
|
|
||||||
* emLen - hLen - 1. */
|
|
||||||
ps.putByte(0x01);
|
|
||||||
ps.putBytes(salt);
|
|
||||||
var db = ps.getBytes();
|
|
||||||
|
|
||||||
/* 9. Let dbMask = MGF(H, emLen - hLen - 1). */
|
|
||||||
var maskLen = emLen - hLen - 1;
|
|
||||||
var dbMask = mgf.generate(h, maskLen);
|
|
||||||
|
|
||||||
/* 10. Let maskedDB = DB \xor dbMask. */
|
|
||||||
var maskedDB = '';
|
|
||||||
for(i = 0; i < maskLen; i ++) {
|
|
||||||
maskedDB += String.fromCharCode(db.charCodeAt(i) ^ dbMask.charCodeAt(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
|
|
||||||
* maskedDB to zero. */
|
|
||||||
var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;
|
|
||||||
maskedDB = String.fromCharCode(maskedDB.charCodeAt(0) & ~mask) +
|
|
||||||
maskedDB.substr(1);
|
|
||||||
|
|
||||||
/* 12. Let EM = maskedDB || H || 0xbc.
|
|
||||||
* 13. Output EM. */
|
|
||||||
return maskedDB + h + String.fromCharCode(0xbc);
|
|
||||||
};
|
|
||||||
|
|
||||||
return pssobj;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'pss';
|
|
||||||
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', './random', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,221 +0,0 @@
|
|||||||
/**
|
|
||||||
* An API for getting cryptographically-secure random bytes. The bytes are
|
|
||||||
* generated using the Fortuna algorithm devised by Bruce Schneier and
|
|
||||||
* Niels Ferguson.
|
|
||||||
*
|
|
||||||
* Getting strong random bytes is not yet easy to do in javascript. The only
|
|
||||||
* truish random entropy that can be collected is from the mouse, keyboard, or
|
|
||||||
* from timing with respect to page loads, etc. This generator makes a poor
|
|
||||||
* attempt at providing random bytes when those sources haven't yet provided
|
|
||||||
* enough entropy to initially seed or to reseed the PRNG.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// forge.random already defined
|
|
||||||
if(forge.random && forge.random.getBytes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(function(jQuery) {
|
|
||||||
|
|
||||||
// the default prng plugin, uses AES-128
|
|
||||||
var prng_aes = {};
|
|
||||||
var _prng_aes_output = new Array(4);
|
|
||||||
var _prng_aes_buffer = forge.util.createBuffer();
|
|
||||||
prng_aes.formatKey = function(key) {
|
|
||||||
// convert the key into 32-bit integers
|
|
||||||
var tmp = forge.util.createBuffer(key);
|
|
||||||
key = new Array(4);
|
|
||||||
key[0] = tmp.getInt32();
|
|
||||||
key[1] = tmp.getInt32();
|
|
||||||
key[2] = tmp.getInt32();
|
|
||||||
key[3] = tmp.getInt32();
|
|
||||||
|
|
||||||
// return the expanded key
|
|
||||||
return forge.aes._expandKey(key, false);
|
|
||||||
};
|
|
||||||
prng_aes.formatSeed = function(seed) {
|
|
||||||
// convert seed into 32-bit integers
|
|
||||||
var tmp = forge.util.createBuffer(seed);
|
|
||||||
seed = new Array(4);
|
|
||||||
seed[0] = tmp.getInt32();
|
|
||||||
seed[1] = tmp.getInt32();
|
|
||||||
seed[2] = tmp.getInt32();
|
|
||||||
seed[3] = tmp.getInt32();
|
|
||||||
return seed;
|
|
||||||
};
|
|
||||||
prng_aes.cipher = function(key, seed) {
|
|
||||||
forge.aes._updateBlock(key, seed, _prng_aes_output, false);
|
|
||||||
_prng_aes_buffer.putInt32(_prng_aes_output[0]);
|
|
||||||
_prng_aes_buffer.putInt32(_prng_aes_output[1]);
|
|
||||||
_prng_aes_buffer.putInt32(_prng_aes_output[2]);
|
|
||||||
_prng_aes_buffer.putInt32(_prng_aes_output[3]);
|
|
||||||
return _prng_aes_buffer.getBytes();
|
|
||||||
};
|
|
||||||
prng_aes.increment = function(seed) {
|
|
||||||
// FIXME: do we care about carry or signed issues?
|
|
||||||
++seed[3];
|
|
||||||
return seed;
|
|
||||||
};
|
|
||||||
prng_aes.md = forge.md.sha1;
|
|
||||||
|
|
||||||
// create default prng context
|
|
||||||
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 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
|
|
||||||
if(typeof window === 'undefined' || window.document === undefined) {
|
|
||||||
// FIXME:
|
|
||||||
}
|
|
||||||
|
|
||||||
// get load time entropy
|
|
||||||
_ctx.collectInt(+new Date(), 32);
|
|
||||||
|
|
||||||
// add some entropy from navigator object
|
|
||||||
if(typeof(navigator) !== 'undefined') {
|
|
||||||
var _navBytes = '';
|
|
||||||
for(var key in navigator) {
|
|
||||||
try {
|
|
||||||
if(typeof(navigator[key]) == 'string') {
|
|
||||||
_navBytes += navigator[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
/* Some navigator keys might not be accessible, e.g. the geolocation
|
|
||||||
attribute throws an exception if touched in Mozilla chrome://
|
|
||||||
context.
|
|
||||||
|
|
||||||
Silently ignore this and just don't use this as a source of
|
|
||||||
entropy. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ctx.collect(_navBytes);
|
|
||||||
_navBytes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add mouse and keyboard collectors if jquery is available
|
|
||||||
if(jQuery) {
|
|
||||||
// set up mouse entropy capture
|
|
||||||
jQuery().mousemove(function(e) {
|
|
||||||
// add mouse coords
|
|
||||||
_ctx.collectInt(e.clientX, 16);
|
|
||||||
_ctx.collectInt(e.clientY, 16);
|
|
||||||
});
|
|
||||||
|
|
||||||
// set up keyboard entropy capture
|
|
||||||
jQuery().keypress(function(e) {
|
|
||||||
_ctx.collectInt(e.charCode, 8);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Random API */
|
|
||||||
if(!forge.random) {
|
|
||||||
forge.random = _ctx;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// extend forge.random with _ctx
|
|
||||||
for(var key in _ctx) {
|
|
||||||
forge.random[key] = _ctx[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets random bytes. If a native secure crypto API is unavailable, this
|
|
||||||
* method tries to make the bytes more unpredictable by drawing from data that
|
|
||||||
* can be collected from the user of the browser, eg: mouse movement.
|
|
||||||
*
|
|
||||||
* If a callback is given, this method will be called asynchronously.
|
|
||||||
*
|
|
||||||
* @param count the number of random bytes to get.
|
|
||||||
* @param [callback(err, bytes)] called once the operation completes.
|
|
||||||
*
|
|
||||||
* @return the random bytes in a string.
|
|
||||||
*/
|
|
||||||
forge.random.getBytes = function(count, callback) {
|
|
||||||
return forge.random.generate(count, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets random bytes asynchronously. If a native secure crypto API is
|
|
||||||
* unavailable, this method tries to make the bytes more unpredictable by
|
|
||||||
* drawing from data that can be collected from the user of the browser,
|
|
||||||
* eg: mouse movement.
|
|
||||||
*
|
|
||||||
* @param count the number of random bytes to get.
|
|
||||||
*
|
|
||||||
* @return the random bytes in a string.
|
|
||||||
*/
|
|
||||||
forge.random.getBytesSync = function(count) {
|
|
||||||
return forge.random.generate(count);
|
|
||||||
};
|
|
||||||
|
|
||||||
})(typeof(jQuery) !== 'undefined' ? jQuery : null);
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'random';
|
|
||||||
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', './md', './prng', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
472
src/lib/rc2.js
472
src/lib/rc2.js
@ -1,472 +0,0 @@
|
|||||||
/**
|
|
||||||
* RC2 implementation.
|
|
||||||
*
|
|
||||||
* @author Stefan Siegl
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
||||||
*
|
|
||||||
* Information on the RC2 cipher is available from RFC #2268,
|
|
||||||
* http://www.ietf.org/rfc/rfc2268.txt
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var piTable = [
|
|
||||||
0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
|
|
||||||
0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
|
|
||||||
0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
|
|
||||||
0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
|
|
||||||
0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
|
|
||||||
0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
|
|
||||||
0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
|
|
||||||
0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
|
|
||||||
0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
|
|
||||||
0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
|
|
||||||
0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
|
|
||||||
0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
|
|
||||||
0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
|
|
||||||
0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
|
|
||||||
0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
|
|
||||||
0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad
|
|
||||||
];
|
|
||||||
|
|
||||||
var s = [1, 2, 3, 5];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotate a word left by given number of bits.
|
|
||||||
*
|
|
||||||
* Bits that are shifted out on the left are put back in on the right
|
|
||||||
* hand side.
|
|
||||||
*
|
|
||||||
* @param word The word to shift left.
|
|
||||||
* @param bits The number of bits to shift by.
|
|
||||||
* @return The rotated word.
|
|
||||||
*/
|
|
||||||
var rol = function(word, bits) {
|
|
||||||
return ((word << bits) & 0xffff) | ((word & 0xffff) >> (16 - bits));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotate a word right by given number of bits.
|
|
||||||
*
|
|
||||||
* Bits that are shifted out on the right are put back in on the left
|
|
||||||
* hand side.
|
|
||||||
*
|
|
||||||
* @param word The word to shift right.
|
|
||||||
* @param bits The number of bits to shift by.
|
|
||||||
* @return The rotated word.
|
|
||||||
*/
|
|
||||||
var ror = function(word, bits) {
|
|
||||||
return ((word & 0xffff) >> bits) | ((word << (16 - bits)) & 0xffff);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* RC2 API */
|
|
||||||
forge.rc2 = forge.rc2 || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform RC2 key expansion as per RFC #2268, section 2.
|
|
||||||
*
|
|
||||||
* @param key variable-length user key (between 1 and 128 bytes)
|
|
||||||
* @param effKeyBits number of effective key bits (default: 128)
|
|
||||||
* @return the expanded RC2 key (ByteBuffer of 128 bytes)
|
|
||||||
*/
|
|
||||||
forge.rc2.expandKey = function(key, effKeyBits) {
|
|
||||||
if(typeof key === 'string') {
|
|
||||||
key = forge.util.createBuffer(key);
|
|
||||||
}
|
|
||||||
effKeyBits = effKeyBits || 128;
|
|
||||||
|
|
||||||
/* introduce variables that match the names used in RFC #2268 */
|
|
||||||
var L = key;
|
|
||||||
var T = key.length();
|
|
||||||
var T1 = effKeyBits;
|
|
||||||
var T8 = Math.ceil(T1 / 8);
|
|
||||||
var TM = 0xff >> (T1 & 0x07);
|
|
||||||
var i;
|
|
||||||
|
|
||||||
for(i = T; i < 128; i ++) {
|
|
||||||
L.putByte(piTable[(L.at(i - 1) + L.at(i - T)) & 0xff]);
|
|
||||||
}
|
|
||||||
|
|
||||||
L.setAt(128 - T8, piTable[L.at(128 - T8) & TM]);
|
|
||||||
|
|
||||||
for(i = 127 - T8; i >= 0; i --) {
|
|
||||||
L.setAt(i, piTable[L.at(i + 1) ^ L.at(i + T8)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return L;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a RC2 cipher object.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use (as base for key generation).
|
|
||||||
* @param bits the number of effective key bits.
|
|
||||||
* @param encrypt false for decryption, true for encryption.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
var createCipher = function(key, bits, encrypt)
|
|
||||||
{
|
|
||||||
var _finish = false, _input = null, _output = null, _iv = null;
|
|
||||||
var mixRound, mashRound;
|
|
||||||
var i, j, K = [];
|
|
||||||
|
|
||||||
/* Expand key and fill into K[] Array */
|
|
||||||
key = forge.rc2.expandKey(key, bits);
|
|
||||||
for(i = 0; i < 64; i ++) {
|
|
||||||
K.push(key.getInt16Le());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(encrypt) {
|
|
||||||
/**
|
|
||||||
* Perform one mixing round "in place".
|
|
||||||
*
|
|
||||||
* @param R Array of four words to perform mixing on.
|
|
||||||
*/
|
|
||||||
mixRound = function(R) {
|
|
||||||
for(i = 0; i < 4; i++) {
|
|
||||||
R[i] += K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +
|
|
||||||
((~R[(i + 3) % 4]) & R[(i + 1) % 4]);
|
|
||||||
R[i] = rol(R[i], s[i]);
|
|
||||||
j ++;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform one mashing round "in place".
|
|
||||||
*
|
|
||||||
* @param R Array of four words to perform mashing on.
|
|
||||||
*/
|
|
||||||
mashRound = function(R) {
|
|
||||||
for(i = 0; i < 4; i ++) {
|
|
||||||
R[i] += K[R[(i + 3) % 4] & 63];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* Perform one r-mixing round "in place".
|
|
||||||
*
|
|
||||||
* @param R Array of four words to perform mixing on.
|
|
||||||
*/
|
|
||||||
mixRound = function(R) {
|
|
||||||
for(i = 3; i >= 0; i--) {
|
|
||||||
R[i] = ror(R[i], s[i]);
|
|
||||||
R[i] -= K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +
|
|
||||||
((~R[(i + 3) % 4]) & R[(i + 1) % 4]);
|
|
||||||
j --;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform one r-mashing round "in place".
|
|
||||||
*
|
|
||||||
* @param R Array of four words to perform mashing on.
|
|
||||||
*/
|
|
||||||
mashRound = function(R) {
|
|
||||||
for(i = 3; i >= 0; i--) {
|
|
||||||
R[i] -= K[R[(i + 3) % 4] & 63];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the specified cipher execution plan.
|
|
||||||
*
|
|
||||||
* This function takes four words from the input buffer, applies the IV on
|
|
||||||
* it (if requested) and runs the provided execution plan.
|
|
||||||
*
|
|
||||||
* The plan must be put together in form of a array of arrays. Where the
|
|
||||||
* outer one is simply a list of steps to perform and the inner one needs
|
|
||||||
* to have two elements: the first one telling how many rounds to perform,
|
|
||||||
* the second one telling what to do (i.e. the function to call).
|
|
||||||
*
|
|
||||||
* @param {Array} plan The plan to execute.
|
|
||||||
*/
|
|
||||||
var runPlan = function(plan) {
|
|
||||||
var R = [];
|
|
||||||
|
|
||||||
/* Get data from input buffer and fill the four words into R */
|
|
||||||
for(i = 0; i < 4; i ++) {
|
|
||||||
var val = _input.getInt16Le();
|
|
||||||
|
|
||||||
if(_iv !== null) {
|
|
||||||
if(encrypt) {
|
|
||||||
/* We're encrypting, apply the IV first. */
|
|
||||||
val ^= _iv.getInt16Le();
|
|
||||||
} else {
|
|
||||||
/* We're decryption, keep cipher text for next block. */
|
|
||||||
_iv.putInt16Le(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
R.push(val & 0xffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset global "j" variable as per spec. */
|
|
||||||
j = encrypt ? 0 : 63;
|
|
||||||
|
|
||||||
/* Run execution plan. */
|
|
||||||
for(var ptr = 0; ptr < plan.length; ptr ++) {
|
|
||||||
for(var ctr = 0; ctr < plan[ptr][0]; ctr ++) {
|
|
||||||
plan[ptr][1](R);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write back result to output buffer. */
|
|
||||||
for(i = 0; i < 4; i ++) {
|
|
||||||
if(_iv !== null) {
|
|
||||||
if(encrypt) {
|
|
||||||
/* We're encrypting in CBC-mode, feed back encrypted bytes into
|
|
||||||
IV buffer to carry it forward to next block. */
|
|
||||||
_iv.putInt16Le(R[i]);
|
|
||||||
} else {
|
|
||||||
R[i] ^= _iv.getInt16Le();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_output.putInt16Le(R[i]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Create cipher object */
|
|
||||||
var cipher = null;
|
|
||||||
cipher = {
|
|
||||||
/**
|
|
||||||
* Starts or restarts the encryption or decryption process, whichever
|
|
||||||
* was previously configured.
|
|
||||||
*
|
|
||||||
* To use the cipher in CBC mode, iv may be given either as a string
|
|
||||||
* of bytes, or as a byte buffer. For ECB mode, give null as iv.
|
|
||||||
*
|
|
||||||
* @param iv the initialization vector to use, null for ECB mode.
|
|
||||||
* @param output the output the buffer to write to, null to create one.
|
|
||||||
*/
|
|
||||||
start: function(iv, output) {
|
|
||||||
if(iv) {
|
|
||||||
/* CBC mode */
|
|
||||||
if(typeof key === 'string' && iv.length === 8) {
|
|
||||||
iv = forge.util.createBuffer(iv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_finish = false;
|
|
||||||
_input = forge.util.createBuffer();
|
|
||||||
_output = output || new forge.util.createBuffer();
|
|
||||||
_iv = iv;
|
|
||||||
|
|
||||||
cipher.output = _output;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the next block.
|
|
||||||
*
|
|
||||||
* @param input the buffer to read from.
|
|
||||||
*/
|
|
||||||
update: function(input) {
|
|
||||||
if(!_finish) {
|
|
||||||
// not finishing, so fill the input buffer with more input
|
|
||||||
_input.putBuffer(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
while(_input.length() >= 8) {
|
|
||||||
runPlan([
|
|
||||||
[ 5, mixRound ],
|
|
||||||
[ 1, mashRound ],
|
|
||||||
[ 6, mixRound ],
|
|
||||||
[ 1, mashRound ],
|
|
||||||
[ 5, mixRound ]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finishes encrypting or decrypting.
|
|
||||||
*
|
|
||||||
* @param pad a padding function to use, null for PKCS#7 padding,
|
|
||||||
* signature(blockSize, buffer, decrypt).
|
|
||||||
*
|
|
||||||
* @return true if successful, false on error.
|
|
||||||
*/
|
|
||||||
finish: function(pad) {
|
|
||||||
var rval = true;
|
|
||||||
|
|
||||||
if(encrypt) {
|
|
||||||
if(pad) {
|
|
||||||
rval = pad(8, _input, !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());
|
|
||||||
_input.fillWithByte(padding, padding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rval) {
|
|
||||||
// do final update
|
|
||||||
_finish = true;
|
|
||||||
cipher.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!encrypt) {
|
|
||||||
// check for error: input data not a multiple of block size
|
|
||||||
rval = (_input.length() === 0);
|
|
||||||
if(rval) {
|
|
||||||
if(pad) {
|
|
||||||
rval = pad(8, _output, !encrypt);
|
|
||||||
} else {
|
|
||||||
// ensure padding byte count is valid
|
|
||||||
var len = _output.length();
|
|
||||||
var count = _output.at(len - 1);
|
|
||||||
|
|
||||||
if(count > len) {
|
|
||||||
rval = false;
|
|
||||||
} else {
|
|
||||||
// trim off padding bytes
|
|
||||||
_output.truncate(count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RC2 cipher object to encrypt data in ECB or CBC mode 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 or a byte buffer.
|
|
||||||
* The cipher is initialized to use 128 effective key bits.
|
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.rc2.startEncrypting = function(key, iv, output) {
|
|
||||||
var cipher = forge.rc2.createEncryptionCipher(key, 128);
|
|
||||||
cipher.start(iv, output);
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the
|
|
||||||
* given symmetric key.
|
|
||||||
*
|
|
||||||
* The key may be given as a string of bytes or a byte buffer.
|
|
||||||
*
|
|
||||||
* To start encrypting call start() on the cipher with an iv and optional
|
|
||||||
* output buffer.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.rc2.createEncryptionCipher = function(key, bits) {
|
|
||||||
return createCipher(key, bits, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RC2 cipher object to decrypt data in ECB or CBC mode 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 or a byte buffer.
|
|
||||||
* The cipher is initialized to use 128 effective key bits.
|
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.rc2.startDecrypting = function(key, iv, output) {
|
|
||||||
var cipher = forge.rc2.createDecryptionCipher(key, 128);
|
|
||||||
cipher.start(iv, output);
|
|
||||||
return cipher;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the
|
|
||||||
* given symmetric key.
|
|
||||||
*
|
|
||||||
* The key may be given as a string of bytes or a byte buffer.
|
|
||||||
*
|
|
||||||
* To start decrypting call start() on the cipher with an iv and optional
|
|
||||||
* output buffer.
|
|
||||||
*
|
|
||||||
* @param key the symmetric key to use.
|
|
||||||
*
|
|
||||||
* @return the cipher.
|
|
||||||
*/
|
|
||||||
forge.rc2.createDecryptionCipher = function(key, bits) {
|
|
||||||
return createCipher(key, bits, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'rc2';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
RequireJS 2.1.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
|
||||||
Available via the MIT or new BSD license.
|
|
||||||
see: http://github.com/jrburke/requirejs for details
|
|
||||||
*/
|
|
||||||
var requirejs,require,define;
|
|
||||||
(function(Z){function H(b){return"[object Function]"===L.call(b)}function I(b){return"[object Array]"===L.call(b)}function y(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function M(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function s(b,c){return ga.call(b,c)}function l(b,c){return s(b,c)&&b[c]}function F(b,c){for(var d in b)if(s(b,d)&&c(b[d],d))break}function Q(b,c,d,h){c&&F(c,function(c,j){if(d||!s(b,j))h&&"string"!==typeof c?(b[j]||(b[j]={}),Q(b[j],
|
|
||||||
c,d,h)):b[j]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function aa(b){throw b;}function ba(b){if(!b)return b;var c=Z;y(b.split("."),function(b){c=c[b]});return c}function A(b,c,d,h){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=h;d&&(c.originalError=d);return c}function ha(b){function c(a,f,b){var e,m,c,g,d,h,j,i=f&&f.split("/");e=i;var n=k.map,p=n&&n["*"];if(a&&"."===a.charAt(0))if(f){e=l(k.pkgs,f)?i=[f]:i.slice(0,i.length-
|
|
||||||
1);f=a=e.concat(a.split("/"));for(e=0;f[e];e+=1)if(m=f[e],"."===m)f.splice(e,1),e-=1;else if(".."===m)if(1===e&&(".."===f[2]||".."===f[0]))break;else 0<e&&(f.splice(e-1,2),e-=2);e=l(k.pkgs,f=a[0]);a=a.join("/");e&&a===f+"/"+e.main&&(a=f)}else 0===a.indexOf("./")&&(a=a.substring(2));if(b&&n&&(i||p)){f=a.split("/");for(e=f.length;0<e;e-=1){c=f.slice(0,e).join("/");if(i)for(m=i.length;0<m;m-=1)if(b=l(n,i.slice(0,m).join("/")))if(b=l(b,c)){g=b;d=e;break}if(g)break;!h&&(p&&l(p,c))&&(h=l(p,c),j=e)}!g&&
|
|
||||||
h&&(g=h,d=j);g&&(f.splice(0,d,g),a=f.join("/"))}return a}function d(a){z&&y(document.getElementsByTagName("script"),function(f){if(f.getAttribute("data-requiremodule")===a&&f.getAttribute("data-requirecontext")===i.contextName)return f.parentNode.removeChild(f),!0})}function h(a){var f=l(k.paths,a);if(f&&I(f)&&1<f.length)return d(a),f.shift(),i.require.undef(a),i.require([a]),!0}function $(a){var f,b=a?a.indexOf("!"):-1;-1<b&&(f=a.substring(0,b),a=a.substring(b+1,a.length));return[f,a]}function n(a,
|
|
||||||
f,b,e){var m,B,g=null,d=f?f.name:null,h=a,j=!0,k="";a||(j=!1,a="_@r"+(L+=1));a=$(a);g=a[0];a=a[1];g&&(g=c(g,d,e),B=l(r,g));a&&(g?k=B&&B.normalize?B.normalize(a,function(a){return c(a,d,e)}):c(a,d,e):(k=c(a,d,e),a=$(k),g=a[0],k=a[1],b=!0,m=i.nameToUrl(k)));b=g&&!B&&!b?"_unnormalized"+(M+=1):"";return{prefix:g,name:k,parentMap:f,unnormalized:!!b,url:m,originalName:h,isDefine:j,id:(g?g+"!"+k:k)+b}}function q(a){var f=a.id,b=l(p,f);b||(b=p[f]=new i.Module(a));return b}function t(a,f,b){var e=a.id,m=l(p,
|
|
||||||
e);if(s(r,e)&&(!m||m.defineEmitComplete))"defined"===f&&b(r[e]);else if(m=q(a),m.error&&"error"===f)b(m.error);else m.on(f,b)}function v(a,f){var b=a.requireModules,e=!1;if(f)f(a);else if(y(b,function(f){if(f=l(p,f))f.error=a,f.events.error&&(e=!0,f.emit("error",a))}),!e)j.onError(a)}function w(){R.length&&(ia.apply(G,[G.length-1,0].concat(R)),R=[])}function x(a){delete p[a];delete T[a]}function E(a,f,b){var e=a.map.id;a.error?a.emit("error",a.error):(f[e]=!0,y(a.depMaps,function(e,c){var g=e.id,
|
|
||||||
d=l(p,g);d&&(!a.depMatched[c]&&!b[g])&&(l(f,g)?(a.defineDep(c,r[g]),a.check()):E(d,f,b))}),b[e]=!0)}function C(){var a,f,b,e,m=(b=1E3*k.waitSeconds)&&i.startTime+b<(new Date).getTime(),c=[],g=[],j=!1,l=!0;if(!U){U=!0;F(T,function(b){a=b.map;f=a.id;if(b.enabled&&(a.isDefine||g.push(b),!b.error))if(!b.inited&&m)h(f)?j=e=!0:(c.push(f),d(f));else if(!b.inited&&(b.fetched&&a.isDefine)&&(j=!0,!a.prefix))return l=!1});if(m&&c.length)return b=A("timeout","Load timeout for modules: "+c,null,c),b.contextName=
|
|
||||||
i.contextName,v(b);l&&y(g,function(a){E(a,{},{})});if((!m||e)&&j)if((z||da)&&!V)V=setTimeout(function(){V=0;C()},50);U=!1}}function D(a){s(r,a[0])||q(n(a[0],null,!0)).init(a[1],a[2])}function J(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!W?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||W)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function K(){var a;for(w();G.length;){a=
|
|
||||||
G.shift();if(null===a[0])return v(A("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));D(a)}}var U,X,i,N,V,k={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},shim:{},config:{}},p={},T={},Y={},G=[],r={},S={},L=1,M=1;N={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){var b=
|
|
||||||
l(k.pkgs,a.map.id);return(b?l(k.config,a.map.id+"/"+b.main):l(k.config,a.map.id))||{}},exports:r[a.map.id]}}};X=function(a){this.events=l(Y,a.id)||{};this.map=a;this.shim=l(k.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};X.prototype={init:function(a,b,c,e){e=e||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=!0;
|
|
||||||
this.ignore=e.ignore;e.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
|
|
||||||
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var e=this.exports,m=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(H(m)){if(this.events.error&&this.map.isDefine||j.onError!==aa)try{e=i.execCb(c,m,b,e)}catch(d){a=d}else e=i.execCb(c,m,b,e);this.map.isDefine&&((b=this.module)&&void 0!==b.exports&&b.exports!==
|
|
||||||
this.exports?e=b.exports:void 0===e&&this.usingExports&&(e=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",v(this.error=a)}else e=m;this.exports=e;if(this.map.isDefine&&!this.ignore&&(r[c]=e,j.onResourceLoad))j.onResourceLoad(i,this.map,this.depMaps);x(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=
|
|
||||||
!0)}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=n(a.prefix);this.depMaps.push(d);t(d,"defined",u(this,function(e){var m,d;d=this.map.name;var g=this.map.parentMap?this.map.parentMap.name:null,h=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(e.normalize&&(d=e.normalize(d,function(a){return c(a,g,!0)})||""),e=n(a.prefix+"!"+d,this.map.parentMap),t(e,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),
|
|
||||||
d=l(p,e.id)){this.depMaps.push(e);if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else m=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),m.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];F(p,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&x(a.map.id)});v(a)}),m.fromText=u(this,function(e,c){var d=a.name,g=n(d),B=O;c&&(e=c);B&&(O=!1);q(g);s(k.config,b)&&(k.config[d]=k.config[b]);try{j.exec(e)}catch(ca){return v(A("fromtexteval",
|
|
||||||
"fromText eval for "+b+" failed: "+ca,ca,[b]))}B&&(O=!0);this.depMaps.push(g);i.completeLoad(d);h([d],m)}),e.load(a.name,h,m,k)}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){T[this.map.id]=this;this.enabling=this.enabled=!0;y(this.depMaps,u(this,function(a,b){var c,e;if("string"===typeof a){a=n(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=l(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;t(a,"defined",u(this,function(a){this.defineDep(b,
|
|
||||||
a);this.check()}));this.errback&&t(a,"error",u(this,this.errback))}c=a.id;e=p[c];!s(N,c)&&(e&&!e.enabled)&&i.enable(a,this)}));F(this.pluginMaps,u(this,function(a){var b=l(p,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){y(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:k,contextName:b,registry:p,defined:r,urlFetched:S,defQueue:G,Module:X,makeModuleMap:n,
|
|
||||||
nextTick:j.nextTick,onError:v,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=k.pkgs,c=k.shim,e={paths:!0,config:!0,map:!0};F(a,function(a,b){e[b]?"map"===b?(k.map||(k.map={}),Q(k[b],a,!0,!0)):Q(k[b],a,!0):k[b]=a});a.shim&&(F(a.shim,function(a,b){I(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);c[b]=a}),k.shim=c);a.packages&&(y(a.packages,function(a){a="string"===typeof a?{name:a}:a;b[a.name]={name:a.name,
|
|
||||||
location:a.location||a.name,main:(a.main||"main").replace(ja,"").replace(ea,"")}}),k.pkgs=b);F(p,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=n(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(Z,arguments));return b||a.exports&&ba(a.exports)}},makeRequire:function(a,f){function d(e,c,h){var g,k;f.enableBuildCallback&&(c&&H(c))&&(c.__requireJsBuild=!0);if("string"===typeof e){if(H(c))return v(A("requireargs",
|
|
||||||
"Invalid require call"),h);if(a&&s(N,e))return N[e](p[a.id]);if(j.get)return j.get(i,e,a,d);g=n(e,a,!1,!0);g=g.id;return!s(r,g)?v(A("notloaded",'Module name "'+g+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[g]}K();i.nextTick(function(){K();k=q(n(null,a));k.skipMap=f.skipMap;k.init(e,c,h,{enabled:!0});C()});return d}f=f||{};Q(d,{isBrowser:z,toUrl:function(b){var d,f=b.lastIndexOf("."),g=b.split("/")[0];if(-1!==f&&(!("."===g||".."===g)||1<f))d=b.substring(f,b.length),b=
|
|
||||||
b.substring(0,f);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return s(r,n(b,a,!1,!0).id)},specified:function(b){b=n(b,a,!1,!0).id;return s(r,b)||s(p,b)}});a||(d.undef=function(b){w();var c=n(b,a,!0),f=l(p,b);delete r[b];delete S[c.url];delete Y[b];f&&(f.events.defined&&(Y[b]=f.events),x(b))});return d},enable:function(a){l(p,a.id)&&q(a).enable()},completeLoad:function(a){var b,c,e=l(k.shim,a)||{},d=e.exports;for(w();G.length;){c=G.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===
|
|
||||||
a&&(b=!0);D(c)}c=l(p,a);if(!b&&!s(r,a)&&c&&!c.inited){if(k.enforceDefine&&(!d||!ba(d)))return h(a)?void 0:v(A("nodefine","No define call for "+a,null,[a]));D([a,e.deps||[],e.exportsFn])}C()},nameToUrl:function(a,b,c){var e,d,h,g,i,n;if(j.jsExtRegExp.test(a))g=a+(b||"");else{e=k.paths;d=k.pkgs;g=a.split("/");for(i=g.length;0<i;i-=1)if(n=g.slice(0,i).join("/"),h=l(d,n),n=l(e,n)){I(n)&&(n=n[0]);g.splice(0,i,n);break}else if(h){a=a===h.name?h.location+"/"+h.main:h.location;g.splice(0,i,a);break}g=g.join("/");
|
|
||||||
g+=b||(/\?/.test(g)||c?"":".js");g=("/"===g.charAt(0)||g.match(/^[\w\+\.\-]+:/)?"":k.baseUrl)+g}return k.urlArgs?g+((-1===g.indexOf("?")?"?":"&")+k.urlArgs):g},load:function(a,b){j.load(i,a,b)},execCb:function(a,b,c,e){return b.apply(e,c)},onScriptLoad:function(a){if("load"===a.type||ka.test((a.currentTarget||a.srcElement).readyState))P=null,a=J(a),i.completeLoad(a.id)},onScriptError:function(a){var b=J(a);if(!h(b.id))return v(A("scripterror","Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();
|
|
||||||
return i}var j,w,x,C,J,D,P,K,q,fa,la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,ea=/\.js$/,ja=/^\.\//;w=Object.prototype;var L=w.toString,ga=w.hasOwnProperty,ia=Array.prototype.splice,z=!!("undefined"!==typeof window&&navigator&&window.document),da=!z&&"undefined"!==typeof importScripts,ka=z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,W="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),E={},t={},R=[],O=
|
|
||||||
!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(H(requirejs))return;t=requirejs;requirejs=void 0}"undefined"!==typeof require&&!H(require)&&(t=require,require=void 0);j=requirejs=function(b,c,d,h){var q,n="_";!I(b)&&"string"!==typeof b&&(q=b,I(c)?(b=c,c=d,d=h):b=[]);q&&q.context&&(n=q.context);(h=l(E,n))||(h=E[n]=j.s.newContext(n));q&&h.configure(q);return h.require(b,c,d)};j.config=function(b){return j(b)};j.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,
|
|
||||||
4)}:function(b){b()};require||(require=j);j.version="2.1.8";j.jsExtRegExp=/^\/|:|\?|\.js$/;j.isBrowser=z;w=j.s={contexts:E,newContext:ha};j({});y(["toUrl","undef","defined","specified"],function(b){j[b]=function(){var c=E._;return c.require[b].apply(c,arguments)}});if(z&&(x=w.head=document.getElementsByTagName("head")[0],C=document.getElementsByTagName("base")[0]))x=w.head=C.parentNode;j.onError=aa;j.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):
|
|
||||||
document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};j.load=function(b,c,d){var h=b&&b.config||{};if(z)return h=j.createNode(h,c,d),h.setAttribute("data-requirecontext",b.contextName),h.setAttribute("data-requiremodule",c),h.attachEvent&&!(h.attachEvent.toString&&0>h.attachEvent.toString().indexOf("[native code"))&&!W?(O=!0,h.attachEvent("onreadystatechange",b.onScriptLoad)):(h.addEventListener("load",b.onScriptLoad,!1),h.addEventListener("error",
|
|
||||||
b.onScriptError,!1)),h.src=d,K=h,C?x.insertBefore(h,C):x.appendChild(h),K=null,h;if(da)try{importScripts(d),b.completeLoad(c)}catch(l){b.onError(A("importscripts","importScripts failed for "+c+" at "+d,l,[c]))}};z&&M(document.getElementsByTagName("script"),function(b){x||(x=b.parentNode);if(J=b.getAttribute("data-main"))return q=J,t.baseUrl||(D=q.split("/"),q=D.pop(),fa=D.length?D.join("/")+"/":"./",t.baseUrl=fa),q=q.replace(ea,""),j.jsExtRegExp.test(q)&&(q=J),t.deps=t.deps?t.deps.concat(q):[q],!0});
|
|
||||||
define=function(b,c,d){var h,j;"string"!==typeof b&&(d=c,c=b,b=null);I(c)||(d=c,c=null);!c&&H(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(h=K))P&&"interactive"===P.readyState||M(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),h=P;h&&(b||(b=h.getAttribute("data-requiremodule")),j=E[h.getAttribute("data-requirecontext")])}(j?j.defQueue:
|
|
||||||
R).push([b,c,d])};define.amd={jQuery:!0};j.exec=function(b){return eval(b)};j(t)}})(this);
|
|
1792
src/lib/rsa.js
1792
src/lib/rsa.js
File diff suppressed because it is too large
Load Diff
339
src/lib/sha1.js
339
src/lib/sha1.js
@ -1,339 +0,0 @@
|
|||||||
/**
|
|
||||||
* Secure Hash Algorithm with 160-bit digest (SHA-1) implementation.
|
|
||||||
*
|
|
||||||
* This implementation is currently limited to message lengths (in bytes) that
|
|
||||||
* are up to 32-bits in size.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2012 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var sha1 = forge.sha1 = forge.sha1 || {};
|
|
||||||
forge.md = forge.md || {};
|
|
||||||
forge.md.algorithms = forge.md.algorithms || {};
|
|
||||||
forge.md.sha1 = forge.md.algorithms['sha1'] = sha1;
|
|
||||||
|
|
||||||
// sha-1 padding bytes not initialized yet
|
|
||||||
var _padding = null;
|
|
||||||
var _initialized = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the constant tables.
|
|
||||||
*/
|
|
||||||
var _init = function() {
|
|
||||||
// create padding
|
|
||||||
_padding = String.fromCharCode(128);
|
|
||||||
_padding += forge.util.fillString(String.fromCharCode(0x00), 64);
|
|
||||||
|
|
||||||
// now initialized
|
|
||||||
_initialized = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a SHA-1 state with the given byte buffer.
|
|
||||||
*
|
|
||||||
* @param s the SHA-1 state to update.
|
|
||||||
* @param w the array to use to store words.
|
|
||||||
* @param bytes the byte buffer to update with.
|
|
||||||
*/
|
|
||||||
var _update = function(s, w, bytes) {
|
|
||||||
// consume 512 bit (64 byte) chunks
|
|
||||||
var t, a, b, c, d, e, f, i;
|
|
||||||
var len = bytes.length();
|
|
||||||
while(len >= 64) {
|
|
||||||
// the w array will be populated with sixteen 32-bit big-endian words
|
|
||||||
// and then extended into 80 32-bit words according to SHA-1 algorithm
|
|
||||||
// and for 32-79 using Max Locktyukhin's optimization
|
|
||||||
|
|
||||||
// initialize hash value for this chunk
|
|
||||||
a = s.h0;
|
|
||||||
b = s.h1;
|
|
||||||
c = s.h2;
|
|
||||||
d = s.h3;
|
|
||||||
e = s.h4;
|
|
||||||
|
|
||||||
// round 1
|
|
||||||
for(i = 0; i < 16; ++i) {
|
|
||||||
t = bytes.getInt32();
|
|
||||||
w[i] = t;
|
|
||||||
f = d ^ (b & (c ^ d));
|
|
||||||
t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = (b << 30) | (b >>> 2);
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
for(; i < 20; ++i) {
|
|
||||||
t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]);
|
|
||||||
t = (t << 1) | (t >>> 31);
|
|
||||||
w[i] = t;
|
|
||||||
f = d ^ (b & (c ^ d));
|
|
||||||
t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = (b << 30) | (b >>> 2);
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
// round 2
|
|
||||||
for(; i < 32; ++i) {
|
|
||||||
t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]);
|
|
||||||
t = (t << 1) | (t >>> 31);
|
|
||||||
w[i] = t;
|
|
||||||
f = b ^ c ^ d;
|
|
||||||
t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = (b << 30) | (b >>> 2);
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
for(; i < 40; ++i) {
|
|
||||||
t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
|
|
||||||
t = (t << 2) | (t >>> 30);
|
|
||||||
w[i] = t;
|
|
||||||
f = b ^ c ^ d;
|
|
||||||
t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = (b << 30) | (b >>> 2);
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
// round 3
|
|
||||||
for(; i < 60; ++i) {
|
|
||||||
t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
|
|
||||||
t = (t << 2) | (t >>> 30);
|
|
||||||
w[i] = t;
|
|
||||||
f = (b & c) | (d & (b ^ c));
|
|
||||||
t = ((a << 5) | (a >>> 27)) + f + e + 0x8F1BBCDC + t;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = (b << 30) | (b >>> 2);
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
// round 4
|
|
||||||
for(; i < 80; ++i) {
|
|
||||||
t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]);
|
|
||||||
t = (t << 2) | (t >>> 30);
|
|
||||||
w[i] = t;
|
|
||||||
f = b ^ c ^ d;
|
|
||||||
t = ((a << 5) | (a >>> 27)) + f + e + 0xCA62C1D6 + t;
|
|
||||||
e = d;
|
|
||||||
d = c;
|
|
||||||
c = (b << 30) | (b >>> 2);
|
|
||||||
b = a;
|
|
||||||
a = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update hash state
|
|
||||||
s.h0 += a;
|
|
||||||
s.h1 += b;
|
|
||||||
s.h2 += c;
|
|
||||||
s.h3 += d;
|
|
||||||
s.h4 += e;
|
|
||||||
|
|
||||||
len -= 64;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a SHA-1 message digest object.
|
|
||||||
*
|
|
||||||
* @return a message digest object.
|
|
||||||
*/
|
|
||||||
sha1.create = function() {
|
|
||||||
// do initialization as necessary
|
|
||||||
if(!_initialized) {
|
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHA-1 state contains five 32-bit integers
|
|
||||||
var _state = null;
|
|
||||||
|
|
||||||
// input buffer
|
|
||||||
var _input = forge.util.createBuffer();
|
|
||||||
|
|
||||||
// used for word storage
|
|
||||||
var _w = new Array(80);
|
|
||||||
|
|
||||||
// message digest object
|
|
||||||
var md = {
|
|
||||||
algorithm: 'sha1',
|
|
||||||
blockLength: 64,
|
|
||||||
digestLength: 20,
|
|
||||||
// length of message so far (does not including padding)
|
|
||||||
messageLength: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the digest.
|
|
||||||
*
|
|
||||||
* @return this digest object.
|
|
||||||
*/
|
|
||||||
md.start = function() {
|
|
||||||
md.messageLength = 0;
|
|
||||||
_input = forge.util.createBuffer();
|
|
||||||
_state = {
|
|
||||||
h0: 0x67452301,
|
|
||||||
h1: 0xEFCDAB89,
|
|
||||||
h2: 0x98BADCFE,
|
|
||||||
h3: 0x10325476,
|
|
||||||
h4: 0xC3D2E1F0
|
|
||||||
};
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
// start digest automatically for first time
|
|
||||||
md.start();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the digest with the given message input. The given input can
|
|
||||||
* treated as raw input (no encoding will be applied) or an encoding of
|
|
||||||
* 'utf8' maybe given to encode the input using UTF-8.
|
|
||||||
*
|
|
||||||
* @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') {
|
|
||||||
msg = forge.util.encodeUtf8(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update message length
|
|
||||||
md.messageLength += msg.length;
|
|
||||||
|
|
||||||
// add bytes to input buffer
|
|
||||||
_input.putBytes(msg);
|
|
||||||
|
|
||||||
// process bytes
|
|
||||||
_update(_state, _w, _input);
|
|
||||||
|
|
||||||
// compact input buffer every 2K or if empty
|
|
||||||
if(_input.read > 2048 || _input.length() === 0) {
|
|
||||||
_input.compact();
|
|
||||||
}
|
|
||||||
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the digest.
|
|
||||||
*
|
|
||||||
* @return a byte buffer containing the digest value.
|
|
||||||
*/
|
|
||||||
md.digest = function() {
|
|
||||||
/* Note: Here we copy the remaining bytes in the input buffer and
|
|
||||||
add the appropriate SHA-1 padding. Then we do the final update
|
|
||||||
on a copy of the state so that if the user wants to get
|
|
||||||
intermediate digests they can do so. */
|
|
||||||
|
|
||||||
/* Determine the number of bytes that must be added to the message
|
|
||||||
to ensure its length is congruent to 448 mod 512. In other words,
|
|
||||||
a 64-bit integer that gives the length of the message will be
|
|
||||||
appended to the message and whatever the length of the message is
|
|
||||||
plus 64 bits must be a multiple of 512. So the length of the
|
|
||||||
message must be congruent to 448 mod 512 because 512 - 64 = 448.
|
|
||||||
|
|
||||||
In order to fill up the message length it must be filled with
|
|
||||||
padding that begins with 1 bit followed by all 0 bits. Padding
|
|
||||||
must *always* be present, so if the message length is already
|
|
||||||
congruent to 448 mod 512, then 512 padding bits must be added. */
|
|
||||||
|
|
||||||
// 512 bits == 64 bytes, 448 bits == 56 bytes, 64 bits = 8 bytes
|
|
||||||
// _padding starts with 1 byte with first bit is set in it which
|
|
||||||
// is byte value 128, then there may be up to 63 other pad bytes
|
|
||||||
var len = md.messageLength;
|
|
||||||
var padBytes = forge.util.createBuffer();
|
|
||||||
padBytes.putBytes(_input.bytes());
|
|
||||||
padBytes.putBytes(_padding.substr(0, 64 - ((len + 8) % 64)));
|
|
||||||
|
|
||||||
/* Now append length of the message. The length is appended in bits
|
|
||||||
as a 64-bit number in big-endian order. Since we store the length
|
|
||||||
in bytes, we must multiply it by 8 (or left shift by 3). So here
|
|
||||||
store the high 3 bits in the low end of the first 32-bits of the
|
|
||||||
64-bit number and the lower 5 bits in the high end of the second
|
|
||||||
32-bits. */
|
|
||||||
padBytes.putInt32((len >>> 29) & 0xFF);
|
|
||||||
padBytes.putInt32((len << 3) & 0xFFFFFFFF);
|
|
||||||
var s2 = {
|
|
||||||
h0: _state.h0,
|
|
||||||
h1: _state.h1,
|
|
||||||
h2: _state.h2,
|
|
||||||
h3: _state.h3,
|
|
||||||
h4: _state.h4
|
|
||||||
};
|
|
||||||
_update(s2, _w, padBytes);
|
|
||||||
var rval = forge.util.createBuffer();
|
|
||||||
rval.putInt32(s2.h0);
|
|
||||||
rval.putInt32(s2.h1);
|
|
||||||
rval.putInt32(s2.h2);
|
|
||||||
rval.putInt32(s2.h3);
|
|
||||||
rval.putInt32(s2.h4);
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'sha1';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,349 +0,0 @@
|
|||||||
/**
|
|
||||||
* Secure Hash Algorithm with 256-bit digest (SHA-256) implementation.
|
|
||||||
*
|
|
||||||
* See FIPS 180-2 for details.
|
|
||||||
*
|
|
||||||
* This implementation is currently limited to message lengths (in bytes) that
|
|
||||||
* are up to 32-bits in size.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2012 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
var sha256 = forge.sha256 = forge.sha256 || {};
|
|
||||||
forge.md = forge.md || {};
|
|
||||||
forge.md.algorithms = forge.md.algorithms || {};
|
|
||||||
forge.md.sha256 = forge.md.algorithms['sha256'] = sha256;
|
|
||||||
|
|
||||||
// sha-256 padding bytes not initialized yet
|
|
||||||
var _padding = null;
|
|
||||||
var _initialized = false;
|
|
||||||
|
|
||||||
// table of constants
|
|
||||||
var _k = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the constant tables.
|
|
||||||
*/
|
|
||||||
var _init = function() {
|
|
||||||
// create padding
|
|
||||||
_padding = String.fromCharCode(128);
|
|
||||||
_padding += forge.util.fillString(String.fromCharCode(0x00), 64);
|
|
||||||
|
|
||||||
// create K table for SHA-256
|
|
||||||
_k = [
|
|
||||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
|
||||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
||||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
||||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
||||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
|
||||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
||||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
|
||||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
||||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
||||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
||||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
|
||||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
||||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
|
||||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
||||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
||||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
|
|
||||||
|
|
||||||
// now initialized
|
|
||||||
_initialized = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a SHA-256 state with the given byte buffer.
|
|
||||||
*
|
|
||||||
* @param s the SHA-256 state to update.
|
|
||||||
* @param w the array to use to store words.
|
|
||||||
* @param bytes the byte buffer to update with.
|
|
||||||
*/
|
|
||||||
var _update = function(s, w, bytes) {
|
|
||||||
// consume 512 bit (64 byte) chunks
|
|
||||||
var t1, t2, s0, s1, ch, maj, i, a, b, c, d, e, f, g, h;
|
|
||||||
var len = bytes.length();
|
|
||||||
while(len >= 64) {
|
|
||||||
// the w array will be populated with sixteen 32-bit big-endian words
|
|
||||||
// and then extended into 64 32-bit words according to SHA-256
|
|
||||||
for(i = 0; i < 16; ++i) {
|
|
||||||
w[i] = bytes.getInt32();
|
|
||||||
}
|
|
||||||
for(; i < 64; ++i) {
|
|
||||||
// XOR word 2 words ago rot right 17, rot right 19, shft right 10
|
|
||||||
t1 = w[i - 2];
|
|
||||||
t1 =
|
|
||||||
((t1 >>> 17) | (t1 << 15)) ^
|
|
||||||
((t1 >>> 19) | (t1 << 13)) ^
|
|
||||||
(t1 >>> 10);
|
|
||||||
// XOR word 15 words ago rot right 7, rot right 18, shft right 3
|
|
||||||
t2 = w[i - 15];
|
|
||||||
t2 =
|
|
||||||
((t2 >>> 7) | (t2 << 25)) ^
|
|
||||||
((t2 >>> 18) | (t2 << 14)) ^
|
|
||||||
(t2 >>> 3);
|
|
||||||
// sum(t1, word 7 ago, t2, word 16 ago) modulo 2^32
|
|
||||||
w[i] = (t1 + w[i - 7] + t2 + w[i - 16]) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize hash value for this chunk
|
|
||||||
a = s.h0;
|
|
||||||
b = s.h1;
|
|
||||||
c = s.h2;
|
|
||||||
d = s.h3;
|
|
||||||
e = s.h4;
|
|
||||||
f = s.h5;
|
|
||||||
g = s.h6;
|
|
||||||
h = s.h7;
|
|
||||||
|
|
||||||
// round function
|
|
||||||
for(i = 0; i < 64; ++i) {
|
|
||||||
// Sum1(e)
|
|
||||||
s1 =
|
|
||||||
((e >>> 6) | (e << 26)) ^
|
|
||||||
((e >>> 11) | (e << 21)) ^
|
|
||||||
((e >>> 25) | (e << 7));
|
|
||||||
// Ch(e, f, g) (optimized the same way as SHA-1)
|
|
||||||
ch = g ^ (e & (f ^ g));
|
|
||||||
// Sum0(a)
|
|
||||||
s0 =
|
|
||||||
((a >>> 2) | (a << 30)) ^
|
|
||||||
((a >>> 13) | (a << 19)) ^
|
|
||||||
((a >>> 22) | (a << 10));
|
|
||||||
// Maj(a, b, c) (optimized the same way as SHA-1)
|
|
||||||
maj = (a & b) | (c & (a ^ b));
|
|
||||||
|
|
||||||
// main algorithm
|
|
||||||
t1 = h + s1 + ch + _k[i] + w[i];
|
|
||||||
t2 = s0 + maj;
|
|
||||||
h = g;
|
|
||||||
g = f;
|
|
||||||
f = e;
|
|
||||||
e = (d + t1) & 0xFFFFFFFF;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b = a;
|
|
||||||
a = (t1 + t2) & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update hash state
|
|
||||||
s.h0 = (s.h0 + a) & 0xFFFFFFFF;
|
|
||||||
s.h1 = (s.h1 + b) & 0xFFFFFFFF;
|
|
||||||
s.h2 = (s.h2 + c) & 0xFFFFFFFF;
|
|
||||||
s.h3 = (s.h3 + d) & 0xFFFFFFFF;
|
|
||||||
s.h4 = (s.h4 + e) & 0xFFFFFFFF;
|
|
||||||
s.h5 = (s.h5 + f) & 0xFFFFFFFF;
|
|
||||||
s.h6 = (s.h6 + g) & 0xFFFFFFFF;
|
|
||||||
s.h7 = (s.h7 + h) & 0xFFFFFFFF;
|
|
||||||
len -= 64;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a SHA-256 message digest object.
|
|
||||||
*
|
|
||||||
* @return a message digest object.
|
|
||||||
*/
|
|
||||||
sha256.create = function() {
|
|
||||||
// do initialization as necessary
|
|
||||||
if(!_initialized) {
|
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHA-256 state contains eight 32-bit integers
|
|
||||||
var _state = null;
|
|
||||||
|
|
||||||
// input buffer
|
|
||||||
var _input = forge.util.createBuffer();
|
|
||||||
|
|
||||||
// used for word storage
|
|
||||||
var _w = new Array(64);
|
|
||||||
|
|
||||||
// message digest object
|
|
||||||
var md = {
|
|
||||||
algorithm: 'sha256',
|
|
||||||
blockLength: 64,
|
|
||||||
digestLength: 32,
|
|
||||||
// length of message so far (does not including padding)
|
|
||||||
messageLength: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the digest.
|
|
||||||
*
|
|
||||||
* @return this digest object.
|
|
||||||
*/
|
|
||||||
md.start = function() {
|
|
||||||
md.messageLength = 0;
|
|
||||||
_input = forge.util.createBuffer();
|
|
||||||
_state = {
|
|
||||||
h0: 0x6A09E667,
|
|
||||||
h1: 0xBB67AE85,
|
|
||||||
h2: 0x3C6EF372,
|
|
||||||
h3: 0xA54FF53A,
|
|
||||||
h4: 0x510E527F,
|
|
||||||
h5: 0x9B05688C,
|
|
||||||
h6: 0x1F83D9AB,
|
|
||||||
h7: 0x5BE0CD19
|
|
||||||
};
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
// start digest automatically for first time
|
|
||||||
md.start();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the digest with the given message input. The given input can
|
|
||||||
* treated as raw input (no encoding will be applied) or an encoding of
|
|
||||||
* 'utf8' maybe given to encode the input using UTF-8.
|
|
||||||
*
|
|
||||||
* @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') {
|
|
||||||
msg = forge.util.encodeUtf8(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update message length
|
|
||||||
md.messageLength += msg.length;
|
|
||||||
|
|
||||||
// add bytes to input buffer
|
|
||||||
_input.putBytes(msg);
|
|
||||||
|
|
||||||
// process bytes
|
|
||||||
_update(_state, _w, _input);
|
|
||||||
|
|
||||||
// compact input buffer every 2K or if empty
|
|
||||||
if(_input.read > 2048 || _input.length() === 0) {
|
|
||||||
_input.compact();
|
|
||||||
}
|
|
||||||
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the digest.
|
|
||||||
*
|
|
||||||
* @return a byte buffer containing the digest value.
|
|
||||||
*/
|
|
||||||
md.digest = function() {
|
|
||||||
/* Note: Here we copy the remaining bytes in the input buffer and
|
|
||||||
add the appropriate SHA-256 padding. Then we do the final update
|
|
||||||
on a copy of the state so that if the user wants to get
|
|
||||||
intermediate digests they can do so. */
|
|
||||||
|
|
||||||
/* Determine the number of bytes that must be added to the message
|
|
||||||
to ensure its length is congruent to 448 mod 512. In other words,
|
|
||||||
a 64-bit integer that gives the length of the message will be
|
|
||||||
appended to the message and whatever the length of the message is
|
|
||||||
plus 64 bits must be a multiple of 512. So the length of the
|
|
||||||
message must be congruent to 448 mod 512 because 512 - 64 = 448.
|
|
||||||
|
|
||||||
In order to fill up the message length it must be filled with
|
|
||||||
padding that begins with 1 bit followed by all 0 bits. Padding
|
|
||||||
must *always* be present, so if the message length is already
|
|
||||||
congruent to 448 mod 512, then 512 padding bits must be added. */
|
|
||||||
|
|
||||||
// 512 bits == 64 bytes, 448 bits == 56 bytes, 64 bits = 8 bytes
|
|
||||||
// _padding starts with 1 byte with first bit is set in it which
|
|
||||||
// is byte value 128, then there may be up to 63 other pad bytes
|
|
||||||
var len = md.messageLength;
|
|
||||||
var padBytes = forge.util.createBuffer();
|
|
||||||
padBytes.putBytes(_input.bytes());
|
|
||||||
padBytes.putBytes(_padding.substr(0, 64 - ((len + 8) % 64)));
|
|
||||||
|
|
||||||
/* Now append length of the message. The length is appended in bits
|
|
||||||
as a 64-bit number in big-endian order. Since we store the length
|
|
||||||
in bytes, we must multiply it by 8 (or left shift by 3). So here
|
|
||||||
store the high 3 bits in the low end of the first 32-bits of the
|
|
||||||
64-bit number and the lower 5 bits in the high end of the second
|
|
||||||
32-bits. */
|
|
||||||
padBytes.putInt32((len >>> 29) & 0xFF);
|
|
||||||
padBytes.putInt32((len << 3) & 0xFFFFFFFF);
|
|
||||||
var s2 = {
|
|
||||||
h0: _state.h0,
|
|
||||||
h1: _state.h1,
|
|
||||||
h2: _state.h2,
|
|
||||||
h3: _state.h3,
|
|
||||||
h4: _state.h4,
|
|
||||||
h5: _state.h5,
|
|
||||||
h6: _state.h6,
|
|
||||||
h7: _state.h7
|
|
||||||
};
|
|
||||||
_update(s2, _w, padBytes);
|
|
||||||
var rval = forge.util.createBuffer();
|
|
||||||
rval.putInt32(s2.h0);
|
|
||||||
rval.putInt32(s2.h1);
|
|
||||||
rval.putInt32(s2.h2);
|
|
||||||
rval.putInt32(s2.h3);
|
|
||||||
rval.putInt32(s2.h4);
|
|
||||||
rval.putInt32(s2.h5);
|
|
||||||
rval.putInt32(s2.h6);
|
|
||||||
rval.putInt32(s2.h7);
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
return md;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'sha256';
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,344 +0,0 @@
|
|||||||
/**
|
|
||||||
* Socket implementation that uses flash SocketPool class as a backend.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// define net namespace
|
|
||||||
var net = forge.net = forge.net || {};
|
|
||||||
|
|
||||||
// map of flash ID to socket pool
|
|
||||||
net.socketPools = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a flash socket pool.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* flashId: the dom ID for the flash object element.
|
|
||||||
* policyPort: the default policy port for sockets, 0 to use the
|
|
||||||
* flash default.
|
|
||||||
* policyUrl: the default policy file URL for sockets (if provided
|
|
||||||
* used instead of a policy port).
|
|
||||||
* msie: true if the browser is msie, false if not.
|
|
||||||
*
|
|
||||||
* @return the created socket pool.
|
|
||||||
*/
|
|
||||||
net.createSocketPool = function(options) {
|
|
||||||
// set default
|
|
||||||
options.msie = options.msie || false;
|
|
||||||
|
|
||||||
// initialize the flash interface
|
|
||||||
var spId = options.flashId;
|
|
||||||
var api = document.getElementById(spId);
|
|
||||||
api.init({marshallExceptions: !options.msie});
|
|
||||||
|
|
||||||
// create socket pool entry
|
|
||||||
var sp = {
|
|
||||||
// ID of the socket pool
|
|
||||||
id: spId,
|
|
||||||
// flash interface
|
|
||||||
flashApi: api,
|
|
||||||
// map of socket ID to sockets
|
|
||||||
sockets: {},
|
|
||||||
// default policy port
|
|
||||||
policyPort: options.policyPort || 0,
|
|
||||||
// default policy URL
|
|
||||||
policyUrl: options.policyUrl || null
|
|
||||||
};
|
|
||||||
net.socketPools[spId] = sp;
|
|
||||||
|
|
||||||
// create event handler, subscribe to flash events
|
|
||||||
if(options.msie === true) {
|
|
||||||
sp.handler = function(e) {
|
|
||||||
if(e.id in sp.sockets) {
|
|
||||||
// get handler function
|
|
||||||
var f;
|
|
||||||
switch(e.type) {
|
|
||||||
case 'connect':
|
|
||||||
f = 'connected';
|
|
||||||
break;
|
|
||||||
case 'close':
|
|
||||||
f = 'closed';
|
|
||||||
break;
|
|
||||||
case 'socketData':
|
|
||||||
f = 'data';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
f = 'error';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* IE calls javascript on the thread of the external object
|
|
||||||
that triggered the event (in this case flash) ... which will
|
|
||||||
either run concurrently with other javascript or pre-empt any
|
|
||||||
running javascript in the middle of its execution (BAD!) ...
|
|
||||||
calling setTimeout() will schedule the javascript to run on
|
|
||||||
the javascript thread and solve this EVIL problem. */
|
|
||||||
setTimeout(function(){sp.sockets[e.id][f](e);}, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sp.handler = function(e) {
|
|
||||||
if(e.id in sp.sockets) {
|
|
||||||
// get handler function
|
|
||||||
var f;
|
|
||||||
switch(e.type) {
|
|
||||||
case 'connect':
|
|
||||||
f = 'connected';
|
|
||||||
break;
|
|
||||||
case 'close':
|
|
||||||
f = 'closed';
|
|
||||||
break;
|
|
||||||
case 'socketData':
|
|
||||||
f = 'data';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
f = 'error';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sp.sockets[e.id][f](e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var handler = 'forge.net.socketPools[\'' + spId + '\'].handler';
|
|
||||||
api.subscribe('connect', handler);
|
|
||||||
api.subscribe('close', handler);
|
|
||||||
api.subscribe('socketData', handler);
|
|
||||||
api.subscribe('ioError', handler);
|
|
||||||
api.subscribe('securityError', handler);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys a socket pool. The socket pool still needs to be cleaned
|
|
||||||
* up via net.cleanup().
|
|
||||||
*/
|
|
||||||
sp.destroy = function() {
|
|
||||||
delete net.socketPools[options.flashId];
|
|
||||||
for(var id in sp.sockets) {
|
|
||||||
sp.sockets[id].destroy();
|
|
||||||
}
|
|
||||||
sp.sockets = {};
|
|
||||||
api.cleanup();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new socket.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* connected: function(event) called when the socket connects.
|
|
||||||
* closed: function(event) called when the socket closes.
|
|
||||||
* data: function(event) called when socket data has arrived,
|
|
||||||
* it can be read from the socket using receive().
|
|
||||||
* error: function(event) called when a socket error occurs.
|
|
||||||
*/
|
|
||||||
sp.createSocket = function(options) {
|
|
||||||
// default to empty options
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
// create flash socket
|
|
||||||
var id = api.create();
|
|
||||||
|
|
||||||
// create javascript socket wrapper
|
|
||||||
var socket = {
|
|
||||||
id: id,
|
|
||||||
// set handlers
|
|
||||||
connected: options.connected || function(e){},
|
|
||||||
closed: options.closed || function(e){},
|
|
||||||
data: options.data || function(e){},
|
|
||||||
error: options.error || function(e){}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys this socket.
|
|
||||||
*/
|
|
||||||
socket.destroy = function() {
|
|
||||||
api.destroy(id);
|
|
||||||
delete sp.sockets[id];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects this socket.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* host: the host to connect to.
|
|
||||||
* port: the port to connect to.
|
|
||||||
* policyPort: the policy port to use (if non-default), 0 to
|
|
||||||
* use the flash default.
|
|
||||||
* policyUrl: the policy file URL to use (instead of port).
|
|
||||||
*/
|
|
||||||
socket.connect = function(options) {
|
|
||||||
// give precedence to policy URL over policy port
|
|
||||||
// if no policy URL and passed port isn't 0, use default port,
|
|
||||||
// otherwise use 0 for the port
|
|
||||||
var policyUrl = options.policyUrl || null;
|
|
||||||
var policyPort = 0;
|
|
||||||
if(policyUrl === null && options.policyPort !== 0) {
|
|
||||||
policyPort = options.policyPort || sp.policyPort;
|
|
||||||
}
|
|
||||||
api.connect(id, options.host, options.port, policyPort, policyUrl);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this socket.
|
|
||||||
*/
|
|
||||||
socket.close = function() {
|
|
||||||
api.close(id);
|
|
||||||
socket.closed({
|
|
||||||
id: socket.id,
|
|
||||||
type: 'close',
|
|
||||||
bytesAvailable: 0
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the socket is connected or not.
|
|
||||||
*
|
|
||||||
* @return true if connected, false if not.
|
|
||||||
*/
|
|
||||||
socket.isConnected = function() {
|
|
||||||
return api.isConnected(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes bytes to this socket.
|
|
||||||
*
|
|
||||||
* @param bytes the bytes (as a string) to write.
|
|
||||||
*
|
|
||||||
* @return true on success, false on failure.
|
|
||||||
*/
|
|
||||||
socket.send = function(bytes) {
|
|
||||||
return api.send(id, forge.util.encode64(bytes));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads bytes from this socket (non-blocking). Fewer than the number
|
|
||||||
* of bytes requested may be read if enough bytes are not available.
|
|
||||||
*
|
|
||||||
* This method should be called from the data handler if there are
|
|
||||||
* enough bytes available. To see how many bytes are available, check
|
|
||||||
* the 'bytesAvailable' property on the event in the data handler or
|
|
||||||
* call the bytesAvailable() function on the socket. If the browser is
|
|
||||||
* msie, then the bytesAvailable() function should be used to avoid
|
|
||||||
* race conditions. Otherwise, using the property on the data handler's
|
|
||||||
* event may be quicker.
|
|
||||||
*
|
|
||||||
* @param count the maximum number of bytes to read.
|
|
||||||
*
|
|
||||||
* @return the bytes read (as a string) or null on error.
|
|
||||||
*/
|
|
||||||
socket.receive = function(count) {
|
|
||||||
var rval = api.receive(id, count).rval;
|
|
||||||
return (rval === null) ? null : forge.util.decode64(rval);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of bytes available for receiving on the socket.
|
|
||||||
*
|
|
||||||
* @return the number of bytes available for receiving.
|
|
||||||
*/
|
|
||||||
socket.bytesAvailable = function() {
|
|
||||||
return api.getBytesAvailable(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
// store and return socket
|
|
||||||
sp.sockets[id] = socket;
|
|
||||||
return socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
return sp;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys a flash socket pool.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* flashId: the dom ID for the flash object element.
|
|
||||||
*/
|
|
||||||
net.destroySocketPool = function(options) {
|
|
||||||
if(options.flashId in net.socketPools) {
|
|
||||||
var sp = net.socketPools[options.flashId];
|
|
||||||
sp.destroy();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new socket.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* flashId: the dom ID for the flash object element.
|
|
||||||
* connected: function(event) called when the socket connects.
|
|
||||||
* closed: function(event) called when the socket closes.
|
|
||||||
* data: function(event) called when socket data has arrived, it
|
|
||||||
* can be read from the socket using receive().
|
|
||||||
* error: function(event) called when a socket error occurs.
|
|
||||||
*
|
|
||||||
* @return the created socket.
|
|
||||||
*/
|
|
||||||
net.createSocket = function(options) {
|
|
||||||
var socket = null;
|
|
||||||
if(options.flashId in net.socketPools) {
|
|
||||||
// get related socket pool
|
|
||||||
var sp = net.socketPools[options.flashId];
|
|
||||||
socket = sp.createSocket(options);
|
|
||||||
}
|
|
||||||
return socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'net';
|
|
||||||
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'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
790
src/lib/task.js
790
src/lib/task.js
@ -1,790 +0,0 @@
|
|||||||
/**
|
|
||||||
* Support for concurrent task management and synchronization in web
|
|
||||||
* applications.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
* @author David I. Lehn <dlehn@digitalbazaar.com>
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
// logging category
|
|
||||||
var cat = 'forge.task';
|
|
||||||
|
|
||||||
// verbose level
|
|
||||||
// 0: off, 1: a little, 2: a whole lot
|
|
||||||
// Verbose debug logging is surrounded by a level check to avoid the
|
|
||||||
// performance issues with even calling the logging code regardless if it
|
|
||||||
// is actually logged. For performance reasons this should not be set to 2
|
|
||||||
// for production use.
|
|
||||||
// ex: if(sVL >= 2) forge.log.verbose(....)
|
|
||||||
var sVL = 0;
|
|
||||||
|
|
||||||
// track tasks for debugging
|
|
||||||
var sTasks = {};
|
|
||||||
var sNextTaskId = 0;
|
|
||||||
// debug access
|
|
||||||
forge.debug.set(cat, 'tasks', sTasks);
|
|
||||||
|
|
||||||
// a map of task type to task queue
|
|
||||||
var sTaskQueues = {};
|
|
||||||
// debug access
|
|
||||||
forge.debug.set(cat, 'queues', sTaskQueues);
|
|
||||||
|
|
||||||
// name for unnamed tasks
|
|
||||||
var sNoTaskName = '?';
|
|
||||||
|
|
||||||
// maximum number of doNext() recursions before a context swap occurs
|
|
||||||
// FIXME: might need to tweak this based on the browser
|
|
||||||
var sMaxRecursions = 30;
|
|
||||||
|
|
||||||
// time slice for doing tasks before a context swap occurs
|
|
||||||
// FIXME: might need to tweak this based on the browser
|
|
||||||
var sTimeSlice = 20;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Task states.
|
|
||||||
*
|
|
||||||
* READY: ready to start processing
|
|
||||||
* RUNNING: task or a subtask is running
|
|
||||||
* BLOCKED: task is waiting to acquire N permits to continue
|
|
||||||
* SLEEPING: task is sleeping for a period of time
|
|
||||||
* DONE: task is done
|
|
||||||
* ERROR: task has an error
|
|
||||||
*/
|
|
||||||
var READY = 'ready';
|
|
||||||
var RUNNING = 'running';
|
|
||||||
var BLOCKED = 'blocked';
|
|
||||||
var SLEEPING = 'sleeping';
|
|
||||||
var DONE = 'done';
|
|
||||||
var ERROR = 'error';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Task actions. Used to control state transitions.
|
|
||||||
*
|
|
||||||
* STOP: stop processing
|
|
||||||
* START: start processing tasks
|
|
||||||
* BLOCK: block task from continuing until 1 or more permits are released
|
|
||||||
* UNBLOCK: release one or more permits
|
|
||||||
* SLEEP: sleep for a period of time
|
|
||||||
* WAKEUP: wakeup early from SLEEPING state
|
|
||||||
* CANCEL: cancel further tasks
|
|
||||||
* FAIL: a failure occured
|
|
||||||
*/
|
|
||||||
var STOP = 'stop';
|
|
||||||
var START = 'start';
|
|
||||||
var BLOCK = 'block';
|
|
||||||
var UNBLOCK = 'unblock';
|
|
||||||
var SLEEP = 'sleep';
|
|
||||||
var WAKEUP = 'wakeup';
|
|
||||||
var CANCEL = 'cancel';
|
|
||||||
var FAIL = 'fail';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* State transition table.
|
|
||||||
*
|
|
||||||
* nextState = sStateTable[currentState][action]
|
|
||||||
*/
|
|
||||||
var sStateTable = {};
|
|
||||||
|
|
||||||
sStateTable[READY] = {};
|
|
||||||
sStateTable[READY][STOP] = READY;
|
|
||||||
sStateTable[READY][START] = RUNNING;
|
|
||||||
sStateTable[READY][CANCEL] = DONE;
|
|
||||||
sStateTable[READY][FAIL] = ERROR;
|
|
||||||
|
|
||||||
sStateTable[RUNNING] = {};
|
|
||||||
sStateTable[RUNNING][STOP] = READY;
|
|
||||||
sStateTable[RUNNING][START] = RUNNING;
|
|
||||||
sStateTable[RUNNING][BLOCK] = BLOCKED;
|
|
||||||
sStateTable[RUNNING][UNBLOCK] = RUNNING;
|
|
||||||
sStateTable[RUNNING][SLEEP] = SLEEPING;
|
|
||||||
sStateTable[RUNNING][WAKEUP] = RUNNING;
|
|
||||||
sStateTable[RUNNING][CANCEL] = DONE;
|
|
||||||
sStateTable[RUNNING][FAIL] = ERROR;
|
|
||||||
|
|
||||||
sStateTable[BLOCKED] = {};
|
|
||||||
sStateTable[BLOCKED][STOP] = BLOCKED;
|
|
||||||
sStateTable[BLOCKED][START] = BLOCKED;
|
|
||||||
sStateTable[BLOCKED][BLOCK] = BLOCKED;
|
|
||||||
sStateTable[BLOCKED][UNBLOCK] = BLOCKED;
|
|
||||||
sStateTable[BLOCKED][SLEEP] = BLOCKED;
|
|
||||||
sStateTable[BLOCKED][WAKEUP] = BLOCKED;
|
|
||||||
sStateTable[BLOCKED][CANCEL] = DONE;
|
|
||||||
sStateTable[BLOCKED][FAIL] = ERROR;
|
|
||||||
|
|
||||||
sStateTable[SLEEPING] = {};
|
|
||||||
sStateTable[SLEEPING][STOP] = SLEEPING;
|
|
||||||
sStateTable[SLEEPING][START] = SLEEPING;
|
|
||||||
sStateTable[SLEEPING][BLOCK] = SLEEPING;
|
|
||||||
sStateTable[SLEEPING][UNBLOCK] = SLEEPING;
|
|
||||||
sStateTable[SLEEPING][SLEEP] = SLEEPING;
|
|
||||||
sStateTable[SLEEPING][WAKEUP] = SLEEPING;
|
|
||||||
sStateTable[SLEEPING][CANCEL] = DONE;
|
|
||||||
sStateTable[SLEEPING][FAIL] = ERROR;
|
|
||||||
|
|
||||||
sStateTable[DONE] = {};
|
|
||||||
sStateTable[DONE][STOP] = DONE;
|
|
||||||
sStateTable[DONE][START] = DONE;
|
|
||||||
sStateTable[DONE][BLOCK] = DONE;
|
|
||||||
sStateTable[DONE][UNBLOCK] = DONE;
|
|
||||||
sStateTable[DONE][SLEEP] = DONE;
|
|
||||||
sStateTable[DONE][WAKEUP] = DONE;
|
|
||||||
sStateTable[DONE][CANCEL] = DONE;
|
|
||||||
sStateTable[DONE][FAIL] = ERROR;
|
|
||||||
|
|
||||||
sStateTable[ERROR] = {};
|
|
||||||
sStateTable[ERROR][STOP] = ERROR;
|
|
||||||
sStateTable[ERROR][START] = ERROR;
|
|
||||||
sStateTable[ERROR][BLOCK] = ERROR;
|
|
||||||
sStateTable[ERROR][UNBLOCK] = ERROR;
|
|
||||||
sStateTable[ERROR][SLEEP] = ERROR;
|
|
||||||
sStateTable[ERROR][WAKEUP] = ERROR;
|
|
||||||
sStateTable[ERROR][CANCEL] = ERROR;
|
|
||||||
sStateTable[ERROR][FAIL] = ERROR;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new task.
|
|
||||||
*
|
|
||||||
* @param options options for this task
|
|
||||||
* run: the run function for the task (required)
|
|
||||||
* name: the run function for the task (optional)
|
|
||||||
* parent: parent of this task (optional)
|
|
||||||
*
|
|
||||||
* @return the empty task.
|
|
||||||
*/
|
|
||||||
var Task = function(options) {
|
|
||||||
// task id
|
|
||||||
this.id = -1;
|
|
||||||
|
|
||||||
// task name
|
|
||||||
this.name = options.name || sNoTaskName;
|
|
||||||
|
|
||||||
// task has no parent
|
|
||||||
this.parent = options.parent || null;
|
|
||||||
|
|
||||||
// save run function
|
|
||||||
this.run = options.run;
|
|
||||||
|
|
||||||
// create a queue of subtasks to run
|
|
||||||
this.subtasks = [];
|
|
||||||
|
|
||||||
// error flag
|
|
||||||
this.error = false;
|
|
||||||
|
|
||||||
// state of the task
|
|
||||||
this.state = READY;
|
|
||||||
|
|
||||||
// number of times the task has been blocked (also the number
|
|
||||||
// of permits needed to be released to continue running)
|
|
||||||
this.blocks = 0;
|
|
||||||
|
|
||||||
// timeout id when sleeping
|
|
||||||
this.timeoutId = null;
|
|
||||||
|
|
||||||
// no swap time yet
|
|
||||||
this.swapTime = null;
|
|
||||||
|
|
||||||
// no user data
|
|
||||||
this.userData = null;
|
|
||||||
|
|
||||||
// initialize task
|
|
||||||
// FIXME: deal with overflow
|
|
||||||
this.id = sNextTaskId++;
|
|
||||||
sTasks[this.id] = this;
|
|
||||||
if(sVL >= 1) {
|
|
||||||
forge.log.verbose(cat, '[%s][%s] init', this.id, this.name, this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs debug information on this task and the system state.
|
|
||||||
*/
|
|
||||||
Task.prototype.debug = function(msg) {
|
|
||||||
msg = msg || '';
|
|
||||||
forge.log.debug(cat, msg,
|
|
||||||
'[%s][%s] task:', this.id, this.name, this,
|
|
||||||
'subtasks:', this.subtasks.length,
|
|
||||||
'queue:', sTaskQueues);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a subtask to run after task.doNext() or task.fail() is called.
|
|
||||||
*
|
|
||||||
* @param name human readable name for this task (optional).
|
|
||||||
* @param subrun a function to run that takes the current task as
|
|
||||||
* its first parameter.
|
|
||||||
*
|
|
||||||
* @return the current task (useful for chaining next() calls).
|
|
||||||
*/
|
|
||||||
Task.prototype.next = function(name, subrun) {
|
|
||||||
// juggle parameters if it looks like no name is given
|
|
||||||
if(typeof(name) === 'function') {
|
|
||||||
subrun = name;
|
|
||||||
|
|
||||||
// inherit parent's name
|
|
||||||
name = this.name;
|
|
||||||
}
|
|
||||||
// create subtask, set parent to this task, propagate callbacks
|
|
||||||
var subtask = new Task({
|
|
||||||
run: subrun,
|
|
||||||
name: name,
|
|
||||||
parent: this
|
|
||||||
});
|
|
||||||
// start subtasks running
|
|
||||||
subtask.state = RUNNING;
|
|
||||||
subtask.type = this.type;
|
|
||||||
subtask.successCallback = this.successCallback || null;
|
|
||||||
subtask.failureCallback = this.failureCallback || null;
|
|
||||||
|
|
||||||
// queue a new subtask
|
|
||||||
this.subtasks.push(subtask);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds subtasks to run in parallel after task.doNext() or task.fail()
|
|
||||||
* is called.
|
|
||||||
*
|
|
||||||
* @param name human readable name for this task (optional).
|
|
||||||
* @param subrun functions to run that take the current task as
|
|
||||||
* their first parameter.
|
|
||||||
*
|
|
||||||
* @return the current task (useful for chaining next() calls).
|
|
||||||
*/
|
|
||||||
Task.prototype.parallel = function(name, subrun) {
|
|
||||||
// juggle parameters if it looks like no name is given
|
|
||||||
if(forge.util.isArray(name)) {
|
|
||||||
subrun = name;
|
|
||||||
|
|
||||||
// inherit parent's name
|
|
||||||
name = this.name;
|
|
||||||
}
|
|
||||||
// Wrap parallel tasks in a regular task so they are started at the
|
|
||||||
// proper time.
|
|
||||||
return this.next(name, function(task) {
|
|
||||||
// block waiting for subtasks
|
|
||||||
var ptask = task;
|
|
||||||
ptask.block(subrun.length);
|
|
||||||
|
|
||||||
// we pass the iterator from the loop below as a parameter
|
|
||||||
// to a function because it is otherwise included in the
|
|
||||||
// closure and changes as the loop changes -- causing i
|
|
||||||
// to always be set to its highest value
|
|
||||||
var startParallelTask = function(pname, pi) {
|
|
||||||
forge.task.start({
|
|
||||||
type: pname,
|
|
||||||
run: function(task) {
|
|
||||||
subrun[pi](task);
|
|
||||||
},
|
|
||||||
success: function(task) {
|
|
||||||
ptask.unblock();
|
|
||||||
},
|
|
||||||
failure: function(task) {
|
|
||||||
ptask.unblock();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
for(var i = 0; i < subrun.length; i++) {
|
|
||||||
// Type must be unique so task starts in parallel:
|
|
||||||
// name + private string + task id + sub-task index
|
|
||||||
// start tasks in parallel and unblock when the finish
|
|
||||||
var pname = name + '__parallel-' + task.id + '-' + i;
|
|
||||||
var pi = i;
|
|
||||||
startParallelTask(pname, pi);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops a running task.
|
|
||||||
*/
|
|
||||||
Task.prototype.stop = function() {
|
|
||||||
this.state = sStateTable[this.state][STOP];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts running a task.
|
|
||||||
*/
|
|
||||||
Task.prototype.start = function() {
|
|
||||||
this.error = false;
|
|
||||||
this.state = sStateTable[this.state][START];
|
|
||||||
|
|
||||||
// try to restart
|
|
||||||
if(this.state === RUNNING) {
|
|
||||||
this.start = new Date();
|
|
||||||
this.run(this);
|
|
||||||
runNext(this, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Blocks a task until it one or more permits have been released. The
|
|
||||||
* task will not resume until the requested number of permits have
|
|
||||||
* been released with call(s) to unblock().
|
|
||||||
*
|
|
||||||
* @param n number of permits to wait for(default: 1).
|
|
||||||
*/
|
|
||||||
Task.prototype.block = function(n) {
|
|
||||||
n = typeof(n) === 'undefined' ? 1 : n;
|
|
||||||
this.blocks += n;
|
|
||||||
if(this.blocks > 0) {
|
|
||||||
this.state = sStateTable[this.state][BLOCK];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases a permit to unblock a task. If a task was blocked by
|
|
||||||
* requesting N permits via block(), then it will only continue
|
|
||||||
* running once enough permits have been released via unblock() calls.
|
|
||||||
*
|
|
||||||
* If multiple processes need to synchronize with a single task then
|
|
||||||
* use a condition variable (see forge.task.createCondition). It is
|
|
||||||
* an error to unblock a task more times than it has been blocked.
|
|
||||||
*
|
|
||||||
* @param n number of permits to release (default: 1).
|
|
||||||
*
|
|
||||||
* @return the current block count (task is unblocked when count is 0)
|
|
||||||
*/
|
|
||||||
Task.prototype.unblock = function(n) {
|
|
||||||
n = typeof(n) === 'undefined' ? 1 : n;
|
|
||||||
this.blocks -= n;
|
|
||||||
if(this.blocks === 0 && this.state !== DONE) {
|
|
||||||
this.state = RUNNING;
|
|
||||||
runNext(this, 0);
|
|
||||||
}
|
|
||||||
return this.blocks;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sleep for a period of time before resuming tasks.
|
|
||||||
*
|
|
||||||
* @param n number of milliseconds to sleep (default: 0).
|
|
||||||
*/
|
|
||||||
Task.prototype.sleep = function(n) {
|
|
||||||
n = typeof(n) === 'undefined' ? 0 : n;
|
|
||||||
this.state = sStateTable[this.state][SLEEP];
|
|
||||||
var task = this;
|
|
||||||
this.timeoutId = setTimeout(function() {
|
|
||||||
task.timeoutId = null;
|
|
||||||
task.state = RUNNING;
|
|
||||||
runNext(task, 0);
|
|
||||||
}, n);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits on a condition variable until notified. The next task will
|
|
||||||
* not be scheduled until notification. A condition variable can be
|
|
||||||
* created with forge.task.createCondition().
|
|
||||||
*
|
|
||||||
* Once cond.notify() is called, the task will continue.
|
|
||||||
*
|
|
||||||
* @param cond the condition variable to wait on.
|
|
||||||
*/
|
|
||||||
Task.prototype.wait = function(cond) {
|
|
||||||
cond.wait(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If sleeping, wakeup and continue running tasks.
|
|
||||||
*/
|
|
||||||
Task.prototype.wakeup = function() {
|
|
||||||
if(this.state === SLEEPING) {
|
|
||||||
cancelTimeout(this.timeoutId);
|
|
||||||
this.timeoutId = null;
|
|
||||||
this.state = RUNNING;
|
|
||||||
runNext(this, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel all remaining subtasks of this task.
|
|
||||||
*/
|
|
||||||
Task.prototype.cancel = function() {
|
|
||||||
this.state = sStateTable[this.state][CANCEL];
|
|
||||||
// remove permits needed
|
|
||||||
this.permitsNeeded = 0;
|
|
||||||
// cancel timeouts
|
|
||||||
if(this.timeoutId !== null) {
|
|
||||||
cancelTimeout(this.timeoutId);
|
|
||||||
this.timeoutId = null;
|
|
||||||
}
|
|
||||||
// remove subtasks
|
|
||||||
this.subtasks = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finishes this task with failure and sets error flag. The entire
|
|
||||||
* task will be aborted unless the next task that should execute
|
|
||||||
* is passed as a parameter. This allows levels of subtasks to be
|
|
||||||
* skipped. For instance, to abort only this tasks's subtasks, then
|
|
||||||
* call fail(task.parent). To abort this task's subtasks and its
|
|
||||||
* parent's subtasks, call fail(task.parent.parent). To abort
|
|
||||||
* all tasks and simply call the task callback, call fail() or
|
|
||||||
* fail(null).
|
|
||||||
*
|
|
||||||
* The task callback (success or failure) will always, eventually, be
|
|
||||||
* called.
|
|
||||||
*
|
|
||||||
* @param next the task to continue at, or null to abort entirely.
|
|
||||||
*/
|
|
||||||
Task.prototype.fail = function(next) {
|
|
||||||
// set error flag
|
|
||||||
this.error = true;
|
|
||||||
|
|
||||||
// finish task
|
|
||||||
finish(this, true);
|
|
||||||
|
|
||||||
if(next) {
|
|
||||||
// propagate task info
|
|
||||||
next.error = this.error;
|
|
||||||
next.swapTime = this.swapTime;
|
|
||||||
next.userData = this.userData;
|
|
||||||
|
|
||||||
// do next task as specified
|
|
||||||
runNext(next, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(this.parent !== null) {
|
|
||||||
// finish root task (ensures it is removed from task queue)
|
|
||||||
var parent = this.parent;
|
|
||||||
while(parent.parent !== null) {
|
|
||||||
// propagate task info
|
|
||||||
parent.error = this.error;
|
|
||||||
parent.swapTime = this.swapTime;
|
|
||||||
parent.userData = this.userData;
|
|
||||||
parent = parent.parent;
|
|
||||||
}
|
|
||||||
finish(parent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// call failure callback if one exists
|
|
||||||
if(this.failureCallback) {
|
|
||||||
this.failureCallback(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously start a task.
|
|
||||||
*
|
|
||||||
* @param task the task to start.
|
|
||||||
*/
|
|
||||||
var start = function(task) {
|
|
||||||
task.error = false;
|
|
||||||
task.state = sStateTable[task.state][START];
|
|
||||||
setTimeout(function() {
|
|
||||||
if(task.state === RUNNING) {
|
|
||||||
task.swapTime = +new Date();
|
|
||||||
task.run(task);
|
|
||||||
runNext(task, 0);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the next subtask or finish this task.
|
|
||||||
*
|
|
||||||
* @param task the task to process.
|
|
||||||
* @param recurse the recursion count.
|
|
||||||
*/
|
|
||||||
var runNext = function(task, recurse) {
|
|
||||||
// get time since last context swap (ms), if enough time has passed set
|
|
||||||
// swap to true to indicate that doNext was performed asynchronously
|
|
||||||
// also, if recurse is too high do asynchronously
|
|
||||||
var swap =
|
|
||||||
(recurse > sMaxRecursions) ||
|
|
||||||
(+new Date() - task.swapTime) > sTimeSlice;
|
|
||||||
|
|
||||||
var doNext = function(recurse) {
|
|
||||||
recurse++;
|
|
||||||
if(task.state === RUNNING) {
|
|
||||||
if(swap) {
|
|
||||||
// update swap time
|
|
||||||
task.swapTime = +new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(task.subtasks.length > 0) {
|
|
||||||
// run next subtask
|
|
||||||
var subtask = task.subtasks.shift();
|
|
||||||
subtask.error = task.error;
|
|
||||||
subtask.swapTime = task.swapTime;
|
|
||||||
subtask.userData = task.userData;
|
|
||||||
subtask.run(subtask);
|
|
||||||
if(!subtask.error)
|
|
||||||
{
|
|
||||||
runNext(subtask, recurse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
finish(task);
|
|
||||||
|
|
||||||
if(!task.error) {
|
|
||||||
// chain back up and run parent
|
|
||||||
if(task.parent !== null) {
|
|
||||||
// propagate task info
|
|
||||||
task.parent.error = task.error;
|
|
||||||
task.parent.swapTime = task.swapTime;
|
|
||||||
task.parent.userData = task.userData;
|
|
||||||
|
|
||||||
// no subtasks left, call run next subtask on parent
|
|
||||||
runNext(task.parent, recurse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if(swap) {
|
|
||||||
// we're swapping, so run asynchronously
|
|
||||||
setTimeout(doNext, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// not swapping, so run synchronously
|
|
||||||
doNext(recurse);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finishes a task and looks for the next task in the queue to start.
|
|
||||||
*
|
|
||||||
* @param task the task to finish.
|
|
||||||
* @param suppressCallbacks true to suppress callbacks.
|
|
||||||
*/
|
|
||||||
var finish = function(task, suppressCallbacks) {
|
|
||||||
// subtask is now done
|
|
||||||
task.state = DONE;
|
|
||||||
|
|
||||||
delete sTasks[task.id];
|
|
||||||
if(sVL >= 1) {
|
|
||||||
forge.log.verbose(cat, '[%s][%s] finish',
|
|
||||||
task.id, task.name, task);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only do queue processing for root tasks
|
|
||||||
if(task.parent === null) {
|
|
||||||
// report error if queue is missing
|
|
||||||
if(!(task.type in sTaskQueues)) {
|
|
||||||
forge.log.error(cat,
|
|
||||||
'[%s][%s] task queue missing [%s]',
|
|
||||||
task.id, task.name, task.type);
|
|
||||||
}
|
|
||||||
// report error if queue is empty
|
|
||||||
else if(sTaskQueues[task.type].length === 0) {
|
|
||||||
forge.log.error(cat,
|
|
||||||
'[%s][%s] task queue empty [%s]',
|
|
||||||
task.id, task.name, task.type);
|
|
||||||
}
|
|
||||||
// report error if this task isn't the first in the queue
|
|
||||||
else if(sTaskQueues[task.type][0] !== task) {
|
|
||||||
forge.log.error(cat,
|
|
||||||
'[%s][%s] task not first in queue [%s]',
|
|
||||||
task.id, task.name, task.type);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// remove ourselves from the queue
|
|
||||||
sTaskQueues[task.type].shift();
|
|
||||||
// clean up queue if it is empty
|
|
||||||
if(sTaskQueues[task.type].length === 0) {
|
|
||||||
if(sVL >= 1) {
|
|
||||||
forge.log.verbose(cat, '[%s][%s] delete queue [%s]',
|
|
||||||
task.id, task.name, task.type);
|
|
||||||
}
|
|
||||||
/* Note: Only a task can delete a queue of its own type. This
|
|
||||||
is used as a way to synchronize tasks. If a queue for a certain
|
|
||||||
task type exists, then a task of that type is running.
|
|
||||||
*/
|
|
||||||
delete sTaskQueues[task.type];
|
|
||||||
}
|
|
||||||
// dequeue the next task and start it
|
|
||||||
else {
|
|
||||||
if(sVL >= 1) {
|
|
||||||
forge.log.verbose(cat,
|
|
||||||
'[%s][%s] queue start next [%s] remain:%s',
|
|
||||||
task.id, task.name, task.type,
|
|
||||||
sTaskQueues[task.type].length);
|
|
||||||
}
|
|
||||||
sTaskQueues[task.type][0].start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!suppressCallbacks) {
|
|
||||||
// call final callback if one exists
|
|
||||||
if(task.error && task.failureCallback) {
|
|
||||||
task.failureCallback(task);
|
|
||||||
}
|
|
||||||
else if(!task.error && task.successCallback) {
|
|
||||||
task.successCallback(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Tasks API */
|
|
||||||
forge.task = forge.task || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a new task that will run the passed function asynchronously.
|
|
||||||
*
|
|
||||||
* In order to finish the task, either task.doNext() or task.fail()
|
|
||||||
* *must* be called.
|
|
||||||
*
|
|
||||||
* The task must have a type (a string identifier) that can be used to
|
|
||||||
* synchronize it with other tasks of the same type. That type can also
|
|
||||||
* be used to cancel tasks that haven't started yet.
|
|
||||||
*
|
|
||||||
* To start a task, the following object must be provided as a parameter
|
|
||||||
* (each function takes a task object as its first parameter):
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* type: the type of task.
|
|
||||||
* run: the function to run to execute the task.
|
|
||||||
* success: a callback to call when the task succeeds (optional).
|
|
||||||
* failure: a callback to call when the task fails (optional).
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param options the object as described above.
|
|
||||||
*/
|
|
||||||
forge.task.start = function(options) {
|
|
||||||
// create a new task
|
|
||||||
var task = new Task({
|
|
||||||
run: options.run,
|
|
||||||
name: options.name || sNoTaskName
|
|
||||||
});
|
|
||||||
task.type = options.type;
|
|
||||||
task.successCallback = options.success || null;
|
|
||||||
task.failureCallback = options.failure || null;
|
|
||||||
|
|
||||||
// append the task onto the appropriate queue
|
|
||||||
if(!(task.type in sTaskQueues)) {
|
|
||||||
if(sVL >= 1) {
|
|
||||||
forge.log.verbose(cat, '[%s][%s] create queue [%s]',
|
|
||||||
task.id, task.name, task.type);
|
|
||||||
}
|
|
||||||
// create the queue with the new task
|
|
||||||
sTaskQueues[task.type] = [task];
|
|
||||||
start(task);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// push the task onto the queue, it will be run after a task
|
|
||||||
// with the same type completes
|
|
||||||
sTaskQueues[options.type].push(task);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels all tasks of the given type that haven't started yet.
|
|
||||||
*
|
|
||||||
* @param type the type of task to cancel.
|
|
||||||
*/
|
|
||||||
forge.task.cancel = function(type) {
|
|
||||||
// find the task queue
|
|
||||||
if(type in sTaskQueues) {
|
|
||||||
// empty all but the current task from the queue
|
|
||||||
sTaskQueues[type] = [sTaskQueues[type][0]];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a condition variable to synchronize tasks. To make a task wait
|
|
||||||
* on the condition variable, call task.wait(condition). To notify all
|
|
||||||
* tasks that are waiting, call condition.notify().
|
|
||||||
*
|
|
||||||
* @return the condition variable.
|
|
||||||
*/
|
|
||||||
forge.task.createCondition = function() {
|
|
||||||
var cond = {
|
|
||||||
// all tasks that are blocked
|
|
||||||
tasks: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Causes the given task to block until notify is called. If the task
|
|
||||||
* is already waiting on this condition then this is a no-op.
|
|
||||||
*
|
|
||||||
* @param task the task to cause to wait.
|
|
||||||
*/
|
|
||||||
cond.wait = function(task) {
|
|
||||||
// only block once
|
|
||||||
if(!(task.id in cond.tasks))
|
|
||||||
{
|
|
||||||
task.block();
|
|
||||||
cond.tasks[task.id] = task;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies all waiting tasks to wake up.
|
|
||||||
*/
|
|
||||||
cond.notify = function() {
|
|
||||||
// since unblock() will run the next task from here, make sure to
|
|
||||||
// clear the condition's blocked task list before unblocking
|
|
||||||
var tmp = cond.tasks;
|
|
||||||
cond.tasks = {};
|
|
||||||
for(var id in tmp) {
|
|
||||||
tmp[id].unblock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return cond;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'task';
|
|
||||||
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', './debug', './log', './util'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
4173
src/lib/tls.js
4173
src/lib/tls.js
File diff suppressed because it is too large
Load Diff
@ -1,306 +0,0 @@
|
|||||||
/**
|
|
||||||
* Socket wrapping functions for TLS.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009-2012 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
/* ########## Begin module implementation ########## */
|
|
||||||
function initModule(forge) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps a forge.net socket with a TLS layer.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* sessionId: a session ID to reuse, null for a new connection if no session
|
|
||||||
* cache is provided or it is empty.
|
|
||||||
* caStore: an array of certificates to trust.
|
|
||||||
* sessionCache: a session cache to use.
|
|
||||||
* cipherSuites: an optional array of cipher suites to use, see
|
|
||||||
* tls.CipherSuites.
|
|
||||||
* socket: the socket to wrap.
|
|
||||||
* virtualHost: the virtual server name to use in a TLS SNI extension.
|
|
||||||
* verify: a handler used to custom verify certificates in the chain.
|
|
||||||
* getCertificate: an optional callback used to get a certificate.
|
|
||||||
* getPrivateKey: an optional callback used to get a private key.
|
|
||||||
* getSignature: an optional callback used to get a signature.
|
|
||||||
* deflate: function(inBytes) if provided, will deflate TLS records using
|
|
||||||
* the deflate algorithm if the server supports it.
|
|
||||||
* inflate: function(inBytes) if provided, will inflate TLS records using
|
|
||||||
* the deflate algorithm if the server supports it.
|
|
||||||
*
|
|
||||||
* @return the TLS-wrapped socket.
|
|
||||||
*/
|
|
||||||
forge.tls.wrapSocket = function(options) {
|
|
||||||
// get raw socket
|
|
||||||
var socket = options.socket;
|
|
||||||
|
|
||||||
// create TLS socket
|
|
||||||
var tlsSocket = {
|
|
||||||
id: socket.id,
|
|
||||||
// set handlers
|
|
||||||
connected: socket.connected || function(e){},
|
|
||||||
closed: socket.closed || function(e){},
|
|
||||||
data: socket.data || function(e){},
|
|
||||||
error: socket.error || function(e){}
|
|
||||||
};
|
|
||||||
|
|
||||||
// create TLS connection
|
|
||||||
var c = forge.tls.createConnection({
|
|
||||||
server: false,
|
|
||||||
sessionId: options.sessionId || null,
|
|
||||||
caStore: options.caStore || [],
|
|
||||||
sessionCache: options.sessionCache || null,
|
|
||||||
cipherSuites: options.cipherSuites || null,
|
|
||||||
virtualHost: options.virtualHost,
|
|
||||||
verify: options.verify,
|
|
||||||
getCertificate: options.getCertificate,
|
|
||||||
getPrivateKey: options.getPrivateKey,
|
|
||||||
getSignature: options.getSignature,
|
|
||||||
deflate: options.deflate,
|
|
||||||
inflate: options.inflate,
|
|
||||||
connected: function(c) {
|
|
||||||
// first handshake complete, call handler
|
|
||||||
if(c.handshakes === 1) {
|
|
||||||
tlsSocket.connected({
|
|
||||||
id: socket.id,
|
|
||||||
type: 'connect',
|
|
||||||
bytesAvailable: c.data.length()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tlsDataReady: function(c) {
|
|
||||||
// send TLS data over socket
|
|
||||||
return socket.send(c.tlsData.getBytes());
|
|
||||||
},
|
|
||||||
dataReady: function(c) {
|
|
||||||
// indicate application data is ready
|
|
||||||
tlsSocket.data({
|
|
||||||
id: socket.id,
|
|
||||||
type: 'socketData',
|
|
||||||
bytesAvailable: c.data.length()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
closed: function(c) {
|
|
||||||
// close socket
|
|
||||||
socket.close();
|
|
||||||
},
|
|
||||||
error: function(c, e) {
|
|
||||||
// send error, close socket
|
|
||||||
tlsSocket.error({
|
|
||||||
id: socket.id,
|
|
||||||
type: 'tlsError',
|
|
||||||
message: e.message,
|
|
||||||
bytesAvailable: 0,
|
|
||||||
error: e
|
|
||||||
});
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle doing handshake after connecting
|
|
||||||
socket.connected = function(e) {
|
|
||||||
c.handshake(options.sessionId);
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle closing TLS connection
|
|
||||||
socket.closed = function(e) {
|
|
||||||
if(c.open && c.handshaking) {
|
|
||||||
// error
|
|
||||||
tlsSocket.error({
|
|
||||||
id: socket.id,
|
|
||||||
type: 'ioError',
|
|
||||||
message: 'Connection closed during handshake.',
|
|
||||||
bytesAvailable: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
c.close();
|
|
||||||
|
|
||||||
// call socket handler
|
|
||||||
tlsSocket.closed({
|
|
||||||
id: socket.id,
|
|
||||||
type: 'close',
|
|
||||||
bytesAvailable: 0
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle error on socket
|
|
||||||
socket.error = function(e) {
|
|
||||||
// error
|
|
||||||
tlsSocket.error({
|
|
||||||
id: socket.id,
|
|
||||||
type: e.type,
|
|
||||||
message: e.message,
|
|
||||||
bytesAvailable: 0
|
|
||||||
});
|
|
||||||
c.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle receiving raw TLS data from socket
|
|
||||||
var _requiredBytes = 0;
|
|
||||||
socket.data = function(e) {
|
|
||||||
// drop data if connection not open
|
|
||||||
if(!c.open) {
|
|
||||||
socket.receive(e.bytesAvailable);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// only receive if there are enough bytes available to
|
|
||||||
// process a record
|
|
||||||
if(e.bytesAvailable >= _requiredBytes) {
|
|
||||||
var count = Math.max(e.bytesAvailable, _requiredBytes);
|
|
||||||
var data = socket.receive(count);
|
|
||||||
if(data !== null) {
|
|
||||||
_requiredBytes = c.process(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys this socket.
|
|
||||||
*/
|
|
||||||
tlsSocket.destroy = function() {
|
|
||||||
socket.destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this socket's TLS session cache. This should be called before
|
|
||||||
* the socket is connected or after it is closed.
|
|
||||||
*
|
|
||||||
* The cache is an object mapping session IDs to internal opaque state.
|
|
||||||
* An application might need to change the cache used by a particular
|
|
||||||
* tlsSocket between connections if it accesses multiple TLS hosts.
|
|
||||||
*
|
|
||||||
* @param cache the session cache to use.
|
|
||||||
*/
|
|
||||||
tlsSocket.setSessionCache = function(cache) {
|
|
||||||
c.sessionCache = tls.createSessionCache(cache);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects this socket.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* host: the host to connect to.
|
|
||||||
* port: the port to connect to.
|
|
||||||
* policyPort: the policy port to use (if non-default), 0 to
|
|
||||||
* use the flash default.
|
|
||||||
* policyUrl: the policy file URL to use (instead of port).
|
|
||||||
*/
|
|
||||||
tlsSocket.connect = function(options) {
|
|
||||||
socket.connect(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this socket.
|
|
||||||
*/
|
|
||||||
tlsSocket.close = function() {
|
|
||||||
c.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the socket is connected or not.
|
|
||||||
*
|
|
||||||
* @return true if connected, false if not.
|
|
||||||
*/
|
|
||||||
tlsSocket.isConnected = function() {
|
|
||||||
return c.isConnected && socket.isConnected();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes bytes to this socket.
|
|
||||||
*
|
|
||||||
* @param bytes the bytes (as a string) to write.
|
|
||||||
*
|
|
||||||
* @return true on success, false on failure.
|
|
||||||
*/
|
|
||||||
tlsSocket.send = function(bytes) {
|
|
||||||
return c.prepare(bytes);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads bytes from this socket (non-blocking). Fewer than the number of
|
|
||||||
* bytes requested may be read if enough bytes are not available.
|
|
||||||
*
|
|
||||||
* This method should be called from the data handler if there are enough
|
|
||||||
* bytes available. To see how many bytes are available, check the
|
|
||||||
* 'bytesAvailable' property on the event in the data handler or call the
|
|
||||||
* bytesAvailable() function on the socket. If the browser is msie, then the
|
|
||||||
* bytesAvailable() function should be used to avoid race conditions.
|
|
||||||
* Otherwise, using the property on the data handler's event may be quicker.
|
|
||||||
*
|
|
||||||
* @param count the maximum number of bytes to read.
|
|
||||||
*
|
|
||||||
* @return the bytes read (as a string) or null on error.
|
|
||||||
*/
|
|
||||||
tlsSocket.receive = function(count) {
|
|
||||||
return c.data.getBytes(count);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of bytes available for receiving on the socket.
|
|
||||||
*
|
|
||||||
* @return the number of bytes available for receiving.
|
|
||||||
*/
|
|
||||||
tlsSocket.bytesAvailable = function() {
|
|
||||||
return c.data.length();
|
|
||||||
};
|
|
||||||
|
|
||||||
return tlsSocket;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end module implementation
|
|
||||||
|
|
||||||
/* ########## Begin module wrapper ########## */
|
|
||||||
var name = 'tlssocket';
|
|
||||||
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', './tls'], function() {
|
|
||||||
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
||||||
});
|
|
||||||
})();
|
|
1
src/lib/underscore-1.4.4.min.js
vendored
1
src/lib/underscore-1.4.4.min.js
vendored
File diff suppressed because one or more lines are too long
6
src/lib/underscore/underscore-min.js
vendored
Normal file
6
src/lib/underscore/underscore-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1641
src/lib/util.js
1641
src/lib/util.js
File diff suppressed because it is too large
Load Diff
3018
src/lib/x509.js
3018
src/lib/x509.js
File diff suppressed because it is too large
Load Diff
758
src/lib/xhr.js
758
src/lib/xhr.js
@ -1,758 +0,0 @@
|
|||||||
/**
|
|
||||||
* XmlHttpRequest implementation that uses TLS and flash SocketPool.
|
|
||||||
*
|
|
||||||
* @author Dave Longley
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
|
|
||||||
*/
|
|
||||||
(function($) {
|
|
||||||
|
|
||||||
// logging category
|
|
||||||
var cat = 'forge.xhr';
|
|
||||||
|
|
||||||
/*
|
|
||||||
XMLHttpRequest interface definition from:
|
|
||||||
http://www.w3.org/TR/XMLHttpRequest
|
|
||||||
|
|
||||||
interface XMLHttpRequest {
|
|
||||||
// event handler
|
|
||||||
attribute EventListener onreadystatechange;
|
|
||||||
|
|
||||||
// state
|
|
||||||
const unsigned short UNSENT = 0;
|
|
||||||
const unsigned short OPENED = 1;
|
|
||||||
const unsigned short HEADERS_RECEIVED = 2;
|
|
||||||
const unsigned short LOADING = 3;
|
|
||||||
const unsigned short DONE = 4;
|
|
||||||
readonly attribute unsigned short readyState;
|
|
||||||
|
|
||||||
// request
|
|
||||||
void open(in DOMString method, in DOMString url);
|
|
||||||
void open(in DOMString method, in DOMString url, in boolean async);
|
|
||||||
void open(in DOMString method, in DOMString url,
|
|
||||||
in boolean async, in DOMString user);
|
|
||||||
void open(in DOMString method, in DOMString url,
|
|
||||||
in boolean async, in DOMString user, in DOMString password);
|
|
||||||
void setRequestHeader(in DOMString header, in DOMString value);
|
|
||||||
void send();
|
|
||||||
void send(in DOMString data);
|
|
||||||
void send(in Document data);
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
// response
|
|
||||||
DOMString getAllResponseHeaders();
|
|
||||||
DOMString getResponseHeader(in DOMString header);
|
|
||||||
readonly attribute DOMString responseText;
|
|
||||||
readonly attribute Document responseXML;
|
|
||||||
readonly attribute unsigned short status;
|
|
||||||
readonly attribute DOMString statusText;
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// readyStates
|
|
||||||
var UNSENT = 0;
|
|
||||||
var OPENED = 1;
|
|
||||||
var HEADERS_RECEIVED = 2;
|
|
||||||
var LOADING = 3;
|
|
||||||
var DONE = 4;
|
|
||||||
|
|
||||||
// exceptions
|
|
||||||
var INVALID_STATE_ERR = 11;
|
|
||||||
var SYNTAX_ERR = 12;
|
|
||||||
var SECURITY_ERR = 18;
|
|
||||||
var NETWORK_ERR = 19;
|
|
||||||
var ABORT_ERR = 20;
|
|
||||||
|
|
||||||
// private flash socket pool vars
|
|
||||||
var _sp = null;
|
|
||||||
var _policyPort = 0;
|
|
||||||
var _policyUrl = null;
|
|
||||||
|
|
||||||
// default client (used if no special URL provided when creating an XHR)
|
|
||||||
var _client = null;
|
|
||||||
|
|
||||||
// all clients including the default, key'd by full base url
|
|
||||||
// (multiple cross-domain http clients are permitted so there may be more
|
|
||||||
// than one client in this map)
|
|
||||||
// TODO: provide optional clean up API for non-default clients
|
|
||||||
var _clients = {};
|
|
||||||
|
|
||||||
// the default maximum number of concurrents connections per client
|
|
||||||
var _maxConnections = 10;
|
|
||||||
|
|
||||||
// local aliases
|
|
||||||
if(typeof forge === 'undefined') {
|
|
||||||
forge = {};
|
|
||||||
}
|
|
||||||
var net = forge.net;
|
|
||||||
var http = forge.http;
|
|
||||||
|
|
||||||
// define the xhr interface
|
|
||||||
var xhrApi = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes flash XHR support.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* url: the default base URL to connect to if xhr URLs are relative,
|
|
||||||
* ie: https://myserver.com.
|
|
||||||
* flashId: the dom ID of the flash SocketPool.
|
|
||||||
* policyPort: the port that provides the server's flash policy, 0 to use
|
|
||||||
* the flash default.
|
|
||||||
* policyUrl: the policy file URL to use instead of a policy port.
|
|
||||||
* msie: true if browser is internet explorer, false if not.
|
|
||||||
* connections: the maximum number of concurrent connections.
|
|
||||||
* caCerts: a list of PEM-formatted certificates to trust.
|
|
||||||
* cipherSuites: an optional array of cipher suites to use,
|
|
||||||
* see forge.tls.CipherSuites.
|
|
||||||
* verify: optional TLS certificate verify callback to use (see forge.tls
|
|
||||||
* for details).
|
|
||||||
* getCertificate: an optional callback used to get a client-side
|
|
||||||
* certificate (see forge.tls for details).
|
|
||||||
* getPrivateKey: an optional callback used to get a client-side private
|
|
||||||
* key (see forge.tls for details).
|
|
||||||
* getSignature: an optional callback used to get a client-side signature
|
|
||||||
* (see forge.tls for details).
|
|
||||||
* persistCookies: true to use persistent cookies via flash local storage,
|
|
||||||
* false to only keep cookies in javascript.
|
|
||||||
* primeTlsSockets: true to immediately connect TLS sockets on their
|
|
||||||
* creation so that they will cache TLS sessions for reuse.
|
|
||||||
*/
|
|
||||||
xhrApi.init = function(options) {
|
|
||||||
forge.log.debug(cat, 'initializing', options);
|
|
||||||
|
|
||||||
// update default policy port and max connections
|
|
||||||
_policyPort = options.policyPort || _policyPort;
|
|
||||||
_policyUrl = options.policyUrl || _policyUrl;
|
|
||||||
_maxConnections = options.connections || _maxConnections;
|
|
||||||
|
|
||||||
// create the flash socket pool
|
|
||||||
_sp = net.createSocketPool({
|
|
||||||
flashId: options.flashId,
|
|
||||||
policyPort: _policyPort,
|
|
||||||
policyUrl: _policyUrl,
|
|
||||||
msie: options.msie || false
|
|
||||||
});
|
|
||||||
|
|
||||||
// create default http client
|
|
||||||
_client = http.createClient({
|
|
||||||
url: options.url || (
|
|
||||||
window.location.protocol + '//' + window.location.host),
|
|
||||||
socketPool: _sp,
|
|
||||||
policyPort: _policyPort,
|
|
||||||
policyUrl: _policyUrl,
|
|
||||||
connections: options.connections || _maxConnections,
|
|
||||||
caCerts: options.caCerts,
|
|
||||||
cipherSuites: options.cipherSuites,
|
|
||||||
persistCookies: options.persistCookies || true,
|
|
||||||
primeTlsSockets: options.primeTlsSockets || false,
|
|
||||||
verify: options.verify,
|
|
||||||
getCertificate: options.getCertificate,
|
|
||||||
getPrivateKey: options.getPrivateKey,
|
|
||||||
getSignature: options.getSignature
|
|
||||||
});
|
|
||||||
_clients[_client.url.full] = _client;
|
|
||||||
|
|
||||||
forge.log.debug(cat, 'ready');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to clean up the clients and socket pool.
|
|
||||||
*/
|
|
||||||
xhrApi.cleanup = function() {
|
|
||||||
// destroy all clients
|
|
||||||
for(var key in _clients) {
|
|
||||||
_clients[key].destroy();
|
|
||||||
}
|
|
||||||
_clients = {};
|
|
||||||
_client = null;
|
|
||||||
|
|
||||||
// destroy socket pool
|
|
||||||
_sp.destroy();
|
|
||||||
_sp = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a cookie.
|
|
||||||
*
|
|
||||||
* @param cookie the cookie with parameters:
|
|
||||||
* name: the name of the cookie.
|
|
||||||
* value: the value of the cookie.
|
|
||||||
* comment: an optional comment string.
|
|
||||||
* maxAge: the age of the cookie in seconds relative to created time.
|
|
||||||
* secure: true if the cookie must be sent over a secure protocol.
|
|
||||||
* httpOnly: true to restrict access to the cookie from javascript
|
|
||||||
* (inaffective since the cookies are stored in javascript).
|
|
||||||
* path: the path for the cookie.
|
|
||||||
* domain: optional domain the cookie belongs to (must start with dot).
|
|
||||||
* version: optional version of the cookie.
|
|
||||||
* created: creation time, in UTC seconds, of the cookie.
|
|
||||||
*/
|
|
||||||
xhrApi.setCookie = function(cookie) {
|
|
||||||
// default cookie expiration to never
|
|
||||||
cookie.maxAge = cookie.maxAge || -1;
|
|
||||||
|
|
||||||
// if the cookie's domain is set, use the appropriate client
|
|
||||||
if(cookie.domain) {
|
|
||||||
// add the cookies to the applicable domains
|
|
||||||
for(var key in _clients) {
|
|
||||||
var client = _clients[key];
|
|
||||||
if(http.withinCookieDomain(client.url, cookie) &&
|
|
||||||
client.secure === cookie.secure) {
|
|
||||||
client.setCookie(cookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// use the default domain
|
|
||||||
// FIXME: should a null domain cookie be added to all clients? should
|
|
||||||
// this be an option?
|
|
||||||
else {
|
|
||||||
_client.setCookie(cookie);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a cookie.
|
|
||||||
*
|
|
||||||
* @param name the name of the cookie.
|
|
||||||
* @param path an optional path for the cookie (if there are multiple cookies
|
|
||||||
* with the same name but different paths).
|
|
||||||
* @param domain an optional domain for the cookie (if not using the default
|
|
||||||
* domain).
|
|
||||||
*
|
|
||||||
* @return the cookie, cookies (if multiple matches), or null if not found.
|
|
||||||
*/
|
|
||||||
xhrApi.getCookie = function(name, path, domain) {
|
|
||||||
var rval = null;
|
|
||||||
|
|
||||||
if(domain) {
|
|
||||||
// get the cookies from the applicable domains
|
|
||||||
for(var key in _clients) {
|
|
||||||
var client = _clients[key];
|
|
||||||
if(http.withinCookieDomain(client.url, domain)) {
|
|
||||||
var cookie = client.getCookie(name, path);
|
|
||||||
if(cookie !== null) {
|
|
||||||
if(rval === null) {
|
|
||||||
rval = cookie;
|
|
||||||
}
|
|
||||||
else if(!forge.util.isArray(rval)) {
|
|
||||||
rval = [rval, cookie];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rval.push(cookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// get cookie from default domain
|
|
||||||
rval = _client.getCookie(name, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a cookie.
|
|
||||||
*
|
|
||||||
* @param name the name of the cookie.
|
|
||||||
* @param path an optional path for the cookie (if there are multiple cookies
|
|
||||||
* with the same name but different paths).
|
|
||||||
* @param domain an optional domain for the cookie (if not using the default
|
|
||||||
* domain).
|
|
||||||
*
|
|
||||||
* @return true if a cookie was removed, false if not.
|
|
||||||
*/
|
|
||||||
xhrApi.removeCookie = function(name, path, domain) {
|
|
||||||
var rval = false;
|
|
||||||
|
|
||||||
if(domain) {
|
|
||||||
// remove the cookies from the applicable domains
|
|
||||||
for(var key in _clients) {
|
|
||||||
var client = _clients[key];
|
|
||||||
if(http.withinCookieDomain(client.url, domain)) {
|
|
||||||
if(client.removeCookie(name, path)) {
|
|
||||||
rval = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// remove cookie from default domain
|
|
||||||
rval = _client.removeCookie(name, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new XmlHttpRequest. By default the base URL, flash policy port,
|
|
||||||
* etc, will be used. However, an XHR can be created to point at another
|
|
||||||
* cross-domain URL.
|
|
||||||
*
|
|
||||||
* @param options:
|
|
||||||
* logWarningOnError: If true and an HTTP error status code is received then
|
|
||||||
* log a warning, otherwise log a verbose message.
|
|
||||||
* verbose: If true be very verbose in the output including the response
|
|
||||||
* event and response body, otherwise only include status, timing, and
|
|
||||||
* data size.
|
|
||||||
* logError: a multi-var log function for warnings that takes the log
|
|
||||||
* category as the first var.
|
|
||||||
* logWarning: a multi-var log function for warnings that takes the log
|
|
||||||
* category as the first var.
|
|
||||||
* logDebug: a multi-var log function for warnings that takes the log
|
|
||||||
* category as the first var.
|
|
||||||
* logVerbose: a multi-var log function for warnings that takes the log
|
|
||||||
* category as the first var.
|
|
||||||
* url: the default base URL to connect to if xhr URLs are relative,
|
|
||||||
* eg: https://myserver.com, and note that the following options will be
|
|
||||||
* ignored if the URL is absent or the same as the default base URL.
|
|
||||||
* policyPort: the port that provides the server's flash policy, 0 to use
|
|
||||||
* the flash default.
|
|
||||||
* policyUrl: the policy file URL to use instead of a policy port.
|
|
||||||
* connections: the maximum number of concurrent connections.
|
|
||||||
* caCerts: a list of PEM-formatted certificates to trust.
|
|
||||||
* cipherSuites: an optional array of cipher suites to use, see
|
|
||||||
* forge.tls.CipherSuites.
|
|
||||||
* verify: optional TLS certificate verify callback to use (see forge.tls
|
|
||||||
* for details).
|
|
||||||
* getCertificate: an optional callback used to get a client-side
|
|
||||||
* certificate.
|
|
||||||
* getPrivateKey: an optional callback used to get a client-side private key.
|
|
||||||
* getSignature: an optional callback used to get a client-side signature.
|
|
||||||
* persistCookies: true to use persistent cookies via flash local storage,
|
|
||||||
* false to only keep cookies in javascript.
|
|
||||||
* primeTlsSockets: true to immediately connect TLS sockets on their
|
|
||||||
* creation so that they will cache TLS sessions for reuse.
|
|
||||||
*
|
|
||||||
* @return the XmlHttpRequest.
|
|
||||||
*/
|
|
||||||
xhrApi.create = function(options) {
|
|
||||||
// set option defaults
|
|
||||||
options = $.extend({
|
|
||||||
logWarningOnError: true,
|
|
||||||
verbose: false,
|
|
||||||
logError: function(){},
|
|
||||||
logWarning: function(){},
|
|
||||||
logDebug: function(){},
|
|
||||||
logVerbose: function(){},
|
|
||||||
url: null
|
|
||||||
}, options || {});
|
|
||||||
|
|
||||||
// private xhr state
|
|
||||||
var _state = {
|
|
||||||
// the http client to use
|
|
||||||
client: null,
|
|
||||||
// request storage
|
|
||||||
request: null,
|
|
||||||
// response storage
|
|
||||||
response: null,
|
|
||||||
// asynchronous, true if doing asynchronous communication
|
|
||||||
asynchronous: true,
|
|
||||||
// sendFlag, true if send has been called
|
|
||||||
sendFlag: false,
|
|
||||||
// errorFlag, true if a network error occurred
|
|
||||||
errorFlag: false
|
|
||||||
};
|
|
||||||
|
|
||||||
// private log functions
|
|
||||||
var _log = {
|
|
||||||
error: options.logError || forge.log.error,
|
|
||||||
warning: options.logWarning || forge.log.warning,
|
|
||||||
debug: options.logDebug || forge.log.debug,
|
|
||||||
verbose: options.logVerbose || forge.log.verbose
|
|
||||||
};
|
|
||||||
|
|
||||||
// create public xhr interface
|
|
||||||
var xhr = {
|
|
||||||
// an EventListener
|
|
||||||
onreadystatechange: null,
|
|
||||||
// readonly, the current readyState
|
|
||||||
readyState: UNSENT,
|
|
||||||
// a string with the response entity-body
|
|
||||||
responseText: '',
|
|
||||||
// a Document for response entity-bodies that are XML
|
|
||||||
responseXML: null,
|
|
||||||
// readonly, returns the HTTP status code (i.e. 404)
|
|
||||||
status: 0,
|
|
||||||
// readonly, returns the HTTP status message (i.e. 'Not Found')
|
|
||||||
statusText: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
// determine which http client to use
|
|
||||||
if(options.url === null) {
|
|
||||||
// use default
|
|
||||||
_state.client = _client;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var url = http.parseUrl(options.url);
|
|
||||||
if(!url) {
|
|
||||||
throw {
|
|
||||||
message: 'Invalid url.',
|
|
||||||
details: {
|
|
||||||
url: options.url
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// find client
|
|
||||||
if(url.full in _clients) {
|
|
||||||
// client found
|
|
||||||
_state.client = _clients[url.full];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// create client
|
|
||||||
_state.client = http.createClient({
|
|
||||||
url: options.url,
|
|
||||||
socketPool: _sp,
|
|
||||||
policyPort: options.policyPort || _policyPort,
|
|
||||||
policyUrl: options.policyUrl || _policyUrl,
|
|
||||||
connections: options.connections || _maxConnections,
|
|
||||||
caCerts: options.caCerts,
|
|
||||||
cipherSuites: options.cipherSuites,
|
|
||||||
persistCookies: options.persistCookies || true,
|
|
||||||
primeTlsSockets: options.primeTlsSockets || false,
|
|
||||||
verify: options.verify,
|
|
||||||
getCertificate: options.getCertificate,
|
|
||||||
getPrivateKey: options.getPrivateKey,
|
|
||||||
getSignature: options.getSignature
|
|
||||||
});
|
|
||||||
_clients[url.full] = _state.client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the request. This method will create the HTTP request to send.
|
|
||||||
*
|
|
||||||
* @param method the HTTP method (i.e. 'GET').
|
|
||||||
* @param url the relative url (the HTTP request path).
|
|
||||||
* @param async always true, ignored.
|
|
||||||
* @param user always null, ignored.
|
|
||||||
* @param password always null, ignored.
|
|
||||||
*/
|
|
||||||
xhr.open = function(method, url, async, user, password) {
|
|
||||||
// 1. validate Document if one is associated
|
|
||||||
// TODO: not implemented (not used yet)
|
|
||||||
|
|
||||||
// 2. validate method token
|
|
||||||
// 3. change method to uppercase if it matches a known
|
|
||||||
// method (here we just require it to be uppercase, and
|
|
||||||
// we do not allow the standard methods)
|
|
||||||
// 4. disallow CONNECT, TRACE, or TRACK with a security error
|
|
||||||
switch(method) {
|
|
||||||
case 'DELETE':
|
|
||||||
case 'GET':
|
|
||||||
case 'HEAD':
|
|
||||||
case 'OPTIONS':
|
|
||||||
case 'POST':
|
|
||||||
case 'PUT':
|
|
||||||
// valid method
|
|
||||||
break;
|
|
||||||
case 'CONNECT':
|
|
||||||
case 'TRACE':
|
|
||||||
case 'TRACK':
|
|
||||||
// FIXME: handle exceptions
|
|
||||||
throw SECURITY_ERR;
|
|
||||||
default:
|
|
||||||
// FIXME: handle exceptions
|
|
||||||
//throw new SyntaxError('Invalid method: ' + method);
|
|
||||||
throw SYNTAX_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: other validation steps in algorithm are not implemented
|
|
||||||
|
|
||||||
// 19. set send flag to false
|
|
||||||
// set response body to null
|
|
||||||
// empty list of request headers
|
|
||||||
// set request method to given method
|
|
||||||
// set request URL
|
|
||||||
// set username, password
|
|
||||||
// set asychronous flag
|
|
||||||
_state.sendFlag = false;
|
|
||||||
xhr.responseText = '';
|
|
||||||
xhr.responseXML = null;
|
|
||||||
|
|
||||||
// custom: reset status and statusText
|
|
||||||
xhr.status = 0;
|
|
||||||
xhr.statusText = '';
|
|
||||||
|
|
||||||
// create the HTTP request
|
|
||||||
_state.request = http.createRequest({
|
|
||||||
method: method,
|
|
||||||
path: url
|
|
||||||
});
|
|
||||||
|
|
||||||
// 20. set state to OPENED
|
|
||||||
xhr.readyState = OPENED;
|
|
||||||
|
|
||||||
// 21. dispatch onreadystatechange
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an HTTP header field to the request.
|
|
||||||
*
|
|
||||||
* @param header the name of the header field.
|
|
||||||
* @param value the value of the header field.
|
|
||||||
*/
|
|
||||||
xhr.setRequestHeader = function(header, value) {
|
|
||||||
// 1. if state is not OPENED or send flag is true, raise exception
|
|
||||||
if(xhr.readyState != OPENED || _state.sendFlag) {
|
|
||||||
// FIXME: handle exceptions properly
|
|
||||||
throw INVALID_STATE_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: other validation steps in spec aren't implemented
|
|
||||||
|
|
||||||
// set header
|
|
||||||
_state.request.setField(header, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the request and any associated data.
|
|
||||||
*
|
|
||||||
* @param data a string or Document object to send, null to send no data.
|
|
||||||
*/
|
|
||||||
xhr.send = function(data) {
|
|
||||||
// 1. if state is not OPENED or 2. send flag is true, raise
|
|
||||||
// an invalid state exception
|
|
||||||
if(xhr.readyState != OPENED || _state.sendFlag) {
|
|
||||||
// FIXME: handle exceptions properly
|
|
||||||
throw INVALID_STATE_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. ignore data if method is GET or HEAD
|
|
||||||
if(data &&
|
|
||||||
_state.request.method !== 'GET' &&
|
|
||||||
_state.request.method !== 'HEAD') {
|
|
||||||
// handle non-IE case
|
|
||||||
if(typeof(XMLSerializer) !== 'undefined') {
|
|
||||||
if(data instanceof Document) {
|
|
||||||
var xs = new XMLSerializer();
|
|
||||||
_state.request.body = xs.serializeToString(data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_state.request.body = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// poorly implemented IE case
|
|
||||||
else {
|
|
||||||
if(typeof(data.xml) !== 'undefined') {
|
|
||||||
_state.request.body = xml;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_state.request.body = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. release storage mutex (not used)
|
|
||||||
|
|
||||||
// 5. set error flag to false
|
|
||||||
_state.errorFlag = false;
|
|
||||||
|
|
||||||
// 6. if asynchronous is true (must be in this implementation)
|
|
||||||
|
|
||||||
// 6.1 set send flag to true
|
|
||||||
_state.sendFlag = true;
|
|
||||||
|
|
||||||
// 6.2 dispatch onreadystatechange
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create send options
|
|
||||||
var options = {};
|
|
||||||
options.request = _state.request;
|
|
||||||
options.headerReady = function(e) {
|
|
||||||
// make cookies available for ease of use/iteration
|
|
||||||
xhr.cookies = _state.client.cookies;
|
|
||||||
|
|
||||||
// TODO: update document.cookie with any cookies where the
|
|
||||||
// script's domain matches
|
|
||||||
|
|
||||||
// headers received
|
|
||||||
xhr.readyState = HEADERS_RECEIVED;
|
|
||||||
xhr.status = e.response.code;
|
|
||||||
xhr.statusText = e.response.message;
|
|
||||||
_state.response = e.response;
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
if(!_state.response.aborted) {
|
|
||||||
// now loading body
|
|
||||||
xhr.readyState = LOADING;
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
options.bodyReady = function(e) {
|
|
||||||
xhr.readyState = DONE;
|
|
||||||
var ct = e.response.getField('Content-Type');
|
|
||||||
// Note: this null/undefined check is done outside because IE
|
|
||||||
// dies otherwise on a "'null' is null" error
|
|
||||||
if(ct) {
|
|
||||||
if(ct.indexOf('text/xml') === 0 ||
|
|
||||||
ct.indexOf('application/xml') === 0 ||
|
|
||||||
ct.indexOf('+xml') !== -1) {
|
|
||||||
try {
|
|
||||||
var doc = new ActiveXObject('MicrosoftXMLDOM');
|
|
||||||
doc.async = false;
|
|
||||||
doc.loadXML(e.response.body);
|
|
||||||
xhr.responseXML = doc;
|
|
||||||
}
|
|
||||||
catch(ex) {
|
|
||||||
var parser = new DOMParser();
|
|
||||||
xhr.responseXML = parser.parseFromString(ex.body, 'text/xml');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var length = 0;
|
|
||||||
if(e.response.body !== null) {
|
|
||||||
xhr.responseText = e.response.body;
|
|
||||||
length = e.response.body.length;
|
|
||||||
}
|
|
||||||
// build logging output
|
|
||||||
var req = _state.request;
|
|
||||||
var output =
|
|
||||||
req.method + ' ' + req.path + ' ' +
|
|
||||||
xhr.status + ' ' + xhr.statusText + ' ' +
|
|
||||||
length + 'B ' +
|
|
||||||
(e.request.connectTime + e.request.time + e.response.time) +
|
|
||||||
'ms';
|
|
||||||
var lFunc;
|
|
||||||
if(options.verbose) {
|
|
||||||
lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
|
|
||||||
_log.warning : _log.verbose;
|
|
||||||
lFunc(cat, output,
|
|
||||||
e, e.response.body ? '\n' + e.response.body : '\nNo content');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
|
|
||||||
_log.warning : _log.debug;
|
|
||||||
lFunc(cat, output);
|
|
||||||
}
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
options.error = function(e) {
|
|
||||||
var req = _state.request;
|
|
||||||
_log.error(cat, req.method + ' ' + req.path, e);
|
|
||||||
|
|
||||||
// 1. set response body to null
|
|
||||||
xhr.responseText = '';
|
|
||||||
xhr.responseXML = null;
|
|
||||||
|
|
||||||
// 2. set error flag to true (and reset status)
|
|
||||||
_state.errorFlag = true;
|
|
||||||
xhr.status = 0;
|
|
||||||
xhr.statusText = '';
|
|
||||||
|
|
||||||
// 3. set state to done
|
|
||||||
xhr.readyState = DONE;
|
|
||||||
|
|
||||||
// 4. asyc flag is always true, so dispatch onreadystatechange
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 7. send request
|
|
||||||
_state.client.send(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aborts the request.
|
|
||||||
*/
|
|
||||||
xhr.abort = function() {
|
|
||||||
// 1. abort send
|
|
||||||
// 2. stop network activity
|
|
||||||
_state.request.abort();
|
|
||||||
|
|
||||||
// 3. set response to null
|
|
||||||
xhr.responseText = '';
|
|
||||||
xhr.responseXML = null;
|
|
||||||
|
|
||||||
// 4. set error flag to true (and reset status)
|
|
||||||
_state.errorFlag = true;
|
|
||||||
xhr.status = 0;
|
|
||||||
xhr.statusText = '';
|
|
||||||
|
|
||||||
// 5. clear user headers
|
|
||||||
_state.request = null;
|
|
||||||
_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)) {
|
|
||||||
// 7. set ready state to unsent
|
|
||||||
xhr.readyState = UNSENT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 6.1 set state to DONE
|
|
||||||
xhr.readyState = DONE;
|
|
||||||
|
|
||||||
// 6.2 set send flag to false
|
|
||||||
_state.sendFlag = false;
|
|
||||||
|
|
||||||
// 6.3 dispatch onreadystatechange
|
|
||||||
if(xhr.onreadystatechange) {
|
|
||||||
xhr.onreadystatechange();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. set state to UNSENT
|
|
||||||
xhr.readyState = UNSENT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all response headers as a string.
|
|
||||||
*
|
|
||||||
* @return the HTTP-encoded response header fields.
|
|
||||||
*/
|
|
||||||
xhr.getAllResponseHeaders = function() {
|
|
||||||
var rval = '';
|
|
||||||
if(_state.response !== null) {
|
|
||||||
var fields = _state.response.fields;
|
|
||||||
$.each(fields, function(name, array) {
|
|
||||||
$.each(array, function(i, value) {
|
|
||||||
rval += name + ': ' + value + '\r\n';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a single header field value or, if there are multiple
|
|
||||||
* fields with the same name, a comma-separated list of header
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @return the header field value(s) or null.
|
|
||||||
*/
|
|
||||||
xhr.getResponseHeader = function(header) {
|
|
||||||
var rval = null;
|
|
||||||
if(_state.response !== null) {
|
|
||||||
if(header in _state.response.fields) {
|
|
||||||
rval = _state.response.fields[header];
|
|
||||||
if(forge.util.isArray(rval)) {
|
|
||||||
rval = rval.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
};
|
|
||||||
|
|
||||||
return xhr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// expose public api
|
|
||||||
forge.xhr = xhrApi;
|
|
||||||
|
|
||||||
})(jQuery);
|
|
@ -7,19 +7,20 @@
|
|||||||
js: '../js',
|
js: '../js',
|
||||||
test: '../../test',
|
test: '../../test',
|
||||||
'node-forge': 'forge',
|
'node-forge': 'forge',
|
||||||
'setimmediate': 'setImmediate',
|
|
||||||
cryptoLib: '../js/crypto',
|
cryptoLib: '../js/crypto',
|
||||||
jquery: 'jquery-1.8.2.min',
|
'setimmediate': 'setImmediate',
|
||||||
underscore: 'underscore-1.4.4.min',
|
'smtp-client': 'smtp-client-browserified',
|
||||||
|
underscore: 'underscore/underscore-min',
|
||||||
|
cordova: 'cordova/cordova-2.5.0',
|
||||||
lawnchair: 'lawnchair/lawnchair-git',
|
lawnchair: 'lawnchair/lawnchair-git',
|
||||||
lawnchairSQL: 'lawnchair/lawnchair-adapter-webkit-sqlite-git',
|
lawnchairSQL: 'lawnchair/lawnchair-adapter-webkit-sqlite-git',
|
||||||
lawnchairIDB: 'lawnchair/lawnchair-adapter-indexed-db-git',
|
lawnchairIDB: 'lawnchair/lawnchair-adapter-indexed-db-git',
|
||||||
cordova: 'cordova-2.5.0',
|
jquery: 'jquery/jquery-2.0.3.min',
|
||||||
'smtp-client': 'smtp-client-browserified',
|
|
||||||
angular: 'angular/angular.min',
|
angular: 'angular/angular.min',
|
||||||
angularRoute: 'angular/angular-route.min',
|
angularRoute: 'angular/angular-route.min',
|
||||||
angularTouch: 'angular/angular-touch.min',
|
angularTouch: 'angular/angular-touch.min',
|
||||||
moment: 'moment.min'
|
moment: 'moment/moment.min',
|
||||||
|
uuid: 'uuid/uuid'
|
||||||
},
|
},
|
||||||
shim: {
|
shim: {
|
||||||
angular: {
|
angular: {
|
||||||
|
Loading…
Reference in New Issue
Block a user