blob: 53f65d22f6c77a083c00d8972fc4a426f296964e [file] [log] [blame]
/**
* Secure Hash Algorithm with 160-bit digest (SHA-1) implementation.
*
* @author Dave Longley
*
* Copyright (c) 2010-2014 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;
/**
* 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,
// 56-bit length of message so far (does not including padding)
messageLength: 0,
// true 64-bit message length as two 32-bit ints
messageLength64: [0, 0]
};
/**
* Starts the digest.
*
* @return this digest object.
*/
md.start = function() {
md.messageLength = 0;
md.messageLength64 = [0, 0];
_input = forge.util.createBuffer();
_state = {
h0: 0x67452301,
h1: 0xEFCDAB89,
h2: 0x98BADCFE,
h3: 0x10325476,
h4: 0xC3D2E1F0
};
return md;
};
// start digest automatically for first time
md.start();
/**
* Updates the digest with the given message input. The given input can
* treated as raw input (no encoding will be applied) or an encoding of
* 'utf8' maybe given to encode the input using UTF-8.
*
* @param msg the message input to update with.
* @param encoding the encoding to use (default: 'raw', other: 'utf8').
*
* @return this digest object.
*/
md.update = function(msg, encoding) {
if(encoding === 'utf8') {
msg = forge.util.encodeUtf8(msg);
}
// update message length
md.messageLength += msg.length;
md.messageLength64[0] += (msg.length / 0x100000000) >>> 0;
md.messageLength64[1] += msg.length >>> 0;
// add bytes to input buffer
_input.putBytes(msg);
// process bytes
_update(_state, _w, _input);
// compact input buffer every 2K or if empty
if(_input.read > 2048 || _input.length() === 0) {
_input.compact();
}
return md;
};
/**
* Produces the digest.
*
* @return a byte buffer containing the digest value.
*/
md.digest = function() {
/* Note: Here we copy the remaining bytes in the input buffer and
add the appropriate SHA-1 padding. Then we do the final update
on a copy of the state so that if the user wants to get
intermediate digests they can do so. */
/* Determine the number of bytes that must be added to the message
to ensure its length is congruent to 448 mod 512. In other words,
the data to be digested must be a multiple of 512 bits (or 128 bytes).
This data includes the message, some padding, and the length of the
message. Since the length of the message will be encoded as 8 bytes (64
bits), that means that the last segment of the data must have 56 bytes
(448 bits) of message and padding. Therefore, the length of the message
plus the padding must be congruent to 448 mod 512 because
512 - 128 = 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 padBytes = forge.util.createBuffer();
padBytes.putBytes(_input.bytes());
// 64 - (remaining msg + 8 bytes msg length) mod 64
padBytes.putBytes(
_padding.substr(0, 64 - ((md.messageLength64[1] + 8) & 0x3F)));
/* 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 the 64-bit length by 8 (or left shift by 3). */
padBytes.putInt32(
(md.messageLength64[0] << 3) | (md.messageLength64[0] >>> 28));
padBytes.putInt32(md.messageLength64[1] << 3);
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;
};
// sha-1 padding bytes not initialized yet
var _padding = null;
var _initialized = false;
/**
* Initializes the constant tables.
*/
function _init() {
// 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.
*/
function _update(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 = (s.h0 + a) | 0;
s.h1 = (s.h1 + b) | 0;
s.h2 = (s.h2 + c) | 0;
s.h3 = (s.h3 + d) | 0;
s.h4 = (s.h4 + e) | 0;
len -= 64;
}
}
} // end module implementation
/* ########## Begin module wrapper ########## */
var name = 'sha1';
if(typeof define !== 'function') {
// NodeJS -> AMD
if(typeof module === 'object' && module.exports) {
var nodeJS = true;
define = function(ids, factory) {
factory(require, module);
};
} 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));
});
})();