<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css" />
  </head>

  <body class="workers">
    <h1>Work! Work, my little web workers!</h1>
    <div class="global-message"></div>
    
    <script src="crypto.js"></script>
    <script src="main.js"></script>
  </body>

</html>
(function(w, d) {
  var hashOptions = {
    passphrase: '94713',
    // Hashing salt
    salt: 'secret-salt',
    // Key stretching iterations
    iterations: 64
  },
  hashToCrack = hash(hashOptions.passphrase, hashOptions.salt, hashOptions.iterations),
  workerCount = 4,
  passphraseLimit = 100000,
  workers = [];

  function toArray(obj) {
    return [].map.call(obj, function(element) {
      return element;
    });
  }

  function splitNumIntoRanges(num, count) {
    var inc = Math.ceil(num / count),
      seq = 1,
      chunks = [];

    for (var i = 1; i < count; i++) {
      chunks.push({
        low: seq,
        high: seq + inc
      });

      seq += inc;
    }

    chunks.push({
      low: seq,
      high: num + 1
    });

    return chunks;
  }

  function createWorkerMonitor(index) {
    var element = d.createElement('section');
    element.id = 'worker-' + index;
    element.classList.add('worker');

    element.innerHTML = '<header><span class="title">Worker ' + index + '</span></header><div class="data-element passphrase"><span class="label">Passphrase</span><span class="value"></span></div><div class="data-element hash"><span class="label">Hash</span><span class="value"></span></div><div class="data-element remaining"><span class="label">Combinations left</span><span class="value"></span></div>';

    d.querySelector('body').appendChild(element);
  }

  function updateTextContent(selector, content) {
    d.querySelector(selector).textContent = content;
  }
  
  updateTextContent('.global-message', 'Starting ' + workerCount + 
  ' workers to brute force the SHA1 hash ' + hashToCrack + '.');

  // Splitting the limit number into pieces and distribute equally along the
  // workers.
  splitNumIntoRanges(passphraseLimit, workerCount).forEach(function(range, index) {
    var worker = new Worker('worker.js');
    createWorkerMonitor(index);

    worker.addEventListener('message', function(e) {
      if (e.data.update) {
        // On a update we update the data of the specific worker
        updateTextContent('#worker-' + index + ' > .passphrase > .value', e.data.update.passphrase);
        updateTextContent('#worker-' + index + ' > .hash > .value', e.data.update.hash);
        updateTextContent('#worker-' + index + ' > .remaining > .value', e.data.update.remaining);
        
      } else if (e.data.found) {
        // If a worker found the correct hash we will set the global message and
        // terminate all workers.
        d.querySelector('#worker-' + index).classList.add('found');
        updateTextContent('.global-message', 'Worker ' + index +
          ' found the passphrase ' + e.data.found.passphrase + ' within ' +
          e.data.found.timeSpent + 'ms!');

        // Terminate all workers
        workers.forEach(function(w) {
          // Worker.terminate() to interrupt the web worker
          w.terminate();
          // Add done class to all worker elements
          toArray(d.querySelectorAll('.worker')).forEach(function(e) {
            e.classList.add('done');
          });
        });
        
      } else if (e.data.done) {
        // If a worker is done before we found a result lets update the data and
        // style.
        updateTextContent('#worker-' + index + ' > .passphrase > .value', e.data.done.passphrase);
        updateTextContent('#worker-' + index + ' > .hash > .value', e.data.done.hash);
        updateTextContent('#worker-' + index + ' > .remaining > .value', '0');
        d.querySelector('#worker-' + index).classList.add('done');
      }
    });

    // Start the worker with a postMessage and pass the parameters
    worker.postMessage({
      hash: hashToCrack,
      range: range,
      salt: hashOptions.salt,
      iterations: hashOptions.iterations,
      updateRate: 50
    });

    // Push into the global workers array so we have controll later on
    workers.push(worker);
  });

}(window, document));
@import url("http://fonts.googleapis.com/css?family=Open+Sans:400,300,600");

*, *:before, *:after {
  box-sizing: border-box;
  -webkit-appearance: none;
  -moz-appearance: none;
  -ms-appearance: none;
  appearance: none;
}

*:before, *:after {
  content: '';
  display: table;
  clear: both;
}

.workers {
  font-family: "Open Sans", sans-serif;
  color: rgba(0, 0, 0, 0.8);
  
  &:after {
    display: table;
    clear: both;
  }
  
  h1 {
    color: rgba(0, 0, 0, 0.7);
    text-align: center;
    text-transform: uppercase;
    font-size: 3em;
    letter-spacing: -1px;
  }
  
  > .global-message {
    text-align: center;
    margin-bottom: 5rem;
  }

  .worker {
    overflow: hidden;
    float: left;
    width: 320px;
    padding: 1rem;
    margin: 0 2rem 2rem 0;
    background-color: #d3d3d3;
    border-radius: 10px;
    font-size: 1rem;
    text-align: center;
    opacity: 1;
    transition: opacity 0.3s ease-out;
    
    &.found {
      > header {
        background-color: rgba(0, 80, 0, 0.5);
      }
    }
    
    &.done:not(.found) {
      opacity: 0.4;
      
      > header {
        background-color: rgba(0, 0, 0, 0.5);
      }
    }
    
    > header {
      margin: -1rem -1rem 1rem -1rem;
      padding: 0.5rem;
      background-color: rgba(80, 0, 0, 0.5);
      color: rgba(255, 255, 255, 0.9);
      transition: background 0.3s ease-out;
      
      > .title {
        font-weight: 600;
        font-size: 2em;
        text-transform: uppercase;
      }
    }
    
    > .data-element {
      margin-bottom: 1rem;
      
      > .label {
        font-size: 1.3em;
        text-transform: uppercase;
      }
      
      > .value {
        display: block;
        font-weight: 600;
        font-size: 0.8em;
      }
      
      &.remaining {
        > .value {
          font-weight: 400;
          font-size: 2em;
        }
      }
    }
  }
}
# Multi-threaded SHA1 hash cracker using Web Workers 

