diff --git a/.covignore b/.covignore new file mode 100644 index 0000000..016f806 --- /dev/null +++ b/.covignore @@ -0,0 +1 @@ +/tests/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f5cd45c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: node_js +node_js: + - "0.11" + - "0.10" + - "0.8" +install: + - npm install mocha -g + - npm install coveralls -g + - npm install mocha-lcov-reporter -g + - npm install +script: + - npm run-script coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md index 678a8fe..9a37e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v0.1.2 / 2015-01-07 + +* Add bower package. +* Fixed JSHint warnings. +* Add travis. +* Add coveralls. + # v0.1.1 / 2014-07-27 Fixed accents bug diff --git a/LICENSE.txt b/LICENSE.txt index dff2ebd..58074d3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2014 emn178@gmail.com +Copyright 2014-2015 emn178@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 3bf1a6e..c3e1045 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,37 @@ # js-sha512 +[](https://travis-ci.org/emn178/js-sha512) +[](https://coveralls.io/r/emn178/js-sha512?branch=master) +[](https://nodei.co/npm/js-sha512/) A simple SHA-512, SHA-384, SHA-512/224, SHA-512/256 hash functions for JavaScript supports UTF-8 encoding. -## Install +## Demo +[SHA512 Online](http://emn178.github.io/online-tools/sha512.html) +[SHA384 Online](http://emn178.github.io/online-tools/sha384.html) +[SHA512/256 Online](http://emn178.github.io/online-tools/sha512_256.html) +[SHA512/224 Online](http://emn178.github.io/online-tools/sha512_224.html) + +## Download +[Compress](https://raw.github.com/emn178/js-sha512/master/build/sha512.min.js) +[Uncompress](https://raw.github.com/emn178/js-sha512/master/src/sha512.js) + +## Installation +You can also install js-sha512 by using Bower. + + bower install js-sha512 + For node.js, you can use this command to install: npm install js-sha512 ## Usage +``` +You could use like this: +```JavaScript +sha512('Message to hash'); +sha384('Message to hash'); +sha512_256('Message to hash'); +sha512_224('Message to hash'); +``` If you use node.js, you should require the module first: ```JavaScript sha512 = require('js-sha512'); @@ -17,14 +42,57 @@ sha512 = require('js-sha512').sha512; sha384 = require('js-sha512').sha384; sha512_256 = require('js-sha512').sha512_256; sha512_224 = require('js-sha512').sha512_224; -``` -And you could use like this: -```JavaScript -sha512('Message to hash'); -sha384('Message to hash'); -sha512_256('Message to hash'); -sha512_224('Message to hash'); -``` + +### Methods + +#### sha512(str, asciiOnly) + +Hash string to sha512, set asciiOnly to true for better performace if you ensure input is ascii. + +##### *str: `String`* + +String to hash. + +##### *asciiOnly: `Boolean` (default: `false`)* + +Specify the string encoding is ASCII. + +#### sha384(str, asciiOnly) + +Hash string to sha384, set asciiOnly to true for better performace if you ensure input is ascii. + +##### *str: `String`* + +String to hash. + +##### *asciiOnly: `Boolean` (default: `false`)* + +Specify the string encoding is ASCII. + +#### sha512_256(str, asciiOnly) + +Hash string to sha512/256, set asciiOnly to true for better performace if you ensure input is ascii. + +##### *str: `String`* + +String to hash. + +##### *asciiOnly: `Boolean` (default: `false`)* + +Specify the string encoding is ASCII. + +#### sha512_224(str, asciiOnly) + +Hash string to sha512/224, set asciiOnly to true for better performace if you ensure input is ascii. + +##### *str: `String`* + +String to hash. + +##### *asciiOnly: `Boolean` (default: `false`)* + +Specify the string encoding is ASCII. + ## Example Code ```JavaScript @@ -72,15 +140,6 @@ Output b6dab29c16ec35ab34a5d92ff135b58de96741dda78b1009a2181cf8b45d2f72 0f46a0ae7f226517dd66ece0ce1efa29ffb7ced05ac4566fdcaed188 -## Tests -You can open `tests/index.html` in browser or use node.js to run test - - node tests/node-test.js - -or - - npm test - ## Extensions ### jQuery If you prefer jQuery style, you can add following code to add a jQuery extension. diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..4cc4b95 --- /dev/null +++ b/bower.json @@ -0,0 +1,9 @@ +{ + "name": "js-sha512", + "version": "0.1.2", + "main": ["build/sha256.min.js"], + "ignore": [ + "samples", + "tests" + ] +} diff --git a/package.json b/package.json index 280d504..c82c31f 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,15 @@ { "name": "js-sha512", - "version": "0.1.1", + "version": "0.1.2", "description": "This is a simple SHA-512, SHA-384, SHA-512/224, SHA-512/256 hash functions for JavaScript supports UTF-8 encoding.", "main": "src/sha512.js", + "devDependencies": { + "expect.js": "~0.3.1", + "jscoverage": "~0.5.9" + }, "scripts": { - "test": "node tests/node-test.js" + "test": "mocha tests/node-test.js -r jscoverage", + "coveralls": "mocha tests/node-test.js -R mocha-lcov-reporter -r jscoverage | coveralls" }, "repository": { "type": "git", diff --git a/src/sha512.js b/src/sha512.js index 229fc8b..f73ae76 100644 --- a/src/sha512.js +++ b/src/sha512.js @@ -1,29 +1,85 @@ /* - * js-sha512 v0.1.1 + * js-sha512 v0.1.2 * https://github.com/emn178/js-sha512 * - * Copyright 2014, emn178@gmail.com + * Copyright 2014-2015, emn178@gmail.com * * Licensed under the MIT license: * http://www.opensource.org/licenses/MIT */ - -(function(root, undefined){ +;(function(root, undefined) { 'use strict'; // Class Long - var Long = function(high, low){ - this.high = high | 0; - this.low = low | 0; + var Long = function(high, low) { + this.high = high << 0; + this.low = low << 0; + }; + + Long.prototype.and = function(other) { + return new Long(this.high & other.high, this.low & other.low); + }; + + Long.prototype.xor = function(other) { + return new Long(this.high ^ other.high, this.low ^ other.low); + }; + + Long.prototype.not = function() { + return new Long(~this.high, ~this.low); + }; + + Long.prototype.shiftRightUnsigned = function(numBits) { + numBits &= 63; + if(numBits === 0) { + return new Long(this.high, this.low); + } + if(numBits < 32) { + return new Long(this.high >>> numBits, (this.low >>> numBits) | (this.high << (32 - numBits))); + } else if(numBits == 32) { + return new Long(0, this.high); + } else { + return new Long(0, this.high >>> (numBits - 32)); + } }; - var HEX_CHARS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; - var HEX_TABLE = { - '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, - 'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15, - 'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15 + Long.prototype.rightRotate = function(numBits) { + numBits &= 63; + if(numBits === 0) { + return new Long(this.high, this.low); + } + if(numBits < 32) { + return new Long((this.high >>> numBits) | (this.low << (32 - numBits)), (this.low >>> numBits) | (this.high << (32 - numBits))); + } else if(numBits == 32) { + return new Long(this.low, this.high); + } else { + return new Long((this.low >>> (numBits - 32)) | (this.high << (64 - numBits)), (this.high >>> (numBits - 32)) | (this.low << (64 - numBits))); + } }; + Long.prototype.add = function(other) { + var a1 = this.low & 0xFFFF; + var a2 = this.low >>> 16; + var a3 = this.high & 0xFFFF; + var a4 = this.high >>> 16; + + var b1 = other.low & 0xFFFF; + var b2 = other.low >>> 16; + var b3 = other.high & 0xFFFF; + var b4 = other.high >>> 16; + + var c1 = a1 + b1; + var c2 = a2 + b2 + (c1 >>> 16); + var c3 = a3 + b3 + (c2 >>> 16); + var c4 = a4 + b4 + (c3 >>> 16); + return new Long((c4 << 16) | (c3 & 0xFFFF), (c2 << 16) | (c1 & 0xFFFF)); + }; + + Long.prototype.toHexString = function() { + return toHexString(this.high) + toHexString(this.low); + }; + + var HEX_CHARS = '0123456789abcdef'.split(''); + var K =[new Long(0x428A2F98, 0xD728AE22), new Long(0x71374491, 0x23EF65CD), new Long(0xB5C0FBCF, 0xEC4D3B2F), new Long(0xE9B5DBA5, 0x8189DBBC), new Long(0x3956C25B, 0xF348B538), new Long(0x59F111F1, 0xB605D019), @@ -65,77 +121,74 @@ new Long(0x4CC5D4BE, 0xCB3E42B6), new Long(0x597F299C, 0xFC657E2A), new Long(0x5FCB6FAB, 0x3AD6FAEC), new Long(0x6C44198C, 0x4A475817)]; - var sha512 = function(message) { - return sha2(message, 512); + var sha512 = function(message, asciiOnly) { + return sha2(message, 512, asciiOnly); }; - var sha384 = function(message) { - return sha2(message, 384); + var sha384 = function(message, asciiOnly) { + return sha2(message, 384, asciiOnly); }; - var sha512_256 = function(message) { - return sha2(message, 256); + var sha512_256 = function(message, asciiOnly) { + return sha2(message, 256, asciiOnly); }; - var sha512_224 = function(message) { - return sha2(message, 224); + var sha512_224 = function(message, asciiOnly) { + return sha2(message, 224, asciiOnly); }; - var sha2 = function(message, tbit) { - var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message); - - if(tbit == 512) - { - var h0 = new Long(0x6A09E667, 0xF3BCC908); - var h1 = new Long(0xBB67AE85, 0x84CAA73B); - var h2 = new Long(0x3C6EF372, 0xFE94F82B); - var h3 = new Long(0xA54FF53A, 0x5F1D36F1); - var h4 = new Long(0x510E527F, 0xADE682D1); - var h5 = new Long(0x9B05688C, 0x2B3E6C1F); - var h6 = new Long(0x1F83D9AB, 0xFB41BD6B); - var h7 = new Long(0x5BE0CD19, 0x137E2179); - } - else if(tbit == 384) - { - var h0 = new Long(0xCBBB9D5D, 0xC1059ED8); - var h1 = new Long(0x629A292A, 0x367CD507); - var h2 = new Long(0x9159015A, 0x3070DD17); - var h3 = new Long(0x152FECD8, 0xF70E5939); - var h4 = new Long(0x67332667, 0xFFC00B31); - var h5 = new Long(0x8EB44A87, 0x68581511); - var h6 = new Long(0xDB0C2E0D, 0x64F98FA7); - var h7 = new Long(0x47B5481D, 0xBEFA4FA4); + var sha2 = function(message, tbit, asciiOnly) { + var blocks, h0, h1, h2, h3, h4, h5, h6, h7; + if(!asciiOnly && /[^\x00-\x7F]/.test(message)) { + blocks = getBlocksFromUtf8(message); + } else { + blocks = getBlocksFromAscii(message); } - else if(tbit == 256) - { - var h0 = new Long(0x22312194, 0xFC2BF72C); - var h1 = new Long(0x9F555FA3, 0xC84C64C2); - var h2 = new Long(0x2393B86B, 0x6F53B151); - var h3 = new Long(0x96387719, 0x5940EABD); - var h4 = new Long(0x96283EE2, 0xA88EFFE3); - var h5 = new Long(0xBE5E1E25, 0x53863992); - var h6 = new Long(0x2B0199FC, 0x2C85B8AA); - var h7 = new Long(0x0EB72DDC, 0x81C52CA2); - } - else if(tbit == 224) - { - var h0 = new Long(0x8C3D37C8, 0x19544DA2); - var h1 = new Long(0x73E19966, 0x89DCD4D6); - var h2 = new Long(0x1DFAB7AE, 0x32FF9C82); - var h3 = new Long(0x679DD514, 0x582F9FCF); - var h4 = new Long(0x0F6D2B69, 0x7BD44DA8); - var h5 = new Long(0x77E36F73, 0x04C48942); - var h6 = new Long(0x3F9D85A8, 0x6A1D36C8); - var h7 = new Long(0x1112E6AD, 0x91D692A1); + + if(tbit == 512) { + h0 = new Long(0x6A09E667, 0xF3BCC908); + h1 = new Long(0xBB67AE85, 0x84CAA73B); + h2 = new Long(0x3C6EF372, 0xFE94F82B); + h3 = new Long(0xA54FF53A, 0x5F1D36F1); + h4 = new Long(0x510E527F, 0xADE682D1); + h5 = new Long(0x9B05688C, 0x2B3E6C1F); + h6 = new Long(0x1F83D9AB, 0xFB41BD6B); + h7 = new Long(0x5BE0CD19, 0x137E2179); + } else if(tbit == 384) { + h0 = new Long(0xCBBB9D5D, 0xC1059ED8); + h1 = new Long(0x629A292A, 0x367CD507); + h2 = new Long(0x9159015A, 0x3070DD17); + h3 = new Long(0x152FECD8, 0xF70E5939); + h4 = new Long(0x67332667, 0xFFC00B31); + h5 = new Long(0x8EB44A87, 0x68581511); + h6 = new Long(0xDB0C2E0D, 0x64F98FA7); + h7 = new Long(0x47B5481D, 0xBEFA4FA4); + } else if(tbit == 256) { + h0 = new Long(0x22312194, 0xFC2BF72C); + h1 = new Long(0x9F555FA3, 0xC84C64C2); + h2 = new Long(0x2393B86B, 0x6F53B151); + h3 = new Long(0x96387719, 0x5940EABD); + h4 = new Long(0x96283EE2, 0xA88EFFE3); + h5 = new Long(0xBE5E1E25, 0x53863992); + h6 = new Long(0x2B0199FC, 0x2C85B8AA); + h7 = new Long(0x0EB72DDC, 0x81C52CA2); + } else if(tbit == 224) { + h0 = new Long(0x8C3D37C8, 0x19544DA2); + h1 = new Long(0x73E19966, 0x89DCD4D6); + h2 = new Long(0x1DFAB7AE, 0x32FF9C82); + h3 = new Long(0x679DD514, 0x582F9FCF); + h4 = new Long(0x0F6D2B69, 0x7BD44DA8); + h5 = new Long(0x77E36F73, 0x04C48942); + h6 = new Long(0x3F9D85A8, 0x6A1D36C8); + h7 = new Long(0x1112E6AD, 0x91D692A1); } - for(var i = 0, length = blocks.length;i < length;i += 16) - { - var w = [], s0, s1; - for(var j = 0;j < 16;++j) + for(var i = 0, length = blocks.length;i < length;i += 16) { + var w = [], s0, s1, j; + for(j = 0;j < 16;++j) { w[j] = blocks[i + j]; - for(var j = 16;j < 80;++j) - { + } + for(j = 16;j < 80;++j) { s0 = w[j - 15].rightRotate(1).xor(w[j - 15].rightRotate(8)).xor(w[j - 15].shiftRightUnsigned(7)); s1 = w[j - 2].rightRotate(19).xor(w[j - 2].rightRotate(61)).xor(w[j - 2].shiftRightUnsigned(6)); w[j] = w[j - 16].add(s0).add(w[j - 7]).add(s1); @@ -151,8 +204,7 @@ var h = h7; var maj, t1, t2, ch; - for(var j = 0;j < 80;++j) - { + for(j = 0;j < 80;++j) { s0 = a.rightRotate(28).xor(a.rightRotate(34)).xor(a.rightRotate(39)); maj = a.and(b).xor(a.and(c)).xor(b.and(c)); t2 = s0.add(maj); @@ -181,145 +233,100 @@ } var hex = h0.toHexString() + h1.toHexString() + h2.toHexString() + h3.toHexString(); - if(tbit == 224) + if(tbit == 224) { return hex.substr(0, hex.length - 8); - if(tbit >= 384) + } + if(tbit >= 384) { hex += h4.toHexString() + h5.toHexString(); - if(tbit == 512) + } + if(tbit == 512) { hex += h6.toHexString() + h7.toHexString(); + } return hex; }; - var hasUTF8 = function(message) { - var i = message.length; - while(i--) - if(message.charCodeAt(i) > 127) - return true; - return false; + var getBytesFromUtf8 = function(str) { + var bytes = [], index = 0; + for (var i = 0;i < str.length; i++) { + var c = str.charCodeAt(i); + if (c < 0x80) { + bytes[index++] = c; + } else if (c < 0x800) { + bytes[index++] = 0xc0 | (c >> 6); + bytes[index++] = 0x80 | (c & 0x3f); + } else if (c < 0xd800 || c >= 0xe000) { + bytes[index++] = 0xe0 | (c >> 12); + bytes[index++] = 0x80 | ((c >> 6) & 0x3f); + bytes[index++] = 0x80 | (c & 0x3f); + } else { + c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(++i) & 0x3ff)); + bytes[index++] = 0xf0 | (c >> 18); + bytes[index++] = 0x80 | ((c >> 12) & 0x3f); + bytes[index++] = 0x80 | ((c >> 6) & 0x3f); + bytes[index++] = 0x80 | (c & 0x3f); + } + } + return bytes; }; - var ASCIItoBlocks = function(message) { + var getBlocksFromAscii = function(message) { // a block is 32 bits(4 bytes), a chunk is 1024 bits(128 bytes) var length = message.length; var chunkCount = ((length + 16) >> 7) + 1; var blockCount = chunkCount << 5; // chunkCount * 32 - var blocks = []; - var i; - for(i = 0;i < blockCount;++i) + var blocks = [], i; + for(i = 0;i < blockCount;++i) { blocks[i] = 0; - for(i = 0;i < length;++i) - blocks[i >> 2] |= message.charCodeAt(i) << (3 - (i % 4) << 3); - blocks[i >> 2] |= 0x80 << (3 - (i % 4) << 3); + } + for(i = 0;i < length;++i) { + blocks[i >> 2] |= message.charCodeAt(i) << (3 - (i & 3) << 3); + } + blocks[i >> 2] |= 0x80 << (3 - (i & 3) << 3); blocks[blockCount - 1] = length << 3; // length * 8 var blocks64 = []; - for(i = 0;i < blockCount;i += 2) + for(i = 0;i < blockCount;i += 2) { blocks64[i >> 1] = new Long(blocks[i], blocks[i + 1]); + } return blocks64; }; - var UTF8toBlocks = function(message) { - var uri = encodeURIComponent(message); - var blocks = []; - for(var i = 0, bytes = 0, length = uri.length;i < length;++i) - { - var c = uri.charCodeAt(i); - if(c == 37) // % - blocks[bytes >> 2] |= ((HEX_TABLE[uri.charAt(++i)] << 4) | HEX_TABLE[uri.charAt(++i)]) << (3 - (bytes % 4) << 3); - else - blocks[bytes >> 2] |= c << (3 - (bytes % 4) << 3); - ++bytes; - } - var chunkCount = ((bytes + 16) >> 7) + 1; + var getBlocksFromUtf8 = function(message) { + var bytes = getBytesFromUtf8(message); + var length = bytes.length; + var chunkCount = ((length + 16) >> 7) + 1; var blockCount = chunkCount << 5; // chunkCount * 32 - var index = bytes >> 2; - blocks[index] |= 0x80 << (3 - (bytes % 4) << 3); - for(var i = index + 1;i < blockCount;++i) + var blocks = [], i; + for(i = 0;i < blockCount;++i) { blocks[i] = 0; - blocks[blockCount - 1] = bytes << 3; // bytes * 8 + } + for(i = 0;i < length;++i) { + blocks[i >> 2] |= bytes[i] << (3 - (i & 3) << 3); + } + blocks[i >> 2] |= 0x80 << (3 - (i & 3) << 3); + blocks[blockCount - 1] = length << 3; // length * 8 var blocks64 = []; - for(i = 0;i < blockCount;i += 2) + for(i = 0;i < blockCount;i += 2) { blocks64[i >> 1] = new Long(blocks[i], blocks[i + 1]); + } return blocks64; }; var toHexString = function(num) { - var hex = ""; - for(var i = 0; i < 4; i++) - { + var hex = ''; + for(var i = 0; i < 4; i++) { var offset = 3 - i << 3; hex += HEX_CHARS[(num >> (offset + 4)) & 0x0F] + HEX_CHARS[(num >> offset) & 0x0F]; } return hex; }; - Long.prototype.and = function(other){ - return new Long(this.high & other.high, this.low & other.low); - }; - - Long.prototype.xor = function(other){ - return new Long(this.high ^ other.high, this.low ^ other.low); - }; - - Long.prototype.not = function(){ - return new Long(~this.high, ~this.low); - }; - - Long.prototype.shiftRightUnsigned = function(numBits){ - numBits &= 63; - if(numBits == 0) - return new Long(this.high, this.low); - if(numBits < 32) - return new Long(this.high >>> numBits, (this.low >>> numBits) | (this.high << (32 - numBits))); - else if(numBits == 32) - return new Long(0, this.high); - else - return new Long(0, this.high >>> (numBits - 32)); - }; - - Long.prototype.rightRotate = function(numBits){ - numBits &= 63; - if(numBits == 0) - return new Long(this.high, this.low); - if(numBits < 32) - return new Long((this.high >>> numBits) | (this.low << (32 - numBits)), (this.low >>> numBits) | (this.high << (32 - numBits))); - else if(numBits == 32) - return new Long(this.low, this.high); - else - return new Long((this.low >>> (numBits - 32)) | (this.high << (64 - numBits)), (this.high >>> (numBits - 32)) | (this.low << (64 - numBits))); - }; - - Long.prototype.add = function(other){ - var a1 = this.low & 0xFFFF; - var a2 = this.low >>> 16; - var a3 = this.high & 0xFFFF; - var a4 = this.high >>> 16; - - var b1 = other.low & 0xFFFF; - var b2 = other.low >>> 16; - var b3 = other.high & 0xFFFF; - var b4 = other.high >>> 16; - - var c1 = a1 + b1; - var c2 = a2 + b2 + (c1 >>> 16); - var c3 = a3 + b3 + (c2 >>> 16); - var c4 = a4 + b4 + (c3 >>> 16); - return new Long((c4 << 16) | (c3 & 0xFFFF), (c2 << 16) | (c1 & 0xFFFF)); - }; - - Long.prototype.toHexString = function() { - return toHexString(this.high) + toHexString(this.low); - }; - - if(typeof(module) != 'undefined') - { + if(typeof(module) != 'undefined') { sha512.sha512 = sha512; sha512.sha384 = sha384; sha512.sha512_256 = sha512_256; sha512.sha512_224 = sha512_224; module.exports = sha512; - } - else if(root) - { + } else if(root) { root.sha512 = sha512; root.sha384 = sha384; root.sha512_256 = sha512_256; diff --git a/tests/debug.js b/tests/debug.js deleted file mode 100644 index 23900bf..0000000 --- a/tests/debug.js +++ /dev/null @@ -1,12 +0,0 @@ -(function(root) { - var assert = function (title, expect, actual) { - if(expect == actual) - console.log(title + ': true'); - else - console.log(title + ': false', 'Except:' + expect, 'Actual: ' + actual); - }; - if(typeof(module) != 'undefined') - global.assert = assert; - else if(root) - root.assert = assert; -})(this); diff --git a/tests/index.html b/tests/index.html index c56c649..f64f790 100644 --- a/tests/index.html +++ b/tests/index.html @@ -3,10 +3,20 @@