Skip to content
Browse files

Hashcash.

  • Loading branch information...
0 parents commit 86934d35eb50754ce6fdcf790d765e6d0bf82466 Justin Thomas committed Feb 1, 2012
Showing with 317 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +13 −0 example.js
  3. +203 −0 lib/hashcash-client.js
  4. +52 −0 lib/hashcash.js
  5. +26 −0 lib/index.html
  6. +22 −0 package.json
1 .gitignore
@@ -0,0 +1 @@
+node_modules
13 example.js
@@ -0,0 +1,13 @@
+var express = require('express');
+var hashcash = require('./lib/hashcash');
+var app = express.createServer();
+app.use(express.static('lib'));
+app.get('/protected',hashcash.middleware(),function(req,res) {
+ res.send('Hello, World!');
+});
+
+app.get('/unprotected',function(req,res) {
+ res.send('You should use protection.');
+});
+
+app.listen(1337);
203 lib/hashcash-client.js
@@ -0,0 +1,203 @@
+/**
+*
+* Secure Hash Algorithm (SHA1)
+* http://www.webtoolkit.info/
+*
+**/
+
+function SHA1 (msg) {
+
+ function rotate_left(n,s) {
+ var t4 = ( n<<s ) | (n>>>(32-s));
+ return t4;
+ };
+
+ function lsb_hex(val) {
+ var str="";
+ var i;
+ var vh;
+ var vl;
+
+ for( i=0; i<=6; i+=2 ) {
+ vh = (val>>>(i*4+4))&0x0f;
+ vl = (val>>>(i*4))&0x0f;
+ str += vh.toString(16) + vl.toString(16);
+ }
+ return str;
+ };
+
+ function cvt_hex(val) {
+ var str="";
+ var i;
+ var v;
+
+ for( i=7; i>=0; i-- ) {
+ v = (val>>>(i*4))&0x0f;
+ str += v.toString(16);
+ }
+ return str;
+ };
+
+
+ function Utf8Encode(string) {
+ string = string.replace(/\r\n/g,"\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+
+ var c = string.charCodeAt(n);
+
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ }
+ else if((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+ else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+
+ }
+
+ return utftext;
+ };
+
+ var blockstart;
+ var i, j;
+ var W = new Array(80);
+ var H0 = 0x67452301;
+ var H1 = 0xEFCDAB89;
+ var H2 = 0x98BADCFE;
+ var H3 = 0x10325476;
+ var H4 = 0xC3D2E1F0;
+ var A, B, C, D, E;
+ var temp;
+
+ msg = Utf8Encode(msg);
+
+ var msg_len = msg.length;
+
+ var word_array = new Array();
+ for( i=0; i<msg_len-3; i+=4 ) {
+ j = msg.charCodeAt(i)<<24 | msg.charCodeAt(i+1)<<16 |
+ msg.charCodeAt(i+2)<<8 | msg.charCodeAt(i+3);
+ word_array.push( j );
+ }
+
+ switch( msg_len % 4 ) {
+ case 0:
+ i = 0x080000000;
+ break;
+ case 1:
+ i = msg.charCodeAt(msg_len-1)<<24 | 0x0800000;
+ break;
+
+ case 2:
+ i = msg.charCodeAt(msg_len-2)<<24 | msg.charCodeAt(msg_len-1)<<16 | 0x08000;
+ break;
+
+ case 3:
+ i = msg.charCodeAt(msg_len-3)<<24 | msg.charCodeAt(msg_len-2)<<16 | msg.charCodeAt(msg_len-1)<<8 | 0x80;
+ break;
+ }
+
+ word_array.push( i );
+
+ while( (word_array.length % 16) != 14 ) word_array.push( 0 );
+
+ word_array.push( msg_len>>>29 );
+ word_array.push( (msg_len<<3)&0x0ffffffff );
+
+
+ for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) {
+
+ for( i=0; i<16; i++ ) W[i] = word_array[blockstart+i];
+ for( i=16; i<=79; i++ ) W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
+
+ A = H0;
+ B = H1;
+ C = H2;
+ D = H3;
+ E = H4;
+
+ for( i= 0; i<=19; i++ ) {
+ temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B,30);
+ B = A;
+ A = temp;
+ }
+
+ for( i=20; i<=39; i++ ) {
+ temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B,30);
+ B = A;
+ A = temp;
+ }
+
+ for( i=40; i<=59; i++ ) {
+ temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B,30);
+ B = A;
+ A = temp;
+ }
+
+ for( i=60; i<=79; i++ ) {
+ temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
+ E = D;
+ D = C;
+ C = rotate_left(B,30);
+ B = A;
+ A = temp;
+ }
+
+ H0 = (H0 + A) & 0x0ffffffff;
+ H1 = (H1 + B) & 0x0ffffffff;
+ H2 = (H2 + C) & 0x0ffffffff;
+ H3 = (H3 + D) & 0x0ffffffff;
+ H4 = (H4 + E) & 0x0ffffffff;
+
+ }
+
+ var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
+
+ return temp.toLowerCase();
+
+}
+
+function hashcash(options) {
+ var og_success = options.success;
+
+ var success = function(response,statusText,xhr) {
+ var challenge = xhr.getResponseHeader('hashcash-challenge');
+ var potential_answer = -1;
+ var answered = false;
+ var start = new Date().getTime();
+
+ while(!answered) {
+ ++potential_answer;
+ var answer = SHA1(challenge+potential_answer);
+ answered = answer.slice(0,3) === '000';
+ }
+
+ console.log('answer took (' + (new Date().getTime()-start)+'ms): '+answer);
+ options.headers = options.headers || {};
+ options.headers['hashcash-challenge'] = challenge;
+ options.headers['hashcash-answer'] = potential_answer;
+ options.success = og_success;
+ $.ajax(options);
+
+ }
+
+ options.success = success;
+ $.ajax(options);
+}
+
52 lib/hashcash.js
@@ -0,0 +1,52 @@
+var crypto = require('crypto');
+var os = require('os');
+var hostname = os.hostname();
+
+var requests = 0;
+var pending = {};
+var HashCash = function() {
+
+ return function(req,res,next) {
+
+ // check header for challenge answer
+ var answer = req.headers['hashcash-answer'];
+ var challenge = req.headers['hashcash-challenge'];
+ var still_pending = pending[challenge];
+ if(!answer || !challenge || !still_pending) {
+ var shasum = crypto.createHash('sha1');
+ var rand = Math.random();
+ var txt = hostname+new Date().getTime()+req.url+requests;
+ shasum.update(txt);
+ var digest = shasum.digest('hex');
+ shasum = crypto.createHash('sha1');
+ shasum.update(digest);
+ shasum.update(''+rand);
+ challenge = shasum.digest('hex');
+ pending[challenge] = req;
+
+ res.header('hashcash-challenge',challenge);
+ res.send('Answer challenge to complete request.');
+ setTimeout(function() {
+ delete pending[challenge];
+ },4000);
+
+ } else {
+ var shasum = crypto.createHash('sha1');
+ shasum.update(challenge);
+ shasum.update(answer);
+ var digest = shasum.digest('hex');
+ var solved = digest.slice(0,3) === "000";
+ if(solved) {
+ next();
+ } else {
+ res.header('hashcash-challenge',challenge);
+ res.send('Wrong answer to challenge.');
+ }
+ }
+
+ requests++;
+
+ };
+};
+
+exports.middleware = HashCash;
26 lib/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+
+<html>
+
+<head>
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
+ </script>
+ <script src="/hashcash-client.js">
+ </script>
+ <script>
+ $(document).ready(function() {
+ hashcash({
+ url: '/protected',
+ success:function(responseText) {
+ console.log(responseText);
+ }
+ );
+ });
+ </script>
+</head>
+
+<body>
+ Yo.
+</body>
+</html>
+
22 package.json
@@ -0,0 +1,22 @@
+{
+ "name": "hashcash",
+ "version": "0.1.0",
+ "description": "HashCash protocol for Node",
+ "author": ["Justin Thomas <justin.thomas1@gmail.com>"],
+ "scripts": {
+ "test": "node test/test.js"
+ },
+ "main":"./lib/hashcash",
+ "dependencies": {
+ "underscore":"1.2.x",
+ "express": ">=2.5.2"
+ },
+ "repository": {
+ "type": "git",
+ "url" : "https://github.com/base698/hashcash"
+ },
+ "devDependencies": {
+ },
+ "engine": "node 0.6.x"
+}
+

0 comments on commit 86934d3

Please sign in to comment.
Something went wrong with that request. Please try again.