mirror of
https://github.com/moparisthebest/mail
synced 2024-08-13 16:43:47 -04:00
11659 lines
332 KiB
JavaScript
11659 lines
332 KiB
JavaScript
/**
|
||
* 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 Nr–1
|
||
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];
|
||
};
|
||
});
|
||
}
|
||
})();
|