A **cryptographic hash** (sometimes called ‘digest’) is a kind of ‘signature’ for a text or
a data file. SHA1 generates an almost-unique 160-bit (20-byte) signature for a text. See below for
the source code.

A hash is not ‘encryption’ – it cannot be decrypted back to the original text (it is a ‘one-way’ cryptographic function, and is a fixed size for any size of source text). This makes it suitable when it is appropriate to compare ‘hashed’ versions of texts, as opposed to decrypting the text to obtain the original version. Such applications include stored passwords, challenge handshake authentication, and digital signatures.

*to validate a password*, you can store a hash of the password, then when when the password is to be authenticated, you hash the password the user supplies, and if the hashed versions match, the password is authenticated; but the original password cannot be obtained from the stored hash- ‘
*challenge handshake authentication*’ (or ‘challenge hash authentication’) avoids transmissing passwords in ‘clear’ – a client can send the hash of a password over the internet for validation by a server without risk of the original password being intercepted *anti-tamper*– link a hash of a message to the original, and the recipient can re-hash the message and compare it to the supplied hash: if they match, the message is unchanged; this can also be used to confirm no data-loss in transmission*digital signatures*are rather more involved, but in essence, you can sign the hash of a document by encrypting it with your private key, producing a digital signature for the document. Anyone else can then check that you authenticated the text by decrypting the signature with your public key to obtain the original hash again, and comparing it with their hash of the text.

