mail/src/lib/forge/forge.rsa.bundle.js

11659 lines
332 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Utility functions for web applications.
*
* @author Dave Longley
*
* Copyright (c) 2010-2012 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/* Utilities API */
var util = forge.util = forge.util || {};
// define setImmediate and nextTick
if(typeof process === 'undefined' || !process.nextTick) {
if(typeof setImmediate === 'function') {
util.setImmediate = setImmediate;
util.nextTick = function(callback) {
return setImmediate(callback);
};
}
else {
util.setImmediate = function(callback) {
setTimeout(callback, 0);
};
util.nextTick = util.setImmediate;
}
}
else {
util.nextTick = process.nextTick;
if(typeof setImmediate === 'function') {
util.setImmediate = setImmediate;
}
else {
util.setImmediate = util.nextTick;
}
}
/**
* Constructor for a byte buffer.
*
* @param b the bytes to wrap (as a UTF-8 string) (optional).
*/
util.ByteBuffer = function(b) {
// the data in this buffer
this.data = b || '';
// the pointer for reading from this buffer
this.read = 0;
};
/**
* Gets the number of bytes in this buffer.
*
* @return the number of bytes in this buffer.
*/
util.ByteBuffer.prototype.length = function() {
return this.data.length - this.read;
};
/**
* Gets whether or not this buffer is empty.
*
* @return true if this buffer is empty, false if not.
*/
util.ByteBuffer.prototype.isEmpty = function() {
return (this.data.length - this.read) === 0;
};
/**
* Puts a byte in this buffer.
*
* @param b the byte to put.
*/
util.ByteBuffer.prototype.putByte = function(b) {
this.data += String.fromCharCode(b);
};
/**
* Puts a byte in this buffer N times.
*
* @param b the byte to put.
* @param n the number of bytes of value b to put.
*/
util.ByteBuffer.prototype.fillWithByte = function(b, n) {
b = String.fromCharCode(b);
var d = this.data;
while(n > 0) {
if(n & 1) {
d += b;
}
n >>>= 1;
if(n > 0) {
b += b;
}
}
this.data = d;
};
/**
* Puts bytes in this buffer.
*
* @param bytes the bytes (as a UTF-8 encoded string) to put.
*/
util.ByteBuffer.prototype.putBytes = function(bytes) {
this.data += bytes;
};
/**
* Puts a UTF-16 encoded string into this buffer.
*
* @param str the string to put.
*/
util.ByteBuffer.prototype.putString = function(str) {
this.data += util.encodeUtf8(str);
};
/**
* Puts a 16-bit integer in this buffer in big-endian order.
*
* @param i the 16-bit integer.
*/
util.ByteBuffer.prototype.putInt16 = function(i) {
this.data +=
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF);
};
/**
* Puts a 24-bit integer in this buffer in big-endian order.
*
* @param i the 24-bit integer.
*/
util.ByteBuffer.prototype.putInt24 = function(i) {
this.data +=
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF);
};
/**
* Puts a 32-bit integer in this buffer in big-endian order.
*
* @param i the 32-bit integer.
*/
util.ByteBuffer.prototype.putInt32 = function(i) {
this.data +=
String.fromCharCode(i >> 24 & 0xFF) +
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF);
};
/**
* Puts a 16-bit integer in this buffer in little-endian order.
*
* @param i the 16-bit integer.
*/
util.ByteBuffer.prototype.putInt16Le = function(i) {
this.data +=
String.fromCharCode(i & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF);
};
/**
* Puts a 24-bit integer in this buffer in little-endian order.
*
* @param i the 24-bit integer.
*/
util.ByteBuffer.prototype.putInt24Le = function(i) {
this.data +=
String.fromCharCode(i & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i >> 16 & 0xFF);
};
/**
* Puts a 32-bit integer in this buffer in little-endian order.
*
* @param i the 32-bit integer.
*/
util.ByteBuffer.prototype.putInt32Le = function(i) {
this.data +=
String.fromCharCode(i & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 24 & 0xFF);
};
/**
* Puts an n-bit integer in this buffer in big-endian order.
*
* @param i the n-bit integer.
* @param n the number of bits in the integer.
*/
util.ByteBuffer.prototype.putInt = function(i, n) {
do {
n -= 8;
this.data += String.fromCharCode((i >> n) & 0xFF);
}
while(n > 0);
};
/**
* Puts the given buffer into this buffer.
*
* @param buffer the buffer to put into this one.
*/
util.ByteBuffer.prototype.putBuffer = function(buffer) {
this.data += buffer.getBytes();
};
/**
* Gets a byte from this buffer and advances the read pointer by 1.
*
* @return the byte.
*/
util.ByteBuffer.prototype.getByte = function() {
return this.data.charCodeAt(this.read++);
};
/**
* Gets a uint16 from this buffer in big-endian order and advances the read
* pointer by 2.
*
* @return the uint16.
*/
util.ByteBuffer.prototype.getInt16 = function() {
var rval = (
this.data.charCodeAt(this.read) << 8 ^
this.data.charCodeAt(this.read + 1));
this.read += 2;
return rval;
};
/**
* Gets a uint24 from this buffer in big-endian order and advances the read
* pointer by 3.
*
* @return the uint24.
*/
util.ByteBuffer.prototype.getInt24 = function() {
var rval = (
this.data.charCodeAt(this.read) << 16 ^
this.data.charCodeAt(this.read + 1) << 8 ^
this.data.charCodeAt(this.read + 2));
this.read += 3;
return rval;
};
/**
* Gets a uint32 from this buffer in big-endian order and advances the read
* pointer by 4.
*
* @return the word.
*/
util.ByteBuffer.prototype.getInt32 = function() {
var rval = (
this.data.charCodeAt(this.read) << 24 ^
this.data.charCodeAt(this.read + 1) << 16 ^
this.data.charCodeAt(this.read + 2) << 8 ^
this.data.charCodeAt(this.read + 3));
this.read += 4;
return rval;
};
/**
* Gets a uint16 from this buffer in little-endian order and advances the read
* pointer by 2.
*
* @return the uint16.
*/
util.ByteBuffer.prototype.getInt16Le = function() {
var rval = (
this.data.charCodeAt(this.read) ^
this.data.charCodeAt(this.read + 1) << 8);
this.read += 2;
return rval;
};
/**
* Gets a uint24 from this buffer in little-endian order and advances the read
* pointer by 3.
*
* @return the uint24.
*/
util.ByteBuffer.prototype.getInt24Le = function() {
var rval = (
this.data.charCodeAt(this.read) ^
this.data.charCodeAt(this.read + 1) << 8 ^
this.data.charCodeAt(this.read + 2) << 16);
this.read += 3;
return rval;
};
/**
* Gets a uint32 from this buffer in little-endian order and advances the read
* pointer by 4.
*
* @return the word.
*/
util.ByteBuffer.prototype.getInt32Le = function() {
var rval = (
this.data.charCodeAt(this.read) ^
this.data.charCodeAt(this.read + 1) << 8 ^
this.data.charCodeAt(this.read + 2) << 16 ^
this.data.charCodeAt(this.read + 3) << 24);
this.read += 4;
return rval;
};
/**
* Gets an n-bit integer from this buffer in big-endian order and advances the
* read pointer by n/8.
*
* @param n the number of bits in the integer.
*
* @return the integer.
*/
util.ByteBuffer.prototype.getInt = function(n) {
var rval = 0;
do {
rval = (rval << n) + this.data.charCodeAt(this.read++);
n -= 8;
}
while(n > 0);
return rval;
};
/**
* Reads bytes out into a UTF-8 string and clears them from the buffer.
*
* @param count the number of bytes to read, undefined or null for all.
*
* @return a UTF-8 string of bytes.
*/
util.ByteBuffer.prototype.getBytes = function(count) {
var rval;
if(count) {
// read count bytes
count = Math.min(this.length(), count);
rval = this.data.slice(this.read, this.read + count);
this.read += count;
}
else if(count === 0) {
rval = '';
}
else {
// read all bytes, optimize to only copy when needed
rval = (this.read === 0) ? this.data : this.data.slice(this.read);
this.clear();
}
return rval;
};
/**
* Gets a UTF-8 encoded string of the bytes from this buffer without modifying
* the read pointer.
*
* @param count the number of bytes to get, omit to get all.
*
* @return a string full of UTF-8 encoded characters.
*/
util.ByteBuffer.prototype.bytes = function(count) {
return (typeof(count) === 'undefined' ?
this.data.slice(this.read) :
this.data.slice(this.read, this.read + count));
};
/**
* Gets a byte at the given index without modifying the read pointer.
*
* @param i the byte index.
*
* @return the byte.
*/
util.ByteBuffer.prototype.at = function(i) {
return this.data.charCodeAt(this.read + i);
};
/**
* Puts a byte at the given index without modifying the read pointer.
*
* @param i the byte index.
* @param b the byte to put.
*/
util.ByteBuffer.prototype.setAt = function(i, b) {
this.data = this.data.substr(0, this.read + i) +
String.fromCharCode(b) +
this.data.substr(this.read + i + 1);
};
/**
* Gets the last byte without modifying the read pointer.
*
* @return the last byte.
*/
util.ByteBuffer.prototype.last = function() {
return this.data.charCodeAt(this.data.length - 1);
};
/**
* Creates a copy of this buffer.
*
* @return the copy.
*/
util.ByteBuffer.prototype.copy = function() {
var c = util.createBuffer(this.data);
c.read = this.read;
return c;
};
/**
* Compacts this buffer.
*/
util.ByteBuffer.prototype.compact = function() {
if(this.read > 0) {
this.data = this.data.slice(this.read);
this.read = 0;
}
};
/**
* Clears this buffer.
*/
util.ByteBuffer.prototype.clear = function() {
this.data = '';
this.read = 0;
};
/**
* Shortens this buffer by triming bytes off of the end of this buffer.
*
* @param count the number of bytes to trim off.
*/
util.ByteBuffer.prototype.truncate = function(count) {
var len = Math.max(0, this.length() - count);
this.data = this.data.substr(this.read, len);
this.read = 0;
};
/**
* Converts this buffer to a hexadecimal string.
*
* @return a hexadecimal string.
*/
util.ByteBuffer.prototype.toHex = function() {
var rval = '';
for(var i = this.read; i < this.data.length; ++i) {
var b = this.data.charCodeAt(i);
if(b < 16) {
rval += '0';
}
rval += b.toString(16);
}
return rval;
};
/**
* Converts this buffer to a UTF-16 string (standard JavaScript string).
*
* @return a UTF-16 string.
*/
util.ByteBuffer.prototype.toString = function() {
return util.decodeUtf8(this.bytes());
};
/**
* Creates a buffer that stores bytes. A value may be given to put into the
* buffer that is either a string of bytes or a UTF-16 string that will
* be encoded using UTF-8 (to do the latter, specify 'utf8' as the encoding).
*
* @param [input] the bytes to wrap (as a string) or a UTF-16 string to encode
* as UTF-8.
* @param [encoding] (default: 'raw', other: 'utf8').
*/
util.createBuffer = function(input, encoding) {
encoding = encoding || 'raw';
if(input !== undefined && encoding === 'utf8') {
input = util.encodeUtf8(input);
}
return new util.ByteBuffer(input);
};
/**
* Fills a string with a particular value. If you want the string to be a byte
* string, pass in String.fromCharCode(theByte).
*
* @param c the character to fill the string with, use String.fromCharCode
* to fill the string with a byte value.
* @param n the number of characters of value c to fill with.
*
* @return the filled string.
*/
util.fillString = function(c, n) {
var s = '';
while(n > 0) {
if(n & 1) {
s += c;
}
n >>>= 1;
if(n > 0) {
c += c;
}
}
return s;
};
/**
* Performs a per byte XOR between two byte strings and returns the result as a
* string of bytes.
*
* @param s1 first string of bytes.
* @param s2 second string of bytes.
* @param n the number of bytes to XOR.
*
* @return the XOR'd result.
*/
util.xorBytes = function(s1, s2, n) {
var s3 = '';
var b = '';
var t = '';
var i = 0;
var c = 0;
for(; n > 0; --n, ++i) {
b = s1.charCodeAt(i) ^ s2.charCodeAt(i);
if(c >= 10) {
s3 += t;
t = '';
c = 0;
}
t += String.fromCharCode(b);
++c;
}
s3 += t;
return s3;
};
/**
* Converts a hex string into a UTF-8 string of bytes.
*
* @param hex the hexadecimal string to convert.
*
* @return the string of bytes.
*/
util.hexToBytes = function(hex) {
var rval = '';
var i = 0;
if(hex.length & 1 == 1) {
// odd number of characters, convert first character alone
i = 1;
rval += String.fromCharCode(parseInt(hex[0], 16));
}
// convert 2 characters (1 byte) at a time
for(; i < hex.length; i += 2) {
rval += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return rval;
};
/**
* Converts a UTF-8 byte string into a string of hexadecimal characters.
*
* @param bytes the byte string to convert.
*
* @return the string of hexadecimal characters.
*/
util.bytesToHex = function(bytes) {
return util.createBuffer(bytes).toHex();
};
/**
* Converts an 32-bit integer to 4-big-endian byte string.
*
* @param i the integer.
*
* @return the byte string.
*/
util.int32ToBytes = function(i) {
return (
String.fromCharCode(i >> 24 & 0xFF) +
String.fromCharCode(i >> 16 & 0xFF) +
String.fromCharCode(i >> 8 & 0xFF) +
String.fromCharCode(i & 0xFF));
};
// base64 characters, reverse mapping
var _base64 =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var _base64Idx = [
/*43 -43 = 0*/
/*'+', 1, 2, 3,'/' */
62, -1, -1, -1, 63,
/*'0','1','2','3','4','5','6','7','8','9' */
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
/*15, 16, 17,'=', 19, 20, 21 */
-1, -1, -1, 64, -1, -1, -1,
/*65 - 43 = 22*/
/*'A','B','C','D','E','F','G','H','I','J','K','L','M', */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
/*'N','O','P','Q','R','S','T','U','V','W','X','Y','Z' */
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
/*91 - 43 = 48 */
/*48, 49, 50, 51, 52, 53 */
-1, -1, -1, -1, -1, -1,
/*97 - 43 = 54*/
/*'a','b','c','d','e','f','g','h','i','j','k','l','m' */
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
/*'n','o','p','q','r','s','t','u','v','w','x','y','z' */
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
];
/**
* Base64 encodes a UTF-8 string of bytes.
*
* @param input the UTF-8 string of bytes to encode.
* @param maxline the maximum number of encoded bytes per line to use,
* defaults to none.
*
* @return the base64-encoded output.
*/
util.encode64 = function(input, maxline) {
var line = '';
var output = '';
var chr1, chr2, chr3;
var i = 0;
while(i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
// encode 4 character group
line += _base64.charAt(chr1 >> 2);
line += _base64.charAt(((chr1 & 3) << 4) | (chr2 >> 4));
if(isNaN(chr2)) {
line += '==';
}
else {
line += _base64.charAt(((chr2 & 15) << 2) | (chr3 >> 6));
line += isNaN(chr3) ? '=' : _base64.charAt(chr3 & 63);
}
if(maxline && line.length > maxline) {
output += line.substr(0, maxline) + '\r\n';
line = line.substr(maxline);
}
}
output += line;
return output;
};
/**
* Base64 decodes a string into a UTF-8 string of bytes.
*
* @param input the base64-encoded input.
*
* @return the raw bytes.
*/
util.decode64 = function(input) {
// remove all non-base64 characters
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
var output = '';
var enc1, enc2, enc3, enc4;
var i = 0;
while(i < input.length) {
enc1 = _base64Idx[input.charCodeAt(i++) - 43];
enc2 = _base64Idx[input.charCodeAt(i++) - 43];
enc3 = _base64Idx[input.charCodeAt(i++) - 43];
enc4 = _base64Idx[input.charCodeAt(i++) - 43];
output += String.fromCharCode((enc1 << 2) | (enc2 >> 4));
if(enc3 !== 64) {
// decoded at least 2 bytes
output += String.fromCharCode(((enc2 & 15) << 4) | (enc3 >> 2));
if(enc4 !== 64) {
// decoded 3 bytes
output += String.fromCharCode(((enc3 & 3) << 6) | enc4);
}
}
}
return output;
};
/**
* UTF-8 encodes the given UTF-16 encoded string (a standard JavaScript
* string). Non-ASCII characters will be encoded as multiple bytes according
* to UTF-8.
*
* @param str the string to encode.
*
* @return the UTF-8 encoded string.
*/
util.encodeUtf8 = function(str) {
return unescape(encodeURIComponent(str));
};
/**
* Decodes a UTF-8 encoded string into a UTF-16 string.
*
* @param str the string to encode.
*
* @return the UTF-16 encoded string (standard JavaScript string).
*/
util.decodeUtf8 = function(str) {
return decodeURIComponent(escape(str));
};
/**
* Deflates the given data using a flash interface.
*
* @param api the flash interface.
* @param bytes the data.
* @param raw true to return only raw deflate data, false to include zlib
* header and trailer.
*
* @return the deflated data as a string.
*/
util.deflate = function(api, bytes, raw) {
bytes = util.decode64(api.deflate(util.encode64(bytes)).rval);
// strip zlib header and trailer if necessary
if(raw) {
// zlib header is 2 bytes (CMF,FLG) where FLG indicates that
// there is a 4-byte DICT (alder-32) block before the data if
// its 5th bit is set
var start = 2;
var flg = bytes.charCodeAt(1);
if(flg & 0x20) {
start = 6;
}
// zlib trailer is 4 bytes of adler-32
bytes = bytes.substring(start, bytes.length - 4);
}
return bytes;
};
/**
* Inflates the given data using a flash interface.
*
* @param api the flash interface.
* @param bytes the data.
* @param raw true if the incoming data has no zlib header or trailer and is
* raw DEFLATE data.
*
* @return the inflated data as a string, null on error.
*/
util.inflate = function(api, bytes, raw) {
// TODO: add zlib header and trailer if necessary/possible
var rval = api.inflate(util.encode64(bytes)).rval;
return (rval === null) ? null : util.decode64(rval);
};
/**
* Sets a storage object.
*
* @param api the storage interface.
* @param id the storage ID to use.
* @param obj the storage object, null to remove.
*/
var _setStorageObject = function(api, id, obj) {
if(!api) {
throw {
message: 'WebStorage not available.'
};
}
var rval;
if(obj === null) {
rval = api.removeItem(id);
}
else {
// json-encode and base64-encode object
obj = util.encode64(JSON.stringify(obj));
rval = api.setItem(id, obj);
}
// handle potential flash error
if(typeof(rval) !== 'undefined' && rval.rval !== true) {
throw rval.error;
}
};
/**
* Gets a storage object.
*
* @param api the storage interface.
* @param id the storage ID to use.
*
* @return the storage object entry or null if none exists.
*/
var _getStorageObject = function(api, id) {
if(!api) {
throw {
message: 'WebStorage not available.'
};
}
// get the existing entry
var rval = api.getItem(id);
/* Note: We check api.init because we can't do (api == localStorage)
on IE because of "Class doesn't support Automation" exception. Only
the flash api has an init method so this works too, but we need a
better solution in the future. */
// flash returns item wrapped in an object, handle special case
if(api.init) {
if(rval.rval === null) {
if(rval.error) {
throw rval.error;
}
// no error, but also no item
rval = null;
}
else {
rval = rval.rval;
}
}
// handle decoding
if(rval !== null) {
// base64-decode and json-decode data
rval = JSON.parse(util.decode64(rval));
}
return rval;
};
/**
* Stores an item in local storage.
*
* @param api the storage interface.
* @param id the storage ID to use.
* @param key the key for the item.
* @param data the data for the item (any javascript object/primitive).
*/
var _setItem = function(api, id, key, data) {
// get storage object
var obj = _getStorageObject(api, id);
if(obj === null) {
// create a new storage object
obj = {};
}
// update key
obj[key] = data;
// set storage object
_setStorageObject(api, id, obj);
};
/**
* Gets an item from local storage.
*
* @param api the storage interface.
* @param id the storage ID to use.
* @param key the key for the item.
*
* @return the item.
*/
var _getItem = function(api, id, key) {
// get storage object
var rval = _getStorageObject(api, id);
if(rval !== null) {
// return data at key
rval = (key in rval) ? rval[key] : null;
}
return rval;
};
/**
* Removes an item from local storage.
*
* @param api the storage interface.
* @param id the storage ID to use.
* @param key the key for the item.
*/
var _removeItem = function(api, id, key) {
// get storage object
var obj = _getStorageObject(api, id);
if(obj !== null && key in obj) {
// remove key
delete obj[key];
// see if entry has no keys remaining
var empty = true;
for(var prop in obj) {
empty = false;
break;
}
if(empty) {
// remove entry entirely if no keys are left
obj = null;
}
// set storage object
_setStorageObject(api, id, obj);
}
};
/**
* Clears the local disk storage identified by the given ID.
*
* @param api the storage interface.
* @param id the storage ID to use.
*/
var _clearItems = function(api, id) {
_setStorageObject(api, id, null);
};
/**
* Calls a storage function.
*
* @param func the function to call.
* @param args the arguments for the function.
* @param location the location argument.
*
* @return the return value from the function.
*/
var _callStorageFunction = function(func, args, location) {
var rval = null;
// default storage types
if(typeof(location) === 'undefined') {
location = ['web', 'flash'];
}
// apply storage types in order of preference
var type;
var done = false;
var exception = null;
for(var idx in location) {
type = location[idx];
try {
if(type === 'flash' || type === 'both') {
if(args[0] === null) {
throw {
message: 'Flash local storage not available.'
};
}
else {
rval = func.apply(this, args);
done = (type === 'flash');
}
}
if(type === 'web' || type === 'both') {
args[0] = localStorage;
rval = func.apply(this, args);
done = true;
}
}
catch(ex) {
exception = ex;
}
if(done) {
break;
}
}
if(!done) {
throw exception;
}
return rval;
};
/**
* Stores an item on local disk.
*
* The available types of local storage include 'flash', 'web', and 'both'.
*
* The type 'flash' refers to flash local storage (SharedObject). In order
* to use flash local storage, the 'api' parameter must be valid. The type
* 'web' refers to WebStorage, if supported by the browser. The type 'both'
* refers to storing using both 'flash' and 'web', not just one or the
* other.
*
* The location array should list the storage types to use in order of
* preference:
*
* ['flash']: flash only storage
* ['web']: web only storage
* ['both']: try to store in both
* ['flash','web']: store in flash first, but if not available, 'web'
* ['web','flash']: store in web first, but if not available, 'flash'
*
* The location array defaults to: ['web', 'flash']
*
* @param api the flash interface, null to use only WebStorage.
* @param id the storage ID to use.
* @param key the key for the item.
* @param data the data for the item (any javascript object/primitive).
* @param location an array with the preferred types of storage to use.
*/
util.setItem = function(api, id, key, data, location) {
_callStorageFunction(_setItem, arguments, location);
};
/**
* Gets an item on local disk.
*
* Set setItem() for details on storage types.
*
* @param api the flash interface, null to use only WebStorage.
* @param id the storage ID to use.
* @param key the key for the item.
* @param location an array with the preferred types of storage to use.
*
* @return the item.
*/
util.getItem = function(api, id, key, location) {
return _callStorageFunction(_getItem, arguments, location);
};
/**
* Removes an item on local disk.
*
* Set setItem() for details on storage types.
*
* @param api the flash interface.
* @param id the storage ID to use.
* @param key the key for the item.
* @param location an array with the preferred types of storage to use.
*/
util.removeItem = function(api, id, key, location) {
_callStorageFunction(_removeItem, arguments, location);
};
/**
* Clears the local disk storage identified by the given ID.
*
* Set setItem() for details on storage types.
*
* @param api the flash interface if flash is available.
* @param id the storage ID to use.
* @param location an array with the preferred types of storage to use.
*/
util.clearItems = function(api, id, location) {
_callStorageFunction(_clearItems, arguments, location);
};
/**
* Parses the scheme, host, and port from an http(s) url.
*
* @param str the url string.
*
* @return the parsed url object or null if the url is invalid.
*/
util.parseUrl = function(str) {
// FIXME: this regex looks a bit broken
var regex = /^(https?):\/\/([^:&^\/]*):?(\d*)(.*)$/g;
regex.lastIndex = 0;
var m = regex.exec(str);
var url = (m === null) ? null : {
full: str,
scheme: m[1],
host: m[2],
port: m[3],
path: m[4]
};
if(url) {
url.fullHost = url.host;
if(url.port) {
if(url.port !== 80 && url.scheme === 'http') {
url.fullHost += ':' + url.port;
}
else if(url.port !== 443 && url.scheme === 'https') {
url.fullHost += ':' + url.port;
}
}
else if(url.scheme === 'http') {
url.port = 80;
}
else if(url.scheme === 'https') {
url.port = 443;
}
url.full = url.scheme + '://' + url.fullHost;
}
return url;
};
/* Storage for query variables */
var _queryVariables = null;
/**
* Returns the window location query variables. Query is parsed on the first
* call and the same object is returned on subsequent calls. The mapping
* is from keys to an array of values. Parameters without values will have
* an object key set but no value added to the value array. Values are
* unescaped.
*
* ...?k1=v1&k2=v2:
* {
* "k1": ["v1"],
* "k2": ["v2"]
* }
*
* ...?k1=v1&k1=v2:
* {
* "k1": ["v1", "v2"]
* }
*
* ...?k1=v1&k2:
* {
* "k1": ["v1"],
* "k2": []
* }
*
* ...?k1=v1&k1:
* {
* "k1": ["v1"]
* }
*
* ...?k1&k1:
* {
* "k1": []
* }
*
* @param query the query string to parse (optional, default to cached
* results from parsing window location search query).
*
* @return object mapping keys to variables.
*/
util.getQueryVariables = function(query) {
var parse = function(q) {
var rval = {};
var kvpairs = q.split('&');
for(var i = 0; i < kvpairs.length; i++) {
var pos = kvpairs[i].indexOf('=');
var key;
var val;
if(pos > 0) {
key = kvpairs[i].substring(0,pos);
val = kvpairs[i].substring(pos+1);
}
else {
key = kvpairs[i];
val = null;
}
if(!(key in rval)) {
rval[key] = [];
}
if(val !== null) {
rval[key].push(unescape(val));
}
}
return rval;
};
var rval;
if(typeof(query) === 'undefined') {
// set cached variables if needed
if(_queryVariables === null) {
if(typeof(window) === 'undefined') {
// no query variables available
_queryVariables = {};
}
else {
// parse window search query
_queryVariables = parse(window.location.search.substring(1));
}
}
rval = _queryVariables;
}
else {
// parse given query
rval = parse(query);
}
return rval;
};
/**
* Parses a fragment into a path and query. This method will take a URI
* fragment and break it up as if it were the main URI. For example:
* /bar/baz?a=1&b=2
* results in:
* {
* path: ["bar", "baz"],
* query: {"k1": ["v1"], "k2": ["v2"]}
* }
*
* @return object with a path array and query object.
*/
util.parseFragment = function(fragment) {
// default to whole fragment
var fp = fragment;
var fq = '';
// split into path and query if possible at the first '?'
var pos = fragment.indexOf('?');
if(pos > 0) {
fp = fragment.substring(0,pos);
fq = fragment.substring(pos+1);
}
// split path based on '/' and ignore first element if empty
var path = fp.split('/');
if(path.length > 0 && path[0] === '') {
path.shift();
}
// convert query into object
var query = (fq === '') ? {} : util.getQueryVariables(fq);
return {
pathString: fp,
queryString: fq,
path: path,
query: query
};
};
/**
* Makes a request out of a URI-like request string. This is intended to
* be used where a fragment id (after a URI '#') is parsed as a URI with
* path and query parts. The string should have a path beginning and
* delimited by '/' and optional query parameters following a '?'. The
* query should be a standard URL set of key value pairs delimited by
* '&'. For backwards compatibility the initial '/' on the path is not
* required. The request object has the following API, (fully described
* in the method code):
* {
* path: <the path string part>.
* query: <the query string part>,
* getPath(i): get part or all of the split path array,
* getQuery(k, i): get part or all of a query key array,
* getQueryLast(k, _default): get last element of a query key array.
* }
*
* @return object with request parameters.
*/
util.makeRequest = function(reqString) {
var frag = util.parseFragment(reqString);
var req = {
// full path string
path: frag.pathString,
// full query string
query: frag.queryString,
/**
* Get path or element in path.
*
* @param i optional path index.
*
* @return path or part of path if i provided.
*/
getPath: function(i) {
return (typeof(i) === 'undefined') ? frag.path : frag.path[i];
},
/**
* Get query, values for a key, or value for a key index.
*
* @param k optional query key.
* @param i optional query key index.
*
* @return query, values for a key, or value for a key index.
*/
getQuery: function(k, i) {
var rval;
if(typeof(k) === 'undefined') {
rval = frag.query;
}
else {
rval = frag.query[k];
if(rval && typeof(i) !== 'undefined')
{
rval = rval[i];
}
}
return rval;
},
getQueryLast: function(k, _default) {
var rval;
var vals = req.getQuery(k);
if(vals) {
rval = vals[vals.length - 1];
}
else {
rval = _default;
}
return rval;
}
};
return req;
};
/**
* Makes a URI out of a path, an object with query parameters, and a
* fragment. Uses jQuery.param() internally for query string creation.
* If the path is an array, it will be joined with '/'.
*
* @param path string path or array of strings.
* @param query object with query parameters. (optional)
* @param fragment fragment string. (optional)
*
* @return string object with request parameters.
*/
util.makeLink = function(path, query, fragment) {
// join path parts if needed
path = jQuery.isArray(path) ? path.join('/') : path;
var qstr = jQuery.param(query || {});
fragment = fragment || '';
return path +
((qstr.length > 0) ? ('?' + qstr) : '') +
((fragment.length > 0) ? ('#' + fragment) : '');
};
/**
* Follows a path of keys deep into an object hierarchy and set a value.
* If a key does not exist or it's value is not an object, create an
* object in it's place. This can be destructive to a object tree if
* leaf nodes are given as non-final path keys.
* Used to avoid exceptions from missing parts of the path.
*
* @param object the starting object.
* @param keys an array of string keys.
* @param value the value to set.
*/
util.setPath = function(object, keys, value) {
// need to start at an object
if(typeof(object) === 'object' && object !== null) {
var i = 0;
var len = keys.length;
while(i < len) {
var next = keys[i++];
if(i == len) {
// last
object[next] = value;
}
else {
// more
var hasNext = (next in object);
if(!hasNext ||
(hasNext && typeof(object[next]) !== 'object') ||
(hasNext && object[next] === null)) {
object[next] = {};
}
object = object[next];
}
}
}
};
/**
* Follows a path of keys deep into an object hierarchy and return a value.
* If a key does not exist, create an object in it's place.
* Used to avoid exceptions from missing parts of the path.
*
* @param object the starting object.
* @param keys an array of string keys.
* @param _default value to return if path not found.
*
* @return the value at the path if found, else default if given, else
* undefined.
*/
util.getPath = function(object, keys, _default) {
var i = 0;
var len = keys.length;
var hasNext = true;
while(hasNext && i < len &&
typeof(object) === 'object' && object !== null) {
var next = keys[i++];
hasNext = next in object;
if(hasNext) {
object = object[next];
}
}
return (hasNext ? object : _default);
};
/**
* Follow a path of keys deep into an object hierarchy and delete the
* last one. If a key does not exist, do nothing.
* Used to avoid exceptions from missing parts of the path.
*
* @param object the starting object.
* @param keys an array of string keys.
*/
util.deletePath = function(object, keys) {
// need to start at an object
if(typeof(object) === 'object' && object !== null) {
var i = 0;
var len = keys.length;
while(i < len) {
var next = keys[i++];
if(i == len) {
// last
delete object[next];
}
else {
// more
if(!(next in object) ||
(typeof(object[next]) !== 'object') ||
(object[next] === null)) {
break;
}
object = object[next];
}
}
}
};
/**
* Check if an object is empty.
*
* Taken from:
* http://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object-from-json/679937#679937
*
* @param object the object to check.
*/
util.isEmpty = function(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop)) {
return false;
}
}
return true;
};
/**
* Format with simple printf-style interpolation.
*
* %%: literal '%'
* %s,%o: convert next argument into a string.
*
* @param format the string to format.
* @param ... arguments to interpolate into the format string.
*/
util.format = function(format) {
var re = /%./g;
// current match
var match;
// current part
var part;
// current arg index
var argi = 0;
// collected parts to recombine later
var parts = [];
// last index found
var last = 0;
// loop while matches remain
while((match = re.exec(format))) {
part = format.substring(last, re.lastIndex - 2);
// don't add empty strings (ie, parts between %s%s)
if(part.length > 0) {
parts.push(part);
}
last = re.lastIndex;
// switch on % code
var code = match[0][1];
switch(code) {
case 's':
case 'o':
// check if enough arguments were given
if(argi < arguments.length) {
parts.push(arguments[argi++ + 1]);
}
else {
parts.push('<?>');
}
break;
// FIXME: do proper formating for numbers, etc
//case 'f':
//case 'd':
case '%':
parts.push('%');
break;
default:
parts.push('<%' + code + '?>');
}
}
// add trailing part of format string
parts.push(format.substring(last));
return parts.join('');
};
/**
* Formats a number.
*
* http://snipplr.com/view/5945/javascript-numberformat--ported-from-php/
*/
util.formatNumber = function(number, decimals, dec_point, thousands_sep) {
// http://kevin.vanzonneveld.net
// + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfix by: Michael White (http://crestidg.com)
// + bugfix by: Benjamin Lupton
// + bugfix by: Allan Jensen (http://www.winternet.no)
// + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
// * example 1: number_format(1234.5678, 2, '.', '');
// * returns 1: 1234.57
var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
var d = dec_point === undefined ? ',' : dec_point;
var t = thousands_sep === undefined ?
'.' : thousands_sep, s = n < 0 ? '-' : '';
var i = parseInt((n = Math.abs(+n || 0).toFixed(c)), 10) + '';
var j = (i.length > 3) ? i.length % 3 : 0;
return s + (j ? i.substr(0, j) + t : '') +
i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + t) +
(c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');
};
/**
* Formats a byte size.
*
* http://snipplr.com/view/5949/format-humanize-file-byte-size-presentation-in-javascript/
*/
util.formatSize = function(size) {
if(size >= 1073741824) {
size = util.formatNumber(size / 1073741824, 2, '.', '') + ' GiB';
}
else if(size >= 1048576) {
size = util.formatNumber(size / 1048576, 2, '.', '') + ' MiB';
}
else if(size >= 1024) {
size = util.formatNumber(size / 1024, 0) + ' KiB';
}
else {
size = util.formatNumber(size, 0) + ' bytes';
}
return size;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'util';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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.
*/
md.start = function() {
md.messageLength = 0;
_input = forge.util.createBuffer();
_state = {
h0: 0x67452301,
h1: 0xEFCDAB89,
h2: 0x98BADCFE,
h3: 0x10325476
};
};
// 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').
*/
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();
}
};
/**
* 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';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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.
*/
md.start = function() {
md.messageLength = 0;
_input = forge.util.createBuffer();
_state = {
h0: 0x67452301,
h1: 0xEFCDAB89,
h2: 0x98BADCFE,
h3: 0x10325476,
h4: 0xC3D2E1F0
};
};
// 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').
*/
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();
}
};
/**
* 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';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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.
*/
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
};
};
// 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').
*/
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();
}
};
/**
* 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';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* Advanced Encryption Standard (AES) Cipher-Block Chaining implementation.
*
* This implementation is based on the public domain library 'jscrypto' which
* was written by:
*
* Emily Stark (estark@stanford.edu)
* Mike Hamburg (mhamburg@stanford.edu)
* Dan Boneh (dabo@cs.stanford.edu)
*
* Parts of this code are based on the OpenSSL implementation of AES:
* http://www.openssl.org
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
var init = false; // not yet initialized
var Nb = 4; // number of words comprising the state (AES = 4)
var sbox; // non-linear substitution table used in key expansion
var isbox; // inversion of sbox
var rcon; // round constant word array
var mix; // mix-columns table
var imix; // inverse mix-columns table
/**
* Performs initialization, ie: precomputes tables to optimize for speed.
*
* One way to understand how AES works is to imagine that 'addition' and
* 'multiplication' are interfaces that require certain mathematical
* properties to hold true (ie: they are associative) but they might have
* different implementations and produce different kinds of results ...
* provided that their mathematical properties remain true. AES defines
* its own methods of addition and multiplication but keeps some important
* properties the same, ie: associativity and distributivity. The
* explanation below tries to shed some light on how AES defines addition
* and multiplication of bytes and 32-bit words in order to perform its
* encryption and decryption algorithms.
*
* The basics:
*
* The AES algorithm views bytes as binary representations of polynomials
* that have either 1 or 0 as the coefficients. It defines the addition
* or subtraction of two bytes as the XOR operation. It also defines the
* multiplication of two bytes as a finite field referred to as GF(2^8)
* (Note: 'GF' means "Galois Field" which is a field that contains a finite
* number of elements so GF(2^8) has 256 elements).
*
* This means that any two bytes can be represented as binary polynomials;
* when they multiplied together and modularly reduced by an irreducible
* polynomial of the 8th degree, the results are the field GF(2^8). The
* specific irreducible polynomial that AES uses in hexadecimal is 0x11b.
* This multiplication is associative with 0x01 as the identity:
*
* (b * 0x01 = GF(b, 0x01) = b).
*
* The operation GF(b, 0x02) can be performed at the byte level by left
* shifting b once and then XOR'ing it (to perform the modular reduction)
* with 0x11b if b is >= 128. Repeated application of the multiplication
* of 0x02 can be used to implement the multiplication of any two bytes.
*
* For instance, multiplying 0x57 and 0x13, denoted as GF(0x57, 0x13), can
* be performed by factoring 0x13 into 0x01, 0x02, and 0x10. Then these
* factors can each be multiplied by 0x57 and then added together. To do
* the multiplication, values for 0x57 multiplied by each of these 3 factors
* can be precomputed and stored in a table. To add them, the values from
* the table are XOR'd together.
*
* AES also defines addition and multiplication of words, that is 4-byte
* numbers represented as polynomials of 3 degrees where the coefficients
* are the values of the bytes.
*
* The word [a0, a1, a2, a3] is a polynomial a3x^3 + a2x^2 + a1x + a0.
*
* Addition is performed by XOR'ing like powers of x. Multiplication
* is performed in two steps, the first is an algebriac expansion as
* you would do normally (where addition is XOR). But the result is
* a polynomial larger than 3 degrees and thus it cannot fit in a word. So
* next the result is modularly reduced by an AES-specific polynomial of
* degree 4 which will always produce a polynomial of less than 4 degrees
* such that it will fit in a word. In AES, this polynomial is x^4 + 1.
*
* The modular product of two polynomials 'a' and 'b' is thus:
*
* d(x) = d3x^3 + d2x^2 + d1x + d0
* with
* d0 = GF(a0, b0) ^ GF(a3, b1) ^ GF(a2, b2) ^ GF(a1, b3)
* d1 = GF(a1, b0) ^ GF(a0, b1) ^ GF(a3, b2) ^ GF(a2, b3)
* d2 = GF(a2, b0) ^ GF(a1, b1) ^ GF(a0, b2) ^ GF(a3, b3)
* d3 = GF(a3, b0) ^ GF(a2, b1) ^ GF(a1, b2) ^ GF(a0, b3)
*
* As a matrix:
*
* [d0] = [a0 a3 a2 a1][b0]
* [d1] [a1 a0 a3 a2][b1]
* [d2] [a2 a1 a0 a3][b2]
* [d3] [a3 a2 a1 a0][b3]
*
* Special polynomials defined by AES (0x02 == {02}):
* a(x) = {03}x^3 + {01}x^2 + {01}x + {02}
* a^-1(x) = {0b}x^3 + {0d}x^2 + {09}x + {0e}.
*
* These polynomials are used in the MixColumns() and InverseMixColumns()
* operations, respectively, to cause each element in the state to affect
* the output (referred to as diffusing).
*
* RotWord() uses: a0 = a1 = a2 = {00} and a3 = {01}, which is the
* polynomial x3.
*
* The ShiftRows() method modifies the last 3 rows in the state (where
* the state is 4 words with 4 bytes per word) by shifting bytes cyclically.
* The 1st byte in the second row is moved to the end of the row. The 1st
* and 2nd bytes in the third row are moved to the end of the row. The 1st,
* 2nd, and 3rd bytes are moved in the fourth row.
*
* More details on how AES arithmetic works:
*
* In the polynomial representation of binary numbers, XOR performs addition
* and subtraction and multiplication in GF(2^8) denoted as GF(a, b)
* corresponds with the multiplication of polynomials modulo an irreducible
* polynomial of degree 8. In other words, for AES, GF(a, b) will multiply
* polynomial 'a' with polynomial 'b' and then do a modular reduction by
* an AES-specific irreducible polynomial of degree 8.
*
* A polynomial is irreducible if its only divisors are one and itself. For
* the AES algorithm, this irreducible polynomial is:
*
* m(x) = x^8 + x^4 + x^3 + x + 1,
*
* or {01}{1b} in hexadecimal notation, where each coefficient is a bit:
* 100011011 = 283 = 0x11b.
*
* For example, GF(0x57, 0x83) = 0xc1 because
*
* 0x57 = 87 = 01010111 = x^6 + x^4 + x^2 + x + 1
* 0x85 = 131 = 10000101 = x^7 + x + 1
*
* (x^6 + x^4 + x^2 + x + 1) * (x^7 + x + 1)
* = x^13 + x^11 + x^9 + x^8 + x^7 +
* x^7 + x^5 + x^3 + x^2 + x +
* x^6 + x^4 + x^2 + x + 1
* = x^13 + x^11 + x^9 + x^8 + x^6 + x^5 + x^4 + x^3 + 1 = y
* y modulo (x^8 + x^4 + x^3 + x + 1)
* = x^7 + x^6 + 1.
*
* The modular reduction by m(x) guarantees the result will be a binary
* polynomial of less than degree 8, so that it can fit in a byte.
*
* The operation to multiply a binary polynomial b with x (the polynomial
* x in binary representation is 00000010) is:
*
* b_7x^8 + b_6x^7 + b_5x^6 + b_4x^5 + b_3x^4 + b_2x^3 + b_1x^2 + b_0x^1
*
* To get GF(b, x) we must reduce that by m(x). If b_7 is 0 (that is the
* most significant bit is 0 in b) then the result is already reduced. If
* it is 1, then we can reduce it by subtracting m(x) via an XOR.
*
* It follows that multiplication by x (00000010 or 0x02) can be implemented
* by performing a left shift followed by a conditional bitwise XOR with
* 0x1b. This operation on bytes is denoted by xtime(). Multiplication by
* higher powers of x can be implemented by repeated application of xtime().
*
* By adding intermediate results, multiplication by any constant can be
* implemented. For instance:
*
* GF(0x57, 0x13) = 0xfe because:
*
* xtime(b) = (b & 128) ? (b << 1 ^ 0x11b) : (b << 1)
*
* Note: We XOR with 0x11b instead of 0x1b because in javascript our
* datatype for b can be larger than 1 byte, so a left shift will not
* automatically eliminate bits that overflow a byte ... by XOR'ing the
* overflow bit with 1 (the extra one from 0x11b) we zero it out.
*
* GF(0x57, 0x02) = xtime(0x57) = 0xae
* GF(0x57, 0x04) = xtime(0xae) = 0x47
* GF(0x57, 0x08) = xtime(0x47) = 0x8e
* GF(0x57, 0x10) = xtime(0x8e) = 0x07
*
* GF(0x57, 0x13) = GF(0x57, (0x01 ^ 0x02 ^ 0x10))
*
* And by the distributive property (since XOR is addition and GF() is
* multiplication):
*
* = GF(0x57, 0x01) ^ GF(0x57, 0x02) ^ GF(0x57, 0x10)
* = 0x57 ^ 0xae ^ 0x07
* = 0xfe.
*/
var initialize = function() {
init = true;
/* Populate the Rcon table. These are the values given by
[x^(i-1),{00},{00},{00}] where x^(i-1) are powers of x (and x = 0x02)
in the field of GF(2^8), where i starts at 1.
rcon[0] = [0x00, 0x00, 0x00, 0x00]
rcon[1] = [0x01, 0x00, 0x00, 0x00] 2^(1-1) = 2^0 = 1
rcon[2] = [0x02, 0x00, 0x00, 0x00] 2^(2-1) = 2^1 = 2
...
rcon[9] = [0x1B, 0x00, 0x00, 0x00] 2^(9-1) = 2^8 = 0x1B
rcon[10] = [0x36, 0x00, 0x00, 0x00] 2^(10-1) = 2^9 = 0x36
We only store the first byte because it is the only one used.
*/
rcon = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36];
// compute xtime table which maps i onto GF(i, 0x02)
var xtime = new Array(256);
for(var i = 0; i < 128; ++i) {
xtime[i] = i << 1;
xtime[i + 128] = (i + 128) << 1 ^ 0x11B;
}
// compute all other tables
sbox = new Array(256);
isbox = new Array(256);
mix = new Array(4);
imix = new Array(4);
for(var i = 0; i < 4; ++i) {
mix[i] = new Array(256);
imix[i] = new Array(256);
}
var e = 0, ei = 0, e2, e4, e8, sx, sx2, me, ime;
for(var i = 0; i < 256; ++i) {
/* We need to generate the SubBytes() sbox and isbox tables so that
we can perform byte substitutions. This requires us to traverse
all of the elements in GF, find their multiplicative inverses,
and apply to each the following affine transformation:
bi' = bi ^ b(i + 4) mod 8 ^ b(i + 5) mod 8 ^ b(i + 6) mod 8 ^
b(i + 7) mod 8 ^ ci
for 0 <= i < 8, where bi is the ith bit of the byte, and ci is the
ith bit of a byte c with the value {63} or {01100011}.
It is possible to traverse every possible value in a Galois field
using what is referred to as a 'generator'. There are many
generators (128 out of 256): 3,5,6,9,11,82 to name a few. To fully
traverse GF we iterate 255 times, multiplying by our generator
each time.
On each iteration we can determine the multiplicative inverse for
the current element.
Suppose there is an element in GF 'e'. For a given generator 'g',
e = g^x. The multiplicative inverse of e is g^(255 - x). It turns
out that if use the inverse of a generator as another generator
it will produce all of the corresponding multiplicative inverses
at the same time. For this reason, we choose 5 as our inverse
generator because it only requires 2 multiplies and 1 add and its
inverse, 82, requires relatively few operations as well.
In order to apply the affine transformation, the multiplicative
inverse 'ei' of 'e' can be repeatedly XOR'd (4 times) with a
bit-cycling of 'ei'. To do this 'ei' is first stored in 's' and
'x'. Then 's' is left shifted and the high bit of 's' is made the
low bit. The resulting value is stored in 's'. Then 'x' is XOR'd
with 's' and stored in 'x'. On each subsequent iteration the same
operation is performed. When 4 iterations are complete, 'x' is
XOR'd with 'c' (0x63) and the transformed value is stored in 'x'.
For example:
s = 01000001
x = 01000001
iteration 1: s = 10000010, x ^= s
iteration 2: s = 00000101, x ^= s
iteration 3: s = 00001010, x ^= s
iteration 4: s = 00010100, x ^= s
x ^= 0x63
This can be done with a loop where s = (s << 1) | (s >> 7). However,
it can also be done by using a single 16-bit (in this case 32-bit)
number 'sx'. Since XOR is an associative operation, we can set 'sx'
to 'ei' and then XOR it with 'sx' left-shifted 1,2,3, and 4 times.
The most significant bits will flow into the high 8 bit positions
and be correctly XOR'd with one another. All that remains will be
to cycle the high 8 bits by XOR'ing them all with the lower 8 bits
afterwards.
At the same time we're populating sbox and isbox we can precompute
the multiplication we'll need to do to do MixColumns() later.
*/
// apply affine transformation
sx = ei ^ (ei << 1) ^ (ei << 2) ^ (ei << 3) ^ (ei << 4);
sx = (sx >> 8) ^ (sx & 255) ^ 0x63;
// update tables
sbox[e] = sx;
isbox[sx] = e;
/* Mixing columns is done using matrix multiplication. The columns
that are to be mixed are each a single word in the current state.
The state has Nb columns (4 columns). Therefore each column is a
4 byte word. So to mix the columns in a single column 'c' where
its rows are r0, r1, r2, and r3, we use the following matrix
multiplication:
[2 3 1 1]*[r0,c]=[r'0,c]
[1 2 3 1] [r1,c] [r'1,c]
[1 1 2 3] [r2,c] [r'2,c]
[3 1 1 2] [r3,c] [r'3,c]
r0, r1, r2, and r3 are each 1 byte of one of the words in the
state (a column). To do matrix multiplication for each mixed
column c' we multiply the corresponding row from the left matrix
with the corresponding column from the right matrix. In total, we
get 4 equations:
r0,c' = 2*r0,c + 3*r1,c + 1*r2,c + 1*r3,c
r1,c' = 1*r0,c + 2*r1,c + 3*r2,c + 1*r3,c
r2,c' = 1*r0,c + 1*r1,c + 2*r2,c + 3*r3,c
r3,c' = 3*r0,c + 1*r1,c + 1*r2,c + 2*r3,c
As usual, the multiplication is as previously defined and the
addition is XOR. In order to optimize mixing columns we can store
the multiplication results in tables. If you think of the whole
column as a word (it might help to visualize by mentally rotating
the equations above by counterclockwise 90 degrees) then you can
see that it would be useful to map the multiplications performed on
each byte (r0, r1, r2, r3) onto a word as well. For instance, we
could map 2*r0,1*r0,1*r0,3*r0 onto a word by storing 2*r0 in the
highest 8 bits and 3*r0 in the lowest 8 bits (with the other two
respectively in the middle). This means that a table can be
constructed that uses r0 as an index to the word. We can do the
same with r1, r2, and r3, creating a total of 4 tables.
To construct a full c', we can just look up each byte of c in
their respective tables and XOR the results together.
Also, to build each table we only have to calculate the word
for 2,1,1,3 for every byte ... which we can do on each iteration
of this loop since we will iterate over every byte. After we have
calculated 2,1,1,3 we can get the results for the other tables
by cycling the byte at the end to the beginning. For instance
we can take the result of table 2,1,1,3 and produce table 3,2,1,1
by moving the right most byte to the left most position just like
how you can imagine the 3 moved out of 2,1,1,3 and to the front
to produce 3,2,1,1.
There is another optimization in that the same multiples of
the current element we need in order to advance our generator
to the next iteration can be reused in performing the 2,1,1,3
calculation. We also calculate the inverse mix column tables,
with e,9,d,b being the inverse of 2,1,1,3.
When we're done, and we need to actually mix columns, the first
byte of each state word should be put through mix[0] (2,1,1,3),
the second through mix[1] (3,2,1,1) and so forth. Then they should
be XOR'd together to produce the fully mixed column.
*/
// calculate mix and imix table values
sx2 = xtime[sx];
e2 = xtime[e];
e4 = xtime[e2];
e8 = xtime[e4];
me =
(sx2 << 24) ^ // 2
(sx << 16) ^ // 1
(sx << 8) ^ // 1
(sx ^ sx2); // 3
ime =
(e2 ^ e4 ^ e8) << 24 ^ // E (14)
(e ^ e8) << 16 ^ // 9
(e ^ e4 ^ e8) << 8 ^ // D (13)
(e ^ e2 ^ e8); // B (11)
// produce each of the mix tables by rotating the 2,1,1,3 value
for(var n = 0; n < 4; ++n) {
mix[n][e] = me;
imix[n][sx] = ime;
// cycle the right most byte to the left most position
// ie: 2,1,1,3 becomes 3,2,1,1
me = me << 24 | me >>> 8;
ime = ime << 24 | ime >>> 8;
}
// get next element and inverse
if(e === 0) {
// 1 is the inverse of 1
e = ei = 1;
}
else {
// e = 2e + 2*2*2*(10e)) = multiply e by 82 (chosen generator)
// ei = ei + 2*2*ei = multiply ei by 5 (inverse generator)
e = e2 ^ xtime[xtime[xtime[e2 ^ e8]]];
ei ^= xtime[xtime[ei]];
}
}
};
/**
* Generates a key schedule using the AES key expansion algorithm.
*
* The AES algorithm takes the Cipher Key, K, and performs a Key Expansion
* routine to generate a key schedule. The Key Expansion generates a total
* of Nb*(Nr + 1) words: the algorithm requires an initial set of Nb words,
* and each of the Nr rounds requires Nb words of key data. The resulting
* key schedule consists of a linear array of 4-byte words, denoted [wi ],
* with i in the range 0 ≤ i < Nb(Nr + 1).
*
* KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk)
* AES-128 (Nb=4, Nk=4, Nr=10)
* AES-192 (Nb=4, Nk=6, Nr=12)
* AES-256 (Nb=4, Nk=8, Nr=14)
* Note: Nr=Nk+6.
*
* Nb is the number of columns (32-bit words) comprising the State (or
* number of bytes in a block). For AES, Nb=4.
*
* @param key the key to schedule (as an array of 32-bit words).
* @param decrypt true to modify the key schedule to decrypt, false not to.
*
* @return the generated key schedule.
*/
var expandKey = function(key, decrypt) {
// copy the key's words to initialize the key schedule
var w = key.slice(0);
/* RotWord() will rotate a word, moving the first byte to the last
byte's position (shifting the other bytes left).
We will be getting the value of Rcon at i / Nk. 'i' will iterate
from Nk to (Nb * Nr+1). Nk = 4 (4 byte key), Nb = 4 (4 words in
a block), Nr = Nk + 6 (10). Therefore 'i' will iterate from
4 to 44 (exclusive). Each time we iterate 4 times, i / Nk will
increase by 1. We use a counter iNk to keep track of this.
*/
// go through the rounds expanding the key
var temp, iNk = 1;
var Nk = w.length;
var Nr1 = Nk + 6 + 1;
var end = Nb * Nr1;
for(var i = Nk; i < end; ++i) {
temp = w[i - 1];
if(i % Nk === 0) {
// temp = SubWord(RotWord(temp)) ^ Rcon[i / Nk]
temp =
sbox[temp >>> 16 & 255] << 24 ^
sbox[temp >>> 8 & 255] << 16 ^
sbox[temp & 255] << 8 ^
sbox[temp >>> 24] ^ (rcon[iNk] << 24);
iNk++;
}
else if(Nk > 6 && (i % Nk == 4)) {
// temp = SubWord(temp)
temp =
sbox[temp >>> 24] << 24 ^
sbox[temp >>> 16 & 255] << 16 ^
sbox[temp >>> 8 & 255] << 8 ^
sbox[temp & 255];
}
w[i] = w[i - Nk] ^ temp;
}
/* When we are updating a cipher block we always use the code path for
encryption whether we are decrypting or not (to shorten code and
simplify the generation of look up tables). However, because there
are differences in the decryption algorithm, other than just swapping
in different look up tables, we must transform our key schedule to
account for these changes:
1. The decryption algorithm gets its key rounds in reverse order.
2. The decryption algorithm adds the round key before mixing columns
instead of afterwards.
We don't need to modify our key schedule to handle the first case,
we can just traverse the key schedule in reverse order when decrypting.
The second case requires a little work.
The tables we built for performing rounds will take an input and then
perform SubBytes() and MixColumns() or, for the decrypt version,
InvSubBytes() and InvMixColumns(). But the decrypt algorithm requires
us to AddRoundKey() before InvMixColumns(). This means we'll need to
apply some transformations to the round key to inverse-mix its columns
so they'll be correct for moving AddRoundKey() to after the state has
had its columns inverse-mixed.
To inverse-mix the columns of the state when we're decrypting we use a
lookup table that will apply InvSubBytes() and InvMixColumns() at the
same time. However, the round key's bytes are not inverse-substituted
in the decryption algorithm. To get around this problem, we can first
substitute the bytes in the round key so that when we apply the
transformation via the InvSubBytes()+InvMixColumns() table, it will
undo our substitution leaving us with the original value that we
want -- and then inverse-mix that value.
This change will correctly alter our key schedule so that we can XOR
each round key with our already transformed decryption state. This
allows us to use the same code path as the encryption algorithm.
We make one more change to the decryption key. Since the decryption
algorithm runs in reverse from the encryption algorithm, we reverse
the order of the round keys to avoid having to iterate over the key
schedule backwards when running the encryption algorithm later in
decryption mode. In addition to reversing the order of the round keys,
we also swap each round key's 2nd and 4th rows. See the comments
section where rounds are performed for more details about why this is
done. These changes are done inline with the other substitution
described above.
*/
if(decrypt) {
var tmp;
var m0 = imix[0];
var m1 = imix[1];
var m2 = imix[2];
var m3 = imix[3];
var wnew = w.slice(0);
var end = w.length;
for(var i = 0, wi = end - Nb; i < end; i += Nb, wi -= Nb) {
// do not sub the first or last round key (round keys are Nb
// words) as no column mixing is performed before they are added,
// but do change the key order
if(i === 0 || i === (end - Nb)) {
wnew[i] = w[wi];
wnew[i + 1] = w[wi + 3];
wnew[i + 2] = w[wi + 2];
wnew[i + 3] = w[wi + 1];
}
else {
// substitute each round key byte because the inverse-mix
// table will inverse-substitute it (effectively cancel the
// substitution because round key bytes aren't sub'd in
// decryption mode) and swap indexes 3 and 1
for(var n = 0; n < Nb; ++n) {
tmp = w[wi + n];
wnew[i + (3&-n)] =
m0[sbox[tmp >>> 24]] ^
m1[sbox[tmp >>> 16 & 255]] ^
m2[sbox[tmp >>> 8 & 255]] ^
m3[sbox[tmp & 255]];
}
}
}
w = wnew;
}
return w;
};
/**
* Updates a single block (16 bytes) using AES. The update will either
* encrypt or decrypt the block.
*
* @param w the key schedule.
* @param input the input block (an array of 32-bit words).
* @param output the updated output block.
* @param decrypt true to decrypt the block, false to encrypt it.
*/
var _updateBlock = function(w, input, output, decrypt) {
/*
Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
begin
byte state[4,Nb]
state = in
AddRoundKey(state, w[0, Nb-1])
for round = 1 step 1 to Nr1
SubBytes(state)
ShiftRows(state)
MixColumns(state)
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
end for
SubBytes(state)
ShiftRows(state)
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
out = state
end
InvCipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
begin
byte state[4,Nb]
state = in
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
for round = Nr-1 step -1 downto 1
InvShiftRows(state)
InvSubBytes(state)
AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
InvMixColumns(state)
end for
InvShiftRows(state)
InvSubBytes(state)
AddRoundKey(state, w[0, Nb-1])
out = state
end
*/
// Encrypt: AddRoundKey(state, w[0, Nb-1])
// Decrypt: AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
var Nr = w.length / 4 - 1;
var m0, m1, m2, m3, sub;
if(decrypt) {
m0 = imix[0];
m1 = imix[1];
m2 = imix[2];
m3 = imix[3];
sub = isbox;
}
else {
m0 = mix[0];
m1 = mix[1];
m2 = mix[2];
m3 = mix[3];
sub = sbox;
}
var a, b, c, d, a2, b2, c2;
a = input[0] ^ w[0];
b = input[decrypt ? 3 : 1] ^ w[1];
c = input[2] ^ w[2];
d = input[decrypt ? 1 : 3] ^ w[3];
var i = 3;
/* In order to share code we follow the encryption algorithm when both
encrypting and decrypting. To account for the changes required in the
decryption algorithm, we use different lookup tables when decrypting
and use a modified key schedule to account for the difference in the
order of transformations applied when performing rounds. We also get
key rounds in reverse order (relative to encryption). */
for(var round = 1; round < Nr; ++round) {
/* As described above, we'll be using table lookups to perform the
column mixing. Each column is stored as a word in the state (the
array 'input' has one column as a word at each index). In order to
mix a column, we perform these transformations on each row in c,
which is 1 byte in each word. The new column for c0 is c'0:
m0 m1 m2 m3
r0,c'0 = 2*r0,c0 + 3*r1,c0 + 1*r2,c0 + 1*r3,c0
r1,c'0 = 1*r0,c0 + 2*r1,c0 + 3*r2,c0 + 1*r3,c0
r2,c'0 = 1*r0,c0 + 1*r1,c0 + 2*r2,c0 + 3*r3,c0
r3,c'0 = 3*r0,c0 + 1*r1,c0 + 1*r2,c0 + 2*r3,c0
So using mix tables where c0 is a word with r0 being its upper
8 bits and r3 being its lower 8 bits:
m0[c0 >> 24] will yield this word: [2*r0,1*r0,1*r0,3*r0]
...
m3[c0 & 255] will yield this word: [1*r3,1*r3,3*r3,2*r3]
Therefore to mix the columns in each word in the state we
do the following (& 255 omitted for brevity):
c'0,r0 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
c'0,r1 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
c'0,r2 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
c'0,r3 = m0[c0 >> 24] ^ m1[c1 >> 16] ^ m2[c2 >> 8] ^ m3[c3]
However, before mixing, the algorithm requires us to perform
ShiftRows(). The ShiftRows() transformation cyclically shifts the
last 3 rows of the state over different offsets. The first row
(r = 0) is not shifted.
s'_r,c = s_r,(c + shift(r, Nb) mod Nb
for 0 < r < 4 and 0 <= c < Nb and
shift(1, 4) = 1
shift(2, 4) = 2
shift(3, 4) = 3.
This causes the first byte in r = 1 to be moved to the end of
the row, the first 2 bytes in r = 2 to be moved to the end of
the row, the first 3 bytes in r = 3 to be moved to the end of
the row:
r1: [c0 c1 c2 c3] => [c1 c2 c3 c0]
r2: [c0 c1 c2 c3] [c2 c3 c0 c1]
r3: [c0 c1 c2 c3] [c3 c0 c1 c2]
We can make these substitutions inline with our column mixing to
generate an updated set of equations to produce each word in the
state (note the columns have changed positions):
c0 c1 c2 c3 => c0 c1 c2 c3
c0 c1 c2 c3 c1 c2 c3 c0 (cycled 1 byte)
c0 c1 c2 c3 c2 c3 c0 c1 (cycled 2 bytes)
c0 c1 c2 c3 c3 c0 c1 c2 (cycled 3 bytes)
Therefore:
c'0 = 2*r0,c0 + 3*r1,c1 + 1*r2,c2 + 1*r3,c3
c'0 = 1*r0,c0 + 2*r1,c1 + 3*r2,c2 + 1*r3,c3
c'0 = 1*r0,c0 + 1*r1,c1 + 2*r2,c2 + 3*r3,c3
c'0 = 3*r0,c0 + 1*r1,c1 + 1*r2,c2 + 2*r3,c3
c'1 = 2*r0,c1 + 3*r1,c2 + 1*r2,c3 + 1*r3,c0
c'1 = 1*r0,c1 + 2*r1,c2 + 3*r2,c3 + 1*r3,c0
c'1 = 1*r0,c1 + 1*r1,c2 + 2*r2,c3 + 3*r3,c0
c'1 = 3*r0,c1 + 1*r1,c2 + 1*r2,c3 + 2*r3,c0
... and so forth for c'2 and c'3. The important distinction is
that the columns are cycling, with c0 being used with the m0
map when calculating c0, but c1 being used with the m0 map when
calculating c1 ... and so forth.
When performing the inverse we transform the mirror image and
skip the bottom row, instead of the top one, and move upwards:
c3 c2 c1 c0 => c0 c3 c2 c1 (cycled 3 bytes) *same as encryption
c3 c2 c1 c0 c1 c0 c3 c2 (cycled 2 bytes)
c3 c2 c1 c0 c2 c1 c0 c3 (cycled 1 byte) *same as encryption
c3 c2 c1 c0 c3 c2 c1 c0
If you compare the resulting matrices for ShiftRows()+MixColumns()
and for InvShiftRows()+InvMixColumns() the 2nd and 4th columns are
different (in encrypt mode vs. decrypt mode). So in order to use
the same code to handle both encryption and decryption, we will
need to do some mapping.
If in encryption mode we let a=c0, b=c1, c=c2, d=c3, and r<N> be
a row number in the state, then the resulting matrix in encryption
mode for applying the above transformations would be:
r1: a b c d
r2: b c d a
r3: c d a b
r4: d a b c
If we did the same in decryption mode we would get:
r1: a d c b
r2: b a d c
r3: c b a d
r4: d c b a
If instead we swap d and b (set b=c3 and d=c1), then we get:
r1: a b c d
r2: d a b c
r3: c d a b
r4: b c d a
Now the 1st and 3rd rows are the same as the encryption matrix. All
we need to do then to make the mapping exactly the same is to swap
the 2nd and 4th rows when in decryption mode. To do this without
having to do it on each iteration, we swapped the 2nd and 4th rows
in the decryption key schedule. We also have to do the swap above
when we first pull in the input and when we set the final output. */
a2 =
m0[a >>> 24] ^
m1[b >>> 16 & 255] ^
m2[c >>> 8 & 255] ^
m3[d & 255] ^ w[++i];
b2 =
m0[b >>> 24] ^
m1[c >>> 16 & 255] ^
m2[d >>> 8 & 255] ^
m3[a & 255] ^ w[++i];
c2 =
m0[c >>> 24] ^
m1[d >>> 16 & 255] ^
m2[a >>> 8 & 255] ^
m3[b & 255] ^ w[++i];
d =
m0[d >>> 24] ^
m1[a >>> 16 & 255] ^
m2[b >>> 8 & 255] ^
m3[c & 255] ^ w[++i];
a = a2;
b = b2;
c = c2;
}
/*
Encrypt:
SubBytes(state)
ShiftRows(state)
AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
Decrypt:
InvShiftRows(state)
InvSubBytes(state)
AddRoundKey(state, w[0, Nb-1])
*/
// Note: rows are shifted inline
output[0] =
(sub[a >>> 24] << 24) ^
(sub[b >>> 16 & 255] << 16) ^
(sub[c >>> 8 & 255] << 8) ^
(sub[d & 255]) ^ w[++i];
output[decrypt ? 3 : 1] =
(sub[b >>> 24] << 24) ^
(sub[c >>> 16 & 255] << 16) ^
(sub[d >>> 8 & 255] << 8) ^
(sub[a & 255]) ^ w[++i];
output[2] =
(sub[c >>> 24] << 24) ^
(sub[d >>> 16 & 255] << 16) ^
(sub[a >>> 8 & 255] << 8) ^
(sub[b & 255]) ^ w[++i];
output[decrypt ? 1 : 3] =
(sub[d >>> 24] << 24) ^
(sub[a >>> 16 & 255] << 16) ^
(sub[b >>> 8 & 255] << 8) ^
(sub[c & 255]) ^ w[++i];
};
/**
* Creates an AES cipher object. CBC (cipher-block-chaining) mode will be
* used.
*
* The key and iv may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words. If an iv is provided, then
* encryption/decryption will be started, otherwise start() must be called
* with an iv.
*
* @param key the symmetric key to use.
* @param iv the initialization vector to start with, null not to start.
* @param output the buffer to write to.
* @param decrypt true for decryption, false for encryption.
*
* @return the cipher.
*/
var _createCipher = function(key, iv, output, decrypt) {
var cipher = null;
if(!init) {
initialize();
}
/* Note: The key may be a string of bytes, an array of bytes, a byte
buffer, or an array of 32-bit integers. If the key is in bytes, then
it must be 16, 24, or 32 bytes in length. If it is in 32-bit
integers, it must be 4, 6, or 8 integers long. */
// convert key string into byte buffer
if(key.constructor == String &&
(key.length == 16 || key.length == 24 || key.length == 32)) {
key = forge.util.createBuffer(key);
}
// convert key integer array into byte buffer
else if(key.constructor == Array &&
(key.length == 16 || key.length == 24 || key.length == 32)) {
var tmp = key;
var key = forge.util.createBuffer();
for(var i = 0; i < tmp.length; ++i) {
key.putByte(tmp[i]);
}
}
// convert key byte buffer into 32-bit integer array
if(key.constructor != Array) {
var tmp = key;
key = [];
// key lengths of 16, 24, 32 bytes allowed
var len = tmp.length();
if(len == 16 || len == 24 || len == 32) {
len = len >>> 2;
for(var i = 0; i < len; ++i) {
key.push(tmp.getInt32());
}
}
}
// key must be an array of 32-bit integers by now
if(key.constructor == Array &&
(key.length == 4 || key.length == 6 || key.length == 8)) {
// private vars for state
var _w = expandKey(key, decrypt);
var _blockSize = Nb << 2;
var _input;
var _output;
var _inBlock;
var _outBlock;
var _prev;
var _finish;
cipher = {
// output from AES (either encrypted or decrypted bytes)
output: null
};
/**
* Updates the next block using CBC mode.
*
* @param input the buffer to read from.
*/
cipher.update = function(input) {
if(!_finish) {
// not finishing, so fill the input buffer with more input
_input.putBuffer(input);
}
/* In encrypt mode, the threshold for updating a block is the
block size. As soon as enough input is available to update
a block, encryption may occur. In decrypt mode, we wait for
2 blocks to be available or for the finish flag to be set
with only 1 block available. This is done so that the output
buffer will not be populated with padding bytes at the end
of the decryption -- they can be truncated before returning
from finish(). */
var threshold = decrypt && !_finish ? _blockSize << 1 : _blockSize;
while(_input.length() >= threshold) {
// get next block
if(decrypt) {
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _input.getInt32();
}
}
else {
// CBC mode XOR's IV (or previous block) with plaintext
for(var i = 0; i < Nb; ++i) {
_inBlock[i] = _prev[i] ^ _input.getInt32();
}
}
// update block
_updateBlock(_w, _inBlock, _outBlock, decrypt);
// write output, save previous ciphered block
if(decrypt) {
// CBC mode XOR's IV (or previous block) with plaintext
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_prev[i] ^ _outBlock[i]);
}
_prev = _inBlock.slice(0);
}
else {
for(var i = 0; i < Nb; ++i) {
_output.putInt32(_outBlock[i]);
}
_prev = _outBlock;
}
}
};
/**
* Finishes encrypting or decrypting.
*
* @param pad a padding function to use, null for default,
* signature(blockSize, buffer, decrypt).
*
* @return true if successful, false on error.
*/
cipher.finish = function(pad) {
var rval = true;
if(!decrypt) {
if(pad) {
rval = pad(_blockSize, _input, decrypt);
}
else {
// add PKCS#7 padding to block (each pad byte is the
// value of the number of pad bytes)
var padding = (_input.length() == _blockSize) ?
_blockSize : (_blockSize - _input.length());
_input.fillWithByte(padding, padding);
}
}
if(rval) {
// do final update
_finish = true;
cipher.update();
}
if(decrypt) {
// check for error: input data not a multiple of blockSize
rval = (_input.length() === 0);
if(rval) {
if(pad) {
rval = pad(_blockSize, _output, decrypt);
}
else {
// ensure padding byte count is valid
var len = _output.length();
var count = _output.at(len - 1);
if(count > (Nb << 2)) {
rval = false;
}
else {
// trim off padding bytes
_output.truncate(count);
}
}
}
}
return rval;
};
/**
* Starts or restarts the encryption or decryption process, whichever
* was previously configured.
*
* The iv may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words.
*
* @param iv the initialization vector to use, null to reuse the
* last ciphered block from a previous update().
* @param output the output the buffer to write to, null to create one.
*/
cipher.start = function(iv, output) {
// if IV is null, reuse block from previous encryption/decryption
iv = iv || _prev.slice(0);
/* Note: The IV may be a string of bytes, an array of bytes, a
byte buffer, or an array of 32-bit integers. If the IV is in
bytes, then it must be Nb (16) bytes in length. If it is in
32-bit integers, then it must be 4 integers long. */
// convert iv string into byte buffer
if(iv.constructor == String && iv.length == 16) {
iv = forge.util.createBuffer(iv);
}
// convert iv byte array into byte buffer
else if(iv.constructor == Array && iv.length == 16) {
var tmp = iv;
var iv = forge.util.createBuffer();
for(var i = 0; i < 16; ++i) {
iv.putByte(tmp[i]);
}
}
// convert iv byte buffer into 32-bit integer array
if(iv.constructor != Array) {
var tmp = iv;
iv = new Array(4);
iv[0] = tmp.getInt32();
iv[1] = tmp.getInt32();
iv[2] = tmp.getInt32();
iv[3] = tmp.getInt32();
}
// set private vars
_input = forge.util.createBuffer();
_output = output || forge.util.createBuffer();
_prev = iv.slice(0);
_inBlock = new Array(Nb);
_outBlock = new Array(Nb);
_finish = false;
cipher.output = _output;
};
if(iv !== null) {
cipher.start(iv, output);
}
}
return cipher;
};
/* AES API */
forge.aes = forge.aes || {};
/**
* Creates an AES cipher object to encrypt data in CBC mode using the
* given symmetric key. The output will be stored in the 'output' member
* of the returned cipher.
*
* The key and iv may be given as a string of bytes, an array of bytes,
* a byte buffer, or an array of 32-bit words.
*
* @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.aes.startEncrypting = function(key, iv, output) {
return _createCipher(key, iv, output, false);
};
/**
* Creates an AES cipher object to encrypt data in CBC mode using the
* given symmetric key.
*
* The key may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words.
*
* 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.aes.createEncryptionCipher = function(key) {
return _createCipher(key, null, null, false);
};
/**
* Creates an AES cipher object to decrypt data in CBC mode using the
* given symmetric key. The output will be stored in the 'output' member
* of the returned cipher.
*
* The key and iv may be given as a string of bytes, an array of bytes,
* a byte buffer, or an array of 32-bit words.
*
* @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.aes.startDecrypting = function(key, iv, output) {
return _createCipher(key, iv, output, true);
};
/**
* Creates an AES cipher object to decrypt data in CBC mode using the
* given symmetric key.
*
* The key may be given as a string of bytes, an array of bytes, a
* byte buffer, or an array of 32-bit words.
*
* 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.aes.createDecryptionCipher = function(key) {
return _createCipher(key, null, null, true);
};
/**
* Expands a key. Typically only used for testing.
*
* @param key the symmetric key to expand, as an array of 32-bit words.
* @param decrypt true to expand for decryption, false for encryption.
*
* @return the expanded key.
*/
forge.aes._expandKey = function(key, decrypt) {
if(!init) {
initialize();
}
return expandKey(key, decrypt);
};
/**
* Updates a single block. Typically only used for testing.
*
* @param w the expanded key to use.
* @param input an array of block-size 32-bit words.
* @param output an array of block-size 32-bit words.
* @param decrypt true to decrypt, false to encrypt.
*/
forge.aes._updateBlock = _updateBlock;
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'aes';
var deps = ['./util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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 module === 'object' && module.exports);
var crypto = null;
if(_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';
var deps = ['./md', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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 module === 'object' && module.exports);
if(!_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';
var deps = ['./aes', './md', './prng', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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(md.constructor == 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(key.constructor == String) {
key = forge.util.createBuffer(key);
}
// convert byte array into byte buffer
else if(key.constructor == Array) {
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';
var deps = ['./md', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
// Copyright (c) 2005 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// Basic JavaScript BN library - subset useful for RSA encryption.
/*
Licensing (LICENSE)
-------------------
This software is covered under the following copyright:
*/
/*
* Copyright (c) 2003-2005 Tom Wu
* All Rights Reserved.
*
* 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" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
/*
Address all questions regarding this license to:
Tom Wu
tjw@cs.Stanford.EDU
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
// Bits per digit
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
this.data = [];
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.
// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this.data[i++]+w.data[j]+c;
c = Math.floor(v/0x4000000);
w.data[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this.data[i]&0x7fff;
var h = this.data[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w.data[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w.data[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this.data[i]&0x3fff;
var h = this.data[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w.data[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w.data[j++] = l&0xfffffff;
}
return c;
}
// node.js (no browser)
if(typeof(navigator) === 'undefined')
{
BigInteger.prototype.am = am3;
dbits = 28;
}
else if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}
// (protected) copy this to r
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r.data[i] = this.data[i];
r.t = this.t;
r.s = this.s;
}
// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this.data[0] = x;
else if(x < -1) this.data[0] = x+DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this.data[this.t++] = x;
else if(sh+k > this.DB) {
this.data[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this.data[this.t++] = (x>>(this.DB-sh));
}
else
this.data[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this.data[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this.data[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this.data[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this.data[i]&((1<<p)-1))<<(k-p);
d |= this.data[--i]>>(p+=this.DB-k);
}
else {
d = (this.data[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}
// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }
// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return r;
while(--i >= 0) if((r=this.data[i]-a.data[i]) != 0) return r;
return 0;
}
// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}
// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this.data[this.t-1]^(this.s&this.DM));
}
// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r.data[i+n] = this.data[i];
for(i = n-1; i >= 0; --i) r.data[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r.data[i-n] = this.data[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r.data[i+ds+1] = (this.data[i]>>cbs)|c;
c = (this.data[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r.data[i] = 0;
r.data[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r.data[0] = this.data[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r.data[i-ds-1] |= (this.data[i]&bm)<<cbs;
r.data[i-ds] = this.data[i]>>bs;
}
if(bs > 0) r.data[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this.data[i]-a.data[i];
r.data[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this.data[i];
r.data[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a.data[i];
r.data[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r.data[i++] = this.DV+c;
else if(c > 0) r.data[i++] = c;
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r.data[i] = 0;
for(i = 0; i < y.t; ++i) r.data[i+x.t] = x.am(0,y.data[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r.data[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x.data[i],r,2*i,0,1);
if((r.data[i+x.t]+=x.am(i+1,2*x.data[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r.data[i+x.t] -= x.DV;
r.data[i+x.t+1] = 1;
}
}
if(r.t > 0) r.data[r.t-1] += x.am(i,x.data[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm.data[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y.data[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y.data[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r.data[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y.data[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r.data[--i]==y0)?this.DM:Math.floor(r.data[i]*d1+(r.data[i-1]+e)*d2);
if((r.data[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r.data[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this.data[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x.data[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x.data[i]*mp mod DV
var j = x.data[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x.data[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x.data[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x.data[j] >= x.DV) { x.data[j] -= x.DV; x.data[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this.data[0]&1):this.s) == 0; }
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;
// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;
// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);
// jsbn2 lib
//Copyright (c) 2005-2009 Tom Wu
//All Rights Reserved.
//See "LICENSE" for details (See jsbn.js for LICENSE).
//Extended JavaScript BN functions, required for RSA private ops.
//Version 1.1: new BigInteger("0", 10) returns "proper" zero
//(public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }
//(public) return value as integer
function bnIntValue() {
if(this.s < 0) {
if(this.t == 1) return this.data[0]-this.DV;
else if(this.t == 0) return -1;
}
else if(this.t == 1) return this.data[0];
else if(this.t == 0) return 0;
// assumes 16 < DB < 32
return ((this.data[1]&((1<<(32-this.DB))-1))<<this.DB)|this.data[0];
}
//(public) return value as byte
function bnByteValue() { return (this.t==0)?this.s:(this.data[0]<<24)>>24; }
//(public) return value as short (assumes DB>=16)
function bnShortValue() { return (this.t==0)?this.s:(this.data[0]<<16)>>16; }
//(protected) return x s.t. r^x < DV
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }
//(public) 0 if this == 0, 1 if this > 0
function bnSigNum() {
if(this.s < 0) return -1;
else if(this.t <= 0 || (this.t == 1 && this.data[0] <= 0)) return 0;
else return 1;
}
//(protected) convert to radix string
function bnpToRadix(b) {
if(b == null) b = 10;
if(this.signum() == 0 || b < 2 || b > 36) return "0";
var cs = this.chunkSize(b);
var a = Math.pow(b,cs);
var d = nbv(a), y = nbi(), z = nbi(), r = "";
this.divRemTo(d,y,z);
while(y.signum() > 0) {
r = (a+z.intValue()).toString(b).substr(1) + r;
y.divRemTo(d,y,z);
}
return z.intValue().toString(b) + r;
}
//(protected) convert from radix string
function bnpFromRadix(s,b) {
this.fromInt(0);
if(b == null) b = 10;
var cs = this.chunkSize(b);
var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
for(var i = 0; i < s.length; ++i) {
var x = intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
continue;
}
w = b*w+x;
if(++j >= cs) {
this.dMultiply(d);
this.dAddOffset(w,0);
j = 0;
w = 0;
}
}
if(j > 0) {
this.dMultiply(Math.pow(b,j));
this.dAddOffset(w,0);
}
if(mi) BigInteger.ZERO.subTo(this,this);
}
//(protected) alternate constructor
function bnpFromNumber(a,b,c) {
if("number" == typeof b) {
// new BigInteger(int,int,RNG)
if(a < 2) this.fromInt(1);
else {
this.fromNumber(a,c);
if(!this.testBit(a-1)) // force MSB set
this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
if(this.isEven()) this.dAddOffset(1,0); // force odd
while(!this.isProbablePrime(b)) {
this.dAddOffset(2,0);
if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
}
}
}
else {
// new BigInteger(int,RNG)
var x = new Array(), t = a&7;
x.length = (a>>3)+1;
b.nextBytes(x);
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
this.fromString(x,256);
}
}
//(public) convert to bigendian byte array
function bnToByteArray() {
var i = this.t, r = new Array();
r[0] = this.s;
var p = this.DB-(i*this.DB)%8, d, k = 0;
if(i-- > 0) {
if(p < this.DB && (d = this.data[i]>>p) != (this.s&this.DM)>>p)
r[k++] = d|(this.s<<(this.DB-p));
while(i >= 0) {
if(p < 8) {
d = (this.data[i]&((1<<p)-1))<<(8-p);
d |= this.data[--i]>>(p+=this.DB-8);
}
else {
d = (this.data[i]>>(p-=8))&0xff;
if(p <= 0) { p += this.DB; --i; }
}
if((d&0x80) != 0) d |= -256;
if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
if(k > 0 || d != this.s) r[k++] = d;
}
}
return r;
}
function bnEquals(a) { return(this.compareTo(a)==0); }
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
//(protected) r = this op a (bitwise)
function bnpBitwiseTo(a,op,r) {
var i, f, m = Math.min(a.t,this.t);
for(i = 0; i < m; ++i) r.data[i] = op(this.data[i],a.data[i]);
if(a.t < this.t) {
f = a.s&this.DM;
for(i = m; i < this.t; ++i) r.data[i] = op(this.data[i],f);
r.t = this.t;
}
else {
f = this.s&this.DM;
for(i = m; i < a.t; ++i) r.data[i] = op(f,a.data[i]);
r.t = a.t;
}
r.s = op(this.s,a.s);
r.clamp();
}
//(public) this & a
function op_and(x,y) { return x&y; }
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
//(public) this | a
function op_or(x,y) { return x|y; }
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
//(public) this ^ a
function op_xor(x,y) { return x^y; }
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
//(public) this & ~a
function op_andnot(x,y) { return x&~y; }
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
//(public) ~this
function bnNot() {
var r = nbi();
for(var i = 0; i < this.t; ++i) r.data[i] = this.DM&~this.data[i];
r.t = this.t;
r.s = ~this.s;
return r;
}
//(public) this << n
function bnShiftLeft(n) {
var r = nbi();
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
return r;
}
//(public) this >> n
function bnShiftRight(n) {
var r = nbi();
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
return r;
}
//return index of lowest 1-bit in x, x < 2^31
function lbit(x) {
if(x == 0) return -1;
var r = 0;
if((x&0xffff) == 0) { x >>= 16; r += 16; }
if((x&0xff) == 0) { x >>= 8; r += 8; }
if((x&0xf) == 0) { x >>= 4; r += 4; }
if((x&3) == 0) { x >>= 2; r += 2; }
if((x&1) == 0) ++r;
return r;
}
//(public) returns index of lowest 1-bit (or -1 if none)
function bnGetLowestSetBit() {
for(var i = 0; i < this.t; ++i)
if(this.data[i] != 0) return i*this.DB+lbit(this.data[i]);
if(this.s < 0) return this.t*this.DB;
return -1;
}
//return number of 1 bits in x
function cbit(x) {
var r = 0;
while(x != 0) { x &= x-1; ++r; }
return r;
}
//(public) return number of set bits
function bnBitCount() {
var r = 0, x = this.s&this.DM;
for(var i = 0; i < this.t; ++i) r += cbit(this.data[i]^x);
return r;
}
//(public) true iff nth bit is set
function bnTestBit(n) {
var j = Math.floor(n/this.DB);
if(j >= this.t) return(this.s!=0);
return((this.data[j]&(1<<(n%this.DB)))!=0);
}
//(protected) this op (1<<n)
function bnpChangeBit(n,op) {
var r = BigInteger.ONE.shiftLeft(n);
this.bitwiseTo(r,op,r);
return r;
}
//(public) this | (1<<n)
function bnSetBit(n) { return this.changeBit(n,op_or); }
//(public) this & ~(1<<n)
function bnClearBit(n) { return this.changeBit(n,op_andnot); }
//(public) this ^ (1<<n)
function bnFlipBit(n) { return this.changeBit(n,op_xor); }
//(protected) r = this + a
function bnpAddTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this.data[i]+a.data[i];
r.data[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c += a.s;
while(i < this.t) {
c += this.data[i];
r.data[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c += a.data[i];
r.data[i++] = c&this.DM;
c >>= this.DB;
}
c += a.s;
}
r.s = (c<0)?-1:0;
if(c > 0) r.data[i++] = c;
else if(c < -1) r.data[i++] = this.DV+c;
r.t = i;
r.clamp();
}
//(public) this + a
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
//(public) this - a
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
//(public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
//(public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
//(public) this % a
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }
//(public) [this/a,this%a]
function bnDivideAndRemainder(a) {
var q = nbi(), r = nbi();
this.divRemTo(a,q,r);
return new Array(q,r);
}
//(protected) this *= n, this >= 0, 1 < n < DV
function bnpDMultiply(n) {
this.data[this.t] = this.am(0,n-1,this,0,0,this.t);
++this.t;
this.clamp();
}
//(protected) this += n << w words, this >= 0
function bnpDAddOffset(n,w) {
if(n == 0) return;
while(this.t <= w) this.data[this.t++] = 0;
this.data[w] += n;
while(this.data[w] >= this.DV) {
this.data[w] -= this.DV;
if(++w >= this.t) this.data[this.t++] = 0;
++this.data[w];
}
}
//A "null" reducer
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }
NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;
//(public) this^e
function bnPow(e) { return this.exp(e,new NullExp()); }
//(protected) r = lower n words of "this * a", a.t <= n
//"this" should be the larger one if appropriate.
function bnpMultiplyLowerTo(a,n,r) {
var i = Math.min(this.t+a.t,n);
r.s = 0; // assumes a,this >= 0
r.t = i;
while(i > 0) r.data[--i] = 0;
var j;
for(j = r.t-this.t; i < j; ++i) r.data[i+this.t] = this.am(0,a.data[i],r,i,0,this.t);
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a.data[i],r,i,0,n-i);
r.clamp();
}
//(protected) r = "this * a" without lower n words, n > 0
//"this" should be the larger one if appropriate.
function bnpMultiplyUpperTo(a,n,r) {
--n;
var i = r.t = this.t+a.t-n;
r.s = 0; // assumes a,this >= 0
while(--i >= 0) r.data[i] = 0;
for(i = Math.max(n-this.t,0); i < a.t; ++i)
r.data[this.t+i-n] = this.am(n-i,a.data[i],r,0,0,this.t+i-n);
r.clamp();
r.drShiftTo(1,r);
}
//Barrett modular reduction
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}
function barrettConvert(x) {
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
else if(x.compareTo(this.m) < 0) return x;
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
}
function barrettRevert(x) { return x; }
//x = x mod m (HAC 14.42)
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
//r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
//r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Barrett.prototype.convert = barrettConvert;
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;
//(public) this^e % m (HAC 14.85)
function bnModPow(e,m) {
var i = e.bitLength(), k, r = nbv(1), z;
if(i <= 0) return r;
else if(i < 18) k = 1;
else if(i < 48) k = 3;
else if(i < 144) k = 4;
else if(i < 768) k = 5;
else k = 6;
if(i < 8)
z = new Classic(m);
else if(m.isEven())
z = new Barrett(m);
else
z = new Montgomery(m);
// precomputation
var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
g[1] = z.convert(this);
if(k > 1) {
var g2 = nbi();
z.sqrTo(g[1],g2);
while(n <= km) {
g[n] = nbi();
z.mulTo(g2,g[n-2],g[n]);
n += 2;
}
}
var j = e.t-1, w, is1 = true, r2 = nbi(), t;
i = nbits(e.data[j])-1;
while(j >= 0) {
if(i >= k1) w = (e.data[j]>>(i-k1))&km;
else {
w = (e.data[j]&((1<<(i+1))-1))<<(k1-i);
if(j > 0) w |= e.data[j-1]>>(this.DB+i-k1);
}
n = k;
while((w&1) == 0) { w >>= 1; --n; }
if((i -= n) < 0) { i += this.DB; --j; }
if(is1) { // ret == 1, don't bother squaring or multiplying it
g[w].copyTo(r);
is1 = false;
}
else {
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
z.mulTo(r2,g[w],r);
}
while(j >= 0 && (e.data[j]&(1<<i)) == 0) {
z.sqrTo(r,r2); t = r; r = r2; r2 = t;
if(--i < 0) { i = this.DB-1; --j; }
}
}
return z.revert(r);
}
//(public) gcd(this,a) (HAC 14.54)
function bnGCD(a) {
var x = (this.s<0)?this.negate():this.clone();
var y = (a.s<0)?a.negate():a.clone();
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
if(g < 0) return x;
if(i < g) g = i;
if(g > 0) {
x.rShiftTo(g,x);
y.rShiftTo(g,y);
}
while(x.signum() > 0) {
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
if(x.compareTo(y) >= 0) {
x.subTo(y,x);
x.rShiftTo(1,x);
}
else {
y.subTo(x,y);
y.rShiftTo(1,y);
}
}
if(g > 0) y.lShiftTo(g,y);
return y;
}
//(protected) this % n, n < 2^26
function bnpModInt(n) {
if(n <= 0) return 0;
var d = this.DV%n, r = (this.s<0)?n-1:0;
if(this.t > 0)
if(d == 0) r = this.data[0]%n;
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this.data[i])%n;
return r;
}
//(public) 1/this % m (HAC 14.61)
function bnModInverse(m) {
var ac = m.isEven();
if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
var u = m.clone(), v = this.clone();
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
while(u.signum() != 0) {
while(u.isEven()) {
u.rShiftTo(1,u);
if(ac) {
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
a.rShiftTo(1,a);
}
else if(!b.isEven()) b.subTo(m,b);
b.rShiftTo(1,b);
}
while(v.isEven()) {
v.rShiftTo(1,v);
if(ac) {
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
c.rShiftTo(1,c);
}
else if(!d.isEven()) d.subTo(m,d);
d.rShiftTo(1,d);
}
if(u.compareTo(v) >= 0) {
u.subTo(v,u);
if(ac) a.subTo(c,a);
b.subTo(d,b);
}
else {
v.subTo(u,v);
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}
var lowprimes = [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];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
//(public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
var i, x = this.abs();
if(x.t == 1 && x.data[0] <= lowprimes[lowprimes.length-1]) {
for(i = 0; i < lowprimes.length; ++i)
if(x.data[0] == lowprimes[i]) return true;
return false;
}
if(x.isEven()) return false;
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}
//(protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
var n1 = this.subtract(BigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
a.fromInt(lowprimes[i]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(BigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
}
}
return true;
}
//protected
BigInteger.prototype.chunkSize = bnpChunkSize;
BigInteger.prototype.toRadix = bnpToRadix;
BigInteger.prototype.fromRadix = bnpFromRadix;
BigInteger.prototype.fromNumber = bnpFromNumber;
BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
BigInteger.prototype.changeBit = bnpChangeBit;
BigInteger.prototype.addTo = bnpAddTo;
BigInteger.prototype.dMultiply = bnpDMultiply;
BigInteger.prototype.dAddOffset = bnpDAddOffset;
BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
BigInteger.prototype.modInt = bnpModInt;
BigInteger.prototype.millerRabin = bnpMillerRabin;
//public
BigInteger.prototype.clone = bnClone;
BigInteger.prototype.intValue = bnIntValue;
BigInteger.prototype.byteValue = bnByteValue;
BigInteger.prototype.shortValue = bnShortValue;
BigInteger.prototype.signum = bnSigNum;
BigInteger.prototype.toByteArray = bnToByteArray;
BigInteger.prototype.equals = bnEquals;
BigInteger.prototype.min = bnMin;
BigInteger.prototype.max = bnMax;
BigInteger.prototype.and = bnAnd;
BigInteger.prototype.or = bnOr;
BigInteger.prototype.xor = bnXor;
BigInteger.prototype.andNot = bnAndNot;
BigInteger.prototype.not = bnNot;
BigInteger.prototype.shiftLeft = bnShiftLeft;
BigInteger.prototype.shiftRight = bnShiftRight;
BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
BigInteger.prototype.bitCount = bnBitCount;
BigInteger.prototype.testBit = bnTestBit;
BigInteger.prototype.setBit = bnSetBit;
BigInteger.prototype.clearBit = bnClearBit;
BigInteger.prototype.flipBit = bnFlipBit;
BigInteger.prototype.add = bnAdd;
BigInteger.prototype.subtract = bnSubtract;
BigInteger.prototype.multiply = bnMultiply;
BigInteger.prototype.divide = bnDivide;
BigInteger.prototype.remainder = bnRemainder;
BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
BigInteger.prototype.modPow = bnModPow;
BigInteger.prototype.modInverse = bnModInverse;
BigInteger.prototype.pow = bnPow;
BigInteger.prototype.gcd = bnGCD;
BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
//BigInteger interfaces not implemented in jsbn:
//BigInteger(int signum, byte[] magnitude)
//double doubleValue()
//float floatValue()
//int hashCode()
//long longValue()
//static BigInteger valueOf(long val)
forge.jsbn = forge.jsbn || {};
forge.jsbn.BigInteger = BigInteger;
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'jsbn';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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.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';
oids['1.2.840.113549.1.9.4'] = 'messageDigest';
oids['messageDigest'] = '1.2.840.113549.1.9.4';
oids['1.2.840.113549.1.9.3'] = 'contentType';
oids['contentType'] = '1.2.840.113549.1.9.3';
oids['1.2.840.113549.1.9.5'] = 'signingTime';
oids['signingTime'] = '1.2.840.113549.1.9.5';
// pkcs#12 safe bags
oids['1.2.840.113549.1.12.10.1.1'] = 'keyBag';
oids['keyBag'] = '1.2.840.113549.1.12.10.1.1';
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';
oids['1.2.840.113549.1.9.1'] = 'emailAddress';
oids['emailAddress'] = '1.2.840.113549.1.9.1';
// X.509 extension OIDs
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';
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'oids';
var deps = [];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* Javascript implementation of Abstract Syntax Notation Number One.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*
* An API for storing data using the Abstract Syntax Notation Number One
* format using DER (Distinguished Encoding Rules) encoding. This encoding is
* commonly used to store data for PKI, i.e. X.509 Certificates, and this
* implementation exists for that purpose.
*
* Abstract Syntax Notation Number One (ASN.1) is used to define the abstract
* syntax of information without restricting the way the information is encoded
* for transmission. It provides a standard that allows for open systems
* communication. ASN.1 defines the syntax of information data and a number of
* simple data types as well as a notation for describing them and specifying
* values for them.
*
* The RSA algorithm creates public and private keys that are often stored in
* X.509 or PKCS#X formats -- which use ASN.1 (encoded in DER format). This
* class provides the most basic functionality required to store and load DSA
* keys that are encoded according to ASN.1.
*
* The most common binary encodings for ASN.1 are BER (Basic Encoding Rules)
* and DER (Distinguished Encoding Rules). DER is just a subset of BER that
* has stricter requirements for how data must be encoded.
*
* Each ASN.1 structure has a tag (a byte identifying the ASN.1 structure type)
* and a byte array for the value of this ASN1 structure which may be data or a
* list of ASN.1 structures.
*
* Each ASN.1 structure using BER is (Tag-Length-Value):
*
* | byte 0 | bytes X | bytes Y |
* |--------|---------|----------
* | tag | length | value |
*
* ASN.1 allows for tags to be of "High-tag-number form" which allows a tag to
* be two or more octets, but that is not supported by this class. A tag is
* only 1 byte. Bits 1-5 give the tag number (ie the data type within a
* particular 'class'), 6 indicates whether or not the ASN.1 value is
* constructed from other ASN.1 values, and bits 7 and 8 give the 'class'. If
* bits 7 and 8 are both zero, the class is UNIVERSAL. If only bit 7 is set,
* then the class is APPLICATION. If only bit 8 is set, then the class is
* CONTEXT_SPECIFIC. If both bits 7 and 8 are set, then the class is PRIVATE.
* The tag numbers for the data types for the class UNIVERSAL are listed below:
*
* UNIVERSAL 0 Reserved for use by the encoding rules
* UNIVERSAL 1 Boolean type
* UNIVERSAL 2 Integer type
* UNIVERSAL 3 Bitstring type
* UNIVERSAL 4 Octetstring type
* UNIVERSAL 5 Null type
* UNIVERSAL 6 Object identifier type
* UNIVERSAL 7 Object descriptor type
* UNIVERSAL 8 External type and Instance-of type
* UNIVERSAL 9 Real type
* UNIVERSAL 10 Enumerated type
* UNIVERSAL 11 Embedded-pdv type
* UNIVERSAL 12 UTF8String type
* UNIVERSAL 13 Relative object identifier type
* UNIVERSAL 14-15 Reserved for future editions
* UNIVERSAL 16 Sequence and Sequence-of types
* UNIVERSAL 17 Set and Set-of types
* UNIVERSAL 18-22, 25-30 Character string types
* UNIVERSAL 23-24 Time types
*
* The length of an ASN.1 structure is specified after the tag identifier.
* There is a definite form and an indefinite form. The indefinite form may
* be used if the encoding is constructed and not all immediately available.
* The indefinite form is encoded using a length byte with only the 8th bit
* set. The end of the constructed object is marked using end-of-contents
* octets (two zero bytes).
*
* The definite form looks like this:
*
* The length may take up 1 or more bytes, it depends on the length of the
* value of the ASN.1 structure. DER encoding requires that if the ASN.1
* structure has a value that has a length greater than 127, more than 1 byte
* will be used to store its length, otherwise just one byte will be used.
* This is strict.
*
* In the case that the length of the ASN.1 value is less than 127, 1 octet
* (byte) is used to store the "short form" length. The 8th bit has a value of
* 0 indicating the length is "short form" and not "long form" and bits 7-1
* give the length of the data. (The 8th bit is the left-most, most significant
* bit: also known as big endian or network format).
*
* In the case that the length of the ASN.1 value is greater than 127, 2 to
* 127 octets (bytes) are used to store the "long form" length. The first
* byte's 8th bit is set to 1 to indicate the length is "long form." Bits 7-1
* give the number of additional octets. All following octets are in base 256
* with the most significant digit first (typical big-endian binary unsigned
* integer storage). So, for instance, if the length of a value was 257, the
* first byte would be set to:
*
* 10000010 = 130 = 0x82.
*
* This indicates there are 2 octets (base 256) for the length. The second and
* third bytes (the octets just mentioned) would store the length in base 256:
*
* octet 2: 00000001 = 1 * 256^1 = 256
* octet 3: 00000001 = 1 * 256^0 = 1
* total = 257
*
* The algorithm for converting a js integer value of 257 to base-256 is:
*
* var value = 257;
* var bytes = [];
* bytes[0] = (value >>> 8) & 0xFF; // most significant byte first
* bytes[1] = value & 0xFF; // least significant byte last
*
* On the ASN.1 UNIVERSAL Object Identifier (OID) type:
*
* An OID can be written like: "value1.value2.value3...valueN"
*
* The DER encoding rules:
*
* The first byte has the value 40 * value1 + value2.
* The following bytes, if any, encode the remaining values. Each value is
* encoded in base 128, most significant digit first (big endian), with as
* few digits as possible, and the most significant bit of each byte set
* to 1 except the last in each value's encoding. For example: Given the
* OID "1.2.840.113549", its DER encoding is (remember each byte except the
* last one in each encoding is OR'd with 0x80):
*
* byte 1: 40 * 1 + 2 = 42 = 0x2A.
* bytes 2-3: 128 * 6 + 72 = 840 = 6 72 = 6 72 = 0x0648 = 0x8648
* bytes 4-6: 16384 * 6 + 128 * 119 + 13 = 6 119 13 = 0x06770D = 0x86F70D
*
* The final value is: 0x2A864886F70D.
* The full OID (including ASN.1 tag and length of 6 bytes) is:
* 0x06062A864886F70D
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
/* ASN.1 API */
var asn1 = forge.asn1 = forge.asn1 || {};
/**
* ASN.1 classes.
*/
asn1.Class = {
UNIVERSAL: 0x00,
APPLICATION: 0x40,
CONTEXT_SPECIFIC: 0x80,
PRIVATE: 0xC0
};
/**
* ASN.1 types. Not all types are supported by this implementation, only
* those necessary to implement a simple PKI are implemented.
*/
asn1.Type = {
NONE: 0,
BOOLEAN: 1,
INTEGER: 2,
BITSTRING: 3,
OCTETSTRING: 4,
NULL: 5,
OID: 6,
ODESC: 7,
EXTERNAL: 8,
REAL: 9,
ENUMERATED: 10,
EMBEDDED: 11,
UTF8: 12,
ROID: 13,
SEQUENCE: 16,
SET: 17,
PRINTABLESTRING: 19,
IA5STRING: 22,
UTCTIME: 23,
GENERALIZEDTIME: 24,
BMPSTRING: 30
};
/**
* Creates a new asn1 object.
*
* @param tagClass the tag class for the object.
* @param type the data type (tag number) for the object.
* @param constructed true if the asn1 object is in constructed form.
* @param value the value for the object, if it is not constructed.
*
* @return the asn1 object.
*/
asn1.create = function(tagClass, type, constructed, value) {
/* An asn1 object has a tagClass, a type, a constructed flag, and a
value. The value's type depends on the constructed flag. If
constructed, it will contain a list of other asn1 objects. If not,
it will contain the ASN.1 value as an array of bytes formatted
according to the ASN.1 data type. */
// remove undefined values
if(value.constructor == Array) {
var tmp = [];
for(var i = 0; i < value.length; ++i) {
if(value[i] !== undefined) {
tmp.push(value[i]);
}
}
value = tmp;
}
return {
tagClass: tagClass,
type: type,
constructed: constructed,
composed: constructed || (value.constructor == Array),
value: value
};
};
/**
* Gets the length of an ASN.1 value.
*
* In case the length is not specified, undefined is returned.
*
* @param b the ASN.1 byte buffer.
*
* @return the length of the ASN.1 value.
*/
var _getValueLength = function(b) {
var b2 = b.getByte();
if(b2 == 0x80) {
return undefined;
}
// see if the length is "short form" or "long form" (bit 8 set)
var length;
var longForm = b2 & 0x80;
if(!longForm) {
// length is just the first byte
length = b2;
}
else {
// the number of bytes the length is specified in bits 7 through 1
// and each length byte is in big-endian base-256
length = b.getInt((b2 & 0x7F) << 3);
}
return length;
};
/**
* Parses an asn1 object from a byte buffer in DER format.
*
* @param bytes the byte buffer to parse from.
*
* @return the parsed asn1 object.
*/
asn1.fromDer = function(bytes) {
// wrap in buffer if needed
if(bytes.constructor == String) {
bytes = forge.util.createBuffer(bytes);
}
// minimum length for ASN.1 DER structure is 2
if(bytes.length() < 2) {
throw {
message: 'Too few bytes to parse DER.',
bytes: bytes.length()
};
}
// get the first byte
var b1 = bytes.getByte();
// get the tag class
var tagClass = (b1 & 0xC0);
// get the type (bits 1-5)
var type = b1 & 0x1F;
// get the value length
var length = _getValueLength(bytes);
// ensure there are enough bytes to get the value
if(bytes.length() < length) {
throw {
message: 'Too few bytes to read ASN.1 value.',
detail: bytes.length() + ' < ' + length
};
}
// prepare to get value
var value;
// constructed flag is bit 6 (32 = 0x20) of the first byte
var constructed = ((b1 & 0x20) == 0x20);
// determine if the value is composed of other ASN.1 objects (if its
// constructed it will be and if its a BITSTRING it may be)
var composed = constructed;
if(!composed && tagClass === asn1.Class.UNIVERSAL &&
type === asn1.Type.BITSTRING && length > 1) {
/* The first octet gives the number of bits by which the length of the
bit string is less than the next multiple of eight (this is called
the "number of unused bits").
The second and following octets give the value of the bit string
converted to an octet string. */
// if there are no unused bits, maybe the bitstring holds ASN.1 objs
var read = bytes.read;
var unused = bytes.getByte();
if(unused === 0) {
// if the first byte indicates UNIVERSAL or CONTEXT_SPECIFIC,
// and the length is valid, assume we've got an ASN.1 object
b1 = bytes.getByte();
var tc = (b1 & 0xC0);
if(tc === asn1.Class.UNIVERSAL ||
tc === asn1.Class.CONTEXT_SPECIFIC) {
try {
var len = _getValueLength(bytes);
composed = (len === length - (bytes.read - read));
if(composed) {
// adjust read/length to account for unused bits byte
++read;
--length;
}
}
catch(ex) {}
}
}
// restore read pointer
bytes.read = read;
}
if(composed) {
// parse child asn1 objects from the value
value = [];
if(length === undefined) {
// asn1 object of indefinite length, read until end tag
for(;;) {
if(bytes.bytes(2) === String.fromCharCode(0, 0)) {
bytes.getBytes(2);
break;
}
value.push(asn1.fromDer(bytes));
}
}
else {
// parsing asn1 object of definite length
var start = bytes.length();
while(length > 0) {
value.push(asn1.fromDer(bytes));
length -= start - bytes.length();
start = bytes.length();
}
}
}
// asn1 not composed, get raw value
else {
// TODO: do DER to OID conversion and vice-versa in .toDer?
if(length === undefined) {
throw {
message: 'Non-constructed ASN.1 object of indefinite length.'
};
}
if(type === asn1.Type.BMPSTRING) {
value = '';
for(var i = 0; i < length; i += 2) {
value += String.fromCharCode(bytes.getInt16());
}
}
else {
value = bytes.getBytes(length);
}
}
// create and return asn1 object
return asn1.create(tagClass, type, constructed, value);
};
/**
* Converts the given asn1 object to a buffer of bytes in DER format.
*
* @param asn1 the asn1 object to convert to bytes.
*
* @return the buffer of bytes.
*/
asn1.toDer = function(obj) {
var bytes = forge.util.createBuffer();
// build the first byte
var b1 = obj.tagClass | obj.type;
// for storing the ASN.1 value
var value = forge.util.createBuffer();
// if composed, use each child asn1 object's DER bytes as value
if(obj.composed) {
// turn on 6th bit (0x20 = 32) to indicate asn1 is constructed
// from other asn1 objects
if(obj.constructed) {
b1 |= 0x20;
}
// if type is a bit string, add unused bits of 0x00
else {
value.putByte(0x00);
}
// add all of the child DER bytes together
for(var i = 0; i < obj.value.length; ++i) {
if(obj.value[i] !== undefined) {
value.putBuffer(asn1.toDer(obj.value[i]));
}
}
}
// use asn1.value directly
else {
if(obj.type === asn1.Type.BMPSTRING) {
for(var i = 0; i < obj.value.length; ++i) {
value.putInt16(obj.value.charCodeAt(i));
}
}
else {
value.putBytes(obj.value);
}
}
// add tag byte
bytes.putByte(b1);
// use "short form" encoding
if(value.length() <= 127) {
// one byte describes the length
// bit 8 = 0 and bits 7-1 = length
bytes.putByte(value.length() & 0x7F);
}
// use "long form" encoding
else {
// 2 to 127 bytes describe the length
// first byte: bit 8 = 1 and bits 7-1 = # of additional bytes
// other bytes: length in base 256, big-endian
var len = value.length();
var lenBytes = '';
do {
lenBytes += String.fromCharCode(len & 0xFF);
len = len >>> 8;
}
while(len > 0);
// set first byte to # bytes used to store the length and turn on
// bit 8 to indicate long-form length is used
bytes.putByte(lenBytes.length | 0x80);
// concatenate length bytes in reverse since they were generated
// little endian and we need big endian
for(var i = lenBytes.length - 1; i >= 0; --i) {
bytes.putByte(lenBytes.charCodeAt(i));
}
}
// concatenate value bytes
bytes.putBuffer(value);
return bytes;
};
/**
* Converts an OID dot-separated string to a byte buffer. The byte buffer
* contains only the DER-encoded value, not any tag or length bytes.
*
* @param oid the OID dot-separated string.
*
* @return the byte buffer.
*/
asn1.oidToDer = function(oid) {
// split OID into individual values
var values = oid.split('.');
var bytes = forge.util.createBuffer();
// first byte is 40 * value1 + value2
bytes.putByte(40 * parseInt(values[0], 10) + parseInt(values[1], 10));
// other bytes are each value in base 128 with 8th bit set except for
// the last byte for each value
var last, valueBytes, value, b;
for(var i = 2; i < values.length; ++i) {
// produce value bytes in reverse because we don't know how many
// bytes it will take to store the value
last = true;
valueBytes = [];
value = parseInt(values[i], 10);
do {
b = value & 0x7F;
value = value >>> 7;
// if value is not last, then turn on 8th bit
if(!last) {
b |= 0x80;
}
valueBytes.push(b);
last = false;
}
while(value > 0);
// add value bytes in reverse (needs to be in big endian)
for(var n = valueBytes.length - 1; n >= 0; --n) {
bytes.putByte(valueBytes[n]);
}
}
return bytes;
};
/**
* Converts a DER-encoded byte buffer to an OID dot-separated string. The
* byte buffer should contain only the DER-encoded value, not any tag or
* length bytes.
*
* @param bytes the byte buffer.
*
* @return the OID dot-separated string.
*/
asn1.derToOid = function(bytes) {
var oid;
// wrap in buffer if needed
if(bytes.constructor == String) {
bytes = forge.util.createBuffer(bytes);
}
// first byte is 40 * value1 + value2
var b = bytes.getByte();
oid = Math.floor(b / 40) + '.' + (b % 40);
// other bytes are each value in base 128 with 8th bit set except for
// the last byte for each value
var value = 0;
while(bytes.length() > 0) {
b = bytes.getByte();
value = value << 7;
// not the last byte for the value
if(b & 0x80) {
value += b & 0x7F;
}
// last byte
else {
oid += '.' + (value + b);
value = 0;
}
}
return oid;
};
/**
* Converts a UTCTime value to a date.
*
* Note: GeneralizedTime has 4 digits for the year and is used for X.509
* dates passed 2049. Parsing that structure hasn't been implemented yet.
*
* @param utc the UTCTime value to convert.
*
* @return the date.
*/
asn1.utcTimeToDate = function(utc) {
/* The following formats can be used:
YYMMDDhhmmZ
YYMMDDhhmm+hh'mm'
YYMMDDhhmm-hh'mm'
YYMMDDhhmmssZ
YYMMDDhhmmss+hh'mm'
YYMMDDhhmmss-hh'mm'
Where:
YY is the least significant two digits of the year
MM is the month (01 to 12)
DD is the day (01 to 31)
hh is the hour (00 to 23)
mm are the minutes (00 to 59)
ss are the seconds (00 to 59)
Z indicates that local time is GMT, + indicates that local time is
later than GMT, and - indicates that local time is earlier than GMT
hh' is the absolute value of the offset from GMT in hours
mm' is the absolute value of the offset from GMT in minutes */
var date = new Date();
// if YY >= 50 use 19xx, if YY < 50 use 20xx
var year = parseInt(utc.substr(0, 2), 10);
year = (year >= 50) ? 1900 + year : 2000 + year;
var MM = parseInt(utc.substr(2, 2), 10) - 1; // use 0-11 for month
var DD = parseInt(utc.substr(4, 2), 10);
var hh = parseInt(utc.substr(6, 2), 10);
var mm = parseInt(utc.substr(8, 2), 10);
var ss = 0;
// not just YYMMDDhhmmZ
if(utc.length > 11) {
// get character after minutes
var c = utc.charAt(10);
var end = 10;
// see if seconds are present
if(c !== '+' && c !== '-') {
// get seconds
ss = parseInt(utc.substr(10, 2), 10);
end += 2;
}
}
// update date
date.setUTCFullYear(year, MM, DD);
date.setUTCHours(hh, mm, ss, 0);
if(end) {
// get +/- after end of time
c = utc.charAt(end);
if(c === '+' || c === '-') {
// get hours+minutes offset
var hhoffset = parseInt(utc.substr(end + 1, 2), 10);
var mmoffset = parseInt(utc.substr(end + 4, 2), 10);
// calculate offset in milliseconds
var offset = hhoffset * 60 + mmoffset;
offset *= 60000;
// apply offset
if(c === '+') {
date.setTime(+date - offset);
}
else {
date.setTime(+date + offset);
}
}
}
return date;
};
/**
* Converts a GeneralizedTime value to a date.
*
* @param gentime the GeneralizedTime value to convert.
*
* @return the date.
*/
asn1.generalizedTimeToDate = function(gentime) {
/* The following formats can be used:
YYYYMMDDHHMMSS
YYYYMMDDHHMMSS.fff
YYYYMMDDHHMMSSZ
YYYYMMDDHHMMSS.fffZ
YYYYMMDDHHMMSS+hh'mm'
YYYYMMDDHHMMSS.fff+hh'mm'
YYYYMMDDHHMMSS-hh'mm'
YYYYMMDDHHMMSS.fff-hh'mm'
Where:
YYYY is the year
MM is the month (01 to 12)
DD is the day (01 to 31)
hh is the hour (00 to 23)
mm are the minutes (00 to 59)
ss are the seconds (00 to 59)
.fff is the second fraction, accurate to three decimal places
Z indicates that local time is GMT, + indicates that local time is
later than GMT, and - indicates that local time is earlier than GMT
hh' is the absolute value of the offset from GMT in hours
mm' is the absolute value of the offset from GMT in minutes */
var date = new Date();
var YYYY = parseInt(gentime.substr(0, 4), 10);
var MM = parseInt(gentime.substr(4, 2), 10) - 1; // use 0-11 for month
var DD = parseInt(gentime.substr(6, 2), 10);
var hh = parseInt(gentime.substr(8, 2), 10);
var mm = parseInt(gentime.substr(10, 2), 10);
var ss = parseInt(gentime.substr(12, 2), 10);
var fff = 0;
var offset = 0;
var isUTC = false;
if(gentime.charAt(gentime.length - 1) == 'Z') {
isUTC = true;
}
var end = gentime.length - 5, c = gentime.charAt(end);
if(c === '+' || c === '-') {
// get hours+minutes offset
var hhoffset = parseInt(gentime.substr(end + 1, 2), 10);
var mmoffset = parseInt(gentime.substr(end + 4, 2), 10);
// calculate offset in milliseconds
offset = hhoffset * 60 + mmoffset;
offset *= 60000;
// apply offset
if(c === '+') {
offset *= -1;
}
isUTC = true;
}
// check for second fraction
if(gentime.charAt(14) == '.') {
fff = parseFloat(gentime.substr(14), 10) * 1000;
}
if(isUTC) {
date.setUTCFullYear(YYYY, MM, DD);
date.setUTCHours(hh, mm, ss, fff);
// apply offset
date.setTime(+date + offset);
}
else {
date.setFullYear(YYYY, MM, DD);
date.setHours(hh, mm, ss, fff);
}
return date;
};
/**
* Converts a date to a UTCTime value.
*
* Note: GeneralizedTime has 4 digits for the year and is used for X.509
* dates passed 2049. Converting to a GeneralizedTime hasn't been
* implemented yet.
*
* @param date the date to convert.
*
* @return the UTCTime value.
*/
asn1.dateToUtcTime = function(date) {
var rval = '';
// create format YYMMDDhhmmssZ
var format = [];
format.push(('' + date.getUTCFullYear()).substr(2));
format.push('' + (date.getUTCMonth() + 1));
format.push('' + date.getUTCDate());
format.push('' + date.getUTCHours());
format.push('' + date.getUTCMinutes());
format.push('' + date.getUTCSeconds());
// ensure 2 digits are used for each format entry
for(var i = 0; i < format.length; ++i) {
if(format[i].length < 2) {
rval += '0';
}
rval += format[i];
}
rval += 'Z';
return rval;
};
/**
* Validates the that given ASN.1 object is at least a super set of the
* given ASN.1 structure. Only tag classes and types are checked. An
* optional map may also be provided to capture ASN.1 values while the
* structure is checked.
*
* To capture an ASN.1 value, set an object in the validator's 'capture'
* parameter to the key to use in the capture map. To capture the full
* ASN.1 object, specify 'captureAsn1'.
*
* Objects in the validator may set a field 'optional' to true to indicate
* that it isn't necessary to pass validation.
*
* @param obj the ASN.1 object to validate.
* @param v the ASN.1 structure validator.
* @param capture an optional map to capture values in.
* @param errors an optional array for storing validation errors.
*
* @return true on success, false on failure.
*/
asn1.validate = function(obj, v, capture, errors) {
var rval = false;
// ensure tag class and type are the same if specified
if((obj.tagClass === v.tagClass || typeof(v.tagClass) === 'undefined') &&
(obj.type === v.type || typeof(v.type) === 'undefined')) {
// ensure constructed flag is the same if specified
if(obj.constructed === v.constructed ||
typeof(v.constructed) === 'undefined') {
rval = true;
// handle sub values
if(v.value && v.value.constructor == Array) {
var j = 0;
for(var i = 0; rval && i < v.value.length; ++i) {
rval = v.value[i].optional || false;
if(obj.value[j]) {
rval = asn1.validate(obj.value[j], v.value[i], capture, errors);
if(rval) {
++j;
}
else if(v.value[i].optional) {
rval = true;
}
}
if(!rval && errors) {
errors.push(
'[' + v.name + '] ' +
'Tag class "' + v.tagClass + '", type "' +
v.type + '" expected value length "' +
v.value.length + '", got "' +
obj.value.length + '"');
}
}
}
if(rval && capture) {
if(v.capture) {
capture[v.capture] = obj.value;
}
if(v.captureAsn1) {
capture[v.captureAsn1] = obj;
}
}
}
else if(errors) {
errors.push(
'[' + v.name + '] ' +
'Expected constructed "' + v.constructed + '", got "' +
obj.constructed + '"');
}
}
else if(errors) {
if(obj.tagClass !== v.tagClass) {
errors.push(
'[' + v.name + '] ' +
'Expected tag class "' + v.tagClass + '", got "' +
obj.tagClass + '"');
}
if(obj.type !== v.type) {
errors.push(
'[' + v.name + '] ' +
'Expected type "' + v.type + '", got "' + obj.type + '"');
}
}
return rval;
};
// regex for testing for non-latin characters
var _nonLatinRegex = /[^\\u0000-\\u00ff]/;
/**
* Pretty prints an ASN.1 object to a string.
*
* @param obj the object to write out.
* @param level the level in the tree.
* @param indentation the indentation to use.
*
* @return the string.
*/
asn1.prettyPrint = function(obj, level, indentation) {
var rval = '';
// set default level and indentation
level = level || 0;
indentation = indentation || 2;
// start new line for deep levels
if(level > 0) {
rval += '\n';
}
// create indent
var indent = '';
for(var i = 0; i < level * indentation; ++i) {
indent += ' ';
}
// print class:type
rval += indent + 'Tag: ';
switch(obj.tagClass) {
case asn1.Class.UNIVERSAL:
rval += 'Universal:';
break;
case asn1.Class.APPLICATION:
rval += 'Application:';
break;
case asn1.Class.CONTEXT_SPECIFIC:
rval += 'Context-Specific:';
break;
case asn1.Class.PRIVATE:
rval += 'Private:';
break;
}
if(obj.tagClass === asn1.Class.UNIVERSAL) {
rval += obj.type;
// known types
switch(obj.type) {
case asn1.Type.NONE:
rval += ' (None)';
break;
case asn1.Type.BOOLEAN:
rval += ' (Boolean)';
break;
case asn1.Type.BITSTRING:
rval += ' (Bit string)';
break;
case asn1.Type.INTEGER:
rval += ' (Integer)';
break;
case asn1.Type.OCTETSTRING:
rval += ' (Octet string)';
break;
case asn1.Type.NULL:
rval += ' (Null)';
break;
case asn1.Type.OID:
rval += ' (Object Identifier)';
break;
case asn1.Type.ODESC:
rval += ' (Object Descriptor)';
break;
case asn1.Type.EXTERNAL:
rval += ' (External or Instance of)';
break;
case asn1.Type.REAL:
rval += ' (Real)';
break;
case asn1.Type.ENUMERATED:
rval += ' (Enumerated)';
break;
case asn1.Type.EMBEDDED:
rval += ' (Embedded PDV)';
break;
case asn1.Type.UTF8:
rval += ' (UTF8)';
break;
case asn1.Type.ROID:
rval += ' (Relative Object Identifier)';
break;
case asn1.Type.SEQUENCE:
rval += ' (Sequence)';
break;
case asn1.Type.SET:
rval += ' (Set)';
break;
case asn1.Type.PRINTABLESTRING:
rval += ' (Printable String)';
break;
case asn1.Type.IA5String:
rval += ' (IA5String (ASCII))';
break;
case asn1.Type.UTCTIME:
rval += ' (UTC time)';
break;
case asn1.Type.GENERALIZEDTIME:
rval += ' (Generalized time)';
break;
case asn1.Type.BMPSTRING:
rval += ' (BMP String)';
break;
}
}
else {
rval += obj.type;
}
rval += '\n';
rval += indent + 'Constructed: ' + obj.constructed + '\n';
if(obj.composed) {
var subvalues = 0;
var sub = '';
for(var i = 0; i < obj.value.length; ++i) {
if(obj.value[i] !== undefined) {
subvalues += 1;
sub += asn1.prettyPrint(obj.value[i], level + 1, indentation);
if((i + 1) < obj.value.length) {
sub += ',';
}
}
}
rval += indent + 'Sub values: ' + subvalues + sub;
}
else {
rval += indent + 'Value: ';
if(obj.type === asn1.Type.OID) {
var oid = asn1.derToOid(obj.value);
rval += oid;
if(forge.pki && forge.pki.oids) {
if(oid in forge.pki.oids) {
rval += ' (' + forge.pki.oids[oid] + ')';
}
}
}
// FIXME: choose output (hex vs. printable) based on asn1.Type
else if(_nonLatinRegex.test(obj.value)) {
rval += '0x' + forge.util.createBuffer(obj.value, 'utf8').toHex();
}
else if(obj.value.length === 0) {
rval += '[null]';
}
else {
rval += obj.value;
}
}
return rval;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'asn1';
var deps = ['./util', './oids'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* Javascript implementation of a basic RSA algorithms.
*
* @author Dave Longley
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
*/
(function() {
function initModule(forge) {
/* ########## Begin module implementation ########## */
var _nodejs = (typeof module === 'object' && module.exports);
if(typeof BigInteger === 'undefined') {
BigInteger = forge.jsbn.BigInteger;
}
// shortcut for asn.1 API
var asn1 = forge.asn1;
/*
* RSA encryption and decryption, see RFC 2313.
*/
forge.pki = forge.pki || {};
forge.pki.rsa = forge.rsa = forge.rsa || {};
var pki = forge.pki;
// for finding primes, which 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];
/**
* Wrap digest in DigestInfo object.
*
* This function implements EMSA-PKCS1-v1_5-ENCODE as per RFC 3447.
*
* DigestInfo ::= SEQUENCE {
* digestAlgorithm DigestAlgorithmIdentifier,
* digest Digest
* }
*
* DigestAlgorithmIdentifier ::= AlgorithmIdentifier
* Digest ::= OCTET STRING
*
* @param md the message digest object with the hash to sign.
* @return the encoded message (ready for RSA encrytion)
*/
var emsaPkcs1v15encode = function(md) {
// get the oid for the algorithm
var oid;
if(md.algorithm in forge.pki.oids) {
oid = forge.pki.oids[md.algorithm];
}
else {
throw {
message: 'Unknown message digest algorithm.',
algorithm: md.algorithm
};
}
var oidBytes = asn1.oidToDer(oid).getBytes();
// create the digest info
var digestInfo = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
var digestAlgorithm = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
digestAlgorithm.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OID, false, oidBytes));
digestAlgorithm.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''));
var digest = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING,
false, md.digest().getBytes());
digestInfo.value.push(digestAlgorithm);
digestInfo.value.push(digest);
// encode digest info
return asn1.toDer(digestInfo).getBytes();
};
/**
* Performs x^c mod n (RSA encryption or decryption operation).
*
* @param x the number to raise and mod.
* @param key the key to use.
* @param pub true if the key is public, false if private.
*
* @return the result of x^c mod n.
*/
var _modPow = function(x, key, pub) {
var y;
if(pub) {
y = x.modPow(key.e, key.n);
}
else {
// pre-compute dP, dQ, and qInv if necessary
if(!key.dP) {
key.dP = key.d.mod(key.p.subtract(BigInteger.ONE));
}
if(!key.dQ) {
key.dQ = key.d.mod(key.q.subtract(BigInteger.ONE));
}
if(!key.qInv) {
key.qInv = key.q.modInverse(key.p);
}
/* Chinese remainder theorem (CRT) states:
Suppose n1, n2, ..., nk are positive integers which are pairwise
coprime (n1 and n2 have no common factors other than 1). For any
integers x1, x2, ..., xk there exists an integer x solving the
system of simultaneous congruences (where ~= means modularly
congruent so a ~= b mod n means a mod n = b mod n):
x ~= x1 mod n1
x ~= x2 mod n2
...
x ~= xk mod nk
This system of congruences has a single simultaneous solution x
between 0 and n - 1. Furthermore, each xk solution and x itself
is congruent modulo the product n = n1*n2*...*nk.
So x1 mod n = x2 mod n = xk mod n = x mod n.
The single simultaneous solution x can be solved with the following
equation:
x = sum(xi*ri*si) mod n where ri = n/ni and si = ri^-1 mod ni.
Where x is less than n, xi = x mod ni.
For RSA we are only concerned with k = 2. The modulus n = pq, where
p and q are coprime. The RSA decryption algorithm is:
y = x^d mod n
Given the above:
x1 = x^d mod p
r1 = n/p = q
s1 = q^-1 mod p
x2 = x^d mod q
r2 = n/q = p
s2 = p^-1 mod q
So y = (x1r1s1 + x2r2s2) mod n
= ((x^d mod p)q(q^-1 mod p) + (x^d mod q)p(p^-1 mod q)) mod n
According to Fermat's Little Theorem, if the modulus P is prime,
for any integer A not evenly divisible by P, A^(P-1) ~= 1 mod P.
Since A is not divisible by P it follows that if:
N ~= M mod (P - 1), then A^N mod P = A^M mod P. Therefore:
A^N mod P = A^(M mod (P - 1)) mod P. (The latter takes less effort
to calculate). In order to calculate x^d mod p more quickly the
exponent d mod (p - 1) is stored in the RSA private key (the same
is done for x^d mod q). These values are referred to as dP and dQ
respectively. Therefore we now have:
y = ((x^dP mod p)q(q^-1 mod p) + (x^dQ mod q)p(p^-1 mod q)) mod n
Since we'll be reducing x^dP by modulo p (same for q) we can also
reduce x by p (and q respectively) before hand. Therefore, let
xp = ((x mod p)^dP mod p), and
xq = ((x mod q)^dQ mod q), yielding:
y = (xp*q*(q^-1 mod p) + xq*p*(p^-1 mod q)) mod n
This can be further reduced to a simple algorithm that only
requires 1 inverse (the q inverse is used) to be used and stored.
The algorithm is called Garner's algorithm. If qInv is the
inverse of q, we simply calculate:
y = (qInv*(xp - xq) mod p) * q + xq
However, there are two further complications. First, we need to
ensure that xp > xq to prevent signed BigIntegers from being used
so we add p until this is true (since we will be mod'ing with
p anyway). Then, there is a known timing attack on algorithms
using the CRT. To mitigate this risk, "cryptographic blinding"
should be used (*Not yet implemented*). This requires simply
generating a random number r between 0 and n-1 and its inverse
and multiplying x by r^e before calculating y and then multiplying
y by r^-1 afterwards.
*/
// TODO: do cryptographic blinding
// calculate xp and xq
var xp = x.mod(key.p).modPow(key.dP, key.p);
var xq = x.mod(key.q).modPow(key.dQ, key.q);
// xp must be larger than xq to avoid signed bit usage
while(xp.compareTo(xq) < 0) {
xp = xp.add(key.p);
}
// do last step
y = xp.subtract(xq)
.multiply(key.qInv).mod(key.p)
.multiply(key.q).add(xq);
}
return y;
};
/**
* Performs RSA encryption.
*
* The parameter bt controls whether to put padding bytes before the
* message passed in. Set bt to either true or false to disable padding
* completely (in order to handle e.g. EMSA-PSS encoding seperately before),
* signaling whether the encryption operation is a public key operation
* (i.e. encrypting data) or not, i.e. private key operation (data signing).
*
* For PKCS#1 v1.5 padding pass in the block type to use, i.e. either 0x01
* (for signing) or 0x02 (for encryption). The key operation mode (private
* or public) is derived from this flag in that case).
*
* @param m the message to encrypt as a byte string.
* @param key the RSA key to use.
* @param bt for PKCS#1 v1.5 padding, the block type to use
* (0x01 for private key, 0x02 for public),
* to disable padding: true = public key, false = private key
* @return the encrypted bytes as a string.
*/
pki.rsa.encrypt = function(m, key, bt) {
var pub = bt;
var eb = forge.util.createBuffer();
// get the length of the modulus in bytes
var k = Math.ceil(key.n.bitLength() / 8);
if(bt !== false && bt !== true) {
/* use PKCS#1 v1.5 padding */
if(m.length > (k - 11)) {
throw {
message: 'Message is too long to encrypt.',
length: m.length,
max: (k - 11)
};
}
/* A block type BT, a padding string PS, and the data D shall be
formatted into an octet string EB, the encryption block:
EB = 00 || BT || PS || 00 || D
The block type BT shall be a single octet indicating the structure of
the encryption block. For this version of the document it shall have
value 00, 01, or 02. For a private-key operation, the block type
shall be 00 or 01. For a public-key operation, it shall be 02.
The padding string PS shall consist of k-3-||D|| octets. For block
type 00, the octets shall have value 00; for block type 01, they
shall have value FF; and for block type 02, they shall be
pseudorandomly generated and nonzero. This makes the length of the
encryption block EB equal to k. */
// build the encryption block
eb.putByte(0x00);
eb.putByte(bt);
// create the padding, get key type
var padNum = k - 3 - m.length;
var padByte;
if(bt === 0x00 || bt === 0x01) {
pub = false;
padByte = (bt === 0x00) ? 0x00 : 0xFF;
for(var i = 0; i < padNum; ++i) {
eb.putByte(padByte);
}
}
else {
pub = true;
for(var i = 0; i < padNum; ++i) {
padByte = Math.floor(Math.random() * 255) + 1;
eb.putByte(padByte);
}
}
// zero followed by message
eb.putByte(0x00);
}
eb.putBytes(m);
// load encryption block as big integer 'x'
// FIXME: hex conversion inefficient, get BigInteger w/byte strings
var x = new BigInteger(eb.toHex(), 16);
// do RSA encryption
var y = _modPow(x, key, pub);
// convert y into the encrypted data byte string, if y is shorter in
// bytes than k, then prepend zero bytes to fill up ed
// FIXME: hex conversion inefficient, get BigInteger w/byte strings
var yhex = y.toString(16);
var ed = forge.util.createBuffer();
var zeros = k - Math.ceil(yhex.length / 2);
while(zeros > 0) {
ed.putByte(0x00);
--zeros;
}
ed.putBytes(forge.util.hexToBytes(yhex));
return ed.getBytes();
};
/**
* Performs RSA decryption.
*
* The parameter ml controls whether to apply PKCS#1 v1.5 padding
* or not. Set ml = false to disable padding removal completely
* (in order to handle e.g. EMSA-PSS later on) and simply pass back
* the RSA encryption block.
*
* @param ed the encrypted data to decrypt in as a byte string.
* @param key the RSA key to use.
* @param pub true for a public key operation, false for private.
* @param ml the message length, if known. false to disable padding.
*
* @return the decrypted message as a byte string.
*/
pki.rsa.decrypt = function(ed, key, pub, ml) {
// get the length of the modulus in bytes
var k = Math.ceil(key.n.bitLength() / 8);
// error if the length of the encrypted data ED is not k
if(ed.length != k) {
throw {
message: 'Encrypted message length is invalid.',
length: ed.length,
expected: k
};
}
// convert encrypted data into a big integer
// FIXME: hex conversion inefficient, get BigInteger w/byte strings
var y = new BigInteger(forge.util.createBuffer(ed).toHex(), 16);
// do RSA decryption
var x = _modPow(y, key, pub);
// create the encryption block, if x is shorter in bytes than k, then
// prepend zero bytes to fill up eb
// FIXME: hex conversion inefficient, get BigInteger w/byte strings
var xhex = x.toString(16);
var eb = forge.util.createBuffer();
var zeros = k - Math.ceil(xhex.length / 2);
while(zeros > 0) {
eb.putByte(0x00);
--zeros;
}
eb.putBytes(forge.util.hexToBytes(xhex));
if(ml !== false) {
/* It is an error if any of the following conditions occurs:
1. The encryption block EB cannot be parsed unambiguously.
2. The padding string PS consists of fewer than eight octets
or is inconsisent with the block type BT.
3. The decryption process is a public-key operation and the block
type BT is not 00 or 01, or the decryption process is a
private-key operation and the block type is not 02.
*/
// parse the encryption block
var first = eb.getByte();
var bt = eb.getByte();
if(first !== 0x00 ||
(pub && bt !== 0x00 && bt !== 0x01) ||
(!pub && bt != 0x02) ||
(pub && bt === 0x00 && typeof(ml) === 'undefined')) {
throw {
message: 'Encryption block is invalid.'
};
}
var padNum = 0;
if(bt === 0x00) {
// check all padding bytes for 0x00
padNum = k - 3 - ml;
for(var i = 0; i < padNum; ++i) {
if(eb.getByte() !== 0x00) {
throw {
message: 'Encryption block is invalid.'
};
}
}
}
else if(bt === 0x01) {
// find the first byte that isn't 0xFF, should be after all padding
padNum = 0;
while(eb.length() > 1) {
if(eb.getByte() !== 0xFF) {
--eb.read;
break;
}
++padNum;
}
}
else if(bt === 0x02) {
// look for 0x00 byte
padNum = 0;
while(eb.length() > 1) {
if(eb.getByte() === 0x00) {
--eb.read;
break;
}
++padNum;
}
}
// zero must be 0x00 and padNum must be (k - 3 - message length)
var zero = eb.getByte();
if(zero !== 0x00 || padNum !== (k - 3 - eb.length())) {
throw {
message: 'Encryption block is invalid.'
};
}
}
// return message
return eb.getBytes();
};
/**
* Creates an RSA key-pair generation state object. It is used to allow
* key-generation to be performed in steps. It also allows for a UI to
* display progress updates.
*
* @param bits the size for the private key in bits, defaults to 1024.
* @param e the public exponent to use, defaults to 65537 (0x10001).
*
* @return the state object to use to generate the key-pair.
*/
pki.rsa.createKeyPairGenerationState = function(bits, e) {
// set default bits
if(typeof(bits) === 'string') {
bits = parseInt(bits, 10);
}
bits = bits || 1024;
// create prng with api that matches BigInteger secure random
var rng = {
// x is an array to fill with bytes
nextBytes: function(x) {
var b = forge.random.getBytes(x.length);
for(var i = 0; i < x.length; ++i) {
x[i] = b.charCodeAt(i);
}
}
};
var rval = {
state: 0,
bits: bits,
rng: rng,
eInt: e || 65537,
e: new BigInteger(null),
p: null,
q: null,
qBits: bits >> 1,
pBits: bits - (bits >> 1),
pqState: 0,
num: null,
keys: null
};
rval.e.fromInt(rval.eInt);
return rval;
};
/**
* Attempts to runs the key-generation algorithm for at most n seconds
* (approximately) using the given state. When key-generation has completed,
* the keys will be stored in state.keys.
*
* To use this function to update a UI while generating a key or to prevent
* causing browser lockups/warnings, set "n" to a value other than 0. A
* simple pattern for generating a key and showing a progress indicator is:
*
* var state = pki.rsa.createKeyPairGenerationState(2048);
* var step = function() {
* // step key-generation, run algorithm for 100 ms, repeat
* if(!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) {
* setTimeout(step, 1);
* }
* // key-generation complete
* else {
* // TODO: turn off progress indicator here
* // TODO: use the generated key-pair in "state.keys"
* }
* };
* // TODO: turn on progress indicator here
* setTimeout(step, 0);
*
* @param state the state to use.
* @param n the maximum number of milliseconds to run the algorithm for, 0
* to run the algorithm to completion.
*
* @return true if the key-generation completed, false if not.
*/
pki.rsa.stepKeyPairGenerationState = function(state, n) {
// do key generation (based on Tom Wu's rsa.js, see jsbn.js license)
// with some minor optimizations and designed to run in steps
// local state vars
var THIRTY = new BigInteger(null);
THIRTY.fromInt(30);
var deltaIdx = 0;
var op_or = function(x,y) { return x|y; };
// keep stepping until time limit is reached or done
var t1 = +new Date();
var t2;
var total = 0;
while(state.keys === null && (n <= 0 || total < n)) {
// generate p or q
if(state.state === 0) {
/* Note: All primes are of the form:
30k+i, for i < 30 and gcd(30, i)=1, where there are 8 values for i
When we generate a random number, we always align it 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 bits = (state.p === null) ? state.pBits : state.qBits;
var bits1 = bits - 1;
// get a random number
if(state.pqState === 0) {
state.num = new BigInteger(bits, state.rng);
// force MSB set
if(!state.num.testBit(bits1)) {
state.num.bitwiseTo(
BigInteger.ONE.shiftLeft(bits1), op_or, state.num);
}
// align number on 30k+1 boundary
state.num.dAddOffset(31 - state.num.mod(THIRTY).byteValue(), 0);
deltaIdx = 0;
++state.pqState;
}
// try to make the number a prime
else if(state.pqState === 1) {
// overflow, try again
if(state.num.bitLength() > bits) {
state.pqState = 0;
}
// do primality test
else if(state.num.isProbablePrime(1)) {
++state.pqState;
}
else {
// get next potential prime
state.num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
}
}
// ensure number is coprime with e
else if(state.pqState === 2) {
state.pqState =
(state.num.subtract(BigInteger.ONE).gcd(state.e)
.compareTo(BigInteger.ONE) === 0) ? 3 : 0;
}
// ensure number is a probable prime
else if(state.pqState === 3) {
state.pqState = 0;
if(state.num.isProbablePrime(10)) {
if(state.p === null) {
state.p = state.num;
}
else {
state.q = state.num;
}
// advance state if both p and q are ready
if(state.p !== null && state.q !== null) {
++state.state;
}
}
state.num = null;
}
}
// ensure p is larger than q (swap them if not)
else if(state.state === 1) {
if(state.p.compareTo(state.q) < 0) {
state.num = state.p;
state.p = state.q;
state.q = state.num;
}
++state.state;
}
// compute phi: (p - 1)(q - 1) (Euler's totient function)
else if(state.state === 2) {
state.p1 = state.p.subtract(BigInteger.ONE);
state.q1 = state.q.subtract(BigInteger.ONE);
state.phi = state.p1.multiply(state.q1);
++state.state;
}
// ensure e and phi are coprime
else if(state.state === 3) {
if(state.phi.gcd(state.e).compareTo(BigInteger.ONE) === 0) {
// phi and e are coprime, advance
++state.state;
}
else {
// phi and e aren't coprime, so generate a new p and q
state.p = null;
state.q = null;
state.state = 0;
}
}
// create n, ensure n is has the right number of bits
else if(state.state === 4) {
state.n = state.p.multiply(state.q);
// ensure n is right number of bits
if(state.n.bitLength() === state.bits) {
// success, advance
++state.state;
}
else {
// failed, get new q
state.q = null;
state.state = 0;
}
}
// set keys
else if(state.state === 5) {
var d = state.e.modInverse(state.phi);
state.keys = {
privateKey: forge.pki.rsa.setPrivateKey(
state.n, state.e, d, state.p, state.q,
d.mod(state.p1), d.mod(state.q1),
state.q.modInverse(state.p)),
publicKey: forge.pki.rsa.setPublicKey(state.n, state.e)
};
}
// update timing
t2 = +new Date();
total += t2 - t1;
t1 = t2;
}
return state.keys !== null;
};
/**
* Generates an RSA public-private key pair in a single call.
*
* To generate a key-pair in steps (to allow for progress updates and to
* prevent blocking or warnings in slow browsers) then use the key-pair
* generation state functions.
*
* To generate a key-pair asynchronously (either through web-workers, if
* available, or by breaking up the work on the main thread), pass a
* callback function.
*
* @param [bits] the size for the private key in bits, defaults to 1024.
* @param [e] the public exponent to use, defaults to 65537.
* @param [options] options for key-pair generation, if given then 'bits'
* and 'e' must *not* be given:
* bits the size for the private key in bits, (default: 1024).
* e the public exponent to use, (default: 65537 (0x10001)).
* workerScript the worker script URL.
* workers the number of web workers (if supported) to use,
* (default: 2).
* workLoad the size of the work load, ie: number of possible prime
* numbers for each web worker to check per work assignment,
* (default: 100).
* e the public exponent to use, defaults to 65537.
* @param [callback(err, keypair)] called once the operation completes.
*
* @return an object with privateKey and publicKey properties.
*/
pki.rsa.generateKeyPair = function(bits, e, options, callback) {
// (bits), (options), (callback)
if(arguments.length === 1) {
if(typeof bits === 'object') {
options = bits;
bits = undefined;
}
else if(typeof bits === 'function') {
callback = bits;
bits = undefined;
}
}
// (bits, options), (bits, callback), (options, callback)
else if(arguments.length === 2) {
if(typeof bits === 'number') {
if(typeof e === 'function') {
callback = e;
}
else {
options = e;
}
}
else {
options = bits;
callback = e;
bits = undefined;
}
e = undefined;
}
// (bits, e, options), (bits, e, callback), (bits, options, callback)
else if(arguments.length === 3) {
if(typeof e === 'number') {
if(typeof options === 'function') {
callback = options;
options = undefined;
}
}
else {
callback = options;
options = e;
e = undefined;
}
}
options = options || {};
if(bits === undefined) {
bits = options.bits || 1024;
}
if(e === undefined) {
e = options.e || 0x10001;
}
var state = pki.rsa.createKeyPairGenerationState(bits, e);
if(!callback) {
pki.rsa.stepKeyPairGenerationState(state, 0);
return state.keys;
}
_generateKeyPair(state, options, callback);
};
/**
* Sets an RSA public key from BigIntegers modulus and exponent.
*
* @param n the modulus.
* @param e the exponent.
*
* @return the public key.
*/
pki.rsa.setPublicKey = function(n, e) {
var key = {
n: n,
e: e
};
/**
* Encrypts the given data with this public key.
*
* @param data the byte string to encrypt.
*
* @return the encrypted byte string.
*/
key.encrypt = function(data) {
return pki.rsa.encrypt(data, key, 0x02);
};
/**
* Verifies the given signature against the given digest.
*
* PKCS#1 supports multiple (currently two) signature schemes:
* RSASSA-PKCS1-v1_5 and RSASSA-PSS.
*
* By default this implementation uses the "old scheme", i.e.
* RSASSA-PKCS1-v1_5, in which case once RSA-decrypted, the
* signature is an OCTET STRING that holds a DigestInfo.
*
* DigestInfo ::= SEQUENCE {
* digestAlgorithm DigestAlgorithmIdentifier,
* digest Digest
* }
* DigestAlgorithmIdentifier ::= AlgorithmIdentifier
* Digest ::= OCTET STRING
*
* To perform PSS signature verification, provide an instance
* of Forge PSS object as scheme parameter.
*
* @param digest the message digest hash to compare against the signature.
* @param signature the signature to verify.
* @param scheme signature scheme to use, undefined for PKCS#1 v1.5
* padding style.
* @return true if the signature was verified, false if not.
*/
key.verify = function(digest, signature, scheme) {
// do rsa decryption
var ml = scheme === undefined ? undefined : false;
var d = pki.rsa.decrypt(signature, key, true, ml);
if(scheme === undefined) {
// d is ASN.1 BER-encoded DigestInfo
var obj = asn1.fromDer(d);
// compare the given digest to the decrypted one
return digest === obj.value[1].value;
}
else {
return scheme.verify(digest, d, key.n.bitLength());
}
};
return key;
};
/**
* Sets an RSA private key from BigIntegers modulus, exponent, primes,
* prime exponents, and modular multiplicative inverse.
*
* @param n the modulus.
* @param e the public exponent.
* @param d the private exponent ((inverse of e) mod n).
* @param p the first prime.
* @param q the second prime.
* @param dP exponent1 (d mod (p-1)).
* @param dQ exponent2 (d mod (q-1)).
* @param qInv ((inverse of q) mod p)
*
* @return the private key.
*/
pki.rsa.setPrivateKey = function(n, e, d, p, q, dP, dQ, qInv) {
var key = {
n: n,
e: e,
d: d,
p: p,
q: q,
dP: dP,
dQ: dQ,
qInv: qInv
};
/**
* Decrypts the given data with this private key.
*
* @param data the byte string to decrypt.
*
* @return the decrypted byte string.
*/
key.decrypt = function(data) {
return pki.rsa.decrypt(data, key, false);
};
/**
* Signs the given digest, producing a signature.
*
* PKCS#1 supports multiple (currently two) signature schemes:
* RSASSA-PKCS1-v1_5 and RSASSA-PSS.
*
* By default this implementation uses the "old scheme", i.e.
* RSASSA-PKCS1-v1_5. In order to generate a PSS signature, provide
* an instance of Forge PSS object as scheme parameter.
*
* @param md the message digest object with the hash to sign.
* @param scheme signature scheme to use, undefined for PKCS#1 v1.5
* padding style.
* @return the signature as a byte string.
*/
key.sign = function(md, scheme) {
var bt = false; /* private key operation */
if(scheme === undefined) {
scheme = { encode: emsaPkcs1v15encode };
bt = 0x01;
}
var d = scheme.encode(md, key.n.bitLength());
return pki.rsa.encrypt(d, key, bt);
};
return key;
};
/**
* Runs the key-generation algorithm asynchronously, either in the background
* via Web Workers, or using the main thread and setImmediate.
*
* @param state the key-pair generation state.
* @param [options] options for key-pair generation:
* workerScript the worker script URL.
* workers the number of web workers (if supported) to use,
* (default: 2).
* workLoad the size of the work load, ie: number of possible prime
* numbers for each web worker to check per work assignment,
* (default: 100).
* @param callback(err, keypair) called once the operation completes.
*/
function _generateKeyPair(state, options, callback) {
if(typeof options === 'function') {
callback = options;
options = {};
}
// web workers unavailable, use setImmediate
if(typeof(Worker) === 'undefined') {
function step() {
// 10 ms gives 5ms of leeway for other calculations before dropping
// below 60fps (1000/60 == 16.67), but in reality, the number will
// likely be higher due to an 'atomic' big int modPow
if(forge.pki.rsa.stepKeyPairGenerationState(state, 10)) {
return callback(null, state.keys);
}
forge.util.setImmediate(step);
}
return step();
}
// use web workers to generate keys
var numWorkers = options.workers || 2;
var workLoad = options.workLoad || 100;
var range = workLoad * 30/8;
var workerScript = options.workerScript || 'forge/prime.worker.js';
var THIRTY = new BigInteger(null);
THIRTY.fromInt(30);
var op_or = function(x,y) { return x|y; };
generate();
function generate() {
// find p and then q (done in series to simplify setting worker number)
getPrime(state.pBits, function(err, num) {
if(err) {
return callback(err);
}
state.p = num;
getPrime(state.qBits, finish);
});
}
// implement prime number generation using web workers
function getPrime(bits, callback) {
// TODO: consider optimizing by starting workers outside getPrime() ...
// note that in order to clean up they will have to be made internally
// asynchronous which may actually be slower
// start workers immediately
var workers = [];
for(var i = 0; i < numWorkers; ++i) {
// FIXME: fix path or use blob URLs
workers[i] = new Worker(workerScript);
}
var running = numWorkers;
// initialize random number
var num = generateRandom();
// listen for requests from workers and assign ranges to find prime
for(var i = 0; i < numWorkers; ++i) {
workers[i].addEventListener('message', workerMessage);
}
/* Note: The distribution of random numbers is unknown. Therefore, each
web worker is continuously allocated a range of numbers to check for a
random number until one is found.
Every 30 numbers will be checked just 8 times, because prime numbers
have the form:
30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)
Therefore, if we want a web worker to run N checks before asking for
a new range of numbers, each range must contain N*30/8 numbers.
For 100 checks (workLoad), this is a range of 375. */
function generateRandom() {
var bits1 = bits - 1;
var num = new BigInteger(bits, state.rng);
// force MSB set
if(!num.testBit(bits1)) {
num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);
}
// align number on 30k+1 boundary
num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);
return num;
}
var found = false;
function workerMessage(e) {
// ignore message, prime already found
if(found) {
return;
}
--running;
var data = e.data;
if(data.found) {
// terminate all workers
for(var i = 0; i < workers.length; ++i) {
workers[i].terminate();
}
found = true;
return callback(null, new BigInteger(data.prime, 16));
}
// overflow, regenerate prime
if(num.bitLength() > bits) {
num = generateRandom();
}
// assign new range to check
var hex = num.toString(16);
// start prime search
e.target.postMessage({
e: state.eInt,
hex: hex,
workLoad: workLoad
});
num.dAddOffset(range, 0);
}
}
function finish(err, num) {
// set q
state.q = num;
// ensure p is larger than q (swap them if not)
if(state.p.compareTo(state.q) < 0) {
var tmp = state.p;
state.p = state.q;
state.q = tmp;
}
// compute phi: (p - 1)(q - 1) (Euler's totient function)
state.p1 = state.p.subtract(BigInteger.ONE);
state.q1 = state.q.subtract(BigInteger.ONE);
state.phi = state.p1.multiply(state.q1);
// ensure e and phi are coprime
if(state.phi.gcd(state.e).compareTo(BigInteger.ONE) !== 0) {
// phi and e aren't coprime, so generate a new p and q
state.p = state.q = null;
generate();
return;
}
// create n, ensure n is has the right number of bits
state.n = state.p.multiply(state.q);
if(state.n.bitLength() !== state.bits) {
// failed, get new q
state.q = null;
getPrime(state.qBits, finish);
return;
}
// set keys
var d = state.e.modInverse(state.phi);
state.keys = {
privateKey: forge.pki.rsa.setPrivateKey(
state.n, state.e, d, state.p, state.q,
d.mod(state.p1), d.mod(state.q1),
state.q.modInverse(state.p)),
publicKey: forge.pki.rsa.setPublicKey(state.n, state.e)
};
callback(null, state.keys);
}
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'rsa';
var deps = ['./asn1', './oids', './random', './util', './jsbn'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* Javascript implementation of a basic Public Key Infrastructure, including
* support for RSA public and private keys.
*
* @author Dave Longley
* @author Stefan Siegl <stesie@brokenpipe.de>
*
* Copyright (c) 2010-2013 Digital Bazaar, Inc.
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
*
* The ASN.1 representation of an X.509v3 certificate is as follows
* (see RFC 2459):
*
* Certificate ::= SEQUENCE {
* tbsCertificate TBSCertificate,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING
* }
*
* TBSCertificate ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* serialNumber CertificateSerialNumber,
* signature AlgorithmIdentifier,
* issuer Name,
* validity Validity,
* subject Name,
* subjectPublicKeyInfo SubjectPublicKeyInfo,
* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version shall be v2 or v3
* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version shall be v2 or v3
* extensions [3] EXPLICIT Extensions OPTIONAL
* -- If present, version shall be v3
* }
*
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
*
* CertificateSerialNumber ::= INTEGER
*
* Name ::= CHOICE {
* // only one possible choice for now
* RDNSequence
* }
*
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
*
* RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
*
* AttributeTypeAndValue ::= SEQUENCE {
* type AttributeType,
* value AttributeValue
* }
* AttributeType ::= OBJECT IDENTIFIER
* AttributeValue ::= ANY DEFINED BY AttributeType
*
* Validity ::= SEQUENCE {
* notBefore Time,
* notAfter Time
* }
*
* Time ::= CHOICE {
* utcTime UTCTime,
* generalTime GeneralizedTime
* }
*
* UniqueIdentifier ::= BIT STRING
*
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING
* }
*
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
*
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING
* }
*
* The only algorithm currently supported for PKI is RSA.
*
* An RSA key is often stored in ASN.1 DER format. The SubjectPublicKeyInfo
* ASN.1 structure is composed of an algorithm of type AlgorithmIdentifier
* and a subjectPublicKey of type bit string.
*
* The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
* for the algorithm, if any. In the case of RSA, there aren't any.
*
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING
* }
*
* AlgorithmIdentifer ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
*
* For an RSA public key, the subjectPublicKey is:
*
* RSAPublicKey ::= SEQUENCE {
* modulus INTEGER, -- n
* publicExponent INTEGER -- e
* }
*
* PrivateKeyInfo ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
* privateKey PrivateKey,
* attributes [0] IMPLICIT Attributes OPTIONAL
* }
*
* Version ::= INTEGER
* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
* PrivateKey ::= OCTET STRING
* Attributes ::= SET OF Attribute
*
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm EncryptionAlgorithmIdentifier,
* encryptedData EncryptedData
* }
*
* EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
* EncryptedData ::= OCTET STRING
*
* An RSA private key as the following structure:
*
* RSAPrivateKey ::= SEQUENCE {
* version Version,
* modulus INTEGER, -- n
* publicExponent INTEGER, -- e
* privateExponent INTEGER, -- d
* prime1 INTEGER, -- p
* prime2 INTEGER, -- q
* exponent1 INTEGER, -- d mod (p-1)
* exponent2 INTEGER, -- d mod (q-1)
* coefficient INTEGER -- (inverse of q) mod p
* }
*
* Version ::= INTEGER
*
* The OID for the RSA key algorithm is: 1.2.840.113549.1.1.1
*
* An EncryptedPrivateKeyInfo:
*
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm EncryptionAlgorithmIdentifier,
* encryptedData EncryptedData }
*
* EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
*
* EncryptedData ::= OCTET STRING
*
* RSASSA-PSS signatures are described in RFC 3447 and RFC 4055.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
if(typeof BigInteger === 'undefined') {
BigInteger = forge.jsbn.BigInteger;
}
// shortcut for asn.1 API
var asn1 = forge.asn1;
/* Public Key Infrastructure (PKI) implementation. */
var pki = forge.pki = forge.pki || {};
var oids = pki.oids;
pki.pbe = {};
// short name OID mappings
var _shortNames = {};
_shortNames['CN'] = oids['commonName'];
_shortNames['commonName'] = 'CN';
_shortNames['C'] = oids['countryName'];
_shortNames['countryName'] = 'C';
_shortNames['L'] = oids['localityName'];
_shortNames['localityName'] = 'L';
_shortNames['ST'] = oids['stateOrProvinceName'];
_shortNames['stateOrProvinceName'] = 'ST';
_shortNames['O'] = oids['organizationName'];
_shortNames['organizationName'] = 'O';
_shortNames['OU'] = oids['organizationalUnitName'];
_shortNames['organizationalUnitName'] = 'OU';
_shortNames['E'] = oids['emailAddress'];
_shortNames['emailAddress'] = 'E';
// validator for an SubjectPublicKeyInfo structure
// Note: Currently only works with an RSA public key
var publicKeyValidator = {
name: 'SubjectPublicKeyInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'subjectPublicKeyInfo',
value: [{
name: 'SubjectPublicKeyInfo.AlgorithmIdentifier',
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: 'publicKeyOid'
}]
}, {
// subjectPublicKey
name: 'SubjectPublicKeyInfo.subjectPublicKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.BITSTRING,
constructed: false,
value: [{
// RSAPublicKey
name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
optional: true,
captureAsn1: 'rsaPublicKey'
}]
}]
};
// validator for an RSA public key
var rsaPublicKeyValidator = {
// RSAPublicKey
name: 'RSAPublicKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
// modulus (n)
name: 'RSAPublicKey.modulus',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'publicKeyModulus'
}, {
// publicExponent (e)
name: 'RSAPublicKey.exponent',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'publicKeyExponent'
}]
};
// validator for an X.509v3 certificate
var x509CertificateValidator = {
name: 'Certificate',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'Certificate.TBSCertificate',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'tbsCertificate',
value: [{
name: 'Certificate.TBSCertificate.version',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 0,
constructed: true,
optional: true,
value: [{
name: 'Certificate.TBSCertificate.version.integer',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'certVersion'
}]
}, {
name: 'Certificate.TBSCertificate.serialNumber',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'certSerialNumber'
}, {
name: 'Certificate.TBSCertificate.signature',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'Certificate.TBSCertificate.signature.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'certinfoSignatureOid'
}, {
name: 'Certificate.TBSCertificate.signature.parameters',
tagClass: asn1.Class.UNIVERSAL,
optional: true,
captureAsn1: 'certinfoSignatureParams'
}]
}, {
name: 'Certificate.TBSCertificate.issuer',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'certIssuer'
}, {
name: 'Certificate.TBSCertificate.validity',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
// Note: UTC and generalized times may both appear so the capture
// names are based on their detected order, the names used below
// are only for the common case, which validity time really means
// "notBefore" and which means "notAfter" will be determined by order
value: [{
// notBefore (Time) (UTC time case)
name: 'Certificate.TBSCertificate.validity.notBefore (utc)',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.UTCTIME,
constructed: false,
optional: true,
capture: 'certValidity1UTCTime'
}, {
// notBefore (Time) (generalized time case)
name: 'Certificate.TBSCertificate.validity.notBefore (generalized)',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.GENERALIZEDTIME,
constructed: false,
optional: true,
capture: 'certValidity2GeneralizedTime'
}, {
// notAfter (Time) (only UTC time is supported)
name: 'Certificate.TBSCertificate.validity.notAfter (utc)',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.UTCTIME,
constructed: false,
optional: true,
capture: 'certValidity3UTCTime'
}, {
// notAfter (Time) (only UTC time is supported)
name: 'Certificate.TBSCertificate.validity.notAfter (generalized)',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.GENERALIZEDTIME,
constructed: false,
optional: true,
capture: 'certValidity4GeneralizedTime'
}]
}, {
// Name (subject) (RDNSequence)
name: 'Certificate.TBSCertificate.subject',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'certSubject'
},
// SubjectPublicKeyInfo
publicKeyValidator,
{
// issuerUniqueID (optional)
name: 'Certificate.TBSCertificate.issuerUniqueID',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 1,
constructed: true,
optional: true,
value: [{
name: 'Certificate.TBSCertificate.issuerUniqueID.id',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.BITSTRING,
constructed: false,
capture: 'certIssuerUniqueId'
}]
}, {
// subjectUniqueID (optional)
name: 'Certificate.TBSCertificate.subjectUniqueID',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 2,
constructed: true,
optional: true,
value: [{
name: 'Certificate.TBSCertificate.subjectUniqueID.id',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.BITSTRING,
constructed: false,
capture: 'certSubjectUniqueId'
}]
}, {
// Extensions (optional)
name: 'Certificate.TBSCertificate.extensions',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 3,
constructed: true,
captureAsn1: 'certExtensions',
optional: true
}]
}, {
// AlgorithmIdentifier (signature algorithm)
name: 'Certificate.signatureAlgorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
// algorithm
name: 'Certificate.signatureAlgorithm.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'certSignatureOid'
}, {
name: 'Certificate.TBSCertificate.signature.parameters',
tagClass: asn1.Class.UNIVERSAL,
optional: true,
captureAsn1: 'certSignatureParams'
}]
}, {
// SignatureValue
name: 'Certificate.signatureValue',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.BITSTRING,
constructed: false,
capture: 'certSignature'
}]
};
// validator for a PrivateKeyInfo structure
var privateKeyValidator = {
// PrivateKeyInfo
name: 'PrivateKeyInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
// Version (INTEGER)
name: 'PrivateKeyInfo.version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyVersion'
}, {
// privateKeyAlgorithm
name: 'PrivateKeyInfo.privateKeyAlgorithm',
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: 'privateKeyOid'
}]
}, {
// PrivateKey
name: 'PrivateKeyInfo',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OCTETSTRING,
constructed: false,
capture: 'privateKey'
}]
};
// validator for an RSA private key
var rsaPrivateKeyValidator = {
// RSAPrivateKey
name: 'RSAPrivateKey',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
// Version (INTEGER)
name: 'RSAPrivateKey.version',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyVersion'
}, {
// modulus (n)
name: 'RSAPrivateKey.modulus',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyModulus'
}, {
// publicExponent (e)
name: 'RSAPrivateKey.publicExponent',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyPublicExponent'
}, {
// privateExponent (d)
name: 'RSAPrivateKey.privateExponent',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyPrivateExponent'
}, {
// prime1 (p)
name: 'RSAPrivateKey.prime1',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyPrime1'
}, {
// prime2 (q)
name: 'RSAPrivateKey.prime2',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyPrime2'
}, {
// exponent1 (d mod (p-1))
name: 'RSAPrivateKey.exponent1',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyExponent1'
}, {
// exponent2 (d mod (q-1))
name: 'RSAPrivateKey.exponent2',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyExponent2'
}, {
// coefficient ((inverse of q) mod p)
name: 'RSAPrivateKey.coefficient',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.INTEGER,
constructed: false,
capture: 'privateKeyCoefficient'
}]
};
// 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'
}]
};
var rsassaPssParameterValidator = {
name: 'rsapss',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'rsapss.hashAlgorithm',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 0,
constructed: true,
value: [{
name: 'rsapss.hashAlgorithm.AlgorithmIdentifier',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Class.SEQUENCE,
constructed: true,
optional: true,
value: [{
name: 'rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'hashOid'
/* parameter block omitted, for SHA1 NULL anyhow. */
}]
}]
}, {
name: 'rsapss.maskGenAlgorithm',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 1,
constructed: true,
value: [{
name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Class.SEQUENCE,
constructed: true,
optional: true,
value: [{
name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'maskGenOid'
}, {
name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Type.OID,
constructed: false,
capture: 'maskGenHashOid'
/* parameter block omitted, for SHA1 NULL anyhow. */
}]
}]
}]
}, {
name: 'rsapss.saltLength',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 2,
optional: true,
value: [{
name: 'rsapss.saltLength.saltLength',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Class.INTEGER,
constructed: false,
capture: 'saltLength'
}]
}, {
name: 'rsapss.trailerField',
tagClass: asn1.Class.CONTEXT_SPECIFIC,
type: 3,
optional: true,
value: [{
name: 'rsapss.trailer.trailer',
tagClass: asn1.Class.UNIVERSAL,
type: asn1.Class.INTEGER,
constructed: false,
capture: 'trailer'
}]
}]
};
/**
* Converts an RDNSequence of ASN.1 DER-encoded RelativeDistinguishedName
* sets into an array with objects that have type and value properties.
*
* @param rdn the RDNSequence to convert.
* @param md a message digest to append type and value to if provided.
*/
pki.RDNAttributesAsArray = function(rdn, md) {
var rval = [];
// each value in 'rdn' in is a SET of RelativeDistinguishedName
var set, attr, obj;
for(var si = 0; si < rdn.value.length; ++si) {
// get the RelativeDistinguishedName set
set = rdn.value[si];
// each value in the SET is an AttributeTypeAndValue sequence
// containing first a type (an OID) and second a value (defined by
// the OID)
for(var i = 0; i < set.value.length; ++i) {
obj = {};
attr = set.value[i];
obj.type = asn1.derToOid(attr.value[0].value);
obj.value = attr.value[1].value;
obj.valueTagClass = attr.value[1].type;
// if the OID is known, get its name and short name
if(obj.type in oids) {
obj.name = oids[obj.type];
if(obj.name in _shortNames) {
obj.shortName = _shortNames[obj.name];
}
}
if(md) {
md.update(obj.type);
md.update(obj.value);
}
rval.push(obj);
}
}
return rval;
};
/**
* Gets an issuer or subject attribute from its name, type, or short name.
*
* @param obj the issuer or subject object.
* @param options a short name string or an object with:
* shortName the short name for the attribute.
* name the name for the attribute.
* type the type for the attribute.
*
* @return the attribute.
*/
var _getAttribute = function(obj, options) {
if(options.constructor == String) {
options = {shortName: options};
}
var rval = null;
var attr;
for(var i = 0; rval === null && i < obj.attributes.length; ++i) {
attr = obj.attributes[i];
if(options.type && options.type === attr.type) {
rval = attr;
}
else if(options.name && options.name === attr.name) {
rval = attr;
}
else if(options.shortName && options.shortName === attr.shortName) {
rval = attr;
}
}
return rval;
};
/**
* Converts an ASN.1 extensions object (with extension sequences as its
* values) into an array of extension objects with types and values.
*
* Supported extensions:
*
* id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
* KeyUsage ::= BIT STRING {
* digitalSignature (0),
* nonRepudiation (1),
* keyEncipherment (2),
* dataEncipherment (3),
* keyAgreement (4),
* keyCertSign (5),
* cRLSign (6),
* encipherOnly (7),
* decipherOnly (8)
* }
*
* id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
* BasicConstraints ::= SEQUENCE {
* cA BOOLEAN DEFAULT FALSE,
* pathLenConstraint INTEGER (0..MAX) OPTIONAL
* }
*
* subjectAltName EXTENSION ::= {
* SYNTAX GeneralNames
* IDENTIFIED BY id-ce-subjectAltName
* }
*
* GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
*
* GeneralName ::= CHOICE {
* otherName [0] INSTANCE OF OTHER-NAME,
* rfc822Name [1] IA5String,
* dNSName [2] IA5String,
* x400Address [3] ORAddress,
* directoryName [4] Name,
* ediPartyName [5] EDIPartyName,
* uniformResourceIdentifier [6] IA5String,
* IPAddress [7] OCTET STRING,
* registeredID [8] OBJECT IDENTIFIER
* }
*
* OTHER-NAME ::= TYPE-IDENTIFIER
*
* EDIPartyName ::= SEQUENCE {
* nameAssigner [0] DirectoryString {ub-name} OPTIONAL,
* partyName [1] DirectoryString {ub-name}
* }
*
* @param exts the extensions ASN.1 with extension sequences to parse.
*
* @return the array.
*/
var _parseExtensions = function(exts) {
var rval = [];
var e, ext, extseq;
for(var i = 0; i < exts.value.length; ++i) {
// get extension sequence
extseq = exts.value[i];
for(var ei = 0; ei < extseq.value.length; ++ei) {
// an extension has:
// [0] extnID OBJECT IDENTIFIER
// [1] critical BOOLEAN DEFAULT FALSE
// [2] extnValue OCTET STRING
ext = extseq.value[ei];
e = {};
e.id = asn1.derToOid(ext.value[0].value);
e.critical = false;
if(ext.value[1].type === asn1.Type.BOOLEAN) {
e.critical = (ext.value[1].value.charCodeAt(0) !== 0x00);
e.value = ext.value[2].value;
}
else {
e.value = ext.value[1].value;
}
// if the oid is known, get its name
if(e.id in oids) {
e.name = oids[e.id];
// handle key usage
if(e.name === 'keyUsage') {
// get value as BIT STRING
var ev = asn1.fromDer(e.value);
var b2 = 0x00;
var b3 = 0x00;
if(ev.value.length > 1) {
// skip first byte, just indicates unused bits which
// will be padded with 0s anyway
// get bytes with flag bits
b2 = ev.value.charCodeAt(1);
b3 = ev.value.length > 2 ? ev.value.charCodeAt(2) : 0;
}
// set flags
e.digitalSignature = (b2 & 0x80) == 0x80;
e.nonRepudiation = (b2 & 0x40) == 0x40;
e.keyEncipherment = (b2 & 0x20) == 0x20;
e.dataEncipherment = (b2 & 0x10) == 0x10;
e.keyAgreement = (b2 & 0x08) == 0x08;
e.keyCertSign = (b2 & 0x04) == 0x04;
e.cRLSign = (b2 & 0x02) == 0x02;
e.encipherOnly = (b2 & 0x01) == 0x01;
e.decipherOnly = (b3 & 0x80) == 0x80;
}
// handle basic constraints
else if(e.name === 'basicConstraints') {
// get value as SEQUENCE
var ev = asn1.fromDer(e.value);
// get cA BOOLEAN flag (defaults to false)
if(ev.value.length > 0) {
e.cA = (ev.value[0].value.charCodeAt(0) !== 0x00);
}
else {
e.cA = false;
}
// get path length constraint
if(ev.value.length > 1) {
var tmp = forge.util.createBuffer(ev.value[1].value);
e.pathLenConstraint = tmp.getInt(tmp.length() << 3);
}
}
// handle subjectAltName/issuerAltName
else if(
e.name === 'subjectAltName' ||
e.name === 'issuerAltName') {
e.altNames = [];
// ev is a SYNTAX SEQUENCE
var gn;
var ev = asn1.fromDer(e.value);
for(var n = 0; n < ev.value.length; ++n) {
// get GeneralName
gn = ev.value[n];
var altName = {
type: gn.type,
value: gn.value
};
e.altNames.push(altName);
// Note: Support for types 1,2,6,7,8
switch(gn.type) {
// rfc822Name
case 1:
// dNSName
case 2:
// uniformResourceIdentifier (URI)
case 6:
break;
// IPAddress
case 7:
// FIXME: convert to IPv4 dotted string/IPv6
break;
// registeredID
case 8:
altName.oid = asn1.derToOid(gn.value);
break;
default:
// unsupported
}
}
}
}
rval.push(e);
}
}
return rval;
};
// regex for stripping PEM header and footer
var _pemRegex = new RegExp(
'-----BEGIN [^-]+-----([A-Za-z0-9+\/=\\s]+)-----END [^-]+-----');
/**
* Converts PEM-formatted data to DER.
*
* @param pem the PEM-formatted data.
*
* @return the DER-formatted data.
*/
pki.pemToDer = function(pem) {
var rval = null;
// get matching base64
var m = _pemRegex.exec(pem);
if(m) {
// base64 decode to get DER
rval = forge.util.createBuffer(forge.util.decode64(m[1]));
}
else {
throw 'Invalid PEM format';
}
return rval;
};
/**
* Converts PEM-formatted data into an certificate or key.
*
* @param pem the PEM-formatted data.
* @param func the certificate or key function to convert from ASN.1.
*
* @return the certificate or key.
*/
var _fromPem = function(pem, func) {
var rval = null;
// parse DER into asn.1 object
var der = pki.pemToDer(pem);
var obj = asn1.fromDer(der);
// convert from asn.1
rval = func(obj);
return rval;
};
/**
* Converts a positive BigInteger into 2's-complement big-endian bytes.
*
* @param b the big integer to convert.
*
* @return the bytes.
*/
var _bnToBytes = function(b) {
// prepend 0x00 if first byte >= 0x80
var hex = b.toString(16);
if(hex[0] >= '8') {
hex = '00' + hex;
}
return forge.util.hexToBytes(hex);
};
/**
* Converts signature parameters from ASN.1 structure.
*
* Currently only RSASSA-PSS supported. The PKCS#1 v1.5 signature scheme had
* no parameters.
*
* RSASSA-PSS-params ::= SEQUENCE {
* hashAlgorithm [0] HashAlgorithm DEFAULT
* sha1Identifier,
* maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT
* mgf1SHA1Identifier,
* saltLength [2] INTEGER DEFAULT 20,
* trailerField [3] INTEGER DEFAULT 1
* }
*
* HashAlgorithm ::= AlgorithmIdentifier
*
* MaskGenAlgorithm ::= AlgorithmIdentifier
*
* AlgorithmIdentifer ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
*
* @param oid The OID specifying the signature algorithm
* @param obj The ASN.1 structure holding the parameters
* @param fillDefaults Whether to use return default values where omitted
* @return signature parameter object
*/
var _readSignatureParameters = function(oid, obj, fillDefaults) {
var params = {};
if(oid !== oids['RSASSA-PSS']) {
return params;
}
if(fillDefaults) {
params = {
hash: {
algorithmOid: oids['sha1']
},
mgf: {
algorithmOid: oids['mgf1'],
hash: {
algorithmOid: oids['sha1']
}
},
saltLength: 20
};
}
var capture = {};
var errors = [];
if(!asn1.validate(obj, rsassaPssParameterValidator, capture, errors)) {
throw {
message: 'Cannot read RSASSA-PSS parameter block.',
errors: errors
};
}
if(capture.hashOid !== undefined) {
params.hash = params.hash || {};
params.hash.algorithmOid = asn1.derToOid(capture.hashOid);
}
if(capture.maskGenOid !== undefined) {
params.mgf = params.mgf || {};
params.mgf.algorithmOid = asn1.derToOid(capture.maskGenOid);
params.mgf.hash = params.mgf.hash || {};
params.mgf.hash.algorithmOid = asn1.derToOid(capture.maskGenHashOid);
}
if(capture.saltLength !== undefined) {
params.saltLength = capture.saltLength.charCodeAt(0);
}
return params;
};
/**
* Converts an X.509 certificate from PEM format.
*
* Note: If the certificate is to be verified then compute hash should
* be set to true. This will scan the TBSCertificate part of the ASN.1
* object while it is converted so it doesn't need to be converted back
* to ASN.1-DER-encoding later.
*
* @param pem the PEM-formatted certificate.
* @param computeHash true to compute the hash for verification.
*
* @return the certificate.
*/
pki.certificateFromPem = function(pem, computeHash) {
return _fromPem(pem, function(obj) {
return pki.certificateFromAsn1(obj, computeHash);
});
};
/**
* Converts an X.509 certificate to PEM format.
*
* @param cert the certificate.
* @param maxline the maximum characters per line, defaults to 64.
*
* @return the PEM-formatted certificate.
*/
pki.certificateToPem = function(cert, maxline) {
// convert to ASN.1, then DER, then base64-encode
var out = asn1.toDer(pki.certificateToAsn1(cert));
out = forge.util.encode64(out.getBytes(), maxline || 64);
return (
'-----BEGIN CERTIFICATE-----\r\n' +
out +
'\r\n-----END CERTIFICATE-----');
};
/**
* Converts an RSA public key from PEM format.
*
* @param pem the PEM-formatted public key.
*
* @return the public key.
*/
pki.publicKeyFromPem = function(pem) {
return _fromPem(pem, pki.publicKeyFromAsn1);
};
/**
* Converts an RSA public key to PEM format.
*
* @param key the public key.
* @param maxline the maximum characters per line, defaults to 64.
*
* @return the PEM-formatted public key.
*/
pki.publicKeyToPem = function(key, maxline) {
// convert to ASN.1, then DER, then base64-encode
var out = asn1.toDer(pki.publicKeyToAsn1(key));
out = forge.util.encode64(out.getBytes(), maxline || 64);
return (
'-----BEGIN PUBLIC KEY-----\r\n' +
out +
'\r\n-----END PUBLIC KEY-----');
};
/**
* Converts an RSA private key from PEM format.
*
* @param pem the PEM-formatted private key.
*
* @return the private key.
*/
pki.privateKeyFromPem = function(pem) {
return _fromPem(pem, pki.privateKeyFromAsn1);
};
/**
* 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 base64-encode
var out = asn1.toDer(pki.privateKeyToAsn1(key));
out = forge.util.encode64(out.getBytes(), maxline || 64);
return (
'-----BEGIN RSA PRIVATE KEY-----\r\n' +
out +
'\r\n-----END RSA PRIVATE KEY-----');
};
/**
* Creates an empty X.509v3 RSA certificate.
*
* @return the certificate.
*/
pki.createCertificate = function() {
var cert = {};
cert.version = 0x02;
cert.serialNumber = '00';
cert.signatureOid = null;
cert.signature = null;
cert.siginfo = {};
cert.siginfo.algorithmOid = null;
cert.validity = {};
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.issuer = {};
cert.issuer.getField = function(sn) {
return _getAttribute(cert.issuer, sn);
};
cert.issuer.addField = function(attr) {
_fillMissingFields([attr]);
cert.issuer.attributes.push(attr);
};
cert.issuer.attributes = [];
cert.issuer.hash = null;
cert.subject = {};
cert.subject.getField = function(sn) {
return _getAttribute(cert.subject, sn);
};
cert.subject.addField = function(attr) {
_fillMissingFields([attr]);
cert.subject.attributes.push(attr);
};
cert.subject.attributes = [];
cert.subject.hash = null;
cert.extensions = [];
cert.publicKey = null;
cert.md = null;
/**
* Fills in missing fields in attributes.
*
* @param attrs the attributes to fill missing fields in.
*/
var _fillMissingFields = function(attrs) {
var attr;
for(var i = 0; i < attrs.length; ++i) {
attr = attrs[i];
// populate missing name
if(typeof(attr.name) === 'undefined') {
if(attr.type && attr.type in pki.oids) {
attr.name = pki.oids[attr.type];
}
else if(attr.shortName && attr.shortName in _shortNames) {
attr.name = pki.oids[_shortNames[attr.shortName]];
}
}
// populate missing type (OID)
if(typeof(attr.type) === 'undefined') {
if(attr.name && attr.name in pki.oids) {
attr.type = pki.oids[attr.name];
}
else {
throw {
message: 'Attribute type not specified.',
attribute: attr
};
}
}
// populate missing shortname
if(typeof(attr.shortName) === 'undefined') {
if(attr.name && attr.name in _shortNames) {
attr.shortName = _shortNames[attr.name];
}
}
if(typeof(attr.value) === 'undefined') {
throw {
message: 'Attribute value not specified.',
attribute: attr
};
}
}
};
/**
* Sets the subject of this certificate.
*
* @param attrs the array of subject attributes to use.
* @param uniqueId an optional a unique ID to use.
*/
cert.setSubject = function(attrs, uniqueId) {
// set new attributes, clear hash
_fillMissingFields(attrs);
cert.subject.attributes = attrs;
delete cert.subject.uniqueId;
if(uniqueId) {
cert.subject.uniqueId = uniqueId;
}
cert.subject.hash = null;
};
/**
* Sets the issuer of this certificate.
*
* @param attrs the array of issuer attributes to use.
* @param uniqueId an optional a unique ID to use.
*/
cert.setIssuer = function(attrs, uniqueId) {
// set new attributes, clear hash
_fillMissingFields(attrs);
cert.issuer.attributes = attrs;
delete cert.issuer.uniqueId;
if(uniqueId) {
cert.issuer.uniqueId = uniqueId;
}
cert.issuer.hash = null;
};
/**
* Sets the extensions of this certificate.
*
* @param exts the array of extensions to use.
*/
cert.setExtensions = function(exts) {
var e;
for(var i = 0; i < exts.length; ++i) {
e = exts[i];
// populate missing name
if(typeof(e.name) === 'undefined') {
if(e.id && e.id in pki.oids) {
e.name = pki.oids[e.id];
}
}
// populate missing id
if(typeof(e.id) === 'undefined') {
if(e.name && e.name in pki.oids) {
e.id = pki.oids[e.name];
}
else {
throw {
message: 'Extension ID not specified.',
extension: e
};
}
}
// handle missing value
if(typeof(e.value) === 'undefined') {
// value is a BIT STRING
if(e.name === 'keyUsage') {
// build flags
var unused = 0;
var b2 = 0x00;
var b3 = 0x00;
if(e.digitalSignature) {
b2 |= 0x80;
unused = 7;
}
if(e.nonRepudiation) {
b2 |= 0x40;
unused = 6;
}
if(e.keyEncipherment) {
b2 |= 0x20;
unused = 5;
}
if(e.dataEncipherment) {
b2 |= 0x10;
unused = 4;
}
if(e.keyAgreement) {
b2 |= 0x08;
unused = 3;
}
if(e.keyCertSign) {
b2 |= 0x04;
unused = 2;
}
if(e.cRLSign) {
b2 |= 0x02;
unused = 1;
}
if(e.encipherOnly) {
b2 |= 0x01;
unused = 0;
}
if(e.decipherOnly) {
b3 |= 0x80;
unused = 7;
}
// create bit string
var value = String.fromCharCode(unused);
if(b3 !== 0) {
value += String.fromCharCode(b2) + String.fromCharCode(b3);
}
else if(b2 !== 0) {
value += String.fromCharCode(b2);
}
e.value = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value);
}
// basicConstraints is a SEQUENCE
else if(e.name === 'basicConstraints') {
e.value = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
// cA BOOLEAN flag defaults to false
if(e.cA) {
e.value.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false,
String.fromCharCode(0xFF)));
}
if(e.pathLenConstraint) {
var num = e.pathLenConstraint;
var tmp = forge.util.createBuffer();
tmp.putInt(num, num.toString(2).length);
e.value.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
tmp.getBytes()));
}
}
else if(e.name === 'subjectAltName' || e.name === 'issuerAltName') {
// SYNTAX SEQUENCE
e.value = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
var altName;
for(var n = 0; n < e.altNames.length; ++n) {
altName = e.altNames[n];
var value = altName.value;
// handle OID
if(altName.type === 8) {
value = asn1.oidToDer(value);
}
e.value.value.push(asn1.create(
asn1.Class.CONTEXT_SPECIFIC, altName.type, false,
value));
}
}
// ensure value has been defined by now
if(typeof(e.value) === 'undefined') {
throw {
message: 'Extension value not specified.',
extension: e
};
}
}
}
// set new extensions
cert.extensions = exts;
};
/**
* Gets an extension by its name or id.
*
* @param options the name to use or an object with:
* name the name to use.
* id the id to use.
*
* @return the extension or null if not found.
*/
cert.getExtension = function(options) {
if(options.constructor == String) {
options = {
name: options
};
}
var rval = null;
var ext;
for(var i = 0; rval === null && i < cert.extensions.length; ++i) {
ext = cert.extensions[i];
if(options.id && ext.id === options.id) {
rval = ext;
}
else if(options.name && ext.name === options.name) {
rval = ext;
}
}
return rval;
};
/**
* Signs this certificate using the given private key.
*
* @param key the private key to sign with.
*/
cert.sign = function(key) {
// TODO: get signature OID from private key
cert.signatureOid = oids['sha1withRSAEncryption'];
cert.siginfo.algorithmOid = oids['sha1withRSAEncryption'];
cert.md = forge.md.sha1.create();
// get TBSCertificate, convert to DER
cert.tbsCertificate = pki.getTBSCertificate(cert);
var bytes = asn1.toDer(cert.tbsCertificate);
// digest and sign
cert.md.update(bytes.getBytes());
cert.signature = key.sign(cert.md);
};
/**
* Attempts verify the signature on the passed certificate using this
* certificate's public key.
*
* @param child the certificate to verify.
*
* @return true if verified, false if not.
*/
cert.verify = function(child) {
var rval = false;
var md = child.md;
if(md === null) {
// check signature OID for supported signature types
if(cert.signatureOid in oids) {
var oid = oids[cert.signatureOid];
switch(oid) {
case 'sha1withRSAEncryption':
md = forge.md.sha1.create();
break;
case 'md5withRSAEncryption':
md = forge.md.md5.create();
break;
case 'sha256WithRSAEncryption':
md = forge.md.sha256.create();
break;
case 'RSASSA-PSS':
md = forge.md.sha256.create();
break;
}
}
if(md === null) {
throw {
message: 'Could not compute certificate digest. ' +
'Unknown signature OID.',
signatureOid: cert.signatureOid
};
}
// produce DER formatted TBSCertificate and digest it
var tbsCertificate = child.tbsCertificate || pki.getTBSCertificate(child);
var bytes = asn1.toDer(tbsCertificate);
md.update(bytes.getBytes());
}
if(md !== null) {
var scheme = undefined;
switch(child.signatureOid) {
case oids['sha1withRSAEncryption']:
scheme = undefined; /* use PKCS#1 v1.5 padding scheme */
break;
case oids['RSASSA-PSS']:
var hash, mgf;
/* initialize mgf */
hash = oids[child.signatureParameters.mgf.hash.algorithmOid];
if(hash === undefined || forge.md[hash] === undefined) {
throw {
message: 'Unsupported MGF hash function.',
oid: child.signatureParameters.mgf.hash.algorithmOid,
name: hash
};
}
mgf = oids[child.signatureParameters.mgf.algorithmOid];
if(mgf === undefined || forge.mgf[mgf] === undefined) {
throw {
message: 'Unsupported MGF function.',
oid: child.signatureParameters.mgf.algorithmOid,
name: mgf
};
}
mgf = forge.mgf[mgf].create(forge.md[hash].create());
/* initialize hash function */
hash = oids[child.signatureParameters.hash.algorithmOid];
if(hash === undefined || forge.md[hash] === undefined) {
throw {
message: 'Unsupported RSASSA-PSS hash function.',
oid: child.signatureParameters.hash.algorithmOid,
name: hash
};
}
scheme = forge.pss.create(forge.md[hash].create(), mgf,
child.signatureParameters.saltLength);
break;
}
// verify signature on cert using public key
rval = cert.publicKey.verify(
md.digest().getBytes(), child.signature, scheme);
}
return rval;
};
/**
* Returns true if the passed certificate's subject is the issuer of
* this certificate.
*
* @param parent the certificate to check.
*
* @return true if the passed certificate's subject is the issuer of
* this certificate.
*/
cert.isIssuer = function(parent) {
var rval = false;
var i = cert.issuer;
var s = parent.subject;
// compare hashes if present
if(i.hash && s.hash) {
rval = (i.hash === s.hash);
}
// if all attributes are the same then issuer matches subject
else if(i.attributes.length === s.attributes.length) {
rval = true;
var iattr, sattr;
for(var n = 0; rval && n < i.attributes.length; ++n) {
iattr = i.attributes[n];
sattr = s.attributes[n];
if(iattr.type !== sattr.type || iattr.value !== sattr.value) {
// attribute mismatch
rval = false;
}
}
}
return rval;
};
return cert;
};
/**
* Converts an X.509v3 RSA certificate from an ASN.1 object.
*
* Note: If the certificate is to be verified then compute hash should
* be set to true. There is currently no implementation for converting
* a certificate back to ASN.1 so the TBSCertificate part of the ASN.1
* object needs to be scanned before the cert object is created.
*
* @param obj the asn1 representation of an X.509v3 RSA certificate.
* @param computeHash true to compute the hash for verification.
*
* @return the certificate.
*/
pki.certificateFromAsn1 = function(obj, computeHash) {
// validate certificate and capture data
var capture = {};
var errors = [];
if(!asn1.validate(obj, x509CertificateValidator, capture, errors)) {
throw {
message: 'Cannot read X.509 certificate. ' +
'ASN.1 object is not an X509v3 Certificate.',
errors: errors
};
}
// get oid
var oid = asn1.derToOid(capture.publicKeyOid);
if(oid !== pki.oids['rsaEncryption']) {
throw {
message: 'Cannot read public key. OID is not RSA.'
};
}
// create certificate
var cert = pki.createCertificate();
cert.version = capture.certVersion ?
capture.certVersion.charCodeAt(0) : 0;
var serial = forge.util.createBuffer(capture.certSerialNumber);
cert.serialNumber = serial.toHex();
cert.signatureOid = forge.asn1.derToOid(capture.certSignatureOid);
cert.signatureParameters = _readSignatureParameters(cert.signatureOid,
capture.certSignatureParams, true);
cert.siginfo.algorithmOid = forge.asn1.derToOid(capture.certinfoSignatureOid);
cert.siginfo.parameters = _readSignatureParameters(cert.siginfo.algorithmOid,
capture.certinfoSignatureParams, false);
// skip "unused bits" in signature value BITSTRING
var signature = forge.util.createBuffer(capture.certSignature);
++signature.read;
cert.signature = signature.getBytes();
var validity = [];
if(capture.certValidity1UTCTime !== undefined) {
validity.push(asn1.utcTimeToDate(capture.certValidity1UTCTime));
}
if(capture.certValidity2GeneralizedTime !== undefined) {
validity.push(asn1.generalizedTimeToDate(
capture.certValidity2GeneralizedTime));
}
if(capture.certValidity3UTCTime !== undefined) {
validity.push(asn1.utcTimeToDate(capture.certValidity3UTCTime));
}
if(capture.certValidity4GeneralizedTime !== undefined) {
validity.push(asn1.generalizedTimeToDate(
capture.certValidity4GeneralizedTime));
}
if(validity.length > 2) {
throw {
message: 'Cannot read notBefore/notAfter validity times; more than ' +
'two times were provided in the certificate.'
};
}
if(validity.length < 2) {
throw {
message: 'Cannot read notBefore/notAfter validity times; they were not ' +
'provided as either UTCTime or GeneralizedTime.'
};
}
cert.validity.notBefore = validity[0];
cert.validity.notAfter = validity[1];
// keep TBSCertificate to preserve signature when exporting
cert.tbsCertificate = capture.tbsCertificate;
if(computeHash) {
// check signature OID for supported signature types
cert.md = null;
if(cert.signatureOid in oids) {
var oid = oids[cert.signatureOid];
switch(oid) {
case 'sha1withRSAEncryption':
cert.md = forge.md.sha1.create();
break;
case 'md5withRSAEncryption':
cert.md = forge.md.md5.create();
break;
case 'sha256WithRSAEncryption':
cert.md = forge.md.sha256.create();
break;
case 'RSASSA-PSS':
cert.md = forge.md.sha256.create();
break;
}
}
if(cert.md === null) {
throw {
message: 'Could not compute certificate digest. ' +
'Unknown signature OID.',
signatureOid: cert.signatureOid
};
}
// produce DER formatted TBSCertificate and digest it
var bytes = asn1.toDer(cert.tbsCertificate);
cert.md.update(bytes.getBytes());
}
// handle issuer, build issuer message digest
var imd = forge.md.sha1.create();
cert.issuer.attributes = pki.RDNAttributesAsArray(capture.certIssuer, imd);
if(capture.certIssuerUniqueId) {
cert.issuer.uniqueId = capture.certIssuerUniqueId;
}
cert.issuer.hash = imd.digest().toHex();
// handle subject, build subject message digest
var smd = forge.md.sha1.create();
cert.subject.attributes = pki.RDNAttributesAsArray(capture.certSubject, smd);
if(capture.certSubjectUniqueId) {
cert.subject.uniqueId = capture.certSubjectUniqueId;
}
cert.subject.hash = smd.digest().toHex();
// handle extensions
if(capture.certExtensions) {
cert.extensions = _parseExtensions(capture.certExtensions);
}
else {
cert.extensions = [];
}
// convert RSA public key from ASN.1
cert.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo);
return cert;
};
/**
* Converts an X.509 subject or issuer to an ASN.1 RDNSequence.
*
* @param obj the subject or issuer (distinguished name).
*
* @return the ASN.1 RDNSequence.
*/
_dnToAsn1 = function(obj) {
// create an empty RDNSequence
var rval = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
// iterate over attributes
var attr, set;
var attrs = obj.attributes;
for(var i = 0; i < attrs.length; ++i) {
attr = attrs[i];
var value = attr.value;
// reuse tag class for attribute value if available
var valueTagClass = asn1.Type.PRINTABLESTRING;
if('valueTagClass' in attr) {
valueTagClass = attr.valueTagClass;
if(valueTagClass === asn1.Type.UTF8) {
value = forge.util.encodeUtf8(value);
}
// FIXME: handle more encodings
}
// create a RelativeDistinguishedName set
// each value in the set is an AttributeTypeAndValue first
// containing the type (an OID) and second the value
set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// AttributeType
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(attr.type).getBytes()),
// AttributeValue
asn1.create(asn1.Class.UNIVERSAL, valueTagClass, false, value)
])
]);
rval.value.push(set);
}
return rval;
};
/**
* Converts X.509v3 certificate extensions to ASN.1.
*
* @param exts the extensions to convert.
*
* @return the extensions in ASN.1 format.
*/
_extensionsToAsn1 = function(exts) {
// create top-level extension container
var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, []);
// create extension sequence (stores a sequence for each extension)
var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
rval.value.push(seq);
var ext, extseq;
for(var i = 0; i < exts.length; ++i) {
ext = exts[i];
// create a sequence for each extension
extseq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
seq.value.push(extseq);
// extnID (OID)
extseq.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(ext.id).getBytes()));
// critical defaults to false
if(ext.critical) {
// critical BOOLEAN DEFAULT FALSE
extseq.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false,
String.fromCharCode(0xFF)));
}
var value = ext.value;
if(ext.value.constructor != String) {
// value is asn.1
value = asn1.toDer(value).getBytes();
}
// extnValue (OCTET STRING)
extseq.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, value));
}
return rval;
};
/**
* Convert signature parameters object to ASN.1
*
* @param {String} oid Signature algorithm OID
* @param params The signature parametrs object
* @return ASN.1 object representing signature parameters
*/
var _signatureParametersToAsn1 = function(oid, params) {
switch(oid) {
case oids['RSASSA-PSS']:
var parts = [];
if(params.hash.algorithmOid !== undefined) {
parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(params.hash.algorithmOid).getBytes()),
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
])
]));
}
if(params.mgf.algorithmOid !== undefined) {
parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(params.mgf.algorithmOid).getBytes()),
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(params.mgf.hash.algorithmOid).getBytes()),
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
])
])
]));
}
if(params.saltLength !== undefined) {
parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
String.fromCharCode(params.saltLength))
]));
}
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, parts);
default:
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '');
}
};
/**
* Gets the ASN.1 TBSCertificate part of an X.509v3 certificate.
*
* @param cert the certificate.
*
* @return the asn1 TBSCertificate.
*/
pki.getTBSCertificate = function(cert) {
// TBSCertificate
var tbs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// version
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
// integer
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
String.fromCharCode(cert.version))
]),
// serialNumber
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
forge.util.hexToBytes(cert.serialNumber)),
// signature
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// algorithm
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(cert.siginfo.algorithmOid).getBytes()),
// parameters (null)
_signatureParametersToAsn1(cert.siginfo.algorithmOid,
cert.siginfo.parameters)
]),
// issuer
_dnToAsn1(cert.issuer),
// validity
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// notBefore
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
asn1.dateToUtcTime(cert.validity.notBefore)),
// notAfter
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
asn1.dateToUtcTime(cert.validity.notAfter))
]),
// subject
_dnToAsn1(cert.subject),
// SubjectPublicKeyInfo
pki.publicKeyToAsn1(cert.publicKey)
]);
if(cert.issuer.uniqueId) {
// issuerUniqueID (optional)
tbs.value.push(
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
String.fromCharCode(0x00) +
cert.issuer.uniqueId
)
])
);
}
if(cert.subject.uniqueId) {
// subjectUniqueID (optional)
tbs.value.push(
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
String.fromCharCode(0x00) +
cert.subject.uniqueId
)
])
);
}
if(cert.extensions.length > 0) {
// extensions (optional)
tbs.value.push(_extensionsToAsn1(cert.extensions));
}
return tbs;
};
/**
* Converts a DistinguishedName (subject or issuer) to an ASN.1 object.
*
* @param dn the DistinguishedName.
*
* @return the asn1 representation of a DistinguishedName.
*/
pki.distinguishedNameToAsn1 = function(dn) {
return _dnToAsn1(dn);
};
/**
* Converts an X.509v3 RSA certificate to an ASN.1 object.
*
* @param cert the certificate.
*
* @return the asn1 representation of an X.509v3 RSA certificate.
*/
pki.certificateToAsn1 = function(cert) {
// prefer cached TBSCertificate over generating one
var tbsCertificate = cert.tbsCertificate || pki.getTBSCertificate(cert);
// Certificate
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// TBSCertificate
tbsCertificate,
// AlgorithmIdentifier (signature algorithm)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// algorithm
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(cert.signatureOid).getBytes()),
// parameters (null)
_signatureParametersToAsn1(cert.signatureOid, cert.signatureParameters)
]),
// SignatureValue
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,
String.fromCharCode(0x00) + cert.signature)
]);
};
/**
* Creates a CA store.
*
* @param certs an optional array of certificate objects or PEM-formatted
* certificate strings to add to the CA store.
*
* @return the CA store.
*/
pki.createCaStore = function(certs) {
// create CA store
var caStore = {
// stored certificates
certs: {}
};
/**
* Gets the certificate that issued the passed certificate or its
* 'parent'.
*
* @param cert the certificate to get the parent for.
*
* @return the parent certificate or null if none was found.
*/
caStore.getIssuer = function(cert) {
var rval = null;
// TODO: produce issuer hash if it doesn't exist
// get the entry using the cert's issuer hash
if(cert.issuer.hash in caStore.certs) {
rval = caStore.certs[cert.issuer.hash];
// see if there are multiple matches
if(rval.constructor == Array) {
// TODO: resolve multiple matches by checking
// authorityKey/subjectKey/issuerUniqueID/other identifiers, etc.
// FIXME: or alternatively do authority key mapping
// if possible (X.509v1 certs can't work?)
throw {
message: 'Resolving multiple issuer matches not implemented yet.'
};
}
}
return rval;
};
/**
* Adds a trusted certificate to the store.
*
* @param cert the certificate to add as a trusted certificate (either a
* pki.certificate object or a PEM-formatted certificate).
*/
caStore.addCertificate = function(cert) {
// convert from pem if necessary
if(cert.constructor == String) {
cert = forge.pki.certificateFromPem(cert);
}
// TODO: produce subject hash if it doesn't exist
if(cert.subject.hash in caStore.certs) {
// subject hash already exists, append to array
var tmp = caStore.certs[cert.subject.hash];
if(tmp.constructor != Array) {
tmp = [tmp];
}
tmp.push(cert);
}
else {
caStore.certs[cert.subject.hash] = cert;
}
};
// auto-add passed in certs
if(certs) {
// parse PEM-formatted certificates as necessary
for(var i = 0; i < certs.length; ++i) {
var cert = certs[i];
caStore.addCertificate(cert);
}
}
return caStore;
};
/**
* Certificate verification errors, based on TLS.
*/
pki.certificateError = {
bad_certificate: 'forge.pki.BadCertificate',
unsupported_certificate: 'forge.pki.UnsupportedCertificate',
certificate_revoked: 'forge.pki.CertificateRevoked',
certificate_expired: 'forge.pki.CertificateExpired',
certificate_unknown: 'forge.pki.CertificateUnknown',
unknown_ca: 'forge.pki.UnknownCertificateAuthority'
};
/**
* Verifies a certificate chain against the given Certificate Authority store
* with an optional custom verify callback.
*
* @param caStore a certificate store to verify against.
* @param chain the certificate chain to verify, with the root or highest
* authority at the end (an array of certificates).
* @param verify called for every certificate in the chain.
*
* The verify callback has the following signature:
*
* verified - Set to true if certificate was verified, otherwise the
* pki.certificateError for why the certificate failed.
* depth - The current index in the chain, where 0 is the end point's cert.
* certs - The certificate chain, *NOTE* an empty chain indicates an anonymous
* end point.
*
* The function returns true on success and on failure either the appropriate
* pki.certificateError or an object with 'error' set to the appropriate
* pki.certificateError and 'message' set to a custom error message.
*
* @return true if successful, error thrown if not.
*/
pki.verifyCertificateChain = function(caStore, chain, verify) {
/* From: RFC3280 - Internet X.509 Public Key Infrastructure Certificate
Section 6: Certification Path Validation
See inline parentheticals related to this particular implementation.
The primary goal of path validation is to verify the binding between
a subject distinguished name or a subject alternative name and subject
public key, as represented in the end entity certificate, based on the
public key of the trust anchor. This requires obtaining a sequence of
certificates that support that binding. That sequence should be provided
in the passed 'chain'. The trust anchor should be in the given CA
store. The 'end entity' certificate is the certificate provided by the
end point (typically a server) and is the first in the chain.
To meet this goal, the path validation process verifies, among other
things, that a prospective certification path (a sequence of n
certificates or a 'chain') satisfies the following conditions:
(a) for all x in {1, ..., n-1}, the subject of certificate x is
the issuer of certificate x+1;
(b) certificate 1 is issued by the trust anchor;
(c) certificate n is the certificate to be validated; and
(d) for all x in {1, ..., n}, the certificate was valid at the
time in question.
Note that here 'n' is index 0 in the chain and 1 is the last certificate
in the chain and it must be signed by a certificate in the connection's
CA store.
The path validation process also determines the set of certificate
policies that are valid for this path, based on the certificate policies
extension, policy mapping extension, policy constraints extension, and
inhibit any-policy extension.
Note: Policy mapping extension not supported (Not Required).
Note: If the certificate has an unsupported critical extension, then it
must be rejected.
Note: A certificate is self-issued if the DNs that appear in the subject
and issuer fields are identical and are not empty.
The path validation algorithm assumes the following seven inputs are
provided to the path processing logic. What this specific implementation
will use is provided parenthetically:
(a) a prospective certification path of length n (the 'chain')
(b) the current date/time: ('now').
(c) user-initial-policy-set: A set of certificate policy identifiers
naming the policies that are acceptable to the certificate user.
The user-initial-policy-set contains the special value any-policy
if the user is not concerned about certificate policy
(Not implemented. Any policy is accepted).
(d) trust anchor information, describing a CA that serves as a trust
anchor for the certification path. The trust anchor information
includes:
(1) the trusted issuer name,
(2) the trusted public key algorithm,
(3) the trusted public key, and
(4) optionally, the trusted public key parameters associated
with the public key.
(Trust anchors are provided via certificates in the CA store).
The trust anchor information may be provided to the path processing
procedure in the form of a self-signed certificate. The trusted anchor
information is trusted because it was delivered to the path processing
procedure by some trustworthy out-of-band procedure. If the trusted
public key algorithm requires parameters, then the parameters are
provided along with the trusted public key (No parameters used in this
implementation).
(e) initial-policy-mapping-inhibit, which indicates if policy mapping is
allowed in the certification path.
(Not implemented, no policy checking)
(f) initial-explicit-policy, which indicates if the path must be valid
for at least one of the certificate policies in the user-initial-
policy-set.
(Not implemented, no policy checking)
(g) initial-any-policy-inhibit, which indicates whether the
anyPolicy OID should be processed if it is included in a
certificate.
(Not implemented, so any policy is valid provided that it is
not marked as critical) */
/* Basic Path Processing:
For each certificate in the 'chain', the following is checked:
1. The certificate validity period includes the current time.
2. The certificate was signed by its parent (where the parent is
either the next in the chain or from the CA store).
3. TODO: The certificate has not been revoked.
4. The certificate issuer name matches the parent's subject name.
5. TODO: If the certificate is self-issued and not the final certificate
in the chain, skip this step, otherwise verify that the subject name
is within one of the permitted subtrees of X.500 distinguished names
and that each of the alternative names in the subjectAltName extension
(critical or non-critical) is within one of the permitted subtrees for
that name type.
6. TODO: If the certificate is self-issued and not the final certificate
in the chain, skip this step, otherwise verify that the subject name
is not within one of the excluded subtrees for X.500 distinguished
names and none of the subjectAltName extension names are excluded for
that name type.
7. The other steps in the algorithm for basic path processing involve
handling the policy extension which is not presently supported in this
implementation. Instead, if a critical policy extension is found, the
certificate is rejected as not supported.
8. If the certificate is not the first or the only certificate in the
chain and it has a critical key usage extension, verify that the
keyCertSign bit is set. If the key usage extension exists, verify that
the basic constraints extension exists. If the basic constraints
extension exists, verify that the cA flag is set.
TODO: handle pathLenConstraint by setting max path length to a lower
number if the parent certificate's pathLenConstraint is lower. Also
ensure that the path isn't already too long. */
// copy cert chain references to another array to protect against changes
// in verify callback
chain = chain.slice(0);
var certs = chain.slice(0);
// get current date
var now = new Date();
// verify each cert in the chain using its parent, where the parent
// is either the next in the chain or from the CA store
var first = true;
var error = null;
var depth = 0;
var parent = null;
do {
var cert = chain.shift();
// 1. check valid time
if(now < cert.validity.notBefore || now > cert.validity.notAfter) {
error = {
message: 'Certificate is not valid yet or has expired.',
error: pki.certificateError.certificate_expired,
notBefore: cert.validity.notBefore,
notAfter: cert.validity.notAfter,
now: now
};
}
// 2. verify with parent
else {
// get parent from chain
var verified = false;
if(chain.length > 0) {
// verify using parent
parent = chain[0];
try {
verified = parent.verify(cert);
}
catch(ex) {
// failure to verify, don't care why, just fail
}
}
// get parent(s) from CA store
else {
var parents = caStore.getIssuer(cert);
if(parents === null) {
// no parent issuer, so certificate not trusted
error = {
message: 'Certificate is not trusted.',
error: pki.certificateError.unknown_ca
};
}
else {
// CA store might have multiple certificates where the issuer
// can't be determined from the certificate (unlikely case for
// old certificates) so normalize by always putting parents into
// an array
if(parents.constructor !== Array) {
parents = [parents];
}
// multiple parents to try verifying with
while(!verified && parents.length > 0) {
parent = parents.shift();
try {
verified = parent.verify(cert);
}
catch(ex) {
// failure to verify, try next one
}
}
}
}
if(error === null && !verified) {
error = {
message: 'Certificate signature is invalid.',
error: pki.certificateError.bad_certificate
};
}
}
// TODO: 3. check revoked
// 4. check for matching issuer/subject
if(error === null && !cert.isIssuer(parent)) {
// parent is not issuer
error = {
message: 'Certificate issuer is invalid.',
error: pki.certificateError.bad_certificate
};
}
// 5. TODO: check names with permitted names tree
// 6. TODO: check names against excluded names tree
// 7. check for unsupported critical extensions
if(error === null) {
// supported extensions
var se = {
keyUsage: true,
basicConstraints: true
};
for(var i = 0; error === null && i < cert.extensions.length; ++i) {
var ext = cert.extensions[i];
if(ext.critical && !(ext.name in se)) {
error = {
message:
'Certificate has an unsupported critical extension.',
error: pki.certificateError.unsupported_certificate
};
}
}
}
// 8. check for CA if cert is not first or is the only certificate
// in chain with no parent, first check keyUsage extension and then basic
// constraints
if(!first || (chain.length === 0 && !parent)) {
var bcExt = cert.getExtension('basicConstraints');
var keyUsageExt = cert.getExtension('keyUsage');
if(keyUsageExt !== null) {
// keyCertSign must be true and there must be a basic
// constraints extension
if(!keyUsageExt.keyCertSign || bcExt === null) {
// bad certificate
error = {
message:
'Certificate keyUsage or basicConstraints conflict ' +
'or indicate that the certificate is not a CA. ' +
'If the certificate is the only one in the chain or ' +
'isn\'t the first then the certificate must be a ' +
'valid CA.',
error: pki.certificateError.bad_certificate
};
}
}
// basic constraints cA flag must be set
if(error === null && bcExt !== null && !bcExt.cA) {
// bad certificate
error = {
message:
'Certificate basicConstraints indicates the certificate ' +
'is not a CA.',
error: pki.certificateError.bad_certificate
};
}
}
// call application callback
var vfd = (error === null) ? true : error.error;
var ret = verify ? verify(vfd, depth, certs) : vfd;
if(ret === true) {
// clear any set error
error = null;
}
else {
// if passed basic tests, set default message and alert
if(vfd === true) {
error = {
message: 'The application rejected the certificate.',
error: pki.certificateError.bad_certificate
};
}
// check for custom error info
if(ret || ret === 0) {
// set custom message and error
if(ret.constructor === Object) {
if(ret.message) {
error.message = ret.message;
}
if(ret.error) {
error.error = ret.error;
}
}
else if(ret.constructor === String) {
// set custom error
error.error = ret;
}
}
// throw error
throw error;
}
// no longer first cert in chain
first = false;
++depth;
}
while(chain.length > 0);
return true;
};
/**
* Converts a public key from an ASN.1 object.
*
* @param obj the asn1 representation of a SubjectPublicKeyInfo.
*
* @return the public key.
*/
pki.publicKeyFromAsn1 = function(obj) {
// validate subject public key info and capture data
var capture = {};
var errors = [];
if(!asn1.validate(obj, publicKeyValidator, capture, errors)) {
throw {
message: 'Cannot read public key. ' +
'ASN.1 object is not a SubjectPublicKeyInfo.',
errors: errors
};
}
// get oid
var oid = asn1.derToOid(capture.publicKeyOid);
if(oid !== pki.oids['rsaEncryption']) {
throw {
message: 'Cannot read public key. Unknown OID.',
oid: oid
};
}
// get RSA params
errors = [];
if(!asn1.validate(
capture.rsaPublicKey, rsaPublicKeyValidator, capture, errors)) {
throw {
message: 'Cannot read public key. ' +
'ASN.1 object is not an RSAPublicKey.',
errors: errors
};
}
// FIXME: inefficient, get a BigInteger that uses byte strings
var n = forge.util.createBuffer(capture.publicKeyModulus).toHex();
var e = forge.util.createBuffer(capture.publicKeyExponent).toHex();
// set public key
return pki.setRsaPublicKey(
new BigInteger(n, 16),
new BigInteger(e, 16));
};
/**
* Converts a public key to an ASN.1 object.
*
* @param key the public key.
*
* @return the asn1 representation of a SubjectPublicKeyInfo.
*/
pki.publicKeyToAsn1 = function(key) {
// SubjectPublicKeyInfo
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// AlgorithmIdentifier
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// algorithm
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(pki.oids['rsaEncryption']).getBytes()),
// parameters (null)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
]),
// subjectPublicKey
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [
// RSAPublicKey
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// modulus (n)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.n)),
// publicExponent (e)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.e))
])
])
]);
};
/**
* Converts a private key from an ASN.1 object.
*
* @param obj the ASN.1 representation of a PrivateKeyInfo containing an
* RSAPrivateKey or an RSAPrivateKey.
*
* @return the private key.
*/
pki.privateKeyFromAsn1 = function(obj) {
// get PrivateKeyInfo
var capture = {};
var errors = [];
if(asn1.validate(obj, privateKeyValidator, capture, errors)) {
obj = asn1.fromDer(forge.util.createBuffer(capture.privateKey));
}
// get RSAPrivateKey
capture = {};
errors = [];
if(!asn1.validate(obj, rsaPrivateKeyValidator, capture, errors)) {
throw {
message: 'Cannot read private key. ' +
'ASN.1 object is not an RSAPrivateKey.',
errors: errors
};
}
// Note: Version is currently ignored.
// capture.privateKeyVersion
// FIXME: inefficient, get a BigInteger that uses byte strings
var n, e, d, p, q, dP, dQ, qInv;
n = forge.util.createBuffer(capture.privateKeyModulus).toHex();
e = forge.util.createBuffer(capture.privateKeyPublicExponent).toHex();
d = forge.util.createBuffer(capture.privateKeyPrivateExponent).toHex();
p = forge.util.createBuffer(capture.privateKeyPrime1).toHex();
q = forge.util.createBuffer(capture.privateKeyPrime2).toHex();
dP = forge.util.createBuffer(capture.privateKeyExponent1).toHex();
dQ = forge.util.createBuffer(capture.privateKeyExponent2).toHex();
qInv = forge.util.createBuffer(capture.privateKeyCoefficient).toHex();
// set private key
return pki.setRsaPrivateKey(
new BigInteger(n, 16),
new BigInteger(e, 16),
new BigInteger(d, 16),
new BigInteger(p, 16),
new BigInteger(q, 16),
new BigInteger(dP, 16),
new BigInteger(dQ, 16),
new BigInteger(qInv, 16));
};
/**
* Converts a private key to an ASN.1 RsaPrivateKey object.
*
* @param key the private key.
*
* @return the ASN.1 representation of an RSAPrivateKey.
*/
pki.privateKeyToAsn1 = function(key) {
// RSAPrivateKey
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// version (0 = only 2 primes, 1 multiple primes)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
String.fromCharCode(0x00)),
// modulus (n)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.n)),
// publicExponent (e)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.e)),
// privateExponent (d)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.d)),
// privateKeyPrime1 (p)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.p)),
// privateKeyPrime2 (q)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.q)),
// privateKeyExponent1 (dP)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.dP)),
// privateKeyExponent2 (dQ)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.dQ)),
// coefficient (qInv)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
_bnToBytes(key.qInv))
]);
};
/**
* Wraps an RSAPrivateKey ASN.1 object in an ASN.1 PrivateKeyInfo object.
*
* @param rsaKey the ASN.1 RSAPrivateKey.
*
* @return the ASN.1 PrivateKeyInfo.
*/
pki.wrapRsaPrivateKey = function(rsaKey) {
// get the oid for the algorithm
var oid = oids['rsaEncryption'];
var oidBytes = asn1.oidToDer(oid).getBytes();
// create the algorithm identifier
var algorithm = asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);
algorithm.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.OID, false, oidBytes));
algorithm.value.push(asn1.create(
asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''));
// PrivateKeyInfo
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// version (0)
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
String.fromCharCode(0x00)),
// privateKeyAlgorithm
algorithm,
// PrivateKey
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
asn1.toDer(rsaKey).getBytes())
]);
};
/**
* Encrypts a ASN.1 PrivateKeyInfo object.
*
* 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 = forge.pkcs12.generateKey(password, saltBytes, 1, count, dkLen);
var iv = forge.pkcs12.generateKey(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;
};
/**
* 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#12 PBE 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 = forge.pkcs12.generateKey(password, salt, 1, count, dkLen);
var iv = forge.pkcs12.generateKey(password, salt, 2, count, dIvLen);
return cipherFn(key, iv);
};
pki.pbe.getCipher = function(oid, params, password) {
switch(oid) {
case pki.oids['pkcs5PBES2']:
return pki.pbe.getCipherForPBES2(oid, params, password);
break;
case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
return pki.pbe.getCipherForPKCS12PBE(oid, params, password);
break;
default:
throw {
message: 'Cannot read encrypted PBE data block. Unsupported OID.',
oid: oid,
supportedOids: [
'pkcs5PBES2',
'pbeWithSHAAnd3-KeyTripleDES-CBC',
'pbewithSHAAnd40BitRC2-CBC'
]
};
}
};
/**
* 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 base64-encode
var out = asn1.toDer(epki);
out = forge.util.encode64(out.getBytes(), maxline || 64);
return (
'-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n' +
out +
'\r\n-----END ENCRYPTED PRIVATE KEY-----');
};
/**
* Converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format.
*
* @param pem the EncryptedPrivateKeyInfo in PEM-format.
*
* @return the ASN.1 EncryptedPrivateKeyInfo.
*/
pki.encryptedPrivateKeyFromPem = function(pem) {
// parse DER into asn.1 object
var der = pki.pemToDer(pem);
return asn1.fromDer(der);
};
/**
* Encrypts an RSA private key.
*
* @param rsaKey the RSA key to encrypt.
* @param password the password to use.
* @param options:
* encAlg the encryption algorithm to use
* ('aes128', 'aes192', 'aes256').
* count the iteration count to use.
* saltSize the salt size to use.
*
* @return the PEM-encoded ASN.1 EncryptedPrivateKeyInfo.
*/
pki.encryptRsaPrivateKey = function(rsaKey, password, options) {
// encrypt PrivateKeyInfo
var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));
rval = pki.encryptPrivateKeyInfo(rval, password, options);
return pki.encryptedPrivateKeyToPem(rval);
};
/**
* 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) {
// get EncryptedPrivateKeyInfo as ASN.1
var rval = pki.encryptedPrivateKeyFromPem(pem);
rval = pki.decryptPrivateKeyInfo(rval, password);
if(rval !== null) {
rval = pki.privateKeyFromAsn1(rval);
}
return rval;
};
/**
* Sets an RSA public key from BigIntegers modulus and exponent.
*
* @param n the modulus.
* @param e the exponent.
*
* @return the public key.
*/
pki.setRsaPublicKey = pki.rsa.setPublicKey;
/**
* Sets an RSA private key from BigIntegers modulus, exponent, primes,
* prime exponents, and modular multiplicative inverse.
*
* @param n the modulus.
* @param e the public exponent.
* @param d the private exponent ((inverse of e) mod n).
* @param p the first prime.
* @param q the second prime.
* @param dP exponent1 (d mod (p-1)).
* @param dQ exponent2 (d mod (q-1)).
* @param qInv ((inverse of q) mod p)
*
* @return the private key.
*/
pki.setRsaPrivateKey = pki.rsa.setPrivateKey;
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'pki';
var deps = [
'./aes',
'./asn1',
'./des',
'./jsbn',
'./md',
'./mgf',
'./oids',
'./pbkdf2',
'./pkcs12',
'./pss',
'./random',
'./rc2',
'./rsa',
'./util'
];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();
/**
* 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.
*/
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.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';
var deps = ['./hmac', './md', './util'];
var nodeDefine = null;
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
nodeDefine = function(ids, factory) {
factory(require, module);
};
}
// <script>
else {
if(typeof forge === 'undefined') {
forge = {};
}
initModule(forge);
}
}
// AMD
if(nodeDefine || typeof define === 'function') {
// define module AMD style
(nodeDefine || define)(['require', 'module'].concat(deps),
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];
};
});
}
})();