SelfHost/Test Environment/admin/spiderbasic/forge/md.js
2025-12-15 19:46:13 +01:00

319 lines
9.3 KiB
JavaScript

/**
* MessageDigest base API.
*
* @author Dave Longley
*
* Copyright (c) 2015 Digital Bazaar, Inc.
*/
(function() {
/* ########## Begin module implementation ########## */
function initModule(forge) {
forge.md = forge.md || {};
// registered algorithms
forge.md.algorithms = forge.md.algorithms || {};
var ByteBuffer = forge.util.ByteBuffer;
var _padding = null;
/**
* Creates a message digest object using the given algorithm. The algorithm
* may be provided as a string value for a previously registered algorithm or
* it may be given as a message digest algorithm API object.
*
* @param algorithm the algorithm to use, either a string or an algorithm API
* object.
*
* @return the message digest object.
*/
forge.md.createMessageDigest = function(algorithm) {
var api = algorithm;
if(typeof api === 'string') {
api = forge.md.getAlgorithm(api);
}
if(!api) {
throw new Error('Unsupported algorithm: ' + algorithm);
}
// TODO: initialize _padding elsewhere, this method is only called
// in backwards-compatibility mode and _padding is needed regardless of
// entry point
// TODO: change _padding to buffer
if(!_padding || _padding.length < api.blockSize) {
// create shared padding
_padding = String.fromCharCode(128);
_padding += forge.util.fillString(
String.fromCharCode(0x00), api.blockSize);
}
return new forge.md.MessageDigest({
algorithm: api
});
};
/**
* Registers an algorithm by name. If the name was already registered, the
* algorithm API object will be overwritten.
*
* @param name the name of the algorithm.
* @param algorithm the algorithm API object.
*/
forge.md.registerAlgorithm = function(name, algorithm) {
forge.md.algorithms[name] = algorithm;
// FIXME: backwards compatibility
if(!('create' in algorithm)) {
algorithm.create = function() {
return forge.md.createMessageDigest(algorithm);
};
}
forge.md[name] = algorithm;
};
/**
* Gets a registered algorithm by name.
*
* @param name the name of the algorithm.
*
* @return the algorithm, if found, null if not.
*/
forge.md.getAlgorithm = function(name) {
if(name in forge.md.algorithms) {
return forge.md.algorithms[name];
}
return null;
};
/**
* Creates a new MessageDigest.
*
* @param options the options to use.
* algorithm the algorithm API.
*/
var MessageDigest = forge.md.MessageDigest = function(options) {
this._algorithm = options.algorithm;
// FIXME: backwards compatibility
this.algorithm = this._algorithm.name;
this.digestLength = this._algorithm.digestLength;
this.blockLength = this._algorithm.blockSize;
// start digest automatically for first time
this.start();
};
/**
* Starts the digest.
*
* @return this digest object.
*/
MessageDigest.prototype.start = function() {
// up to 56-bit message length for convenience
this.messageLength = 0;
// full message length
this.fullMessageLength = [];
var int32s = this._algorithm.messageLengthSize / 4;
for(var i = 0; i < int32s; ++i) {
this.fullMessageLength.push(0);
}
// input buffer
this._input = new ByteBuffer();
// get starting state
this.state = this._algorithm.start();
return this;
};
/**
* Updates the digest with the given message input. The input can be
* a ByteBuffer or a string to be consumed using the specified-encoding.
*
* @param msg the message input to update with (ByteBuffer or string).
* @param encoding the encoding to use (eg: 'utf8', 'binary',
* 'hex', 'base64').
*
* @return this digest object.
*/
MessageDigest.prototype.update = function(msg, encoding) {
// TODO: remove copy, copying is caller's responsibility
if(msg instanceof ByteBuffer) {
msg = msg.copy();
} else if(!encoding) {
throw new Error('String encoding must be specified.');
} else {
msg = new ByteBuffer(msg, encoding);
}
// update message length
var len = msg.length();
this.messageLength += len;
len = [(len / 0x100000000) >>> 0, len >>> 0];
for(var i = this.fullMessageLength.length - 1; i >= 0; --i) {
this.fullMessageLength[i] += len[1];
len[1] = len[0] + ((this.fullMessageLength[i] / 0x100000000) >>> 0);
this.fullMessageLength[i] = this.fullMessageLength[i] >>> 0;
len[0] = ((len[1] / 0x100000000) >>> 0);
}
// add bytes to input buffer
this._input.putBuffer(msg);
// digest blocks
while(this._input.length() >= this._algorithm.blockSize) {
this.state = this._algorithm.digest(this.state, this._input);
}
// compact input buffer every 2K or if empty
if(this._input.read > 2048 || this._input.length() === 0) {
this._input.compact();
}
return this;
};
/**
* Produces the digest.
*
* @return a byte buffer containing the digest value.
*/
MessageDigest.prototype.digest = function() {
// TODO: May need to better abstract padding and writing message length
// etc. in the future, but for now popular hashes generally all work the
// same way.
/* Note: Here we copy the remaining bytes in the input buffer and add the
appropriate 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 appropriately congruent. In other words, the data to
be digested must be a multiple of `blockSize`. This data includes the
message, some padding, and the length of the message. Since the length of
the message will be encoded as `messageLengthSize` bytes, that means that
the last segment of the data must have `blockSize` - `messageLengthSize`
bytes of message and padding. Therefore, the length of the message plus the
padding must be congruent to X mod `blockSize` because
`blockSize` - `messageLengthSize` = X.
For example, SHA-1 is congruent to 448 mod 512 and SHA-512 is congruent to
896 mod 1024. SHA-1 uses a `blockSize` of 64 bytes (512 bits) and a
`messageLengthSize` of 8 bytes (64 bits). SHA-512 uses a `blockSize` of
128 bytes (1024 bits) and a `messageLengthSize` of 16 bytes (128 bits).
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, then `blockSize` padding bits
must be added. */
// create final block
var finalBlock = new ByteBuffer();
finalBlock.putBytes(this._input.bytes());
// compute remaining size to be digested (include message length size)
var remaining = (
this.fullMessageLength[this.fullMessageLength.length - 1] +
this._algorithm.messageLengthSize);
// add padding for overflow blockSize - overflow
// _padding starts with 1 byte with first bit is set (byte value 128), then
// there may be up to (blockSize - 1) other pad bytes
var overflow = remaining & (this._algorithm.blockSize - 1);
finalBlock.putBytes(_padding.substr(0, this._algorithm.blockSize - overflow));
// serialize message length in bits in big-endian order; since length
// is stored in bytes we multiply by 8 and add carry from next int
var messageLength = new ByteBuffer();
var next, carry;
var bits = this.fullMessageLength[0] * 8;
for(var i = 0; i < this.fullMessageLength.length; ++i) {
next = this.fullMessageLength[i + 1] * 8;
carry = (next / 0x100000000) >>> 0;
bits += carry;
messageLength.putInt32(bits >>> 0);
bits = next;
}
// write the length of the message (algorithm-specific)
this._algorithm.writeMessageLength(finalBlock, messageLength);
// digest final block
var state = this._algorithm.digest(this.state.copy(), finalBlock);
// write state to buffer
var rval = new ByteBuffer();
state.write(rval);
return rval;
};
/**
* Copies this MessageDigest in its current state.
*
* @return a copy of this MessageDigest.
*/
MessageDigest.prototype.copy = function() {
var rval = new MessageDigest({
algorithm: this._algorithm
});
rval.state = this.state.copy();
return rval;
};
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'md';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} else {
// <script>
if(typeof forge === 'undefined') {
forge = {};
}
return initModule(forge);
}
}
// AMD
var deps;
var defineFunc = function(require, module) {
module.exports = function(forge) {
var mods = deps.map(function(dep) {
return require(dep);
}).concat(initModule);
// handle circular dependencies
forge = forge || {};
forge.defined = forge.defined || {};
if(forge.defined[name]) {
return forge[name];
}
forge.defined[name] = true;
for(var i = 0; i < mods.length; ++i) {
mods[i](forge);
}
return forge[name];
};
};
var tmpDefine = define;
define = function(ids, factory) {
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
if(nodeJS) {
delete define;
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
}
define = tmpDefine;
return define.apply(null, Array.prototype.slice.call(arguments, 0));
};
define(
['require', 'module', './util'], function() {
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
});
})();