*Note on passwords:* it is no longer considered safe to use even salted
sha-1 hashes to store passwords, largely because sha-1 hashing is designed to be efficient; with modern GPUs and
rainbow lookup tables, (salted) hashed passwords can still be insecure. For password hashing,
bcrypt is probably the preferred option
(PHP now has a good implementation with
password_hash().

**SHA-1** is one of the most secure hash algorithms. It is used in SSL (Secure Sockets Level),
PGP (Pretty Good Privacy), XML Signatures, and in Microsoft’s Xbox; the
*git* source-code management system
uses sha-1 hashes extensively, and it is used in hundreds of other applications
(including from IBM, Cisco, Nokia, etc). It is defined in the NIST (National Institute of Standards
and Technology) standard ‘FIPS
180-2’. NIST also provide a number of test
vectors to verify correctness of implementation. There is a good description at
Wikipedia.

*Note on security:* SHA-1 was subjected to cryptanalysis through 2005 which
showed it to be weaker than its theoretical strength. Cryptanalysis is complex (and I’m no expert),
but Xiaoyun Wang effectively announced that given thousands of years of supercomputer time, a ‘collision
pair’ could be found. Even this, however, would be unlikely to be exploited to compromise any real-life
cryptographic hash (for which a ‘pre-image’ attack would be necessary). SHA1 is still extremely
secure, for the moment. However, NIST made a recommendation that federal agencies should
migrate to SHA-2 algorithms
for most purposes by 2010.

In this **JavaScript implementation**, I have tried to make the script as clear and concise
as possible, and equally as close as possible to the NIST specification, to make the operation
of the script readily understandable.

This script is oriented toward hashing text messages rather than binary data. The standard considers hashing byte-stream (or bit-stream) messages only. Text which contains (multi-byte) characters outside ISO 8859-1 (i.e. accented characters outside Latin-1 or non-European character sets – anything with Unicode code-point above U+FF), can’t be encoded 4-per-word, so the script defaults to encoding the text as UTF-8 before hashing it.

Notes on the implementation of the preprocessing stage:

- FIPS 180-2 specifies that the message has a ‘1’ bit appended, and is then padded to a whole number of 512-bit blocks, including the message length (in bits) in the final 64 bits of the last block
- Since we have a byte-stream rather than a bit-stream, adding a byte ‘10000000’ (0x80) appends the required bit “1”.
- To convert the message to 512-bit blocks, I calculate the number of blocks required, N, then for each of these I create a 16-integer (i.e. 512-bit) array. For each if these integers, I take four bytes from the message (using charCodeAt), and left-shift them by the appropriate amount to pack them into the 32-bit integer.
- The charCodeAt() method returns NaN for out-of-bounds, but the ‘|’ operator converts this to zero, so the 0-padding is done implicitly on conversion into blocks.
- Then the length of the message (in bits) needs to be appended in the last 64 bits, that is
the last two integers of the final block. In principle, this could be done by

`M[N-1][14] = ((msg.length-1)*8) >>> 32;`

M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;

However, JavaScript bit-ops convert their arguments to 32-bits, so n >>> 32 would give 0. Hence I use arithmetic operators instead: for the most-significant 32-bit number, I divide the (original) length by 2^32, and use floor() convert the result to an integer.

Note that what is returned is the textual hexadecimal representation of the binary hash. This can be useful for instance for storing hashed passwords, but if you want to use the hash as a key to an encryption routine, for example, you will want to use the binary value not this textual representation.

Using IE on a 1GHz PIII machine, this script will process the message at a speed of around 20kb/sec.

I have now also developed an implementation of SHA-256.

If you need an encryption algorithm rather than a cryptographic hash algorithm, look at my JavaScript implementation of TEA (Tiny Encryption Algorithm) or JavaScript implementation of AES.

See below for the source code of the JavaScript implementation. §ection numbers relate the code back to sections in the standard. I offer these formulæ & scripts for free use and adaptation as my contribution to the open-source info-sphere from which I have received so much. You are welcome to re-use these scripts [under a simple attribution license, without any warranty express or implied] provided solely that you retain my copyright notice and a link to this page.

If you would like to show your appreciation and support continued development of these scripts, I would most gratefully accept donations.

If you have any queries or find any problems, contact me at ku.oc.epyt-elbavom@cne-stpircs.

*© 2002-2013 Chris Veness*

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* SHA-1 implementation in JavaScript | (c) Chris Veness 2002-2013 | www.movable-type.co.uk */ /* - see http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html */ /* http://csrc.nist.gov/groups/ST/toolkit/examples.html */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Sha1 = {}; // Sha1 namespace /** * Generates SHA-1 hash of string * * @param {String} msg String to be hashed * @param {Boolean} [utf8encode=true] Encode msg as UTF-8 before generating hash * @returns {String} Hash of msg as hex character string */ Sha1.hash = function(msg, utf8encode) { utf8encode = (typeof utf8encode == 'undefined') ? true : utf8encode; // convert string to UTF-8, as SHA only deals with byte-streams if (utf8encode) msg = Utf8.encode(msg); // constants [§4.2.1] var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; // PREPROCESSING msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1] // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1] var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length var N = Math.ceil(l/16); // number of 16-integer-blocks required to hold 'l' ints var M = new Array(N); for (var i=0; i<N; i++) { M[i] = new Array(16); for (var j=0; j<16; j++) { // encode 4 chars per integer, big-endian encoding M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) | (msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3)); } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0 } // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1] // note: most significant word would be (len-1)*8 >>> 32, but since JS converts // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]) M[N-1][15] = ((msg.length-1)*8) & 0xffffffff; // set initial hash value [§5.3.1] var H0 = 0x67452301; var H1 = 0xefcdab89; var H2 = 0x98badcfe; var H3 = 0x10325476; var H4 = 0xc3d2e1f0; // HASH COMPUTATION [§6.1.2] var W = new Array(80); var a, b, c, d, e; for (var i=0; i<N; i++) { // 1 - prepare message schedule 'W' for (var t=0; t<16; t++) W[t] = M[i][t]; for (var t=16; t<80; t++) W[t] = Sha1.ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); // 2 - initialise five working variables a, b, c, d, e with previous hash value a = H0; b = H1; c = H2; d = H3; e = H4; // 3 - main loop for (var t=0; t<80; t++) { var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants var T = (Sha1.ROTL(a,5) + Sha1.f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff; e = d; d = c; c = Sha1.ROTL(b, 30); b = a; a = T; } // 4 - compute the new intermediate hash value H0 = (H0+a) & 0xffffffff; // note 'addition modulo 2^32' H1 = (H1+b) & 0xffffffff; H2 = (H2+c) & 0xffffffff; H3 = (H3+d) & 0xffffffff; H4 = (H4+e) & 0xffffffff; } return Sha1.toHexStr(H0) + Sha1.toHexStr(H1) + Sha1.toHexStr(H2) + Sha1.toHexStr(H3) + Sha1.toHexStr(H4); } // // function 'f' [§4.1.1] // Sha1.f = function(s, x, y, z) { switch (s) { case 0: return (x & y) ^ (~x & z); // Ch() case 1: return x ^ y ^ z; // Parity() case 2: return (x & y) ^ (x & z) ^ (y & z); // Maj() case 3: return x ^ y ^ z; // Parity() } } // // rotate left (circular left shift) value x by n positions [§3.2.5] // Sha1.ROTL = function(x, n) { return (x<<n) | (x>>>(32-n)); } // // hexadecimal representation of a number // (note toString(16) is implementation-dependant, and // in IE returns signed numbers when used on full words) // Sha1.toHexStr = function(n) { var s="", v; for (var i=7; i>=0; i--) { v = (n>>>(i*4)) & 0xf; s += v.toString(16); } return s; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Utf8 class: encode / decode between multi-byte Unicode characters and UTF-8 multiple */ /* single-byte character encoding (c) Chris Veness 2002-2013 */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Utf8 = {}; // Utf8 namespace /** * Encode multi-byte Unicode string into utf-8 multiple single-byte characters * (BMP / basic multilingual plane only) * * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars * * @param {String} strUni Unicode string to be encoded as UTF-8 * @returns {String} encoded string */ Utf8.encode = function(strUni) { // use regular expressions & String.replace callback function for better efficiency // than procedural approaches var strUtf = strUni.replace( /[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } ); strUtf = strUtf.replace( /[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } ); return strUtf; } /** * Decode utf-8 encoded string back into multi-byte Unicode characters * * @param {String} strUtf UTF-8 string to be decoded back to Unicode * @returns {String} decoded string */ Utf8.decode = function(strUtf) { // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char! var strUni = strUtf.replace( /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { // (note parentheses for precence) var cc = ((c.charCodeAt(0)&0x0f)<<12) | ((c.charCodeAt(1)&0x3f)<<6) | ( c.charCodeAt(2)&0x3f); return String.fromCharCode(cc); } ); strUni = strUni.replace( /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { // (note parentheses for precence) var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; return String.fromCharCode(cc); } ); return strUni; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */