Skip to content

Commit

Permalink
Hashcash.
Browse files Browse the repository at this point in the history
  • Loading branch information
Justin Thomas committed Feb 2, 2012
0 parents commit 86934d3
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
node_modules
13 changes: 13 additions & 0 deletions 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 changes: 203 additions & 0 deletions 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 changes: 52 additions & 0 deletions 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 changes: 26 additions & 0 deletions 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 changes: 22 additions & 0 deletions 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.