This example should demonstrate how to make use of Web Workers to use all CPU 
power that is available on the machine.
(function(s){
  
  // In web workers we can use importScripts to load external javascripts
  importScripts('crypto.js');
  
  // This is the entry point for our worker
  s.addEventListener('message', function(e) {
    // Save a timestamp when we started
    var startTime = new Date(),
        lastUpdateTime = new Date(),
        currentTime = new Date(),
        i,
        hexHash;
    
    // Perform the loop on the range and start generating hashes :-)
    for(i = e.data.range.low; i < e.data.range.high; i++) {
      // Hash our number password with a salt and key stretching
      hexHash = hash('' + i, e.data.salt, e.data.iterations);
      
      // Get current time for resporting the status
      currentTime = new Date();
      
      // If last status update is older than updateRate in ms or fallback 
      // to 100ms
      if(currentTime.getTime() - lastUpdateTime.getTime() > e.data.updateRate || 100) {
        // Update status to host
        s.postMessage({
          update: {
            hash: hexHash,
            passphrase: i,
            remaining: e.data.range.high - i
          }
        });
        // Update lastUpdateTime
        lastUpdateTime = currentTime;
      } 
      
      // We found the hash!!!! :-)
      if(hexHash === e.data.hash) {
        s.postMessage({
          found: {
            hash: hexHash,
            passphrase: i,
            timeSpent: new Date().getTime() - startTime.getTime()
          }
        });
        
        break;
      }
    }
    
    s.postMessage({
      done: {
        timeSpent: new Date().getTime() - startTime.getTime(),
        hash: hexHash
      }
    });
  });
  
}(self));

/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(e,m){var p={},j=p.lib={},l=function(){},f=j.Base={extend:function(a){l.prototype=this;var c=new l;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
n=j.WordArray=f.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=m?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var c=this.words,q=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var b=0;b<a;b++)c[d+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((d+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[d+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=e.ceil(c/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*e.random()|0);return new n.init(c,a)}}),b=p.enc={},h=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d<a;d++){var f=c[d>>>2]>>>24-8*(d%4)&255;b.push((f>>>4).toString(16));b.push((f&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d<c;d+=2)b[d>>>3]|=parseInt(a.substr(d,
2),16)<<24-4*(d%8);return new n.init(b,c/2)}},g=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d<a;d++)b.push(String.fromCharCode(c[d>>>2]>>>24-8*(d%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d<c;d++)b[d>>>2]|=(a.charCodeAt(d)&255)<<24-8*(d%4);return new n.init(b,c)}},r=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(g.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return g.parse(unescape(encodeURIComponent(a)))}},
k=j.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new n.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,d=c.sigBytes,f=this.blockSize,h=d/(4*f),h=a?e.ceil(h):e.max((h|0)-this._minBufferSize,0);a=h*f;d=e.min(4*a,d);if(a){for(var g=0;g<a;g+=f)this._doProcessBlock(b,g);g=b.splice(0,a);c.sigBytes-=d}return new n.init(g,d)},clone:function(){var a=f.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});j.Hasher=k.extend({cfg:f.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){k.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,b){return(new a.init(b)).finalize(c)}},_createHmacHelper:function(a){return function(b,f){return(new s.HMAC.init(a,
f)).finalize(b)}}});var s=p.algo={};return p}(Math);
(function(){var e=CryptoJS,m=e.lib,p=m.WordArray,j=m.Hasher,l=[],m=e.algo.SHA1=j.extend({_doReset:function(){this._hash=new p.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(f,n){for(var b=this._hash.words,h=b[0],g=b[1],e=b[2],k=b[3],j=b[4],a=0;80>a;a++){if(16>a)l[a]=f[n+a]|0;else{var c=l[a-3]^l[a-8]^l[a-14]^l[a-16];l[a]=c<<1|c>>>31}c=(h<<5|h>>>27)+j+l[a];c=20>a?c+((g&e|~g&k)+1518500249):40>a?c+((g^e^k)+1859775393):60>a?c+((g&e|g&k|e&k)-1894007588):c+((g^e^
k)-899497514);j=k;k=e;e=g<<30|g>>>2;g=h;h=c}b[0]=b[0]+h|0;b[1]=b[1]+g|0;b[2]=b[2]+e|0;b[3]=b[3]+k|0;b[4]=b[4]+j|0},_doFinalize:function(){var f=this._data,e=f.words,b=8*this._nDataBytes,h=8*f.sigBytes;e[h>>>5]|=128<<24-h%32;e[(h+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(h+64>>>9<<4)+15]=b;f.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=j.clone.call(this);e._hash=this._hash.clone();return e}});e.SHA1=j._createHelper(m);e.HmacSHA1=j._createHmacHelper(m)})();

// Standard hashing function that uses SHA1 a salt and salted key stretching
function hash(passphrase, salt, iterations) {
  var sha1 = CryptoJS.SHA1(salt + passphrase),
      i;
  
  for(i = 0; i < iterations; i++) {
    sha1 = CryptoJS.SHA1(salt + sha1.toString());
  }
  
  return sha1.toString();
}