hashcash-1.21/0000775000076400007640000000000010411051106011604 5ustar adamadamhashcash-1.21/fastmint_ansi_standard_2.c0000664000076400007640000002627610351101612016726 0ustar adamadam#include #include "libfastmint.h" int minter_ansi_standard_2_test(void) { /* This minter runs on any hardware */ return 1; } /* Define low-level primitives in terms of operations */ #define S(n,X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) #define XOR(a,b) ( (a) ^ (b) ) #define AND(a,b) ( (a) & (b) ) #define ANDNOT(a,b) ( (a) & ~(b) ) #define OR(a,b) ( (a) | (b) ) #define ADD(a,b) ( (a) + (b) ) #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ /* #define F1( B, C, D, F, G ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) */ #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F2( B, C, D, F, G ) ( \ F = XOR(B,C), \ XOR(F,D) ) */ /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) /* #define F3( B, C, D, F, G ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) */ #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F4(B,C,D,F,G) F2(B,C,D,F,G) */ #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* #define Wmod(W,t) ((t) < 16 ? (W)[t] : (W)[((t) & 0xF) + 16]) */ #define Wmod(W,t) ((W)[t]) #define Wf(W,t) (Wmod(W,t) = S(1, Wmod(W,t-16) ^ Wmod(W,t-14) ^ Wmod(W,t-8) ^ Wmod(W,t-3))) /*#define Wf(W,t,F,G) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) */ #define Wfly(W,t) ( (t) < 16 ? (W)[t] : Wf(W,t) ) /* #define ROUND(t,A,B,C,D,E,F,G,Func,K,W) \ E = ADD(E,K); \ F = S(5,A); \ E = ADD(F,E); \ E = ADD((W)[t],E); \ F = Func(B,C,D,F,G); \ E = ADD(F,E); \ B = S(30,B); */ #define ROUND(u,t,A,B,C,D,E,Func,K,W) \ E += S(5,A) + Func(B,C,D) + ((u) ? Wfly(W,t) : (W)[t]) + K; \ B = S(30,B); #define ROUNDn(t,A,B,C,D,E,Func,K,W) \ ROUND(0,t,A##1,B##1,C##1,D##1,E##1,Func,K,W##1); \ ROUND(0,t,A##2,B##2,C##2,D##2,E##2,Func,K,W##2); #define ROUNDu(t,A,B,C,D,E,Func,K,W) \ ROUND(1,t,A##1,B##1,C##1,D##1,E##1,Func,K,W##1); \ ROUND(1,t,A##2,B##2,C##2,D##2,E##2,Func,K,W##2); #define ROUND5n( t, Func, K ) \ ROUNDn( t + 0, A, B, C, D, E, Func, K, W );\ ROUNDn( t + 1, E, A, B, C, D, Func, K, W );\ ROUNDn( t + 2, D, E, A, B, C, Func, K, W );\ ROUNDn( t + 3, C, D, E, A, B, Func, K, W );\ ROUNDn( t + 4, B, C, D, E, A, Func, K, W ); #define ROUND5u( t, Func, K ) \ ROUNDu( t + 0, A, B, C, D, E, Func, K, W );\ ROUNDu( t + 1, E, A, B, C, D, Func, K, W );\ ROUNDu( t + 2, D, E, A, B, C, Func, K, W );\ ROUNDu( t + 3, C, D, E, A, B, Func, K, W );\ ROUNDu( t + 4, B, C, D, E, A, Func, K, W ); unsigned long minter_ansi_standard_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { MINTER_CALLBACK_VARS; unsigned long iters = 0 ; int n = 0, t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; uInt32 A = 0 , B = 0 , *W = NULL ; /*register*/ uInt32 A1 = 0 , B1 = 0 , C1 = 0 , D1 = 0 , E1 = 0 ; /*register*/ uInt32 A2 = 0 , B2 = 0 , C2 = 0 , D2 = 0 , E2 = 0 ; uInt32 W1[80] = {0}; uInt32 W2[80] = {0}; uInt32 H[5] = {0}, pH[5] = {0}; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X1 = (unsigned char*) W1; unsigned char *X2 = (unsigned char*) W2; int addressMask = 0 ; static const int endTest = 3; unsigned char *output = (unsigned char*) block; *best = 0; /* Work out whether we need to swap bytes during encoding */ addressMask = ( *(char*)&endTest ); /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } maxBits = 0; /* Copy block and IV to internal storage */ for(t=0; t < 16; t++) W1[t] = W2[t] = GET_WORD(output + t*4); for(t=0; t < 5; t++) pH[t] = H[t] = IV[t]; /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-2; iters += 2) { /* Encode iteration count into tail */ X1[(tailIndex - 1) ^ addressMask] = p[((iters+0) ) & 0x3f]; X2[(tailIndex - 1) ^ addressMask] = p[((iters+1) ) & 0x3f]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X1[(tailIndex - 2) ^ addressMask] = X2[(tailIndex - 2) ^ addressMask] = p[((iters) >> 6) & 0x3f]; } if ( iters >> 12 ) { X1[(tailIndex - 3) ^ addressMask] = X2[(tailIndex - 3) ^ addressMask] = p[((iters) >> 12) & 0x3f]; } if ( iters >> 18 ) { X1[(tailIndex - 4) ^ addressMask] = X2[(tailIndex - 4) ^ addressMask] = p[((iters) >> 18) & 0x3f]; } if ( iters >> 24 ) { X1[(tailIndex - 5) ^ addressMask] = X2[(tailIndex - 5) ^ addressMask] = p[((iters) >> 24) & 0x3f]; } if ( iters >> 30 ) { X1[(tailIndex - 6) ^ addressMask] = X2[(tailIndex - 6) ^ addressMask] = p[((iters) >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A1 = H[0]; B1 = H[1]; C1 = H[2]; D1 = H[3]; E1 = H[4]; for(t=16; t < 32; t++) { Wf(W1,t); Wf(W2,t); } ROUND(0, 0, A1, B1, C1, D1, E1, F1, K1, W1 ); ROUND(0, 1, E1, A1, B1, C1, D1, F1, K1, W1 ); ROUND(0, 2, D1, E1, A1, B1, C1, F1, K1, W1 ); ROUND(0, 3, C1, D1, E1, A1, B1, F1, K1, W1 ); ROUND(0, 4, B1, C1, D1, E1, A1, F1, K1, W1 ); ROUND(0, 5, A1, B1, C1, D1, E1, F1, K1, W1 ); ROUND(0, 6, E1, A1, B1, C1, D1, F1, K1, W1 ); if(tailIndex == 52) { ROUND(0, 7, D1, E1, A1, B1, C1, F1, K1, W1 ); ROUND(0, 8, C1, D1, E1, A1, B1, F1, K1, W1 ); ROUND(0, 9, B1, C1, D1, E1, A1, F1, K1, W1 ); ROUND(0,10, A1, B1, C1, D1, E1, F1, K1, W1 ); ROUND(0,11, E1, A1, B1, C1, D1, F1, K1, W1 ); } pH[0] = A1; pH[1] = B1; pH[2] = C1; pH[3] = D1; pH[4] = E1; } /* Set up working variables */ A1 = A2 = pH[0]; B1 = B2 = pH[1]; C1 = C2 = pH[2]; D1 = D2 = pH[3]; E1 = E2 = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUNDn( 0, A, B, C, D, E, F1, K1, W ); ROUNDn( 1, E, A, B, C, D, F1, K1, W ); ROUNDn( 2, D, E, A, B, C, F1, K1, W ); ROUNDn( 3, C, D, E, A, B, F1, K1, W ); ROUNDn( 4, B, C, D, E, A, F1, K1, W ); ROUNDn( 5, A, B, C, D, E, F1, K1, W ); ROUNDn( 6, E, A, B, C, D, F1, K1, W ); case 32: ROUNDn( 7, D, E, A, B, C, F1, K1, W ); ROUNDn( 8, C, D, E, A, B, F1, K1, W ); ROUNDn( 9, B, C, D, E, A, F1, K1, W ); ROUNDn(10, A, B, C, D, E, F1, K1, W ); ROUNDn(11, E, A, B, C, D, F1, K1, W ); case 52: ROUNDn(12, D, E, A, B, C, F1, K1, W ); ROUNDn(13, C, D, E, A, B, F1, K1, W ); ROUNDn(14, B, C, D, E, A, F1, K1, W ); ROUNDn(15, A, B, C, D, E, F1, K1, W ); } if(tailIndex == 52) { ROUNDn(16, E, A, B, C, D, F1, K1, W ); ROUNDn(17, D, E, A, B, C, F1, K1, W ); ROUNDn(18, C, D, E, A, B, F1, K1, W ); ROUNDn(19, B, C, D, E, A, F1, K1, W ); ROUNDu(20, A, B, C, D, E, F2, K2, W ); ROUNDn(21, E, A, B, C, D, F2, K2, W ); ROUNDn(22, D, E, A, B, C, F2, K2, W ); ROUNDu(23, C, D, E, A, B, F2, K2, W ); ROUNDn(24, B, C, D, E, A, F2, K2, W ); ROUNDn(25, A, B, C, D, E, F2, K2, W ); ROUNDu(26, E, A, B, C, D, F2, K2, W ); ROUNDn(27, D, E, A, B, C, F2, K2, W ); ROUNDu(28, C, D, E, A, B, F2, K2, W ); ROUNDu(29, B, C, D, E, A, F2, K2, W ); ROUNDn(30, A, B, C, D, E, F2, K2, W ); } else if (tailIndex == 32) { ROUNDn(16, E, A, B, C, D, F1, K1, W ); ROUNDn(17, D, E, A, B, C, F1, K1, W ); ROUNDn(18, C, D, E, A, B, F1, K1, W ); ROUNDn(19, B, C, D, E, A, F1, K1, W ); ROUNDn(20, A, B, C, D, E, F2, K2, W ); ROUNDu(21, E, A, B, C, D, F2, K2, W ); ROUNDn(22, D, E, A, B, C, F2, K2, W ); ROUNDu(23, C, D, E, A, B, F2, K2, W ); ROUNDu(24, B, C, D, E, A, F2, K2, W ); ROUNDn(25, A, B, C, D, E, F2, K2, W ); ROUNDu(26, E, A, B, C, D, F2, K2, W ); ROUNDu(27, D, E, A, B, C, F2, K2, W ); ROUNDn(28, C, D, E, A, B, F2, K2, W ); ROUNDu(29, B, C, D, E, A, F2, K2, W ); ROUNDu(30, A, B, C, D, E, F2, K2, W ); } else { ROUNDu(16, E, A, B, C, D, F1, K1, W ); ROUNDu(17, D, E, A, B, C, F1, K1, W ); ROUNDu(18, C, D, E, A, B, F1, K1, W ); ROUNDu(19, B, C, D, E, A, F1, K1, W ); ROUNDu(20, A, B, C, D, E, F2, K2, W ); ROUNDu(21, E, A, B, C, D, F2, K2, W ); ROUNDu(22, D, E, A, B, C, F2, K2, W ); ROUNDu(23, C, D, E, A, B, F2, K2, W ); ROUNDu(24, B, C, D, E, A, F2, K2, W ); ROUNDu(25, A, B, C, D, E, F2, K2, W ); ROUNDu(26, E, A, B, C, D, F2, K2, W ); ROUNDu(27, D, E, A, B, C, F2, K2, W ); ROUNDu(28, C, D, E, A, B, F2, K2, W ); ROUNDu(29, B, C, D, E, A, F2, K2, W ); ROUNDu(30, A, B, C, D, E, F2, K2, W ); } ROUNDu(31, E, A, B, C, D, F2, K2, W ); ROUNDu(32, D, E, A, B, C, F2, K2, W ); ROUNDu(33, C, D, E, A, B, F2, K2, W ); ROUNDu(34, B, C, D, E, A, F2, K2, W ); ROUNDu(35, A, B, C, D, E, F2, K2, W ); ROUNDu(36, E, A, B, C, D, F2, K2, W ); ROUNDu(37, D, E, A, B, C, F2, K2, W ); ROUNDu(38, C, D, E, A, B, F2, K2, W ); ROUNDu(39, B, C, D, E, A, F2, K2, W ); ROUND5u(40, F3, (K3) ); ROUND5u(45, F3, (K3) ); ROUND5u(50, F3, (K3) ); ROUND5u(55, F3, (K3) ); ROUND5u(60, F4, (K4) ); ROUND5u(65, F4, (K4) ); ROUND5u(70, F4, (K4) ); ROUND5u(75, F4, (K4) ); /* Mix in the IV again */ A1 += H[0]; B1 += H[1]; A2 += H[0]; B2 += H[1]; /* Debugging! */ if(0 && iters==0) { for(t=0; t < 80; t++) { X1 = (unsigned char*) (W1+t); X2 = (unsigned char*) (W2+t); printf("%2X %2X %2X %2X | %2X %2X %2X %2X\n", X1 [ 0 ] , X1 [ 1 ] , X1 [ 2 ] , X1 [ 3 ] , X2 [ 0 ] , X2 [ 1 ] , X2 [ 2 ] , X2 [ 3 ] ) ; } X1 = (unsigned char*) W1; X2 = (unsigned char*) W2; } /* Quickly check which pipe contains the best result */ if(A1 < A2) n = 0; else if(A1 > A2) n = 1; else if(B1 < B2) n = 0; else n = 1; switch(n) { case 0: A = A1; B = B1; W = W1; break; case 1: A = A2; B = B2; W = W2; break; } /* Is this the best bit count so far? */ if(!(A & bitMask1Low) && !(B & bitMask1High)) { /* Count bits */ gotBits = 0; if(A) { s = A; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(B) { s = B; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best = gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } /* Copy this result back to the block buffer */ for(t=0; t < 16; t++) PUT_WORD(output + t*4, W[t]); /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+2; } } if(0) return 0; MINTER_CALLBACK(); } return iters+2; } hashcash-1.21/INSTALL0000664000076400007640000000276610351101612012651 0ustar adamadamCOMPILING To compile type: make to see a list of platforms. Current platforms are: x86, mingw, mingw-dll, g3-osx, ppc-linux, generic Choose platform (or generic if your platform is not listed) and then type: make to use the openssl SHA1 implementation rather than builtin: make CFLAGS=-DOPENSSL LDFLAGS=-lcrypto Note: if you are using the libhashcash.a or linking with other software that uses openssl, you will want to compile it this way or you will get conflicts with function names. COMPILING WINDOWS Note the mingw target is using the minimum gnu for windows (http://www.mingw.org) system. On that system you have to type: mingw32-make mingw to build win32 executables. COMPILING WINDOWS DLL The windows HASHCASH.DLL is also built using mingw: mingw32-make mingw-dll builds a win32 HASHCASH.DLL and HASHCASH.LIB (and a HASHCASH.EXE win32 app that relies on the HASHCASH.DLL) INSTALLING To install the compiled binaries type: make install (obviously install that doesn't work on windows, unix only). This will install the binaries sha1 and hashcash in /usr/local/bin, and the man page hashcash.1 in /usr/local/man/man1. To change the binary installation location change the INSTALL_PATH in the Makefile. To change the man page installation location change the MAN_INSTALL_PATH in the Makefile. For usage information see the man page. CUSTOM CC OPTIMIZER FLAGS To override the COPT argument without editing the Makefile do: make "COPT=-O3 -my-optimization-flags" hashcash-1.21/fastmint_altivec_compact_2.c0000664000076400007640000010464710351101612017250 0ustar adamadam#include #include "libfastmint.h" int minter_altivec_compact_2_test(void) { /* This minter runs only on PowerPC G4 and higher hardware */ #if defined(__POWERPC__) && defined(__ALTIVEC__) && defined(__GNUC__) return (gProcessorSupportFlags & HC_CPU_SUPPORTS_ALTIVEC) != 0; #endif /* Not a PowerPC, or compiler doesn't support Altivec or GNU assembly */ return 0; } /* Define low-level primitives in terms of operations */ /* #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) */ #define S(n,X) ( vec_rl( X, (vector unsigned int) (n) ) ) #define XOR(a,b) ( vec_xor(a,b) ) #define AND(a,b) ( vec_and(a,b) ) #define ANDNOT(a,b) ( vec_andc(a,b) ) #define OR(a,b) ( vec_or(a,b) ) #define ADD(a,b) ( vec_add(a,b) ) /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ #define F1( B, C, D, F, G ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) /* #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F2( B, C, D, F, G ) ( \ F = XOR(B,C), \ XOR(F,D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ /* #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) */ #define F3( B, C, D, F, G ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) /* #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F4(B,C,D,F,G) F2(B,C,D,F,G) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) */ #define Wf(W,t,F,G) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) #define Wfly(W,t,F,G) ( (t) < 16 ? (W)[t] : Wf(W,t,F,G) ) #define ROUND_F1_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t vand v5, %[b1], %[c1]" /* begin F1(B1,C1,D1) */ \ "\n\t lvx v16, %[w1], %[T]" /* load W1[t] in advance */ \ "\n\t vand v13, %[b2], %[c2]" /* begin F1(B2,C2,D2) */ \ "\n\t lvx v17, %[w2], %[T]" /* load W2[t] in advance */ \ "\n\t vandc v6, %[d1], %[b1]" \ "\n\t vandc v14, %[d2], %[b2]" \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t vor v5, v5, v6" /* finish F1(B1,C1,D1) */ \ "\n\t vor v13, v13, v14" /* finish F1(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r0" \ ); #define ROUND_F1_u(t,A,B,C,D,E,K,W) \ asm volatile ( \ "\n\t vand v5, %[b1], %[c1]" /* begin F1(B1,C1,D1) */ \ "\n\t lvx v15, %[w1], %[T]" /* load W1[t-16] */ \ "\n\t addi r20, %[T], 32" \ "\n\t vand v13, %[b2], %[c2]" /* begin F1(B2,C2,D2) */ \ "\n\t lvx v16, %[w2], %[T]" /* load W2[t-16] */ \ "\n\t addi %[T], %[T], 128" \ "\n\t lvx v6, %[w1], r20" /* load W1[t-14] */ \ "\n\t lvx v14, %[w2], r20" /* load W2[t-14] */ \ "\n\t addi r20, r20, 176" \ "\n\t lvx v7, %[w1], %[T]" /* load W1[t-8] */ \ "\n\t vxor v6, v6, v15" \ "\n\t lvx v15, %[w2], %[T]" /* load W2[t-8] */ \ "\n\t vxor v14, v14, v16" \ "\n\t lvx v16, %[w1], r20" /* load W1[t-3] */ \ "\n\t vxor v7, v7, v6" \ "\n\t lvx v17, %[w2], r20" /* load W2[t-3] */ \ "\n\t vxor v15, v15, v14" \ "\n\t vxor v16, v16, v7" \ "\n\t vxor v17, v17, v15" \ "\n\t vandc v6, %[d1], %[b1]" \ "\n\t vandc v14, %[d2], %[b2]" \ "\n\t addi %[T], r20, 48" \ "\n\t vrlw v16, v16, %[s1]" /* complete W1[t] */ \ "\n\t vrlw v17, v17, %[s1]" /* complete W2[t] */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t stvx v16, %[w1], %[T]" /* store W1[t] */ \ "\n\t vor v5, v5, v6" /* finish F1(B1,C1,D1) */ \ "\n\t stvx v17, %[w2], %[T]" /* store W2[t] */ \ "\n\t vor v13, v13, v14" /* finish F1(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)), [s1] "v" ((vector unsigned int) (1)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r20", "r0", "memory" \ ); #define ROUND_F2_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t vxor v5, %[b1], %[c1]" /* begin F2(B1,C1,D1) */ \ "\n\t lvx v16, %[w1], %[T]" /* load W1[t] in advance */ \ "\n\t vxor v13, %[b2], %[c2]" /* begin F2(B2,C2,D2) */ \ "\n\t lvx v17, %[w2], %[T]" /* load W2[t] in advance */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t vxor v5, v5, %[d1]" /* finish F2(B1,C1,D1) */ \ "\n\t vxor v13, v13, %[d2]" /* finish F2(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r0" \ ); #define ROUND_F2_u(t,A,B,C,D,E,K,W) \ asm volatile ( \ "\n\t vxor v5, %[b1], %[c1]" /* begin F2(B1,C1,D1) */ \ "\n\t lvx v15, %[w1], %[T]" /* load W1[t-16] */ \ "\n\t addi r20, %[T], 32" \ "\n\t vxor v13, %[b2], %[c2]" /* begin F2(B2,C2,D2) */ \ "\n\t lvx v16, %[w2], %[T]" /* load W2[t-16] */ \ "\n\t addi %[T], %[T], 128" \ "\n\t lvx v6, %[w1], r20" /* load W1[t-14] */ \ "\n\t lvx v14, %[w2], r20" /* load W2[t-14] */ \ "\n\t addi r20, r20, 176" \ "\n\t lvx v7, %[w1], %[T]" /* load W1[t-8] */ \ "\n\t vxor v6, v6, v15" \ "\n\t lvx v15, %[w2], %[T]" /* load W2[t-8] */ \ "\n\t vxor v14, v14, v16" \ "\n\t lvx v16, %[w1], r20" /* load W1[t-3] */ \ "\n\t vxor v7, v7, v6" \ "\n\t lvx v17, %[w2], r20" /* load W2[t-3] */ \ "\n\t vxor v15, v15, v14" \ "\n\t vxor v16, v16, v7" \ "\n\t vxor v17, v17, v15" \ "\n\t addi %[T], r20, 48" \ "\n\t vrlw v16, v16, %[s1]" /* complete W1[t] */ \ "\n\t vrlw v17, v17, %[s1]" /* complete W2[t] */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t stvx v16, %[w1], %[T]" /* store W1[t] */ \ "\n\t vxor v5, v5, %[d1]" /* finish F2(B1,C1,D1) */ \ "\n\t stvx v17, %[w2], %[T]" /* store W2[t] */ \ "\n\t vxor v13, v13, %[d2]" /* finish F2(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)), [s1] "v" ((vector unsigned int) (1)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r20", "r0", "memory" \ ); #define ROUND_F3_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t vor v5, %[d1], %[c1]" /* begin F3(B1,C1,D1) */ \ "\n\t lvx v16, %[w1], %[T]" /* load W1[t] in advance */ \ "\n\t vor v13, %[d2], %[c2]" /* begin F3(B2,C2,D2) */ \ "\n\t vand v6, %[d1], %[c1]" \ "\n\t vand v14, %[d2], %[c2]" \ "\n\t lvx v17, %[w2], %[T]" /* load W2[t] in advance */ \ "\n\t vand v5, v5, %[b1]" \ "\n\t vand v13, v13, %[b2]" \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t vor v5, v5, v6" /* finish F3(B1,C1,D1) */ \ "\n\t vor v13, v13, v14" /* finish F3(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r0" \ ); #define ROUND_F3_u(t,A,B,C,D,E,K,W) \ asm volatile ( \ "\n\t vor v5, %[d1], %[c1]" /* begin F3(B1,C1,D1) */ \ "\n\t lvx v15, %[w1], %[T]" /* load W1[t-16] */ \ "\n\t addi r20, %[T], 32" \ "\n\t vor v13, %[d2], %[c2]" /* begin F3(B2,C2,D2) */ \ "\n\t lvx v16, %[w2], %[T]" /* load W2[t-16] */ \ "\n\t addi %[T], %[T], 128" \ "\n\t lvx v6, %[w1], r20" /* load W1[t-14] */ \ "\n\t lvx v14, %[w2], r20" /* load W2[t-14] */ \ "\n\t addi r20, r20, 176" \ "\n\t lvx v7, %[w1], %[T]" /* load W1[t-8] */ \ "\n\t vxor v6, v6, v15" \ "\n\t lvx v15, %[w2], %[T]" /* load W2[t-8] */ \ "\n\t vxor v14, v14, v16" \ "\n\t lvx v16, %[w1], r20" /* load W1[t-3] */ \ "\n\t vxor v7, v7, v6" \ "\n\t lvx v17, %[w2], r20" /* load W2[t-3] */ \ "\n\t vxor v15, v15, v14" \ "\n\t vand v6, %[d1], %[c1]" \ "\n\t vand v14, %[d2], %[c2]" \ "\n\t vxor v16, v16, v7" \ "\n\t vxor v17, v17, v15" \ "\n\t vand v5, v5, %[b1]" \ "\n\t vand v13, v13, %[b2]" \ "\n\t addi %[T], r20, 48" \ "\n\t vrlw v16, v16, %[s1]" /* complete W1[t] */ \ "\n\t vrlw v17, v17, %[s1]" /* complete W2[t] */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t stvx v16, %[w1], %[T]" /* store W1[t] */ \ "\n\t vor v5, v5, v6" /* finish F3(B1,C1,D1) */ \ "\n\t stvx v17, %[w2], %[T]" /* store W2[t] */ \ "\n\t vor v13, v13, v14" /* finish F3(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)), [s1] "v" ((vector unsigned int) (1)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r20", "r0", "memory" \ ); #define ROUND_F4_n ROUND_F2_n #define ROUND_F4_u ROUND_F2_u #define ROUNDn(t,A,B,C,D,E,Func,K,W) \ o = (t) * sizeof(vector unsigned int); \ ROUND_##Func##_n(o,A,B,C,D,E,K,W); #define ROUND5( t, Func, K ) \ ROUNDn( t + 0, A, B, C, D, E, Func, K, W );\ ROUNDn( t + 1, E, A, B, C, D, Func, K, W );\ ROUNDn( t + 2, D, E, A, B, C, Func, K, W );\ ROUNDn( t + 3, C, D, E, A, B, Func, K, W );\ ROUNDn( t + 4, B, C, D, E, A, Func, K, W ); #if defined(__POWERPC__) && defined(__ALTIVEC__) && defined(__GNUC__) static void WF(vector unsigned int *W, int tailIndex) { asm volatile ( "\n\t mfctr r14" "\n\t li r10,4" "\n\t li r11,0" "\n\t li r12,16" "\n\t mtctr r10" /* load entire circular W buffer into registers */ "\n\t lvx v0,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v1,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v2,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v3,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v4,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v5,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v6,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v7,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v8,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v9,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v10,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v11,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v12,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v13,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v14,%[w],r11" "\n\t addi r11,r11,32" "\n\t cmpwi cr0,%[tail],52" "\n\t lvx v15,%[w],r12" "\n\t addi r12,r12,32" "\n\t cmpwi cr1,%[tail],32" "\n\t addi r13,r11,32" /* special-case for the fast paths */ "\n\t beq+ cr0,myword12" "\n\t bne- cr1,myloop" /* for counting in word 7 */ "\n\t li r10,3" "\n\t mtctr r10" "\n\t lvx v0,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v1,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v2,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v3,%[w],r12" /* "\n\t lvx v5,%[w],r12" -- word 21 */ "\n\t vxor v5, v5, v7" "\n\t addi r12,r12,32" "\n\t vxor v5, v5,v13" "\n\t lvx v4,%[w],r11" "\n\t vxor v5, v5, v2" "\n\t addi r11,r11,32" "\n\t vrlw v5, v5, %[s]" "\n\t stvx v5, %[w], r12" "\n\t addi r12,r12,32" "\n\t lvx v6,%[w],r11" "\n\t addi r11,r11,32" /* "\n\t lvx v7,%[w],r12" -- word 23 */ /* "\n\t lvx v8,%[w],r11" -- word 24 */ "\n\t vxor v7, v7, v9" "\n\t vxor v8, v8,v10" "\n\t vxor v7, v7,v15" "\n\t vxor v8, v8, v0" "\n\t vxor v7, v7, v4" "\n\t vxor v8, v8, v5" "\n\t vrlw v7, v7, %[s]" "\n\t vrlw v8, v8, %[s]" "\n\t stvx v7, %[w], r12" "\n\t addi r12,r12,32" "\n\t stvx v8, %[w], r11" "\n\t addi r11,r11,32" "\n\t lvx v9,%[w],r12" "\n\t addi r12,r12,32" /* "\n\t lvx v10,%[w],r11" -- word 26 */ /* "\n\t lvx v11,%[w],r12" -- word 27 */ "\n\t vxor v10,v10,v12" "\n\t vxor v11,v11,v13" "\n\t vxor v10,v10, v2" "\n\t vxor v11,v11, v3" "\n\t vxor v10,v10, v7" "\n\t vxor v11,v11, v8" "\n\t vrlw v10,v10, %[s]" "\n\t vrlw v11,v11, %[s]" "\n\t stvx v10, %[w], r11" "\n\t addi r11,r11,32" "\n\t stvx v11, %[w], r12" "\n\t addi r12,r12,32" "\n\t lvx v12,%[w],r11" "\n\t addi r11,r11,32" /* "\n\t lvx v13,%[w],r12" -- word 29 */ /* "\n\t lvx v14,%[w],r11" -- word 30 */ /* "\n\t lvx v15,%[w],r12" -- word 31 */ "\n\t vxor v13,v13,v15" "\n\t vxor v14,v14, v0" "\n\t vxor v15,v15, v1" "\n\t vxor v13,v13, v5" "\n\t vxor v14,v14, v6" "\n\t vxor v15,v15, v7" "\n\t vxor v13,v13,v10" "\n\t vxor v14,v14,v11" "\n\t vxor v15,v15,v12" "\n\t vrlw v13,v13, %[s]" "\n\t vrlw v14,v14, %[s]" "\n\t vrlw v15,v15, %[s]" "\n\t stvx v13, %[w], r12" "\n\t addi r12,r12,32" "\n\t stvx v14, %[w], r11" "\n\t addi r11,r11,32" "\n\t stvx v15, %[w], r12" "\n\t addi r12,r12,32" "\n\t addi r13,r11,32" "\n\t b myloop" /* for counting in word 12 */ "\nmyword12:" "\n\t li r10,3" "\n\t mtctr r10" "\n\t lvx v0,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v1,%[w],r12" "\n\t addi r12,r12,32" "\n\t lvx v2,%[w],r11" "\n\t addi r11,r11,32" "\n\t lvx v3,%[w],r12" "\n\t addi r12,r12,32" /* "\n\t lvx v4,%[w],r11" -- word 20 */ "\n\t vxor v4, v4, v6" "\n\t addi r13,r11,32" "\n\t lvx v5,%[w],r12" "\n\t vxor v4, v4,v12" "\n\t lvx v6,%[w],r13" "\n\t addi r12,r12,32" "\n\t vxor v4, v4, v1" "\n\t vrlw v4, v4, %[s]" "\n\t stvx v4, %[w], r11" "\n\t addi r11,r13,32" /* "\n\t lvx v7,%[w],r12" -- word 23 */ "\n\t vxor v7, v7, v9" "\n\t addi r13,r12,32" "\n\t lvx v8,%[w],r11" "\n\t vxor v7, v7,v15" "\n\t lvx v9,%[w],r13" "\n\t addi r11,r11,32" "\n\t vxor v7, v7, v4" "\n\t vrlw v7, v7, %[s]" "\n\t stvx v7, %[w], r12" "\n\t addi r12,r13,32" /* "\n\t lvx v10,%[w],r11" -- word 26 */ "\n\t vxor v10,v10,v12" "\n\t vxor v12,v12,v14" "\n\t lvx v11,%[w],r12" "\n\t vxor v10,v10, v2" "\n\t vxor v12,v12, v4" "\n\t addi r12,r12,32" "\n\t vxor v10,v10, v7" "\n\t vxor v12,v12, v9" /* "\n\t lvx v12,%[w],r11" -- word 28 */ "\n\t addi r13,r11,32" "\n\t vrlw v10,v10, %[s]" "\n\t vrlw v12,v12, %[s]" "\n\t stvx v10, %[w], r11" "\n\t addi r11,r13,32" "\n\t stvx v12, %[w], r13" /* "\n\t lvx v13,%[w],r12" -- word 29 */ "\n\t vxor v13,v13,v15" "\n\t vxor v15,v15, v1" "\n\t lvx v14,%[w],r11" "\n\t vxor v13,v13, v5" "\n\t vxor v15,v15, v7" "\n\t addi r11,r11,32" "\n\t vxor v13,v13,v10" "\n\t vxor v15,v15,v12" /* "\n\t lvx v15,%[w],r12" -- word 31 */ "\n\t addi r13,r12,32" "\n\t vrlw v13,v13, %[s]" "\n\t vrlw v15,v15, %[s]" "\n\t stvx v13, %[w], r12" "\n\t addi r12,r13,32" "\n\t stvx v15, %[w], r13" "\n\t addi r13,r11,32" /* spew out the rest of the W buffer, in three or four passes */ "\nmyloop:" "\n\t vxor v0, v0, v2" "\n\t vxor v1, v1, v3" "\n\t vxor v2, v2, v4" "\n\t vxor v0, v0, v8" "\n\t vxor v1, v1, v9" "\n\t vxor v2, v2,v10" "\n\t vxor v0, v0,v13" "\n\t vxor v1, v1,v14" "\n\t vxor v2, v2,v15" "\n\t vrlw v0, v0, %[s]" "\n\t vrlw v1, v1, %[s]" "\n\t vrlw v2, v2, %[s]" "\n\t stvx v0, %[w], r11" "\n\t addi r11, r11, 48" "\n\t stvx v1, %[w], r12" "\n\t addi r12, r12, 48" "\n\t stvx v2, %[w], r13" "\n\t addi r13, r13, 48" "\n\t vxor v3, v3, v5" "\n\t vxor v4, v4, v6" "\n\t vxor v5, v5, v7" "\n\t vxor v3, v3,v11" "\n\t vxor v4, v4,v12" "\n\t vxor v5, v5,v13" "\n\t vxor v3, v3, v0" "\n\t vxor v4, v4, v1" "\n\t vxor v5, v5, v2" "\n\t vrlw v3, v3, %[s]" "\n\t vrlw v4, v4, %[s]" "\n\t vrlw v5, v5, %[s]" "\n\t stvx v3, %[w], r11" "\n\t addi r11, r11, 48" "\n\t stvx v4, %[w], r12" "\n\t addi r12, r12, 48" "\n\t stvx v5, %[w], r13" "\n\t addi r13, r13, 48" "\n\t vxor v6, v6, v8" "\n\t vxor v7, v7, v9" "\n\t vxor v8, v8,v10" "\n\t vxor v6, v6,v14" "\n\t vxor v7, v7,v15" "\n\t vxor v8, v8, v0" "\n\t vxor v6, v6, v3" "\n\t vxor v7, v7, v4" "\n\t vxor v8, v8, v5" "\n\t vrlw v6, v6, %[s]" "\n\t vrlw v7, v7, %[s]" "\n\t vrlw v8, v8, %[s]" "\n\t stvx v6, %[w], r11" "\n\t addi r11, r11, 48" "\n\t stvx v7, %[w], r12" "\n\t addi r12, r12, 48" "\n\t stvx v8, %[w], r13" "\n\t addi r13, r13, 48" "\n\t vxor v9, v9,v11" "\n\t vxor v10,v10,v12" "\n\t vxor v11,v11,v13" "\n\t vxor v9, v9, v1" "\n\t vxor v10,v10, v2" "\n\t vxor v11,v11, v3" "\n\t vxor v9, v9, v6" "\n\t vxor v10,v10, v7" "\n\t vxor v11,v11, v8" "\n\t vrlw v9, v9, %[s]" "\n\t vrlw v10,v10, %[s]" "\n\t vrlw v11,v11, %[s]" "\n\t stvx v9, %[w], r11" "\n\t addi r11, r11, 48" "\n\t stvx v10, %[w], r12" "\n\t addi r12, r12, 48" "\n\t stvx v11, %[w], r13" "\n\t addi r13, r13, 48" "\n\t vxor v12,v12,v14" "\n\t vxor v13,v13,v15" "\n\t vxor v12,v12, v4" "\n\t vxor v13,v13, v5" "\n\t vxor v12,v12, v9" "\n\t vxor v13,v13,v10" "\n\t vrlw v12,v12, %[s]" "\n\t vrlw v13,v13, %[s]" "\n\t stvx v12, %[w], r11" "\n\t addi r11, r11, 32" "\n\t stvx v13, %[w], r12" "\n\t addi r12, r12, 32" "\n\t vxor v14,v14, v0" "\n\t vxor v15,v15, v1" "\n\t vxor v14,v14, v6" "\n\t vxor v15,v15, v7" "\n\t vxor v14,v14,v11" "\n\t vxor v15,v15,v12" "\n\t vrlw v14,v14, %[s]" "\n\t vrlw v15,v15, %[s]" "\n\t stvx v14, %[w], r11" "\n\t addi r11, r11, 32" "\n\t stvx v15, %[w], r12" "\n\t addi r12, r12, 32" "\n\t addi r13, r11, 32" "\n\t bdnz myloop" "\n\t mtctr r14" : /* no outputs */ : [w] "r" (W), [s] "v" ((vector unsigned int)(1)), [tail] "r" (tailIndex) : "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "r10", "r11", "r12", "r13", "r14", "r0", "memory" ); } #endif unsigned long minter_altivec_compact_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { #if defined(__POWERPC__) && defined(__ALTIVEC__) && defined(__GNUC__) MINTER_CALLBACK_VARS; unsigned long iters; unsigned int m, n, o, t, gotBits, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low, bitMask1High, s; vector unsigned int vBitMaskHigh, vBitMaskLow; register vector unsigned int A1,B1,C1,D1,E1,Fa,Ga; register vector unsigned int A2,B2,C2,D2,E2,Fb,Gb; vector unsigned int A,B,N; vector unsigned int W1[80], W2[80], *W; vector unsigned int H[5], pH[5], K[4] = { (vector unsigned int) (K1), (vector unsigned int) (K2), (vector unsigned int) (K3), (vector unsigned int) (K4) }; vector bool int M; uInt32 *Hw = (uInt32*) H; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X1 = (unsigned char*) W1; unsigned char *X2 = (unsigned char*) W2; unsigned char *output = (unsigned char*) block, *X; *best = 0; /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } *((uInt32*) &vBitMaskLow ) = bitMask1Low ; vBitMaskLow = vec_splat(vBitMaskLow , 0); *((uInt32*) &vBitMaskHigh) = bitMask1High; vBitMaskHigh = vec_splat(vBitMaskHigh, 0); maxBits = 0; /* Copy block and IV to vectorised internal storage */ for(t=0; t < 16; t++) { X1[t*16+ 0] = X1[t*16+ 4] = X1[t*16+ 8] = X1[t*16+12] = output[t*4+0]; X1[t*16+ 1] = X1[t*16+ 5] = X1[t*16+ 9] = X1[t*16+13] = output[t*4+1]; X1[t*16+ 2] = X1[t*16+ 6] = X1[t*16+10] = X1[t*16+14] = output[t*4+2]; X1[t*16+ 3] = X1[t*16+ 7] = X1[t*16+11] = X1[t*16+15] = output[t*4+3]; X2[t*16+ 0] = X2[t*16+ 4] = X2[t*16+ 8] = X2[t*16+12] = output[t*4+0]; X2[t*16+ 1] = X2[t*16+ 5] = X2[t*16+ 9] = X2[t*16+13] = output[t*4+1]; X2[t*16+ 2] = X2[t*16+ 6] = X2[t*16+10] = X2[t*16+14] = output[t*4+2]; X2[t*16+ 3] = X2[t*16+ 7] = X2[t*16+11] = X2[t*16+15] = output[t*4+3]; } for(t=0; t < 5; t++) { Hw[t*4+0] = Hw[t*4+1] = Hw[t*4+2] = Hw[t*4+3] = IV[t]; pH[t] = H[t]; } /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-8; iters += 8) { /* Encode iteration count into tail */ /* Iteration count is always 8-aligned, so only least-significant character needs multiple lookup */ /* Further, we assume we're always big-endian */ X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 0] = p[(iters & 0x38) + 0]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 0] = p[(iters & 0x38) + 1]; X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 4] = p[(iters & 0x38) + 2]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 4] = p[(iters & 0x38) + 3]; X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 8] = p[(iters & 0x38) + 4]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 8] = p[(iters & 0x38) + 5]; X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 12] = p[(iters & 0x38) + 6]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 12] = p[(iters & 0x38) + 7]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 0] = X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 4] = X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 8] = X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 12] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 0] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 4] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 8] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 12] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 0] = X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 4] = X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 8] = X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 12] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 0] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 4] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 8] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 12] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 0] = X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 4] = X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 8] = X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 12] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 0] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 4] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 8] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 12] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 0] = X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 4] = X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 8] = X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 12] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 0] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 4] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 8] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 12] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 0] = X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 4] = X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 8] = X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 12] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 0] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 4] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 8] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 12] = p[(iters >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { WF(W1, 0); WF(W2, 0); A1 = A2 = H[0]; B1 = B2 = H[1]; C1 = C2 = H[2]; D1 = D2 = H[3]; E1 = E2 = H[4]; ROUNDn( 0, A, B, C, D, E, F1, K[0], W ); ROUNDn( 1, E, A, B, C, D, F1, K[0], W ); ROUNDn( 2, D, E, A, B, C, F1, K[0], W ); ROUNDn( 3, C, D, E, A, B, F1, K[0], W ); ROUNDn( 4, B, C, D, E, A, F1, K[0], W ); ROUNDn( 5, A, B, C, D, E, F1, K[0], W ); ROUNDn( 6, E, A, B, C, D, F1, K[0], W ); if(tailIndex == 52) { ROUNDn( 7, D, E, A, B, C, F1, K[0], W ); ROUNDn( 8, C, D, E, A, B, F1, K[0], W ); ROUNDn( 9, B, C, D, E, A, F1, K[0], W ); ROUNDn(10, A, B, C, D, E, F1, K[0], W ); ROUNDn(11, E, A, B, C, D, F1, K[0], W ); } pH[0] = A1; pH[1] = B1; pH[2] = C1; pH[3] = D1; pH[4] = E1; } /* Populate W buffer */ WF(W1, tailIndex); WF(W2, tailIndex); /* for(t=16; t < 80; t++) { if(vec_any_ne(W1[t], W2[t])) { printf("\nW buffers differ at round %d!\n", t); return 0; } } */ /* Set up working variables */ A1 = A2 = pH[0]; B1 = B2 = pH[1]; C1 = C2 = pH[2]; D1 = D2 = pH[3]; E1 = E2 = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUNDn( 0, A, B, C, D, E, F1, K[0], W ); ROUNDn( 1, E, A, B, C, D, F1, K[0], W ); ROUNDn( 2, D, E, A, B, C, F1, K[0], W ); ROUNDn( 3, C, D, E, A, B, F1, K[0], W ); ROUNDn( 4, B, C, D, E, A, F1, K[0], W ); ROUNDn( 5, A, B, C, D, E, F1, K[0], W ); ROUNDn( 6, E, A, B, C, D, F1, K[0], W ); case 32: ROUNDn( 7, D, E, A, B, C, F1, K[0], W ); ROUNDn( 8, C, D, E, A, B, F1, K[0], W ); ROUNDn( 9, B, C, D, E, A, F1, K[0], W ); ROUNDn(10, A, B, C, D, E, F1, K[0], W ); ROUNDn(11, E, A, B, C, D, F1, K[0], W ); case 52: ROUNDn(12, D, E, A, B, C, F1, K[0], W ); ROUNDn(13, C, D, E, A, B, F1, K[0], W ); ROUNDn(14, B, C, D, E, A, F1, K[0], W ); } ROUND5(15, F1, K[0] ); ROUND5(20, F2, K[1] ); ROUND5(25, F2, K[1] ); ROUND5(30, F2, K[1] ); ROUND5(35, F2, K[1] ); ROUND5(40, F3, K[2] ); ROUND5(45, F3, K[2] ); ROUND5(50, F3, K[2] ); ROUND5(55, F3, K[2] ); ROUND5(60, F4, K[3] ); ROUND5(65, F4, K[3] ); ROUND5(70, F4, K[3] ); ROUND5(75, F4, K[3] ); /* for(n=0; n < 4; n++) { ROUND5( 0 + n*5, F1, K[0] ); } for(n=0; n < 4; n++) { ROUND5( 0 + n*5, F2, K[1] ); } for(n=0; n < 4; n++) { ROUND5( 0 + n*5, F3, K[2] ); } for(n=0; n < 4; n++) { ROUND5( 0 + n*5, F4, K[3] ); } */ /* Mix in the IV again */ A1 = vec_add(A1, H[0]); B1 = vec_add(B1, H[1]); A2 = vec_add(A2, H[0]); B2 = vec_add(B2, H[1]); /* Debugging! */ if(0 && iters==0) { for(t=0; t < 80; t++) { X1 = (unsigned char*) (W1+t); printf("%2X %2X %2X %2X | %2X %2X %2X %2X\n", *(X1++), *(X1++), *(X1++), *(X1++), *(X1++), *(X1++), *(X1++), *(X1++) ); } X1 = (unsigned char*) W1; } /* Quickly extract the best results from each pipe into a single vector set */ M = vec_sel(vec_cmpgt(A1,A2), vec_cmpgt(B1,B2), vec_cmpeq(A1,A2)); N = vec_sel((vector unsigned int) (0), (vector unsigned int) (1), M); A = vec_sel(A1, A2, M); B = vec_sel(B1, B2, M); /* Is this the best bit count so far? */ if(vec_any_ne( vec_and( vec_cmpeq(vec_and(A, vBitMaskLow), (vector unsigned int) (0)), vec_cmpeq(vec_and(A, vBitMaskHigh), (vector unsigned int) (0)) ), (vector unsigned int) (0))) { uInt32 IA, IB; /* Go over each vector element in turn */ for(n=0; n < 4; n++) { /* Extract A and B components */ IA = ((uInt32*) &A)[n]; IB = ((uInt32*) &B)[n]; m = ((uInt32*) &N)[n]; /* Count bits */ gotBits = 0; if(IA) { s = IA; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(IB) { s = IB; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } *((uInt32*) &vBitMaskLow ) = bitMask1Low ; vBitMaskLow = vec_splat(vBitMaskLow , 0); *((uInt32*) &vBitMaskHigh) = bitMask1High; vBitMaskHigh = vec_splat(vBitMaskHigh, 0); /* Copy this result back to the block buffer */ if(m) X = X2; else X = X1; for(t=0; t < 16; t++) { output[t*4+0] = X[t*16+0+n*4]; output[t*4+1] = X[t*16+1+n*4]; output[t*4+2] = X[t*16+2+n*4]; output[t*4+3] = X[t*16+3+n*4]; } /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+8; } } } if(0) return 0; MINTER_CALLBACK(); } return iters+8; /* For other platforms */ #else return 0; #endif } hashcash-1.21/fastmint_mmx_compact_1.c0000664000076400007640000003522110352046606016424 0ustar adamadam#include "libfastmint.h" #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) typedef int mmx_d_t __attribute__ ((vector_size (8))); typedef int mmx_q_t __attribute__ ((vector_size (8))); #endif int minter_mmx_compact_1_test(void) { /* This minter runs only on x86 and AMD64 hardware supporting MMX - and will only compile on GCC */ #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) return (gProcessorSupportFlags & HC_CPU_SUPPORTS_MMX) != 0; #endif /* Not an x86 or AMD64, or compiler doesn't support MMX or GNU assembly */ return 0; } /* Define low-level primitives in terms of operations */ /* #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) */ #define XOR(a,b) ( (mmx_d_t) __builtin_ia32_pxor( (mmx_q_t) a, (mmx_q_t) b) ) #define AND(a,b) ( (mmx_d_t) __builtin_ia32_pand( (mmx_q_t) a, (mmx_q_t) b) ) #define ANDNOT(a,b) ( (mmx_d_t) __builtin_ia32_pandn( (mmx_q_t) b, (mmx_q_t) a) ) #define OR(a,b) ( (mmx_d_t) __builtin_ia32_por( (mmx_q_t) a, (mmx_q_t) b) ) #define ADD(a,b) ( __builtin_ia32_paddd(a,b) ) #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) static inline mmx_d_t S(int n, mmx_d_t X) { mmx_d_t G = {} ; asm ("movq %[x],%[g]\n\t" "pslld %[sl],%[x]\n\t" "psrld %[sr],%[g]\n\t" "por %[g],%[x]" : [g] "=y" (G), [x] "=y" (X) : "[x]" (X), [sl] "g" (n), [sr] "g" (32-n) ); return X; } #endif /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ #define F1( B, C, D ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) /* #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F2( B, C, D ) ( \ F = XOR(B,C), \ XOR(F,D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ /* #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) */ #define F3( B, C, D ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) /* #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F4(B,C,D) F2(B,C,D) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) */ #define Wf(W,t) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) #define Wfly(W,t) ( (t) < 16 ? (W)[t] : Wf(W,t) ) /* #define ROUND(t,A,B,C,D,E,Func,K,W) \ E = ADD(E,K); \ F = S(5,A); \ E = ADD(F,E); \ F = Wfly(W,t); \ E = ADD(F,E); \ F = Func(B,C,D); \ E = ADD(F,E); \ B = S(30,B); */ #define ROUND_F1_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[d], %%mm5" /* begin F1(B,C,D) */ \ "\n\t pxor %[c], %%mm5" \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pand %[b], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t pxor %[d], %%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F1(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t pslld $30, %[b]" \ "\n\t psrld $2, %%mm5" \ "\n\t paddd %[Wt],%[e]" /* sum W[t] to E */ \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E) \ : [Wt] "m" ((W)[t]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F2_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[b], %%mm5" /* begin F2(B,C,D) */ \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pxor %[c], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t pxor %[d], %%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F2(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t pslld $30, %[b]" \ "\n\t psrld $2, %%mm5" \ "\n\t paddd %[Wt],%[e]" /* sum W[t] to E */ \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E) \ : [Wt] "m" ((W)[t]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F3_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[d], %%mm5" /* begin F3(B,C,D) */ \ "\n\t movq %[d], %%mm6" \ "\n\t por %[c], %%mm5" \ "\n\t pand %[c], %%mm6" \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pand %[b], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t por %%mm6,%%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F3(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t pslld $30, %[b]" \ "\n\t psrld $2, %%mm5" \ "\n\t paddd %[Wt],%[e]" /* sum W[t] to E */ \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E) \ : [Wt] "m" ((W)[t]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F4_n ROUND_F2_n #define ROUND(t,A,B,C,D,E,Func,K) \ ROUND_##Func##_n(t,A,B,C,D,E,K,W); \ #define ROUND5( t, Func, K ) \ ROUND( t + 0, A, B, C, D, E, Func, K );\ ROUND( t + 1, E, A, B, C, D, Func, K );\ ROUND( t + 2, D, E, A, B, C, Func, K );\ ROUND( t + 3, C, D, E, A, B, Func, K );\ ROUND( t + 4, B, C, D, E, A, Func, K ) #if defined(MINTER_CALLBACK_CLEANUP_FP) #undef MINTER_CALLBACK_CLEANUP_FP #endif #define MINTER_CALLBACK_CLEANUP_FP __builtin_ia32_emms() unsigned long minter_mmx_compact_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) MINTER_CALLBACK_VARS; unsigned long iters = 0 ; int n = 0, t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; mmx_d_t vBitMaskHigh = {} , vBitMaskLow = {} ; register mmx_d_t A = {} , B = {} , C = {} , D = {} , E = {} ; mmx_d_t MA = {} , MB = {} ; mmx_d_t W[80] = {} ; mmx_d_t H[5] = {} , pH[5] = {} ; mmx_d_t K[4] = {} ; uInt32 *Hw = (uInt32*) H; uInt32 *pHw = (uInt32*) pH; uInt32 IA = 0 , IB = 0 ; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X = (unsigned char*) W; unsigned char *output = (unsigned char*) block; *best = 0; /* Splat Kn constants into MMX-style array */ ((uInt32*)K)[0] = ((uInt32*)K)[1] = K1; ((uInt32*)K)[2] = ((uInt32*)K)[3] = K2; ((uInt32*)K)[4] = ((uInt32*)K)[5] = K3; ((uInt32*)K)[6] = ((uInt32*)K)[7] = K4; /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } ((uInt32*) &vBitMaskLow )[0] = bitMask1Low ; ((uInt32*) &vBitMaskLow )[1] = bitMask1Low ; ((uInt32*) &vBitMaskHigh)[0] = bitMask1High; ((uInt32*) &vBitMaskHigh)[1] = bitMask1High; maxBits = 0; /* Copy block and IV to vectorised internal storage */ /* Assume little-endian order, as we're on x86 or AMD64 */ for(t=0; t < 16; t++) { X[t*8+ 0] = X[t*8+ 4] = output[t*4+3]; X[t*8+ 1] = X[t*8+ 5] = output[t*4+2]; X[t*8+ 2] = X[t*8+ 6] = output[t*4+1]; X[t*8+ 3] = X[t*8+ 7] = output[t*4+0]; } for(t=0; t < 5; t++) { Hw[t*2+0] = Hw[t*2+1] = pHw[t*2+0] = pHw[t*2+1] = IV[t]; } /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-2; iters += 2) { /* Encode iteration count into tail */ /* Iteration count is always 2-aligned, so only least-significant character needs multiple lookup */ /* Further, we assume we're always little-endian */ X[(((tailIndex - 1) & ~3) << 1) + (((tailIndex - 1) & 3) ^ 3) + 0] = p[(iters & 0x3e) + 0]; X[(((tailIndex - 1) & ~3) << 1) + (((tailIndex - 1) & 3) ^ 3) + 4] = p[(iters & 0x3e) + 1]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(((tailIndex - 2) & ~3) << 1) + (((tailIndex - 2) & 3) ^ 3) + 0] = X[(((tailIndex - 2) & ~3) << 1) + (((tailIndex - 2) & 3) ^ 3) + 4] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(((tailIndex - 3) & ~3) << 1) + (((tailIndex - 3) & 3) ^ 3) + 0] = X[(((tailIndex - 3) & ~3) << 1) + (((tailIndex - 3) & 3) ^ 3) + 4] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(((tailIndex - 4) & ~3) << 1) + (((tailIndex - 4) & 3) ^ 3) + 0] = X[(((tailIndex - 4) & ~3) << 1) + (((tailIndex - 4) & 3) ^ 3) + 4] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(((tailIndex - 5) & ~3) << 1) + (((tailIndex - 5) & 3) ^ 3) + 0] = X[(((tailIndex - 5) & ~3) << 1) + (((tailIndex - 5) & 3) ^ 3) + 4] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(((tailIndex - 6) & ~3) << 1) + (((tailIndex - 6) & 3) ^ 3) + 0] = X[(((tailIndex - 6) & ~3) << 1) + (((tailIndex - 6) & 3) ^ 3) + 4] = p[(iters >> 30) & 0x3f]; } } /* Force compiler to flush and reload MMX registers */ asm volatile ( "nop" : : : "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "memory" ); /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; ROUND( 0, A, B, C, D, E, F1, K[0] ); ROUND( 1, E, A, B, C, D, F1, K[0] ); ROUND( 2, D, E, A, B, C, F1, K[0] ); ROUND( 3, C, D, E, A, B, F1, K[0] ); ROUND( 4, B, C, D, E, A, F1, K[0] ); ROUND( 5, A, B, C, D, E, F1, K[0] ); ROUND( 6, E, A, B, C, D, F1, K[0] ); if(tailIndex == 52) { ROUND( 7, D, E, A, B, C, F1, K[0] ); ROUND( 8, C, D, E, A, B, F1, K[0] ); ROUND( 9, B, C, D, E, A, F1, K[0] ); ROUND(10, A, B, C, D, E, F1, K[0] ); ROUND(11, E, A, B, C, D, F1, K[0] ); } pH[0] = A; pH[1] = B; pH[2] = C; pH[3] = D; pH[4] = E; } /* Populate W buffer */ for(t=16; t < 80; t += 4) { asm volatile ( /* Use pairs of adjacent MMX registers to build four nearly-independent chains */ "\n\t movq -128(%[w]),%%mm0" "\n\t movq -120(%[w]),%%mm2" "\n\t movq -112(%[w]),%%mm4" "\n\t movq -104(%[w]),%%mm6" "\n\t pxor %%mm4, %%mm0" "\n\t pxor %%mm6, %%mm2" "\n\t pxor -96(%[w]),%%mm4" "\n\t pxor -88(%[w]),%%mm6" "\n\t pxor -64(%[w]),%%mm0" "\n\t pxor -56(%[w]),%%mm2" "\n\t pxor -48(%[w]),%%mm4" "\n\t pxor -40(%[w]),%%mm6" "\n\t pxor -24(%[w]),%%mm0" "\n\t pxor -16(%[w]),%%mm2" /* 0(%[w]) is not yet valid! */ "\n\t movq %%mm0, %%mm1" "\n\t movq %%mm2, %%mm3" "\n\t pslld $1, %%mm0" "\n\t psrld $31, %%mm1" "\n\t pslld $1, %%mm2" "\n\t psrld $31, %%mm3" "\n\t por %%mm1, %%mm0" "\n\t por %%mm3, %%mm2" /* ...now it is */ "\n\t pxor -8(%[w]),%%mm4" "\n\t pxor %%mm0, %%mm6" "\n\t movq %%mm4, %%mm5" "\n\t movq %%mm6, %%mm7" "\n\t pslld $1, %%mm4" "\n\t psrld $31, %%mm5" "\n\t pslld $1, %%mm6" "\n\t psrld $31, %%mm7" "\n\t por %%mm5, %%mm4" "\n\t por %%mm7, %%mm6" "\n\t movq %%mm0, 0(%[w])" "\n\t movq %%mm2, 8(%[w])" "\n\t movq %%mm4,16(%[w])" "\n\t movq %%mm6,24(%[w])" : /* no outputs */ : [w] "r" (W+t) : "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "memory" ); } /* Set up working variables */ A = pH[0]; B = pH[1]; C = pH[2]; D = pH[3]; E = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUND( 0, A, B, C, D, E, F1, K[0] ); ROUND( 1, E, A, B, C, D, F1, K[0] ); ROUND( 2, D, E, A, B, C, F1, K[0] ); ROUND( 3, C, D, E, A, B, F1, K[0] ); ROUND( 4, B, C, D, E, A, F1, K[0] ); ROUND( 5, A, B, C, D, E, F1, K[0] ); ROUND( 6, E, A, B, C, D, F1, K[0] ); case 32: ROUND( 7, D, E, A, B, C, F1, K[0] ); ROUND( 8, C, D, E, A, B, F1, K[0] ); ROUND( 9, B, C, D, E, A, F1, K[0] ); ROUND(10, A, B, C, D, E, F1, K[0] ); ROUND(11, E, A, B, C, D, F1, K[0] ); case 52: ROUND(12, D, E, A, B, C, F1, K[0] ); ROUND(13, C, D, E, A, B, F1, K[0] ); ROUND(14, B, C, D, E, A, F1, K[0] ); ROUND(15, A, B, C, D, E, F1, K[0] ); ROUND(16, E, A, B, C, D, F1, K[0] ); ROUND(17, D, E, A, B, C, F1, K[0] ); ROUND(18, C, D, E, A, B, F1, K[0] ); ROUND(19, B, C, D, E, A, F1, K[0] ); } ROUND5(20, F2, K[1] ); ROUND5(25, F2, K[1] ); ROUND5(30, F2, K[1] ); ROUND5(35, F2, K[1] ); ROUND5(40, F3, K[2] ); ROUND5(45, F3, K[2] ); ROUND5(50, F3, K[2] ); ROUND5(55, F3, K[2] ); ROUND5(60, F4, K[3] ); ROUND5(65, F4, K[3] ); ROUND5(70, F4, K[3] ); ROUND5(75, F4, K[3] ); /* Mix in the IV again */ MA = ADD(A, H[0]); MB = ADD(B, H[1]); /* Go over each vector element in turn */ for(n=0; n < 2; n++) { /* Extract A and B components */ IA = ((uInt32*) &MA)[n]; IB = ((uInt32*) &MB)[n]; /* Is this the best bit count so far? */ if(!(IA & bitMask1Low) && !(IB & bitMask1High)) { /* Count bits */ gotBits = 0; if(IA) { s = IA; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(IB) { s = IB; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } ((uInt32*) &vBitMaskLow )[0] = bitMask1Low ; ((uInt32*) &vBitMaskLow )[1] = bitMask1Low ; ((uInt32*) &vBitMaskHigh)[0] = bitMask1High; ((uInt32*) &vBitMaskHigh)[1] = bitMask1High; /* Copy this result back to the block buffer, little-endian */ for(t=0; t < 16; t++) { output[t*4+0] = X[t*8+3+n*4]; output[t*4+1] = X[t*8+2+n*4]; output[t*4+2] = X[t*8+1+n*4]; output[t*4+3] = X[t*8+0+n*4]; } /* Is it good enough to bail out? */ if(gotBits >= bits) { /* Shut down use of MMX */ __builtin_ia32_emms(); return iters+2; } } } MINTER_CALLBACK(); } /* Shut down use of MMX */ __builtin_ia32_emms(); return iters+2; /* For other platforms */ #else return 0; #endif } hashcash-1.21/test-msg.txt0000664000076400007640000000057610351101612014121 0ustar adamadamFrom: Someone To: Adam Back Cc: Someone , Adam Back 16) ? 16 : bits ; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; const char *p = encodeAlphabets[EncodeBase64]; uInt32 A = 0, B = 0; int addressMask = 0 ; static const int endTest = 3; unsigned char *output = (unsigned char*) block; unsigned char Xa[ SHA1_INPUT_BYTES*2 ]; /* worst case */ unsigned char* X = Xa; uInt32 *W = (uInt32*)Xa; uInt32 H[ SHA1_DIGEST_WORDS ]; int blocks = (tailIndex+1+8) > SHA1_INPUT_BYTES ? 2 : 1; *best = 0; /* Work out whether we need to swap bytes during encoding */ addressMask = ( *(char*)&endTest ); for(t=0; t < 16*blocks; t++) { W[t] = GET_WORD(output + t*4); } /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } maxBits = 0; /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter; iters++) { /* Encode iteration count into tail */ X[(tailIndex - 1) ^ addressMask] = p[(iters ) & 0x3f]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(tailIndex - 2) ^ addressMask] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(tailIndex - 3) ^ addressMask] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(tailIndex - 4) ^ addressMask] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(tailIndex - 5) ^ addressMask] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(tailIndex - 6) ^ addressMask] = p[(iters >> 30) & 0x3f]; } } memcpy( H, IV, SHA1_DIGEST_BYTES ); SHA1_Transform( H, X ); if ( blocks == 2 ) { SHA1_Transform( H, X+SHA1_INPUT_BYTES ); } A = H[0]; B = H[1]; /* Is this the best bit count so far? */ if(!(A & bitMask1Low) && !(B & bitMask1High)) { /* Count bits */ gotBits = 0; if(A) { s = A; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(B) { s = B; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } /* Copy this result back to the block buffer */ for(t=0; t < 16*blocks; t++) { PUT_WORD(output + t*4, W[t]); } /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+1; } } MINTER_CALLBACK(); } return iters+1; } hashcash-1.21/types.h0000664000076400007640000000143210351101612013122 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _types_h ) #define _types_h #include #define word unsigned #define byte unsigned char #define bool byte #define true 1 #define false 0 #define word8 unsigned char #define int8 signed char #define int16 signed short #define word16 unsigned short #if ( ULONG_MAX > 0xFFFFFFFFUL ) #define int32 signed int #define word32 unsigned int #define int64 signed long #define word64 unsigned long #elif ( UINT_MAX == 0xFFFFFFFFUL ) #define int32 signed int #define word32 unsigned int #else #define int32 signed long #define word32 unsigned long #endif #if defined( __GNUC__ ) && !defined( word32 ) #define int64 signed long long #define word64 unsigned long long #endif #endif hashcash-1.21/getopt.c0000664000076400007640000005211010351101612013252 0ustar adamadam/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef __STDC__ # ifndef const # define const # endif #endif /* This tells Alpha OSF/1 not to define a getopt prototype in . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #include #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #if defined (_LIBC) || !defined (__GNU_LIBRARY__) /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #endif /* GNU C library. */ /* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a long-named option. Because this is not POSIX.2 compliant, it is being phased out. */ /* #define GETOPT_COMPAT */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = 0; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ #define BAD_OPTION '\0' int optopt = BAD_OPTION; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #define my_strlen strlen #else /* Avoid depending on library functions or files whose names are inconsistent. */ #if __STDC__ || defined(PROTO) extern char *getenv(const char *name); extern int strcmp (const char *s1, const char *s2); /*extern int strncmp(const char *s1, const char *s2, int n);*/ static int my_strlen(const char *s); static char *my_index (const char *str, int chr); #else extern char *getenv (); #endif static int my_strlen (str) const char *str; { int n = 0; while (*str++) n++; return n; } static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } #endif /* GNU C library. */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. To perform the swap, we first reverse the order of all elements. So all options now come before all non options, but they are in the wrong order. So we put back the options and non options in original order by reversing them again. For example: original input: a b c -x -y reverse all: -y -x c b a reverse options: -x -y c b a reverse non options: -x -y a b c */ #if __STDC__ || defined(PROTO) static void exchange (char **argv); #endif static void exchange (argv) char **argv; { char *temp = NULL , **first = NULL , **last = NULL ; /* Reverse all the elements [first_nonopt, optind) */ first = &argv[first_nonopt]; last = &argv[optind-1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } /* Put back the options in order */ first = &argv[first_nonopt]; first_nonopt += (optind - last_nonopt); last = &argv[first_nonopt - 1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } /* Put back the non options in order */ first = &argv[first_nonopt]; last_nonopt = optind; last = &argv[last_nonopt-1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns `EOF'. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return BAD_OPTION after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return BAD_OPTION. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int option_index = 0 ; optarg = 0; /* Initialize the internal data when the first call is made. Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ if (optind == 0) { first_nonopt = last_nonopt = optind = 1; nextchar = NULL; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (getenv ("POSIXLY_CORRECT") != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; } if (nextchar == NULL || *nextchar == '\0') { if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Now skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) optind++; last_nonopt = optind; } /* Special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if ((argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Start decoding its characters. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only)) #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ )) { const struct option *p; char *s = nextchar; int exact = 0; int ambig = 0; const struct option *pfound = NULL; int indfound = 0; while (*s && *s != '=') s++; /* Test all options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, s - nextchar)) { if (s - nextchar == my_strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += my_strlen (nextchar); optind++; return BAD_OPTION; } if (pfound != NULL) { option_index = indfound; optind++; if (*s) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = s + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += my_strlen (nextchar); return BAD_OPTION; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += my_strlen (nextchar); return optstring[0] == ':' ? ':' : BAD_OPTION; } } nextchar += my_strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return BAD_OPTION; } } /* Look at and handle the next option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { #if 0 if (c < 040 || c >= 0177) fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c); else fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); #else /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); #endif } optopt = c; return BAD_OPTION; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = 0; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { #if 0 fprintf (stderr, "%s: option `-%c' requires an argument\n", argv[0], c); #else /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: option requires an argument -- %c\n", argv[0], c); #endif } optopt = c; if (optstring[0] == ':') c = ':'; else c = BAD_OPTION; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } int getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } #endif /* _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c = 0 ; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == EOF) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case BAD_OPTION: break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ hashcash-1.21/test/0000775000076400007640000000000010351101612012564 5ustar adamadamhashcash-1.21/hashcash.html0000644000076400007640000013036110351101612014257 0ustar adamadam hashcash


NAME

hashcash - hashcash anti-spam / denial of service counter-measure tool


SYNOPSIS

Mint (create) stamps:

hashcash -m [ [ options ] [ resource ... ] ... ]

Check stamps:

hashcash -c [ [ options ] -r resource ... ] [ stamp ... ]

Speed estimates:

hashcash -s [ options ] [ -b bits ]

Purge database:

hashcash -p now [ -j resource ] [ -k ] [ -t time ] [ -u ]

Count collision bits:

hashcash -w [ options ] [ stamp ... ]

Get resource name:

hashcash -n [ options ] [ stamp ... ]

Get time remaining until expiry

hashcash -l [ options ] -e period [ stamp ... ]


DESCRIPTION

Hashcash is a denial-of-service counter measure tool. Its main current use is to help hashcash users avoid losing email due to content based and blacklist based anti-spam systems.

The hashcash tool allows you to create hashcash stamps to attach to emails you send, and to verify hashcash stamps attached to emails you receive. Email senders attach hashcash stamps with the X-Hashcash: header. Vendors and authors of anti-spam tools are encouraged to exempt mail sent with hashcash from their blacklists and content based filtering rules.

A hashcash stamp constitutes a proof-of-work which takes a parameterizable amount of work to compute for the sender. The recipient can verify received stamps efficiently. This package also includes a sha1 implementation which is equivalent in functionality to the sha1sum program available on some systems.

The hashcash proof-of-work function is to compute partial hash collisions (the SHA1 hash function is used). By choosing the number of bits of collision, the work required to compute a collision can be made arbitrarily expensive -- from fractions of a second to minutes or hours. The verification is efficient requiring the same small cost whatever the collision size.

For more detailed discussion of other applications hashcash has been used for see http://www.hashcash.org/


USAGE NOTES

In this man page a resource name is the name of the service or address the stamp is created for. In the case of email, the resource name is the recipient's email address in the form user@domain.com.

Minting stamps

The -m flag must be given to mint a stamp.

The resource name (recipient's email address) to mint the stamp against can be passed as an argument, or if omitted is read from stdin. If stdin is a tty the user is prompted, if stdin is a pipe the resource name is just silently read. The desired collision size can be specified with the -b option. If no collision size is specified, the default is 20 bits. See also the -b default option.

Checking stamps

The -c flag must be given to check a stamps expiry. The stamp to check can be given as an argument to hashcash. If no stamp is given the stamp is read from stdin. If stdin is a tty the user will be prompted, if stdin is a pipe the stamp is just silently read. A resource name (the recipient's email address) can be given with the -r option. If a resource name is given the resource name is compared to the resource name in the stamp, if they do not match, the stamp is rejected.

Note: if no resource name is given the stamp is anyway checked to see if it is otherwise valid, but it could be minted for a different resource, which would allow stamps to be reused across different resources, so hashcash will return unchecked exit code on exit.

Stamps are by default considered to be valid for 28 days. The validity period can be changed using the -e flag.

If the stamp has expired or has a date in the future the stamp is rejected and the program exits immediately.

If a required collision size is given with the -b flag, the stamps value is computed and compared, if the stamp has insufficent value it is rejected, and the program exits immediately. If the -b flag is not given, the stamp is checked to see if it is otherwise valid, but hashcash will return unchecked exit code on exit.

If the stamp is double spent the stamp is rejected. Double spending protection is discussed in more detail below in Double Spending Protection. If double spending protection is not enabled, the stamp could be double spent, so hashcash will return unchecked exit code (exit code 2) on exit.

The -w flag can be used to request that the number of bits of the collision are counted and displayed. The -n flag can be used to request that the resource name in the stamp is parsed out and displayed. The -l flag can be used to request the number of seconds until expiry of the stamp is output.

The program will only return exit codes valid or invalid if the -c flag is used, the -b flag is used, -d, -r resource are used. These are the minimum set of options necessary to fully check the validty of a stamp. If these criteria are not met, the program will return exit code unchecked (exit code 2) on exit. (See also the -y flag.)

Double Spending Protection

If the -d flag is used when checking stamps, a database of spent stamps is kept.

By default stamps expire after 28 days, without expiry the database would grow indefinately. You can specify an alternate expiry period with the -e flag. The recommended (and default) expiry period for email is 28 days. After the expiry period amount of time, the stamp is anyway considered expired and may be purged from the database to save space. (See Purging Periodically vs on Next Access for how to purge stamps.)

For efficiency reasons a stamp is verified before it is checked in the database; if it is otherwise invalid no database activity will occur.

Note: The decision about how long the stamp should be considered valid is up to the verifier. If it is too short it is possible for some applications that the stamp will expire before arriving at the recipient (eg with email.) The suggested value of 28 days should be safe for normal email delivery delays. The choice is a trade-off between database size and risk of expiry prior to arrival, and depends on the application.

Note: Different stamps in the same database can have different validity periods, so for example stamps for different resources with different validity periods can be stored in the same database, or the recipient may change the validity period for future stamps without affecting the validity of old stamps.

Purging Periodically vs on Next Access

To purge old stamps periodically while checking stamps use the -p period option to purge no sooner than the given time period since the last purge. Purging can be used with the -k option to purge unexpired stamps also, and with the -j resource flag to purge only stamps for the given resource.

There are circumstances where it may be inconvenient to purge stamps on the next access, for example if there is a large double spend database which takes some time to purge, and the response time of the hashcash checker is important. To avoid this problem, purging can be done separately using just the -p now option to request just the purge operation. On unix for example you could call hashcash -p now in a cron job once per day, or on demand when disk was running low.

Speed Estimates

The -s flag requests measurement of how many collisions can be tested per second. No stamp is minted, or verified.

If the -b flag is used with this option, instead an estimate of how many seconds it would take to mint a stamp of the given size in bits is computed. To find out how much time it will take to mint a default sized stamp use -s -b default.

Notes

All informational output is printed on stderr. Minted stamps, and results of stamp verification and timing are printed on stdout. The quiet flag -q suppresses all informational output. The -v flag requests more informational output. The requested output, which is the only information that is output in quiet mode (when -q is specified) is printed on standard output. If stdout is a pipe, or when quiet mode is in effect the output is printed without description (ie just bits, just seconds, just resource).


OPTIONS

-c
Check the expiry information of stamps given as an argument or on stdin. (Use with -b, -d and -r resource to fully check stamps).

-m
Mint stamps with the resources given as arguments or on stdin.

-b bits
When minting a stamp, create a collision of at least this many bits. When verifying a stamp require that it have a collision of at minimum this many bits, otherwise reject it. If omitted the default is used.

When checking stamps, require that the stamps have this many bits.

The default number of bits can be specified with -b default. Bits relative to the default can also be specified with -b +n for n bits more than the default and -b -n for n bits less than the default.

-b default, -b +0 and -b -0 are all equivalent.

When doing the speed test -s, can to measure speed of default token with -s -b default.

-r resource
When minting stamps, the resource name (recipient's email address) to mint the stamp against can be given either with -r resource or as an argument to hashcash.

When checking stamps, the resource name (your own email address) is given with the -r option. If the resource name is given it is checked against the resource name in the stamp, and if they do not match the stamp is rejected. Note if the resource name is not given, stamps for other resources would be accepted, and therefore hashcash returns exit code unchecked (exit code 2) on exit.

-o
When verifying stamps multiple resources can be given. By default the resources are just checked one by one until a matching valid resource is found. However when you use wildcards or regular expressions (see -E), it is useful to be able to specify that one resource overrides another. For example this: -b15 -r adam@dev.null -o -b10 *@dev.null states that mail to address adam@dev.null requires 15 bits, but mail to *@dev.null requires only 10 bits. If we omitted the -o override relationship between the two resources, a stamp of 10 bits would be accepted for address adam@dev.null because while it would be rejected as having insufficient bits under the first rule, it would be accepted under the 2nd rule. The -o option allows you avoid this problem.

-e time
Expiry period for spent stamps. While checking stamps (using the -c flag), if the stamp was minted more than the specified amount of time ago, it is considered expired. If this option is not used, by default stamps expire after 28 days. The expiry period is given in seconds by default (an argument of 0 means forever). A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds).

If used with the -d option, the spent stamp and its expiry period is recorded in the database. See the -p option for description of how to purge stamps from the database.

While minting stamps, the -e flag can have an effect on the resolution of time created in the stamp. Without the -e option, the default resolution is days (time format: YYMMDD). Alternate formats based on range of expiry period are as follows:

While minting you can also given an explicit time width with the -z option instead. (-z overrides -e if both are given. If neither are given the default is 6 chars (time format: YYMMDD)).

The rules for automatically determining appropriate time width from -e if no -z option is given are:

Note the rounding down is based on UTC time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than GMT (UTC = GMT). It may be clearer to understand if you use the -u option.

-z width
The -z option is for use during minting and allows user choice of width of time width field. See also the -e option given in combination with -m to specify an implicit time field width under the description of the -e flag. Valid widths are 6,10 or 12 chars corresponding respectively to: YYMMDD, YYMMDDhhmm, and YYMMDDhhmmss rounded down to the nearest day, or minute respectively.

Note the rounding down is based on UTC time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than GMT (UTC = GMT). It may be clearer to understand if you use the -u option.

-g period
The -g option is for use when checking hashcash stamps with the -c option and specifies a grace period for clock skew, ie if a hashcash stamp arrives with a date in the future or in the past it will not be rejected as having a futuristic date (or as being expired) unless it is more futuristic (or has been expired for longer) than this period. The default is 2 days, which means as long as the sending system's clock is no more than 2 days ahead (or 2 days behind) of the receiving system's clock, the hashcash stamp will still be accepted.

The default units for grace period are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds).

-d
Store stamps in a double spend database. If stamp has been seen before it will be rejected even if it is otherwise valid. The default database file is database.sdb in the current directory. Only otherwise valid stamps will be stored in the database. Only fully validated stamps will be stored in the database, unless the -y option is given.

-f dbname
Use dbname instead of default filename for double spend database.

-p period
Purges the database of expired stamps if the given time period has passed since the last time it was purged. As a convenience -p now is equivalent to -p 0 both of which mean purge now, regardless of when the database was last purged.

If used in combination with -j resource only the stamps minted for the given resource are purged.

If used in combination with -k all stamps even un-expired stamps are purged. Can be used in combination with -t time to expire as if the current time were the given time.

-k
Use with option -p to request all stamps are purged rather than just expired ones.

-j resource
Use with option -p to request that just stamps matching the given resource name are to be purged, rather than the default which is to purge all expired stamps. If the resource name is the empty string, all stamps are matched (this is equivalent to omitting the -j option).

Note the -E, -M and -S type of match flags also apply to resources given with the -j resource flag.

-s
Print timing information only, and don't proceed to create a stamp. If combined with -b bits flag print estimate of how long the requested collision size would take to compute, if -s given by itself, just prints speed of the collision finder. To print an estimate of how long the default number of bits would take use -b default.

-h
Print short usage information.

-v
Print more verbose informational output about the stamp minting or verification. (If -v is the only argument, prints the tool version number.)

-V
Prints tool version number.

-q
Batch mode. Prints no information other than output. This option overrides the -v option.

-X
When minting, prints the hashcash email X-header 'X-Hashcash: ' before the stamp. Without this option just the bare stamp is printed.

When checking, after scanning stamps given as arguments, scans stdin for lines starting with the string 'X-Hashcash:', and uses the rest of the matching line as the stamp. Only the lines up to and ending at the first blank line are scanned (see also -i flag which can be used to override this). A blank line is the separator used to separate the headers from the body of a mail message or USENET article. This is meant to make it convenient to pipe a mail message or USENET article to hashcash on stdin.

-x extension
An extension string composed of name value sets. The extension format is described below in the section on the hashcash stamp format. This allows users to define their own stamp extensions which are hashed into the stamp, verified by recipients that support them, and ignored by recipients that don't support them. Note the extension hook mechanism has not yet been implemented. This will come in a subsequent release.

-i
When checking and using the -X flag, ignore the blank line boundary between headers and body of the message, and check for collision in the body too if one is not found in the headers.

-t time
Pretend the current time is the time given for purposes of minting stamps, verifying stamps and purging old stamps from the database. Time is given in a format based on UTCTIME format YYMMDD[hhmm[ss]].

Time is expressed in local time by default. Use with -u flag to give time in UTC (GMT).

You can also give time relative to the current time by prefixing the argument with + or -. The default units for relative time are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds).

Note: when time is expressed in local time, if there is daylight savings in your timezone, there are one or two ambiguous hours per year at the time of change from daylight savings time to normal time.

-u
Input and output absolute times in UTC (GMT) instead of local time.

-a period
Add (or subtract if number is negative) a random value from the current time before minting the stamp. This hides the time the stamp was created, which may be useful for anonymous users. Note adding (rather than subtracting) a random time may be risky if the stamp takes less than the added time to arrive as the recipient will reject stamps with time stamps in the future.

-n
Print resource name parsed from stamp being verified. Returns exit code unchecked on exit.

-l
Print number of seconds left before stamp expires. Returns exit code unchecked on exit.

Note: the calculation includes the grace period, so can be up to 2 times grace period longer than you might otherwise expect (clock fast but system has to presume it could be slow). If you want to exclude the grace period add -g0 to set grace period to 0 for the calculation.

-w
Print number of bits of collision of stamp. Returns exit code unchecked on exit.

-y
Returns success if the stamp is valid even if it is not fully checked. Use with -c where not all of -d, -r are specified to get success exit code on valid but partially checked stamp. Similarly can use with -n, -l, -w with same effect.

-M
When checking stamps, allow wildcard * matching in the resource name to make it simpler to specify multiple email addresses and to allow matching catch-all addresses and addresses including subdomains. This is the default. See also -S, -E and -C

-S
When checking stamps use simple text compare to compare resource names to those in stamps. See also -M, -E and -C.

-E
When checking stamps use regular expressions to specify resource names to make it simpler to specify multiple email addresses, catch-all addresses, classes of extension addresses and addresses including subdomains. Note regular expression syntax is POSIX style: special characters do not need to be quoted to have their special meaning; but they do have to be quoted with \ to that character in the searched string. The regular expression automatically has ^ added at the beginning and $ added at the end, if they are not specified. The special characters ^ matches the beginning of the resouce, and $ matches the end of resource.

(Note even if compiled with BSD regular expressions, POSIX style syntax is used; also note BSD regular expressions do not support ranges {}.)

-C
By default resources are canonicalized to lower case on minting and on checking. The -C flag overrides this so that resources are treated as case sensitive on checking, and not canonizalized on minting.

-P
Print progress info (number of iterations, expected iterations, percentage done, best stamp size found so far).

-O core
Select hashcash core with that number. Currently 0-9 are valid cores. Not all cores work on all architectures. Eg some are x86 specific assembler, others PPC specific assembler. If a core is not valid hashcash returns failure and explains what happened.

-Z n
Compress the stamp. This is a time vs space trade off. Larger stamps are faster, but arguably slightly ugly. For fastest stamps (the default) use -Z 0; for partly compressed stamps use -Z 1; for very compressed, but somewhat slow stamps use -Z 2. (Note: due to a late discovered bug, -Z2 is the same as -Z1 for now until I can fix that.)


EXAMPLES

Creating stamps

hashcash -s
Print timing information about how many collisions the machine can try per second.

hashcash -sv
More accurate but quite slow benchmarking of different processor specific minting cores.

hashcash -s -b default
Print how long it would take the machine to compute a default sized collision (but don't actually compute a collision).

hashcash -s -b 32
Print how long it would take the machine to compute a 32 bit collision (but don't actually compute a collision).

hashcash -m
Mint a stamp. Will prompt for resource name and mint with default value (number of collision bits).

hashcash -m foo
Compute collision on resource foo. Will mint with default value (number of collision bits).

hashcash -m foo -b 10
Compute 10 bit collision on resource foo.

hashcash -a -3d
Subtract a random time of between 0 days and 3 days from the stamp's creation time. This is the same fuzz factor used by mixmaster to reduce risk of timing-correlations.

Examining Stamps

hashcash -w 1:24:040806:foo::511801694b4cd6b0:1e7297a
Report the value of the stamp (how many bits of collision) there are. The example is a 24 bit collision, which takes on average 25 seconds to create on a 3Ghz P4.

hashcash -mq -b 10 foo | hashcash -w
Create a stamp in batch mode, pass to hashcash on stdin to verify, have it print how many bits there were.

hashcash -n 1:24:040806:foo::511801694b4cd6b0:1e7297a
Report the resource name from the stamp. The resource name in the example is foo.

hashcash -l -e 30y 1:24:040806:foo::511801694b4cd6b0:1e7297a
Report how long until the stamp expires if it expires in 30 years from its creation date. (Note dates too far into the future run into the 2038 end of Epoch, which is the unix time analog of the y2k bug).

Verifying Stamps

hashcash -c 1:24:040806:foo::511801694b4cd6b0:1e7297a
Check if the stamp is valid. Note as we are not checking the stamp in a double spend database, and did not specify a resource name or required number of bits of collision and hashcash will consider the stamp not fully checked, and it will report it as valid but not fully unchecked, or as invalid if there is any problem with the stamp.

hashcash -c -b24 1:24:040806:foo::511801694b4cd6b0:1e7297a
Check that the value of the stamp is greater or equal to 24 bits. This example has 24 bit value. If you increase the requested number of bits or replace the stamp with one with less than 24 bit collision the stamp will be rejected.

hashcash -c -b24 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a
As above check if the stamp has sufficient value, but in addition check that the resource name given matches the resource name in the stamp.

Double Spending Prevention

The examples given in Verifying Stamps can be modified to keep a double spend database so that the same stamp will not be accepted twice. Note a stamp will only be checked in and added to the database if it is otherwise valid and fully checked (a required number of bits of collision has been specified and a resource has been specified).

hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a
Check the stamp and add to double spent database if it's valid (has correct resource name and sufficient value).

hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a
Try to double spend the stamp. It will be rejected as double spent.

Stamp Expiry

To prevent the double spend database growing indefinately, the recipient can request that stamps be no older than a specified period. After expiry old stamps can dropped from the double spend database as they will no longer be needed -- expired stamps can be rejected based purely on their old date, so the space taken by expired stamps in the double spend database can be saved without risk of accepting an expired though otherwise valid stamp.

The third field of the stamp is the UTC time since 1st January 1970. The default time format is YYMMDD, time rounded down to the nearest day. The default validity period is 28 days.

You can provide an alternative validity period with the -e option.

hashcash -cd -b 10 -e 2d -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a
Try verifying an old stamp, the above stamp was created 11 Aug 2002.

We gave option -e 2d so the stamps expiry date is 2 days after creation, which is now in the past.

Note: if the creation time is expressed in the stamp in days, the precise creation date is the begining of the specified day in UTC time (similarly for alternate units the creation time is rounded down to the begining of the unit it is expressed in). For units in days, for example, this may mean depending on your time zone that the stamp appears to be considered invalid in under the specified expiry period in days relative to your relative view of what day it is, as the calculation is based on current time in UTC, and the creation time of the stamp is expressed in UTC time.

hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a
Test whether the stamp is otherwise valid, apart from having expired. Omitting the -e tells hashcash that the stamp will never expire. An expiry period of forever can also be given explitly like this: -e 0, where an expiry period of 0 means forever.

Purging old stamps

If the -c, -d options are used together, each time a stamp is checked, if it is valid and all of the mandatory aspects of the stamp are verified (collision bits check, resource name check) then the stamp and its expiry period is written to the database file. The default expiry period if an expiry period is not given explicitly with the -e option is 28 days (ie stamps expire after 4 weeks).

First mint and then add a stamp:

hashcash -m -b 10 foo -e 1m > stamp
Note: we specified an expiry on minting in this example, to ensure that the stamp creation time is given in high enough resolution in the stamp that the stamp will not be considered expired at time of creation. (Recall the default resolution is in days, a stamp created with a creation time rounded down to the beginging of the day is unlikely to be considered valid 1 minute later unless you mint it at midnight UTC time.)

hashcash -cd -e 1m -b 10 -r foo < stamp
The stamp expires in 1 minute. Wait 1 minute and then explicitly request that expired stamps be purged:

hashcash -p now
Then try resubmitting the same stamp:

hashcash -cd -e 1m -b 10 -r foo < stamp
and the stamp will be rejected anyway as it has expired, illustrating why it was not necessary to keep this stamp in the database.

With the default database (the sdb format) the database contents are human readable, so you can view their contents by cating them to the terminal:

cat hashcash.sdb
to see that the stamp really is added and then after puring subsequently purged due to expiry.

Purging old stamps on Demand

As a convenience you can purge at the same time as checking stamps by using the -p option with the -c option.

hashcash -m -b 10 foo > stamp
hashcash -cd -p now -e 1 -b 10 -r foo < stamp
It may be inefficient to purge stamps on every use as the entire database has to be scanned for expired stamps. By giving a time period to the -p option, you can tell hashcash to purge no more frequently than that time period since the previous purge.

For example:

hashcash -cd -p 1d -e 1 -b 10 -r foo < stamp
tells hashcash to purge any expired stamps no more than once per day.

hashcash -p 1M -j foo
tells hashcash to purge only expired stamps matching resource foo once per month.

hashcash -p now -k
tells hashcash to purge all stamps (expired and unexpired) now.


stamp format (version 1)

The current stamp format is version 1. This tool can verify hashcash version 0 stamps also, but version 0 stamps are no longer created as they are being phased out in favor of the more extensible v1 stamp format.

ver:bits:date:resource:[ext]:rand:counter

where

ver = 1
bits = how many bits of partial-collision the stamp is claimed to have
date = YYMMDD[hhmm[ss]]
resource = resource string (eg IP address, email address)
ext = extension -- ignored in the current version
Format of extension:
[name1[=val1[,val2...]];[name2[=val1[,val2...]]...]]
Note the value can also contain =. Example extension (not a real one):
        name1=2,3;name2;name3=var1=2,var2=3,2,val

Which would be extension name1 has values 2 and 3; extension name2 has no values; extension name3 has 3 values ``var1=2'', ``var2=3'', ``2'' and ``val''. The hashcash extension may interpret the values as it sees fit eg ``var1=2'' could be the value of an option to the extension name3.

rand = string of random characters from alphabet a-zA-Z0-9+/= to avoid collisions with other sender's stamps
counter = to find a stamp with the desired number of collision bits need to try lots of different strings this counter is incremented on each try. The Counter is also composed of characters from the alphabet a-zA-Z0-9+/=. (Note an implementation is not required to count sequentially).


FILES

hashcash.sdb
default double spend database


EXIT STATUS

hashcash returns success (exit code 0) after successfully minting a stamp, after fully checking a stamp and finding it valid, and after a timing test.

If when checking a stamp it is found to be invalid (due to being malformed, being expired, having insufficient value, having a date in the future, or being double spent), hashcash returns failure (exit code 1).

If insufficient options are given to fully check a stamp, if the stamp is otherwise valid return unchecked (exit code 2). If the -y flag is given and hashcash would normally return unchecked, exit code success is returned instead.

If any exception occurs (file read failure for database checking or corrupted database contents) an exit status of 3 is returned.


AUTHOR

Written by Adam Back <adam@cypherspace.org>


SEE ALSO

sha1sum(1), sha1-hashcash(1), sha1(1), http://www.hashcash.org/

hashcash-1.21/libfastmint.c0000664000076400007640000004603210352046520014301 0ustar adamadam#if defined(__POWERPC__) && defined(__ALTIVEC__) && (!defined(__GNUC__) || !defined(__MACH__)) #include #endif #ifdef __CARBON__ #include #endif #if defined(__POWERPC__) && !defined(__MACH__) && !defined(__UNIX__) && !defined(__CARBON__) #include #endif #include #include #include #include #include #include #include "random.h" #include "sha1.h" #define BUILD_DLL #include "libfastmint.h" /* Index into array of available minters */ static int fastest_minter = -1; static unsigned int num_minters = 0; #define MAX_MINTERS 20 static HC_Minter minters[MAX_MINTERS]; const char *encodeAlphabets[] = { "0123456789ABCDEF", "0123456789abcdef", "ABCDEFGHIJKLMNOP", "abcdefghijklmnop", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/" }; const int EncodeBitRate[] = { 4, 4, 4, 4, 6 }; /* Keep track of what the CPU supports */ static volatile int gIllegalInstructionTrapped = 0; static jmp_buf gEnv; int gProcessorSupportFlags = 0; /* SHA-1 magic gunge */ #define H0 0x67452301 #define H1 0xEFCDAB89 #define H2 0x98BADCFE #define H3 0x10325476 #define H4 0xC3D2E1F0 static const uInt32 SHA1_IV[ 5 ] = { H0, H1, H2, H3, H4 }; /* SIGILL handler */ static void sig_ill_handler(int sig) { sig = sig; gIllegalInstructionTrapped = 1; longjmp(gEnv,1); } #if defined(__ALTIVEC__) && !defined(__GNUC__) #pragma dont_inline on /* Dummy Altivec operation for testing */ static void dummy_altivec_fn(void) { volatile vector unsigned int v; v = vec_or(v,v); } #pragma dont_inline reset #endif /* Detect whether extended CPU features like Altivec, MMX, etc are supported */ static void hashcash_detect_features(void) { #if defined(__POWERPC__) && defined(__ALTIVEC__) int hasAltivec = 0; #if defined(__UNIX__) || defined(__MACH__) /* Use generic SIGILL trap handler */ void *oldhandler; gIllegalInstructionTrapped = 0; oldhandler = signal(SIGILL, sig_ill_handler); if(!setjmp(gEnv)) { #ifdef __GNUC__ asm volatile ( "vor v0,v0,v0" ); #else dummy_altivec_fn(); #endif } signal(SIGILL, oldhandler); hasAltivec = !gIllegalInstructionTrapped; #else /* Carbon and MacOS Classic */ long cpuAttributes; OSErr err = Gestalt(gestaltPowerPCProcessorFeatures, &cpuAttributes); if(err == 0) hasAltivec = ((1 << gestaltPowerPCHasVectorInstructions) & cpuAttributes) != 0; #endif if(hasAltivec) gProcessorSupportFlags |= HC_CPU_SUPPORTS_ALTIVEC; else gProcessorSupportFlags &= ~(HC_CPU_SUPPORTS_ALTIVEC); #elif defined(__i386__) && defined(__GNUC__) void *oldhandler; int hasMMX = 0; gIllegalInstructionTrapped = 0; oldhandler = signal(SIGILL, sig_ill_handler); if(!setjmp(gEnv)) { asm volatile ( "movl $1, %%eax\n\t" "push %%ebx\n\t" "cpuid\n\t" "andl $0x800000, %%edx\n\t" "pop %%ebx\n\t" : "=d" (hasMMX) : /* no input */ : "eax", "ecx" ); } signal(SIGILL, oldhandler); if(hasMMX && !gIllegalInstructionTrapped) gProcessorSupportFlags |= HC_CPU_SUPPORTS_MMX; else gProcessorSupportFlags &= ~(HC_CPU_SUPPORTS_MMX); #elif defined(__AMD64__) gProcessorSupportFlags = HC_CPU_SUPPORTS_MMX; #else gProcessorSupportFlags = 0; #endif } /* Statically guesstimate the fastest hashcash minting routine. Takes * into account only the gross hardware architecture and features * available. Updates fastest_minter. Also initialises and populates * the available minter array if necessary. */ static const EncodeAlphabet encodings[] = { EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64, EncodeBase64 }; void hashcash_select_minter() { static const HC_Mint_Routine funcs[] = { minter_library, minter_ansi_compact_1, minter_ansi_standard_1, minter_ansi_ultracompact_1, minter_ansi_compact_2, minter_ansi_standard_2, minter_altivec_standard_1, minter_altivec_compact_2, minter_altivec_standard_2, minter_mmx_compact_1, minter_mmx_standard_1, NULL }; static const HC_Mint_Capable_Routine tests[] = { minter_library_test, minter_ansi_compact_1_test, minter_ansi_standard_1_test, minter_ansi_ultracompact_1_test, minter_ansi_compact_2_test, minter_ansi_standard_2_test, minter_altivec_standard_1_test, minter_altivec_compact_2_test, minter_altivec_standard_2_test, minter_mmx_compact_1_test, minter_mmx_standard_1_test, NULL }; static const char *names[] = { #if defined( OPENSSL ) "SHA1 library (openSSL)", #else "SHA1 library (hashcash)", #endif "ANSI Compact 1-pipe", "ANSI Standard 1-pipe", "ANSI Ultra-Compact 1-pipe", "ANSI Compact 2-pipe", "ANSI Standard 2-pipe", "PowerPC Altivec Standard 1x4-pipe", "PowerPC Altivec Compact 2x4-pipe", "PowerPC Altivec Standard 2x4-pipe", "AMD64/x86 MMX Compact 1x2-pipe", "AMD64/x86 MMX Standard 1x2-pipe", NULL }; int i = 0 ; /* Populate array */ if(!num_minters) { num_minters = (sizeof(funcs) / sizeof(*funcs)) - 1; if ( num_minters > MAX_MINTERS ) { fprintf(stderr, "INTERNAL ERROR: " "increase size of MAX_MINTERS\n"); exit(3); /* EXIT_ERROR */ } for(i=0; i < num_minters; i++) { minters[i].name = names[i]; minters[i].func = funcs[i]; minters[i].test = tests[i]; minters[i].encoding = encodings[i]; } } /* If nothing else works, just use the compact_1 minter on x86 and standard_1 elsewhere */ #ifdef __i386__ fastest_minter = 1; #elif defined(__M68000__) fastest_minter = 3; #else fastest_minter = 2; #endif /* Detect actual CPU capabilities */ hashcash_detect_features(); /* See if any of the vectorised minters work, choose the highest-numbered one that does */ for(i=6; i < num_minters; i++) { if(minters[i].test()) { fastest_minter = i; } } } /* Do a quick, silent benchmark of the selected backend. Assumes it * works. */ unsigned long hashcash_per_sec_calc( void ) { static const unsigned int test_bits = 64; static const char *test_string = "1:32:040404:foo@fnord.gov::0123456789abcdef:00000000"; static const int test_tail = 52; unsigned long rate = 0, iter_count = 256; volatile clock_t begin = 0 , end = 0 , tmp = 0 , res = 0 , taken = 0 ; double elapsed = 0 , multiple = 0 ; unsigned char block[SHA1_INPUT_BYTES] = {0}; int gotbits = 0; HC_Mint_Routine best_minter_fp; /* Ensure a valid minter backend is selected */ if(!num_minters) { hashcash_select_minter(); } best_minter_fp = minters[fastest_minter].func; /* Determine clock resolution */ end = clock(); while((begin = clock()) == end) ; while((end = clock()) == begin) ; if ( end < begin ) { tmp = begin; begin = end; end = tmp; } res = end - begin; /* where there is poor resolution use this */ /* less accurate but faster -- otherwise takes 0.5secs */ if ( res > 1000 ) { /* Run minter, with clock running */ begin = end; do { /* set up SHA-1 block */ strncpy((char*)block, test_string, SHA1_INPUT_BYTES); block[test_tail] = 0x80; memset(block+test_tail+1, 0, 59-test_tail); PUT_WORD(block+60, test_tail << 3); best_minter_fp(test_bits, &gotbits, block, SHA1_IV, test_tail, iter_count, NULL, NULL, 0, 0); if ( gotbits >= test_bits) { /* The benchmark will be inaccurate if we actually find a collision! */ fprintf(stderr, "Error in hashcash_quickbench(): found collision while trying to benchmark!\n"); return 1; } rate += iter_count; end = clock(); if ( end < begin ) { taken = begin-end; } else { taken = end-begin; } } while ( taken < 8*res ); multiple = CLOCKS_PER_SEC / (double)(8*res); rate *= multiple; return rate; } /* Run increasing lengths of minting until we have sufficient * elapsed time for precision */ while(iter_count) { /* set up SHA-1 block */ strncpy((char*)block, test_string, SHA1_INPUT_BYTES); block[test_tail] = 0x80; memset(block+test_tail+1, 0, 59-test_tail); PUT_WORD(block+60, test_tail << 3); /* Run minter, with clock running */ end = clock(); while((begin = clock()) == end) {} best_minter_fp(test_bits,&gotbits,block,SHA1_IV,test_tail, iter_count, NULL, NULL, 0, 0); if ( gotbits >= test_bits) { /* The benchmark will be inaccurate if we * actually find a collision! */ fprintf(stderr, "Error in hashcash_quickbench(): found collision while trying to benchmark!\n"); return 1; } end = clock(); if ( end < begin ) { tmp = begin; begin = end; end = tmp; } elapsed = (end-begin) / (double) CLOCKS_PER_SEC; if(end-begin > (res * 16)) break; iter_count <<= 1; } rate = iter_count / elapsed; return rate; } /* version of hashcash_per_sec_calc which caches result, so only doing * the work once. Note: hashcash_use_core will dirty the cache to * trigger a recalc */ static int cached_per_sec = 0; unsigned long hashcash_per_sec(void) { static unsigned long cache = 0; if ( !cached_per_sec ) { cache = hashcash_per_sec_calc(); cached_per_sec = 1; } return cache; } /* Test and benchmark available hashcash minting backends. Returns * the speed of the fastest valid routine, and updates fastest_minter * as appropriate. */ unsigned long hashcash_benchtest(int verbose, int core) { unsigned long i, a, b; int best_minter = -1, got_bits = 0; static const unsigned int test_bits = 22; static const char *test_string = "1:22:040404:foo@bar.net::0123456789abcdef:0000000000"; static const int test_tail = 52; /* must be less than 56 */ static const int bit_stats[] = { 8, 10, 16, 20, 22, 24, 26, 28, 30, 0 }; unsigned char block[SHA1_INPUT_BYTES+1] = {0}; volatile clock_t begin = 0, end = 0, tmp = 0; double elapsed = 0, rate = 0, peak_rate = 0; SHA1_ctx crypter; unsigned char hash[SHA1_DIGEST_BYTES] = {0}; const char *p = NULL , *q = NULL ; int start = 0, stop = 0; /* If minter list isn't valid, make it so */ hashcash_select_minter(); /* print header */ if(verbose > 0 ) { printf(" Rate Name (* machine default)\n"); } if(verbose >= 3) { printf("\n"); } if ( core >= 0 ) { start = core; stop = start+1; } else { start = 0; stop = num_minters; } for(i = start; i < stop; i++) { /* If the minter can't run... */ if(!minters[i].test()) { if(verbose >= 2) { printf(" --- %s (Not available on this machine)\n", minters[i].name); } continue; } if(verbose) { printf(" %s\r", minters[i].name); fflush(stdout); } /* set up SHA-1 block */ strncpy((char*)block, test_string, SHA1_INPUT_BYTES); block[test_tail] = 0x80; memset(block+test_tail+1, 0, 59-test_tail); PUT_WORD(block+60, test_tail << 3); /* Run minter, with clock running */ end = clock(); while((begin = clock()) == end) {} minters[i].func(test_bits, &got_bits, block, SHA1_IV, test_tail, 1 << 30, NULL, NULL, 0, 0); end = clock(); if ( end < begin ) { tmp = begin; begin = end; end = tmp; } elapsed = (end-begin) / (double) CLOCKS_PER_SEC; /* Different minter iteration patterns will find * different solutions */ /* Verify solution correctness first, using reference * SHA-1 library */ SHA1_Init(&crypter); SHA1_Update(&crypter, block, test_tail); SHA1_Final(&crypter, hash); for(a=0; a < SHA1_DIGEST_BYTES-1 && hash[a] == 0; a++) {} for(b=0; b < 8 && (hash[a] & 0x80) == 0; b++) { hash[a] <<= 1; } if(got_bits != (a*8)+b || got_bits < test_bits || block[test_tail] != (unsigned char) 0x80) { if(verbose) { printf("ERROR!\n"); printf(" Wanted %u bits, reported %d bits, got %lu bits.\n", test_bits, got_bits, (a*8)+b); if(block[test_tail] == (unsigned char) 0x80) { printf(" End-of-block marker remains intact.\n"); } else { printf(" End-of-block marker damaged!\n"); } block[test_tail] = 0; printf(" \"%s\"\n", block); printf(" Time taken: %.3f\n\n", elapsed); } continue; } /* Use knowledge of encoding alphabet to calculate iterations taken */ a = test_tail-8; b = 0; p = encodeAlphabets[minters[i].encoding]; while(a < test_tail && block[a] == '0') { a++; } for( ; a < test_tail; a++) { q = strchr(p, block[a]); if(!q) break; b = (b * strlen(p)) + (q - p); } if(a != test_tail) { if(verbose) { printf("ERROR!\n"); printf(" Unable to parse iteration count.\n"); printf(" \"%s\"\n", block); printf(" \"%s\"\n", p); } continue; } /* We know the elapsed time and the iteration count, so calculate the rate */ rate = b / elapsed; if(verbose) { printf("%9lu %s %c\n", (unsigned long) rate, minters[i].name, (i == fastest_minter) ? '*' : ' '); } if(rate > peak_rate) { peak_rate = rate; best_minter = i; } /* Optionally print out the stats */ if(verbose >= 3) { block[test_tail] = 0; printf(" Solution: %s\n", block); printf(" Iterations: %lu\n", b); printf(" Time taken: %.3f\n\n", elapsed); } } fastest_minter = best_minter; if(verbose && best_minter >= 0) { printf("Best minter: %s (%lu hashes/sec)\n", minters[best_minter].name, (unsigned long) peak_rate); } if(verbose >= 2 && best_minter >= 0) { printf("Projected average times to mint:\n"); for(i = 0; bit_stats[i]; i++) { elapsed = (1 << bit_stats[i]) / peak_rate; printf("%3d bits: %9.3f seconds", bit_stats[i], elapsed); if(elapsed > 200000) { printf(" (%.1f days)", elapsed/(3600*24)); } else if(elapsed > 5000) { printf(" (%.1f hours)", elapsed/3600); } else if(elapsed > 100) { printf(" (%.1f minutes)", elapsed/60); } else if(elapsed < 0.005) { printf(" (%.1f microseconds)", elapsed * 1000000); } printf("\n"); } } return (unsigned long) peak_rate; } /* Attempt to mint a hashcash token with a given bit-value. * Will append a random string to token that produces the required * collision, then return a pointer to the resultant string in result. * Caller must free() result buffer after use. * Returns the number of bits actually minted (may be more or less * than requested). */ #define BIG_ITER 0x40000000U /* 2^30 */ double hashcash_fastmint(const int bits, const char *token, int compress, char **result, hashcash_callback cb, void* user_args) { SHA1_ctx crypter; unsigned char hash[SHA1_DIGEST_BYTES] = {0}; unsigned int IV[SHA1_DIGEST_WORDS] = {0}; unsigned char *buffer = NULL, *block = NULL, c = 0; unsigned int buflen = 0, tail = 0, a = 0, b = 0, save_tail = 0; unsigned long t = 0, loop = 0, iters = 0, i = 0, first = 1; HC_Mint_Routine best_minter; double counter = 0, expected = 0; /* this is to allow this fn to call the same callback macro */ MINTER_CALLBACK_VARS; int gotBits = 0, bit_rate = 6, chars = 0, blocks = 1, oldblocks = 0; int* best; /* NB this is needed for CALLBACK macros */ best = &gotBits; /* Make sure list of minters is valid */ if(fastest_minter < 0) { hashcash_select_minter(); } /* only the library minter can cope with split blocks */ if ( compress > 1 ) { fastest_minter = 0; } best_minter = minters[fastest_minter].func; expected = hashcash_expected_tries( bits ); again: /* Set up string for hashing */ tail = strlen(token); buflen = (tail - (tail % SHA1_INPUT_BYTES)) + 2*SHA1_INPUT_BYTES; buffer = malloc(buflen); memset(buffer, 0, buflen); strncpy((char*)buffer, token, buflen); /* Add 96 bits of random data */ t = tail + 16; for( ; tail < t; tail++) { random_getbytes(&c, 1); buffer[tail] = encodeAlphabets[EncodeBase64][c & 0x3f]; } /* Add separator and zeroed count field (for v1 hashcash format) */ buffer[tail++] = ':'; save_tail = tail; bit_rate = EncodeBitRate[encodings[fastest_minter]]; chars = 31/bit_rate; for ( i = compress ? 1 : chars; i <= chars && (first || gotBits < bits); i++ ) { first = 0; tail = save_tail; t = tail + i; for( ; tail < t; tail++) { buffer[tail] = '0'; } switch (compress) { case 0: /* fast stamps */ /* Align to optimal counting positions */ for( ; (tail % SHA1_INPUT_BYTES) != 32 && (tail % SHA1_INPUT_BYTES) != 52; tail++) { buffer[tail] = '0'; } break; case 1: /* produce moderately compact stamps */ /* ensure counting is all within one SHA-1 block */ for( ; (tail % SHA1_INPUT_BYTES) < i || (tail % SHA1_INPUT_BYTES) >= 56; tail++) { buffer[tail] = '0'; } break; default: /* produce very compact stamps */ oldblocks = blocks; if ( (tail % SHA1_INPUT_BYTES) < i || (tail % SHA1_INPUT_BYTES) >= 56 ) { blocks = 2; /* split across block */ } /* no padding at all! */ } /* Hash all but the final block, due to invariance */ t = tail - (tail % SHA1_INPUT_BYTES); if ( blocks > 1 && t >= 64 && (tail % SHA1_INPUT_BYTES) < i ) { t -= 64; } SHA1_Init(&crypter); SHA1_Update(&crypter, buffer, t); #if defined(OPENSSL) IV[0]=crypter.h0; IV[1]=crypter.h1; IV[2]=crypter.h2; IV[3]=crypter.h3; IV[4]=crypter.h4; #else for(a=0; a < 5; a++) { IV[a] = crypter.H[a]; } #endif block = buffer + t; /* Fill in the padding and trailer */ buffer[tail] = 0x80; /* if number of blocks change get rid of old padding */ if ( blocks > oldblocks) { /* note only need 7 chars as 1 char wider */ memset(buffer+tail+1,0,7); } PUT_WORD(block+(blocks>1?64:0)+60, tail << 3); tail -= t; /* Run the minter over the last block */ loop=best_minter(bits, &gotBits, block, IV, tail, 0x1U << (i*bit_rate), cb, user_args,counter,expected); if (loop==0) { free(buffer); if (*best==-1) {return -1;} else { return 0; } } counter += (double)loop; } /* if we succeeded call the callback also */ if ( gotBits >= bits ) { MINTER_CALLBACK(); } block[tail] = 0; /* Verify solution using reference library */ SHA1_Update(&crypter, block, tail); SHA1_Final(&crypter, hash); for(a=0; a < SHA1_DIGEST_BYTES-1 && hash[a] == 0; a++) ; for(b=0; b < 8 && (hash[a] & 0x80) == 0; b++) hash[a] <<= 1; b += a*8; /* The minter appears to be broken! */ if(b < gotBits) { fprintf(stderr, "ERROR: requested %d bits, reported %d bits, got %d bits using %s minter: \"%s\"\n", bits, gotBits, b, minters[fastest_minter].name, buffer); exit(3); } /* The minter might not be able to detect unusually large * (32+) bit counts, so we're allowed to give it another try. */ if(b < bits) { /* fprintf( stderr, "buffer = %s\n", buffer ); fprintf( stderr, "wrapped\n" ); */ free(buffer); goto again; } *result = (char*)buffer; return counter; } int hashcash_core(void) { if (!num_minters) { hashcash_select_minter(); } return fastest_minter; } int hashcash_use_core(int core) { if (!num_minters) { hashcash_select_minter(); } if ( core < 0 || core >= num_minters ) { return -1; } if ( !minters[core].test() ) { return 0; } fastest_minter = core; /* force recalc */ cached_per_sec = 0; return 1; } const char* hashcash_core_name(int core) { if (!num_minters) { hashcash_select_minter(); } if ( core < 0 || core >= num_minters ) { return "undefined core"; } return minters[core].name; } hashcash-1.21/lock.c0000664000076400007640000000070210351101612012700 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include "lock.h" #if defined( _WIN32 ) int lock_write( FILE* f ) { return 1; } int lock_read( FILE* f ) { return 1; } int lock_unlock( FILE* f ) { return 1; } #else int lock_write( FILE* f ) { return flock( fileno(f), LOCK_EX ) == 0; } int lock_read( FILE* f ) { return flock( fileno(f), LOCK_SH ) == 0; } int lock_unlock( FILE* f ) { return flock( fileno(f), LOCK_UN ) == 0; } #endif hashcash-1.21/sha1-hashcash.pod0000664000076400007640000000700310351101612014725 0ustar adamadam=head1 NAME sha1 - Secure Hash Algorithm (version 1) hash function =head1 SYNOPSIS =head2 hash files: B [ I ] =head1 DESCRIPTION This is an implementation of the Secure Hash Algorithm version 1 as defined in US Federal Information Processing Standard "FIPS 180-1". This was shipped for convenience with the hashcash tool, but also functions similarly to the widely distributed md5sum utility but using SHA1 instead of MD5 so you may find other uses for it. (Some have argued that MD5 is too small a hash to use for checking files if the attacker can insert chosen files into your distribution as then a birthday attack becomes possible for the master md5sum with work factor of about 2^64, which is a fairly high cost but not infeasible.) =head1 OTHER IMPLEMENTATIONS This program is fairly compatible with B (sha1(1)) and B (sha1sum(1)) installed on some unix systems and included with hashcash package so that a sha1 implementation is available for testing and scripting if those packages are missing. =head1 USAGE NOTES The sha1 program will hash the files given on the command line, or if no files are given it will hash the input fed to it on standard input. The output format is a list of SHA1 hashes in hex followed by the corresponding filenames, one per line. =head1 EXAMPLES =head2 Hashing files =over 4 =item C Hashes the files listed on the command line and outputs their SHA1 message digests as 40 hexadecimal digits, followed by the filename, one per line. =item C When no files are given, hashes from standard in. The example command hashes the string "abc" from standard input. This string is coincidentally one of the test vectors specified in FIPS 180-1 and should output: I. =item C Equivalent to above. The filename of I<-> means read from standard input. =item C You can also mix filenames and reading from standard input with the I<-> pseudo file. The above command first hashes file C then reads the string abc from standard in. =back =head1 Verifying Hashcash The SHA1 hash function is used by hashcash. You can use this sha1 utility to write shell scripts to verify hashcash tokens without using the hashcash command line tool, or just to verify visually. Say you received the following email: From: Someone To: Adam Back Subject: test hashcash Date: Thu, 15 Aug 2002 11:12:02 +0000 X-Hashcash: 0:030626:adam@cypherspace.org:6470e06d773e05a8 Then the following command would show visually the size of the collision: =over 4 =item C 00000000c70db7389f241b8f441fcf068aead3f0 =back and you can see the leading hex 0s. =head1 LIMITATIONS Doesn't have the check option that md5sum has. Perhaps I'll add that in a future version. Also doesn't have the binary / text distinction that md5sum introduced for DOS/Windows benefit, nor the output convention signifying text (* after hash). Can't say I've ever seen anyone use that feature though. =head1 EXIT STATUS C returns success (exit code 0) normally. If it can't read any of the files you give it on the comamnd line, it instead returns failure (exit code 1). =head1 AUTHOR Written by Adam Back Eadam@cypherspace.orgE =head1 SEE ALSO md5sum(1), sha1(1), sha1sum(1), hashcash(1), http://www.hashcash.org/, http://www.itl.nist.gov/fipspubs/fip180-1.htm hashcash-1.21/fastmint_ansi_compact_2.c0000664000076400007640000002227510351101612016547 0ustar adamadam#include #include "libfastmint.h" int minter_ansi_compact_2_test(void) { /* This minter runs on any hardware */ return 1; } /* Define low-level primitives in terms of operations */ #define S(n,X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) #define XOR(a,b) ( (a) ^ (b) ) #define AND(a,b) ( (a) & (b) ) #define ANDNOT(a,b) ( (a) & ~(b) ) #define OR(a,b) ( (a) | (b) ) #define ADD(a,b) ( (a) + (b) ) #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ /* #define F1( B, C, D, F, G ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) */ #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F2( B, C, D, F, G ) ( \ F = XOR(B,C), \ XOR(F,D) ) */ /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) /* #define F3( B, C, D, F, G ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) */ #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F4(B,C,D,F,G) F2(B,C,D,F,G) */ #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ #define Wf(W,t) ((W)[t] = S(1, (W)[t-16] ^ (W)[t-14] ^ (W)[t-8] ^ (W)[t-3])) /* #define Wf(W,t,F,G) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) */ #define Wfly(W,t) ( (t) < 16 ? (W)[t] : Wf(W,t) ) /* #define ROUND(t,A,B,C,D,E,F,G,Func,K,W) \ E = ADD(E,K); \ F = S(5,A); \ E = ADD(F,E); \ E = ADD((W)[t],E); \ F = Func(B,C,D,F,G); \ E = ADD(F,E); \ B = S(30,B); */ #define ROUND(u,t,A,B,C,D,E,Func,K,W) \ E += S(5,A) + Func(B,C,D) + ((u) ? Wfly(W,t) : (W)[t]) + K; \ B = S(30,B); #define ROUNDn(t,A,B,C,D,E,Func,K,W) \ ROUND(0,t,A##1,B##1,C##1,D##1,E##1,Func,K,W##1); \ ROUND(0,t,A##2,B##2,C##2,D##2,E##2,Func,K,W##2); #define ROUNDu(t,A,B,C,D,E,Func,K,W) \ ROUND(1,t,A##1,B##1,C##1,D##1,E##1,Func,K,W##1); \ ROUND(1,t,A##2,B##2,C##2,D##2,E##2,Func,K,W##2); #define ROUND5n( t, Func, K ) \ ROUNDn( t + 0, A, B, C, D, E, Func, K, W );\ ROUNDn( t + 1, E, A, B, C, D, Func, K, W );\ ROUNDn( t + 2, D, E, A, B, C, Func, K, W );\ ROUNDn( t + 3, C, D, E, A, B, Func, K, W );\ ROUNDn( t + 4, B, C, D, E, A, Func, K, W ); #define ROUND5u( t, Func, K ) \ ROUNDu( t + 0, A, B, C, D, E, Func, K, W );\ ROUNDu( t + 1, E, A, B, C, D, Func, K, W );\ ROUNDu( t + 2, D, E, A, B, C, Func, K, W );\ ROUNDu( t + 3, C, D, E, A, B, Func, K, W );\ ROUNDu( t + 4, B, C, D, E, A, Func, K, W ); unsigned long minter_ansi_compact_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { MINTER_CALLBACK_VARS; unsigned long iters = 0 ; int n = 0, t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; uInt32 A = 0 , B = 0 , *W = 0 ; /*register*/ uInt32 A1 = 0 , B1 = 0 , C1 = 0 , D1 = 0 , E1 = 0 ; /*register*/ uInt32 A2 = 0 , B2 = 0 , C2 = 0 , D2 = 0 , E2 = 0 ; uInt32 W1[80] = {0}; uInt32 W2[80] = {0}; uInt32 H[5] = {0}, pH[5] = {0}; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X1 = (unsigned char*) W1; unsigned char *X2 = (unsigned char*) W2; int addressMask = 0 ; static const int endTest = 3; unsigned char *output = (unsigned char*) block; const int W32[] = {21,23,24,26,27,29,30,31,0}, W52[] = {20,23,26,28,29,31,0}; char wordUpdate[80] = {0}; *best = 0; /* Work out whether we need to swap bytes during encoding */ addressMask = ( *(char*)&endTest ); /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } maxBits = 0; /* Copy block and IV to internal storage */ for(t=0; t < 16; t++) W1[t] = W2[t] = GET_WORD(output + t*4); for(t=0; t < 5; t++) pH[t] = H[t] = IV[t]; /* Precalculate which words need the W buffer updating */ switch(tailIndex) { default: for(t=16; t < 32; t++) wordUpdate[t] = 1; break; case 32: for(t=0; W32[t]; t++) wordUpdate[W32[t]] = 1; break; case 52: for(t=0; W52[t]; t++) wordUpdate[W52[t]] = 1; break; } for(t=32; t < 80; t++) wordUpdate[t] = 1; /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-2; iters += 2) { /* Encode iteration count into tail */ X1[(tailIndex - 1) ^ addressMask] = p[((iters+0) ) & 0x3f]; X2[(tailIndex - 1) ^ addressMask] = p[((iters+1) ) & 0x3f]; if(!(iters & 0x3f)) { X1[(tailIndex - 2) ^ addressMask] = X2[(tailIndex - 2) ^ addressMask] = p[((iters) >> 6) & 0x3f]; X1[(tailIndex - 3) ^ addressMask] = X2[(tailIndex - 3) ^ addressMask] = p[((iters) >> 12) & 0x3f]; X1[(tailIndex - 4) ^ addressMask] = X2[(tailIndex - 4) ^ addressMask] = p[((iters) >> 18) & 0x3f]; X1[(tailIndex - 5) ^ addressMask] = X2[(tailIndex - 5) ^ addressMask] = p[((iters) >> 24) & 0x3f]; X1[(tailIndex - 6) ^ addressMask] = X2[(tailIndex - 6) ^ addressMask] = p[((iters) >> 30) & 0x3f]; } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A1 = H[0]; B1 = H[1]; C1 = H[2]; D1 = H[3]; E1 = H[4]; for(t=16; t < 32; t++) { Wf(W1,t); Wf(W2,t); } ROUND(0, 0, A1, B1, C1, D1, E1, F1, K1, W1 ); ROUND(0, 1, E1, A1, B1, C1, D1, F1, K1, W1 ); ROUND(0, 2, D1, E1, A1, B1, C1, F1, K1, W1 ); ROUND(0, 3, C1, D1, E1, A1, B1, F1, K1, W1 ); ROUND(0, 4, B1, C1, D1, E1, A1, F1, K1, W1 ); ROUND(0, 5, A1, B1, C1, D1, E1, F1, K1, W1 ); ROUND(0, 6, E1, A1, B1, C1, D1, F1, K1, W1 ); if(tailIndex == 52) { ROUND(0, 7, D1, E1, A1, B1, C1, F1, K1, W1 ); ROUND(0, 8, C1, D1, E1, A1, B1, F1, K1, W1 ); ROUND(0, 9, B1, C1, D1, E1, A1, F1, K1, W1 ); ROUND(0,10, A1, B1, C1, D1, E1, F1, K1, W1 ); ROUND(0,11, E1, A1, B1, C1, D1, F1, K1, W1 ); } pH[0] = A1; pH[1] = B1; pH[2] = C1; pH[3] = D1; pH[4] = E1; } /* Fill W buffer */ for(t=16; t < 80; t++) { if(wordUpdate[t]) { Wf(W1,t); Wf(W2,t); } } /* Set up working variables */ A1 = A2 = pH[0]; B1 = B2 = pH[1]; C1 = C2 = pH[2]; D1 = D2 = pH[3]; E1 = E2 = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUNDn( 0, A, B, C, D, E, F1, K1, W ); ROUNDn( 1, E, A, B, C, D, F1, K1, W ); ROUNDn( 2, D, E, A, B, C, F1, K1, W ); ROUNDn( 3, C, D, E, A, B, F1, K1, W ); ROUNDn( 4, B, C, D, E, A, F1, K1, W ); ROUNDn( 5, A, B, C, D, E, F1, K1, W ); ROUNDn( 6, E, A, B, C, D, F1, K1, W ); case 32: ROUNDn( 7, D, E, A, B, C, F1, K1, W ); ROUNDn( 8, C, D, E, A, B, F1, K1, W ); ROUNDn( 9, B, C, D, E, A, F1, K1, W ); ROUNDn(10, A, B, C, D, E, F1, K1, W ); ROUNDn(11, E, A, B, C, D, F1, K1, W ); case 52: ROUNDn(12, D, E, A, B, C, F1, K1, W ); ROUNDn(13, C, D, E, A, B, F1, K1, W ); ROUNDn(14, B, C, D, E, A, F1, K1, W ); } ROUND5n(15, F1, (K1) ); ROUND5n(20, F2, (K2) ); ROUND5n(25, F2, (K2) ); ROUND5n(30, F2, (K2) ); ROUND5n(35, F2, (K2) ); ROUND5n(40, F3, (K3) ); ROUND5n(45, F3, (K3) ); ROUND5n(50, F3, (K3) ); ROUND5n(55, F3, (K3) ); ROUND5n(60, F4, (K4) ); ROUND5n(65, F4, (K4) ); ROUND5n(70, F4, (K4) ); ROUND5n(75, F4, (K4) ); /* Mix in the IV again */ A1 += H[0]; B1 += H[1]; A2 += H[0]; B2 += H[1]; /* Debugging! */ if(0 && iters==0) { for(t=0; t < 80; t++) { X1 = (unsigned char*) (W1+t); X2 = (unsigned char*) (W2+t); printf("%2X %2X %2X %2X | %2X %2X %2X %2X\n", X1 [ 0 ] , X1 [ 1 ] , X1 [ 2 ] , X1 [ 3 ] , X2 [ 0 ] , X2 [ 1 ] , X2 [ 2 ] , X2 [ 3 ] ) ; } X1 = (unsigned char*) W1; X2 = (unsigned char*) W2; } /* Quickly check which pipe contains the best result */ if(A1 < A2) n = 0; else if(A1 > A2) n = 1; else if(B1 < B2) n = 0; else n = 1; switch(n) { case 0: A = A1; B = B1; W = W1; break; case 1: A = A2; B = B2; W = W2; break; } /* Is this the best bit count so far? */ if(!(A & bitMask1Low) && !(B & bitMask1High)) { /* Count bits */ gotBits = 0; if(A) { s = A; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(B) { s = B; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } /* Copy this result back to the block buffer */ for(t=0; t < 16; t++) PUT_WORD(output + t*4, W[t]); /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+2; } } if(0) return 0; MINTER_CALLBACK(); } return iters+2; } hashcash-1.21/fip180-1.txt0000664000076400007640000012060610351101612013520 0ustar adamadam FIPS PUB 180-1 FEDERAL INFORMATION PROCESSING STANDARDS PUBLICATION (Supersedes FIPS PUB 180 - 1993 May 11) 1995 April 17 U.S. DEPARTMENT OF COMMERCE/National Institute of Standards and Technology SECURE HASH STANDARD /*** NOTE: NOT OFFICIAL. HARD COPY IS THE OFFICIAL VERSION. ^ is used for exponentiation or superscript. ***/ CATEGORY: COMPUTER SECURITY U.S. DEPARTMENT OF COMMERCE, Ronald H. Brown, Secretary NATIONAL INSTITUTE OF STANDARDS AND TECHNOLOGY Foreword The Federal Information Processing Standards Publication Series of the National Institute of Standards and Technology (NIST) is the official series of publications relating to standards and guidelines adopted and promulgated under the provisions of Section 111(d) of the Federal Property and Administrative Services Act of 1949 as amended by the Computer Security Act of 1987, Public Law 100-235. These mandates have given the Secretary of Commerce and NIST important responsibilities for improving the utilization and management of computer and related telecommunications systems in the Federal Government. The NIST, through the Computer Systems Laboratory, provides leadership, technical guidance, and coordination of Government efforts in the development of standards and guidelines in these areas. Comments concerning Federal Information Processing Standards Publications are welcomed and should be addressed to the Director, Computer Systems Laboratory, National Institute of Standards and Technology, Gaithersburg, MD 20899. James H. Burrows, Director Computer Systems Laboratory Abstract This standard specifies a Secure Hash Algorithm (SHA-1) which can be used to generate a condensed representation of a message called a message digest. The SHA-1 is required for use with the Digital Signature Algorithm (DSA) as specified in the Digital Signature Standard (DSS) and whenever a secure hash algorithm is required for Federal applications. The SHA-1 is used by both the transmitter and intended receiver of a message in computing and verifying a digital signature. Key words: computer security; digital signatures; Federal Information Processing Standard (FIPS); hash algorithm. FIPS PUB 180-1 Federal Information Processing Standards Publication 180-1 1995 APRIL 17 ANNOUNCING THE SECURE HASH STANDARD Federal Information Processing Standards Publications (FIPS PUBS) are issued by the National Institute of Standards and Technology (NIST) after approval by the Secretary of Commerce pursuant to Section 111(d) of the Federal Property and Administrative Services Act of 1949 as amended by the Computer Security Act of 1987, Public Law 100-235. Name of Standard: Secure Hash Standard. Category of Standard: Computer Security. Explanation: This Standard specifies a Secure Hash Algorithm, SHA-1, for computing a condensed representation of a message or a data file. When a message of any length < 2^64 bits is input, the SHA-1 produces a 160-bit output called a message digest. The message digest can then be input to the Digital Signature Algorithm (DSA) which generates or verifies the signature for the message. Signing the message digest rather than the message often improves the efficiency of the process because the message digest is usually much smaller in size than the message. The same hash algorithm must be used by the verifier of a digital signature as was used by the creator of the digital signature. The SHA-1 is called secure because it is computationally infeasible to find a message which corresponds to a given message digest, or to find two different messages which produce the same message digest. Any change to a message in transit will, with very high probability, result in a different message digest, and the signature will fail to verify. SHA-1 is a technical revision of SHA (FIPS 180). A circular left shift operation has been added to the specifications in section 7, line b, page 9 of FIPS 180 and its equivalent in section 8, line c, page 10 of FIPS 180. This revision improves the security provided by this standard. The SHA-1 is based on principles similar to those used by Professor Ronald L. Rivest of MIT when designing the MD4 message digest algorithm ("The MD4 Message Digest Algorithm," Advances in Cryptology - CRYPTO '90 Proceedings, Springer-Verlag, 1991, pp. 303-311), and is closely modelled after that algorithm. Approving Authority: Secretary of Commerce. Maintenance Agency: U.S. Department of Commerce, National Institute of Standards and Technology, Computer Systems Laboratory. Applicability: This standard is applicable to all Federal departments and agencies for the protection of unclassified information that is not subject to section 2315 of Title 10, United States Code, or section 3502(2) of Title 44, United States Code. This standard is required for use with the Digital Signature Algorithm (DSA) as specified in the Digital Signature Standard (DSS) and whenever a secure hash algorithm is required for Federal applica- tions. Private and commercial organizations are encouraged to adopt and use this standard. Applications: The SHA-1 may be used with the DSA in electronic mail, electronic funds transfer, software distribution, data storage, and other applications which require data integrity assurance and data origin authentication. The SHA-1 may also be used whenever it is necessary to generate a condensed version of a message. Implementations: The SHA-1 may be implemented in software, firmware, hardware, or any combination thereof. Only implementations of the SHA-1 that are validated by NIST will be considered as complying with this standard. Information about the requirements for validating implementations of this standard can be obtained from the National Institute of Standards and Technology, Computer Systems Laboratory, Attn: SHS Validation, Gaithersburg, MD 20899. Export Control: Implementations of this standard are subject to Federal Government export controls as specified in Title 15, Code of Federal Regulations, Parts 768 through 799. Exporters are advised to contact the Department of Commerce, Bureau of Export Administration for more information. Patents: Implementations of the SHA-1 in this standard may be covered by U.S. and foreign patents. Implementation Schedule: This standard becomes effective October 2, 1995. Specifications: Federal Information Processing Standard (FIPS 180-1) Secure Hash Standard (affixed). Cross Index: a. FIPS PUB 46-2, Data Encryption Standard. b. FIPS PUB 73, Guidelines for Security of Computer Applications. c. FIPS PUB 140-1, Security Requirements for Cryptographic Modules. d. FIPS PUB 186, Digital Signature Standard. e. Federal Informations Resources Management Regulations (FIRMR) subpart 201.20.303, Standards, and subpart 201.39.1002, Federal Standards. Objectives: The objectives of this standard are to: a. Specify the secure hash algorithm required for use with the Digital Signature Standard (FIPS 186) in the generation and verification of digital signatures; b. Specify the secure hash algorithm to be used whenever a secure hash algorithm is required for Federal applications; and c. Encourage the adoption and use of the specified secure hash algorithm by private and commercial organizations. Qualifications: While it is the intent of this standard to specify a secure hash algorithm, conformance to this standard does not assure that a particular implementation is secure. The responsible authority in each agency or department shall assure that an overall implementation provides an acceptable level of security. This standard will be reviewed every five years in order to assess its adequacy. Waiver Procedure: Under certain exceptional circumstances, the heads of Federal departments and agencies may approve waivers to Federal Information Processing Standards (FIPS). The head of such agency may redelegate such authority only to a senior official designated pursuant to section 3506(b) of Title 44, United States Code. Waiver shall be granted only when: a. Compliance with a standard would adversely affect the accomplishment of the mission of an operator of a Federal computer system; or b. Compliance with a standard would cause a major adverse financial impact on the operator which is not offset by Government-wide savings. Agency heads may act upon a written waiver request containing the information detailed above. Agency heads may also act without a written waiver request when they determine that conditions for meeting the standard cannot be met. Agency heads may approve waivers only by a written decision which explains the basis on which the agency head made the required finding(s). A copy of each decision, with procurement sensitive or classified portions clearly identified, shall be sent to: National Institute of Standards and Technology; ATTN: FIPS Waiver Decisions, Technology Building, Room B-154, Gaithersburg, MD 20899. In addition, notice of each waiver granted and each delegation of authority to approve waivers shall be sent promptly to the Committee on Government Operations of the House of Representatives and the Committee on Government Affairs of the Senate and shall be published promptly in the Federal Register. When the determination on a waiver applies to the procurement of equipment and/or services, a notice of the waiver determination must be published in the Commerce Business Daily as a part of the notice of solicitation for offers of an acquisition or, if the waiver determination is made after that notice is published, by amendment to such notice. A copy of the waiver, any supporting documents, the document approving the waiver and any accompanying documents, with such deletions as the agency is authorized and decides to make under 5 United States Code Section 552(b), shall be part of the procurement documentation and retained by the agency. Where to Obtain Copies of the Standard: Copies of this publication are for sale by the National Technical Information Service, U.S. Department of Commerce, Springfield, VA 22161. When ordering, refer to Federal Information Processing Standards Publication 180-1 (FIPSPUB180-1), and identify the title. When microfiche is desired, this should be specified. Prices are published by NTIS in current catalogs and other issuances. Payment may be made by check, money order, deposit account or charged to a credit card accepted by NTIS. --------------------- Federal Information Processing Standards Publication 180-1 1995 April 17 Specifications for the SECURE HASH STANDARD 1. INTRODUCTION The Secure Hash Algorithm (SHA-1) is required for use with the Digital Signature Algorithm (DSA) as specified in the Digital Signature Standard (DSS) and whenever a secure hash algorithm is required for federal applica- tions. For a message of length < 2^64 bits, the SHA-1 produces a 160-bit condensed representation of the message called a message digest. The message digest is used during generation of a signature for the message. The SHA-1 is also used to compute a message digest for the received version of the message during the process of verifying the signature. Any change to the message in transit will, with very high probability, result in a different message digest, and the signature will fail to verify. The SHA-1 is designed to have the following properties: it is computationally infeasible to find a message which corresponds to a given message digest, or to find two different messages which produce the same message digest. 2. BIT STRINGS AND INTEGERS The following terminology related to bit strings and integers will be used: a. A hex digit is an element of the set {0, 1, ... , 9, A, ... , F}. A hex digit is the representation of a 4-bit string. Examples: 7 = 0111, A = 1010. b. A word equals a 32-bit string which may be represented as a sequence of 8 hex digits. To convert a word to 8 hex digits each 4-bit string is converted to its hex equivalent as described in (a) above. Example: 1010 0001 0000 0011 1111 1110 0010 0011 = A103FE23. c. An integer between 0 and 2^32 - 1 inclusive may be represented as a word. The least significant four bits of the integer are represented by the right-most hex digit of the word representation. Example: the integer 291 = 2^8+2^5+2^1+2^0 = 256+32+2+1 is represented by the hex word, 00000123. If z is an integer, 0 <= z < 2^64, then z = (2^32)x + y where 0 <= x < 2^32 and 0 <= y < 2^32. Since x and y can be represented as words X and Y, respectively, z can be represented as the pair of words (X,Y). d. block = 512-bit string. A block (e.g., B) may be represented as a sequence of 16 words. 3. OPERATIONS ON WORDS The following logical operators will be applied to words: a. Bitwise logical word operations X AND Y = bitwise logical "and" of X and Y. X OR Y = bitwise logical "inclusive-or" of X and Y. X XOR Y = bitwise logical "exclusive-or" of X and Y. NOT X = bitwise logical "complement" of X. Example: 01101100101110011101001001111011 XOR 01100101110000010110100110110111 -------------------------------- = 00001001011110001011101111001100 b. The operation X + Y is defined as follows: words X and Y represent integers x and y, where 0 <= x < 2^32 and 0 <= y < 2^32. For positive integers n and m, let n mod m be the remainder upon dividing n by m. Compute z = (x + y) mod 2^32. Then 0 <= z < 2^32. Convert z to a word, Z, and define Z = X + Y. c. The circular left shift operation S^n(X), where X is a word and n is an integer with 0 <= n < 32, is defined by S^n(X) = (X << n) OR (X >> 32-n). In the above, X << n is obtained as follows: discard the left-most n bits of X and then pad the result with n zeroes on the right (the result will still be 32 bits). X >> n is obtained by discarding the right-most n bits of X and then padding the result with n zeroes on the left. Thus S^n(X) is equivalent to a circular shift of X by n positions to the left. 4. MESSAGE PADDING The SHA-1 is used to compute a message digest for a message or data file that is provided as input. The message or data file should be considered to be a bit string. The length of the message is the number of bits in the message (the empty message has length 0). If the number of bits in a message is a multiple of 8, for compactness we can represent the message in hex. The purpose of message padding is to make the total length of a padded message a multiple of 512. The SHA-1 sequentially processes blocks of 512 bits when computing the message digest. The following specifies how this padding shall be performed. As a summary, a "1" followed by m "0"s followed by a 64-bit integer are appended to the end of the message to produce a padded message of length 512 * n. The 64-bit integer is l, the length of the original message. The padded message is then processed by the SHA-1 as n 512-bit blocks. Suppose a message has length l < 2^64. Before it is input to the SHA-1, the message is padded on the right as follows: a. "1" is appended. Example: if the original message is "01010000", this is padded to "010100001". b. "0"s are appended. The number of "0"s will depend on the original length of the message. The last 64 bits of the last 512-bit block are reserved for the length l of the original message. Example: Suppose the original message is the bit string 01100001 01100010 01100011 01100100 01100101. After step (a) this gives 01100001 01100010 01100011 01100100 01100101 1. Since l = 40, the number of bits in the above is 41 and 407 "0"s are appended, making the total now 448. This gives (in hex) 61626364 65800000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000. c. Obtain the 2-word representation of l, the number of bits in the original message. If l < 2^32 then the first word is all zeroes. Append these two words to the padded message. Example: Suppose the original message is as in (b). Then l = 40 (note that l is computed before any padding). The two-word representation of 40 is hex 00000000 00000028. Hence the final padded message is hex 61626364 65800000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000028. The padded message will contain 16 * n words for some n > 0. The padded message is regarded as a sequence of n blocks M(1) , M(2), ... , M(n), where each M(i) contains 16 words and M(1) contains the first characters (or bits) of the message. 5. FUNCTIONS USED A sequence of logical functions f(0), f(1),..., f(79) is used in the SHA-1. Each f(t), 0 <= t <= 79, operates on three 32-bit words B, C, D and produces a 32-bit word as output. f(t;B,C,D) is defined as follows: for words B, C, D, f(t;B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19) f(t;B,C,D) = B XOR C XOR D (20 <= t <= 39) f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59) f(t;B,C,D) = B XOR C XOR D (60 <= t <= 79). 6. CONSTANTS USED A sequence of constant words K(0), K(1), ... , K(79) is used in the SHA-1. In hex these are given by K(t) = 5A827999 ( 0 <= t <= 19) K(t) = 6ED9EBA1 (20 <= t <= 39) K(t) = 8F1BBCDC (40 <= t <= 59) K(t) = CA62C1D6 (60 <= t <= 79). 7. COMPUTING THE MESSAGE DIGEST The message digest is computed using the final padded message. The computation uses two buffers, each consisting of five 32-bit words, and a sequence of eighty 32-bit words. The words of the first 5-word buffer are labeled A,B,C,D,E. The words of the second 5-word buffer are labeled H0, H1, H2, H3, H4. The words of the 80-word sequence are labeled W(0), W(1),..., W(79). A single word buffer TEMP is also employed. To generate the message digest, the 16-word blocks M(1), M(2),..., M(n) defined in Section 4 are processed in order. The processing of each M(i) involves 80 steps. Before processing any blocks, the H's are initialized as follows: in hex, H0 = 67452301 H1 = EFCDAB89 H2 = 98BADCFE H3 = 10325476 H4 = C3D2E1F0. Now M(1), M(2), ... , M(n) are processed. To process M(i), we proceed as follows: a. Divide M(i) into 16 words W(0), W(1), ... , W(15), where W(0) is the left-most word. b. For t = 16 to 79 let W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16)). c. Let A = H0, B = H1, C = H2, D = H3, E = H4. d. For t = 0 to 79 do TEMP = S^5(A) + f(t;B,C,D) + E + W(t) + K(t); E = D; D = C; C = S^30(B); B = A; A = TEMP; e. Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. After processing M(n), the message digest is the 160-bit string represented by the 5 words H0 H1 H2 H3 H4. 8. ALTERNATE METHOD OF COMPUTATION The above assumes that the sequence W(0), ... , W(79) is implemented as an array of eighty 32-bit words. This is efficient from the standpoint of minimization of execution time, since the addresses of W(t-3), ... ,W(t-16) in step (b) are easily computed. If space is at a premium, an alternative is to regard { W(t) } as a circular queue, which may be implemented using an array of sixteen 32-bit words W[0], ... W[15]. In this case, in hex let MASK = 0000000F. Then processing of M(i) is as follows: a. Divide M(i) into 16 words W[0], ... , W[15], where W[0] is the left-most word. b. Let A = H0, B = H1, C = H2, D = H3, E = H4. c. For t = 0 to 79 do s = t AND MASK; if (t >= 16) W[s] = S^1(W[(s + 13) AND MASK] XOR W[(s + 8) AND MASK] XOR W[(s + 2) AND MASK] XOR W[s]); TEMP = S^5(A) + f(t;B,C,D) + E + W[s] + K(t); E = D; D = C; C = S^30(B); B = A; A = TEMP; d. Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. 9. COMPARISON OF METHODS The methods of Sections 7 and 8 yield the same message digest. Although using the method of Section 8 saves sixty-four 32-bit words of storage, it is likely to lengthen execution time due to the increased complexity of the address computations for the { W[t] } in step (c). Other computation methods which give identical results may be implemented in conformance with the standard. APPENDIX A. A SAMPLE MESSAGE AND ITS MESSAGE DIGEST This appendix is for informational purposes only and is not required to meet the standard. Let the message be the ASCII binary-coded form of "abc", i.e., 01100001 01100010 01100011. This message has length l = 24. In step (a) of Section 4, we append "1". In step (b) we append 423 "0"s. In step (c) we append hex 00000000 00000018, the 2-word representation of 24. Thus the final padded message consists of one block, so that n = 1 in the notation of Section 4. The initial hex values of {Hi} are H0 = 67452301 H1 = EFCDAB89 H2 = 98BADCFE H3 = 10325476 H4 = C3D2E1F0. Start processing block 1. The words of block 1 are W[0] = 61626380 W[1] = 00000000 W[2] = 00000000 W[3] = 00000000 W[4] = 00000000 W[5] = 00000000 W[6] = 00000000 W[7] = 00000000 W[8] = 00000000 W[9] = 00000000 W[10] = 00000000 W[11] = 00000000 W[12] = 00000000 W[13] = 00000000 W[14] = 00000000 W[15] = 00000018. The hex values of A,B,C,D,E after pass t of the "for t = 0 to 79" loop (step (d) of Section 7 or step (c) of Section 8) are A B C D E t = 0: 0116FC33 67452301 7BF36AE2 98BADCFE 10325476 t = 1: 8990536D 0116FC33 59D148C0 7BF36AE2 98BADCFE t = 2: A1390F08 8990536D C045BF0C 59D148C0 7BF36AE2 t = 3: CDD8E11B A1390F08 626414DB C045BF0C 59D148C0 t = 4: CFD499DE CDD8E11B 284E43C2 626414DB C045BF0C t = 5: 3FC7CA40 CFD499DE F3763846 284E43C2 626414DB t = 6: 993E30C1 3FC7CA40 B3F52677 F3763846 284E43C2 t = 7: 9E8C07D4 993E30C1 0FF1F290 B3F52677 F3763846 t = 8: 4B6AE328 9E8C07D4 664F8C30 0FF1F290 B3F52677 t = 9: 8351F929 4B6AE328 27A301F5 664F8C30 0FF1F290 t = 10: FBDA9E89 8351F929 12DAB8CA 27A301F5 664F8C30 t = 11: 63188FE4 FBDA9E89 60D47E4A 12DAB8CA 27A301F5 t = 12: 4607B664 63188FE4 7EF6A7A2 60D47E4A 12DAB8CA t = 13: 9128F695 4607B664 18C623F9 7EF6A7A2 60D47E4A t = 14: 196BEE77 9128F695 1181ED99 18C623F9 7EF6A7A2 t = 15: 20BDD62F 196BEE77 644A3DA5 1181ED99 18C623F9 t = 16: 4E925823 20BDD62F C65AFB9D 644A3DA5 1181ED99 t = 17: 82AA6728 4E925823 C82F758B C65AFB9D 644A3DA5 t = 18: DC64901D 82AA6728 D3A49608 C82F758B C65AFB9D t = 19: FD9E1D7D DC64901D 20AA99CA D3A49608 C82F758B t = 20: 1A37B0CA FD9E1D7D 77192407 20AA99CA D3A49608 t = 21: 33A23BFC 1A37B0CA 7F67875F 77192407 20AA99CA t = 22: 21283486 33A23BFC 868DEC32 7F67875F 77192407 t = 23: D541F12D 21283486 0CE88EFF 868DEC32 7F67875F t = 24: C7567DC6 D541F12D 884A0D21 0CE88EFF 868DEC32 t = 25: 48413BA4 C7567DC6 75507C4B 884A0D21 0CE88EFF t = 26: BE35FBD5 48413BA4 B1D59F71 75507C4B 884A0D21 t = 27: 4AA84D97 BE35FBD5 12104EE9 B1D59F71 75507C4B t = 28: 8370B52E 4AA84D97 6F8D7EF5 12104EE9 B1D59F71 t = 29: C5FBAF5D 8370B52E D2AA1365 6F8D7EF5 12104EE9 t = 30: 1267B407 C5FBAF5D A0DC2D4B D2AA1365 6F8D7EF5 t = 31: 3B845D33 1267B407 717EEBD7 A0DC2D4B D2AA1365 t = 32: 046FAA0A 3B845D33 C499ED01 717EEBD7 A0DC2D4B t = 33: 2C0EBC11 046FAA0A CEE1174C C499ED01 717EEBD7 t = 34: 21796AD4 2C0EBC11 811BEA82 CEE1174C C499ED01 t = 35: DCBBB0CB 21796AD4 4B03AF04 811BEA82 CEE1174C t = 36: 0F511FD8 DCBBB0CB 085E5AB5 4B03AF04 811BEA82 t = 37: DC63973F 0F511FD8 F72EEC32 085E5AB5 4B03AF04 t = 38: 4C986405 DC63973F 03D447F6 F72EEC32 085E5AB5 t = 39: 32DE1CBA 4C986405 F718E5CF 03D447F6 F72EEC32 t = 40: FC87DEDF 32DE1CBA 53261901 F718E5CF 03D447F6 t = 41: 970A0D5C FC87DEDF 8CB7872E 53261901 F718E5CF t = 42: 7F193DC5 970A0D5C FF21F7B7 8CB7872E 53261901 t = 43: EE1B1AAF 7F193DC5 25C28357 FF21F7B7 8CB7872E t = 44: 40F28E09 EE1B1AAF 5FC64F71 25C28357 FF21F7B7 t = 45: 1C51E1F2 40F28E09 FB86C6AB 5FC64F71 25C28357 t = 46: A01B846C 1C51E1F2 503CA382 FB86C6AB 5FC64F71 t = 47: BEAD02CA A01B846C 8714787C 503CA382 FB86C6AB t = 48: BAF39337 BEAD02CA 2806E11B 8714787C 503CA382 t = 49: 120731C5 BAF39337 AFAB40B2 2806E11B 8714787C t = 50: 641DB2CE 120731C5 EEBCE4CD AFAB40B2 2806E11B t = 51: 3847AD66 641DB2CE 4481CC71 EEBCE4CD AFAB40B2 t = 52: E490436D 3847AD66 99076CB3 4481CC71 EEBCE4CD t = 53: 27E9F1D8 E490436D 8E11EB59 99076CB3 4481CC71 t = 54: 7B71F76D 27E9F1D8 792410DB 8E11EB59 99076CB3 t = 55: 5E6456AF 7B71F76D 09FA7C76 792410DB 8E11EB59 t = 56: C846093F 5E6456AF 5EDC7DDB 09FA7C76 792410DB t = 57: D262FF50 C846093F D79915AB 5EDC7DDB 09FA7C76 t = 58: 09D785FD D262FF50 F211824F D79915AB 5EDC7DDB t = 59: 3F52DE5A 09D785FD 3498BFD4 F211824F D79915AB t = 60: D756C147 3F52DE5A 4275E17F 3498BFD4 F211824F t = 61: 548C9CB2 D756C147 8FD4B796 4275E17F 3498BFD4 t = 62: B66C020B 548C9CB2 F5D5B051 8FD4B796 4275E17F t = 63: 6B61C9E1 B66C020B 9523272C F5D5B051 8FD4B796 t = 64: 19DFA7AC 6B61C9E1 ED9B0082 9523272C F5D5B051 t = 65: 101655F9 19DFA7AC 5AD87278 ED9B0082 9523272C t = 66: 0C3DF2B4 101655F9 0677E9EB 5AD87278 ED9B0082 t = 67: 78DD4D2B 0C3DF2B4 4405957E 0677E9EB 5AD87278 t = 68: 497093C0 78DD4D2B 030F7CAD 4405957E 0677E9EB t = 69: 3F2588C2 497093C0 DE37534A 030F7CAD 4405957E t = 70: C199F8C7 3F2588C2 125C24F0 DE37534A 030F7CAD t = 71: 39859DE7 C199F8C7 8FC96230 125C24F0 DE37534A t = 72: EDB42DE4 39859DE7 F0667E31 8FC96230 125C24F0 t = 73: 11793F6F EDB42DE4 CE616779 F0667E31 8FC96230 t = 74: 5EE76897 11793F6F 3B6D0B79 CE616779 F0667E31 t = 75: 63F7DAB7 5EE76897 C45E4FDB 3B6D0B79 CE616779 t = 76: A079B7D9 63F7DAB7 D7B9DA25 C45E4FDB 3B6D0B79 t = 77: 860D21CC A079B7D9 D8FDF6AD D7B9DA25 C45E4FDB t = 78: 5738D5E1 860D21CC 681E6DF6 D8FDF6AD D7B9DA25 t = 79: 42541B35 5738D5E1 21834873 681E6DF6 D8FDF6AD. Block 1 has been processed. The values of {Hi} are H0 = 67452301 + 42541B35 = A9993E36 H1 = EFCDAB89 + 5738D5E1 = 4706816A H2 = 98BADCFE + 21834873 = BA3E2571 H3 = 10325476 + 681E6DF6 = 7850C26C H4 = C3D2E1F0 + D8FDF6AD = 9CD0D89D. Message digest = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D APPENDIX B. A SECOND SAMPLE MESSAGE AND ITS MESSAGE DIGEST This appendix is for informational purposes only and is not required to meet the standard. Let the message be the binary-coded form (cf. Appendix A) of the ASCII string "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq". Since each of the 56 characters is converted to 8 bits, the length of the message is l = 448. In step (a) of Section 4, we append "1". In step (b) we append 511 "0"s. In step (c) we append the 2-word representation of 448, i.e., hex 00000000 000001C0. This gives n = 2. The initial hex values of {Hi} are H0 = 67452301 H1 = EFCDAB89 H2 = 98BADCFE H3 = 10325476 H4 = C3D2E1F0. Start processing block 1. The words of block 1 are W[0] = 61626364 W[1] = 62636465 W[2] = 63646566 W[3] = 64656667 W[4] = 65666768 W[5] = 66676869 W[6] = 6768696A W[7] = 68696A6B W[8] = 696A6B6C W[9] = 6A6B6C6D W[10] = 6B6C6D6E W[11] = 6C6D6E6F W[12] = 6D6E6F70 W[13] = 6E6F7071 W[14] = 80000000 W[15] = 00000000. The hex values of A,B,C,D,E after pass t of the "for t = 0 to 79" loop (step (d) of Section 7 or step (c) of Section 8) are A B C D E t = 0: 0116FC17 67452301 7BF36AE2 98BADCFE 10325476 t = 1: EBF3B452 0116FC17 59D148C0 7BF36AE2 98BADCFE t = 2: 5109913A EBF3B452 C045BF05 59D148C0 7BF36AE2 t = 3: 2C4F6EAC 5109913A BAFCED14 C045BF05 59D148C0 t = 4: 33F4AE5B 2C4F6EAC 9442644E BAFCED14 C045BF05 t = 5: 96B85189 33F4AE5B 0B13DBAB 9442644E BAFCED14 t = 6: DB04CB58 96B85189 CCFD2B96 0B13DBAB 9442644E t = 7: 45833F0F DB04CB58 65AE1462 CCFD2B96 0B13DBAB t = 8: C565C35E 45833F0F 36C132D6 65AE1462 CCFD2B96 t = 9: 6350AFDA C565C35E D160CFC3 36C132D6 65AE1462 t = 10: 8993EA77 6350AFDA B15970D7 D160CFC3 36C132D6 t = 11: E19ECAA2 8993EA77 98D42BF6 B15970D7 D160CFC3 t = 12: 8603481E E19ECAA2 E264FA9D 98D42BF6 B15970D7 t = 13: 32F94A85 8603481E B867B2A8 E264FA9D 98D42BF6 t = 14: B2E7A8BE 32F94A85 A180D207 B867B2A8 E264FA9D t = 15: 42637E39 B2E7A8BE 4CBE52A1 A180D207 B867B2A8 t = 16: 6B068048 42637E39 ACB9EA2F 4CBE52A1 A180D207 t = 17: 426B9C35 6B068048 5098DF8E ACB9EA2F 4CBE52A1 t = 18: 944B1BD1 426B9C35 1AC1A012 5098DF8E ACB9EA2F t = 19: 6C445652 944B1BD1 509AE70D 1AC1A012 5098DF8E t = 20: 95836DA5 6C445652 6512C6F4 509AE70D 1AC1A012 t = 21: 09511177 95836DA5 9B111594 6512C6F4 509AE70D t = 22: E2B92DC4 09511177 6560DB69 9B111594 6512C6F4 t = 23: FD224575 E2B92DC4 C254445D 6560DB69 9B111594 t = 24: EEB82D9A FD224575 38AE4B71 C254445D 6560DB69 t = 25: 5A142C1A EEB82D9A 7F48915D 38AE4B71 C254445D t = 26: 2972F7C7 5A142C1A BBAE0B66 7F48915D 38AE4B71 t = 27: D526A644 2972F7C7 96850B06 BBAE0B66 7F48915D t = 28: E1122421 D526A644 CA5CBDF1 96850B06 BBAE0B66 t = 29: 05B457B2 E1122421 3549A991 CA5CBDF1 96850B06 t = 30: A9C84BEC 05B457B2 78448908 3549A991 CA5CBDF1 t = 31: 52E31F60 A9C84BEC 816D15EC 78448908 3549A991 t = 32: 5AF3242C 52E31F60 2A7212FB 816D15EC 78448908 t = 33: 31C756A9 5AF3242C 14B8C7D8 2A7212FB 816D15EC t = 34: E9AC987C 31C756A9 16BCC90B 14B8C7D8 2A7212FB t = 35: AB7C32EE E9AC987C 4C71D5AA 16BCC90B 14B8C7D8 t = 36: 5933FC99 AB7C32EE 3A6B261F 4C71D5AA 16BCC90B t = 37: 43F87AE9 5933FC99 AADF0CBB 3A6B261F 4C71D5AA t = 38: 24957F22 43F87AE9 564CFF26 AADF0CBB 3A6B261F t = 39: ADEB7478 24957F22 50FE1EBA 564CFF26 AADF0CBB t = 40: D70E5010 ADEB7478 89255FC8 50FE1EBA 564CFF26 t = 41: 79BCFB08 D70E5010 2B7ADD1E 89255FC8 50FE1EBA t = 42: F9BCB8DE 79BCFB08 35C39404 2B7ADD1E 89255FC8 t = 43: 633E9561 F9BCB8DE 1E6F3EC2 35C39404 2B7ADD1E t = 44: 98C1EA64 633E9561 BE6F2E37 1E6F3EC2 35C39404 t = 45: C6EA241E 98C1EA64 58CFA558 BE6F2E37 1E6F3EC2 t = 46: A2AD4F02 C6EA241E 26307A99 58CFA558 BE6F2E37 t = 47: C8A69090 A2AD4F02 B1BA8907 26307A99 58CFA558 t = 48: 88341600 C8A69090 A8AB53C0 B1BA8907 26307A99 t = 49: 7E846F58 88341600 3229A424 A8AB53C0 B1BA8907 t = 50: 86E358BA 7E846F58 220D0580 3229A424 A8AB53C0 t = 51: 8D2E76C8 86E358BA 1FA11BD6 220D0580 3229A424 t = 52: CE892E10 8D2E76C8 A1B8D62E 1FA11BD6 220D0580 t = 53: EDEA95B1 CE892E10 234B9DB2 A1B8D62E 1FA11BD6 t = 54: 36D1230A EDEA95B1 33A24B84 234B9DB2 A1B8D62E t = 55: 776C3910 36D1230A 7B7AA56C 33A24B84 234B9DB2 t = 56: A681B723 776C3910 8DB448C2 7B7AA56C 33A24B84 t = 57: AC0A794F A681B723 1DDB0E44 8DB448C2 7B7AA56C t = 58: F03D3782 AC0A794F E9A06DC8 1DDB0E44 8DB448C2 t = 59: 9EF775C3 F03D3782 EB029E53 E9A06DC8 1DDB0E44 t = 60: 36254B13 9EF775C3 BC0F4DE0 EB029E53 E9A06DC8 t = 61: 4080D4DC 36254B13 E7BDDD70 BC0F4DE0 EB029E53 t = 62: 2BFAF7A8 4080D4DC CD8952C4 E7BDDD70 BC0F4DE0 t = 63: 513F9CA0 2BFAF7A8 10203537 CD8952C4 E7BDDD70 t = 64: E5895C81 513F9CA0 0AFEBDEA 10203537 CD8952C4 t = 65: 1037D2D5 E5895C81 144FE728 0AFEBDEA 10203537 t = 66: 14A82DA9 1037D2D5 79625720 144FE728 0AFEBDEA t = 67: 6D17C9FD 14A82DA9 440DF4B5 79625720 144FE728 t = 68: 2C7B07BD 6D17C9FD 452A0B6A 440DF4B5 79625720 t = 69: FDF6EFFF 2C7B07BD 5B45F27F 452A0B6A 440DF4B5 t = 70: 112B96E3 FDF6EFFF 4B1EC1EF 5B45F27F 452A0B6A t = 71: 84065712 112B96E3 FF7DBBFF 4B1EC1EF 5B45F27F t = 72: AB89FB71 84065712 C44AE5B8 FF7DBBFF 4B1EC1EF t = 73: C5210E35 AB89FB71 A10195C4 C44AE5B8 FF7DBBFF t = 74: 352D9F4B C5210E35 6AE27EDC A10195C4 C44AE5B8 t = 75: 1A0E0E0A 352D9F4B 7148438D 6AE27EDC A10195C4 t = 76: D0D47349 1A0E0E0A CD4B67D2 7148438D 6AE27EDC t = 77: AD38620D D0D47349 86838382 CD4B67D2 7148438D t = 78: D3AD7C25 AD38620D 74351CD2 86838382 CD4B67D2 t = 79: 8CE34517 D3AD7C25 6B4E1883 74351CD2 86838382. Block 1 has been processed. The values of {Hi} are H0 = 67452301 + 8CE34517 = F4286818 H1 = EFCDAB89 + D3AD7C25 = C37B27AE H2 = 98BADCFE + 6B4E1883 = 0408F581 H3 = 10325476 + 74351CD2 = 84677148 H4 = C3D2E1F0 + 86838382 = 4A566572. Start processing block 2. The words of block 2 are W[0] = 00000000 W[1] = 00000000 W[2] = 00000000 W[3] = 00000000 W[4] = 00000000 W[5] = 00000000 W[6] = 00000000 W[7] = 00000000 W[8] = 00000000 W[9] = 00000000 W[10] = 00000000 W[11] = 00000000 W[12] = 00000000 W[13] = 00000000 W[14] = 00000000 W[15] = 000001C0. The hex values of A,B,C,D,E after pass t of the for "t = 0 to 79" loop (step (d) of Section 7 or step (c) of Section 8) are A B C D E t = 0: 2DF257E9 F4286818 B0DEC9EB 0408F581 84677148 t = 1: 4D3DC58F 2DF257E9 3D0A1A06 B0DEC9EB 0408F581 t = 2: C352BB05 4D3DC58F 4B7C95FA 3D0A1A06 B0DEC9EB t = 3: EEF743C6 C352BB05 D34F7163 4B7C95FA 3D0A1A06 t = 4: 41E34277 EEF743C6 70D4AEC1 D34F7163 4B7C95FA t = 5: 5443915C 41E34277 BBBDD0F1 70D4AEC1 D34F7163 t = 6: E7FA0377 5443915C D078D09D BBBDD0F1 70D4AEC1 t = 7: C6946813 E7FA0377 1510E457 D078D09D BBBDD0F1 t = 8: FDDE1DE1 C6946813 F9FE80DD 1510E457 D078D09D t = 9: B8538ACA FDDE1DE1 F1A51A04 F9FE80DD 1510E457 t = 10: 6BA94F63 B8538ACA 7F778778 F1A51A04 F9FE80DD t = 11: 43A2792F 6BA94F63 AE14E2B2 7F778778 F1A51A04 t = 12: FECD7BBF 43A2792F DAEA53D8 AE14E2B2 7F778778 t = 13: A2604CA8 FECD7BBF D0E89E4B DAEA53D8 AE14E2B2 t = 14: 258B0BAA A2604CA8 FFB35EEF D0E89E4B DAEA53D8 t = 15: D9772360 258B0BAA 2898132A FFB35EEF D0E89E4B t = 16: 5507DB6E D9772360 8962C2EA 2898132A FFB35EEF t = 17: A51B58BC 5507DB6E 365DC8D8 8962C2EA 2898132A t = 18: C2EB709F A51B58BC 9541F6DB 365DC8D8 8962C2EA t = 19: D8992153 C2EB709F 2946D62F 9541F6DB 365DC8D8 t = 20: 37482F5F D8992153 F0BADC27 2946D62F 9541F6DB t = 21: EE8700BD 37482F5F F6264854 F0BADC27 2946D62F t = 22: 9AD594B9 EE8700BD CDD20BD7 F6264854 F0BADC27 t = 23: 8FBAA5B9 9AD594B9 7BA1C02F CDD20BD7 F6264854 t = 24: 88FB5867 8FBAA5B9 66B5652E 7BA1C02F CDD20BD7 t = 25: EEC50521 88FB5867 63EEA96E 66B5652E 7BA1C02F t = 26: 50BCE434 EEC50521 E23ED619 63EEA96E 66B5652E t = 27: 5C416DAF 50BCE434 7BB14148 E23ED619 63EEA96E t = 28: 2429BE5F 5C416DAF 142F390D 7BB14148 E23ED619 t = 29: 0A2FB108 2429BE5F D7105B6B 142F390D 7BB14148 t = 30: 17986223 0A2FB108 C90A6F97 D7105B6B 142F390D t = 31: 8A4AF384 17986223 028BEC42 C90A6F97 D7105B6B t = 32: 6B629993 8A4AF384 C5E61888 028BEC42 C90A6F97 t = 33: F15F04F3 6B629993 2292BCE1 C5E61888 028BEC42 t = 34: 295CC25B F15F04F3 DAD8A664 2292BCE1 C5E61888 t = 35: 696DA404 295CC25B FC57C13C DAD8A664 2292BCE1 t = 36: CEF5AE12 696DA404 CA573096 FC57C13C DAD8A664 t = 37: 87D5B80C CEF5AE12 1A5B6901 CA573096 FC57C13C t = 38: 84E2A5F2 87D5B80C B3BD6B84 1A5B6901 CA573096 t = 39: 03BB6310 84E2A5F2 21F56E03 B3BD6B84 1A5B6901 t = 40: C2D8F75F 03BB6310 A138A97C 21F56E03 B3BD6B84 t = 41: BFB25768 C2D8F75F 00EED8C4 A138A97C 21F56E03 t = 42: 28589152 BFB25768 F0B63DD7 00EED8C4 A138A97C t = 43: EC1D3D61 28589152 2FEC95DA F0B63DD7 00EED8C4 t = 44: 3CAED7AF EC1D3D61 8A162454 2FEC95DA F0B63DD7 t = 45: C3D033EA 3CAED7AF 7B074F58 8A162454 2FEC95DA t = 46: 7316056A C3D033EA CF2BB5EB 7B074F58 8A162454 t = 47: 46F93B68 7316056A B0F40CFA CF2BB5EB 7B074F58 t = 48: DC8E7F26 46F93B68 9CC5815A B0F40CFA CF2BB5EB t = 49: 850D411C DC8E7F26 11BE4EDA 9CC5815A B0F40CFA t = 50: 7E4672C0 850D411C B7239FC9 11BE4EDA 9CC5815A t = 51: 89FBD41D 7E4672C0 21435047 B7239FC9 11BE4EDA t = 52: 1797E228 89FBD41D 1F919CB0 21435047 B7239FC9 t = 53: 431D65BC 1797E228 627EF507 1F919CB0 21435047 t = 54: 2BDBB8CB 431D65BC 05E5F88A 627EF507 1F919CB0 t = 55: 6DA72E7F 2BDBB8CB 10C7596F 05E5F88A 627EF507 t = 56: A8495A9B 6DA72E7F CAF6EE32 10C7596F 05E5F88A t = 57: E785655A A8495A9B DB69CB9F CAF6EE32 10C7596F t = 58: 5B086C42 E785655A EA1256A6 DB69CB9F CAF6EE32 t = 59: A65818F7 5B086C42 B9E15956 EA1256A6 DB69CB9F t = 60: 7AAB101B A65818F7 96C21B10 B9E15956 EA1256A6 t = 61: 93614C9C 7AAB101B E996063D 96C21B10 B9E15956 t = 62: F66D9BF4 93614C9C DEAAC406 E996063D 96C21B10 t = 63: D504902B F66D9BF4 24D85327 DEAAC406 E996063D t = 64: 60A9DA62 D504902B 3D9B66FD 24D85327 DEAAC406 t = 65: 8B687819 60A9DA62 F541240A 3D9B66FD 24D85327 t = 66: 083E90C3 8B687819 982A7698 F541240A 3D9B66FD t = 67: F6226BBF 083E90C3 62DA1E06 982A7698 F541240A t = 68: 76C0563B F6226BBF C20FA430 62DA1E06 982A7698 t = 69: 989DD165 76C0563B FD889AEF C20FA430 62DA1E06 t = 70: 8B2C7573 989DD165 DDB0158E FD889AEF C20FA430 t = 71: AE1B8E7B 8B2C7573 66277459 DDB0158E FD889AEF t = 72: CA1840DE AE1B8E7B E2CB1D5C 66277459 DDB0158E t = 73: 16F3BABB CA1840DE EB86E39E E2CB1D5C 66277459 t = 74: D28D83AD 16F3BABB B2861037 EB86E39E E2CB1D5C t = 75: 6BC02DFE D28D83AD C5BCEEAE B2861037 EB86E39E t = 76: D3A6E275 6BC02DFE 74A360EB C5BCEEAE B2861037 t = 77: DA955482 D3A6E275 9AF00B7F 74A360EB C5BCEEAE t = 78: 58C0AAC0 DA955482 74E9B89D 9AF00B7F 74A360EB t = 79: 906FD62C 58C0AAC0 B6A55520 74E9B89D 9AF00B7F. Block 2 has been processed. The values of {Hi} are H0 = F4286818 + 906FD62C = 84983E44 H1 = C37B27AE + 58C0AAC0 = 1C3BD26E H2 = 0408F581 + B6A55520 = BAAE4AA1 H3 = 84677148 + 74E9B89D = F95129E5 H4 = 4A566572 + 9AF00B7F = E54670F1. Message digest = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 APPENDIX C. A THIRD SAMPLE MESSAGE AND ITS MESSAGE DIGEST This appendix is for informational purposes only and is not required to meet the standard. Let the message be the binary-coded form of the ASCII string which consists of 1,000,000 repetitions of "a". Message digest = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F hashcash-1.21/fastmint_altivec_standard_2.c0000664000076400007640000007074110351101612017417 0ustar adamadam#include #include "libfastmint.h" int minter_altivec_standard_2_test(void) { /* This minter runs only on PowerPC G4 and higher hardware */ #if defined(__POWERPC__) && defined(__ALTIVEC__) && defined(__GNUC__) return (gProcessorSupportFlags & HC_CPU_SUPPORTS_ALTIVEC) != 0; #endif /* Not a PowerPC, or compiler doesn't support Altivec or GNU assembly */ return 0; } /* Define low-level primitives in terms of operations */ /* #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) */ #define S(n,X) ( vec_rl( X, (vector unsigned int) (n) ) ) #define XOR(a,b) ( vec_xor(a,b) ) #define AND(a,b) ( vec_and(a,b) ) #define ANDNOT(a,b) ( vec_andc(a,b) ) #define OR(a,b) ( vec_or(a,b) ) #define ADD(a,b) ( vec_add(a,b) ) /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ #define F1( B, C, D, F, G ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) /* #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F2( B, C, D, F, G ) ( \ F = XOR(B,C), \ XOR(F,D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ /* #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) */ #define F3( B, C, D, F, G ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) /* #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F4(B,C,D,F,G) F2(B,C,D,F,G) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) */ #define Wf(W,t,F,G) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) #define Wfly(W,t,F,G) ( (t) < 16 ? (W)[t] : Wf(W,t,F,G) ) #define ROUND_F1_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t vand v5, %[b1], %[c1]" /* begin F1(B1,C1,D1) */ \ "\n\t lvx v16, %[w1], %[T]" /* load W1[t] in advance */ \ "\n\t vand v13, %[b2], %[c2]" /* begin F1(B2,C2,D2) */ \ "\n\t lvx v17, %[w2], %[T]" /* load W2[t] in advance */ \ "\n\t vandc v6, %[d1], %[b1]" \ "\n\t vandc v14, %[d2], %[b2]" \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t vor v5, v5, v6" /* finish F1(B1,C1,D1) */ \ "\n\t vor v13, v13, v14" /* finish F1(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r0" \ ); #define ROUND_F1_u(t,A,B,C,D,E,K,W) \ asm volatile ( \ "\n\t vand v5, %[b1], %[c1]" /* begin F1(B1,C1,D1) */ \ "\n\t lvx v15, %[w1], %[T]" /* load W1[t-16] */ \ "\n\t addi r20, %[T], 32" \ "\n\t vand v13, %[b2], %[c2]" /* begin F1(B2,C2,D2) */ \ "\n\t lvx v16, %[w2], %[T]" /* load W2[t-16] */ \ "\n\t addi %[T], %[T], 128" \ "\n\t lvx v6, %[w1], r20" /* load W1[t-14] */ \ "\n\t lvx v14, %[w2], r20" /* load W2[t-14] */ \ "\n\t addi r20, r20, 176" \ "\n\t lvx v7, %[w1], %[T]" /* load W1[t-8] */ \ "\n\t vxor v6, v6, v15" \ "\n\t lvx v15, %[w2], %[T]" /* load W2[t-8] */ \ "\n\t vxor v14, v14, v16" \ "\n\t lvx v16, %[w1], r20" /* load W1[t-3] */ \ "\n\t vxor v7, v7, v6" \ "\n\t lvx v17, %[w2], r20" /* load W2[t-3] */ \ "\n\t vxor v15, v15, v14" \ "\n\t vxor v16, v16, v7" \ "\n\t vxor v17, v17, v15" \ "\n\t vandc v6, %[d1], %[b1]" \ "\n\t vandc v14, %[d2], %[b2]" \ "\n\t addi %[T], r20, 48" \ "\n\t vrlw v16, v16, %[s1]" /* complete W1[t] */ \ "\n\t vrlw v17, v17, %[s1]" /* complete W2[t] */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t stvx v16, %[w1], %[T]" /* store W1[t] */ \ "\n\t vor v5, v5, v6" /* finish F1(B1,C1,D1) */ \ "\n\t stvx v17, %[w2], %[T]" /* store W2[t] */ \ "\n\t vor v13, v13, v14" /* finish F1(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)), [s1] "v" ((vector unsigned int) (1)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r20", "r0", "memory" \ ); #define ROUND_F2_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t vxor v5, %[b1], %[c1]" /* begin F2(B1,C1,D1) */ \ "\n\t lvx v16, %[w1], %[T]" /* load W1[t] in advance */ \ "\n\t vxor v13, %[b2], %[c2]" /* begin F2(B2,C2,D2) */ \ "\n\t lvx v17, %[w2], %[T]" /* load W2[t] in advance */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t vxor v5, v5, %[d1]" /* finish F2(B1,C1,D1) */ \ "\n\t vxor v13, v13, %[d2]" /* finish F2(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r0" \ ); #define ROUND_F2_u(t,A,B,C,D,E,K,W) \ asm volatile ( \ "\n\t vxor v5, %[b1], %[c1]" /* begin F2(B1,C1,D1) */ \ "\n\t lvx v15, %[w1], %[T]" /* load W1[t-16] */ \ "\n\t addi r20, %[T], 32" \ "\n\t vxor v13, %[b2], %[c2]" /* begin F2(B2,C2,D2) */ \ "\n\t lvx v16, %[w2], %[T]" /* load W2[t-16] */ \ "\n\t addi %[T], %[T], 128" \ "\n\t lvx v6, %[w1], r20" /* load W1[t-14] */ \ "\n\t lvx v14, %[w2], r20" /* load W2[t-14] */ \ "\n\t addi r20, r20, 176" \ "\n\t lvx v7, %[w1], %[T]" /* load W1[t-8] */ \ "\n\t vxor v6, v6, v15" \ "\n\t lvx v15, %[w2], %[T]" /* load W2[t-8] */ \ "\n\t vxor v14, v14, v16" \ "\n\t lvx v16, %[w1], r20" /* load W1[t-3] */ \ "\n\t vxor v7, v7, v6" \ "\n\t lvx v17, %[w2], r20" /* load W2[t-3] */ \ "\n\t vxor v15, v15, v14" \ "\n\t vxor v16, v16, v7" \ "\n\t vxor v17, v17, v15" \ "\n\t addi %[T], r20, 48" \ "\n\t vrlw v16, v16, %[s1]" /* complete W1[t] */ \ "\n\t vrlw v17, v17, %[s1]" /* complete W2[t] */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t stvx v16, %[w1], %[T]" /* store W1[t] */ \ "\n\t vxor v5, v5, %[d1]" /* finish F2(B1,C1,D1) */ \ "\n\t stvx v17, %[w2], %[T]" /* store W2[t] */ \ "\n\t vxor v13, v13, %[d2]" /* finish F2(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)), [s1] "v" ((vector unsigned int) (1)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r20", "r0", "memory" \ ); #define ROUND_F3_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t vor v5, %[d1], %[c1]" /* begin F3(B1,C1,D1) */ \ "\n\t lvx v16, %[w1], %[T]" /* load W1[t] in advance */ \ "\n\t vor v13, %[d2], %[c2]" /* begin F3(B2,C2,D2) */ \ "\n\t vand v6, %[d1], %[c1]" \ "\n\t vand v14, %[d2], %[c2]" \ "\n\t lvx v17, %[w2], %[T]" /* load W2[t] in advance */ \ "\n\t vand v5, v5, %[b1]" \ "\n\t vand v13, v13, %[b2]" \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t vor v5, v5, v6" /* finish F3(B1,C1,D1) */ \ "\n\t vor v13, v13, v14" /* finish F3(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r0" \ ); #define ROUND_F3_u(t,A,B,C,D,E,K,W) \ asm volatile ( \ "\n\t vor v5, %[d1], %[c1]" /* begin F3(B1,C1,D1) */ \ "\n\t lvx v15, %[w1], %[T]" /* load W1[t-16] */ \ "\n\t addi r20, %[T], 32" \ "\n\t vor v13, %[d2], %[c2]" /* begin F3(B2,C2,D2) */ \ "\n\t lvx v16, %[w2], %[T]" /* load W2[t-16] */ \ "\n\t addi %[T], %[T], 128" \ "\n\t lvx v6, %[w1], r20" /* load W1[t-14] */ \ "\n\t lvx v14, %[w2], r20" /* load W2[t-14] */ \ "\n\t addi r20, r20, 176" \ "\n\t lvx v7, %[w1], %[T]" /* load W1[t-8] */ \ "\n\t vxor v6, v6, v15" \ "\n\t lvx v15, %[w2], %[T]" /* load W2[t-8] */ \ "\n\t vxor v14, v14, v16" \ "\n\t lvx v16, %[w1], r20" /* load W1[t-3] */ \ "\n\t vxor v7, v7, v6" \ "\n\t lvx v17, %[w2], r20" /* load W2[t-3] */ \ "\n\t vxor v15, v15, v14" \ "\n\t vand v6, %[d1], %[c1]" \ "\n\t vand v14, %[d2], %[c2]" \ "\n\t vxor v16, v16, v7" \ "\n\t vxor v17, v17, v15" \ "\n\t vand v5, v5, %[b1]" \ "\n\t vand v13, v13, %[b2]" \ "\n\t addi %[T], r20, 48" \ "\n\t vrlw v16, v16, %[s1]" /* complete W1[t] */ \ "\n\t vrlw v17, v17, %[s1]" /* complete W2[t] */ \ "\n\t vrlw v7, %[a1], %[s5]" /* S(5,A1) */ \ "\n\t vrlw v15, %[a2], %[s5]" /* S(5,A2) */ \ "\n\t stvx v16, %[w1], %[T]" /* store W1[t] */ \ "\n\t vor v5, v5, v6" /* finish F3(B1,C1,D1) */ \ "\n\t stvx v17, %[w2], %[T]" /* store W2[t] */ \ "\n\t vor v13, v13, v14" /* finish F3(B2,C2,D2) */ \ "\n\t vadduwm v7, v7, %[k]" /* sum K, S(5,A1) */ \ "\n\t vadduwm v15, v15, %[k]" /* sum K, S(5,A2) */ \ "\n\t vadduwm %[e1], %[e1], v16" /* sum W1[t] to E1 */ \ "\n\t vadduwm %[e2], %[e2], v17" /* sum W2[t] to E1 */ \ "\n\t vadduwm v7, v7, v5" /* sum K, S(5,A1), F1(B1,C1,D1) */ \ "\n\t vadduwm v15, v15, v13" /* sum K, S(5,A2), F1(B2,C2,D2) */ \ "\n\t vrlw %[b1], %[b1],%[s30]" /* S(30,B1) */ \ "\n\t vrlw %[b2], %[b2],%[s30]" /* S(30,B2) */ \ "\n\t vadduwm %[e1], %[e1], v7" /* sum everything to E1 */ \ "\n\t vadduwm %[e2], %[e2], v15" /* sum everything to E2 */ \ : [a1] "+v" (A##1), [b1] "+v" (B##1), [c1] "+v" (C##1), [d1] "+v" (D##1), [e1] "+v" (E##1), \ [a2] "+v" (A##2), [b2] "+v" (B##2), [c2] "+v" (C##2), [d2] "+v" (D##2), [e2] "+v" (E##2) \ : [w1] "r" (W##1), [w2] "r" (W##2), [k] "v" (K), [T] "r" (t), \ [s30] "v" ((vector unsigned int) (30)), [s5] "v" ((vector unsigned int) (5)), [s1] "v" ((vector unsigned int) (1)) \ : "v5", "v6", "v7", "v13", "v14", "v15", "v16", "v17", "r20", "r0", "memory" \ ); #define ROUND_F4_n ROUND_F2_n #define ROUND_F4_u ROUND_F2_u #define ROUNDu(t,A,B,C,D,E,Func,K,W) \ if((t) < 16) { \ o = (t) * sizeof(vector unsigned int); \ ROUND_##Func##_n(o,A,B,C,D,E,K,W); \ } else { \ o = (t-16) * sizeof(vector unsigned int); \ ROUND_##Func##_u(o,A,B,C,D,E,K,W); \ } #define ROUNDn(t,A,B,C,D,E,Func,K,W) \ o = (t) * sizeof(vector unsigned int); \ ROUND_##Func##_n(o,A,B,C,D,E,K,W); #define ROUND5( t, Func, K ) \ ROUNDu( t + 0, A, B, C, D, E, Func, K, W );\ ROUNDu( t + 1, E, A, B, C, D, Func, K, W );\ ROUNDu( t + 2, D, E, A, B, C, Func, K, W );\ ROUNDu( t + 3, C, D, E, A, B, Func, K, W );\ ROUNDu( t + 4, B, C, D, E, A, Func, K, W ); unsigned long minter_altivec_standard_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { #if defined(__POWERPC__) && defined(__ALTIVEC__) && defined(__GNUC__) MINTER_CALLBACK_VARS; unsigned long iters; unsigned int m, n, o, t, gotBits, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low, bitMask1High, s; vector unsigned int vBitMaskHigh, vBitMaskLow; register vector unsigned int A1,B1,C1,D1,E1,Fa,Ga; register vector unsigned int A2,B2,C2,D2,E2,Fb,Gb; vector unsigned int A,B,N; vector unsigned int W1[80], W2[80]; vector unsigned int H[5], pH[5], K[4] = { (vector unsigned int) (K1), (vector unsigned int) (K2), (vector unsigned int) (K3), (vector unsigned int) (K4) }; vector bool int M; uInt32 *Hw = (uInt32*) H; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X1 = (unsigned char*) W1; unsigned char *X2 = (unsigned char*) W2; unsigned char *output = (unsigned char*) block, *X; *best = 0; /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } *((uInt32*) &vBitMaskLow ) = bitMask1Low ; vBitMaskLow = vec_splat(vBitMaskLow , 0); *((uInt32*) &vBitMaskHigh) = bitMask1High; vBitMaskHigh = vec_splat(vBitMaskHigh, 0); maxBits = 0; /* Copy block and IV to vectorised internal storage */ for(t=0; t < 16; t++) { X1[t*16+ 0] = X1[t*16+ 4] = X1[t*16+ 8] = X1[t*16+12] = output[t*4+0]; X1[t*16+ 1] = X1[t*16+ 5] = X1[t*16+ 9] = X1[t*16+13] = output[t*4+1]; X1[t*16+ 2] = X1[t*16+ 6] = X1[t*16+10] = X1[t*16+14] = output[t*4+2]; X1[t*16+ 3] = X1[t*16+ 7] = X1[t*16+11] = X1[t*16+15] = output[t*4+3]; X2[t*16+ 0] = X2[t*16+ 4] = X2[t*16+ 8] = X2[t*16+12] = output[t*4+0]; X2[t*16+ 1] = X2[t*16+ 5] = X2[t*16+ 9] = X2[t*16+13] = output[t*4+1]; X2[t*16+ 2] = X2[t*16+ 6] = X2[t*16+10] = X2[t*16+14] = output[t*4+2]; X2[t*16+ 3] = X2[t*16+ 7] = X2[t*16+11] = X2[t*16+15] = output[t*4+3]; } for(t=0; t < 5; t++) { Hw[t*4+0] = Hw[t*4+1] = Hw[t*4+2] = Hw[t*4+3] = IV[t]; pH[t] = H[t]; } /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-8; iters += 8) { /* Encode iteration count into tail */ /* Iteration count is always 8-aligned, so only least-significant character needs multiple lookup */ /* Further, we assume we're always big-endian */ X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 0] = p[(iters & 0x3c) + 0]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 0] = p[(iters & 0x3c) + 1]; X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 4] = p[(iters & 0x3c) + 2]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 4] = p[(iters & 0x3c) + 3]; X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 8] = p[(iters & 0x3c) + 4]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 8] = p[(iters & 0x3c) + 5]; X1[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 12] = p[(iters & 0x3c) + 6]; X2[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 12] = p[(iters & 0x3c) + 7]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 0] = X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 4] = X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 8] = X1[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 12] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 0] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 4] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 8] = X2[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 12] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 0] = X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 4] = X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 8] = X1[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 12] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 0] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 4] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 8] = X2[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 12] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 0] = X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 4] = X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 8] = X1[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 12] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 0] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 4] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 8] = X2[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 12] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 0] = X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 4] = X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 8] = X1[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 12] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 0] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 4] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 8] = X2[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 12] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 0] = X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 4] = X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 8] = X1[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 12] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 0] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 4] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 8] = X2[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 12] = p[(iters >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A1 = A2 = H[0]; B1 = B2 = H[1]; C1 = C2 = H[2]; D1 = D2 = H[3]; E1 = E2 = H[4]; for(t=16; t < 32; t++) { Wf(W1,t,Fa,Ga); Wf(W2,t,Fb,Gb); } ROUNDn( 0, A, B, C, D, E, F1, K[0], W ); ROUNDn( 1, E, A, B, C, D, F1, K[0], W ); ROUNDn( 2, D, E, A, B, C, F1, K[0], W ); ROUNDn( 3, C, D, E, A, B, F1, K[0], W ); ROUNDn( 4, B, C, D, E, A, F1, K[0], W ); ROUNDn( 5, A, B, C, D, E, F1, K[0], W ); ROUNDn( 6, E, A, B, C, D, F1, K[0], W ); if(tailIndex == 52) { ROUNDn( 7, D, E, A, B, C, F1, K[0], W ); ROUNDn( 8, C, D, E, A, B, F1, K[0], W ); ROUNDn( 9, B, C, D, E, A, F1, K[0], W ); ROUNDn(10, A, B, C, D, E, F1, K[0], W ); ROUNDn(11, E, A, B, C, D, F1, K[0], W ); } pH[0] = A1; pH[1] = B1; pH[2] = C1; pH[3] = D1; pH[4] = E1; } /* Set up working variables */ A1 = A2 = pH[0]; B1 = B2 = pH[1]; C1 = C2 = pH[2]; D1 = D2 = pH[3]; E1 = E2 = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUNDn( 0, A, B, C, D, E, F1, K[0], W ); ROUNDn( 1, E, A, B, C, D, F1, K[0], W ); ROUNDn( 2, D, E, A, B, C, F1, K[0], W ); ROUNDn( 3, C, D, E, A, B, F1, K[0], W ); ROUNDn( 4, B, C, D, E, A, F1, K[0], W ); ROUNDn( 5, A, B, C, D, E, F1, K[0], W ); ROUNDn( 6, E, A, B, C, D, F1, K[0], W ); case 32: ROUNDn( 7, D, E, A, B, C, F1, K[0], W ); ROUNDn( 8, C, D, E, A, B, F1, K[0], W ); ROUNDn( 9, B, C, D, E, A, F1, K[0], W ); ROUNDn(10, A, B, C, D, E, F1, K[0], W ); ROUNDn(11, E, A, B, C, D, F1, K[0], W ); case 52: ROUNDn(12, D, E, A, B, C, F1, K[0], W ); ROUNDn(13, C, D, E, A, B, F1, K[0], W ); ROUNDn(14, B, C, D, E, A, F1, K[0], W ); ROUNDn(15, A, B, C, D, E, F1, K[0], W ); } if(tailIndex == 52) { ROUNDn(16, E, A, B, C, D, F1, K[0], W ); ROUNDn(17, D, E, A, B, C, F1, K[0], W ); ROUNDn(18, C, D, E, A, B, F1, K[0], W ); ROUNDn(19, B, C, D, E, A, F1, K[0], W ); ROUNDu(20, A, B, C, D, E, F2, K[1], W ); ROUNDn(21, E, A, B, C, D, F2, K[1], W ); ROUNDn(22, D, E, A, B, C, F2, K[1], W ); ROUNDu(23, C, D, E, A, B, F2, K[1], W ); ROUNDn(24, B, C, D, E, A, F2, K[1], W ); ROUNDn(25, A, B, C, D, E, F2, K[1], W ); ROUNDu(26, E, A, B, C, D, F2, K[1], W ); ROUNDn(27, D, E, A, B, C, F2, K[1], W ); ROUNDu(28, C, D, E, A, B, F2, K[1], W ); ROUNDu(29, B, C, D, E, A, F2, K[1], W ); ROUNDn(30, A, B, C, D, E, F2, K[1], W ); } else if (tailIndex == 32) { ROUNDn(16, E, A, B, C, D, F1, K[0], W ); ROUNDn(17, D, E, A, B, C, F1, K[0], W ); ROUNDn(18, C, D, E, A, B, F1, K[0], W ); ROUNDn(19, B, C, D, E, A, F1, K[0], W ); ROUNDn(20, A, B, C, D, E, F2, K[1], W ); ROUNDu(21, E, A, B, C, D, F2, K[1], W ); ROUNDn(22, D, E, A, B, C, F2, K[1], W ); ROUNDu(23, C, D, E, A, B, F2, K[1], W ); ROUNDu(24, B, C, D, E, A, F2, K[1], W ); ROUNDn(25, A, B, C, D, E, F2, K[1], W ); ROUNDu(26, E, A, B, C, D, F2, K[1], W ); ROUNDu(27, D, E, A, B, C, F2, K[1], W ); ROUNDn(28, C, D, E, A, B, F2, K[1], W ); ROUNDu(29, B, C, D, E, A, F2, K[1], W ); ROUNDu(30, A, B, C, D, E, F2, K[1], W ); } else { ROUNDu(16, E, A, B, C, D, F1, K[0], W ); ROUNDu(17, D, E, A, B, C, F1, K[0], W ); ROUNDu(18, C, D, E, A, B, F1, K[0], W ); ROUNDu(19, B, C, D, E, A, F1, K[0], W ); ROUNDu(20, A, B, C, D, E, F2, K[1], W ); ROUNDu(21, E, A, B, C, D, F2, K[1], W ); ROUNDu(22, D, E, A, B, C, F2, K[1], W ); ROUNDu(23, C, D, E, A, B, F2, K[1], W ); ROUNDu(24, B, C, D, E, A, F2, K[1], W ); ROUNDu(25, A, B, C, D, E, F2, K[1], W ); ROUNDu(26, E, A, B, C, D, F2, K[1], W ); ROUNDu(27, D, E, A, B, C, F2, K[1], W ); ROUNDu(28, C, D, E, A, B, F2, K[1], W ); ROUNDu(29, B, C, D, E, A, F2, K[1], W ); ROUNDu(30, A, B, C, D, E, F2, K[1], W ); } ROUNDu(31, E, A, B, C, D, F2, K[1], W ); ROUNDu(32, D, E, A, B, C, F2, K[1], W ); ROUNDu(33, C, D, E, A, B, F2, K[1], W ); ROUNDu(34, B, C, D, E, A, F2, K[1], W ); ROUNDu(35, A, B, C, D, E, F2, K[1], W ); ROUNDu(36, E, A, B, C, D, F2, K[1], W ); ROUNDu(37, D, E, A, B, C, F2, K[1], W ); ROUNDu(38, C, D, E, A, B, F2, K[1], W ); ROUNDu(39, B, C, D, E, A, F2, K[1], W ); ROUND5(40, F3, K[2] ); ROUND5(45, F3, K[2] ); ROUND5(50, F3, K[2] ); ROUND5(55, F3, K[2] ); ROUND5(60, F4, K[3] ); ROUND5(65, F4, K[3] ); ROUND5(70, F4, K[3] ); ROUND5(75, F4, K[3] ); /* Mix in the IV again */ A1 = vec_add(A1, H[0]); B1 = vec_add(B1, H[1]); A2 = vec_add(A2, H[0]); B2 = vec_add(B2, H[1]); /* Debugging! */ if(0 && iters==0) { for(t=0; t < 80; t++) { X1 = (unsigned char*) (W1+t); printf("%2X %2X %2X %2X | %2X %2X %2X %2X\n", *(X1++), *(X1++), *(X1++), *(X1++), *(X1++), *(X1++), *(X1++), *(X1++) ); } X1 = (unsigned char*) W1; } /* Quickly extract the best results from each pipe into a single vector set */ M = vec_sel(vec_cmpgt(A1,A2), vec_cmpgt(B1,B2), vec_cmpeq(A1,A2)); N = vec_sel((vector unsigned int) (0), (vector unsigned int) (1), M); A = vec_sel(A1, A2, M); B = vec_sel(B1, B2, M); /* Is this the best bit count so far? */ if(vec_any_ne( vec_and( vec_cmpeq(vec_and(A, vBitMaskLow), (vector unsigned int) (0)), vec_cmpeq(vec_and(A, vBitMaskHigh), (vector unsigned int) (0)) ), (vector unsigned int) (0))) { uInt32 IA, IB; /* Go over each vector element in turn */ for(n=0; n < 4; n++) { /* Extract A and B components */ IA = ((uInt32*) &A)[n]; IB = ((uInt32*) &B)[n]; m = ((uInt32*) &N)[n]; /* Count bits */ gotBits = 0; if(IA) { s = IA; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(IB) { s = IB; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } *((uInt32*) &vBitMaskLow ) = bitMask1Low ; vBitMaskLow = vec_splat(vBitMaskLow , 0); *((uInt32*) &vBitMaskHigh) = bitMask1High; vBitMaskHigh = vec_splat(vBitMaskHigh, 0); /* Copy this result back to the block buffer */ if(m) X = X2; else X = X1; for(t=0; t < 16; t++) { output[t*4+0] = X[t*16+0+n*4]; output[t*4+1] = X[t*16+1+n*4]; output[t*4+2] = X[t*16+2+n*4]; output[t*4+3] = X[t*16+3+n*4]; } /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+8; } } } if(0) return 0; MINTER_CALLBACK(); } return iters+8; /* For other platforms */ #else return 0; #endif } hashcash-1.21/sha1.c0000664000076400007640000000313010351101612012602 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include "sha1.h" #define BUFFER_SIZE 1024 int SHA1_file( char* filename, byte md[ SHA1_DIGEST_BYTES ] ) { FILE* file = NULL ; byte buffer[ BUFFER_SIZE ] = {0}; size_t bytes_read = 0 ; int opened = 0; SHA1_ctx ctx; if ( strcmp( filename, "-" ) == 0 ) { file = stdin; } else { file = fopen( filename, "rb" ); if ( file == 0 ) { return -1; } opened = 1; } SHA1_Init( &ctx ); while ( !feof( file ) ) { bytes_read = fread( buffer, 1, BUFFER_SIZE, file ); if ( bytes_read < BUFFER_SIZE && ferror( file ) ) { return -1; } SHA1_Update( &ctx, buffer, bytes_read ); } SHA1_Final( &ctx, md ); if ( opened ) { fclose( file ); } return 0; } const char* hex_digest( byte md[ SHA1_DIGEST_BYTES ] ) { int i = 0 ; static char hex[ SHA1_DIGEST_BYTES * 2 + 1 ] = {0}; for ( i = 0; i < SHA1_DIGEST_BYTES; i++ ) { sprintf( hex + 2 * i, "%02x", md[ i ] ); } hex[ sizeof( hex ) - 1 ] = '\0'; return hex; } int main( int argc, char* argv[] ) { int i = 0 ; byte digest[ SHA1_DIGEST_BYTES ] = {0}; int status = 0 ; if ( argc == 1 ) { status = SHA1_file( "-", digest ); if ( status < 0 ) { perror( "(stdin)" ); } else { printf( "%s\n", hex_digest( digest ) ); } } else { for ( i = 1; i < argc; i++ ) { status = SHA1_file( argv[ i ], digest ); if ( status < 0 ) { perror( argv[ i ] ); return 1; } else { printf( "%s %s\n", hex_digest( digest ), argv[ i ] ); } } } return 0; } hashcash-1.21/getopt.h0000664000076400007640000001043010351101612013256 0ustar adamadam/* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if __STDC__ || defined(PROTO) #if defined(__GNU_LIBRARY__) /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); #endif /* not __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int getopt (); extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* not __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ hashcash-1.21/sstring.c0000664000076400007640000000236010351101612013443 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #include "sstring.h" /* strtok/strtok_r is a disaster, so here's a more sane one */ /* if *tok is NULL space is allocated; if the *tok is not NULL, and * the token is too large it is truncated; tok_len returns the length, * a pointer to **tok is also returned * if tok is NULL do not return the parsed value */ char* sstrtok( const char* str, const char* delim, char** tok, int tok_max, int* tok_len, char** s ) { char *end = NULL ; int use = 0 ; if ( delim == NULL ) { return NULL; } if ( str != NULL ) { *s = (char*)str; } if ( **s == '\0' ) { return NULL; } /* consumed last token */ end = strpbrk( *s, delim ); if ( end == NULL ) { *tok_len = strlen(*s); } else { *tok_len = end - *s; } if ( tok ) { if ( tok_max > 0 ) { use = tok_max < *tok_len ? tok_max : *tok_len; } else { use = *tok_len; } if ( !*tok ) { *tok = malloc( use+1 ); } sstrncpy( *tok, *s, use ); } *s += *tok_len + ( end == NULL ? 0 : 1 ); return tok ? *tok : ""; } void stolower( char* str ) { if ( !str ) { return; } for ( ; *str; str++ ) { *str = tolower( *str ); } } hashcash-1.21/README0000664000076400007640000000022510351101612012464 0ustar adamadamSee hashcash man page, and http://www.hashcash.org for information. There is a hashcash FAQ and mailing-list on the web page if you have questions. hashcash-1.21/random.c0000664000076400007640000001166310351101612013240 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #include #include "random.h" int initialized = 0; /* on machines that have /dev/urandom -- use it */ #if defined( __linux__ ) || defined( __FreeBSD__ ) || defined( __MACH__ ) || \ defined( __OpenBSD__ ) || defined( DEV_URANDOM ) #define URANDOM_FILE "/dev/urandom" FILE* urandom = NULL; int random_init( void ) { int res = (urandom = fopen( URANDOM_FILE, "r" )) != NULL; if ( res ) { initialized = 1; } return res; } int random_getbytes( void* data, size_t len ) { if ( !initialized && !random_init() ) { return 0; } return fread( data, len, 1, urandom ); } int random_final( void ) { int res = 0; if ( urandom ) { res = (fclose( urandom ) == 0); } return res; } #else /* if no /dev/urandom fall back to a crappy rng who's only * entropy source is your high resolution timer */ /* on windows we are ok as we can use CAPI, but use CAPI combined with the below just to be sure! */ /* WARNING: on platforms other than windows this is not of * cryptographic quality */ #include #if defined( unix ) || defined( VMS ) #include #include #elif defined( WIN32 ) #include #include #include #include #else #include #endif #include #if defined( OPENSSL ) #include #define SHA1_ctx SHA_CTX #define SHA1_Final( x, digest ) SHA1_Final( digest, x ) #define SHA1_DIGEST_BYTES SHA_DIGEST_LENGTH #else #include "sha1.h" #endif #if defined( WIN32 ) #define pid_t int typedef BOOL (WINAPI *CRYPTACQUIRECONTEXT)(HCRYPTPROV *, LPCTSTR, LPCTSTR, DWORD, DWORD); typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV, DWORD, BYTE *); typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV, DWORD); HCRYPTPROV hProvider = 0; CRYPTRELEASECONTEXT release = 0; CRYPTGENRANDOM gen = 0; #endif byte state[ SHA1_DIGEST_BYTES ]; byte output[ SHA1_DIGEST_BYTES ]; long counter = 0; /* output = SHA1( input || time || pid || counter++ ) */ static void random_stir( const byte input[SHA1_DIGEST_BYTES], byte output[SHA1_DIGEST_BYTES] ) { SHA1_ctx sha1; #if defined(__unix__) || defined(WIN32) pid_t pid = getpid(); #else unsigned long pid = rand(); #endif #if defined(__unix__) struct timeval tv = {}; struct timezone tz = {}; #endif #if defined(WIN32) SYSTEMTIME tw; BYTE buf[64]; #endif clock_t t = clock(); time_t t2 = time(0); SHA1_Init( &sha1 ); #if defined(__unix__) gettimeofday(&tv,&tz); SHA1_Update( &sha1, &tv, sizeof( tv ) ); SHA1_Update( &sha1, &tz, sizeof( tz ) ); #endif #if defined(WIN32) GetSystemTime(&tw); SHA1_Update( &sha1, &tw, sizeof( tw ) ); if ( gen ) { if (gen(hProvider, sizeof(buf), buf)) { SHA1_Update( &sha1, buf, sizeof(buf) ); } } #endif SHA1_Update( &sha1, input, SHA1_DIGEST_BYTES ); SHA1_Update( &sha1, &t, sizeof( clock_t ) ); SHA1_Update( &sha1, &t2, sizeof( time_t ) ); SHA1_Update( &sha1, &pid, sizeof( pid ) ); SHA1_Update( &sha1, &counter, sizeof( long ) ); SHA1_Final( &sha1, output ); counter++; } int random_init( void ) { #if defined(WIN32) HMODULE advapi = 0; CRYPTACQUIRECONTEXT acquire = 0; #endif #if defined(WIN32) advapi = LoadLibrary(TEXT("ADVAPI32.DLL")); if (advapi) { acquire = (CRYPTACQUIRECONTEXT) GetProcAddress(advapi, TEXT("CryptAcquireContextA")); gen = (CRYPTGENRANDOM) GetProcAddress(advapi, TEXT("CryptGenRandom")); release = (CRYPTRELEASECONTEXT) GetProcAddress(advapi, TEXT("CryptReleaseContext")); } if ( acquire && gen ) { if (!acquire(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { gen = NULL; } } #endif srand(clock()); random_stir( state, state ); initialized = 1; return 1; } int random_final( void ) { #if defined(WIN32) if ( hProvider && release ) { release(hProvider,0); } #endif return 1; } #define CHUNK_LEN (SHA1_DIGEST_BYTES) int random_getbytes( void* rnd, size_t len ) { byte* rndp = (byte*)rnd; int use = 0; if ( !initialized && !random_init() ) { return 0; } random_stir( state, state ); /* mix in the time, pid */ for ( ; len > 0; len -= use, rndp += CHUNK_LEN ) { random_stir( state, output ); use = len > CHUNK_LEN ? CHUNK_LEN : len; memcpy( rndp, output, use ); } return 1; } #endif static int count_bits( long val ) { int count = 0 ; for ( count = 0; val; val >>= 1, count++ ) { } return count; } int random_rectangular( long top, long* resp ) { long mask = 0 ; int neg = 1; long res = 0 ; if ( top < 0 ) { neg = -1; top = -top; } mask = ~( LONG_MAX << count_bits( top ) ); do { if ( !random_getbytes( &res, sizeof( long ) ) ) { return 0; } res &= mask; } while ( res > top ); *resp = res * neg; return 1; } hashcash-1.21/fastmint_ansi_ultracompact_1.c0000664000076400007640000001341310351101612017610 0ustar adamadam#include #include #include "libfastmint.h" int minter_ansi_ultracompact_1_test(void) { /* This minter runs on any hardware */ return 1; } /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) #define ROUND(t,A,B,C,D,E,Func,K) \ E += S(5,A) + Func(B,C,D) + W[t] + K; \ B = S(30,B); #define ROUNDg(t,A,B,C,D,E,Func,K) \ switch((t) % 5) { \ case 0: \ E += S(5,A) + Func(B,C,D) + W[t] + K; \ B = S(30,B); \ break; \ case 1: \ D += S(5,E) + Func(A,B,C) + W[t] + K; \ A = S(30,A); \ break; \ case 2: \ C += S(5,D) + Func(E,A,B) + W[t] + K; \ E = S(30,E); \ break; \ case 3: \ B += S(5,C) + Func(D,E,A) + W[t] + K; \ D = S(30,D); \ break; \ case 4: \ A += S(5,B) + Func(C,D,E) + W[t] + K; \ C = S(30,C); \ break; \ } #define ROUND5( n, Func, K ) \ ROUND( t + 0, A, B, C, D, E, Func, K );\ ROUND( t + 1, E, A, B, C, D, Func, K );\ ROUND( t + 2, D, E, A, B, C, Func, K );\ ROUND( t + 3, C, D, E, A, B, Func, K );\ ROUND( t + 4, B, C, D, E, A, Func, K ); #define ROUND20( n, Func, K )\ for(t=n; t < n+20; t += 5) {\ ROUND5( t, Func, K );\ } unsigned long minter_ansi_ultracompact_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { MINTER_CALLBACK_VARS; unsigned long iters = 0 ; int t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; uInt32 A = 0 , B = 0 , C = 0 , D = 0 , E = 0 ; uInt32 W[80] = {0}; uInt32 H[5] = {0}, pH[5] = {0}; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X = (unsigned char*) W; int addressMask = 0 ; static const int endTest = 3; unsigned char *output = (unsigned char*) block; const int W32[] = {21,23,24,26,27,29,30,31,0}, W52[] = {20,23,26,28,29,31,0}; char wordUpdate[80] = {0}; *best = 0; /* Work out whether we need to swap bytes during encoding */ addressMask = ( *(char*)&endTest ); /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } maxBits = 0; /* Copy block and IV to internal storage */ for(t=0; t < 16; t++) W[t] = GET_WORD(output + t*4); for(t=0; t < 5; t++) pH[t] = H[t] = IV[t]; /* Precalculate which words need the W buffer updating */ switch(tailIndex) { default: for(t=16; t < 32; t++) wordUpdate[t] = 1; break; case 32: for(t=0; W32[t]; t++) wordUpdate[W32[t]] = 1; break; case 52: for(t=0; W52[t]; t++) wordUpdate[W52[t]] = 1; break; } for(t=32; t < 80; t++) wordUpdate[t] = 1; /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter; iters++) { /* Encode iteration count into tail */ X[(tailIndex - 1) ^ addressMask] = p[(iters ) & 0x3f]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(tailIndex - 2) ^ addressMask] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(tailIndex - 3) ^ addressMask] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(tailIndex - 4) ^ addressMask] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(tailIndex - 5) ^ addressMask] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(tailIndex - 6) ^ addressMask] = p[(iters >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if(!(iters & 0x3f)) { A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; for(t=16; t < 32; t++) Wf(t); for(t = 0; t < ((tailIndex-1) / 20) * 5; t += 5) { ROUND5( t, F1, K1 ); } pH[0] = A; pH[1] = B; pH[2] = C; pH[3] = D; pH[4] = E; } /* Fill W buffer */ for(t=16; t < 80; t++) if(wordUpdate[t]) Wf(t); /* Set up working variables */ A = pH[0]; B = pH[1]; C = pH[2]; D = pH[3]; E = pH[4]; /* Do the rounds */ for(t = ((tailIndex-1) / 20) * 5; t < 20; t += 5) { ROUND5( t, F1, K1 ); } ROUND20(20,F2,K2); ROUND20(40,F3,K3); ROUND20(60,F4,K4); /* Mix in the IV again */ A += H[0]; B += H[1]; C += H[2]; D += H[3]; E += H[4]; /* Is this the best bit count so far? */ if(!(A & bitMask1Low) && !(B & bitMask1High)) { /* Count bits */ gotBits = 0; if(A) { s = A; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(B) { s = B; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best = gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } /* Copy this result back to the block buffer */ for(t=0; t < 16; t++) PUT_WORD(output + t*4, W[t]); /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+1; } } MINTER_CALLBACK(); } return iters+1; } hashcash-1.21/hashcash.h0000664000076400007640000002733110411051064013550 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _hashcash_h ) #define _hashcash_h #if defined( __cplusplus ) extern "C" { #endif #include #if defined(WIN32) && !defined(MONOLITHIC) #include #endif #if !defined(HCEXPORT) #if !defined(WIN32) || defined(MONOLITHIC) #define HCEXPORT #elif defined(BUILD_DLL) #define HCEXPORT __declspec(dllexport) #else /* USE_DLL */ #define HCEXPORT extern __declspec(dllimport) #endif #endif extern int verbose_flag; extern int no_purge_flag; #define HASHCASH_VERSION 1.21 #define HASHCASH_FORMAT_VERSION 1 #define stringify( x ) stringify2( x ) #define stringify2( x ) #x #define HASHCASH_VERSION_STRING stringify(HASHCASH_VERSION) #define TYPE_STR 0 #define TYPE_WILD 1 #define TYPE_REGEXP 2 #define MAX_UTC 13 #define MAX_CTR 64 #define MAX_RES 256 #define MAX_STR 256 #define MAX_VER 2 #define MAX_EXT 10240 #define MAX_TOK ((MAX_VER)+1+(MAX_RES)+1+(MAX_EXT)+1+(MAX_UTC)+1+(MAX_STR)) #define TIME_MINUTE 60 #define TIME_HOUR (TIME_MINUTE*60) #define TIME_DAY (TIME_HOUR*24) #define TIME_YEAR (TIME_DAY*365) #define TIME_MONTH (TIME_YEAR/12) #define TIME_AEON (TIME_YEAR*1000000000.0) #define TIME_MILLI_SECOND (1.0/1000) #define TIME_MICRO_SECOND (TIME_MILLI_SECOND/1000) #define TIME_NANO_SECOND (TIME_MICRO_SECOND/1000) #define HASHCASH_OK 1 #define HASHCASH_FAIL 0 #define HASHCASH_VALID_FOREVER 0 #define HASHCASH_INVALID_TOK_LEN -1 #define HASHCASH_RNG_FAILED -2 #define HASHCASH_INVALID_TIME -3 #define HASHCASH_TOO_MANY_TRIES -4 #define HASHCASH_EXPIRED_ON_CREATION -5 #define HASHCASH_INVALID_VALIDITY_PERIOD -6 #define HASHCASH_INTERNAL_ERROR -7 #define HASHCASH_INVALID_TIME_WIDTH -8 #define HASHCASH_VALID_IN_FUTURE -9 #define HASHCASH_EXPIRED -10 #define HASHCASH_INVALID -11 #define HASHCASH_WRONG_RESOURCE -12 #define HASHCASH_INSUFFICIENT_BITS -13 #define HASHCASH_UNSUPPORTED_VERSION -14 #define HASHCASH_SPENT -15 #define HASHCASH_NO_TOKEN -16 #define HASHCASH_REGEXP_ERROR -17 #define HASHCASH_OUT_OF_MEMORY -18 #define HASHCASH_USER_ABORT -19 /* return hashcash version number major.minor eg "1.13" */ HCEXPORT const char* hashcash_version( void ); /* function hashcash_mint calling info: * * returns HASHCASH_OK on success or one of the error codes above on * failure (values < 0) * * arguments are: * * now_time -- should be time in UTC, use time(2) * * time_width -- how many chars to abbreviate the time to * default 0 is 6 chars YYMMDD * * resource -- resource name, unique descriptor for resource you're * trying to protect * * bits -- bits of collision the resource demands * * anon_period -- add (or subtract if negative) a random period * in seconds between this value and 0 * to get same as mixmaster does set this to -3days * (default 0) * * stamp -- the stamp output * * anon_random -- default NULL, if set to pointer to long * returns actual random offset added to time * * tries_taken -- default NULL, if set to pointer to double * returns number of stamps tested before finding * returned stamp. * * ext -- extension field * * compress -- compress the stamp 0 = fast (no compression), 1 = medium * (some compression), 2 = very compressed (but slow) * * small -- optimize for stamp size (true) or stamp generation speed * (false) * * cb -- user defined callback function which is called every * 1/10th second; if it returns false hashcash_mint * will return HASHCASH_USER_ABORT; if you do not need * the callback function, pass NULL * * user_arg -- user argument passed to the callback function, if you * do not need the callback function or if you do not need * the arguement pass NULL. * */ /* note: it is the callers responsibility to call hashcash_free on * stamp when finished */ typedef int (*hashcash_callback)(int percent, int largest, int target, double count, double expected, void* user); HCEXPORT int hashcash_mint( time_t now_time, int time_width, const char* resource, unsigned bits, long anon_period, char** stamp, long* anon_random, double* tries_taken, char* ext, int compress, hashcash_callback cb, void* user_arg ); /* simpler API for minting */ HCEXPORT char* hashcash_simple_mint( const char* resource, unsigned bits, long anon_period, char* ext, int compress ); /* convert a stamp into a X-Hashcash: header wrapping lines at line_len * chars. If line_len = 0, does not wrap, just prepends header. * * arguments are: * * stamp -- the stamp to put in the header * line_len -- the number of chars you want to wrap after (or no wrapping * if 0 is given) * lf -- what kind of line feed you want "\r\n" (DOS), "\n" (UNIX), * "\r" (MAC). Actually I think RFC822 requires DOS lf. If * you pass NULL it uses the standard RFC822 lf ("\r\n"). * */ HCEXPORT char* hashcash_make_header( const char* stamp, int line_len, const char* header, char cont, const char* lf ); /* return time field width necessary to express time in sufficient * resolution to avoid premature expiry of stamp minted * for a resource which advertises the given validity period * */ HCEXPORT int hashcash_validity_to_width( long validity_period ); /* count bits of collision in stamp */ HCEXPORT unsigned hashcash_count( const char* stamp ); /* parse stamp into time field and resouce name * max lengths exclude space for trailing \0 * * arguments are: * * stamp -- stamp to parse * vers -- stamp version (currently 0 or 1) * bits -- (used in vers 1 only; with ver 0 returns -1) * utct -- utc time field * utct_max -- max width of utct field (definition is 13 max) * stamp_resource -- resource name from stamp * res_max -- max width of resource name * ext -- extension field * ext_max -- max width of extension field */ /* note: if don't care about ext field pass NULL argument */ /* note: if you pass non-NULL in ext, but *ext = NULL, hashcash_parse * will allocate the space, in this case it is the callers * responsibility to call hashcash_free on ext when finished */ HCEXPORT int hashcash_parse( const char* stamp, int* vers, int* bits, char* utct, int utct_max, char* stamp_resource, int res_max, char** ext, int ext_max ); /* return how many seconds the stamp remains valid for * return value HASHCASH_VALID_FOREVER (value 0) means forever * return value < 0 is error code * codes are: HASHCASH_VALID_IN_FUTURE * HASHCASH_EXPIRED * * arguments are: * * stamp_time -- time field from stamp * validity_period -- how long stamps are valid for * grace_period -- how much clock error to tolerate * now_time -- current time (UTC) */ HCEXPORT long hashcash_valid_for( time_t stamp_time, long validity_period, long grace_period, time_t now_time ); /* simple function calling hashcash_parse, hashcash_count for convenience * * on error returns -ve values: * * HASHCASH_INVALID * HASHCASH_UNSUPPORTED_VERSION, * HASHCASH_WRONG_RESOURCE * HASHCASH_REGEXP_ERROR * HASHCASH_INSUFFICIENT_BITS * * on success returns time: left * * HASHCASH_FOREVER (actually 0) * +ve number being remaining validity time in seconds * * arguments are: * * stamp -- stamp to check * case_flag -- if set true case-sensitive (false case insensitive) * resource -- resource expecting (maybe wildcard, regexp...) * compile -- cache for compiled regexp (caller free afterwards) * re_err -- if get HASHCASH_REGEXP_ERROR this contains error * type -- type of string (TYPE_STR, TYPE_WILD, TYPE_REGEXP) * now_time -- current time (UTC) * validity_period -- how long stamps are valid for * grace_period -- how much clock error to tolerate * required_bits -- how many bits the stamp must have * stamp_time -- returns: parsed and decoded time field from stamp */ /* note: compile argument can be NULL in which case regexps * compilations are not cached (and compile arg does not have to be * hashcash_free'd) */ /* note: it is the callers responsibility to call hashcash_free on * compile when finished if regexps are used */ HCEXPORT int hashcash_check( const char* stamp, int case_flag, const char* resource, void **compile, char** re_err, int type, time_t now_time, long validity_period, long grace_period, int required_bits, time_t* stamp_time ); /* return how many tries per second the machine can do */ HCEXPORT unsigned long hashcash_per_sec( void ); /* estimate how many seconds it would take to mint a stamp of given size */ HCEXPORT double hashcash_estimate_time( int b ); /* expected number of tries to mint stamp of size b */ /* note: equivalent to pow( 2, b ) but without using libm */ HCEXPORT double hashcash_expected_tries( int b ); /* hashcash_resource_match * * For comparing received stamps to addresses the user is willing to * receive email as. * * returns 0 and *err == NULL if no match * returns 0 and *err != NULL if regexp error, regexp explanatory error msg * held in *err * returns 1 on match * * arguments are: * * type -- type of comparison: TYPE_STR (simple string), * TYPE_WILD (wildcard '*'), TYPE_REGEXP * (full regular expression) * * stamp_res -- resource from the stamp being compared * * res -- resource we're comparing against * * compile -- cache for regexp compilation result * (hashcash_free it when you've finished) * * err -- NULL on success, regexp error string if regexp fails */ HCEXPORT int hashcash_resource_match( int type, const char* stamp_res, const char* res, void** compile, char** err ); /* free memory which may beallocated by: * * hashcash_check * hashcash_mint * hashcash_parse * hashcash_simple_mint * hashcash_resource_match */ HCEXPORT void hashcash_free( void* ptr); /* bit of redundancy to avoid needing to distribute multiple header * files with the DLL (this is an excerpt from utct.h) */ #define MAX_UTC 13 /* convert utct string into a time_t * * returns (time_t)-1 on error * * arguments are: * * utct -- utct string to convert * * utc -- if true use UTC, else express in local time zone */ HCEXPORT time_t hashcash_from_utctimestr( const char utct[MAX_UTC+1], int utc ); /* convert time_t into utct string (note utct is always in utc time) * * returns 1 on success, 0 on failure * * arguments are: * * utct -- return utct converted string * * len -- how many digits of utc to use; valid utc widths are: * 6,10,12 (though the code can generate 2,4,6,8,10,12 */ HCEXPORT int hashcash_to_utctimestr( char utct[MAX_UTC+1], int len, time_t t ); /* returns max rate of hashcash / sec using a slower but more accurate * benchmarking method * * arguments are: * * verbose -- verbosity level 0 = no output, 1 = some, 2 = more, 3 = most * core -- core number CORE_ALL (= -1) = all cores, or specific */ /* returns speed of hashcash */ HCEXPORT unsigned long hashcash_benchtest( int verbose, int core ); /* returns current core */ HCEXPORT int hashcash_core(void); /* use specified core */ HCEXPORT int hashcash_use_core(int); /* give name of specified core */ HCEXPORT const char* hashcash_core_name(int); #if defined( __cplusplus ) } #endif #endif hashcash-1.21/sdb.c0000664000076400007640000001502710352071070012533 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #if defined( unix ) || defined(WIN32 ) #include #elif defined( VMS ) #include #endif #include #include #include #include #include #include "types.h" #include "lock.h" #define BUILD_DLL #include "sdb.h" #include "utct.h" #if defined( VMS ) #define unlink(x) delete(x) #endif #if defined( WIN32 ) #define ftruncate chsize #endif #define MAX_UTC 13 /* simple though inefficient implementation of a database function */ static int sdb_insert( DB*, const char* key, const char* val, int* err ); int sdb_open( DB* h, const char* filename, int* err ) { int fd = 0 ; FILE* fp = NULL ; *err = 0; h->file = NULL; if ( filename == NULL ) { return 0; } fd = open( filename, O_RDWR | O_CREAT, S_IREAD | S_IWRITE ); if ( fd == -1 ) { goto fail; } fp = fdopen( fd, "w+" ); if ( fp == NULL ) { goto fail; } h->file = fp; if ( !lock_write( h->file ) ) { goto fail; } strncpy( h->filename, filename, PATH_MAX ); h->filename[PATH_MAX] = '\0'; h->write_pos = 0; return 1; fail: *err = errno; return 0; } int sdb_close( DB* h, int* err ) { if ( h == NULL || h->file == NULL ) { return 0; } if ( fclose( h->file ) == EOF ) { *err = errno; return 0; } *err = 0; return 1; } int sdb_add( DB* h, const char* key, const char* val, int* err ) { *err = 0; if ( h == NULL || h->file == NULL ) { return 0; } if ( strlen( key ) > MAX_KEY ) { return 0; } if ( strlen( val ) > MAX_VAL ) { return 0; } if ( fseek( h->file, 0, SEEK_END ) == -1 ) { goto fail; } if ( fprintf( h->file, "%s %s\n", key, val ) == 0 ) { goto fail; } return 1; fail: *err = errno; return 0; } #define MAX_LINE (MAX_KEY+MAX_VAL) static void sdb_rewind( DB* h ) { rewind( h->file ); h->write_pos = 0; } int sdb_findfirst( DB* h, char* key, int klen, char* val, int vlen, int* err ) { sdb_rewind( h ); return sdb_findnext( h, key, klen, val, vlen, err ); } int sdb_findnext( DB* h, char* key, int klen, char* val, int vlen, int* err ) { char line[MAX_LINE+1] = {0}; int line_len = 0 ; char* fkey = line; char* fval = NULL ; *err = 0; if ( h->file == NULL ) { return 0; } if ( feof( h->file ) ) { return 0; } if ( fgets( line, MAX_LINE, h->file ) == NULL ) { return 0; } line_len = strlen( line ); if ( line_len == 0 ) { return 0; } /* remove unix, DOS, and MAC linefeeds */ if ( line[line_len-1] == '\n' ) { line[--line_len] = '\0'; } if ( line[line_len-1] == '\r' ) { line[--line_len] = '\0'; } if ( line[line_len-1] == '\n' ) { line[--line_len] = '\0'; } fval = strchr( line, ' ' ); if ( fval != NULL ) { *fval = '\0'; fval++; } else { fval = ""; } /* empty */ strncpy( key, fkey, klen ); key[klen] = '\0'; strncpy( val, fval, vlen ); val[vlen] = '\0'; return 1; } static int sdb_cb_notkeymatch( const char* key, const char* val, void* arg, int* err ) { *err = 0; if ( strncmp( key, arg, MAX_KEY ) != 0 ) { return 1; } return 0; } static int sdb_cb_keymatch( const char* key, const char* val, void* arg, int* err ) { *err = 0; if ( strncmp( key, arg, MAX_KEY ) == 0 ) { return 1; } return 0; } int sdb_del( DB* h, const char* key, int* err ) { return sdb_updateiterate( h, (sdb_wcallback)sdb_cb_notkeymatch, (void*)key, err ); } int sdb_lookup( DB* h, const char* key, char* val, int vlen, int* err ) { char fkey[MAX_KEY+1] = {0}; return sdb_callbacklookup( h, sdb_cb_keymatch, (void*)key, fkey, MAX_KEY, val, vlen, err ); } int sdb_lookupnext( DB* h, const char* key, char* val, int vlen, int* err ) { char fkey[MAX_KEY+1] = {0}; return sdb_callbacklookupnext( h, sdb_cb_keymatch, (void*)key, fkey, MAX_KEY, val, vlen, err ); } int sdb_callbacklookup( DB* h, sdb_rcallback cb, void* arg, char* key, int klen, char* val, int vlen, int* err ) { rewind( h->file ); return sdb_callbacklookupnext( h, cb, arg, key, klen, val, vlen, err ); } int sdb_callbacklookupnext( DB* h, sdb_rcallback cb, void* arg, char* key, int klen, char* val, int vlen, int* err ) { char fkey[MAX_KEY+1] = {0}; while ( sdb_findnext( h, fkey, MAX_KEY, val, vlen, err ) ) { if ( cb( fkey, val, arg, err ) ) { strncpy( key, fkey, klen ); key[klen] = '\0'; return 1; } } return 0; } int sdb_insert( DB* db, const char* key, const char* val, int* err ) { int res = 0 ; *err = 0; res = ftell( db->file ); if ( res < 0 ) { goto fail; } db->read_pos = res; if ( fseek( db->file, db->write_pos, SEEK_SET ) == -1 ) { goto fail; } if ( fprintf( db->file, "%s %s\n", key, val ) == 0 ) { goto fail; } res = ftell( db->file ); if ( res < 0 ) { goto fail; } db->write_pos = res; if ( fseek( db->file, db->read_pos, SEEK_SET ) == -1 ) { goto fail; } return 1; fail: *err = errno; return 0; } int sdb_updateiterate( DB* h, sdb_wcallback cb, void* arg, int* err ) { int found = 0, res = 0; char fkey[MAX_KEY+1] = {0}; char fval[MAX_VAL+1] = {0}; for ( found = sdb_findfirst( h, fkey, MAX_KEY, fval, MAX_VAL, err ); found; found = sdb_findnext( h, fkey, MAX_KEY, fval, MAX_VAL, err ) ) { if ( cb( fkey, fval, arg, err ) ) { if ( *err ) { goto fail; } if ( !sdb_insert( h, fkey, fval, err ) ) { goto fail; } } else if ( *err ) { goto fail; } } res = ftruncate( fileno( h->file ), h->write_pos ); return 1; fail: return 0; } /* higher level functions */ int hashcash_db_open( DB* db, const char* db_filename, int* err ) { int my_err; if ( !err ) { err = &my_err; } if ( !sdb_open( db, db_filename, err ) ) { return 0; } fgetc( db->file ); /* try read to trigger EOF */ if ( feof( db->file ) ) { if ( !sdb_add( db, PURGED_KEY, "700101000000", err ) ) { return 0; } } rewind( db->file ); return 1; } int hashcash_db_in( DB* db, char* token, char *period, int* err ) { int in_db = 0; int my_err; if ( !err ) { err = &my_err; } *err = 0; in_db = sdb_lookup( db, token, period, MAX_UTC, err ); if ( *err ) { return 0; } return in_db; } int hashcash_db_add( DB* db, char* token, char *period, int* err ) { if ( !sdb_add( db, token, period, err ) ) { return 0; } return 1; } int hashcash_db_close( DB* db, int* err ) { if ( !sdb_close( db, err ) ) { return 0; } return 1; } hashcash-1.21/hashcash.exp0000664000076400007640000001132310411051106014104 0ustar adamadamLFR$DuL.edataédNJ@@.debug$SC2@BFR$D##  !"hashcash.dllhashcash_benchtesthashcash_checkhashcash_corehashcash_core_namehashcash_counthashcash_db_addhashcash_db_closehashcash_db_inhashcash_db_openhashcash_estimate_timehashcash_expected_trieshashcash_freehashcash_from_utctimestrhashcash_make_headerhashcash_minthashcash_parsehashcash_per_sechashcash_resource_matchhashcash_simple_minthashcash_to_utctimestrhashcash_use_corehashcash_valid_forhashcash_validity_to_widthhashcash_versionsdb_addsdb_callbacklookupsdb_callbacklookupnextsdb_closesdb_delsdb_findfirstsdb_findnextsdb_lookupsdb_lookupnextsdb_opensdb_updateiterate  $()´,*¸0+¼4,À 8-Ä <.È @/Ì D0Ð H1ÔL2ØP3ÜT4àX5ä\6è`7ìd8ðh9ôl:øp;üt<x=|>€? „@ˆAŒBC ”D !˜E$"œF(# G,$¤H0%¨I4&¬J8'°K<( hashcash.exp(  Microsoft (R) LINK@comp.id \ÿÿ.edataszName†rgpv(rgszName´rgwOrd@$N00001“$N00002¦$N00003µ$N00004Ã$N00005Ö$N00006å$N00007õ$N00008$N00009$N00010'$N00011>$N00012V$N00013d$N00014}$N00015’$N00016 $N00017¯$N00018À$N00019Ø$N00020í$N00021$N00022$N00023)$N00024D$N00025U$N00026]$N00027p$N00028‡$N00029‘$N00030™$N00031§$N00032´$N00033¿$N00034Î$N00035×(7K[l¡¹Òáû 0B[q‰œ°Ì_sdb_addÞò _sdb_del$2>NX_hashcash_benchtest_hashcash_check_hashcash_core_hashcash_core_name_hashcash_count_hashcash_db_add_hashcash_db_close_hashcash_db_in_hashcash_db_open_hashcash_estimate_time_hashcash_expected_tries_hashcash_free_hashcash_from_utctimestr_hashcash_make_header_hashcash_mint_hashcash_parse_hashcash_per_sec_hashcash_resource_match_hashcash_simple_mint_hashcash_to_utctimestr_hashcash_use_core_hashcash_valid_for_hashcash_validity_to_width_hashcash_version_sdb_callbacklookup_sdb_callbacklookupnext_sdb_close_sdb_findfirst_sdb_findnext_sdb_lookup_sdb_lookupnext_sdb_open_sdb_updateiterate__imp__hashcash_mint__imp__hashcash_core__imp__hashcash_resource_match__imp__hashcash_valid_for__imp__sdb_add__imp__sdb_del__imp__hashcash_check__imp__sdb_lookup__imp__sdb_close__imp__hashcash_simple_mint__imp__hashcash_db_close__imp__sdb_lookupnext__imp__sdb_findfirst__imp__hashcash_make_header__imp__hashcash_from_utctimestr__imp__sdb_callbacklookup__imp__sdb_findnext__imp__sdb_updateiterate__imp__hashcash_db_in__imp__sdb_open__imp__hashcash_benchtest__imp__hashcash_count__imp__hashcash_per_sec__imp__hashcash_parse__imp__hashcash_version__imp__hashcash_estimate_time__imp__hashcash_db_add__imp__hashcash_to_utctimestr__imp__hashcash_validity_to_width__imp__hashcash_use_core__imp__sdb_callbacklookupnext__imp__hashcash_db_open__imp__hashcash_expected_tries__imp__hashcash_free__imp__hashcash_core_name__IMPORT_DESCRIPTOR_hashcash__NULL_IMPORT_DESCRIPTORhashcash_NULL_THUNK_DATAhashcash-1.21/hashcash.c0000664000076400007640000012604110411046565013553 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ /* hashcash mint and verification * * hashcash is payment in burnt CPU cycles by calculating n-bit * partial hash collisions * * see: http://www.hashcash.org/ * */ #if defined( THINK_C ) #include #include #elif defined( unix ) || defined( VMS ) || defined( __APPLE__ ) #include #elif defined ( WIN32 ) #include #endif #include #include #include #include #include #include #include #include #include "sdb.h" #include "utct.h" #include "random.h" #include "hashcash.h" #include "libfastmint.h" #include "sstring.h" #include "getopt.h" #if defined( OPENSSL ) #include #define SHA1_ctx SHA_CTX #define SHA1_Final( x, digest ) SHA1_Final( digest, x ) #define SHA1_DIGEST_BYTES SHA_DIGEST_LENGTH #else #include "sha1.h" #endif #define EOK 0 /* no error */ #define EINPUT -1 /* error invalid input */ #define EXIT_UNCHECKED 2 /* don't consider the token valid because */ /* there are aspects of the token that */ /* were not checked */ #define EXIT_ERROR 3 #define MAX_PERIOD 11 /* max time_t = 10 plus 's' */ #define MAX_HDR 256 #define MAX_LINE 1024+MAX_EXT+1 /* WARNING: the *PRINTF macros assume a coding style which always uses * braces {} around conditional code -- unfortunately there is no * portable way to do vararg macros or functions which call other * vararg functions */ #define QQE (!quiet_flag) #define PPE (!out_is_tty||quiet_flag) #define VVE (verbose_flag) #define QQ if (QQE) #define PP if (PPE) #define VV if (VVE) #define QPRINTF QQ fprintf #define PPRINTF PP fprintf #define VPRINTF VV fprintf #define QPUTS(f,str) QQ { fputs(str,f); } #define PPUTS(f,str) PP { fputs(str,f); } #define VPUTS(f,str) VV { fputs(str,f); } #define HDR_LINE_LEN 80 char *read_header( FILE* f, char** s, int* slen, int* alloc, char* a, int alen ); int read_eof( FILE* fp, char* a ); void chomplf( char* token ); void trimspace( char* token ); void die( int err ); void die_msg( const char* ); void usage( const char* ); int parse_period( const char* aperiod, long* resp ); double report_speed( int bits, double* time_est, int display ); #if defined( WIN32 ) #define LINEFEED "\r\n" #else #define LINEFEED "\n" #endif /* avoid needing to export stolower from hashcash.dll */ void mystolower( char* str ); #define stolower mystolower typedef struct { char* str; void* regexp; /* compiled regexp */ int type; int case_flag; long validity; long grace; long anon; int width; int bits; int over; } ELEMENT; typedef struct { int num; int max; ELEMENT* elt; } ARRAY; void db_open( DB* db, const char* db_filename ); void db_purge_arr( DB* db, ARRAY* purge_resource, int purge_all, long purge_period, time_t now_time, long validity_period, long grace_period ); int db_purge( DB* db, ARRAY* purge_resource, int purge_all, long purge_period, time_t now_time, long validity_period, long grace_period, int verbose, int* err ); int db_in( DB* db, char* token, char *period ); void db_add( DB* db, char* token, char *token_utime ); void db_close( DB* db ) ; void array_alloc( ARRAY* array, int num ); void array_push( ARRAY* array, const char *str, int type, int case_flag, long validity, long grace, long anon, int width, int bits, int over ); #define array_num( a ) ( (a)->num ) int bit_cmp( const void* ap, const void* bp ); void array_sort( ARRAY* array, int(*cmp)(const void*, const void*) ); #define hc_est_time(b) ( hashcash_expected_tries(b) / \ (double)hashcash_per_sec() ) int quiet_flag; int verbose_flag; int out_is_tty; int in_is_tty; int progress_callback(int percent, int largest, int target, double counter, double expected, void* user); #define PURGED_KEY "last_purged" int main( int argc, char* argv[] ) { long validity_period = 28*TIME_DAY; /* default validity period: 28 days */ long purge_validity_period = 28*TIME_DAY; /* same default */ long grace_period = 2*TIME_DAY; /* default grace period: 2 days */ char period[ MAX_PERIOD+1 ], utcttime[ MAX_UTC+1 ]; const char* db_filename = "hashcash.sdb"; int boundary, err, anon_flag = 0, over_flag = 0, grace_flag = 0; long anon_period = 0, anon_random = 0 ; int default_bits = 20; /* default 20 bits */ int count_bits, claimed_bits = 0, bits = 0; int check_flag = 0, case_flag = 0, hdr_flag = 0; int width_flag = 0, left_flag = 0, speed_flag = 0, utc_flag = 0; int bits_flag = 0, str_type = TYPE_WILD; /* default to wildcard match */ int validity_flag = 0, db_flag = 0, yes_flag = 0, purge_flag = 0; int mint_flag = 0, ignore_boundary_flag = 0, name_flag = 0, res_flag = 0; int auto_version = 0, version_flag = 0, checked = 0, comma = 0; char header[ MAX_HDR+1 ] = { 0 }; char header2[ MAX_HDR+1 ] = { 0 }; char* header_wrapped = NULL; int hdr_len, hdr2_len, token_found = 0, token2_found = 0, hdrs_found = 0; char token[ MAX_TOK+1 ] = { 0 }, token_resource[ MAX_RES+1 ] = { 0 }; char *new_token = NULL ; char line_arr[ MAX_LINE+1 ] = { 0 }, *line = line_arr; int line_max = MAX_LINE, line_alloc = 0; char ahead[ MAX_LINE+1 ] = { 0 } , *ext = NULL, *junk = NULL; ARRAY purge_resource, resource, tokens, args; clock_t start = 0, end = 0, tmp = 0; long purge_period = 0, valid_for = HASHCASH_INVALID, time_period = 0 ; int purge_all = 0; int multiple_validity = 0, multiple_bits = 0, multiple_resources = 0; int multiple_grace = 0, accept = 0, parsed = 0; char token_utime[ MAX_UTC+1 ] = { 0 } ; /* time token created */ time_t real_time = time(0); /* now, in UTCTIME */ time_t now_time = real_time; time_t token_time = 0, expiry_time = 0; int time_width_flag = 0; /* -z option, default 6 YYMMDD */ int compress = 0; /* fast by default */ int inferred_time_width = 0, time_width = 6; /* default YYMMDD */ int core = 0, res = 0, core_flag = 0; double tries_taken = 0, taken = 0, tries_expected = 0, time_est = 0; int opt = 0, vers = 0, db_opened = 0, i = 0, t = 0, tty_info = 0; int in_headers = 0, skip = 0, over = 0; char *re_err = NULL; ELEMENT* ent = NULL; DB db; hashcash_callback callback = NULL; #if defined( THINK_C ) argc = ccommand(&argv); #endif out_is_tty = isatty( fileno( stdout ) ); in_is_tty = isatty( fileno( stdin ) ) && !feof( stdin ); quiet_flag = 0; verbose_flag = 0; token_resource[0] = '\0'; token[0] = '\0'; opterr = 0; array_alloc( &resource, 32 ); array_alloc( &purge_resource, 32 ); array_alloc( &tokens, 32 ); array_alloc( &args, 32 ); while ( (opt=getopt(argc, argv, "-a:b:cde:f:g:hij:klmnop:qr:st:uvwx:yz:CEMO:PSVXZ:")) >0 ) { switch ( opt ) { case 'a': anon_flag = 1; if ( !parse_period( optarg, &anon_period ) ) { usage( "error: -a invalid period arg" ); } break; case 'b': if ( bits_flag ) { multiple_bits = 1; } bits_flag = 1; if ( strcmp( optarg, "default" ) == 0 ) { bits = default_bits; } else if ( optarg[0] == '-' || optarg[0] == '+' ) { bits = atoi( optarg+1 ); if ( bits < 0 ) { usage( "error: -b invalid bits arg" ); } if ( optarg[0] == '-' ) { bits = -bits; } bits = default_bits + bits; if ( bits < 0 ) { bits = 0; } } else { /* absolute */ bits = atoi( optarg ); if ( bits < 0 ) { usage( "error: -b invalid bits arg" ); } } if ( bits > SHA1_DIGEST_BYTES * 8 ) { usage( "error: max collision with sha1 is 160 bits" ); } break; case 'C': case_flag = 1; break; case 'c': check_flag = 1; break; case 'd': db_flag = 1; break; case 'e': if ( validity_flag ) { multiple_validity = 1; } validity_flag = 1; if ( !parse_period( optarg, &validity_period ) || validity_period < 0 ) { usage( "error: -e invalid validity period" ); } break; case 'E': str_type = TYPE_REGEXP; break; case 'f': db_filename = strdup( optarg ); break; case 'g': if ( grace_flag ) { multiple_grace = 1; } grace_flag = 1; if ( optarg[0] == '-' ) { usage( "error: -g -ve grace period not valid" ); } if ( !parse_period( optarg, &grace_period ) ) { usage( "error: -g invalid grace period format" ); } break; case 'h': usage( "" ); break; case 'i': ignore_boundary_flag = 1; break; case 'j': array_push( &purge_resource, optarg, str_type, case_flag, 0, 0, 0, 0, 0, 0 ); over_flag = 0; break; case 'k': purge_all = 1; break; case 'l': left_flag = 1; break; case 'n': name_flag = 1; break; case 'o': over_flag = 1; break; case 'O': core_flag = 1; core = atoi( optarg ); res = hashcash_use_core( core ); if ( res < 1 ) { usage( res == -1 ? "error: -O no such core\n" : "error: -O core does not work on this platform" ); } break; case 'm': mint_flag = 1; if ( !bits_flag ) { bits_flag = 1; bits = default_bits; } break; case 'M': str_type = TYPE_WILD; break; case 'p': if ( purge_flag ) { usage("error: only one -p flag per call"); } purge_flag = 1; if ( strcmp( optarg, "now" ) == 0 ) { purge_period = 0; } else if ( !parse_period( optarg, &purge_period ) || purge_period < 0 ) { usage( "error: -p invalid purge interval" ); } purge_validity_period = validity_period; break; case 'P': callback = progress_callback; break; case 'q': quiet_flag = 1; break; case 1: case 'r': if ( opt == 'r' ) { if ( res_flag ) { multiple_resources = 1; } res_flag = 1; } /* infer appropriate time_width from validity period */ if ( validity_flag && !time_width_flag && !inferred_time_width ) { time_width = hashcash_validity_to_width( validity_period ); if ( !time_width ) { die_msg( "error: -ve validity period" ); } inferred_time_width = time_width; } array_push( &args, optarg, str_type, case_flag, validity_period, grace_period, anon_period, time_width, bits, 0 ); if ( opt == 'r' ) { array_push( &resource, optarg, str_type, case_flag, validity_period, grace_period, anon_period, time_width, bits, over_flag ); } else if ( opt == 1 ) { array_push( &tokens, optarg, str_type, case_flag, validity_period, grace_period, anon_period, time_width, bits, 0 ); } over_flag = 0; break; case 's': speed_flag = 1; break; case 'S': str_type = TYPE_STR; break; case 't': if ( optarg[0] == '-' || optarg[0] == '+' ) { if ( !parse_period( optarg, &time_period ) ) { usage( "error: -t invalid relative time format" ); } now_time += time_period; } else { now_time = hashcash_from_utctimestr( optarg, utc_flag ); if ( now_time == (time_t)-1 ) { usage( "error: -t invalid time format" ); } } break; case 'u': utc_flag = 1; break; case 'v': verbose_flag = 1; break; case 'V': version_flag = 1; break; case 'w': width_flag = 1; break; case 'x': ext = strdup( optarg ); /* deprecate old -x flag */ /* hdr_flag = 1; */ /* sstrncpy( header, optarg, MAX_HDR ); */ break; case 'X': hdr_flag = 1; sstrncpy( header, "X-Hashcash: ", MAX_HDR ); sstrncpy( header2, "Hashcash: ", MAX_HDR ); break; case 'y': yes_flag = 1; break; case 'z': time_width_flag = 1; time_width = atoi( optarg ); if ( time_width != 6 && time_width != 10 && time_width != 12 ) { usage( "error: -z invalid time width: must be 6, 10 or 12" ); } break; case 'Z': compress = atoi( optarg ); /* temporary work around for bug discovered in 1.15, disable -Z2 */ /* if ( compress > 1 ) { compress = 1; } */ break; case '?': fprintf( stderr, "error: unrecognized option -%c", optopt ); usage( "" ); break; case ':': fprintf( stderr, "error: option -%c missing argument", optopt ); usage( "" ); break; default: usage( "error with argument processing" ); break; } } if ( validity_flag && !time_width_flag && !inferred_time_width ) { time_width = hashcash_validity_to_width( resource.elt[0].validity ); if ( !time_width ) { die_msg( "error: -ve validity period" ); } inferred_time_width = time_width; } if ( array_num( &args ) == 0 && verbose_flag && name_flag + left_flag + width_flag + check_flag + mint_flag + purge_flag + speed_flag < 1 ) { auto_version = 1; version_flag = 1; verbose_flag = 0; } if ( version_flag ) { QPRINTF( stderr, "Version: hashcash-%s\n", HASHCASH_VERSION_STRING ); PPRINTF( stdout, "%s\n", HASHCASH_VERSION_STRING ); if ( auto_version ) { exit( EXIT_FAILURE ); } } if ( mint_flag + check_flag > 1 ) { usage( "can only specify one of -m, -c" ); } if ( mint_flag && ( name_flag || width_flag || left_flag ) ) { usage( "can not use -n, -w or -l with -m" ); } if ( mint_flag + check_flag + name_flag + left_flag + width_flag + db_flag+ bits_flag + res_flag + purge_flag + speed_flag + version_flag == 0 ) { usage( "must specify at least one of -m, -c, -d, -n, -l-, -w, -b, -r, -p or -s"); } if ( quiet_flag ) { verbose_flag = 0; } /* quiet overrides verbose */ if ( speed_flag && check_flag ) { speed_flag = 0; } /* ignore speed */ if ( purge_flag ) { db_open( &db, db_filename ); db_opened = 1; db_purge_arr( &db, &purge_resource, purge_all, purge_period, now_time, validity_flag ? purge_validity_period : 0, grace_period ); if ( mint_flag + check_flag + name_flag + left_flag + width_flag + bits_flag + res_flag + speed_flag == 0 ) { db_close( &db ); /* just -p we're done */ exit( EXIT_SUCCESS ); } } if ( mint_flag || speed_flag ) { /* mint stamp */ if ( mint_flag ) { /* no resources given, read them from stdin */ if ( array_num( &args ) == 0 ) { if ( in_is_tty ) { fprintf( stderr, "enter resources to mint stamps for one per line:\n" ); } while ( !feof(stdin) ) { line[0] = '\0'; junk = fgets( line, MAX_LINE, stdin ); chomplf( line ); trimspace( line ); if ( line[0] != '\0' ) { array_push( &args, line, 0, 0, validity_period, grace_period, anon_period, time_width, bits, 0 ); } } } } /* integrate the bench test */ if ( verbose_flag && speed_flag && !mint_flag ) { if ( core_flag ) { hashcash_benchtest( 3, core ); } else { hashcash_benchtest( 3, -1 ); } exit( EXIT_SUCCESS ); /* don't actually calculate it */ } if ( !verbose_flag && speed_flag && mint_flag ) { QPRINTF( stderr, "core: %s\n", hashcash_core_name( hashcash_core() ) ); QPRINTF( stderr, "speed: %ld collision tests per second\n", hashcash_per_sec() ); PPRINTF( stdout, "%ld\n", hashcash_per_sec() ); QPRINTF( stderr, "compression: %d\n", compress ); } if ( verbose_flag || ( speed_flag && !bits_flag ) ) { QPRINTF( stderr, "core: %s\n", hashcash_core_name( hashcash_core() ) ); QPRINTF( stderr, "speed: %ld collision tests per second\n", hashcash_per_sec() ); if ( speed_flag && !bits_flag && !mint_flag ) { PPRINTF( stdout, "%ld\n", hashcash_per_sec() ); } QPRINTF( stderr, "compression: %d\n", compress ); if ( speed_flag && !bits_flag && !mint_flag ) { exit( EXIT_SUCCESS ); /* don't actually calculate it */ } } if ( speed_flag && bits_flag && !mint_flag ) { tries_expected = report_speed( bits, (PPE) ? &time_est : 0, !quiet_flag ); PPRINTF( stdout, "%.0f\n", time_est ); exit( EXIT_SUCCESS ); /* don't actually calculate it */ } for ( i = 0; i < array_num( &args ); i++ ) { start = clock(); ent = &(args.elt[i]); tries_expected = report_speed( ent->bits, NULL, verbose_flag||speed_flag ); if ( !ent->case_flag ) { stolower( ent->str ); } err = hashcash_mint( now_time, ent->width, ent->str, ent->bits, ent->anon, &new_token, &anon_random, &tries_taken, ext, compress, callback, NULL ); end = clock(); switch ( err ) { case HASHCASH_INVALID_TOK_LEN: die_msg( "error: maximum collision with sha1 is 160 bits" ); case HASHCASH_RNG_FAILED: die_msg( "error: random number generator failed" ); case HASHCASH_INVALID_TIME: die_msg( "error: outside unix time Epoch" ); case HASHCASH_TOO_MANY_TRIES: die_msg( "error: failed to find collision in 2^96 tries!" ); case HASHCASH_EXPIRED_ON_CREATION: die_msg( "error: -a token may expire on creation" ); case HASHCASH_INVALID_VALIDITY_PERIOD: die_msg( "error: negative validity period" ); case HASHCASH_INVALID_TIME_WIDTH: die_msg( "error: invalid time width" ); case HASHCASH_INTERNAL_ERROR: die_msg( "error: internal error" ); case HASHCASH_OK: break; default: die_msg( "error: unknown failure" ); } if ( anon_flag ) { VPRINTF( stderr, "anon period: %ld seconds\n", (long)anon_period ); VPRINTF( stderr, "adding: %ld seconds\n", anon_random ); } VPRINTF( stderr, "tries: %.0f", tries_taken ); VPRINTF( stderr, " %.0f%% of expected \n", tries_taken / tries_expected * 100 ); if ( end < start ) { tmp = end; end = start; start = tmp; } taken = (end-start)/(double)CLOCKS_PER_SEC; VPRINTF( stderr, "time: %.0f seconds\n", taken ); if ( hdr_flag ) { header_wrapped = hashcash_make_header( new_token, HDR_LINE_LEN, header, '\t', LINEFEED ); if ( header_wrapped == NULL ) { /* don't expect this but for security fail closed */ fprintf(stderr, "out of memory\n"); exit( EXIT_FAILURE ); } fprintf( stdout, "%s", header_wrapped ); free( header_wrapped ); } else { fprintf( stdout, "%s%s\n", ((quiet_flag||!out_is_tty)? "" : "hashcash token: "), new_token ); } free( new_token ); } exit( EXIT_SUCCESS ); } else if ( check_flag || name_flag || width_flag || left_flag || bits_flag || res_flag || db_flag ) { /* check token is valid */ trimspace( header ); /* be forgiving about missing space */ hdr_len = strlen( header ); if ( header2[0] ) { trimspace( header2 ); hdr2_len = strlen( header2 ); } /* if no resources given create a kind of NULL entry to carry */ /* the other options along; hashcash_check knows to not check */ /* resource name if resource given is NULL */ if ( array_num( &resource ) == 0 ) { array_push( &resource, NULL, str_type, case_flag, validity_period, grace_period, anon_period, time_width, bits, 0 ); } hdrs_found = 0; tty_info = 0; in_headers = 0; ahead[0] = '\0'; for ( t = 0, token_found = 0, boundary = 0; !boundary; ) { /* read tokens from cmd line args 1st */ if ( t < array_num( &tokens ) ) { sstrncpy( token, tokens.elt[t].str, MAX_TOK ); t++; token_found = 1; } /* if no hdr flag, and were cmd line tokens, we're done */ else if ( !hdr_flag && array_num( &tokens ) > 0 ) { break; } else { if ( !in_headers ) { in_headers = 1; } if ( in_is_tty && !tty_info ) { if ( hdr_flag ) { if ( ignore_boundary_flag ) { QPRINTF( stderr, "enter email headers followed by mail body:\n" ); } else { QPRINTF( stderr, "enter email headers:\n" ); } } else { QPRINTF( stderr, "enter stamps to check one per line:\n" ); } tty_info = 1; } if ( read_eof( stdin, ahead ) ) { break; } read_header( stdin, &line, &line_max, &line_alloc, ahead, MAX_LINE ); if ( line[0] == '\0' ) { continue; } if ( hdr_flag ) { token_found = (strncasecmp( line, header, hdr_len ) == 0); token2_found = header2[0] && (strncasecmp( line, header2, hdr2_len ) == 0); if ( token_found ) { sstrncpy( token, line+hdr_len, MAX_TOK ); hdrs_found = 1; } else if ( token2_found ) { sstrncpy( token, line+hdr2_len, MAX_TOK ); hdrs_found = 1; token_found = 1; } } else { token_found = 1; sstrncpy( token, line, MAX_TOK ); } trimspace( token ); } /* end of stamp finder which results in clean stamp in token */ if ( token_found ) { VPRINTF( stderr, "examining token: %s\n", token ); skip = 0; over = 0; accept = 0; for ( i = 0; i < array_num( &resource ) && !skip; i++ ) { ent = &resource.elt[i]; /* keep going until find non overridden */ if ( over && ent->over ) { if ( multiple_resources ) { VPRINTF( stderr, "checking against resource: %s\n", ent->str ); } QPRINTF( stderr, "no match: overridden resource\n" ); continue; } over = 0; if ( !ent->case_flag ) { stolower( ent->str ); } valid_for = hashcash_check( token, ent->case_flag, ent->str, &(ent->regexp), &re_err, ent->type, now_time, ent->validity, ent->grace, bits_flag ? ent->bits : 0, &token_time ); if ( valid_for < 0 ) { switch ( valid_for ) { /* reasons token is broken, give up */ case HASHCASH_INVALID: QPRINTF( stderr, "skipped: token invalid\n" ); skip = 1; continue; case HASHCASH_UNSUPPORTED_VERSION: QPRINTF( stderr, "skipped: unsupported version\n" ); skip = 1; continue; /* avoid plural reporting if one of something */ /* not possible for match with */ /* other resources in these situations */ /* as will just fail for same reason again */ case HASHCASH_WRONG_RESOURCE: if ( !multiple_resources ) { QPRINTF( stderr, "skipped: token with wrong resource\n" ); skip = 1; continue; } break; case HASHCASH_INSUFFICIENT_BITS: if ( !multiple_bits && bits_flag ) { QPRINTF( stderr, "skipped: token has insufficient bits\n" ); skip = 1; continue; } break; case HASHCASH_EXPIRED: if ( !multiple_validity && check_flag ) { QPRINTF( stderr, "skipped: token expired\n" ); skip = 1; continue; } break; case HASHCASH_VALID_IN_FUTURE: if ( !multiple_grace && check_flag ) { QPRINTF( stderr, "skipped: valid in future\n" ); skip = 1; continue; } break; } } if ( multiple_resources ) { VPRINTF( stderr, "checking against resource: %s\n", ent->str ); } if ( valid_for >= 0 ) { break; } if ( width_flag || left_flag || name_flag ) { accept = 1; } /* reason token failed for this resource */ /* but may succeed with diff resource */ switch ( valid_for ) { /* need non override to continue after this */ /* kind of failure */ case HASHCASH_INSUFFICIENT_BITS: if ( bits_flag ) { QPUTS( stderr, "no match: token has insufficient bits\n" ); over = 1; accept = 0; continue; } break; case HASHCASH_VALID_IN_FUTURE: if ( check_flag ) { QPRINTF( stderr, "no match: valid in future\n" ); over = 1; accept = 0; continue; } break; case HASHCASH_EXPIRED: if ( check_flag ) { QPUTS( stderr, "no match: token expired\n" ); over = 1; accept = 0; continue; } break; case HASHCASH_WRONG_RESOURCE: QPRINTF( stderr, "no match: wrong resource\n" ); accept = 0; continue; /* fatal error stop */ case HASHCASH_REGEXP_ERROR: fprintf( stderr, "regexp error: " ); die_msg( re_err ); break; default: die_msg( "internal error" ); break; } } if ( valid_for >= 0 || accept ) { if ( db_flag ) { if ( !db_opened ) { db_open( &db, db_filename ); db_opened = 1; } if ( db_in( &db, token, token_utime ) ) { QPRINTF( stderr, "skipped: spent token\n" ); valid_for = HASHCASH_SPENT; continue; /* to next token */ } } QPRINTF( stderr, "matched token: %s\n", token ); if ( name_flag || verbose_flag ) { if ( ext ) { free( ext ); ext = NULL; } hashcash_parse( token, &vers, &claimed_bits, utcttime, MAX_UTC, token_resource, MAX_RES, &ext, 0 ); parsed = 1; QPRINTF( stderr, "token resource name: %s\n", token_resource ); PPRINTF( stdout, "%s", token_resource ); } if ( width_flag || verbose_flag ) { count_bits = hashcash_count( token ); if ( !parsed ) { hashcash_parse( token, &vers, &claimed_bits, utcttime, MAX_UTC, token_resource, MAX_RES, &ext, 0 ); parsed = 1; } if ( vers == 1 ) { count_bits = ( count_bits < claimed_bits ) ? 0 : claimed_bits; } QPRINTF( stderr, "token value: %d\n", count_bits ); if ( name_flag || verbose_flag ) { PPUTS( stdout, " " ); } PPRINTF( stdout, "%d", count_bits ); } if ( left_flag || verbose_flag ) { QPRINTF( stderr, "valid: " ); if ( valid_for > 0 ) { QPRINTF( stderr, "for %ld seconds\n", (long)valid_for ); if ( name_flag || width_flag ) { PPUTS( stdout, " " ); } PPRINTF( stdout, "%ld", (long)valid_for ); } else { expiry_time = token_time + validity_period; switch ( valid_for ) { case HASHCASH_VALID_FOREVER: QPUTS( stderr, "forever\n" ); if ( name_flag || width_flag ) { PPUTS( stdout, " " ); } PPUTS( stdout, "0" ); break; case HASHCASH_VALID_IN_FUTURE: QPRINTF( stderr, "in %ld seconds\n", token_time-(now_time+grace_period) ); if ( name_flag || width_flag ) { PPUTS( stdout, " " ); } PPRINTF( stdout, "+%ld", token_time-(now_time+grace_period) ); break; case HASHCASH_EXPIRED: QPRINTF( stderr, "expired %ld seconds ago\n", now_time-(expiry_time+grace_period) ); if ( name_flag || width_flag ) { PPUTS( stdout, " " ); } PPRINTF( stdout, "-%ld", now_time-(expiry_time+grace_period) ); break; default: QPRINTF( stderr, "not valid\n" ); } } } if ( name_flag || width_flag || left_flag ) { PPUTS( stdout, "\n" ); continue; /* to next token */ } if ( db_flag ) { VPUTS( stderr, "database: not double spent\n" ); checked = yes_flag || ( res_flag && bits_flag); if ( checked ) { sprintf( period, "%ld", validity_period ); db_add( &db, token, period ); } } else { checked = yes_flag; } goto leave; /* matched so leave */ } } else if ( hdr_flag && in_headers && !ignore_boundary_flag ) { if ( line[0] == '\0' ) { boundary = 1; } } } if ( !width_flag && !left_flag && !name_flag ) { if ( hdr_flag && !hdrs_found ) { QPRINTF( stderr, "warning: no line matching %s found in input\n", header ); } QPUTS( stderr, "rejected: no valid tokens found\n" ); valid_for = HASHCASH_NO_TOKEN; } leave: if ( width_flag || left_flag || name_flag ) { exit( yes_flag ? EXIT_SUCCESS : EXIT_UNCHECKED ); } QPRINTF( stderr, "check: %s", ( valid_for >= 0 ) ? ( checked ? "ok" : "ok but not fully checked as" ) : "failed" ); if ( valid_for >= 0 ) { if ( !checked ) { if ( !bits_flag ) { comma = 1; QPUTS( stderr, " bits" ); } if ( !check_flag ) { if ( comma ) { QPUTS( stderr, "," ); } comma = 1; QPUTS( stderr, " expiry" ); } if ( !res_flag ) { if ( comma ) { QPUTS( stderr, "," ); } comma = 1; QPUTS( stderr, " resource" ); } if ( !db_flag ) { if ( comma ) { QPUTS( stderr, "," ); } comma = 1; QPUTS( stderr, " database" ); } if ( comma ) { QPUTS( stderr, " not specified" ); } } } QPUTS( stderr, "\n" ); if ( valid_for < 0 ) { exit( EXIT_FAILURE ); } exit( checked ? EXIT_SUCCESS : ( yes_flag ? EXIT_SUCCESS : EXIT_UNCHECKED ) ); } exit( EXIT_ERROR ); /* get here if no functional flags */ return 0; } int progress_callback(int percent, int largest, int target, double counter, double expected, void* user) { static int previous_percent = -1; static int previous_largest; double previous_counter = -1; if ( previous_counter != counter || previous_percent != percent || previous_largest != largest ) { fprintf( stderr, "percent: %.0lf/%.0lf = %d%% [%d/%d bits]\r", counter, expected, percent, largest, target ); previous_percent = percent; previous_largest = largest; previous_counter = counter; } return 1; } int parse_period( const char* aperiod, long* resp ) { int period_len; char last_char; long res = 1; char period_array[MAX_PERIOD+1]; char* period = period_array; if ( period == NULL ) { return 0; } period_len = strlen( aperiod ); if ( period_len == 0 ) { return 0; } last_char = aperiod[period_len-1]; if ( ! isdigit( last_char ) && ! strchr( "YyMdhmsw", last_char ) ) { return 0; } sstrncpy( period, aperiod, MAX_PERIOD ); if ( ! isdigit( last_char ) ) { period[--period_len] = '\0'; } if ( period[0] == '+' || period[0] == '-' ) { if ( period[0] == '-' ) { res = -1; } period++; period_len--; } if ( period_len > 0 ) { res *= atoi( period ); } switch ( last_char ) { case 's': break; case 'm': res *= TIME_MINUTE; break; case 'h': res *= TIME_HOUR; break; case 'd': res *= TIME_DAY; break; case 'w': res *= TIME_DAY*7; break; case 'M': res *= TIME_MONTH; break; case 'y': case 'Y': res *= TIME_YEAR; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; default: return 0; } *resp = res; return 1; } void usage( const char* msg ) { if ( msg ) { fputs( msg, stderr ); fputs( "\n\n", stderr ); } fprintf( stderr, "mint:\t\thashcash [-m] [opts] resource\n" ); fprintf( stderr, "measure speed:\thashcash -s [-b bits]\n" ); fprintf( stderr, "check:\t\thashcash -c [opts] -d -r resource [-e period] [token]\n" ); fprintf( stderr, "purge expired:\thashcash -p now [-k] [-j resource] [-t time] [-u]\n" ); fprintf( stderr, "count bits:\thashcash -w [opts] [token]\n" ); fprintf( stderr, "get resource:\thashcash -n [opts] [token]\n" ); fprintf( stderr, "time remaining:\thashcash -l [opts] -e period [token]\n" ); fprintf( stderr, "\n" ); fprintf( stderr, "\t-b bits\t\tfind or check partial hash collision of length bits\n" ); fprintf( stderr, "\t-d\t\tuse database (for double spending detection)\n" ); fprintf( stderr, "\t-r resource\tresource name for minting or checking token\n" ); fprintf( stderr, "\t-o\t\tprevious resource overrides this resource\n" ); fprintf( stderr, "\t-e period\ttime until token expires\n" ); fprintf( stderr, "\t-g\t\tgrace period for clock skew\n" ); fprintf( stderr, "\t-t time\t\tmodify current time token created at\n" ); fprintf( stderr, "\t-a time\t\tmodify time by random amount in range given\n" ); fprintf( stderr, "\t-u\t\tgive time in UTC instead of local time\n" ); fprintf( stderr, "\t-q\t\tquiet -- suppress all informational output\n" ); fprintf( stderr, "\t-v\t\tprint verbose informational output\n" ); fprintf( stderr, "\t-h\t\tprint this usage info\n" ); fprintf( stderr, "\t-f dbfile\tuse filename dbfile for database\n" ); fprintf( stderr, "\t-j resource\twith -p delete just tokens matching the given resource\n" ); fprintf( stderr, "\t-k\t\twith -p delete all not just expired\n" ); fprintf( stderr, "\t-x ext\t\tput in extension field\n" ); fprintf( stderr, "\t-X\t\toutput with header format 'X-Hashcash: '\n" ); fprintf( stderr, "\t-i\t\twith -X and -c, check msg body as well\n" ); fprintf( stderr, "\t-y\t\treturn success if token is valid but not fully checked\n" ); fprintf( stderr, "\t-z width\twidth of time field 6,10 or 12 chars (default 6)\n" ); fprintf( stderr, "\t-C\t\tmatch resources as case sensitive (default insensitive)\n" ); fprintf( stderr, "\t-S\t\tmatch following resources as text strings\n" ); fprintf( stderr, "\t-W\t\tmatch following resources with wildcards (default)\n" ); fprintf( stderr, "\t-E\t\tmatch following resources as regular expression\n" ); fprintf( stderr, "\t-P\t\tshow progress while searching\n"); fprintf( stderr, "\t-O core\t\tuse specified minting core\n"); fprintf( stderr, "\t-Z n\t\t0 = fast (default), 1 = medium, 2 = small/slow\n"); fprintf( stderr, "examples:\n" ); fprintf( stderr, "\thashcash -mb20 foo # mint 20 bit collision\n" ); fprintf( stderr, "\thashcash -cdb20 -r foo 1:20:040806:foo::831d0c6f22eb81ff:15eae4 # check collision\n" ); fprintf( stderr, "\n" ); fprintf( stderr, "see hashcash (1) man page or http://www.hashcash.org/ for more details.\n" ); fflush( stderr ); exit( EXIT_ERROR ); } typedef struct { time_t expires_before; char now_utime[ MAX_UTC+1 ]; int all; long validity; long grace; ARRAY* resource; } db_arg; /* compile time assert */ #if MAX_UTC > MAX_VAL #error "MAX_UTC must be less than MAX_VAL" #endif static int sdb_cb_token_matcher( const char* key, char* val, void* argp, int* err ) { db_arg* arg = (db_arg*)argp; char token_utime[ MAX_UTC+1 ] = {0}; char token_res[ MAX_RES+1 ] = {0}, lower_token_res[ MAX_RES+1 ] = {0}; char *res = NULL; time_t expires = 0; time_t expiry_period = 0; time_t created = 0; int vers = 0, i = 0, bits = 0, matched = 0, type = 0, case_flag = 0; int lowered = 0 ; void** compile = NULL; char* re_err = NULL; *err = 0; if ( strcmp( key, PURGED_KEY ) == 0 ) { sstrncpy( val, arg->now_utime, MAX_UTC ); return 1; /* update purge time */ } if ( !hashcash_parse( key, &vers, &bits, token_utime, MAX_UTC, token_res, MAX_RES, NULL, 0 ) ) { *err = EINPUT; /* corrupted token in DB */ return 0; } if ( vers != 0 && vers != 1 ) { *err = EINPUT; /* unsupported version number in DB */ return 0; } /* if purging only for given resource */ if ( array_num( arg->resource ) > 0 ) { matched = 0; for ( i = 0; !matched && i < array_num( arg->resource ); i++ ) { res = arg->resource->elt[i].str; type = arg->resource->elt[i].type; case_flag = arg->resource->elt[i].case_flag; compile = &(arg->resource->elt[i].regexp); if ( !case_flag && !lowered ) { strncpy( lower_token_res, token_res, MAX_RES ); stolower( lower_token_res ); lowered = 1; } matched = hashcash_resource_match( type, case_flag ? token_res : lower_token_res, res, compile, &re_err ); if ( re_err != NULL ) { fprintf( stderr, "regexp error: " ); die_msg( re_err ); } } if ( !matched ) { return 1; } /* if it doesn't match, keep it */ } if ( arg->all ) { return 0; } /* delete all regardless of expiry */ if ( val[0] == '0' && val[1] == '\0' ) { return 1; } /* keep forever */ if ( strlen( val ) != strspn( val, "0123456789" ) ) { *err = EINPUT; return 0; } expiry_period = atoi( val ); if ( expiry_period < 0 ) { *err = EINPUT; return 0; } /* corrupted */ created = hashcash_from_utctimestr( token_utime, 1 ); if ( created < 0 ) { *err = EINPUT; return 0; } /* corrupted */ expires = created + (arg->validity ? arg->validity : expiry_period) + arg->grace; if ( expires <= arg->expires_before ) { return 0; } /* delete if expired */ return 1; /* otherwise keep */ } int hashcash_db_purge( DB* db, const char* purge_resource, int type, int case_flag, long validity_period, long grace_period, int purge_all, long purge_period, time_t now_time, int* err ) { ARRAY* purge_resource_arr = NULL; array_alloc( purge_resource_arr, 1 ); array_push( purge_resource_arr, purge_resource, type, case_flag, validity_period, grace_period, 0, 0, 0, 0 ); return db_purge( db, purge_resource_arr, purge_all, purge_period, now_time, validity_period, grace_period, 0, err ); } void db_purge_arr( DB* db, ARRAY* purge_resource, int purge_all, long purge_period, time_t now_time, long validity_period, long grace_period ) { int res, err = HASHCASH_FAIL; res = db_purge( db, purge_resource, purge_all, purge_period, now_time, validity_period, grace_period, verbose_flag, &err ); switch ( res ) { case HASHCASH_INVALID_TIME: die_msg( "error: invalid time argument" ); break; case HASHCASH_FAIL: die( err ); break; case HASHCASH_OK: break; default: die( err ); } } int db_purge( DB* db, ARRAY* purge_resource, int purge_all, long purge_period, time_t now_time, long validity_period, long grace_period, int verbose, int* err ) { time_t last_time = 0 ; char purge_utime[ MAX_UTC+1 ] = {0}; /* time token created */ int ret = 0; db_arg arg; if ( now_time < 0 ) { return HASHCASH_INVALID_TIME; } if ( !sdb_lookup( db, PURGED_KEY, purge_utime, MAX_UTC, err ) ) { return 0; } last_time = hashcash_from_utctimestr( purge_utime, 1 ); if ( last_time < 0 ) { /* not first time, but corrupted */ purge_period = 0; /* purge now */ } if ( !hashcash_to_utctimestr( arg.now_utime, MAX_UTC, now_time ) ) { return HASHCASH_INVALID_TIME; } arg.expires_before = now_time; arg.resource = purge_resource; arg.all = purge_all; arg.validity = validity_period; arg.grace = grace_period; if ( purge_period == 0 || now_time >= last_time + purge_period ) { VPRINTF( stderr, "purging database: ..." ); ret = sdb_updateiterate( db, sdb_cb_token_matcher, (void*)&arg, err ); VPRINTF( stderr, ret ? "done\n" : "failed\n" ); } else { ret = 1; } return ret; } void db_open( DB* db, const char* db_filename ) { int err; if (!hashcash_db_open( db, db_filename, &err )) { die(err); } } int db_in( DB* db, char* token, char *period ) { int err = 0; int res; res = hashcash_db_in( db, token, period, &err ); if ( err ) { die( err ); } return res; } void db_add( DB* db, char* token, char *period ) { int err = 0; if ( !hashcash_db_add( db, token, period, &err ) ) { die( err ); } } void db_close( DB* db ) { int err = 0; if ( !hashcash_db_close( db, &err ) ) { die( err ); } } /* num = start size, auto-grows */ void array_alloc( ARRAY* array, int num ) { array->num = 0; array->max = num; array->elt = malloc( sizeof( ELEMENT ) * num ); if ( array->elt == NULL ) { die_msg( "out of memory" ); } } /* auto-grow array */ void array_push( ARRAY* array, const char *str, int type, int case_flag, long validity, long grace, long anon, int width, int bits, int over ) { if ( array->num >= array->max ) { array->elt = realloc( array->elt, sizeof( ELEMENT) * array->max * 2 ); if ( array->elt == NULL ) { die_msg( "out of memory" ); } array->max *= 2; } array->elt[array->num].regexp = NULL; /* compiled regexp */ array->elt[array->num].type = type; array->elt[array->num].case_flag = case_flag; array->elt[array->num].validity = validity; array->elt[array->num].grace = grace; array->elt[array->num].anon = anon; array->elt[array->num].width = width; array->elt[array->num].bits = bits; array->elt[array->num].over = over; if ( str ) { array->elt[array->num].str = strdup( str ); if ( array->elt[array->num].str == NULL ) { die_msg( "out of memory" ); } } else { array->elt[array->num].str = NULL; } array->num++; } int bit_cmp( const void* ap, const void* bp ) { ELEMENT* a = (ELEMENT*) ap; ELEMENT* b = (ELEMENT*) bp; return a->bits == b->bits ? 0 : ( a->bits < b->bits ? 1 : -1 ); } void array_sort( ARRAY* array, int(*cmp)(const void*, const void*) ) { qsort( array->elt, array->num, sizeof( ELEMENT ), cmp ); } void die( int err ) { const char* str = ""; switch ( err ) { case EOK: exit( EXIT_SUCCESS ); break; case EINPUT: str = "invalid inputs"; break; default: str = strerror( err ); break; } QPRINTF( stderr, "error: %s\n", str ); exit( EXIT_ERROR ); } void die_msg( const char* str ) { QPUTS( stderr, str ); QPUTS( stderr, "\n" ); exit( EXIT_ERROR ); } /* remove unix, DOS and MAC linefeeds from line ending */ void chomplf( char* token ) { int tok_len = strlen(token); if ( token[tok_len-1] == '\n' ) { token[--tok_len] = '\0'; } if ( token[tok_len-1] == '\r' ) { token[--tok_len] = '\0'; } if ( token[tok_len-1] == '\n' ) { token[--tok_len] = '\0'; } } void trimspace( char* token ) { int tok_len = strlen(token); int tok_begin = 0; while ( tok_begin < tok_len && isspace(token[tok_begin]) ) { tok_begin++; } if ( tok_begin > 0 ) { tok_len -= tok_begin; memmove( token, token+tok_begin, strlen(token+tok_begin)+1 ); } while ( tok_len > 0 && isspace(token[tok_len-1]) ) { token[--tok_len] = '\0'; } } double report_speed( int bits, double* time_est, int display ) { double te = 0; double tries_expected = hashcash_expected_tries( bits ); VPRINTF( stderr, "mint: %d bit partial hash collision\n", bits ); VPRINTF( stderr, "expected: %.0f tries (= 2^%d tries)\n", tries_expected, bits ); if ( time_est ) { *time_est = hc_est_time( bits ); } if ( display ) { te = hc_est_time( bits ); QPRINTF( stderr, "time estimate: %.0f seconds", te ); if ( te > TIME_AEON ) { QPRINTF( stderr, " (%.0f aeons)", te / TIME_AEON ); } else if ( te > TIME_YEAR * 10 ) { QPRINTF( stderr, " (%.0f years)", te / TIME_YEAR ); } else if ( te > TIME_YEAR ) { QPRINTF( stderr, " (%.1f years)", te / TIME_YEAR ); } else if ( te > TIME_MONTH ) { QPRINTF( stderr, " (%.1f months)", te / TIME_MONTH ); } else if ( te > TIME_DAY * 10 ) { QPRINTF( stderr, " (%.0f days)", te / TIME_DAY ); } else if ( te > TIME_DAY ) { QPRINTF( stderr, " (%.1f days)", te / TIME_DAY ); } else if ( te > TIME_HOUR ) { QPRINTF( stderr, " (%.0f hours)", te / TIME_HOUR ); } else if ( te > TIME_MINUTE ) { QPRINTF( stderr, " (%.0f minutes)", te / TIME_MINUTE ); } else if ( te > 1 ) { /* already printed seconds */ } else if ( te > TIME_MILLI_SECOND ) { QPRINTF( stderr, " (%.0f milli-seconds)", te / TIME_MILLI_SECOND ); } else if ( te > TIME_MICRO_SECOND ) { QPRINTF( stderr, " (%.0f micro-seconds)", te / TIME_MICRO_SECOND ); } else if ( te > TIME_NANO_SECOND ) { QPRINTF( stderr, " (%.0f nano-seconds)", te / TIME_NANO_SECOND ); } QPUTS( stderr, "\n" ); } return tries_expected; } int read_append( char** s, int* smax, int* alloc, char* append ) { int slen = strlen( *s ); int alen = strlen( append ); if ( slen + alen > *smax ) { if ( *alloc ) { *s = realloc( *s, slen+alen+1 ); } else { *s = malloc( slen+alen+1 ); *alloc = 1; } if ( *s == NULL ) { return 0; } /* out of memory */ } sstrncpy( (*s)+slen, append, alen ); return 1; } char *read_header( FILE* f, char** s, int* smax, int* alloc, char* a, int amax ) { char* junk; (*s)[0] = '\0'; if ( a[0] ) { sstrncpy( *s, a, *smax ); } else { junk = fgets( *s, *smax, f ); chomplf( *s ); } do { a[0] = '\0'; junk = fgets( a, amax, f ); chomplf( a ); if ( a[0] == '\t' || a[0] == ' ') { read_append( s, smax, alloc, a+1 ); } } while ( a[0] == '\t' || a[0] == ' ' ); return *s; } int read_eof( FILE* fp, char* a ) { return feof(fp) && a[0] == '\0'; } void mystolower( char* str ) { if ( !str ) { return; } for ( ; *str; str++ ) { *str = tolower( *str ); } } hashcash-1.21/sstring.h0000664000076400007640000000036610351101612013454 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #define sstrncpy(d,s,l) ((d)[l]='\0',strncpy(d,s,l)) char* sstrtok( const char* str, const char* delim, char** tok, int tok_max, int* tok_len, char** s ); void stolower( char* str ); hashcash-1.21/sha1-hashcash.html0000644000076400007640000001362610351101612015115 0ustar adamadam sha1


NAME

sha1 - Secure Hash Algorithm (version 1) hash function


SYNOPSIS

hash files:

sha1 [ files ]


DESCRIPTION

This is an implementation of the Secure Hash Algorithm version 1 as defined in US Federal Information Processing Standard ``FIPS 180-1''.

This was shipped for convenience with the hashcash tool, but also functions similarly to the widely distributed md5sum utility but using SHA1 instead of MD5 so you may find other uses for it. (Some have argued that MD5 is too small a hash to use for checking files if the attacker can insert chosen files into your distribution as then a birthday attack becomes possible for the master md5sum with work factor of about 2^64, which is a fairly high cost but not infeasible.)


OTHER IMPLEMENTATIONS

This program is fairly compatible with openssl sha1 (sha1(1)) and sha1sum (sha1sum(1)) installed on some unix systems and included with hashcash package so that a sha1 implementation is available for testing and scripting if those packages are missing.


USAGE NOTES

The sha1 program will hash the files given on the command line, or if no files are given it will hash the input fed to it on standard input. The output format is a list of SHA1 hashes in hex followed by the corresponding filenames, one per line.


EXAMPLES

Hashing files

sha1 file1 file2 [...]
Hashes the files listed on the command line and outputs their SHA1 message digests as 40 hexadecimal digits, followed by the filename, one per line.

echo -n abc | sha1
When no files are given, hashes from standard in. The example command hashes the string ``abc'' from standard input. This string is coincidentally one of the test vectors specified in FIPS 180-1 and should output: a9993e364706816aba3e25717850c26c9cd0d89d.

echo -n abc | sha1 -
Equivalent to above. The filename of - means read from standard input.

echo -n abc | sha1 test.txt -
You can also mix filenames and reading from standard input with the - pseudo file. The above command first hashes file test.txt then reads the string abc from standard in.


Verifying Hashcash

The SHA1 hash function is used by hashcash. You can use this sha1 utility to write shell scripts to verify hashcash tokens without using the hashcash command line tool, or just to verify visually. Say you received the following email:

 From: Someone <test@test.invalid>
 To: Adam Back <adam@cypherspace.org>
 Subject: test hashcash
 Date: Thu, 15 Aug 2002 11:12:02 +0000
 X-Hashcash: 0:030626:adam@cypherspace.org:6470e06d773e05a8
 
Then the following command would show visually the size of the
collision:
echo -n 0:030626:adam@cypherspace.org:6470e06d773e05a8 | sha1
 00000000c70db7389f241b8f441fcf068aead3f0

and you can see the leading hex 0s.


LIMITATIONS

Doesn't have the check option that md5sum has. Perhaps I'll add that in a future version. Also doesn't have the binary / text distinction that md5sum introduced for DOS/Windows benefit, nor the output convention signifying text (* after hash). Can't say I've ever seen anyone use that feature though.


EXIT STATUS

sha1 returns success (exit code 0) normally.

If it can't read any of the files you give it on the comamnd line, it instead returns failure (exit code 1).


AUTHOR

Written by Adam Back <adam@cypherspace.org>


SEE ALSO

md5sum(1), sha1(1), sha1sum(1), hashcash(1), http://www.hashcash.org/, http://www.itl.nist.gov/fipspubs/fip180-1.htm

hashcash-1.21/debian/0000775000076400007640000000000010351101612013027 5ustar adamadamhashcash-1.21/debian/compat0000664000076400007640000000000210351101612014225 0ustar adamadam4 hashcash-1.21/debian/README0000664000076400007640000000120210351101612013702 0ustar adamadamThe original hashcash package includes a program called sha1-hashcash. However, since Debian already includes sha1sum in the coreutils program, sha1-hashcash is not distributed in this Debian package. This distribution also includes Kyle Hasselbacher's hashcash-sendmail and hashcash-request scripts, which can be found in the examples directory. The hashcash-sendmail script can be used to integrate hashcash into MUAs that do not support hashcash natively, such as mutt. hashcash-request can be used to pre-request hashcash tokens for future use. The current version of both scripts are considered ALPHA quality. Use at your own risk. hashcash-1.21/debian/docs0000664000076400007640000000005010351101612013675 0ustar adamadamhashcash.txt hashcash.html test-msg.txt hashcash-1.21/debian/rules0000664000076400007640000000377110351101612014114 0ustar adamadam#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # GNU copyright 1997 to 1999 by Joey Hess. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 CFLAGS = -Wall -g INSTALL = /usr/bin/install ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_PROGRAM += -s endif configure: configure-stamp configure-stamp: dh_testdir # Add here commands to configure the package. touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. $(MAKE) hashcash $(MAKE) docs #/usr/bin/docbook-to-man debian/hashcash.sgml > hashcash.1 touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. -$(MAKE) distclean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/hashcash. #$(MAKE) install INSTALL_PATH=$(CURDIR)/debian/hashcash/usr/bin \ # MAN_INSTALL_PATH=$(CURDIR)/debian/hashcash/usr/share/man/man1 $(INSTALL) hashcash $(CURDIR)/debian/hashcash/usr/bin $(INSTALL) -m 644 hashcash.1 $(CURDIR)/debian/hashcash/usr/share/man/man1 # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs CHANGELOG dh_installdocs dh_installexamples # dh_install # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_python # dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure hashcash-1.21/debian/dirs0000664000076400007640000000003310351101612013707 0ustar adamadamusr/bin usr/share/man/man1 hashcash-1.21/debian/watch0000664000076400007640000000041310351101612014056 0ustar adamadam# Example watch control file for uscan # Rename this file to "watch" and then you can run the "uscan" command # to check for upstream updates and more. # Site Directory Pattern Version Script version=2 http://hashcash.org/source/hashcash-(.*)\.tgz debian uupdate hashcash-1.21/debian/hashcash.examples0000664000076400007640000000001210351101612016342 0ustar adamadamcontrib/* hashcash-1.21/debian/changelog0000664000076400007640000000401410351101612014700 0ustar adamadamhashcash (1.00-1) unstable; urgency=low * New upstream release. * This release adds the new stamp format. (closes: 239157) * Update of Kyle Hasselbacher's scripts. * Add link to the hashcash-sendmail page in copyright file and in hashcash-sendmail.txt. * New license from upstream. * Add rule to debian/rules to rebuild documentation if needed. * Apply patch from Justin to fix segfault when checking future version stamps. -- Hubert Chan Sun, 8 Aug 2004 00:09:33 -0400 hashcash (0.32-1) unstable; urgency=low * New upstream release. * Updated copyright file to add new request by author. * Change upstream website in copyright file to reflect new address. * Add Kyle Hasselbacher's hashcash-sendmail and hashmail-request scripts (ALPHA) to examples directory. -- Hubert Chan Wed, 26 May 2004 23:47:13 -0400 hashcash (0.28-4) unstable; urgency=low * Remove beginning "a" from package description. * Fixed typos in changelog, and improved consistency. * Re-add fip180-1.txt to source package. (Not needed for binary package.) All FIPS are public domain worldwide, as per discussion on debian-legal. http://lists.debian.org/debian-legal/2004/debian-legal-200403/msg00028.html -- Hubert Chan Thu, 4 Mar 2004 15:42:31 -0500 hashcash (0.28-3) unstable; urgency=low * Don't install sha1 or sha1.1. Debian already has sha1sum. * Remove fip180-1.txt -- copyright status uncertain. * Fix the copyright file to make it clear that Debian does not specifically make the same pledges that upstream does. -- Hubert Chan Fri, 27 Feb 2004 21:16:19 -0500 hashcash (0.28-2) unstable; urgency=low * Bump policy version to 3.6.1. * Add note to copyright file about getopt being GPL licensed. -- Hubert Chan Sun, 22 Feb 2004 21:32:22 -0500 hashcash (0.28-1) unstable; urgency=low * Initial Release. (closes: 234743) -- Hubert Chan Sat, 27 Dec 2003 15:23:30 -0500 hashcash-1.21/debian/copyright0000664000076400007640000000166710351101612014774 0ustar adamadamThis package was debianized by Hubert Chan on Sat, 27 Dec 2003 15:23:30 -0500. It was downloaded from http://www.hashcash.org/ Upstream Author: Adam Back Copyright: The source contains getopt.c and getopt.h, which are copyright by the FSF, and licensed under the GPL. On Debian systems, the GPL can be found at /usr/share/common-licenses/GPL. The source contains fip180-1.txt, written by the United States government, and is in the public domain. The hashcash-sendmail and hashcash-request programs in the contrib directory are copyright Kyle Hasselbacher, and licensed under the GPL. The rest of the software is licensed under your choice of (in the upstream author's preference): - public domain - BSD - LGPL - GPL On Debian systems, the BSD license can be found at /usr/share/common-licenses/BSD, the LGPL at /usr/share/common-licenses/LGPL, and the GPL at /usr/share/common-licenses/GPL. hashcash-1.21/debian/control0000664000076400007640000000101710351101612014431 0ustar adamadamSource: hashcash Section: mail Priority: optional Maintainer: Hubert Chan Build-Depends: debhelper (>= 4.0.0) Standards-Version: 3.6.1 Package: hashcash Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: postage payment scheme for email based on hash calculations Hash cash is a payment scheme that uses CPU cycles as the form of payment. This can be used as a counter-measure for junk email (spam) by using the hash cash token as a proof of payment for each email that you sent. hashcash-1.21/random.h0000664000076400007640000000063310351101612013240 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _random_h ) #define _random_h #ifdef __UNIX__ #include #endif #if defined( __cplusplus ) extern "C" { #endif int random_init( void ); int random_addbytes( void*, size_t ); int random_getbytes( void*, size_t ); int random_rectangular( long top, long* resp ); int random_final( void ); #if defined( __cplusplus ) } #endif #endif hashcash-1.21/CHANGELOG0000664000076400007640000007067010411050735013037 0ustar adamadamhashcash-1.21 - 24-Mar-2005 - Adam Back * fix potential heap overflow bug reported by Andreas Seltenreich hashcash-1.20 - 20-Dec-2005 - Adam Back * fix bug that -w reports using version 0 semantics even with version 1 stamps (was ignoring the version field and so defaulting to version 0 semantics). * rest are fedora core4 related fixes * remove dependency on openssl libcrypto.so.4 -- I compile rpm on rh9 for forwards compatibility, but then on fc4 they have only libcrypto.so.5, so the rpm fails to install with missing dependency. Dependencies are ugly so I built with static link to libcrypto.a instead of dynamic to libcrypto.so the builtin SHA1 and avoid using openssl. * -mcpu is deprecated apparently so I am supposed to use the equivalent -mtune, but that doesn't work on rh9 era gcc compilers so used -march=pentium-mmx instead; I think the result should still work on 486 via Jonathan's exception catcher trapping absense of mmx cpu. * the (mode) syntax for mmx registers is deprecated, apparently we're supposed to use (vector_size) * with fc4 the strncpy builtin is more fussy about char/unsigned char matching. * with fc4 rpmbuild options getting warnings about ignoring fgets return code hashcash-1.19 - 17-Dec-2005 - Adam Back * update to documentation for hashcash_free in hashcash.h at Simon Bohlin's suggestion * fix for -mb0 reported by bas hashcash-1.18 - 05-Jul-2005 - Adam Back * add a simpler minting API to make it easier to mint stamps from VB scripting hashcash-1.17 - 30-Mar-2005 - Adam Back * fix for bug Steve Crook reported with purging and checking at same time. * fix for bug Steve Crook reported in double spend checking. (Was bug in new hashcash_db_in higher level db function). * apply patch from Jonathan Morton which fixes bug triggered when minting large stamps (over 31-bits). * fix printf vulnerability reported by Tavis Ormandy hashcash-1.16 - 15-Jan-2005 - Adam Back * fixed bug with -Z2 (and removed workaround mentioned in 1.15 changelog entry) hashcash-1.15 - 12-Jan-2005 - Adam Back * make "Hashcash:" be accepted as well as "X-Hashcash:" suggestion by Simon Josefsson . This way if/when the X- is dropped from hashcash headers we will not have a backwards compatibility problem. (Well not after version 1.15). * implement the -Z option to compress stamps; in fact the usage changed so -Z takes an argument: 0, 1 or 2. 0 = not compressed, 1 = compressed but not so the counter + padding is split, and 2 = very compressed, but slow. (Due to a late discovered bug 2 is the same as 1 for now until I can fix that.) * added -O x -sv to request benchtest of core x only * make code work with -DOPENSSL, think this slipped during integration of Jonathan's libfastmint as it uses some lower level openssl APIs internally. I fixed it but it might be a bit openssl version specific, if they changed the state fields at any point. (This change coincidentally I think should work around the linking with openssl problem that Hal Finney reported). * add libhashcash.a intermediate target to make hashcash more convenient to link into other software on linux. (A suggestion from Hal Finney who was trying to link to his RPOW system.) hashcash-1.14 - 14-Dec-2004 - Adam Back * make hashcash -cX accept continuation lines starting with space as well as tab * add library function to wrap lines and use it from hashcash command line tool. * fix long vs time_t prototype mismatch that was giving compile errors on BSD; also cleaned up some warnings that can be obtained with gcc -Wall. hashcash-1.13 - 16-Oct-2004 - Adam Back * fix bug where grace period not applied to double-spend db. I think this could allow people to double-spend in the time period after the resulting premature purging and before expiry (which is the grace period) * add new feature where -e can be used with -p to override the expiry given at spend time (that is stored in the double-spend db). Inspired by question from Atom Smasher . * clean up some memory leaks * add hashcash_free function (DLL scenario caller can't rely on having same deallocator to call as library compiled with) * lots more function documentation in hashcash.h for library/DLL users, renamed all exported library functions to start with hashcash_ prefix. * added hashcash_version function. * added callback function to allow user quit (returns HASHCASH_USER_ABORT) and to give progress feedback. * added option -P which uses the callback to show progress. * added -O core option to allow user selection of core. * added small parameter to hashcash (to request small stamps rather than slightly larger fast to generate stamps), and -Z option to turn this on. (In fact it is not implemented yet but want to avoid changing library interface later). hashcash-1.12 - 03-Oct-2004 - Adam Back * make a HASHCASH.DLL on windows using MINGW. hashcash-1.11 - 02-Oct-2004 - Adam Back * _really_ fix trailing ascii(32) (spaces). I have no idea how I decided the 1.10 code fixed it. Must have messed up the test I was using to check it worked. hashcash-1.10 - 01-Oct-2004 - Adam Back * remove trailing ascii(32) (spaces) which padding somehow leaves hashcash-1.09 - 17-Sep-2004 - Adam Back * fix missing space between resource name and width with -cv reported by Panta Admin. * apply cumulative patch of 3 patches from Jonathan Morton. hashcash-1.08 - 12-Sep-2004 - Adam Back * fix bug reported by Panta Admin must have introduced in recent version where with pipe prints stamp twice. * attempt to work around MINGW problem with signals -- somehow it is changing it's mind about whether the MMX core can run from the first call to the 2nd call. But the test result is cached so it is hard to see how this happens. Not clear if this work-around will work as don't have a 486 to test on. (Work around is make single gIsMMXpresent shared between the two mmx cores, should at least result in signal call being used fewer times (max 1 time). Also pass 1 to longjmp.) * give up entirely on conditional make. One Makefile, no funky stuff. Prints info about what you need to do to compile on your platform and goes ahead and compiles with generic anyway. Make new gnu (generic) target for gcc, and normal generic with no gcc specific flags. hashcash-1.07 - 11-Sep-2004 - Adam Back * patch from J H Wilson to initialize a bunch of variables (actually I had to back some of these out to get to compile on non gnu compilers -- some of those structure initializations are gnu extensions I think.) * also J H Wilson one of patch changes was to avoid mmx assembler code clobbering ebx register which is needed for fPIC support. * better randomness on windows using the CAPI rng CryptGenRandom. Still compiles using MINGW ... whee! * made a separate GNUmakefile for gnu make (it takes that one first over Makefile) and a Makefile which is the same but no ifdef stuff which confuses some other makes, and no gnu specific compile options (for x86 anyway). * add gettimeofday timer into entropy to improve randomness on systems which do not have /dev/urandom, and are not windows hashcash-1.06 - 10-Sep-2004 - Adam Back * patch from Justin Guyett to fix unsigned error which would have made libfastmint do something odd if no minter worked. * Makefile changes to compile under MINGW (gnu for windows portability layer which produces win32 exes). new target mingw-exe * #ifdefs to avoid locking on MINGW (seems no lock support!) * #ifdefs to use longjmp instead of siglongjmp, signal instead of sigaction on MINGW * #ifdef to use chsize instead of ftruncate on MINGW hashcash-1.05 - 08-Sep-2004 - Adam Back * and another issue (last I promise!) with case comparison. 1.04 change was good; however flaw in 1.02 means 1.04 minted stamps (resource not canonicalized to lowercase) can falsely fail to verify with 1.02 - 1.00 as those versions presume canonicalized stamp. So we go back to making -C have effect on minting also. With no -C canonicalize to lowercase, with -C use resource as-is. * get rid of -W flag reserved for posix, use -M in it's place. hashcash-1.04 - 07-Sep-2004 - Adam Back * fixed issue with case comparisons -- presumed resources were in lower case at minting -- better to just ignore case during comparison. Then will accept stamps containing upper case or mixed-case resource names. * integrate fastmint_benchtest as -sv option to hashcash. * introduce COPT as well as CFLAGS as optimization subset of flags passed with CFLAGS to nested make -- suits RPM hashcash-1.03 - 07-Sep-2004 - Adam Back * remove TARGET_ARCH again, let's keep things simple. Just use CFLAGS period. * figured out what GENTOO ebuild is doing, changed ebuild file * remove recursion from hashcash_fastmint * start using TARGET_ARCH in Makefile, hope it's portable; it's an attempt to be more GENTOO ebuild friendly * Makefile change: when PACKAGE is defined build default target, otherwise echo the target info as before * removed a bunch of old code replaced by fastmint, got rid of CHROMATIX define * fix following errors reported by Atom Smasher * fix width measuring bug with v0 stamps * fix resource read from stdin bug * use GNU getopt source always -- getopt on BSD behaves differently (the getopt source is smart -- it comments itself out on machines with GNU_LIBRARY so using it always is not a problem) * bug: should send the time estimate to stderr, and the stamp to stdout * -z width flag not properly error checked to enforce UTCTIME restrictions (only valid widths 6, 10 or 12 digits) * update LICENSE to note you end up with some GNU GPL taint from getopt on systems without POSIX getopt which probably forces you to use GPL as I link against it. Unless someone wants to re-write the getopt or can point me at a public domain replacement. In particular this includes BSD and MACH (OSX) and as before windows. * apply patch from Jonathan Morton with following fixes: * fix bug in mmx assembler code exposed by integration * add generic target in Makefile * increase benchmark work factor to improve accuracy of results (as not used at run-time) * new hashcash_quickbench() * changes to hashcash_quickbench() to give faster timing on linux x86 which has low resolution clock() (1/100th sec vs 1usec on OSX). (It was taking ~ 0.6sec on linux x86, code takes 0.1 sec now which is less noticeable) * related to above studiously avoided calling hc_per_sec() which invokes hashcash_quickbench() -- seems a shame to burn 1/10th sec in default mint creation path if user typically doesn't care about the info about how long it took. Now you have to give -s or -v to get speed info when minting. * fix case sensitivity bug reported by Atom Smasher * add CPL option back to LICENSE file * add make targets for different processors * remove 2nd call of hashcash_benchtest (supposed to call hc_per_second which caches not hashcash_per_second) * enable static selection of fastmint cores, disable run time benchtest * applied fastmint fix patch from Jonathan Morton and re-enabled fastmint (remove -DCHROMATIX in Makefile to disable) hashcash-1.02 - 11-Aug-2004 - Adam Back * minor documentation stuff (put back ref to sha1-hashcash in hashcash.pod manpage) * add back requests to LICENSE file hashcash-1.01 - 08-Aug-2004 - Adam Back * fold in patches from by Hubert Chan and Justin Guyett to clean up some stuff and fix minor bugs. * another couple of minor bug fixes. hashcash-1.00 - 07-Aug-2004 - Adam Back * increment version number, 1.x to reflect move to version 1 stamp format * explicit bits field in token (helps people who want to prevalidate header and parse, and who want to know what the intended bits were vs how lucky the sender got); new stamp size definition is min( counted_bits, explicit bits field ) * no : in resource field to make easier to parse eg with cut, awk etc * new extension field * reclaimed -x to use for passing eXtension data (old -x no longer available, use -X, which has string fixed to X-Hashcash) * put /dev/urandom macro for MAC from Jonathan Morton so we use /dev/urandom on MAC * copy in and adjust Makefile for Jonathan Morton's optimized minter (need to integrate) * copy in Jonathan Morton's COMPACT option (method B vs method A from fips-180-1, uses less registers) for libsha1.c * copy in next rev of minter breaks 4megahashes/sec barrier on 3.06Ghz P4 -- disabled at present until some stuff gets fixed and we figure out rpm/deb package portability * added back v0 read support (but still only generates v1) * pr5: first attempt at integrating libfastmint (some bug in my integration code, broken so far) * (pr6: libfastmint integ still not working) * pr6: made X-Hashcash header accepatance case insensitive * pr7: also reject tokens with count_bits < claimed_bits (as previous logic of setting bits = min( count_bits, claimed_bits) necessary to avoid people getting lucky * use clock() instead of wall time * expand max stamp size out to cope with 10KB extension fields * update man page and usage with v1 stuff * and release as 1.00 ready for Hubert Chan to package for the imminent debian release hashcash-0.33 - 13-Apr-2004 - Adam Back * allow wild card without @ sign if there is no @ sign in pattern hashcash-0.32 - 09-Apr-2004 - Adam Back * documentation fixes * change multiple regexp behavior; previous algorithm only allowed higher overrides; need to support both higher and lower overrides. This also required introducing -o option to join regexps which are set intersections where otherwise risk of uninteded override occuring and mail being rejected as spent or insufficent bits. Now revert to lexical order most specific regexp first. * wrote test script test.sh * fix a few minor bugs uncovered by above test script * -c now means check date * allow -n etc with -X * introduced -b relative to default way of specifying bits * -b is no optional, if want token fully checked, but can give -b default; or new relative to default -b +0. hashcash-0.31 - 01-Apr-2004 - Adam Back * final 0.x version (v0 format) release before 1.x version (v1 format) (bug fixes / maintenance only afterwards on 0.x version) * remove -O3 from Makefile, use -O instead as fails on HPUX or sun. * fix some out of date usage stuff in hashcash man page. * disable timing loop unless timing needed * fix multiple reciept bug in -cX/-cx reported by Junior Ang . If you receive a mail multiple times because you are on the receipt list multiple times, there will be multiple hashcash headers for you. In this case it is necessary to examine the first matching, non-spent stamp. The bug was previous versions stopped on the first matching stamp and then failed because it was spent. Need to keep going and check later also matching stamps until find one which is not spent. * rationalize command line args further. No implied -m , more things that are awkward to implement but not that useful are disallowed. * change purge operation to use read-write operations in the same sdb file rather than creating a temporary file. This makes locking easier and is also aesthetically nicer. * add flock(2) database file write locking, and change creation logic to use open(2) to avoid creation db race-condition also. * make resource string case insensitive by default to match email semantics; add -C option to force case sensitivity if desired (email addresses are converted to and stored in lower case, so you have to both mint and verify with case sensitivity turned on to make use of case sensitivity) * support minting multiple resources with multiple command line args. Also if no resources given on command line, read resources from stdin. * support supplying multiple email addresses, for people who want to accept as multiple addresses. * support multiple resources on purging also. * support multiple tokens with check mode as cmd line args, if none given as args, read tokens from stdin; if -X/-x read from cmd line args, then from stdin as email (matching stamp headers skipping stamp headers) * rename default simple database to hashcash.sdb (.sdb extension), to distinguish from planned support for better database. * fix bug in PPUTS didn't match PPRINTF * fixup -l, -w, -n so they support multiple tokens also * made use of -b optional (get the default on mint & check) * added "-b default" to specify default number of bits with -s (otherwise no way to measure the default speed without specifying the number of bits -- and when this can change over time it would be inconvenient for scripting to have to separately obtain this) * added support for wildcard email addresses with '*' wildcard marker. '*' before '@' does not match '@', '*' after '@' does not match '.'. And both email addresses must contain @ sign and same number of '.' separated sub domains as wildcard address. Wildcard matching is the new default. Use -S to get plain string match. Can turn back on with -W. * increased size of random string to reduce chance of collisions between users. Now negligible chance of collision with typical token sizes. * added support for regexps. Can work from POSIX library or BSD regexp library. Use -E to get regexps. Input is always in POSIX syntax (specials are not quoted to have special action; are quoted to have plain meaning). If using BSD library still give input in POSIX syntax, it's converted to BSD internally. * implement highest matching semantics. Ensures that eg -c -b10 *@bar.invalid -b15 adam@bar.invalid will not accept a 10 bit token for adam@bar.invalid. (This is done by sorting resources highest bits required first and accepting only the first highest matching resource.) * change arg parsing so -b, -e, -g, -z, -E, -W, -S, apply to the following resources and tokens, and can be changed for later resources/tokens with tokens and args interspersed. Means you have to give these args before the resource/token or you will get defaults. hashcash-0.30 - 04-Mar-2004 - Adam Back * make -cX check multiple X-Hashcash lines until it finds the right one. Bug reported by Kyle Hasselbacher . hashcash-0.29 - 04-Mar-2004 - Adam Back * fix prototype mismatch of function hashcash_check in hashcash.h vs libhc.c which caused compile failure on openBSD and freeBSD * change to make it compile on MAC OSX (need to recognize OSX and treat as unix like for headers to include) * change to make it compile on solaris 9 (and POSIX / SVR systems in general). Turns out putenv(3) is more portable than setenv(3) / unsetenv(3) * update sha1 man page to name it sha1-hashcash to avoid collision with openssl sha1(1). By default with openssl there is not actually any program named sha1, rather sha1 is a subprogram of openssl invoked "openssl sha1". However I think it may still be possible to compile that as a separate program or symlink sha1 to openssl to get that. * update sha1 man pages to refer to the other common sha1 implementations. * update LICENSE request hashcash-0.28 - 18-Sep-2003 - Adam Back * fix bug with timezone handling reported by Joris Bontje (turns out utctime function is redundant and wrong; time(2) gives time in UTCTime (doh)). Reason for confusion is ctime is in local time, so converts UTC time into local time even if you don't want that. But c-library time handling functions are really not designed for converting back from local to UTCTime when represented as a time_t. Changed a bunch of time related stuff to fix this up. * add -g flag for grace period suggested by Marc Lehmann to avoid undue rejection of hashcash stamps because of clock skew (or timezone / daylight savings time off by one errors). Default for email is 2 days (48 hours) fast or slow. * change validity period default to 28 days; previous default was forever which did not match recommended email default * add -z flag to explicitly specify the width in chars of the time field. This flag deprecates and overrides the -m -e way to implicitly specify date field width. As before the default is 6 chars (date only in YYMMDD). hashcash-0.27 - 11-Sep-2003 - Adam Back * add space char after 'X-Hashcash:' header that you get with -X option * add LICENSE file hashcash-0.26 - 26-Jun-2003 - Adam Back * accidentally folded in some non-standard VERBOSE output I was playing with. This change makes it really standard output comparable to FIPS 180-1 appendices. * added a version number -- use -V to display it. Also -v (lowercase) by itself with no other arguments displays the version umber also. * bug fix for quiet output (when not going to a pipe, still need to see output!) * update everything to point at http://www.hashcash.org now we have the domain courtesy of it's previous owner Ashish Gulhati (at no charge -- he declined my offer to pay for it and instead gave it to me!) * made a man page for sha1 utility also. * add -X option which is a shorthand version of -x 'X-Hashcash:' * remove leading and trailing blanks from hashcash tokens to make them more robustly survive email transit. hashcash-0.25 - 15-May-2003 - Adam Back * fix VERBOSE trace output of sha1 implementation to match FIPS 180-1 standard test vector examples (Note: this no impact on the result, just allows direct comparison to the test vectors in 180-1). The FIPS 180-1 VERBOSE output got broken when I unrolled the rotates in the round function. * remove the automatic turning on of quiet mode if the output is a pipe per note from Simon Josefsson about how that can be inconvenient for scripting hashcash-0.24 - 15-Mar-2003 - Adam Back * fix minor error which made hashcash_expected_tries a tiny amount less efficient than realised hashcash-0.23 - 15-Mar-2003 - Adam Back * fix error in libhc would always fail in hashcash_check reported by Alan Barclay ; note this bug does not affect the hashcash command line tool as it does not use this API * minor Makefile changes hashcash-0.22 - 16-Aug-2002 - Adam Back * fix uninitialized counter string in hashcash_per_sec, bug reported by Mathias Langer hashcash-0.21 - 15-Aug-2002 - Adam Back * fix compile warnings with VC++ 6. hashcash-0.20 - 13-Aug-2002 - Adam Back * change format to include version number * change collision to be on 0^k string instead of on SHA1(date:resource) hashcash-0.19 - 25-Apr-2002 - Adam Back * fix bug reported that minting always returns failure * fix erroneous logic involving validation of validity periods hashcash-0.18 - 23-Mar-2002 - Adam Back * fix token parsing to allow resource names with :s in them (eg. urls etc) * change hashcash_mint API a bit * add programming APIs for all other functions except double spend db, and move library code to libhc.c hashcash-0.17 - 23-Mar-2002 - Adam Back * add programming API for hashcash_mint * separate hashcash tool program from hashcash library code * remove trailing 'Z' option for times -- adds no value hashcash-0.16 - 15-Mar-2002 - Adam Back * minor clean up stuff: * allow longer collision strings for compatibility with future versions * reject collision strings with non-printable and non ascii chars (< ascii(32) and > ascii(127) * make install now installs man page * fix VMS compile warning * fix bad portability assumption about time_t being signed * simple VMS scripting build script hashcash-0.15 - 14-Mar-2002 - Adam Back * deal with unix, DOS, and mac lf endings (rather than just unix) * fix bug reported by Doc.Cypher * modifications to compile under VC++ 6.0 on win32 * add -i feature hashcash-0.14 - 13-Mar-2002 - Adam Back * revamped flags and arguments * lots of new options and features * more safe error reporting (avoid risk of false positives) * fix all warnings * make compile on machines with 64 bit longs * make compile on IRIX * make Makefile more portable * more reliable timing strategy * remove separate endian.c -- put endian stuff in libsha1.c * create man page * update documentation * re-implement SDB simple database to have a more standard interface to facilitate plugging in gdb etc * import random number interface to /dev/urandom hashcash-0.13 - 03-Mar-2002 - Adam Back * fix bug introduced in 0.12 * update documentation hashcash-0.12 - 03-Mar-2002 - Adam Back * add option to verify resource name given on command line matches resource name in collision * also more compatible with 0.10 command line as a result * add option to print resource name parsed from collision hashcash-0.11 - 02-Mar-2002 - Adam Back * fix buffer overflow reported by Max Greenius * add -v mode suggested by Michael Shinn * add interactive mode instead of usage as default, another suggestion from Michael Shinn * add explicit -h for usage * add -q mode for even quieter output (batch like mode) * add -x option to take collision to verify from stdin * update the documentation in readme.txt to reflect new format and options * add install target to makefile to install binaries for hashcash and sha1 in /usr/local/bin, another suggestion from Michael Shinn hashcash-0.10 - 20-Feb-2002 - Michael Shinn * change output format slightly to make it easier to recognize the collision amongst the verbose output hashcash-0.09 - 25-May-2001 - Adam Back * clean up sha1.h * add simple makefile * fix bug in endian.h with linux redhat62 * change format to use : delimiters * changed old version numers to be two digit * standardised on bsd indentation hashcash-0.08 - 28-Feb-2000 - Adam Back * support for openSSL sha1 (-DOPENSSL) hashcash-0.07 - 08-Dec-1997 - Adam Back * bug fix in SHA1 code hashcash-0.06 - 07-May-1997 - Andy Dustman * bug fix + SPARSE compile option for library use hashcash-0.05 - 07-Mar-1997 - Adam Back * made mods as suggested by Andy Dustman to avoid collision collisions on larger collision lengths previous method was bugged hashcash-0.04 - 31-Mar-1997 - Adam Back * no description hashcash-0.03 - 30-Mar-1997 - Adam Back * put in fast SHA1, and other mods giving a 4x speedup * plus a few bug fixes hashcash-0.02 - 28-Mar-1997 - Chris Kuethe * added minimal macintosh support (Think C) hashcash-0.01 - 27-Mar-1997 - Adam Back * first version hashcash-1.21/fastmint_mmx_standard_1.c0000664000076400007640000005147710352046637016615 0ustar adamadam#include #include #include "libfastmint.h" #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) typedef int mmx_d_t __attribute__ ((vector_size (8))); typedef int mmx_q_t __attribute__ ((vector_size (8))); #endif int minter_mmx_standard_1_test(void) { /* This minter runs only on x86 and AMD64 hardware supporting MMX - and will only compile on GCC */ #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) return (gProcessorSupportFlags & HC_CPU_SUPPORTS_MMX) != 0; #endif /* Not an x86 or AMD64, or compiler doesn't support MMX or GNU assembly */ return 0; } /* Define low-level primitives in terms of operations */ /* #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) */ #define XOR(a,b) ( (mmx_d_t) __builtin_ia32_pxor( (mmx_q_t) a, (mmx_q_t) b) ) #define AND(a,b) ( (mmx_d_t) __builtin_ia32_pand( (mmx_q_t) a, (mmx_q_t) b) ) #define ANDNOT(a,b) ( (mmx_d_t) __builtin_ia32_pandn( (mmx_q_t) b, (mmx_q_t) a) ) #define OR(a,b) ( (mmx_d_t) __builtin_ia32_por( (mmx_q_t) a, (mmx_q_t) b) ) #define ADD(a,b) ( __builtin_ia32_paddd(a,b) ) #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) static inline mmx_d_t S(int n, mmx_d_t X) { mmx_d_t G = {} ; asm ("movq %[x],%[g]\n\t" "pslld %[sl],%[x]\n\t" "psrld %[sr],%[g]\n\t" "por %[g],%[x]" : [g] "=y" (G), [x] "=y" (X) : "[x]" (X), [sl] "g" (n), [sr] "g" (32-n) ); return X; } #endif /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ #define F1( B, C, D ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) /* #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F2( B, C, D ) ( \ F = XOR(B,C), \ XOR(F,D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ /* #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) */ #define F3( B, C, D ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) /* #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F4(B,C,D) F2(B,C,D) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) */ #define Wf(W,t) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) #define Wfly(W,t) ( (t) < 16 ? (W)[t] : Wf(W,t) ) /* #define ROUND(t,A,B,C,D,E,Func,K,W) \ E = ADD(E,K); \ F = S(5,A); \ E = ADD(F,E); \ F = Wfly(W,t); \ E = ADD(F,E); \ F = Func(B,C,D); \ E = ADD(F,E); \ B = S(30,B); */ #define ROUND_F1_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[d], %%mm5" /* begin F1(B,C,D) */ \ "\n\t pxor %[c], %%mm5" \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pand %[b], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t pxor %[d], %%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F1(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t pslld $30, %[b]" \ "\n\t psrld $2, %%mm5" \ "\n\t paddd %[Wt],%[e]" /* sum W[t] to E */ \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E) \ : [Wt] "m" ((W)[t]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F1_u(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[d], %%mm5" /* begin F1(B,C,D) */ \ "\n\t pxor %[c], %%mm5" \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pand %[b], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t pxor %[d], %%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F1(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t movq %[Wt_3],%%mm7" /* begin Wf(t) */ \ "\n\t movq %[Wt_8],%%mm6" \ "\n\t pxor %[Wt_14],%%mm7" \ "\n\t pxor %[Wt_16],%%mm6" \ "\n\t pslld $30, %[b]" \ "\n\t pxor %%mm6,%%mm7" \ "\n\t movq %%mm7,%%mm6" \ "\n\t pslld $1, %%mm7" \ "\n\t psrld $31, %%mm6" \ "\n\t psrld $2, %%mm5" \ "\n\t por %%mm6,%%mm7" \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum Wf(t) to E */ \ "\n\t movq %%mm7,%[Wt]" /* write back Wf(t) to W[t] */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E), [Wt] "=m" ((W)[t]) \ : [Wt_3] "m" ((W)[t-3]), [Wt_14] "m" ((W)[t-14]), [Wt_8] "m" ((W)[t-8]), [Wt_16] "m" ((W)[t-16]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F2_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[b], %%mm5" /* begin F2(B,C,D) */ \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pxor %[c], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t pxor %[d], %%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F2(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t pslld $30, %[b]" \ "\n\t psrld $2, %%mm5" \ "\n\t paddd %[Wt],%[e]" /* sum W[t] to E */ \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E) \ : [Wt] "m" ((W)[t]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F2_u(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[b], %%mm5" /* begin F2(B,C,D) */ \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pxor %[c], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t pxor %[d], %%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F2(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t movq %[Wt_3],%%mm7" /* begin Wf(t) */ \ "\n\t movq %[Wt_8],%%mm6" \ "\n\t pxor %[Wt_14],%%mm7" \ "\n\t pxor %[Wt_16],%%mm6" \ "\n\t pslld $30, %[b]" \ "\n\t pxor %%mm6,%%mm7" \ "\n\t movq %%mm7,%%mm6" \ "\n\t pslld $1, %%mm7" \ "\n\t psrld $31, %%mm6" \ "\n\t psrld $2, %%mm5" \ "\n\t por %%mm6,%%mm7" \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum Wf(t) to E */ \ "\n\t movq %%mm7,%[Wt]" /* write back Wf(t) to W[t] */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E), [Wt] "=m" ((W)[t]) \ : [Wt_3] "m" ((W)[t-3]), [Wt_14] "m" ((W)[t-14]), [Wt_8] "m" ((W)[t-8]), [Wt_16] "m" ((W)[t-16]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F3_n(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[d], %%mm5" /* begin F3(B,C,D) */ \ "\n\t movq %[d], %%mm6" \ "\n\t por %[c], %%mm5" \ "\n\t pand %[c], %%mm6" \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pand %[b], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t por %%mm6,%%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F3(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t pslld $30, %[b]" \ "\n\t psrld $2, %%mm5" \ "\n\t paddd %[Wt],%[e]" /* sum W[t] to E */ \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E) \ : [Wt] "m" ((W)[t]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F3_u(t,A,B,C,D,E,K,W) \ asm ( \ "\n\t movq %[d], %%mm5" /* begin F3(B,C,D) */ \ "\n\t movq %[d], %%mm6" \ "\n\t por %[c], %%mm5" \ "\n\t pand %[c], %%mm6" \ "\n\t movq %[a], %%mm7" /* begin S(5,A) */ \ "\n\t pand %[b], %%mm5" \ "\n\t pslld $5, %%mm7" \ "\n\t por %%mm6,%%mm5" \ "\n\t movq %[a], %%mm6" \ "\n\t paddd %%mm5,%[e]" /* sum F3(B,C,D) to E */ \ "\n\t psrld $27, %%mm6" \ "\n\t paddd %[k], %[e]" /* sum K to E */ \ "\n\t por %%mm6,%%mm7" \ "\n\t movq %[b], %%mm5" /* begin S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum S(5,A) to E */ \ "\n\t movq %[Wt_3],%%mm7" /* begin Wf(t) */ \ "\n\t movq %[Wt_8],%%mm6" \ "\n\t pxor %[Wt_14],%%mm7" \ "\n\t pxor %[Wt_16],%%mm6" \ "\n\t pslld $30, %[b]" \ "\n\t pxor %%mm6,%%mm7" \ "\n\t movq %%mm7,%%mm6" \ "\n\t pslld $1, %%mm7" \ "\n\t psrld $31, %%mm6" \ "\n\t psrld $2, %%mm5" \ "\n\t por %%mm6,%%mm7" \ "\n\t por %%mm5,%[b]" /* complete S(30,B) */ \ "\n\t paddd %%mm7,%[e]" /* sum Wf(t) to E */ \ "\n\t movq %%mm7,%[Wt]" /* write back Wf(t) to W[t] */ \ : [a] "+y" (A), [b] "+y" (B), [c] "+y" (C), [d] "+y" (D), [e] "+y" (E), [Wt] "=m" ((W)[t]) \ : [Wt_3] "m" ((W)[t-3]), [Wt_14] "m" ((W)[t-14]), [Wt_8] "m" ((W)[t-8]), [Wt_16] "m" ((W)[t-16]), [k] "m" (K) \ : "mm5", "mm6", "mm7" ); #define ROUND_F4_u ROUND_F2_u #define ROUND_F4_n ROUND_F2_n #define ROUNDu(t,A,B,C,D,E,Func,K) \ if((t) < 16) { \ ROUND_##Func##_n(t,A,B,C,D,E,K,W); \ } else { \ ROUND_##Func##_u(t,A,B,C,D,E,K,W); \ } #define ROUNDn(t,A,B,C,D,E,Func,K) \ ROUND_##Func##_n(t,A,B,C,D,E,K,W); \ #define ROUND5( t, Func, K ) \ ROUNDu( t + 0, A, B, C, D, E, Func, K );\ ROUNDu( t + 1, E, A, B, C, D, Func, K );\ ROUNDu( t + 2, D, E, A, B, C, Func, K );\ ROUNDu( t + 3, C, D, E, A, B, Func, K );\ ROUNDu( t + 4, B, C, D, E, A, Func, K ); #if defined(MINTER_CALLBACK_CLEANUP_FP) #undef MINTER_CALLBACK_CLEANUP_FP #endif #define MINTER_CALLBACK_CLEANUP_FP __builtin_ia32_emms() unsigned long minter_mmx_standard_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { #if (defined(__i386__) || defined(__AMD64__)) && defined(__GNUC__) && defined(__MMX__) MINTER_CALLBACK_VARS; unsigned long iters = 0 ; int n = 0, t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; mmx_d_t vBitMaskHigh = {} , vBitMaskLow = {} ; register mmx_d_t A = {} , B = {} , C = {} , D = {} , E = {} ; mmx_d_t MA = {} , MB = {} ; mmx_d_t W[80] = {} ; mmx_d_t H[5] = {} , pH[5] = {} ; mmx_d_t K[4] = {} ; uInt32 *Hw = (uInt32*) H; uInt32 *pHw = (uInt32*) pH; uInt32 IA = 0 , IB = 0 ; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X = (unsigned char*) W; unsigned char *output = (unsigned char*) block; *best = 0; /* Splat Kn constants into MMX-style array */ ((uInt32*)K)[0] = ((uInt32*)K)[1] = K1; ((uInt32*)K)[2] = ((uInt32*)K)[3] = K2; ((uInt32*)K)[4] = ((uInt32*)K)[5] = K3; ((uInt32*)K)[6] = ((uInt32*)K)[7] = K4; /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } ((uInt32*) &vBitMaskLow )[0] = bitMask1Low ; ((uInt32*) &vBitMaskLow )[1] = bitMask1Low ; ((uInt32*) &vBitMaskHigh)[0] = bitMask1High; ((uInt32*) &vBitMaskHigh)[1] = bitMask1High; maxBits = 0; /* Copy block and IV to vectorised internal storage */ /* Assume little-endian order, as we're on x86 or AMD64 */ for(t=0; t < 16; t++) { X[t*8+ 0] = X[t*8+ 4] = output[t*4+3]; X[t*8+ 1] = X[t*8+ 5] = output[t*4+2]; X[t*8+ 2] = X[t*8+ 6] = output[t*4+1]; X[t*8+ 3] = X[t*8+ 7] = output[t*4+0]; } for(t=0; t < 5; t++) { Hw[t*2+0] = Hw[t*2+1] = pHw[t*2+0] = pHw[t*2+1] = IV[t]; } /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-2; iters += 2) { /* Encode iteration count into tail */ /* Iteration count is always 2-aligned, so only least-significant character needs multiple lookup */ /* Further, we assume we're always little-endian */ X[(((tailIndex - 1) & ~3) << 1) + (((tailIndex - 1) & 3) ^ 3) + 0] = p[(iters & 0x3e) + 0]; X[(((tailIndex - 1) & ~3) << 1) + (((tailIndex - 1) & 3) ^ 3) + 4] = p[(iters & 0x3e) + 1]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(((tailIndex - 2) & ~3) << 1) + (((tailIndex - 2) & 3) ^ 3) + 0] = X[(((tailIndex - 2) & ~3) << 1) + (((tailIndex - 2) & 3) ^ 3) + 4] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(((tailIndex - 3) & ~3) << 1) + (((tailIndex - 3) & 3) ^ 3) + 0] = X[(((tailIndex - 3) & ~3) << 1) + (((tailIndex - 3) & 3) ^ 3) + 4] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(((tailIndex - 4) & ~3) << 1) + (((tailIndex - 4) & 3) ^ 3) + 0] = X[(((tailIndex - 4) & ~3) << 1) + (((tailIndex - 4) & 3) ^ 3) + 4] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(((tailIndex - 5) & ~3) << 1) + (((tailIndex - 5) & 3) ^ 3) + 0] = X[(((tailIndex - 5) & ~3) << 1) + (((tailIndex - 5) & 3) ^ 3) + 4] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(((tailIndex - 6) & ~3) << 1) + (((tailIndex - 6) & 3) ^ 3) + 0] = X[(((tailIndex - 6) & ~3) << 1) + (((tailIndex - 6) & 3) ^ 3) + 4] = p[(iters >> 30) & 0x3f]; } } /* Force compiler to flush and reload MMX registers */ asm volatile ( "nop" : : : "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "memory" ); /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { /* Populate W buffer */ for(t=16; t < 32; t += 4) { asm volatile ( /* Use pairs of adjacent MMX registers to build four nearly-independent chains */ "\n\t movq -128(%[w]),%%mm0" "\n\t movq -120(%[w]),%%mm2" "\n\t movq -112(%[w]),%%mm4" "\n\t movq -104(%[w]),%%mm6" "\n\t pxor %%mm4, %%mm0" "\n\t pxor %%mm6, %%mm2" "\n\t pxor -96(%[w]),%%mm4" "\n\t pxor -88(%[w]),%%mm6" "\n\t pxor -64(%[w]),%%mm0" "\n\t pxor -56(%[w]),%%mm2" "\n\t pxor -48(%[w]),%%mm4" "\n\t pxor -40(%[w]),%%mm6" "\n\t pxor -24(%[w]),%%mm0" "\n\t pxor -16(%[w]),%%mm2" /* 0(%[w]) is not yet valid! */ "\n\t movq %%mm0, %%mm1" "\n\t movq %%mm2, %%mm3" "\n\t pslld $1, %%mm0" "\n\t psrld $31, %%mm1" "\n\t pslld $1, %%mm2" "\n\t psrld $31, %%mm3" "\n\t por %%mm1, %%mm0" "\n\t por %%mm3, %%mm2" /* ...now it is */ "\n\t pxor -8(%[w]),%%mm4" "\n\t pxor %%mm0, %%mm6" "\n\t movq %%mm4, %%mm5" "\n\t movq %%mm6, %%mm7" "\n\t pslld $1, %%mm4" "\n\t psrld $31, %%mm5" "\n\t pslld $1, %%mm6" "\n\t psrld $31, %%mm7" "\n\t por %%mm5, %%mm4" "\n\t por %%mm7, %%mm6" "\n\t movq %%mm0, 0(%[w])" "\n\t movq %%mm2, 8(%[w])" "\n\t movq %%mm4,16(%[w])" "\n\t movq %%mm6,24(%[w])" : /* no outputs */ : [w] "r" (W+t) : "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "memory" ); } A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; ROUNDn( 0, A, B, C, D, E, F1, K[0] ); ROUNDn( 1, E, A, B, C, D, F1, K[0] ); ROUNDn( 2, D, E, A, B, C, F1, K[0] ); ROUNDn( 3, C, D, E, A, B, F1, K[0] ); ROUNDn( 4, B, C, D, E, A, F1, K[0] ); ROUNDn( 5, A, B, C, D, E, F1, K[0] ); ROUNDn( 6, E, A, B, C, D, F1, K[0] ); if(tailIndex == 52) { ROUNDn( 7, D, E, A, B, C, F1, K[0] ); ROUNDn( 8, C, D, E, A, B, F1, K[0] ); ROUNDn( 9, B, C, D, E, A, F1, K[0] ); ROUNDn(10, A, B, C, D, E, F1, K[0] ); ROUNDn(11, E, A, B, C, D, F1, K[0] ); } pH[0] = A; pH[1] = B; pH[2] = C; pH[3] = D; pH[4] = E; } /* Set up working variables */ A = pH[0]; B = pH[1]; C = pH[2]; D = pH[3]; E = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUNDn( 0, A, B, C, D, E, F1, K[0] ); ROUNDn( 1, E, A, B, C, D, F1, K[0] ); ROUNDn( 2, D, E, A, B, C, F1, K[0] ); ROUNDn( 3, C, D, E, A, B, F1, K[0] ); ROUNDn( 4, B, C, D, E, A, F1, K[0] ); ROUNDn( 5, A, B, C, D, E, F1, K[0] ); ROUNDn( 6, E, A, B, C, D, F1, K[0] ); case 32: ROUNDn( 7, D, E, A, B, C, F1, K[0] ); ROUNDn( 8, C, D, E, A, B, F1, K[0] ); ROUNDn( 9, B, C, D, E, A, F1, K[0] ); ROUNDn(10, A, B, C, D, E, F1, K[0] ); ROUNDn(11, E, A, B, C, D, F1, K[0] ); case 52: ROUNDn(12, D, E, A, B, C, F1, K[0] ); ROUNDn(13, C, D, E, A, B, F1, K[0] ); ROUNDn(14, B, C, D, E, A, F1, K[0] ); ROUNDn(15, A, B, C, D, E, F1, K[0] ); } if(tailIndex == 52) { ROUNDn(16, E, A, B, C, D, F1, K[0] ); ROUNDn(17, D, E, A, B, C, F1, K[0] ); ROUNDn(18, C, D, E, A, B, F1, K[0] ); ROUNDn(19, B, C, D, E, A, F1, K[0] ); ROUNDu(20, A, B, C, D, E, F2, K[1] ); ROUNDn(21, E, A, B, C, D, F2, K[1] ); ROUNDn(22, D, E, A, B, C, F2, K[1] ); ROUNDu(23, C, D, E, A, B, F2, K[1] ); ROUNDn(24, B, C, D, E, A, F2, K[1] ); ROUNDn(25, A, B, C, D, E, F2, K[1] ); ROUNDu(26, E, A, B, C, D, F2, K[1] ); ROUNDn(27, D, E, A, B, C, F2, K[1] ); ROUNDu(28, C, D, E, A, B, F2, K[1] ); ROUNDu(29, B, C, D, E, A, F2, K[1] ); ROUNDn(30, A, B, C, D, E, F2, K[1] ); } else if (tailIndex == 32) { ROUNDn(16, E, A, B, C, D, F1, K[0] ); ROUNDn(17, D, E, A, B, C, F1, K[0] ); ROUNDn(18, C, D, E, A, B, F1, K[0] ); ROUNDn(19, B, C, D, E, A, F1, K[0] ); ROUNDn(20, A, B, C, D, E, F2, K[1] ); ROUNDu(21, E, A, B, C, D, F2, K[1] ); ROUNDn(22, D, E, A, B, C, F2, K[1] ); ROUNDu(23, C, D, E, A, B, F2, K[1] ); ROUNDu(24, B, C, D, E, A, F2, K[1] ); ROUNDn(25, A, B, C, D, E, F2, K[1] ); ROUNDu(26, E, A, B, C, D, F2, K[1] ); ROUNDu(27, D, E, A, B, C, F2, K[1] ); ROUNDn(28, C, D, E, A, B, F2, K[1] ); ROUNDu(29, B, C, D, E, A, F2, K[1] ); ROUNDu(30, A, B, C, D, E, F2, K[1] ); } else { ROUNDu(16, E, A, B, C, D, F1, K[0] ); ROUNDu(17, D, E, A, B, C, F1, K[0] ); ROUNDu(18, C, D, E, A, B, F1, K[0] ); ROUNDu(19, B, C, D, E, A, F1, K[0] ); ROUNDu(20, A, B, C, D, E, F2, K[1] ); ROUNDu(21, E, A, B, C, D, F2, K[1] ); ROUNDu(22, D, E, A, B, C, F2, K[1] ); ROUNDu(23, C, D, E, A, B, F2, K[1] ); ROUNDu(24, B, C, D, E, A, F2, K[1] ); ROUNDu(25, A, B, C, D, E, F2, K[1] ); ROUNDu(26, E, A, B, C, D, F2, K[1] ); ROUNDu(27, D, E, A, B, C, F2, K[1] ); ROUNDu(28, C, D, E, A, B, F2, K[1] ); ROUNDu(29, B, C, D, E, A, F2, K[1] ); ROUNDu(30, A, B, C, D, E, F2, K[1] ); } ROUNDu(31, E, A, B, C, D, F2, K[1] ); ROUNDu(32, D, E, A, B, C, F2, K[1] ); ROUNDu(33, C, D, E, A, B, F2, K[1] ); ROUNDu(34, B, C, D, E, A, F2, K[1] ); ROUNDu(35, A, B, C, D, E, F2, K[1] ); ROUNDu(36, E, A, B, C, D, F2, K[1] ); ROUNDu(37, D, E, A, B, C, F2, K[1] ); ROUNDu(38, C, D, E, A, B, F2, K[1] ); ROUNDu(39, B, C, D, E, A, F2, K[1] ); ROUND5(40, F3, K[2] ); ROUND5(45, F3, K[2] ); ROUND5(50, F3, K[2] ); ROUND5(55, F3, K[2] ); ROUND5(60, F4, K[3] ); ROUND5(65, F4, K[3] ); ROUND5(70, F4, K[3] ); ROUND5(75, F4, K[3] ); /* Mix in the IV again */ MA = ADD(A, H[0]); MB = ADD(B, H[1]); /* Go over each vector element in turn */ for(n=0; n < 2; n++) { /* Extract A and B components */ IA = ((uInt32*) &MA)[n]; IB = ((uInt32*) &MB)[n]; /* Is this the best bit count so far? */ if(!(IA & bitMask1Low) && !(IB & bitMask1High)) { /* Count bits */ gotBits = 0; if(IA) { s = IA; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(IB) { s = IB; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best = gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } ((uInt32*) &vBitMaskLow )[0] = bitMask1Low ; ((uInt32*) &vBitMaskLow )[1] = bitMask1Low ; ((uInt32*) &vBitMaskHigh)[0] = bitMask1High; ((uInt32*) &vBitMaskHigh)[1] = bitMask1High; /* Copy this result back to the block buffer, little-endian */ for(t=0; t < 16; t++) { output[t*4+0] = X[t*8+3+n*4]; output[t*4+1] = X[t*8+2+n*4]; output[t*4+2] = X[t*8+1+n*4]; output[t*4+3] = X[t*8+0+n*4]; } /* Is it good enough to bail out? */ if(gotBits >= bits) { /* Shut down use of MMX */ __builtin_ia32_emms(); return iters+2; } } } MINTER_CALLBACK(); } /* Shut down use of MMX */ __builtin_ia32_emms(); return iters+2; /* For other platforms */ #else return 0; #endif } hashcash-1.21/hashcash.10000644000076400007640000012540010351101612013451 0ustar adamadam.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "HASHCASH 1" .TH HASHCASH 1 "2005-01-12" "1.15" "hashcash" .SH "NAME" hashcash \- hashcash anti\-spam / denial of service counter\-measure tool .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Sh "Mint (create) stamps:" .IX Subsection "Mint (create) stamps:" \&\fBhashcash\fR \fI\-m\fR [ [ \fIoptions\fR ] [ \fIresource\fR ... ] ... ] .Sh "Check stamps:" .IX Subsection "Check stamps:" \&\fBhashcash\fR \fI\-c\fR [ [ \fIoptions\fR ] \fI\-r resource\fR ... ] [ \fIstamp\fR ... ] .Sh "Speed estimates:" .IX Subsection "Speed estimates:" \&\fBhashcash\fR \fI\-s\fR [ \fIoptions\fR ] [ \fI\-b bits\fR ] .Sh "Purge database:" .IX Subsection "Purge database:" \&\fBhashcash\fR \fI\-p now\fR [ \fI\-j resource\fR ] [ \fI\-k\fR ] [ \fI\-t time\fR ] [ \fI\-u\fR ] .Sh "Count collision bits:" .IX Subsection "Count collision bits:" \&\fBhashcash\fR \fI\-w\fR [ \fIoptions\fR ] [ \fIstamp\fR ... ] .Sh "Get resource name:" .IX Subsection "Get resource name:" \&\fBhashcash\fR \fI\-n\fR [ \fIoptions\fR ] [ \fIstamp\fR ... ] .Sh "Get time remaining until expiry" .IX Subsection "Get time remaining until expiry" \&\fBhashcash\fR \fI\-l\fR [ \fIoptions\fR ] \fI\-e period\fR [ \fIstamp\fR ... ] .SH "DESCRIPTION" .IX Header "DESCRIPTION" Hashcash is a denial-of-service counter measure tool. Its main current use is to help hashcash users avoid losing email due to content based and blacklist based anti-spam systems. .PP The hashcash tool allows you to create hashcash stamps to attach to emails you send, and to verify hashcash stamps attached to emails you receive. Email senders attach hashcash stamps with the X\-Hashcash: header. Vendors and authors of anti-spam tools are encouraged to exempt mail sent with hashcash from their blacklists and content based filtering rules. .PP A hashcash stamp constitutes a proof-of-work which takes a parameterizable amount of work to compute for the sender. The recipient can verify received stamps efficiently. This package also includes a sha1 implementation which is equivalent in functionality to the sha1sum program available on some systems. .PP The hashcash proof-of-work function is to compute partial hash collisions (the \s-1SHA1\s0 hash function is used). By choosing the number of bits of collision, the work required to compute a collision can be made arbitrarily expensive \*(-- from fractions of a second to minutes or hours. The verification is efficient requiring the same small cost whatever the collision size. .PP For more detailed discussion of other applications hashcash has been used for see http://www.hashcash.org/ .SH "USAGE NOTES" .IX Header "USAGE NOTES" In this man page a resource name is the name of the service or address the stamp is created for. In the case of email, the resource name is the recipient's email address in the form user@domain.com. .Sh "Minting stamps" .IX Subsection "Minting stamps" The \fI\-m\fR flag must be given to mint a stamp. .PP The resource name (recipient's email address) to mint the stamp against can be passed as an argument, or if omitted is read from stdin. If stdin is a tty the user is prompted, if stdin is a pipe the resource name is just silently read. The desired collision size can be specified with the \-b option. If no collision size is specified, the default is 20 bits. See also the \fI\-b default\fR option. .Sh "Checking stamps" .IX Subsection "Checking stamps" The \fI\-c\fR flag must be given to check a stamps expiry. The stamp to check can be given as an argument to \f(CW\*(C`hashcash\*(C'\fR. If no stamp is given the stamp is read from stdin. If stdin is a tty the user will be prompted, if stdin is a pipe the stamp is just silently read. A resource name (the recipient's email address) can be given with the \&\fI\-r\fR option. If a resource name is given the resource name is compared to the resource name in the stamp, if they do not match, the stamp is rejected. .PP Note: if no resource name is given the stamp is anyway checked to see if it is otherwise valid, but it could be minted for a different resource, which would allow stamps to be reused across different resources, so hashcash will return unchecked exit code on exit. .PP Stamps are by default considered to be valid for 28 days. The validity period can be changed using the \fI\-e\fR flag. .PP If the stamp has expired or has a date in the future the stamp is rejected and the program exits immediately. .PP If a required collision size is given with the \fI\-b\fR flag, the stamps value is computed and compared, if the stamp has insufficent value it is rejected, and the program exits immediately. If the \fI\-b\fR flag is not given, the stamp is checked to see if it is otherwise valid, but hashcash will return unchecked exit code on exit. .PP If the stamp is double spent the stamp is rejected. Double spending protection is discussed in more detail below in \&\*(L"Double Spending Protection\*(R". If double spending protection is not enabled, the stamp could be double spent, so hashcash will return unchecked exit code (exit code 2) on exit. .PP The \fI\-w\fR flag can be used to request that the number of bits of the collision are counted and displayed. The \fI\-n\fR flag can be used to request that the resource name in the stamp is parsed out and displayed. The \fI\-l\fR flag can be used to request the number of seconds until expiry of the stamp is output. .PP The program will only return exit codes valid or invalid if the \fI\-c\fR flag is used, the \fI\-b\fR flag is used, \fI\-d\fR, \fI\-r resource\fR are used. These are the minimum set of options necessary to fully check the validty of a stamp. If these criteria are not met, the program will return exit code unchecked (exit code 2) on exit. (See also the \fI\-y\fR flag.) .Sh "Double Spending Protection" .IX Subsection "Double Spending Protection" If the \fI\-d\fR flag is used when checking stamps, a database of spent stamps is kept. .PP By default stamps expire after 28 days, without expiry the database would grow indefinately. You can specify an alternate expiry period with the \fI\-e\fR flag. The recommended (and default) expiry period for email is 28 days. After the expiry period amount of time, the stamp is anyway considered expired and may be purged from the database to save space. (See \*(L"Purging Periodically vs on Next Access\*(R" for how to purge stamps.) .PP For efficiency reasons a stamp is verified before it is checked in the database; if it is otherwise invalid no database activity will occur. .PP Note: The decision about how long the stamp should be considered valid is up to the verifier. If it is too short it is possible for some applications that the stamp will expire before arriving at the recipient (eg with email.) The suggested value of 28 days should be safe for normal email delivery delays. The choice is a trade-off between database size and risk of expiry prior to arrival, and depends on the application. .PP Note: Different stamps in the same database can have different validity periods, so for example stamps for different resources with different validity periods can be stored in the same database, or the recipient may change the validity period for future stamps without affecting the validity of old stamps. .Sh "Purging Periodically vs on Next Access" .IX Subsection "Purging Periodically vs on Next Access" To purge old stamps periodically while checking stamps use the \fI\-p period\fR option to purge no sooner than the given time period since the last purge. Purging can be used with the \fI\-k\fR option to purge unexpired stamps also, and with the \fI\-j resource\fR flag to purge only stamps for the given resource. .PP There are circumstances where it may be inconvenient to purge stamps on the next access, for example if there is a large double spend database which takes some time to purge, and the response time of the hashcash checker is important. To avoid this problem, purging can be done separately using just the \fI\-p now\fR option to request just the purge operation. On unix for example you could call \f(CW\*(C`hashcash \-p now\*(C'\fR in a cron job once per day, or on demand when disk was running low. .Sh "Speed Estimates" .IX Subsection "Speed Estimates" The \fI\-s\fR flag requests measurement of how many collisions can be tested per second. No stamp is minted, or verified. .PP If the \fI\-b\fR flag is used with this option, instead an estimate of how many seconds it would take to mint a stamp of the given size in bits is computed. To find out how much time it will take to mint a default sized stamp use \fI\-s \-b default\fR. .Sh "Notes" .IX Subsection "Notes" All informational output is printed on stderr. Minted stamps, and results of stamp verification and timing are printed on stdout. The quiet flag \fI\-q\fR suppresses all informational output. The \fI\-v\fR flag requests more informational output. The requested output, which is the only information that is output in quiet mode (when \fI\-q\fR is specified) is printed on standard output. If stdout is a pipe, or when quiet mode is in effect the output is printed without description (ie just bits, just seconds, just resource). .SH "OPTIONS" .IX Header "OPTIONS" .IP "\fI\-c\fR" 4 .IX Item "-c" Check the expiry information of stamps given as an argument or on stdin. (Use with \fI\-b\fR, \fI\-d\fR and \fI\-r resource\fR to fully check stamps). .IP "\fI\-m\fR" 4 .IX Item "-m" Mint stamps with the resources given as arguments or on stdin. .IP "\fI\-b bits\fR" 4 .IX Item "-b bits" When minting a stamp, create a collision of at least this many bits. When verifying a stamp require that it have a collision of at minimum this many bits, otherwise reject it. If omitted the default is used. .Sp When checking stamps, require that the stamps have this many bits. .Sp The default number of bits can be specified with \fI\-b default\fR. Bits relative to the default can also be specified with \fI\-b +n\fR for n bits more than the default and \fI\-b \-n\fR for n bits less than the default. .Sp \&\fI\-b default\fR, \fI\-b +0\fR and \fI\-b \-0\fR are all equivalent. .Sp When doing the speed test \fI\-s\fR, can to measure speed of default token with \fI\-s \-b default\fR. .IP "\fI\-r resource\fR" 4 .IX Item "-r resource" When minting stamps, the resource name (recipient's email address) to mint the stamp against can be given either with \fI\-r resource\fR or as an argument to \f(CW\*(C`hashcash\*(C'\fR. .Sp When checking stamps, the resource name (your own email address) is given with the \fI\-r\fR option. If the resource name is given it is checked against the resource name in the stamp, and if they do not match the stamp is rejected. Note if the resource name is not given, stamps for other resources would be accepted, and therefore hashcash returns exit code unchecked (exit code 2) on exit. .IP "\fI\-o\fR" 4 .IX Item "-o" When verifying stamps multiple resources can be given. By default the resources are just checked one by one until a matching valid resource is found. However when you use wildcards or regular expressions (see \fI\-E\fR), it is useful to be able to specify that one resource overrides another. For example this: \fI\-b15 \-r adam@dev.null \-o \-b10 *@dev.null\fR states that mail to address \fIadam@dev.null\fR requires 15 bits, but mail to \fI*@dev.null\fR requires only 10 bits. If we omitted the \fI\-o\fR override relationship between the two resources, a stamp of 10 bits would be accepted for address \&\fIadam@dev.null\fR because while it would be rejected as having insufficient bits under the first rule, it would be accepted under the 2nd rule. The \&\fI\-o\fR option allows you avoid this problem. .IP "\fI\-e time\fR" 4 .IX Item "-e time" Expiry period for spent stamps. While checking stamps (using the \fI\-c\fR flag), if the stamp was minted more than the specified amount of time ago, it is considered expired. If this option is not used, by default stamps expire after 28 days. The expiry period is given in seconds by default (an argument of 0 means forever). A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). .Sp If used with the \fI\-d\fR option, the spent stamp and its expiry period is recorded in the database. See the \fI\-p\fR option for description of how to purge stamps from the database. .Sp While minting stamps, the \fI\-e\fR flag can have an effect on the resolution of time created in the stamp. Without the \fI\-e\fR option, the default resolution is days (time format: \s-1YYMMDD\s0). Alternate formats based on range of expiry period are as follows: .Sp While minting you can also given an explicit time width with the \fI\-z\fR option instead. (\fI\-z\fR overrides \fI\-e\fR if both are given. If neither are given the default is 6 chars (time format: \s-1YYMMDD\s0)). .Sp The rules for automatically determining appropriate time width from \&\fI\-e\fR if no \fI\-z\fR option is given are: .RS 4 .IP "* period >= 2 years then time format \s-1YY\s0 is used rounded down to the nearest year start;" 8 .IX Item "period >= 2 years then time format YY is used rounded down to the nearest year start;" .PD 0 .IP "* 2 years < period <= 2 months then time format \s-1YYMM\s0 is used rounded down to the nearest month start;" 8 .IX Item "2 years < period <= 2 months then time format YYMM is used rounded down to the nearest month start;" .IP "* 2 months < period <= 2 days then time format \s-1YYMMDD\s0 is used rounded down to the begining of the nearest day;" 8 .IX Item "2 months < period <= 2 days then time format YYMMDD is used rounded down to the begining of the nearest day;" .IP "* 2 days < period <= 2 minutes then time format YYMMDDhhmm is used rounded down to the begining of the nearest minute;" 8 .IX Item "2 days < period <= 2 minutes then time format YYMMDDhhmm is used rounded down to the begining of the nearest minute;" .IP "* period < 2 minutes then time format YYMMDDhhmmss is used in seconds." 8 .IX Item "period < 2 minutes then time format YYMMDDhhmmss is used in seconds." .RE .RS 4 .PD .Sp Note the rounding down is based on \s-1UTC\s0 time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than \s-1GMT\s0 (\s-1UTC\s0 = \s-1GMT\s0). It may be clearer to understand if you use the \fI\-u\fR option. .RE .IP "\fI\-z width\fR" 4 .IX Item "-z width" The \fI\-z\fR option is for use during minting and allows user choice of width of time width field. See also the \fI\-e\fR option given in combination with \fI\-m\fR to specify an implicit time field width under the description of the \fI\-e\fR flag. Valid widths are 6,10 or 12 chars corresponding respectively to: \s-1YYMMDD\s0, YYMMDDhhmm, and YYMMDDhhmmss rounded down to the nearest day, or minute respectively. .Sp Note the rounding down is based on \s-1UTC\s0 time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than \s-1GMT\s0 (\s-1UTC\s0 = \s-1GMT\s0). It may be clearer to understand if you use the \fI\-u\fR option. .IP "\fI\-g period\fR" 4 .IX Item "-g period" The \fI\-g\fR option is for use when checking hashcash stamps with the \&\fI\-c\fR option and specifies a grace period for clock skew, ie if a hashcash stamp arrives with a date in the future or in the past it will not be rejected as having a futuristic date (or as being expired) unless it is more futuristic (or has been expired for longer) than this period. The default is 2 days, which means as long as the sending system's clock is no more than 2 days ahead (or 2 days behind) of the receiving system's clock, the hashcash stamp will still be accepted. .Sp The default units for grace period are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). .IP "\fI\-d\fR" 4 .IX Item "-d" Store stamps in a double spend database. If stamp has been seen before it will be rejected even if it is otherwise valid. The default database file is \fIdatabase.sdb\fR in the current directory. Only otherwise valid stamps will be stored in the database. Only fully validated stamps will be stored in the database, unless the \fI\-y\fR option is given. .IP "\fI\-f dbname\fR" 4 .IX Item "-f dbname" Use \fIdbname\fR instead of default filename for double spend database. .IP "\fI\-p period\fR" 4 .IX Item "-p period" Purges the database of expired stamps if the given time period has passed since the last time it was purged. As a convenience \fI\-p now\fR is equivalent to \fI\-p 0\fR both of which mean purge now, regardless of when the database was last purged. .Sp If used in combination with \fI\-j resource\fR only the stamps minted for the given resource are purged. .Sp If used in combination with \fI\-k\fR all stamps even un-expired stamps are purged. Can be used in combination with \fI\-t time\fR to expire as if the current time were the given time. .IP "\fI\-k\fR" 4 .IX Item "-k" Use with option \fI\-p\fR to request all stamps are purged rather than just expired ones. .IP "\fI\-j resource\fR" 4 .IX Item "-j resource" Use with option \fI\-p\fR to request that just stamps matching the given resource name are to be purged, rather than the default which is to purge all expired stamps. If the resource name is the empty string, all stamps are matched (this is equivalent to omitting the \fI\-j\fR option). .Sp Note the \fI\-E\fR, \fI\-M\fR and \fI\-S\fR type of match flags also apply to resources given with the \fI\-j resource\fR flag. .IP "\fI\-s\fR" 4 .IX Item "-s" Print timing information only, and don't proceed to create a stamp. If combined with \fI\-b bits\fR flag print estimate of how long the requested collision size would take to compute, if \fI\-s\fR given by itself, just prints speed of the collision finder. To print an estimate of how long the default number of bits would take use \fI\-b default\fR. .IP "\fI\-h\fR" 4 .IX Item "-h" Print short usage information. .IP "\fI\-v\fR" 4 .IX Item "-v" Print more verbose informational output about the stamp minting or verification. (If \-v is the only argument, prints the tool version number.) .IP "\fI\-V\fR" 4 .IX Item "-V" Prints tool version number. .IP "\fI\-q\fR" 4 .IX Item "-q" Batch mode. Prints no information other than output. This option overrides the \fI\-v\fR option. .IP "\fI\-X\fR" 4 .IX Item "-X" When minting, prints the hashcash email X\-header 'X\-Hashcash: ' before the stamp. Without this option just the bare stamp is printed. .Sp When checking, after scanning stamps given as arguments, scans stdin for lines starting with the string 'X\-Hashcash:', and uses the rest of the matching line as the stamp. Only the lines up to and ending at the first blank line are scanned (see also \fI\-i\fR flag which can be used to override this). A blank line is the separator used to separate the headers from the body of a mail message or \s-1USENET\s0 article. This is meant to make it convenient to pipe a mail message or \s-1USENET\s0 article to hashcash on stdin. .IP "\fI\-x extension\fR" 4 .IX Item "-x extension" An extension string composed of name value sets. The extension format is described below in the section on the hashcash stamp format. This allows users to define their own stamp extensions which are hashed into the stamp, verified by recipients that support them, and ignored by recipients that don't support them. Note the extension hook mechanism has not yet been implemented. This will come in a subsequent release. .IP "\fI\-i\fR" 4 .IX Item "-i" When checking and using the \fI\-X\fR flag, ignore the blank line boundary between headers and body of the message, and check for collision in the body too if one is not found in the headers. .IP "\fI\-t time\fR" 4 .IX Item "-t time" Pretend the current time is the time given for purposes of minting stamps, verifying stamps and purging old stamps from the database. Time is given in a format based on \s-1UTCTIME\s0 format YYMMDD[hhmm[ss]]. .Sp Time is expressed in local time by default. Use with \fI\-u\fR flag to give time in \s-1UTC\s0 (\s-1GMT\s0). .Sp You can also give time relative to the current time by prefixing the argument with + or \-. The default units for relative time are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). .Sp Note: when time is expressed in local time, if there is daylight savings in your timezone, there are one or two ambiguous hours per year at the time of change from daylight savings time to normal time. .IP "\fI\-u\fR" 4 .IX Item "-u" Input and output absolute times in \s-1UTC\s0 (\s-1GMT\s0) instead of local time. .IP "\fI\-a period\fR" 4 .IX Item "-a period" Add (or subtract if number is negative) a random value from the current time before minting the stamp. This hides the time the stamp was created, which may be useful for anonymous users. Note adding (rather than subtracting) a random time may be risky if the stamp takes less than the added time to arrive as the recipient will reject stamps with time stamps in the future. .IP "\fI\-n\fR" 4 .IX Item "-n" Print resource name parsed from stamp being verified. Returns exit code unchecked on exit. .IP "\fI\-l\fR" 4 .IX Item "-l" Print number of seconds left before stamp expires. Returns exit code unchecked on exit. .Sp Note: the calculation includes the grace period, so can be up to 2 times grace period longer than you might otherwise expect (clock fast but system has to presume it could be slow). If you want to exclude the grace period add \fI\-g0\fR to set grace period to 0 for the calculation. .IP "\fI\-w\fR" 4 .IX Item "-w" Print number of bits of collision of stamp. Returns exit code unchecked on exit. .IP "\fI\-y\fR" 4 .IX Item "-y" Returns success if the stamp is valid even if it is not fully checked. Use with \fI\-c\fR where not all of \fI\-d\fR, \fI\-r\fR are specified to get success exit code on valid but partially checked stamp. Similarly can use with \fI\-n\fR, \fI\-l\fR, \fI\-w\fR with same effect. .IP "\fI\-M\fR" 4 .IX Item "-M" When checking stamps, allow wildcard \fI*\fR matching in the resource name to make it simpler to specify multiple email addresses and to allow matching catch-all addresses and addresses including subdomains. This is the default. See also \fI\-S\fR, \fI\-E\fR and \fI\-C\fR .IP "\fI\-S\fR" 4 .IX Item "-S" When checking stamps use simple text compare to compare resource names to those in stamps. See also \fI\-M\fR, \fI\-E\fR and \fI\-C\fR. .IP "\fI\-E\fR" 4 .IX Item "-E" When checking stamps use regular expressions to specify resource names to make it simpler to specify multiple email addresses, catch-all addresses, classes of extension addresses and addresses including subdomains. Note regular expression syntax is \s-1POSIX\s0 style: special characters do not need to be quoted to have their special meaning; but they do have to be quoted with \e to that character in the searched string. The regular expression automatically has ^ added at the beginning and $ added at the end, if they are not specified. The special characters ^ matches the beginning of the resouce, and $ matches the end of resource. .Sp (Note even if compiled with \s-1BSD\s0 regular expressions, \s-1POSIX\s0 style syntax is used; also note \s-1BSD\s0 regular expressions do not support ranges {}.) .IP "\fI\-C\fR" 4 .IX Item "-C" By default resources are canonicalized to lower case on minting and on checking. The \fI\-C\fR flag overrides this so that resources are treated as case sensitive on checking, and not canonizalized on minting. .IP "\fI\-P\fR" 4 .IX Item "-P" Print progress info (number of iterations, expected iterations, percentage done, best stamp size found so far). .IP "\fI\-O core\fR" 4 .IX Item "-O core" Select hashcash core with that number. Currently 0\-9 are valid cores. Not all cores work on all architectures. Eg some are x86 specific assembler, others \s-1PPC\s0 specific assembler. If a core is not valid hashcash returns failure and explains what happened. .IP "\fI\-Z n\fR" 4 .IX Item "-Z n" Compress the stamp. This is a time vs space trade off. Larger stamps are faster, but arguably slightly ugly. For fastest stamps (the default) use \-Z 0; for partly compressed stamps use \-Z 1; for very compressed, but somewhat slow stamps use \-Z 2. (Note: due to a late discovered bug, \-Z2 is the same as \-Z1 for now until I can fix that.) .SH "EXAMPLES" .IX Header "EXAMPLES" .Sh "Creating stamps" .IX Subsection "Creating stamps" .ie n .IP """hashcash \-s""" 4 .el .IP "\f(CWhashcash \-s\fR" 4 .IX Item "hashcash -s" Print timing information about how many collisions the machine can try per second. .ie n .IP """hashcash \-sv""" 4 .el .IP "\f(CWhashcash \-sv\fR" 4 .IX Item "hashcash -sv" More accurate but quite slow benchmarking of different processor specific minting cores. .ie n .IP """hashcash \-s \-b default""" 4 .el .IP "\f(CWhashcash \-s \-b default\fR" 4 .IX Item "hashcash -s -b default" Print how long it would take the machine to compute a default sized collision (but don't actually compute a collision). .ie n .IP """hashcash \-s \-b 32""" 4 .el .IP "\f(CWhashcash \-s \-b 32\fR" 4 .IX Item "hashcash -s -b 32" Print how long it would take the machine to compute a 32 bit collision (but don't actually compute a collision). .ie n .IP """hashcash \-m""" 4 .el .IP "\f(CWhashcash \-m\fR" 4 .IX Item "hashcash -m" Mint a stamp. Will prompt for resource name and mint with default value (number of collision bits). .ie n .IP """hashcash \-m foo""" 4 .el .IP "\f(CWhashcash \-m foo\fR" 4 .IX Item "hashcash -m foo" Compute collision on resource foo. Will mint with default value (number of collision bits). .ie n .IP """hashcash \-m foo \-b 10""" 4 .el .IP "\f(CWhashcash \-m foo \-b 10\fR" 4 .IX Item "hashcash -m foo -b 10" Compute 10 bit collision on resource foo. .ie n .IP """hashcash \-a \-3d""" 4 .el .IP "\f(CWhashcash \-a \-3d\fR" 4 .IX Item "hashcash -a -3d" Subtract a random time of between 0 days and 3 days from the stamp's creation time. This is the same fuzz factor used by mixmaster to reduce risk of timing\-correlations. .Sh "Examining Stamps" .IX Subsection "Examining Stamps" .ie n .IP """hashcash \-w 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-w 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -w 1:24:040806:foo::511801694b4cd6b0:1e7297a" Report the value of the stamp (how many bits of collision) there are. The example is a 24 bit collision, which takes on average 25 seconds to create on a 3Ghz P4. .ie n .IP """hashcash \-mq \-b 10 foo | hashcash \-w""" 4 .el .IP "\f(CWhashcash \-mq \-b 10 foo | hashcash \-w\fR" 4 .IX Item "hashcash -mq -b 10 foo | hashcash -w" Create a stamp in batch mode, pass to hashcash on stdin to verify, have it print how many bits there were. .ie n .IP """hashcash \-n 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-n 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -n 1:24:040806:foo::511801694b4cd6b0:1e7297a" Report the resource name from the stamp. The resource name in the example is foo. .ie n .IP """hashcash \-l \-e 30y 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-l \-e 30y 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -l -e 30y 1:24:040806:foo::511801694b4cd6b0:1e7297a" Report how long until the stamp expires if it expires in 30 years from its creation date. (Note dates too far into the future run into the 2038 end of Epoch, which is the unix time analog of the y2k bug). .Sh "Verifying Stamps" .IX Subsection "Verifying Stamps" .ie n .IP """hashcash \-c 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-c 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -c 1:24:040806:foo::511801694b4cd6b0:1e7297a" Check if the stamp is valid. Note as we are not checking the stamp in a double spend database, and did not specify a resource name or required number of bits of collision and hashcash will consider the stamp not fully checked, and it will report it as valid but not fully unchecked, or as invalid if there is any problem with the stamp. .ie n .IP """hashcash \-c \-b24 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-c \-b24 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -c -b24 1:24:040806:foo::511801694b4cd6b0:1e7297a" Check that the value of the stamp is greater or equal to 24 bits. This example has 24 bit value. If you increase the requested number of bits or replace the stamp with one with less than 24 bit collision the stamp will be rejected. .ie n .IP """hashcash \-c \-b24 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-c \-b24 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -c -b24 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" As above check if the stamp has sufficient value, but in addition check that the resource name given matches the resource name in the stamp. .Sh "Double Spending Prevention" .IX Subsection "Double Spending Prevention" The examples given in \*(L"Verifying Stamps\*(R" can be modified to keep a double spend database so that the same stamp will not be accepted twice. Note a stamp will only be checked in and added to the database if it is otherwise valid and fully checked (a required number of bits of collision has been specified and a resource has been specified). .ie n .IP """hashcash \-cd \-b 10 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-cd \-b 10 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Check the stamp and add to double spent database if it's valid (has correct resource name and sufficient value). .ie n .IP """hashcash \-cd \-b 10 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-cd \-b 10 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Try to double spend the stamp. It will be rejected as double spent. .Sh "Stamp Expiry" .IX Subsection "Stamp Expiry" To prevent the double spend database growing indefinately, the recipient can request that stamps be no older than a specified period. After expiry old stamps can dropped from the double spend database as they will no longer be needed \*(-- expired stamps can be rejected based purely on their old date, so the space taken by expired stamps in the double spend database can be saved without risk of accepting an expired though otherwise valid stamp. .PP The third field of the stamp is the \s-1UTC\s0 time since 1st January 1970. The default time format is \s-1YYMMDD\s0, time rounded down to the nearest day. The default validity period is 28 days. .PP You can provide an alternative validity period with the \fI\-e\fR option. .ie n .IP """hashcash \-cd \-b 10 \-e 2d \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-cd \-b 10 \-e 2d \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -cd -b 10 -e 2d -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Try verifying an old stamp, the above stamp was created 11 Aug 2002. .Sp We gave option \fI\-e 2d\fR so the stamps expiry date is 2 days after creation, which is now in the past. .Sp Note: if the creation time is expressed in the stamp in days, the precise creation date is the begining of the specified day in \s-1UTC\s0 time (similarly for alternate units the creation time is rounded down to the begining of the unit it is expressed in). For units in days, for example, this may mean depending on your time zone that the stamp appears to be considered invalid in under the specified expiry period in days relative to your relative view of what day it is, as the calculation is based on current time in \s-1UTC\s0, and the creation time of the stamp is expressed in \s-1UTC\s0 time. .ie n .IP """hashcash \-cd \-b 10 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a""" 4 .el .IP "\f(CWhashcash \-cd \-b 10 \-r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a\fR" 4 .IX Item "hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Test whether the stamp is otherwise valid, apart from having expired. Omitting the \fI\-e\fR tells hashcash that the stamp will never expire. An expiry period of forever can also be given explitly like this: \fI\-e 0\fR, where an expiry period of 0 means forever. .Sh "Purging old stamps" .IX Subsection "Purging old stamps" If the \fI\-c\fR, \fI\-d\fR options are used together, each time a stamp is checked, if it is valid and all of the mandatory aspects of the stamp are verified (collision bits check, resource name check) then the stamp and its expiry period is written to the database file. The default expiry period if an expiry period is not given explicitly with the \fI\-e\fR option is 28 days (ie stamps expire after 4 weeks). .PP First mint and then add a stamp: .ie n .IP """hashcash \-m \-b 10 foo \-e 1m > stamp""" 4 .el .IP "\f(CWhashcash \-m \-b 10 foo \-e 1m > stamp\fR" 4 .IX Item "hashcash -m -b 10 foo -e 1m > stamp" Note: we specified an expiry on minting in this example, to ensure that the stamp creation time is given in high enough resolution in the stamp that the stamp will not be considered expired at time of creation. (Recall the default resolution is in days, a stamp created with a creation time rounded down to the beginging of the day is unlikely to be considered valid 1 minute later unless you mint it at midnight \s-1UTC\s0 time.) .ie n .IP """hashcash \-cd \-e 1m \-b 10 \-r foo < stamp""" 4 .el .IP "\f(CWhashcash \-cd \-e 1m \-b 10 \-r foo < stamp\fR" 4 .IX Item "hashcash -cd -e 1m -b 10 -r foo < stamp" The stamp expires in 1 minute. Wait 1 minute and then explicitly request that expired stamps be purged: .ie n .IP """hashcash \-p now""" 4 .el .IP "\f(CWhashcash \-p now\fR" 4 .IX Item "hashcash -p now" Then try resubmitting the same stamp: .ie n .IP """hashcash \-cd \-e 1m \-b 10 \-r foo < stamp""" 4 .el .IP "\f(CWhashcash \-cd \-e 1m \-b 10 \-r foo < stamp\fR" 4 .IX Item "hashcash -cd -e 1m -b 10 -r foo < stamp" and the stamp will be rejected anyway as it has expired, illustrating why it was not necessary to keep this stamp in the database. .Sp With the default database (the sdb format) the database contents are human readable, so you can view their contents by cating them to the terminal: .ie n .IP """cat hashcash.sdb""" 4 .el .IP "\f(CWcat hashcash.sdb\fR" 4 .IX Item "cat hashcash.sdb" to see that the stamp really is added and then after puring subsequently purged due to expiry. .Sh "Purging old stamps on Demand" .IX Subsection "Purging old stamps on Demand" As a convenience you can purge at the same time as checking stamps by using the \fI\-p\fR option with the \fI\-c\fR option. .ie n .IP """hashcash \-m \-b 10 foo > stamp""" 4 .el .IP "\f(CWhashcash \-m \-b 10 foo > stamp\fR" 4 .IX Item "hashcash -m -b 10 foo > stamp" .PD 0 .ie n .IP """hashcash \-cd \-p now \-e 1 \-b 10 \-r foo < stamp""" 4 .el .IP "\f(CWhashcash \-cd \-p now \-e 1 \-b 10 \-r foo < stamp\fR" 4 .IX Item "hashcash -cd -p now -e 1 -b 10 -r foo < stamp" .PD It may be inefficient to purge stamps on every use as the entire database has to be scanned for expired stamps. By giving a time period to the \fI\-p\fR option, you can tell \f(CW\*(C`hashcash\*(C'\fR to purge no more frequently than that time period since the previous purge. .Sp For example: .ie n .IP """hashcash \-cd \-p 1d \-e 1 \-b 10 \-r foo < stamp""" 4 .el .IP "\f(CWhashcash \-cd \-p 1d \-e 1 \-b 10 \-r foo < stamp\fR" 4 .IX Item "hashcash -cd -p 1d -e 1 -b 10 -r foo < stamp" tells \f(CW\*(C`hashcash\*(C'\fR to purge any expired stamps no more than once per day. .ie n .IP """hashcash \-p 1M \-j foo""" 4 .el .IP "\f(CWhashcash \-p 1M \-j foo\fR" 4 .IX Item "hashcash -p 1M -j foo" tells \f(CW\*(C`hashcash\*(C'\fR to purge only expired stamps matching resource foo once per month. .ie n .IP """hashcash \-p now \-k""" 4 .el .IP "\f(CWhashcash \-p now \-k\fR" 4 .IX Item "hashcash -p now -k" tells \f(CW\*(C`hashcash\*(C'\fR to purge all stamps (expired and unexpired) now. .SH "stamp format (version 1)" .IX Header "stamp format (version 1)" The current stamp format is version 1. This tool can verify hashcash version 0 stamps also, but version 0 stamps are no longer created as they are being phased out in favor of the more extensible v1 stamp format. .IP "\fIver\fR:\fIbits\fR:\fIdate\fR:\fIresource\fR:[\fIext\fR]:\fIrand\fR:\fIcounter\fR" 4 .IX Item "ver:bits:date:resource:[ext]:rand:counter" .PP where .IP "\fIver\fR = 1" 4 .IX Item "ver = 1" .PD 0 .IP "\fIbits\fR = how many bits of partial-collision the stamp is claimed to have" 4 .IX Item "bits = how many bits of partial-collision the stamp is claimed to have" .IP "\fIdate\fR = YYMMDD[hhmm[ss]]" 4 .IX Item "date = YYMMDD[hhmm[ss]]" .IP "\fIresource\fR = resource string (eg \s-1IP\s0 address, email address)" 4 .IX Item "resource = resource string (eg IP address, email address)" .IP "\fIext\fR = extension \*(-- ignored in the current version" 4 .IX Item "ext = extension ignored in the current version" .PD Format of extension: .RS 4 .IP "[name1[=val1[,val2...]];[name2[=val1[,val2...]]...]]" 4 .IX Item "[name1[=val1[,val2...]];[name2[=val1[,val2...]]...]]" Note the value can also contain =. Example extension (not a real one): .Sp .Vb 1 \& name1=2,3;name2;name3=var1=2,var2=3,2,val .Ve .Sp Which would be extension name1 has values 2 and 3; extension name2 has no values; extension name3 has 3 values \*(L"var1=2\*(R", \*(L"var2=3\*(R", \*(L"2\*(R" and \&\*(L"val\*(R". The hashcash extension may interpret the values as it sees fit eg \*(L"var1=2\*(R" could be the value of an option to the extension name3. .RE .RS 4 .RE .IP "\fIrand\fR = string of random characters from alphabet a\-zA\-Z0\-9+/= to avoid collisions with other sender's stamps" 4 .IX Item "rand = string of random characters from alphabet a-zA-Z0-9+/= to avoid collisions with other sender's stamps" .PD 0 .IP "\fIcounter\fR = to find a stamp with the desired number of collision bits need to try lots of different strings this counter is incremented on each try. The Counter is also composed of characters from the alphabet a\-zA\-Z0\-9+/=. (Note an implementation is not required to count sequentially)." 4 .IX Item "counter = to find a stamp with the desired number of collision bits need to try lots of different strings this counter is incremented on each try. The Counter is also composed of characters from the alphabet a-zA-Z0-9+/=. (Note an implementation is not required to count sequentially)." .PD .SH "FILES" .IX Header "FILES" .IP "\fIhashcash.sdb\fR" 4 .IX Item "hashcash.sdb" default double spend database .SH "EXIT STATUS" .IX Header "EXIT STATUS" \&\f(CW\*(C`hashcash\*(C'\fR returns success (exit code 0) after successfully minting a stamp, after fully checking a stamp and finding it valid, and after a timing test. .PP If when checking a stamp it is found to be invalid (due to being malformed, being expired, having insufficient value, having a date in the future, or being double spent), \f(CW\*(C`hashcash\*(C'\fR returns failure (exit code 1). .PP If insufficient options are given to fully check a stamp, if the stamp is otherwise valid return unchecked (exit code 2). If the \fI\-y\fR flag is given and hashcash would normally return unchecked, exit code success is returned instead. .PP If any exception occurs (file read failure for database checking or corrupted database contents) an exit status of 3 is returned. .SH "AUTHOR" .IX Header "AUTHOR" Written by Adam Back .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fIsha1sum\fR\|(1), \fIsha1\-hashcash\fR\|(1), \fIsha1\fR\|(1), http://www.hashcash.org/ hashcash-1.21/utct.c0000664000076400007640000001227110351101612012733 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #include #include #include #include "sstring.h" #define BUILD_DLL #include "utct.h" #if defined( WIN32 ) #define snprintf _snprintf #endif static int char_pair_atoi( const char* pair ) { char str[3] = {0}; str[0] = pair[0]; str[1] = pair[1]; str[2] = '\0'; if ( !isdigit( str[0] ) || !isdigit( str[1] ) ) { return -1; } return atoi( str ); } /* deal with 2 char year issue */ static int century_offset_to_year( int century_offset ) { time_t time_now = time( 0 ); /* local time */ struct tm* now = gmtime( &time_now ); /* gmt broken down time */ int current_year = now->tm_year + 1900; int current_century_offset = current_year % 100; int current_century = (current_year - current_century_offset) / 100; int year = current_century * 100 + century_offset; /* assume year is in current century */ /* unless that leads to very old or very new, then adjust */ if ( year - current_year > 50 ) { year -= 100; } else if ( year - current_year < -50 ) { year += 100; } return year; } #define MAX_DATE 50 /* Sun Mar 10 19:25:06 2002 (EST) */ /* more logical time_t to string conversion */ const char* strtime( time_t* timep, int utc ) { static char str[MAX_DATE+1] = {0}; struct tm* isdst = NULL; char date[MAX_DATE+1] = {0}; char* timestr = NULL ; char* zone = NULL ; if ( utc ) { timestr = asctime( gmtime( timep ) ); zone = "UTC"; } else { isdst = localtime( timep ); timestr = asctime( isdst ); zone = tzname[isdst->tm_isdst]; } sstrncpy( date, timestr, MAX_DATE ); date[strlen(date)-1]='\0'; /* remove trailing \n */ snprintf( str, MAX_DATE, "%s (%s)", date, zone ); return str; } /* alternate form of mktime, this tm struct is in UTC time */ time_t mk_utctime( struct tm* tms ) { char* tz = getenv( "TZ" ); time_t res = 0 ; char *set_tz = NULL ; putenv( "TZ=UTC+0" ); res = mktime( tms ); if ( tz ) { set_tz = malloc( strlen( tz ) + 3 + 1 ); sprintf( set_tz, "TZ=%s", tz ); putenv( set_tz ); free( set_tz ); } else { putenv( "TZ" ); } return res; } time_t hashcash_from_utctimestr( const char utct[MAX_UTC+1], int utc ) { time_t failed = -1; time_t res = 0 ; struct tm tms; int tms_hour = 0; struct tm* dst = NULL; int utct_len = strlen( utct ); int century_offset = 0; if ( utct_len > MAX_UTC || utct_len < 2 || ( utct_len % 2 == 1 ) ) { return failed; } /* defaults */ tms.tm_mon = 0; tms.tm_mday = 1; tms.tm_hour = 0; tms.tm_min = 0; tms.tm_sec = 0; tms.tm_isdst = 0; /* daylight saving on */ tms_hour = tms.tm_hour; /* year */ century_offset = char_pair_atoi( utct ); if ( century_offset < 0 ) { return failed; } tms.tm_year = century_offset_to_year( century_offset ) - 1900; /* month -- optional */ if ( utct_len <= 2 ) { goto convert; } tms.tm_mon = char_pair_atoi( utct+2 ) - 1; if ( tms.tm_mon < 0 ) { return failed; } /* day */ if ( utct_len <= 4 ) { goto convert; } tms.tm_mday = char_pair_atoi( utct+4 ); if ( tms.tm_mday < 0 ) { return failed; } /* hour -- optional */ if ( utct_len <= 6 ) { goto convert; } tms.tm_hour = char_pair_atoi( utct+6 ); if ( tms.tm_hour < 0 ) { return failed; } /* minute -- optional */ if ( utct_len <= 8 ) { goto convert; } tms.tm_min = char_pair_atoi( utct+8 ); if ( tms.tm_min < 0 ) { return failed; } if ( utct_len <= 10 ) { goto convert; } /* second -- optional */ tms.tm_sec = char_pair_atoi( utct+10 ); if ( tms.tm_sec < 0 ) { return failed; } convert: if ( utc ) { return mk_utctime( &tms ); } else { /* note when switching from daylight to standard the last daylight hour(s) are ambiguous with the first hour(s) of standard time. The system calls give you the first hour(s) of standard time which is as good a choice as any. */ /* note also the undefined hour(s) between the last hour of standard time and the first hour of daylight time (when expressed in localtime) are illegal values and have undefined conversions, this code will do whatever the system calls do */ tms_hour = tms.tm_hour; res = mktime( &tms ); /* get time without DST adjust */ dst = localtime( &res ); /* convert back to get DST adjusted */ dst->tm_hour = tms_hour; /* put back in hour to convert */ res = mktime( dst ); /* redo conversion with DST adjustment */ return res; } } int hashcash_to_utctimestr( char utct[MAX_UTC+1], int len, time_t t ) { struct tm* tms = gmtime( &t ); if ( tms == NULL || len > MAX_UTC || len < 2 ) { return 0; } sprintf( utct, "%02d", tms->tm_year % 100 ); if ( len == 2 ) { goto leave; } sprintf( utct+2, "%02d", tms->tm_mon+1 ); if ( len == 4 ) { goto leave; } sprintf( utct+4, "%02d", tms->tm_mday ); if ( len == 6 ) { goto leave; } sprintf( utct+6, "%02d", tms->tm_hour ); if ( len == 8 ) { goto leave; } sprintf( utct+8, "%02d", tms->tm_min ); if ( len == 10 ) { goto leave; } sprintf( utct+10, "%02d", tms->tm_sec ); leave: return 1; } hashcash-1.21/fastmint_ansi_standard_1.c0000664000076400007640000002043310351101612016712 0ustar adamadam#include "libfastmint.h" int minter_ansi_standard_1_test(void) { /* This minter runs on any hardware */ return 1; } #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) #define Wfly(t) ( (t) < 16 ? W[t] : Wf( (t) ) ) #define ROUNDu(t,A,B,C,D,E,Func,K) \ E += S(5,A) + Func(B,C,D) + Wfly(t) + K; \ B = S(30,B); #define ROUNDn(t,A,B,C,D,E,Func,K) \ E += S(5,A) + Func(B,C,D) + W[t] + K; \ B = S(30,B); #define ROUND(t,A,B,C,D,E,Func,K) ROUNDu(t,A,B,C,D,E,Func,K) #define ROUND5( t, Func, K ) \ ROUND( t + 0, A, B, C, D, E, Func, K );\ ROUND( t + 1, E, A, B, C, D, Func, K );\ ROUND( t + 2, D, E, A, B, C, Func, K );\ ROUND( t + 3, C, D, E, A, B, Func, K );\ ROUND( t + 4, B, C, D, E, A, Func, K ) #define ROUND20( t, Func, K )\ ROUND5( t + 0, Func, K );\ ROUND5( t + 5, Func, K );\ ROUND5( t + 10, Func, K );\ ROUND5( t + 15, Func, K ) unsigned long minter_ansi_standard_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { MINTER_CALLBACK_VARS; unsigned long iters = 0; int t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits ; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; uInt32 A = 0 , B = 0 , C = 0 , D = 0 , E = 0 ; uInt32 W[80] = {0}; uInt32 H[5] = {0}, pH[5] = {0}; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X = (unsigned char*) W; int addressMask = 0 ; static const int endTest = 3; unsigned char *output = (unsigned char*) block; *best = 0; /* Work out whether we need to swap bytes during encoding */ addressMask = ( *(char*)&endTest ); /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } maxBits = 0; /* Copy block and IV to internal storage */ for(t=0; t < 16; t++) W[t] = GET_WORD(output + t*4); for(t=0; t < 5; t++) pH[t] = H[t] = IV[t]; /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter; iters++) { /* Encode iteration count into tail */ X[(tailIndex - 1) ^ addressMask] = p[(iters ) & 0x3f]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(tailIndex - 2) ^ addressMask] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(tailIndex - 3) ^ addressMask] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(tailIndex - 4) ^ addressMask] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(tailIndex - 5) ^ addressMask] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(tailIndex - 6) ^ addressMask] = p[(iters >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; for(t=16; t < 32; t++) Wf(t); ROUND( 0, A, B, C, D, E, F1, K1 ); ROUND( 1, E, A, B, C, D, F1, K1 ); ROUND( 2, D, E, A, B, C, F1, K1 ); ROUND( 3, C, D, E, A, B, F1, K1 ); ROUND( 4, B, C, D, E, A, F1, K1 ); ROUND( 5, A, B, C, D, E, F1, K1 ); ROUND( 6, E, A, B, C, D, F1, K1 ); if(tailIndex == 52) { ROUND( 7, D, E, A, B, C, F1, K1 ); ROUND( 8, C, D, E, A, B, F1, K1 ); ROUND( 9, B, C, D, E, A, F1, K1 ); ROUND(10, A, B, C, D, E, F1, K1 ); ROUND(11, E, A, B, C, D, F1, K1 ); } pH[0] = A; pH[1] = B; pH[2] = C; pH[3] = D; pH[4] = E; } /* Set up working variables */ A = pH[0]; B = pH[1]; C = pH[2]; D = pH[3]; E = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUND( 0, A, B, C, D, E, F1, K1 ); ROUND( 1, E, A, B, C, D, F1, K1 ); ROUND( 2, D, E, A, B, C, F1, K1 ); ROUND( 3, C, D, E, A, B, F1, K1 ); ROUND( 4, B, C, D, E, A, F1, K1 ); ROUND( 5, A, B, C, D, E, F1, K1 ); ROUND( 6, E, A, B, C, D, F1, K1 ); case 32: ROUND( 7, D, E, A, B, C, F1, K1 ); ROUND( 8, C, D, E, A, B, F1, K1 ); ROUND( 9, B, C, D, E, A, F1, K1 ); ROUND(10, A, B, C, D, E, F1, K1 ); ROUND(11, E, A, B, C, D, F1, K1 ); case 52: ROUND(12, D, E, A, B, C, F1, K1 ); ROUND(13, C, D, E, A, B, F1, K1 ); ROUND(14, B, C, D, E, A, F1, K1 ); ROUND(15, A, B, C, D, E, F1, K1 ); } if(tailIndex == 52) { ROUNDn(16, E, A, B, C, D, F1, K1 ); ROUNDn(17, D, E, A, B, C, F1, K1 ); ROUNDn(18, C, D, E, A, B, F1, K1 ); ROUNDn(19, B, C, D, E, A, F1, K1 ); ROUNDu(20, A, B, C, D, E, F2, K2 ); ROUNDn(21, E, A, B, C, D, F2, K2 ); ROUNDn(22, D, E, A, B, C, F2, K2 ); ROUNDu(23, C, D, E, A, B, F2, K2 ); ROUNDn(24, B, C, D, E, A, F2, K2 ); ROUNDn(25, A, B, C, D, E, F2, K2 ); ROUNDu(26, E, A, B, C, D, F2, K2 ); ROUNDn(27, D, E, A, B, C, F2, K2 ); ROUNDu(28, C, D, E, A, B, F2, K2 ); ROUNDu(29, B, C, D, E, A, F2, K2 ); ROUNDn(30, A, B, C, D, E, F2, K2 ); } else if (tailIndex == 32) { ROUNDn(16, E, A, B, C, D, F1, K1 ); ROUNDn(17, D, E, A, B, C, F1, K1 ); ROUNDn(18, C, D, E, A, B, F1, K1 ); ROUNDn(19, B, C, D, E, A, F1, K1 ); ROUNDn(20, A, B, C, D, E, F2, K2 ); ROUNDu(21, E, A, B, C, D, F2, K2 ); ROUNDn(22, D, E, A, B, C, F2, K2 ); ROUNDu(23, C, D, E, A, B, F2, K2 ); ROUNDu(24, B, C, D, E, A, F2, K2 ); ROUNDn(25, A, B, C, D, E, F2, K2 ); ROUNDu(26, E, A, B, C, D, F2, K2 ); ROUNDu(27, D, E, A, B, C, F2, K2 ); ROUNDn(28, C, D, E, A, B, F2, K2 ); ROUNDu(29, B, C, D, E, A, F2, K2 ); ROUNDu(30, A, B, C, D, E, F2, K2 ); } else { ROUNDu(16, E, A, B, C, D, F1, K1 ); ROUNDu(17, D, E, A, B, C, F1, K1 ); ROUNDu(18, C, D, E, A, B, F1, K1 ); ROUNDu(19, B, C, D, E, A, F1, K1 ); ROUNDu(20, A, B, C, D, E, F2, K2 ); ROUNDu(21, E, A, B, C, D, F2, K2 ); ROUNDu(22, D, E, A, B, C, F2, K2 ); ROUNDu(23, C, D, E, A, B, F2, K2 ); ROUNDu(24, B, C, D, E, A, F2, K2 ); ROUNDu(25, A, B, C, D, E, F2, K2 ); ROUNDu(26, E, A, B, C, D, F2, K2 ); ROUNDu(27, D, E, A, B, C, F2, K2 ); ROUNDu(28, C, D, E, A, B, F2, K2 ); ROUNDu(29, B, C, D, E, A, F2, K2 ); ROUNDu(30, A, B, C, D, E, F2, K2 ); } ROUNDu(31, E, A, B, C, D, F2, K2 ); ROUNDu(32, D, E, A, B, C, F2, K2 ); ROUNDu(33, C, D, E, A, B, F2, K2 ); ROUNDu(34, B, C, D, E, A, F2, K2 ); ROUNDu(35, A, B, C, D, E, F2, K2 ); ROUNDu(36, E, A, B, C, D, F2, K2 ); ROUNDu(37, D, E, A, B, C, F2, K2 ); ROUNDu(38, C, D, E, A, B, F2, K2 ); ROUNDu(39, B, C, D, E, A, F2, K2 ); ROUND20(40,F3,K3); ROUND20(60,F4,K4); /* Mix in the IV again */ A += H[0]; B += H[1]; C += H[2]; D += H[3]; E += H[4]; /* Is this the best bit count so far? */ if(!(A & bitMask1Low) && !(B & bitMask1High)) { /* Count bits */ gotBits = 0; if(A) { s = A; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(B) { s = B; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } /* Copy this result back to the block buffer */ for(t=0; t < 16; t++) PUT_WORD(output + t*4, W[t]); /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+1; } } MINTER_CALLBACK(); } return iters+1; } hashcash-1.21/lock.h0000664000076400007640000000033710351101612012711 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _lock_h ) #define _lock_h #include #include int lock_write( FILE* f ); int lock_read( FILE* f ); int lock_unlock( FILE* f ); #endif hashcash-1.21/example.c0000664000076400007640000002702110351101612013406 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #include "sstring.h" #include "sdb.h" #include "hashcash.h" #include "getopt.h" /* simplified hashcash.c example showing how to use the library * functions. This code can only handle one operation per invocation */ void usage( const char* msg ); void die( int err ); void die_msg( const char* str ); int parse_period( const char* aperiod, long* resp ); int progress_callback(int percent, int largest, int target, double counter, double expected, void* user); #define EOK 0 /* no error */ #define EINPUT -1 /* error invalid input */ #define MAX_PERIOD 11 /* max time_t = 10 plus 's' */ #define MAX_HDR 256 #define EXIT_ERROR 3 int main( int argc, char* argv[] ) { int err = 0; int opt, bits=20, str_type = TYPE_WILD, ignore_boundary_flag; int case_flag = 0, check_flag = 0, db_flag = 0, core, res = HASHCASH_FAIL; int mint_flag = 0, purge_flag = 0, core_flag = 0; int time_width = 6, compress = 0, checked = 0; long anon_random = 0, anon_period = 0, purge_period = 0, time_period = 0; time_t real_time = time(0); /* now, in UTCTIME */ time_t now_time = real_time, token_time; int speed_flag = 0, utc_flag = 0, version_flag = 0, hdr_flag = 0; char* header = NULL; char* db_filename = "hashcash.sdb"; char* purge_resource = NULL; char* resource = NULL; char* ext = NULL; int purge_all = 0; long validity_period = 28*TIME_DAY; /* default validity period: 28 days */ long purge_validity_period = 28*TIME_DAY; /* same default */ long grace_period = 2*TIME_DAY; /* default grace period: 2 days */ long valid_for = HASHCASH_INVALID; char *new_token = NULL ; char token[MAX_TOK+1], token_utime[MAX_UTC+1], period[MAX_PERIOD+1]; double tries_taken = 0; void** compile = NULL; char* re_err = NULL; DB db; hashcash_callback callback = NULL; while ( (opt=getopt( argc, argv, "-a:b:cde:f:g:hij:klmnop:qr:sSt:uvwx:yz:CEMO:PSVXZ:")) >0 ) { switch( opt ) { case 'a': if ( !parse_period( optarg, &anon_period ) ) { usage( "error: -a invalid period arg" ); } break; case 'b': bits = atoi( optarg+1 ); if ( bits < 0 ) { usage( "error: -b invalid bits arg" ); } break; case 'C': case_flag = 1; break; case 'c': check_flag = 1; break; case 'd': db_flag = 1; break; case 'e': if ( !parse_period( optarg, &validity_period ) || validity_period < 0 ) { usage( "error: -e invalid validity period" ); } break; case 'E': str_type = TYPE_REGEXP; break; case 'f': db_filename = strdup( optarg ); break; case 'g': if ( optarg[0] == '-' ) { usage( "error: -g -ve grace period not valid" ); } if ( !parse_period( optarg, &grace_period ) ) { usage( "error: -g invalid grace period format" ); } break; case 'h': usage( "" ); break; case 'i': ignore_boundary_flag = 1; break; case 'j': purge_resource = optarg; break; case 'k': purge_all = 1; break; case 'm': mint_flag = 1; break; case 'M': str_type = TYPE_WILD; break; case 'O': core_flag = 1; core = atoi( optarg ); res = hashcash_use_core( core ); if ( res < 1 ) { usage( res == -1 ? "error: -O no such core\n" : "error: -O core does not work on this platform" ); } break; case 'p': purge_flag = 1; if ( strcmp( optarg, "now" ) == 0 ) { purge_period = 0; } else if ( !parse_period( optarg, &purge_period ) || purge_period < 0 ) { usage( "error: -p invalid purge interval" ); } break; case 'P': callback = progress_callback; break; case 1: case 'r': resource = optarg; break; case 's': speed_flag = 1; break; case 'S': str_type = TYPE_STR; break; case 't': if ( optarg[0] == '-' || optarg[0] == '+' ) { if ( !parse_period( optarg, &time_period ) ) { usage( "error: -t invalid relative time format" ); } now_time += time_period; } else { now_time = hashcash_from_utctimestr( optarg, utc_flag ); if ( now_time == (time_t)-1 ) { usage( "error: -t invalid time format" ); } } break; case 'u': utc_flag = 1; break; case 'V': version_flag = 1; break; case 'x': ext = strdup( optarg ); break; case 'X': hdr_flag = 1; sstrncpy( header, "X-Hashcash: ", MAX_HDR ); break; case 'z': time_width = atoi( optarg ); if ( time_width != 6 && time_width != 10 && time_width != 12 ) { usage( "error: -z invalid time width: must be 6, 10 or 12" ); } break; case 'Z': compress = atoi( optarg ); break; case '?': fprintf( stderr, "error: unrecognized option -%c", optopt ); usage( "" ); break; case ':': fprintf( stderr, "error: option -%c missing argument", optopt ); usage( "" ); break; default: usage( "error with argument processing" ); break; } } if ( version_flag ) { fprintf( stdout, "%s\n", HASHCASH_VERSION_STRING ); exit( EXIT_FAILURE ); } if ( speed_flag ) { if ( core_flag ) { hashcash_benchtest( 3, core ); } else { hashcash_benchtest( 3, -1 ); } exit( EXIT_FAILURE ); } if ( mint_flag ) { err = hashcash_mint( now_time, time_width, resource, bits, anon_period, &new_token, &anon_random, &tries_taken, ext, compress, callback, NULL ); } else if ( purge_flag ) { if ( !hashcash_db_open( &db, db_filename, &err ) ) { die(err); } if ( !hashcash_db_purge( &db, purge_resource, str_type, case_flag, validity_period, grace_period, purge_all, purge_period, now_time, &err ) ) { die(err); } if ( !hashcash_db_close( &db, &err ) ) { die(err); } } else if ( check_flag ) { valid_for = hashcash_check( token, case_flag, resource, compile, &re_err, str_type, now_time, validity_period, grace_period, bits, &token_time ); if ( valid_for < 0 ) { switch ( valid_for ) { case HASHCASH_INSUFFICIENT_BITS: fprintf( stderr, "no match: token has insufficient bits\n" ); break; case HASHCASH_VALID_IN_FUTURE: fprintf( stderr, "no match: valid in future\n" ); break; case HASHCASH_EXPIRED: fprintf( stderr, "no match: token expired\n" ); break; case HASHCASH_WRONG_RESOURCE: fprintf( stderr, "no match: wrong resource\n" ); break; case HASHCASH_REGEXP_ERROR: fprintf( stderr, "regexp error: " ); die_msg( re_err ); break; default: die_msg( "internal error" ); break; } exit( EXIT_FAILURE ); } checked = 1; } if ( db_flag && check_flag && checked ) { if ( !hashcash_db_open( &db, db_filename, &err ) ) { die(err); } if ( hashcash_db_in( &db, token, token_utime, &err ) ) { if ( err ) { die(err); } fprintf( stderr, "stamp: double spent\n" ); } sprintf( period, "%ld", (long)validity_period ); if ( !hashcash_db_add( &db, token, period, &err ) ) { die(err); } if ( !hashcash_db_close( &db, &err ) ) { die(err); } } exit( EXIT_SUCCESS ); } int progress_callback(int percent, int largest, int target, double counter, double expected, void* user) { static int previous_percent = -1; static int previous_largest; double previous_counter = -1; if ( previous_counter != counter || previous_percent != percent || previous_largest != largest ) { fprintf( stderr, "percent: %.0lf/%.0lf = %d%% [%d/%d bits]\r", counter, expected, percent, largest, target ); previous_percent = percent; previous_largest = largest; previous_counter = counter; } return 0; return 1; } void usage( const char* msg ) { if ( msg ) { fputs( msg, stderr ); fputs( "\n\n", stderr ); } fprintf( stderr, "mint:\t\thashcash [-m] [opts] resource\n" ); fprintf( stderr, "measure speed:\thashcash -s [-b bits]\n" ); fprintf( stderr, "check:\t\thashcash -c [opts] -d -r resource [-e period] [token]\n" ); fprintf( stderr, "purge expired:\thashcash -p now [-k] [-j resource] [-t time] [-u]\n" ); fprintf( stderr, "\n" ); fprintf( stderr, "\t-b bits\t\tfind or check partial hash collision of length bits\n" ); fprintf( stderr, "\t-d\t\tuse database (for double spending detection)\n" ); fprintf( stderr, "\t-r resource\tresource name for minting or checking token\n" ); fprintf( stderr, "\t-e period\ttime until token expires\n" ); fprintf( stderr, "\t-g\t\tgrace period for clock skew\n" ); fprintf( stderr, "\t-t time\t\tmodify current time token created at\n" ); fprintf( stderr, "\t-a time\t\tmodify time by random amount in range given\n" ); fprintf( stderr, "\t-u\t\tgive time in UTC instead of local time\n" ); fprintf( stderr, "\t-h\t\tprint this usage info\n" ); fprintf( stderr, "\t-f dbfile\tuse filename dbfile for database\n" ); fprintf( stderr, "\t-j resource\twith -p delete just tokens matching the given resource\n" ); fprintf( stderr, "\t-k\t\twith -p delete all not just expired\n" ); fprintf( stderr, "\t-x ext\t\tput in extension field\n" ); fprintf( stderr, "\t-X\t\toutput with header format 'X-Hashcash: '\n" ); fprintf( stderr, "\t-i\t\twith -X and -c, check msg body as well\n" ); fprintf( stderr, "\t-z width\twidth of time field 6,10 or 12 chars (default 6)\n" ); fprintf( stderr, "\t-C\t\tmatch resources as case sensitive (default insensitive)\n" ); fprintf( stderr, "\t-S\t\tmatch following resources as text strings\n" ); fprintf( stderr, "\t-W\t\tmatch following resources with wildcards (default)\n" ); fprintf( stderr, "\t-E\t\tmatch following resources as regular expression\n" ); fprintf( stderr, "\t-P\t\tshow progress while searching\n"); fprintf( stderr, "\t-O core\t\tuse specified minting core\n"); fprintf( stderr, "examples:\n" ); fprintf( stderr, "\thashcash -mb20 foo # mint 20 bit collision\n" ); fprintf( stderr, "\thashcash -cdb20 -r foo 1:20:040806:foo::831d0c6f22eb81ff:15eae4 # check collision\n" ); fprintf( stderr, "\n" ); fprintf( stderr, "see hashcash (1) man page or http://www.hashcash.org/ for more details.\n" ); exit( EXIT_ERROR ); } int parse_period( const char* aperiod, long* resp ) { int period_len; char last_char; long res = 1; char period_array[MAX_PERIOD+1]; char* period = period_array; if ( period == NULL ) { return 0; } period_len = strlen( aperiod ); if ( period_len == 0 ) { return 0; } last_char = aperiod[period_len-1]; if ( ! isdigit( last_char ) && ! strchr( "YyMdhmsw", last_char ) ) { return 0; } sstrncpy( period, aperiod, MAX_PERIOD ); if ( ! isdigit( last_char ) ) { period[--period_len] = '\0'; } if ( period[0] == '+' || period[0] == '-' ) { if ( period[0] == '-' ) { res = -1; } period++; period_len--; } if ( period_len > 0 ) { res *= atoi( period ); } switch ( last_char ) { case 's': break; case 'm': res *= TIME_MINUTE; break; case 'h': res *= TIME_HOUR; break; case 'd': res *= TIME_DAY; break; case 'w': res *= TIME_DAY*7; break; case 'M': res *= TIME_MONTH; break; case 'y': case 'Y': res *= TIME_YEAR; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; default: return 0; } *resp = res; return 1; } void die( int err ) { const char* str = ""; switch ( err ) { case EOK: exit( EXIT_SUCCESS ); break; case EINPUT: str = "invalid inputs"; break; default: str = strerror( err ); break; } fprintf( stderr, "error: %s\n", str ); exit( EXIT_ERROR ); } void die_msg( const char* str ) { fprintf( stderr, str ); fprintf( stderr, "\n" ); exit( EXIT_ERROR ); } hashcash-1.21/sha1test.c0000664000076400007640000000577610351101612013524 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #include "sha1.h" byte b = 0 ; #define BUF_SIZE 1024 int main( int argc, char* argv[] ) { SHA1_ctx ctx = {} ; int i = 0 , j = 0 ; byte digest[ SHA1_DIGEST_BYTES ] = {} ; clock_t start = 0 , end = 0 , tmp = 0 ; double elapsed = 0 ; /* test 1 data */ const char* test1 = "abc"; byte result1[ SHA1_DIGEST_BYTES ] = { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }; /* test 2 date */ const char* test2 = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; byte result2[ SHA1_DIGEST_BYTES ] = { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }; /* test 3 data */ /* test 3 is 1,000,000 letter "a"s. */ /* fill test 3 with "a"s and hash this 5000 times */ byte test3[ 200 ]; byte result3[ SHA1_DIGEST_BYTES ] = { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F }; /* test 1 */ printf( "test 1\n\n" ); SHA1_Init( &ctx ); SHA1_Update( &ctx, test1, strlen( test1 ) ); SHA1_Final( &ctx, digest ); printf( "SHA1(\"%s\") = \n\t", test1 ); for ( i = 0; i < 20 ; i++ ) { printf( "%02x", digest[ i ] ); } if ( memcmp( digest, result1, SHA1_DIGEST_BYTES ) == 0 ) { printf( " test ok\n" ); } else { printf( " test failed\n" ); } printf( "\n" ); /* test 2 */ printf( "test 2\n\n" ); SHA1_Init( &ctx ); SHA1_Update( &ctx, test2, strlen( test2 ) ); SHA1_Final( &ctx, digest ); printf( "SHA1(\"%s\") = \n\t", test2 ); for ( i = 0; i < SHA1_DIGEST_BYTES ; i++ ) { printf( "%02x", digest[ i ] ); } if ( memcmp( digest, result2, SHA1_DIGEST_BYTES ) == 0 ) { printf( " test ok\n" ); } else { printf( " test failed\n" ); } printf( "\n" ); /* test 3 */ printf( "test 3\n\n" ); /* we'll time test3 to see how many Mb/s we can do */ end = clock(); do { start = clock(); } while ( start == end ); printf( "start = %ld\n", start ); for ( j = 0; j < 10; j++ ) { SHA1_Init( &ctx ); memset( test3 , 'a', 200 ); /* fill test3 with "a"s */ for( i = 0; i < 5000; i++ ) { /* digest 5000 times */ SHA1_Update( &ctx, test3, 200 ); } SHA1_Final( &ctx, digest ); } end = clock(); if ( end < start ) { tmp = end; end = start; start = tmp; } elapsed = ((end-start)/(double)CLOCKS_PER_SEC); printf( "SHA1(\"a\" x 1,000,000) = \n\t" ); for ( i = 0; i < SHA1_DIGEST_BYTES ; i++ ) { printf( "%02x", digest[ i ] ); } if ( memcmp( digest, result3, SHA1_DIGEST_BYTES ) == 0 ) { printf( " test ok\n" ); } else { printf( " test failed\n" ); } printf( "\n" ); /* report timing information */ printf( "speed: %.2f Mb/s\n", 10.0 / elapsed ); return 0; } hashcash-1.21/sha1.h0000664000076400007640000000314410351101612012614 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _sha1_h ) #define _sha1_h /* for size_t */ #include #include "types.h" #if defined( __cplusplus ) extern "C" { #endif #define SHA1_INPUT_BYTES 64 /* 512 bits */ #define SHA1_INPUT_WORDS ( SHA1_INPUT_BYTES >> 2 ) #define SHA1_DIGEST_WORDS 5 /* 160 bits */ #define SHA1_DIGEST_BYTES ( SHA1_DIGEST_WORDS * 4 ) #if defined( OPENSSL ) #include #define SHA1_ctx SHA_CTX #define SHA1_Final( ctx, digest ) SHA1_Final( digest, ctx ) #undef SHA1_DIGEST_BYTES #define SHA1_DIGEST_BYTES SHA_DIGEST_LENGTH #define SHA1_Init_With_IV( ctx, iv ) \ do { \ (ctx)->h0 = iv[0]; \ (ctx)->h1 = iv[1]; \ (ctx)->h2 = iv[2]; \ (ctx)->h3 = iv[3]; \ (ctx)->h4 = iv[4]; \ (ctx)->Nh = 0; \ (ctx)->Nl = 0; \ (ctx)->num = 0l \ } while (0) #define SHA1_Transform( iv, data ) SHA1_Xform( iv, data ) #else typedef struct { word32 H[ SHA1_DIGEST_WORDS ]; #if defined( word64 ) word64 bits; /* we want a 64 bit word */ #else word32 hbits, lbits; /* if we don't have one we simulate it */ #endif byte M[ SHA1_INPUT_BYTES ]; } SHA1_ctx; void SHA1_Init ( SHA1_ctx* ); void SHA1_Update( SHA1_ctx*, const void*, size_t ); void SHA1_Final ( SHA1_ctx*, byte[ SHA1_DIGEST_BYTES ] ); /* these provide extra access to internals of SHA1 for MDC and MACs */ void SHA1_Init_With_IV( SHA1_ctx*, const byte[ SHA1_DIGEST_BYTES ] ); #endif void SHA1_Xform( word32[ SHA1_DIGEST_WORDS ], const byte[ SHA1_INPUT_BYTES ] ); #if defined( __cplusplus ) } #endif #endif hashcash-1.21/stamp0000664000076400007640000000006510351101612012655 0ustar adamadam1:10:041008:foo::Dvt2MghwuVEHkF/k:0000000000000000Bg hashcash-1.21/libfastmint.h0000664000076400007640000002061210351101612014273 0ustar adamadam#if !defined( _libfastmint_h ) #define _libfastmint_h #if defined(WIN32) #include #undef small #else #include #endif #include "hashcash.h" #if defined(WIN32) #define MILLISEC 1 #define TIMETYPE DWORD #define TIME0 0 #define timer(x) (*(x)=GetTickCount()) #else #define MILLISEC 1000 #define TIME0 {0,0} #define TIMETYPE struct timeval #define timer(x) gettimeofday(x,NULL) #endif #define MINTER_CALLBACK_ARGS hashcash_callback cb, void* user_args, \ double counter, double expected #define MINTER_CALLBACK_VARS double percent; \ TIMETYPE prev=TIME0, curr; \ int lastBits = 0 /* in minter if have to do something for acces to floating point * operations override this */ /* eg mmx minter sets to this: __builtin_ia32_emms() */ #define MINTER_CALLBACK_CLEANUP_FP /**/ #if defined(WIN32) #define small_time_diff_gt( c, p, intv ) \ ((c>p?c-p:(c+(((TIMETYPE)-1)-p)))>(intv)) #else /* works for intervals < 1 second */ #define small_time_diff_gt( c, p, intv ) \ (c.tv_sec-p.tv_sec>1 || \ (c.tv_usec+((c.tv_sec - p.tv_sec)?1000000:0))-p.tv_usec>(intv)) #endif #define MINTER_CALLBACK() \ do { \ if (cb != NULL && (iters & 0xFFFF) == 0) { \ timer(&curr); \ if (gotBits > lastBits || \ small_time_diff_gt(curr,prev,100*MILLISEC)) { \ MINTER_CALLBACK_CLEANUP_FP; \ percent = (int)(((counter+iters)/ \ expected*100)+0.5); \ if (!cb(percent,*best,bits, \ counter+iters,expected,user_args)) { \ *best = -1; \ return 0; \ } \ prev = curr; \ lastBits = gotBits; \ } \ } \ } while (0) typedef unsigned int uInt32; typedef unsigned long (* HC_Mint_Routine)(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); typedef int (* HC_Mint_Capable_Routine)(void); typedef enum { EncodeHexUpper, /* 0123456789ABCDEF */ EncodeHexLower, /* 0123456789abcdef */ EncodeAlpha16Upper, /* ABCDEFGHIJKLMNOP */ EncodeAlpha16Lower, /* abcdefghijklmnop */ EncodeBase64 /* 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/ */ } EncodeAlphabet; typedef struct { const char *name; EncodeAlphabet encoding; HC_Mint_Routine func; HC_Mint_Capable_Routine test; } HC_Minter; #define HC_CPU_SUPPORTS_ALTIVEC 0x01 #define HC_CPU_SUPPORTS_MMX 0x02 extern int gProcessorSupportFlags; extern const char *encodeAlphabets[]; extern const int EncodeBitRate[]; extern void hashcash_select_minter(); /* Portably write a word into a byte array */ #define PUT_WORD(_dst, _src) { \ *((unsigned char*)(_dst)+0) = ((_src) >> 24) & 0xFF; \ *((unsigned char*)(_dst)+1) = ((_src) >> 16) & 0xFF; \ *((unsigned char*)(_dst)+2) = ((_src) >> 8) & 0xFF; \ *((unsigned char*)(_dst)+3) = ((_src) ) & 0xFF; \ } /* Portably load bytes into a word */ #define GET_WORD(_src) ( \ (*((unsigned char*)(_src)+0) << 24) | \ (*((unsigned char*)(_src)+1) << 16) | \ (*((unsigned char*)(_src)+2) << 8) | \ (*((unsigned char*)(_src)+3) ) ) /* Attempt to mint a hashcash token with a given bit-value. * Will append a random string to token that produces the required collision, * then return a pointer to the resultant string in result. Caller must free() * result buffer after use. * Returns the number of bits actually minted (may be more or less than requested). */ extern double hashcash_fastmint(const int bits, const char *token, int small, char **result, hashcash_callback cb, void* user_arg); /* Perform a quick benchmark of the selected minting backend. Returns speed. */ extern unsigned long hashcash_per_sec(void); /* Test and benchmark available hashcash minting backends. Returns the speed * of the fastest valid routine, and updates fastest_minter as appropriate. * Uses one or more known solutions to gauge both speed and accuracy. * Optionally displays status and benchmark results on console. */ extern unsigned long hashcash_benchtest(int verbose, int core); /* Minting backend routines. * These are in two parts: * - HC_Mint_Routine is the actual backend. Attempts to produce at least N-bit * collision by incrementing the tail section of the block. Block need not * be initial block, as custom IV is provided, but must be terminal block. * Tail index points just after last character - ie. to beginning of SHA-1 * padding - do not overwrite padding. At least 8 characters will be present. * Will bail out after maxIter attempts, within some reasonable tolerance. * - HC_Mint_Capable_Routine is a trivial test to see if the current machine * supports necessary hardware features for the backend. Returns boolean. */ /* Standard ANSI-C 1-pipe "compact" implementation - for use on x86 and other * register-limited architectures. */ extern unsigned long minter_ansi_compact_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_ansi_compact_1_test(void); /* Standard ANSI-C 1-pipe "Method B" implementation - for use on register-rich architectures. */ extern unsigned long minter_ansi_standard_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_ansi_standard_1_test(void); /* Standard ANSI-C 1-pipe "ultra-compact" implementation - for use on architectures with * severely limited L1 caches (eg. M68K). */ extern unsigned long minter_ansi_ultracompact_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_ansi_ultracompact_1_test(void); /* Standard ANSI-C 2-pipe "compact" implementation - for use on register-rich architectures. */ extern unsigned long minter_ansi_compact_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_ansi_compact_2_test(void); /* Standard ANSI-C 2-pipe "Method B" implementation - for use on register-rich architectures. */ extern unsigned long minter_ansi_standard_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_ansi_standard_2_test(void); /* PowerPC Altivec 1x4-pipe "Method B" implementation - for use on G4 and higher systems. */ extern unsigned long minter_altivec_standard_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_altivec_standard_1_test(void); /* PowerPC Altivec 2x4-pipe "Method B" implementation - for use on G4 and higher systems. Hand-optimised. */ extern unsigned long minter_altivec_standard_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_altivec_standard_2_test(void); /* PowerPC Altivec 2x4-pipe "compact" implementation - for use on G4 and higher systems. Hand-optimised. */ extern unsigned long minter_altivec_compact_2(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_altivec_compact_2_test(void); /* AMD64/x86 MMX 1x2-pipe "Method B" implementation - for use on Pentium-MMX/2/3/4, K6-2/3, * Athlon, Athlon64, Opteron, and compatible systems. Hand-optimised. */ extern unsigned long minter_mmx_standard_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_mmx_standard_1_test(void); /* AMD64/x86 MMX 1x2-pipe "compact" implementation - for use on Pentium-MMX/2/3/4, K6-2/3, * Athlon, Athlon64, Opteron, and compatible systems. Hand-optimised. */ extern unsigned long minter_mmx_compact_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); extern int minter_mmx_compact_1_test(void); /* use SHA1 library (integrated or openSSL depending on how compiled) */ extern int minter_library_test(void); extern unsigned long minter_library(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS); #endif hashcash-1.21/sdb.h0000664000076400007640000000451410351101612012532 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _sdb_h ) #define _sdb_h #include #if !defined( PATH_MAX ) #define PATH_MAX 256 /* sane smallish value */ #endif #if defined( __cplusplus ) extern "C" { #endif #if !defined(HCEXPORT) #if !defined(WIN32) || defined(MONOLITHIC) #define HCEXPORT #elif defined(BUILD_DLL) #define HCEXPORT __declspec(dllexport) #else /* USE_DLL */ #define HCEXPORT extern __declspec(dllimport) #endif #endif typedef struct { FILE* file; char filename[PATH_MAX+1]; long read_pos; long write_pos; } DB; #define MAX_KEY 10240+1024+1 #define MAX_VAL 1024 #define READ_MODE 0 #define WRITE_MODE 1 /* higher level functions */ #define PURGED_KEY "last_purged" HCEXPORT int hashcash_db_open( DB* db, const char* db_filename, int* err ); HCEXPORT int hashcash_db_in( DB* db, char* token, char *period, int* err ); HCEXPORT int hashcash_db_add( DB* db, char* token, char *period, int* err ); HCEXPORT int hashcash_db_close( DB* db, int* err ); /* low level functions */ /* empty string matches any key for sdb_find, sdb_findnext */ #define ANY_KEY "" typedef int (*sdb_wcallback)( const char* key, char* val, void* arg, int* err ); typedef int (*sdb_rcallback)( const char* key, const char* val, void* arg, int* err ); /* NOTE: keys should not contain spaces */ HCEXPORT int sdb_open( DB*, const char* filename, int* err ); HCEXPORT int sdb_add( DB*, const char* key, const char* val, int* err ); HCEXPORT int sdb_updateiterate( DB*, sdb_wcallback cb, void* arg, int* err ); HCEXPORT int sdb_del( DB*, const char* key, int* err ); HCEXPORT int sdb_findfirst( DB*, char* key, int klen, char* val, int vlen, int* err ); HCEXPORT int sdb_findnext( DB*, char* key, int klen, char* val, int vlen, int* err ); HCEXPORT int sdb_lookup( DB*, const char* key, char* val, int vlen, int* err ); HCEXPORT int sdb_lookupnext( DB*, const char* key, char* val, int vlen, int* err ); HCEXPORT int sdb_close( DB*, int* err ); HCEXPORT int sdb_callbacklookup( DB*, sdb_rcallback cb, void* arg, char* key, int klen, char* val, int vlen, int* err ); HCEXPORT int sdb_callbacklookupnext( DB*, sdb_rcallback cb, void* arg, char* key, int klen, char* val, int vlen, int* err ); #if defined( __cplusplus ) } #endif #endif hashcash-1.21/contrib/0000775000076400007640000000000010351101612013245 5ustar adamadamhashcash-1.21/contrib/hashcash-sendmail0000664000076400007640000014454210351101612016556 0ustar adamadam#!/usr/bin/perl my $RCS = '$Id: hashcash-sendmail,v 1.18 2004/08/07 18:36:16 kyle Exp $'; # # hashcash-sendmail -- queues messages for later delivery after adding hashcash # This is meant to be a program called AS sendmail from a MUA. # # An up-to-date version is normally here: # http://www.toehold.com/~kyle/hashcash/ # # Consider this ALPHA software. It works for me, but it has not been through # much real testing. # # Copyright (C) 2004 Kyle Hasselbacher # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # This script is supposed to be a stand-in for sendmail. It records the # arguments its given and the standard input it receives, and those are # eventually passed on to sendmail. Typically the arguments are passed # faithfully to sendmail, but we meddle with them if there's a Bcc. The # message is always modified by adding X-Hashcash to the end of the headers. # use strict; use Data::Dumper; use File::Copy; use Fcntl qw(:DEFAULT :flock); use POSIX qw( setsid ); use IO::File; use vars qw( @rcpt @args ); use IPC::Open3; my $home = $ENV{'HOME'} || $ENV{'LOGDIR'} || (getpwid($<))[7]; # # The workdir is expected to have a queue directory and a tmp directory. # hashcash-sendmail looks for bitconf there later. # All messages that I haven't seen yet are in queue/*.msg files, and they # each have a corresponding *.dat file with instructions. The .msg file # is exactly the standard input for sendmail. The *.dat file is a Perl # parsable file which contains two arrays. One is the exact argument list # for sendmail. The other is instructions for what hashcash to add to the # message. The .dat files never move from the queue, but the .msg files # move to tmp/ when they're being worked on. # my $workdir = "$home/.hashcash"; END { cleanup(); } # When I get a signal, cleanup() is called twice. I use this flag to avoid # doing anything the second time. It also serves to keep from doing cleanup # if I die before even entering the queue. my $need_cleanup = 0; # Where to write log messages. my $logfile = "$workdir/hashcash-log"; # Where to find the recipients file. See read_bitconf() comments for format. my $conffile = "$workdir/bitconf"; # Create my directories if they're not there already. foreach my $dir ( $workdir, "$workdir/queue", "$workdir/tmp" ) { if ( ! -d $dir && ! mkdir( $dir ) ) { die "Can't make nonexistant workdir '$dir': $!\n"; } } my @bitconf = read_bitconf(); # This is the number of bits we use if nothing else is specified. my $bits_to_compute = 20; # Part of the filename used for output. my $filebase = time() . ".$$"; my $extra_expense = 0; # Whether this message is extra expensive (low priority) my $confirmed = 0; # Whether this is a confirmed message (needing no cash) # This is used for the Received: line I add to messages, and it gets logged # by the daemon when it starts. my $hostname = `hostname -f 2> /dev/null`; $hostname =~ s/\s//g; # If there are no args, read a request and queue that. # hashcash-request just opens a pipe to hashcash-sendmail and ships # in a Data::Dumper file. if ( ! @ARGV ) { my $rcptcode = join( '', ); eval $rcptcode; if ( $@ ) { fatal( "input request does not parse: $@\n" ); } queue( $filebase, undef, \@rcpt, \@bitconf ); } else { my @args = @ARGV; # This will be all the sendmail arguments BEFORE the list of envelope # recipients (so that we can meddle with them later). my @pre_args = (); # Try to figure out our envelope recipients. # This is probably NOT close enough to sendmail compatibility for prime time. my @env_recip = (); process_args( \@args, \@pre_args, \@env_recip ); my @to = (); # According-to-headers message recipients. my $msgfh = new IO::File; if ( ! open( $msgfh, ">$workdir/tmp/$filebase.msg" ) ) { die "Can't write $workdir/tmp/$filebase.msg: $!\n"; } else { process_msg( $msgfh, \@to ); close( $msgfh ); } queue_messages( \@args, \@env_recip, \@to, \@pre_args ); # Done with the temp file. unlink( "$workdir/tmp/$filebase.msg" ) || die "Can't unlink '$workdir/tmp/$filebase.msg': $!\n"; } #### #### DAEMON STARTS HERE #### # # At this point, the incoming message/request has been queued. We're about # to fork away from the caller to work on processing the queue in the # background. If there's already an earlier daemon doing this, we just # send it a wake-up call and die. # # For now, at least, the daemon code and the sendmail code are distinct. # They haven't needed to call the other's functions. That having been # said, I haven't really marked which functions are which. If you're # doing some work in here, make sure that the daemon calls fatal() and # complain() rather than 'die' and 'warn'. There's nothing stopping # sendmail from using those (and logline), but it hasn't yet. use sigtrap qw( handler cleanup normal-signals ); # # Ignore the USR1 signal for now. We toggle this on and off. When waiting # for a token to finish, we set a handler that will process the interrupt # (by rescanning the queue for a better job to do). When working (not # waiting), we ignore the hangup. # $SIG{ USR1 } = 'IGNORE'; # Separate myself from the shell. # It's important to do this before doing the pidfile stuff # because daemonize() forks, and we want to get the right PID in the pidfile. daemonize(); # $pidfile is where we store the daemon's process ID. my $pidfile = "$workdir/daemon.pid"; handle_pidfile( $pidfile ); # Check for leftover queue items. recover_dead_queue(); if ( $hostname =~ /\S/ ) { logline( "daemon started on $hostname; computing $bits_to_compute bits" ); } else { logline( "daemon started; computing $bits_to_compute bits" ); } logline( $RCS ); # # This is our stack of tasks. Whatever we're working on right now is always # at the end of this list. When we see something more important, we SIGSTOP # the process and start a new one, adding the new task to the stack. # Each element of the stack is a reference to a hash of stuff we need to # retain. Its elements are: # # 'msg' -- the name of the file that has the message # 'dat' -- the name of the job's .dat file # 'sendmailfh' -- where I write the message # 'sendmailpid' -- the sendmail PID # 'msgfh' -- whence I read the message # 'hashcashfh' -- whence I read a token # 'hashcashpid' -- what to signal with STOP and CONT # 'blankline' -- the line between the headers and the body # 'cashinfo' -- a string describing the hashcash we're making # It looks like 12:3456:x@example.com where the fields # are bits:expiry:resource # my @stack = (); # At this point we'll need to do a cleanup if we die. $need_cleanup = 1; # How many seconds to sleep when waiting. my $sleep_time = 60 * 60; # If this is true, we'll just process what's in the queue and then go away. # The daemon won't wait around polling for something else to show up. my $run_queue_and_die = 1; while ( 1 ) { # This looks for messages in the queue that we haven't processed yet. opendir( QUEUE, "$workdir/queue" ) || fatal( "Can't opendir queue: $!\n" ); my @messages = sort prioritize grep( /\.(msg|req)$/, readdir( QUEUE ) ); closedir( QUEUE ); # Nothing to do. Look again in a while, or after an interrupt. if ( ! @messages && ! @stack ) { if ( $run_queue_and_die ) { logline( "queue is empty; exiting." ); exit 0; } $SIG{ USR1 } = sub { logline( "awoke on $_[0]" ); die; }; eval { sleep( $sleep_time ); }; $SIG{ USR1 } = 'IGNORE'; next; } if ( @messages ) { # This is the most important message that's not being worked on. my $msg = $messages[ 0 ]; if ( ! @stack ) { # If there's nothing on the stack, we don't have to worry about # priorities or stopping any current process. Just go to work. begin_delivery( $msg, \@stack ); } else { my $topstate = $stack[ -1 ]; # First we check if the new message we found is more important # than what we're working on right now. my @check = ( $msg, $$topstate{ 'msg' } ); @check = sort prioritize @check; if ( $check[ 0 ] eq $msg ) { # The new best message is better than the previous. # Stop the current job and push a new one on the stack. interrupt_with( $msg, \@stack ); } } } # The only way the stack is empty at this point is if we had nothing # to work on and found a message with no recipients. In that case, # it's already been delivered. if ( @stack ) { my $topstate = $stack[ -1 ]; # I'm doing this here because there's probably time. We're waiting # on some token to mint. I'd do this when I'm about to exit, but I # don't want to fool some baby daemon into thinking that I'm on the # job when I'm not. expire_premade(); # We're going to wait for a short time for the hascash process # to be ready to read (i.e., it gave us our token). Once it # does, we continue delivery. If it turns out delivery is # finished, we pop the task off the stack and try to continue # anything that's left. my $rin = fhbits( $$topstate{ 'hashcashfh' } ); my $finished_token = 0; # If this stays zero, no token was finished. $SIG{ USR1 } = sub { logline( "awoke on $_[0]" ); die; }; eval { $finished_token = select( $rin, undef, undef, $sleep_time ); }; $SIG{ USR1 } = 'IGNORE'; if ( $finished_token && ! continue_delivery( $topstate ) ) { # Delivery finished. Throw away the state pop( @stack ); while ( @stack ) { $topstate = $stack[ -1 ]; my $restart = $$topstate{ 'hashcashpid' }; logline( "continuing $$topstate{ 'msg' }" ); last if kill( 'CONT', $restart ); logline( "failed to restart $$topstate{ 'msg' } (pid $restart)" ); pop( @stack ); } } } } #### #### SUBROUTINES START HERE #### # # This converts a resource name to something that's appropriate to a filename. # It's basically URL encoding with '-' instead of '%'. You can decode what # the original resource was, but we never actually need to do that. This is # just so I can find files that contain tokens for the resource I want. # sub res2file { my ( $resource ) = @_; my $filename = $resource; $filename =~ s/(\W)/uc sprintf( "-%02x", ord( $1 ) )/eg; return $filename; } # # We know the addresses for the envelope (@env_recip), # and the addresses listed in the headers (@to). # For which do we compute hashcash? (@hash_to) # # This figures out what messages to queue and does the queueing. Though # only one message was given, we may queue several, depending on Bccs. # sub queue_messages { my ( $args, $env_recip, $to, $pre_args ) = @_; my @hash_to = (); if ( scalar @$env_recip == 1 ) { # There's only one envelope recipient. That's who gets hashcash! @hash_to = @$env_recip; } else { # This looks for the intersection of envelope recipients and header # recipients. An envelope recipient who's not listed in headers is # a Bcc, and we don't want to leak that otherwise private info. # A recipient in the headers who's not on the envelope isn't really # getting the message, so there's no point wasting our time. my %envelope_map = (); foreach my $arg ( @$env_recip ) { $arg =~ tr/A-Z/a-z/; $envelope_map{ $arg }++; } foreach my $addr ( @$to ) { my $flat = $addr; $flat =~ tr/A-Z/a-z/; if ( $envelope_map{ $flat } ) { delete( $envelope_map{ $flat } ); push( @hash_to, $addr ); } } # Anything left here is a Bcc. # We need to strip them out of the arguments list that we're about to # write, and we need to queue separate messages for each of them. # This is the only case where we change the argument list from what we # got to something else. if ( %envelope_map ) { foreach my $bcc ( keys %envelope_map ) { my $shortarg = [ @$pre_args, $bcc ]; queue( $filebase, $shortarg, [ msgrcpt( [ $bcc ] ) ], \@bitconf ); } @$args = ( @$pre_args, @hash_to ); } } # This queues the "main" message. # It's possible to send a message of all Bcc and no header recipients, # in which case, everything's been queued already. if ( @hash_to ) { queue( $filebase, $args, [ msgrcpt( \@hash_to ) ], \@bitconf ); } } # Try to figure out my time zone. sub time_zone { my @lt = localtime(); my @gt = gmtime(); my $tz = $lt[ 2 ] - $gt[ 2 ]; if ( $gt[ 7 ] > $lt[ 7 ] || $gt[ 5 ] > $lt[ 5 ] ) { $tz -= 24; } if ( $gt[ 7 ] < $lt[ 7 ] || $gt[ 5 ] < $lt[ 5 ] ) { $tz += 24; } $tz .= "00"; $tz =~ s/^(-)?(\d)00$/${1}0${2}00/; $tz =~ s/^(\d)/+$1/; return $tz; } # RFC2821, section 4.4 sub add_trace_line { my ( $msgfh ) = @_; my $progname = $0; $progname =~ s:^.*/([^/]+)$:$1:; print $msgfh "Received: "; print $msgfh "by $hostname " if ( $hostname =~ /\S/ ); print $msgfh "($progname, from uid $>);\n"; # Format this produces: # Wed, 25 Feb 2004 16:37:30 -0600 print $msgfh "\t"; my @lt = localtime; my @days = qw( Sun Mon Tue Wed Thu Fri Sat ); print $msgfh $days[ $lt[ 6 ] ] . ", "; print $msgfh $lt[ 3 ]; print $msgfh " "; my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); print $msgfh $months[ $lt[ 4 ] ]; print $msgfh " "; print $msgfh $lt[ 5 ] + 1900; print $msgfh " "; foreach my $n ( @lt[ 0, 1, 2 ] ) { $n = "0$n" while ( length( $n ) < 2 ); } print $msgfh join( ':', @lt[ 2, 1, 0 ] ); print $msgfh " "; print $msgfh time_zone(); print $msgfh "\n"; } # # This copies the incoming message to the filehandle given and in # the process picks out what addresses are referenced as recipients in # the headers of the message. # sub process_msg { my ( $msgfh, $toref ) = @_; add_trace_line( $msgfh ); my $in_headers = 1; # Whether we're still in the headers of the message. my $in_to = 0; # Whether we're in a To: or Cc: line while ( ) { print $msgfh $_; $in_to = 0 if ( $in_to && ! /^\s/ ); $in_headers = 0 if ( $in_headers && !/\S/ ); my $line = $_; # This is a challenge sent to an unknown party. I make it extra # expensive so it's "last in line" for processing. if ( $in_headers && ( $line =~ /^Reply-To: kyle-cnf-/ ) ) { $extra_expense = 1; } # This is a message on its way to me locally. It needs no cash. if ( $in_headers && ( $line =~ /^X-TMDA-Confirm-Done: / ) ) { $confirmed = 1; } # Pull addresses off Cc: and To: lines. This is used to tell # which envelope recipients are blind carbon copies. if ( $in_headers && ( $in_to || $line =~ s/^(Cc|To): // ) ) { # Get rid of all whitespace $line =~ s/\s+//g; # Get rid of any quoted text $line =~ s/\"[^\"]*\"//g; foreach my $addr ( split( ',', $line ) ) { next if ( $addr =~ m/undisclosed-recipients/i ); next if ( $addr =~ m/recipientlistsuppressed/i ); # Does it contain a route-addr? if ($addr =~ m/<(.*)>/) { # Use the route-addr $addr = $1; } # Does it have single quotes around it? # Microsoft Exchange sometimes does this $addr =~ s/^\'//; $addr =~ s/\'$//; # Get rid of comments in ()'s $addr =~ s/\(.*\)//g; if ( defined( $addr ) && $addr =~ m/^[-_.+\w]+\@[-_.+\w]+$/ ) { push( @$toref, $addr ); } } $in_to = 1; } } } # # This looks at the arguments that were passed in (meant for sendmail) # and tries to pick out the envelope recipients and the "pre args" that # I should maintain if I end up meddling with the evenlope recipients. # sub process_args { my ( $args, $pre_args, $env_recip ) = @_; if ( @$env_recip = grep( /^--$/ .. undef, @$args ) ) { shift( @$env_recip ); my @tmp = @$args; while ( my $t = shift( @tmp ) ) { push( @$pre_args, $t ); last if ( $t eq '--' ); } } else { # There's no '--' argument. my @a = @$args; while ( my $arg = shift( @a ) ) { if ( $arg eq '-f' ) { push( @$pre_args, $arg ); push( @$pre_args, shift( @a ) ); next; } if ( $arg =~ /^-/ ) { push( @$pre_args, $arg ); } else { push( @$env_recip, $arg ); } } } } # # Interrupt the currently running hashcash to deliver a more important message. # If the interruption is successful, this calls begin_delivery() for the # specified message and puts it on the stack. # sub interrupt_with { my ( $msg, $stackr ) = @_; # This is what's running right now. my $topstate = $$stackr[ -1 ]; my $stoppid = $$topstate{ 'hashcashpid' }; if ( kill( 'STOP', $stoppid ) ) { logline( "interrupted $$topstate{ 'msg' } for $msg" ); if ( ! begin_delivery( $msg, $stackr ) ) { # In this case, the message was delivered without any work done. # We need to restart the message we just interrupted. logline( "continuing $$topstate{ 'msg' }" ); if ( ! kill( 'CONT', $stoppid ) ) { fatal( "failed to restart $$topstate{ 'msg' } (pid $stoppid)" ); } } } } # # This is used to sort the messages. We expect two filenames of the form: # 1234-1234567890.1234.msg # The first number is the expense, the second is when it entered the queue, # and the third is just the PID of the queue writer. # Above all, a .msg comes before a .req. # Then we compare expenses and dates, and lower number wins for each. # If they're equal on all counts, we just string compare the names, which # probably means we're deciding based on a PID. # sub prioritize { my ( $ap, $ad, $bp, $bd ) = ( undef, undef, undef, undef ); my ( $ae, $be ) = ( undef, undef ); if ( $a =~ /\.(\w{3})$/ ) { $ae = $1; } if ( $b =~ /\.(\w{3})$/ ) { $be = $1; } if ( defined( $ae ) && defined( $be ) && $ae ne $be ) { # It just so happens that ( 'msg' cmp 'req' ) comes out "right". # If not, I'd have to be explicit about which I wanted first. return $ae cmp $be; } if ( $a =~ /^(\d+)-(\d+)\./ ) { $ap = $1; #priority (expense) $ad = $2; #date } if ( $b =~ /^(\d+)-(\d+)\./ ) { $bp = $1; $bd = $2; } if ( defined( $bp ) && defined( $ap ) && $bp != $ap ) { return ( $ap <=> $bp ); } if ( defined( $bd ) && defined( $ad ) && $bd != $ad ) { return ( $ad <=> $bd ); } return ( $a cmp $b ); } # # Call begin_delivery with a message to deliver. # From there, everything spawns and runs. # When hashcashfh is ready, call continue_delivery( $state ) # Delivery is finished when *_delivery() returns zero. # # # begin_delivery takes a filename (of a message to work on) and returns # a $state hash reference. It returns zero if the message is already # delivered (i.e., when it had no hashcash to compute). # # Its job is basically to set up the process. It does everything up to # where we need to invoke the hashcash executable, and then it calls # continue_delivery() which does that invocation. # # This pushes a state onto the stack and it expects it to stay on top. # If it calls continue_delivery() and finds that the message is all done, # it pops the top item off the stack. # XXX Perhaps I should verify it's the same state I pushed. # sub begin_delivery { my ( $msg, $stackr ) = @_; # The state we'll eventually return. my $state = { 'msg' => $msg }; logline( "found $msg\n" ); # Move the message out of the queue and into the tmp directory. # If this fails, we skip it. Maybe some other daemon got it first? if ( rename( "$workdir/queue/$msg", "$workdir/tmp/$msg" ) ) { push( @$stackr, $state ); # Get the name of the corresponding .dat file and import its data. my $dat = "$workdir/tmp/$msg"; # If $msg is a .req, $dat isn't changed. $dat =~ s:/tmp/(.*)\.msg$:/queue/$1.dat:; # XXX It might be nice to handle errors in the dat file. require "$dat"; $$state{ 'dat' } = $dat; $$state{ 'recip' } = [ @rcpt ]; # This was read from .dat # A .req doesn't need all this sendmail and stuff. if ( $msg =~ /\.msg$/ ) { # Open the message for reading. my $msgfh = new IO::File; open( $msgfh, "$workdir/tmp/$msg" ) || fatal( "Can't read $workdir/tmp/$msg: $!\n" ); $$state{ 'msgfh' } = $msgfh; # Build the sendmail command line. my $com = "|/usr/sbin/sendmail"; foreach my $arg ( @args ) { $com .= " \"" . shell_escape( $arg ) . "\""; } # Open the pipe to sendmail. spawn_sendmail( $state, $com ); my $sendmailfh = $$state{ 'sendmailfh' }; # Read the header of the message and send it to sendmail. my $line = ''; while ( $line = $msgfh->getline ) { last if ( $line !~ /\S/ ); print $sendmailfh $line; } # Remember exactly what the blank line was. $$state{ 'blankline' } = $line; } # go get some hashcash my $out = continue_delivery( $state ); if ( ! $out ) { pop( @$stackr ); } return $out; } return 0; } # # This starts up the sendmail process and puts the writable filehandle in # the given $state for later. We also open sendmail's stdout/stderr for # reading, and this function forks to read what it has to say without # blocking. Whatever sendmail outputs is logged. # sub spawn_sendmail { my ( $state, $com ) = @_; # Open3 knows this is a pipe; it's an error to have it there. $com =~ s/^\|//; my $smin = new IO::File; my $smout = new IO::File; # XXX Doc says failures don't return; they raise an exception. What? my $smpid = open3( $smin, $smout, $smout, $com ); if ( ! $smpid ) { fatal( "Can't open sendmail: $!\n" ); } $$state{ 'sendmailfh' } = $smin; $$state{ 'sendmailpid' } = $smpid; # Now, fork a reader just to get and log sendmail's error messages. my $pid; if ( $pid = fork() ) { # parent close( $smout ); # parent does not read $$state{ 'loggerpid' } = $pid; return; } elsif ( ! defined( $pid ) ) { # child $need_cleanup = 0; fatal( "Can't fork: $!\n" ); } # This should NOT do cleanup. It's not THE daemon, just a little logger. $need_cleanup = 0; close( $smin ); # child does not write while ( my $line = $smout->getline ) { logline( "sendmail[$smpid] said: $line" ); } close( $smout ); exit; } # # There's a hashcash subprocess with a token ready. Read it, and do the # right thing with it. We'll either feed it to waiting sendmail or put # it in a premade token file for later. If it does not go to sendmail, # we return 0 so the caller knows to call finish_delivery() and clean up. # sub take_new_token { my ( $state ) = @_; my $hc = $$state{ 'hashcashfh' }; my $tok = join( '', $hc->getlines ); close( $hc ); waitpid( $$state{ 'hashcashpid' }, 0 ); logline( "made token $tok" ); if ( exists( $$state{ 'sendmailfh' } ) ) { my $sm = $$state{ 'sendmailfh' }; print $sm $tok if ( $tok ); } else { # If there's no sendmailfh, this is a request, not a message. my ( $bits, $expiry, $r ) = ( $$state{ 'cashinfo' } =~ /^(\d+):(\d*):(.*)/ ); # Take the expiry on the token regardless of what we thought # it would be. # v0 = 0:date # v1 = 1:bits:date if ( $tok =~ /X-Hashcash: (?:0|1:\d+):(\d+):/ ) { $expiry = $1; } # Make sure the premade directory exists if ( ! -d "$workdir/premade" && ! mkdir( "$workdir/premade" ) ) { fatal( "Can't create $workdir/premade: $!\n" ); } # Initial filename. That '0' on the end gets incremented until # we find one that's not taken. my $prefile = "$workdir/premade/" . res2file( $r ) . ".$expiry.$bits.0"; my $fh = new IO::File; while ( -e $prefile || ! sysopen( $fh, $prefile, O_RDWR|O_CREAT ) || ! flock( $fh, LOCK_EX|LOCK_NB ) ) { $prefile =~ s/\.(\d+)$/"." . ($1 + 1)/e; } logline( "saving to $prefile" ); if ( ! truncate( $fh, 0 ) ) { fatal( "Can't truncate $prefile: $!\n" ); } print $fh $tok if ( $tok ); close( $fh ); # Multiple recipients in one .req not supported. return 0; } } # # continue_delivery takes a state as an argument and returns a state. It # returns zero if the delivery is finished. # # What this does is invoke hashcash and then return the state information # used to keep track of the job. If there's a job already in progress, it # pulls in the token and adds it to the outgoing message. The caller is # responsible for making sure the job is ready to read. # sub continue_delivery { my ( $state ) = @_; # If there's hashcash to read, read it and add it to the outgoing # message. This won't happen the first time continue_delivery is # called for a message. if ( exists( $$state{ 'hashcashfh' } ) && ! take_new_token( $state ) ) { return finish_delivery( $state ); } # Look through the recip list to find a recipient. my $recipient = undef; my $expiry = undef; my $bits = $bits_to_compute; my $r = undef; while ( @{ $$state{ 'recip' } } && ! defined( $recipient ) ) { $recipient = shift( @{ $$state{ 'recip' } } ); if ( defined( $recipient ) ) { if ( $$recipient{ 'bits' } ) { $bits = $$recipient{ 'bits' }; } if ( exists( $$recipient{ 'expiry' } ) ) { $expiry = $$recipient{ 'expiry' }; } # The guess is used in places where I always want to have # something. The $expiry is used for the command line. my $expguess = $expiry ? $expiry : expire_today(); $$state{ 'cashinfo' } = "$bits:$expguess:$$recipient{ 'addr' }"; if ( exists( $$state{ 'sendmailfh' } ) ) { my $tok = get_premade( $bits, $expguess, $$recipient{ 'addr' }, $$state{ 'sendmailfh' } ); if ( $tok ) { # Using this means we don't need to process this recipient # any further, so pretend we didn't find it. $recipient = undef; } } } } # If there are no more recipients, call finish_delivery(), # which always returns zero. if ( ! defined( $recipient ) ) { return finish_delivery( $state ); } # Figure out the command line for hashcash. if ( defined( $$recipient{ 'reso' } ) ) { $r = shell_escape( $$recipient{ 'reso' } ); } else { $r = shell_escape( $$recipient{ 'addr' } ); } my $nice = ( $$recipient{ 'nice' } > 0 ) ? "nice -$$recipient{ 'nice' }" : ''; my $ex = defined( $expiry ) ? "-t $expiry" : ''; logline( "making token for $r ($bits bits)" ); # Spawn the hashcash executable to do the grunt work. # We retain the PID and filehandle for later communication. my $rdfh = new IO::File; my $pid = open( $rdfh, "$nice hashcash -qm $ex -b $bits -r \"$r\" -X|" ); if ( ! $pid ) { complain( "failed to execute hashcash: $!\n" ); # Go on to next recipient. return continue_delivery( $state ); } # Search the stack for another message that's making the same stamp my $steal = undef; foreach my $s ( @stack ) { next if ( $s == $state ); if ( $$state{ 'cashinfo' } eq $$s{ 'cashinfo' } ) { $steal = $s; last; } } if ( ! $steal ) { # We're not stealing an existing process. Just store the # PID and filehandle and go. $$state{ 'hashcashpid' } = $pid; $$state{ 'hashcashfh' } = $rdfh; } else { # This can most easily happen if the daemon is processing a .req # but hasn't finished. We'll steal the request's work-so-far # and have it start over. # This can also happen if there's one message with many recipients, # and the recipient being computed right now gets another message # addressed just to that one recipient. The new message will be # lower cost than the old, and it'll take its work. # Otherwise this only happens if you edit the bitconf between # messages, which is how I tested (meddle with the nice value). # NOTE that in the first and third case, the 'nice' level of the # process we steal is not necessarily what we want. logline( "using PID $$steal{ 'hashcashpid' } in progress for $r" ); # Stop the new one. # logline( "stopping $pid" ); if ( ! kill( 'STOP', $pid ) ) { fatal( "Can't stop hashcash (pid $pid): $!\n" ); } # Take the PID and filehandle of the (stopped) one already in progress. $$state{ 'hashcashpid' } = $$steal{ 'hashcashpid' }; $$state{ 'hashcashfh' } = $$steal{ 'hashcashfh' }; # The one in progress becomes the one we just started. $$steal{ 'hashcashpid' } = $pid; $$steal{ 'hashcashfh' } = $rdfh; # Restart the old one. # logline( "starting $$state{ 'hashcashpid' }" ); if ( ! kill( 'CONT', $$state{ 'hashcashpid' } ) ) { fatal( "Can't restart hashcash: $!\n" ); } } return $state; } # What the 'expire' part of a token will look like for something made now. sub expire_today { my ( $sec,$min,$hour, $mday,$mon,$year, $wday,$yday, $isdst ) = gmtime; $year =~ s/^\d*(\d\d)$/$1/; $mon++; $year = "0$year" while ( length( $year ) < 2 ); $mon = "0$mon" while ( length( $mon ) < 2 ); $mday = "0$mday" while ( length( $mday ) < 2 ); return "$year$mon$mday"; } sub get_recipient_premade { my ( $recipient, $outfh ) = @_; return unless ( $recipient ); return unless ( $outfh ); my $bits = $bits_to_compute; if ( $$recipient{ 'bits' } ) { $bits = $$recipient{ 'bits' }; } my $expiry = undef; if ( exists( $$recipient{ 'expiry' } ) ) { $expiry = $$recipient{ 'expiry' }; } # The guess is used in places where I always want to have # something. The $expiry is used for the command line. my $expguess = $expiry ? $expiry : expire_today(); # $$state{ 'cashinfo' } = "$bits:$expguess:$$recipient{ 'addr' }"; return get_premade( $bits, $expguess, $$recipient{ 'addr' }, $outfh ); } # # Look through the premade tokens to find one that satisfies the given # requirements. The expiry and resource must match, but we'll take any # token with the given bits or more. # sub get_premade { my ( $bits, $expiry, $r, $outfh ) = @_; my $out = undef; if ( $outfh && opendir( PREMADE, "$workdir/premade" ) ) { # This sorts the premade files by their cost in bits. # This way I can take the cheapest token that matches. my $bybits = sub { if ( $a =~ /^[\w-]*\.\d*\.(\d+)\./ ) { my $abits = $1; if ( $b =~ /^[\w-]*\.\d*\.(\d+)\./ ) { my $bbits = $1; return ( $abits <=> $bbits ); } } return ( $a cmp $b ); }; my $sha = res2file( $r ); # All premade tokens for this recipient, sorted by bits. my @premade_files = sort $bybits readdir( PREMADE ); closedir( PREMADE ); foreach my $premade ( @premade_files ) { my ( $fsha, $fexpiry, $fbits ) = ( $premade =~ /^([\w-]*)\.(\d*)\.(\d+)\./ ); next if ( $fsha ne $sha ); next if ( $fexpiry ne $expiry ); next if ( $fbits < $bits ); my $pfh = undef; # If I'm able to rename the file, that ensures no other daemon # got a hold of it first. if ( rename( "$workdir/premade/$premade", "$workdir/tmp/$premade" ) ) { if ( ! open( $pfh, "$workdir/tmp/$premade" ) ) { complain( "Can't open $workdir/tmp/$premade: $!\n" ); next; } $out = join( '', $pfh->getlines ); close( $pfh ); if ( ! unlink( "$workdir/tmp/$premade" ) ) { complain( "Can't unlink $workdir/tmp/$premade: $!\n" ); } # Look at no more files. last; } } } if ( $out ) { # If we have a premade token, use it. logline( "using premade token $out" ); print $outfh $out; } return $out; } # # finish_delivery() takes a state as an argument and returns zero. # This function's job is just to push the message body out to sendmail. # sub finish_delivery { my ( $state ) = @_; my $msg = $$state{ 'msg' }; my $sm = $$state{ 'sendmailfh' }; my $msgfh = $$state{ 'msgfh' }; if ( $sm ) { if ( exists( $$state{ 'blankline' } ) ) { print $sm $$state{ 'blankline' }; delete( $$state{ 'blankline' } ); } if ( $msgfh ) { while ( my $line = $msgfh->getline ) { print $sm $line; } } # This checks sendmail's exit status. If something's wrong, we # put the message back in the queue, whine, and abort with the same # exit code that sendmail gave. if ( ! close( $sm ) && ! $! ) { # Propogate the exit code. my $exit_code = $?; logline( "putting $msg back in queue" ); rename( "$workdir/tmp/$msg", "$workdir/queue/$msg" ); complain( "sendmail exited with $exit_code\n" ); exit( $exit_code ); } # Reap my subprocesses properly. if ( $$state{ 'sendmailpid' } ) { waitpid( $$state{ 'sendmailpid' }, 0 ); } if ( $$state{ 'loggerpid' } ) { waitpid( $$state{ 'loggerpid' }, 0 ); } } close( $msgfh ) if ( $msgfh ); # Delete the message and the dat file. if ( ! unlink( "$workdir/tmp/$msg", $$state{ 'dat' } ) ) { fatal( "Can't unlink: $!\n" ); } if ( $msg =~ /^\d+-(\d+)\.\d+\.msg/ ) { my $intime = $1; my $qtime = time() - $intime; logline( "delivered $msg about $qtime seconds since it was queued" ); } else { logline( "delivered $msg" ); } %{ $state } = (); return 0; } # It's a 'warn' that logs as well. sub complain { my ( $line ) = @_; logline( $line ); warn $line; } # It's a 'die' that logs as well. sub fatal { my ( $line ) = @_; logline( "FATAL: $line" ); die $line; } # Writes a line to the log. It does nice stuff like add a datestamp # and the program name and such. sub logline { my ( $line ) = @_; if ( $line =~ /\S/ ) { my $out = scalar localtime(); my $name = $0; $name =~ s:^/.*/([^/]+)$:$1:; $out .= " $name\[$$]: "; $line =~ tr/\r\n/ /; $line =~ s/^\s+//; $line =~ s/\s+$//; $out .= "$line\n"; lograw( $out ); } } # Logs stuff. It does locking on the log file, but it does no formatting. sub lograw { my @logstuff = @_; my $fh = new IO::File; sysopen( $fh, $logfile, O_RDWR|O_CREAT ) || die "Can't write $logfile: $!\n"; flock( $fh, LOCK_EX ) || die "Can't LOCK_EX $logfile: $!\n"; seek( $fh, 0, SEEK_END ) || die "Can't seek on $logfile: $!\n"; print $fh @logstuff; close( $fh ); } # Camel book, 3rd ed, page 782 sub fhbits { my @fhlist = @_; my $bits; for ( @fhlist ) { vec( $bits, fileno( $_ ), 1 ) = 1; } return $bits; } # This is called any time we're about to die. Its job is to put .msg # files back in the queue and kill lingering hashcash jobs. sub cleanup { my ( $sig ) = @_; my $sigmess = ( $sig ) ? " on signal $sig" : ''; if ( ! $need_cleanup ) { logline( "exit$sigmess" ) if ( $sigmess ); } else { $need_cleanup = 0; logline( "cleanup$sigmess" ); foreach my $state ( @stack ) { my $pid = $$state{ 'sendmailpid' }; if ( $pid ) { logline( "TERM sendmail PID $pid" ); kill( 'TERM', $pid ); } # This was 'TERM', but that didn't get it. $pid = $$state{ 'hashcashpid' }; logline( "KILL hashcash PID $pid" ); kill( 'KILL', $pid ); my $msg = $$state{ 'msg' }; logline( "putting $msg back in queue" ); rename( "$workdir/tmp/$msg", "$workdir/queue/$msg" ); } # It's tempting to unlink $pidfile here, but it's possible # someone has it open for reading. If I unlink it, they'll # write to a file that disappears when they close it. } exit; } # This looks through premade tokens looking for things that are expired. sub expire_premade { if ( opendir( PREMADE, "$workdir/premade" ) ) { my $now = expire_today(); while ( my $premade = readdir( PREMADE ) ) { if ( $premade =~ /^[\w-]*\.(\d+)\./ ) { my $e = $1; if ( $e < $now ) { logline( "discarding expired premade token in $premade" ); if ( ! unlink( "$workdir/premade/$premade" ) ) { complain( "Can't unlink $premade: $!\n" ); } } } } closedir( PREMADE ); } } # # Look for a .msg or .req in tmp/ and return it to the queue/ # This is called after we've determined that this is to be the one and only # daemon but before we start processing messages, so we know tmp/ SHOULD # be empty. Messages might be left in tmp/ if hashcash-sendmail was running # during a power failure, for instance. # sub recover_dead_queue { if ( ! opendir( TMP, "$workdir/tmp" ) ) { complain( "Can't opendir( $workdir/tmp ): $!\n" ); return; } my @replace = grep( !/^\./ && /\.(req|msg)$/, readdir( TMP ) ); closedir( TMP ); if ( @replace ) { complain( "Earlier hashcash daemon must have died; replacing lost queue items." ); foreach my $file ( @replace ) { if ( -f "$workdir/queue/$file" ) { complain( "'$file' exists in both tmp and queue!" ); next; } rename( "$workdir/tmp/$file", "$workdir/queue/$file" ) || complain( "Can't rename tmp/$file to queue: $!\n" ); } } } # # This checks if another daemon is running and exits if there is. # If not, we write our PID to the pidfile. # sub handle_pidfile { my ( $pidfile ) = @_; # Open for read and write, create if not there. my $pidfh = new IO::File; if ( ! sysopen( $pidfh, "$pidfile", O_RDWR|O_CREAT ) ) { fatal( "Can't write $pidfile: $!\n" ); } # Locking avoids race conditions. I can share a lock with someone else # who's also trying to read, but I can't share with a writer. flock( $pidfh, LOCK_SH ) || fatal( "Can't LOCK_SH $pidfile: $!\n" ); my $oldpid = $pidfh->getline; # Check if the old daemon is still alive. if ( $oldpid && kill( 0, $oldpid ) ) { # There's already a daemon running. logline( "waking PID $oldpid" ); kill( 'USR1', $oldpid ); exit 0; } # Truncate the file and write my own PID there. flock( $pidfh, LOCK_EX|LOCK_NB ) || fatal( "Can't lock $pidfile: $!\n" ); seek( $pidfh, 0, SEEK_SET ) || fatal( "Can't seek on $pidfile: $!\n" ); truncate( $pidfh, 0 ) || fatal( "Can't truncate $pidfile: $!\n" ); print $pidfh "$$\n"; close( $pidfh ); } # This is to disconnect from the shell that ran me. sub daemonize { my $pid; # Doing this instead of just closing them makes some oddball case work. # I wish I knew why. open( STDIN, "/dev/null" ) || fatal( "Can't reopen STDIN: $!\n" ); open( STDOUT, ">/dev/null" ) || fatal( "Can't reopen STDOUT: $!\n" ); open( STDERR, ">/dev/null" ) || fatal( "Can't reopen STDERR: $!\n" ); # Ignore the fact that the parent dies. $SIG{ HUP } = sub { logline( "ignored $_[0]" ); }; if ( $pid = fork() ) { # Parent exits immediately. exit 0; } elsif ( ! defined( $pid ) ) { complain( "Can't fork: $!\n" ); } setpgrp( 0, $$ ); setsid(); } # Escapes shell meta characters, so I can safely use it on the command line. # This does NOT escape spaces. What you get from this is meant to be put in # double quotes on the command line. sub shell_escape { my ( $string ) = @_; $string =~ s/\\/\\\\/g; $string =~ s/\"/\\\"/g; $string =~ s/\$/\\\$/g; $string =~ s/\`/\\\`/g; return $string; } # # Given a reference to a list of hashcash recipients, this will construct a # simple @rcpt list, which is just a bunch of references to hashes. Each # hash this produces will have JUST the recipient set, but other things will # be added to those hashes later. # sub msgrcpt { my ( $hashref ) = @_; my @rcpt = (); if ( ! $confirmed ) { foreach my $addr ( @$hashref ) { my $add = { 'addr' => $addr }; push( @rcpt, $add ); } } return @rcpt; } # # Given a reference to a @rcpt list, and a reference to a @bitconf, this # will add missing information to the @rcpt list. For each thing in the # @rcpt list, we add 'bits' and 'nice' if they're not there. # sub heed_bitconf { my ( $rcptref, $confref ) = @_; foreach my $add ( @$rcptref ) { next unless ( defined( $$add{ 'addr' } ) ); my $addr = $$add{ 'addr' }; my $need_bitconf = 0; $need_bitconf = 1 if ( ! defined( $$add{ 'bits' } ) ); $need_bitconf = 1 if ( ! defined( $$add{ 'nice' } ) ); $need_bitconf = 1 if ( ! defined( $$add{ 'reso' } ) ); next unless ( $need_bitconf ); foreach my $pair ( @$confref ) { # These are the fields in the bitconf, separated by colons. # $pat -- a regular expression # $bits -- how many bits to compute # $nice -- what nice value to run at. # $reso -- non-standard resource (optional) my ( $pat, $bits, $nice, $reso ) = split( /:/, $pair ); # If this address matches the pattern, we treat it as the # config says. if ( $addr =~ /$pat/i ) { if ( ! defined( $$add{ 'bits' } ) ) { $$add{ 'bits' } = $bits; } if ( ! defined( $$add{ 'nice' } ) ) { $$add{ 'nice' } = $nice; } if ( ! defined( $$add{ 'reso' } ) && defined( $reso ) ) { $$add{ 'reso' } = $reso; } last; } } # It's possible that $addr does not match any pattern. # I'd still like to have some defaults in that case. $$add{ 'bits' } = $bits_to_compute if ( ! defined( $$add{ 'bits' } ) ); # $$add{ 'nice' } = 19 if ( ! defined( $$add{ 'nice' } ) ); } } # # Given a reference to a @rcpt list, this computes the expense of the message. # Assumes that heed_bitconf was called, so there's 'nice' and 'bits' for all. # sub message_expense { my ( $rcptref ) = @_; # For each recipient, we take log-base-2 of the number of bits we need # and multiply by 100. Without the multiplier, lots of values for # $bits would be indistinguishable. Then we multiply by the nice value # assigned because I figure the less we want to hog the CPU, the less # we care about this recipient, and the more "expensive" we should # consider it. The cost of all the recipients are summed. my $expense = 0; foreach my $add ( @$rcptref ) { next if ( ! defined( $add ) ); my $bits = $$add{ 'bits' } || $bits_to_compute; my $exp = int( 100 * log( $bits ) / log( 2 ) ); $exp *= $$add{ 'nice' } if ( $$add{ 'nice' } ); $expense += $exp; } $expense *= 10 if ( $extra_expense ); return $expense; } # # This queues one message. # $filebase -- the name of the temp file that has the message text. # $argsref -- (ref to) the arguments that'll be passed to sendmail. # $rcptref -- list of refs to hashes with recipient info. # $confref -- the config file we read in # # $filebase will be COPIED, so the caller is responsible for unlinking it. # This function computes the expense of the message and writes out # everything that will go to the queue. # # If $argsref is false or points to an empty list, queue() thinks this is # a request for a premade token. In that case, $filebase doesn't exist, # and we do things a little differently to queue the .req # sub queue { my ( $filebase, $argsref, $rcptref, $confref ) = @_; # Add bitconf info to the recipients. heed_bitconf( $rcptref, $confref ); my $is_req = ( $argsref && @$argsref ) ? 0 : 1; my $ext = $is_req ? 'req' : 'msg'; my $tmpfile = "$workdir/tmp/msgcopy$$"; if ( ! $is_req ) { if ( ! copy_with_premade( "$workdir/tmp/$filebase.msg", "$tmpfile", $rcptref ) ) { unlink( "$workdir/tmp/$filebase.msg" ); die "Can't copy $filebase.msg: $!\n"; } } # Compute expense based on the recipients who need hashcash. my $expense = message_expense( $rcptref ); # Make sure we're not about to overwrite something. If so, we # increment $expense until we find a place to write. my $outbase = "$workdir/queue/$expense-$filebase"; while ( -e "$outbase.$ext" ) { $expense++; $outbase = "$workdir/queue/$expense-$filebase"; } # This is where we'll write the metadata for the message. # For requests, this is the only thing we'll write. my $dat = ( $is_req ) ? "$workdir/tmp/$expense-$filebase.req" : "$outbase.dat"; # Now we write our .dat file with the stuff the daemon will need. # If this is a request, we're writing the final .req file that will go in the # queue. We write it to tmp so we can do a nice atomic rename() later. if ( ! open( META, ">$dat" ) ) { die "Can't write $dat: $!\n"; } $Data::Dumper::Useqq = 1; my $dump; if ( $argsref && @$argsref ) { $dump = Data::Dumper->Dump( [ $argsref, $rcptref ], [ qw( *args *rcpt ) ] ); } else { $dump = Data::Dumper->Dump( [ $rcptref ], [ qw( *rcpt ) ] ); } $dump .= "\n1;\n"; print META $dump; close( META ); # Put the message in the queue. # This is the point at which it's "live" and ready for processing. # Make no more modifications to the message after this! The daemon may # already be working on it. if ( $is_req ) { $tmpfile = $dat; } # I use a temp file to avoid a race condition where the daemon starts # reading the message before copy() is done writing it. if ( rename( $tmpfile, "$outbase.$ext" ) ) { my $rpt = "$outbase.$ext"; $rpt =~ s:^.*/([^/]+)$:$1:; logline( "queued $rpt" ); } else { unlink( $tmpfile ); if ( ! $is_req ) { unlink( $dat, "$workdir/tmp/$filebase.msg" ); } die "Can't rename $tmpfile: $!\n"; } return; } # # This makes a copy of a message (for queueing) and also adds any premade # hashcash that is available. Pass in a recipient list reference so it # knows what recipients need hashcash. It will remove from that list # any recipients it was able to find premade tokens for. Returns 1 for # success and undef for failure. # sub copy_with_premade { my ( $msgfile, $outfile, $rcptref ) = @_; # Open the input message for reading. my $msgfh = new IO::File; if ( ! open( $msgfh, $msgfile ) ) { complain( "Can't read $msgfile: $!\n" ); return undef; } # Open the output file for writing. my $outfh = new IO::File; if ( ! open( $outfh, ">$outfile" ) ) { complain( "Can't write $outfile: $!\n" ); close( $msgfh ); return undef; } # Copy the headers (everything up to a line that does not contain # any non-whitespace characters). my $line; while ( $line = $msgfh->getline ) { last if ( $line !~ /\S/ ); print $outfh $line; } # Finished copying headers. Now loop through recipients and add what # hashcash we can. my @leftover = (); foreach my $r ( @$rcptref ) { if ( ! $r || ! get_recipient_premade( $r, $outfh ) ) { push( @leftover, $r ); } } # @leftover is just the ones for which we didn't have a premade token. # So @$rcptref is now smaller if we found premade tokens for things in it. @$rcptref = @leftover; print $outfh $line; # The blank line we read at the end of headers. # Copy the rest of the message. while ( $line = $msgfh->getline ) { print $outfh $line; } close( $outfh ); close( $msgfh ); return 1; } # # Read in the config file. # # Example line: # kyle\@(.+\.)*toehold\.com:20:0:kyleha # # It's three or four fields separated by colons. # # The first field is a Perl regular expression to match. # The second is how many bits to compute for that recipient. # The third is the nice level to run at for that recipient. # The fourth is a resource value to use instead of the email address. # # The fourth field is optional. Such an entry looks like this: # # kyle\@(.+\.)*toehold\.com:20:0 # # Comments start with a "#". # Any line that doesn't match the format is ignored. # # First match wins. It's probably good to have a "default" at the end of the # file like this: # # ^:26:19 # # ...which means anyone not listed gets 26 bits running at nice -19 (lowest # priority). # sub read_bitconf { my @bitconf = (); if ( open( BITCONF, $conffile ) ) { while ( ) { next if /^#/; next unless /^[^:]+:\d+:\d+(:[^:]*)?$/; chomp; push( @bitconf, $_ ); } close( BITCONF ); } return @bitconf; } # $Log: hashcash-sendmail,v $ # Revision 1.18 2004/08/07 18:36:16 kyle # Check for queue items in tmp in case of crash. # Read multi-line tokens. # # Revision 1.17 2004/06/24 16:13:39 kyle # Check for and complaint about not disconnecting from parent doesn't work # and isn't needed. # # Revision 1.16 2004/06/11 15:27:25 kyle # Daemonize better. # Use SIGUSR1 for IPC instead of SIGHUP. # # Revision 1.15 2004/06/02 17:18:56 kyle # Bitconf can specify an arbitrary resource for any address. # If hashcash gave us a v1 stamp, hashcash-sendmail finds the date correctly. # # Revision 1.14 2004/04/13 18:58:02 kyle # Make sure time zone always has either a + or a - before the digits. # # Revision 1.13 2004/04/12 20:27:19 kyle # Fix a bug related to the new Recieved: headers I'm inserting (thanks to # Jason Mastaler for finding it). # # Revision 1.12 2004/04/06 16:59:15 kyle # Avoid crashing via log( $bits = 0 ). # Create the premade token directory if it doesn't exist. # Use premade tokens at queue time to speed up delivery. # # Revision 1.11 2004/04/02 20:55:45 kyle # Add a Received: line on the way through. # Properly reap a couple of child processes. # Log my hostname and RCS ID when the daemon starts. # # Revision 1.10 2004/04/02 18:57:45 kyle # Fixed a bug where premade tokens would clobber each other. # Better error reporting when sendmail complains. # Fixed a bug where the daemon would hang (I think). # # Revision 1.9 2004/03/31 05:14:59 kyle # hashcash-daemon has been incorporated into hashcash-sendmail now. After # messages are queued, it disconnects from the caller and tries to process # the queue. All the code from hashcash-daemon has been basically pasted in # and adapted slightly. # If hashcash-sendmail is invoked with no arguments, it expects a token # request on standard input. There's been lots of reorganization to support # the new dual purpose. # This also fixes a bug where the daemon wouldn't die properly if it had a # hashcash-in-progress when it received the kill signal. # # Revision 1.8 2004/03/19 20:36:46 kyle # Fix a possible race condition when copying the message to the queue. # Invoke the daemon right before exit. # # Revision 1.7 2004/03/16 01:47:58 kyle # Make sure not to overwrite a message I just queued in the case where I'm # handling a Bcc by making multiple copies. Thanks to Gerhard A. Blab for # pointing this out. # Also fix a bug where a failed queue() call would stop future queue() calls # from working. # # Revision 1.6 2004/03/09 06:01:59 kyle # Correctly handle the case where we have ALL Bcc and NO visible header # recipients. Also did some comment maintenance. # # Revision 1.5 2004/03/08 21:38:49 kyle # Queue separate messages for Bcc recipients so they still get hashcash but # don't get revealed to other recipients. Thanks to Adam Back for the # suggestion. # # Revision 1.4 2004/03/08 17:40:10 kyle # Fix the env_recip grep line (thanks Adam Back for figuring out the problem) # so that it works on other versions of Perl. # Add my email address and URL to the comments. # # Revision 1.3 2004/03/08 02:03:32 kyle # Create the working directories if they don't exist. # # Revision 1.2 2004/03/07 22:20:14 kyle # Make the working directory less hardcoded. # # Revision 1.1 2004/03/07 22:14:24 kyle # Initial revision # hashcash-1.21/contrib/hashcash-request0000664000076400007640000000704610351101612016447 0ustar adamadam#!/usr/bin/perl # $Id: hashcash-request,v 1.2 2004/03/31 05:05:10 kyle Exp $ # # hashcash-request # # This goes with hashcash-daemon. Use this script to make a request for # the daemon to precompute a token for a later message. When the daemon # isn't busy, it will make the token to be used later. # # An up-to-date version is normally here: # http://www.toehold.com/~kyle/hashcash/ # # Consider this ALPHA software. It works for me, but it has not been through # much real testing. # # Copyright (C) 2004 Kyle Hasselbacher # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # use strict; use Data::Dumper; # # I want to support some of the same flags that hashcash does. # # -b bits find or check partial hash collision of length bits # -r resource resource name for minting or checking token # -e period time until token expires # -t time modify current time token created at # -a time modify time by random amount in range given # -u give time in UTC instead of local time # -h print this usage info # -z width width of time field 2,4,6,8,10 or 12 chars (default 6) # -L limit -- make no more than this many tokens for a given resource # This hash will be sent to hashcash-sendmail when it's done. my $add = { 'nice' => 19 }; # Process command line arguments. A better processor would do some # sanity checks on these. while ( defined( my $arg = shift ) ) { if ( $arg eq '-b' ) { $$add{ 'bits' } = shift; } elsif ( $arg eq '-t' ) { $$add{ 'expiry' } = shift; } elsif ( $arg eq '-r' ) { $$add{ 'addr' } = shift; } elsif ( $arg eq '-h' ) { print<<'END_OF_HELP'; -b bits find partial hash collision of length bits -r resource resource name for minting token -t time modify current time token created at -h print this usage info END_OF_HELP exit; } } if ( ! exists( $$add{ 'addr' } ) ) { $$add{ 'addr' } = undef; } my @rcpt = ( $add ); #my $width = 6; #$width = $opt_z if ( defined( $opt_z ) ); #if ( $width != 2 && $width != 4 && $width != 6 && $width != 8 && # $width != 10 && $width != 12 ) { # die "Acceptible widths are 2, 4, 6, 8, 10 or 12.\n"; #} # Open a pipe to hashcash-sendmail. Invoking it this way implies that # a request is on the way in on standard input. if ( ! open( HCSM, "|hashcash-sendmail" ) ) { die "Can't execute hashcash-sendmail: $!\n"; } $Data::Dumper::Useqq = 1; my $dump = Data::Dumper->Dump( [ \@rcpt ], [ qw( *rcpt ) ] ); $dump .= "\n1;\n"; print HCSM $dump; close( HCSM ); exit 0; # $Log: hashcash-request,v $ # Revision 1.2 2004/03/31 05:05:10 kyle # It does the same thing but completely differently to go with the new # hashcash-sendmail which doesn't have a hashcash-daemon # # Revision 1.1 2004/03/24 17:49:48 kyle # Initial revision # hashcash-1.21/contrib/hashcash-sendmail.txt~0000664000076400007640000000047010351101612017561 0ustar adamadamhashcash-sendmail and hashcash-request are written by Kyle Hasselbacher. Both programs are considered alpha-quality by the author. Use at your own risk. hashcash-sendmail is used to add hashcash support for MUAs that do not natively support hashcash. hashcash-request is used to pre-request hashcash tokens. hashcash-1.21/contrib/hashcash-sendmail.txt0000664000076400007640000000057210351101612017366 0ustar adamadamhashcash-sendmail and hashcash-request are written by Kyle Hasselbacher. Both programs are considered alpha-quality by the author. Use at your own risk. hashcash-sendmail is used to add hashcash support for MUAs that do not natively support hashcash. hashcash-request is used to pre-request hashcash tokens. For more information, see http://www.toehold.com/~kyle/hashcash/ hashcash-1.21/contrib/hashfork.c0000600000076400007640000000233710351101612015211 0ustar adamadam/* hashfork.c * * by Hubert Chan * * This file is hereby placed in the public domain. * */ #include #include #include #include #include #include #include const int NUM_PROCS=15; int main (int argc, char *argv[]) { int i; pid_t children[NUM_PROCS]; pid_t pid; int status; for (i = 0; i < NUM_PROCS; i++) { pid = fork (); if (pid) { /* parent */ children[i] = pid; } else { int wait_time = 0; pid = getpid (); printf ("process %d started.\n", pid); /* this should be a function call to calculate the hashcash instead of * an execl */ execl ("/usr/bin/hashcash", "/usr/bin/hashcash", "-mb25", "foo", NULL); exit(0); /* --- ignore this --- */ srand (time (NULL) + pid); wait_time = rand () % 20; printf ("%d: waiting %d seconds.\n", pid, wait_time); sleep (wait_time); printf ("%d: exiting.\n", pid); exit (0); } } pid = wait (&status); printf("process %d exited.\nkilling all children.\n", pid); for (i = 0; i < NUM_PROCS; i++) { printf ("%d\n", children[i]); kill (children[i], SIGKILL); } return 0; } hashcash-1.21/contrib/hashfork.txt0000664000076400007640000000032110351101612015607 0ustar adamadamhashfork.py is contributed code by Hubert Chan and is useful for getting faster minting performance from multi-processor machines. It is a wrapper in Python around the hashcash executable. hashcash-1.21/contrib/hashfork.py0000600000076400007640000000334110351101612015413 0ustar adamadam#!/usr/bin/python # hashfork.py # # by Hubert Chan, March 2005 # # This file is hereby placed in the public domain. import os import signal import sys if len(sys.argv) < 3: print "Run multiple instances of hashcash in parallel" print "usage:" print " ", sys.argv[0], " " print print " is the number of hashcash processes to start" print " is the command line argumens to pass to" print " hashcash. Note that the -m option will be automatically added, and" print " so does not need to be specified." print print "Note: you MUST specify a resource on the command line." sys.exit () NUM_PROCS = int (sys.argv[1]) del (sys.argv[0:2]) sys.argv.insert(0, "hashcash") sys.argv.insert(1, "-m") children = [ ] pipes = [ ] for i in range (NUM_PROCS): pipe = os.pipe () # create a pipe to communicate with the child pid = os.fork () if pid: # parent children.append (pid) os.close (pipe[1]) # close the write end of the pipe pipes.append (os.fdopen (pipe[0])) # print "process", pid, "started" else: # child os.close (pipe[0]) # close the read end of the pipe os.dup2 (pipe[1], 1) # redirect stdout to the pipe os.close (pipe[1]) os.execvp ("hashcash", sys.argv) status = os.wait () #print "process", status[0], "exited.\nkilling all children." for i in range (NUM_PROCS): # print children[i] if children[i] == status[0]: print (pipes[i].readline ()), try: os.kill (children[i], signal.SIGKILL) except OSError: # kill raises an OSError when the process already exited pass hashcash-1.21/hashcash.txt0000644000076400007640000010355710351101612014141 0ustar adamadamNAME hashcash - hashcash anti-spam / denial of service counter-measure tool SYNOPSIS Mint (create) stamps: hashcash *-m* [ [ *options* ] [ *resource* ... ] ... ] Check stamps: hashcash *-c* [ [ *options* ] *-r resource* ... ] [ *stamp* ... ] Speed estimates: hashcash *-s* [ *options* ] [ *-b bits* ] Purge database: hashcash *-p now* [ *-j resource* ] [ *-k* ] [ *-t time* ] [ *-u* ] Count collision bits: hashcash *-w* [ *options* ] [ *stamp* ... ] Get resource name: hashcash *-n* [ *options* ] [ *stamp* ... ] Get time remaining until expiry hashcash *-l* [ *options* ] *-e period* [ *stamp* ... ] DESCRIPTION Hashcash is a denial-of-service counter measure tool. Its main current use is to help hashcash users avoid losing email due to content based and blacklist based anti-spam systems. The hashcash tool allows you to create hashcash stamps to attach to emails you send, and to verify hashcash stamps attached to emails you receive. Email senders attach hashcash stamps with the X-Hashcash: header. Vendors and authors of anti-spam tools are encouraged to exempt mail sent with hashcash from their blacklists and content based filtering rules. A hashcash stamp constitutes a proof-of-work which takes a parameterizable amount of work to compute for the sender. The recipient can verify received stamps efficiently. This package also includes a sha1 implementation which is equivalent in functionality to the sha1sum program available on some systems. The hashcash proof-of-work function is to compute partial hash collisions (the SHA1 hash function is used). By choosing the number of bits of collision, the work required to compute a collision can be made arbitrarily expensive -- from fractions of a second to minutes or hours. The verification is efficient requiring the same small cost whatever the collision size. For more detailed discussion of other applications hashcash has been used for see http://www.hashcash.org/ USAGE NOTES In this man page a resource name is the name of the service or address the stamp is created for. In the case of email, the resource name is the recipient's email address in the form user@domain.com. Minting stamps The *-m* flag must be given to mint a stamp. The resource name (recipient's email address) to mint the stamp against can be passed as an argument, or if omitted is read from stdin. If stdin is a tty the user is prompted, if stdin is a pipe the resource name is just silently read. The desired collision size can be specified with the -b option. If no collision size is specified, the default is 20 bits. See also the *-b default* option. Checking stamps The *-c* flag must be given to check a stamps expiry. The stamp to check can be given as an argument to "hashcash". If no stamp is given the stamp is read from stdin. If stdin is a tty the user will be prompted, if stdin is a pipe the stamp is just silently read. A resource name (the recipient's email address) can be given with the *-r* option. If a resource name is given the resource name is compared to the resource name in the stamp, if they do not match, the stamp is rejected. Note: if no resource name is given the stamp is anyway checked to see if it is otherwise valid, but it could be minted for a different resource, which would allow stamps to be reused across different resources, so hashcash will return unchecked exit code on exit. Stamps are by default considered to be valid for 28 days. The validity period can be changed using the *-e* flag. If the stamp has expired or has a date in the future the stamp is rejected and the program exits immediately. If a required collision size is given with the *-b* flag, the stamps value is computed and compared, if the stamp has insufficent value it is rejected, and the program exits immediately. If the *-b* flag is not given, the stamp is checked to see if it is otherwise valid, but hashcash will return unchecked exit code on exit. If the stamp is double spent the stamp is rejected. Double spending protection is discussed in more detail below in "Double Spending Protection". If double spending protection is not enabled, the stamp could be double spent, so hashcash will return unchecked exit code (exit code 2) on exit. The *-w* flag can be used to request that the number of bits of the collision are counted and displayed. The *-n* flag can be used to request that the resource name in the stamp is parsed out and displayed. The *-l* flag can be used to request the number of seconds until expiry of the stamp is output. The program will only return exit codes valid or invalid if the *-c* flag is used, the *-b* flag is used, *-d*, *-r resource* are used. These are the minimum set of options necessary to fully check the validty of a stamp. If these criteria are not met, the program will return exit code unchecked (exit code 2) on exit. (See also the *-y* flag.) Double Spending Protection If the *-d* flag is used when checking stamps, a database of spent stamps is kept. By default stamps expire after 28 days, without expiry the database would grow indefinately. You can specify an alternate expiry period with the *-e* flag. The recommended (and default) expiry period for email is 28 days. After the expiry period amount of time, the stamp is anyway considered expired and may be purged from the database to save space. (See "Purging Periodically vs on Next Access" for how to purge stamps.) For efficiency reasons a stamp is verified before it is checked in the database; if it is otherwise invalid no database activity will occur. Note: The decision about how long the stamp should be considered valid is up to the verifier. If it is too short it is possible for some applications that the stamp will expire before arriving at the recipient (eg with email.) The suggested value of 28 days should be safe for normal email delivery delays. The choice is a trade-off between database size and risk of expiry prior to arrival, and depends on the application. Note: Different stamps in the same database can have different validity periods, so for example stamps for different resources with different validity periods can be stored in the same database, or the recipient may change the validity period for future stamps without affecting the validity of old stamps. Purging Periodically vs on Next Access To purge old stamps periodically while checking stamps use the *-p period* option to purge no sooner than the given time period since the last purge. Purging can be used with the *-k* option to purge unexpired stamps also, and with the *-j resource* flag to purge only stamps for the given resource. There are circumstances where it may be inconvenient to purge stamps on the next access, for example if there is a large double spend database which takes some time to purge, and the response time of the hashcash checker is important. To avoid this problem, purging can be done separately using just the *-p now* option to request just the purge operation. On unix for example you could call "hashcash -p now" in a cron job once per day, or on demand when disk was running low. Speed Estimates The *-s* flag requests measurement of how many collisions can be tested per second. No stamp is minted, or verified. If the *-b* flag is used with this option, instead an estimate of how many seconds it would take to mint a stamp of the given size in bits is computed. To find out how much time it will take to mint a default sized stamp use *-s -b default*. Notes All informational output is printed on stderr. Minted stamps, and results of stamp verification and timing are printed on stdout. The quiet flag *-q* suppresses all informational output. The *-v* flag requests more informational output. The requested output, which is the only information that is output in quiet mode (when *-q* is specified) is printed on standard output. If stdout is a pipe, or when quiet mode is in effect the output is printed without description (ie just bits, just seconds, just resource). OPTIONS *-c* Check the expiry information of stamps given as an argument or on stdin. (Use with *-b*, *-d* and *-r resource* to fully check stamps). *-m* Mint stamps with the resources given as arguments or on stdin. *-b bits* When minting a stamp, create a collision of at least this many bits. When verifying a stamp require that it have a collision of at minimum this many bits, otherwise reject it. If omitted the default is used. When checking stamps, require that the stamps have this many bits. The default number of bits can be specified with *-b default*. Bits relative to the default can also be specified with *-b +n* for n bits more than the default and *-b -n* for n bits less than the default. *-b default*, *-b +0* and *-b -0* are all equivalent. When doing the speed test *-s*, can to measure speed of default token with *-s -b default*. *-r resource* When minting stamps, the resource name (recipient's email address) to mint the stamp against can be given either with *-r resource* or as an argument to "hashcash". When checking stamps, the resource name (your own email address) is given with the *-r* option. If the resource name is given it is checked against the resource name in the stamp, and if they do not match the stamp is rejected. Note if the resource name is not given, stamps for other resources would be accepted, and therefore hashcash returns exit code unchecked (exit code 2) on exit. *-o* When verifying stamps multiple resources can be given. By default the resources are just checked one by one until a matching valid resource is found. However when you use wildcards or regular expressions (see *-E*), it is useful to be able to specify that one resource overrides another. For example this: *-b15 -r adam@dev.null -o -b10 *@dev.null* states that mail to address *adam@dev.null* requires 15 bits, but mail to **@dev.null* requires only 10 bits. If we omitted the *-o* override relationship between the two resources, a stamp of 10 bits would be accepted for address *adam@dev.null* because while it would be rejected as having insufficient bits under the first rule, it would be accepted under the 2nd rule. The *-o* option allows you avoid this problem. *-e time* Expiry period for spent stamps. While checking stamps (using the *-c* flag), if the stamp was minted more than the specified amount of time ago, it is considered expired. If this option is not used, by default stamps expire after 28 days. The expiry period is given in seconds by default (an argument of 0 means forever). A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). If used with the *-d* option, the spent stamp and its expiry period is recorded in the database. See the *-p* option for description of how to purge stamps from the database. While minting stamps, the *-e* flag can have an effect on the resolution of time created in the stamp. Without the *-e* option, the default resolution is days (time format: YYMMDD). Alternate formats based on range of expiry period are as follows: While minting you can also given an explicit time width with the *-z* option instead. (*-z* overrides *-e* if both are given. If neither are given the default is 6 chars (time format: YYMMDD)). The rules for automatically determining appropriate time width from *-e* if no *-z* option is given are: * period >= 2 years then time format YY is used rounded down to the nearest year start; * 2 years < period <= 2 months then time format YYMM is used rounded down to the nearest month start; * 2 months < period <= 2 days then time format YYMMDD is used rounded down to the begining of the nearest day; * 2 days < period <= 2 minutes then time format YYMMDDhhmm is used rounded down to the begining of the nearest minute; * period < 2 minutes then time format YYMMDDhhmmss is used in seconds. Note the rounding down is based on UTC time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than GMT (UTC = GMT). It may be clearer to understand if you use the *-u* option. *-z width* The *-z* option is for use during minting and allows user choice of width of time width field. See also the *-e* option given in combination with *-m* to specify an implicit time field width under the description of the *-e* flag. Valid widths are 6,10 or 12 chars corresponding respectively to: YYMMDD, YYMMDDhhmm, and YYMMDDhhmmss rounded down to the nearest day, or minute respectively. Note the rounding down is based on UTC time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than GMT (UTC = GMT). It may be clearer to understand if you use the *-u* option. *-g period* The *-g* option is for use when checking hashcash stamps with the *-c* option and specifies a grace period for clock skew, ie if a hashcash stamp arrives with a date in the future or in the past it will not be rejected as having a futuristic date (or as being expired) unless it is more futuristic (or has been expired for longer) than this period. The default is 2 days, which means as long as the sending system's clock is no more than 2 days ahead (or 2 days behind) of the receiving system's clock, the hashcash stamp will still be accepted. The default units for grace period are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). *-d* Store stamps in a double spend database. If stamp has been seen before it will be rejected even if it is otherwise valid. The default database file is database.sdb in the current directory. Only otherwise valid stamps will be stored in the database. Only fully validated stamps will be stored in the database, unless the *-y* option is given. *-f dbname* Use dbname instead of default filename for double spend database. *-p period* Purges the database of expired stamps if the given time period has passed since the last time it was purged. As a convenience *-p now* is equivalent to *-p 0* both of which mean purge now, regardless of when the database was last purged. If used in combination with *-j resource* only the stamps minted for the given resource are purged. If used in combination with *-k* all stamps even un-expired stamps are purged. Can be used in combination with *-t time* to expire as if the current time were the given time. *-k* Use with option *-p* to request all stamps are purged rather than just expired ones. *-j resource* Use with option *-p* to request that just stamps matching the given resource name are to be purged, rather than the default which is to purge all expired stamps. If the resource name is the empty string, all stamps are matched (this is equivalent to omitting the *-j* option). Note the *-E*, *-M* and *-S* type of match flags also apply to resources given with the *-j resource* flag. *-s* Print timing information only, and don't proceed to create a stamp. If combined with *-b bits* flag print estimate of how long the requested collision size would take to compute, if *-s* given by itself, just prints speed of the collision finder. To print an estimate of how long the default number of bits would take use *-b default*. *-h* Print short usage information. *-v* Print more verbose informational output about the stamp minting or verification. (If -v is the only argument, prints the tool version number.) *-V* Prints tool version number. *-q* Batch mode. Prints no information other than output. This option overrides the *-v* option. *-X* When minting, prints the hashcash email X-header 'X-Hashcash: ' before the stamp. Without this option just the bare stamp is printed. When checking, after scanning stamps given as arguments, scans stdin for lines starting with the string 'X-Hashcash:', and uses the rest of the matching line as the stamp. Only the lines up to and ending at the first blank line are scanned (see also *-i* flag which can be used to override this). A blank line is the separator used to separate the headers from the body of a mail message or USENET article. This is meant to make it convenient to pipe a mail message or USENET article to hashcash on stdin. *-x extension* An extension string composed of name value sets. The extension format is described below in the section on the hashcash stamp format. This allows users to define their own stamp extensions which are hashed into the stamp, verified by recipients that support them, and ignored by recipients that don't support them. Note the extension hook mechanism has not yet been implemented. This will come in a subsequent release. *-i* When checking and using the *-X* flag, ignore the blank line boundary between headers and body of the message, and check for collision in the body too if one is not found in the headers. *-t time* Pretend the current time is the time given for purposes of minting stamps, verifying stamps and purging old stamps from the database. Time is given in a format based on UTCTIME format YYMMDD[hhmm[ss]]. Time is expressed in local time by default. Use with *-u* flag to give time in UTC (GMT). You can also give time relative to the current time by prefixing the argument with + or -. The default units for relative time are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). Note: when time is expressed in local time, if there is daylight savings in your timezone, there are one or two ambiguous hours per year at the time of change from daylight savings time to normal time. *-u* Input and output absolute times in UTC (GMT) instead of local time. *-a period* Add (or subtract if number is negative) a random value from the current time before minting the stamp. This hides the time the stamp was created, which may be useful for anonymous users. Note adding (rather than subtracting) a random time may be risky if the stamp takes less than the added time to arrive as the recipient will reject stamps with time stamps in the future. *-n* Print resource name parsed from stamp being verified. Returns exit code unchecked on exit. *-l* Print number of seconds left before stamp expires. Returns exit code unchecked on exit. Note: the calculation includes the grace period, so can be up to 2 times grace period longer than you might otherwise expect (clock fast but system has to presume it could be slow). If you want to exclude the grace period add *-g0* to set grace period to 0 for the calculation. *-w* Print number of bits of collision of stamp. Returns exit code unchecked on exit. *-y* Returns success if the stamp is valid even if it is not fully checked. Use with *-c* where not all of *-d*, *-r* are specified to get success exit code on valid but partially checked stamp. Similarly can use with *-n*, *-l*, *-w* with same effect. *-M* When checking stamps, allow wildcard *** matching in the resource name to make it simpler to specify multiple email addresses and to allow matching catch-all addresses and addresses including subdomains. This is the default. See also *-S*, *-E* and *-C* *-S* When checking stamps use simple text compare to compare resource names to those in stamps. See also *-M*, *-E* and *-C*. *-E* When checking stamps use regular expressions to specify resource names to make it simpler to specify multiple email addresses, catch-all addresses, classes of extension addresses and addresses including subdomains. Note regular expression syntax is POSIX style: special characters do not need to be quoted to have their special meaning; but they do have to be quoted with \ to that character in the searched string. The regular expression automatically has ^ added at the beginning and $ added at the end, if they are not specified. The special characters ^ matches the beginning of the resouce, and $ matches the end of resource. (Note even if compiled with BSD regular expressions, POSIX style syntax is used; also note BSD regular expressions do not support ranges {}.) *-C* By default resources are canonicalized to lower case on minting and on checking. The *-C* flag overrides this so that resources are treated as case sensitive on checking, and not canonizalized on minting. *-P* Print progress info (number of iterations, expected iterations, percentage done, best stamp size found so far). *-O core* Select hashcash core with that number. Currently 0-9 are valid cores. Not all cores work on all architectures. Eg some are x86 specific assembler, others PPC specific assembler. If a core is not valid hashcash returns failure and explains what happened. *-Z n* Compress the stamp. This is a time vs space trade off. Larger stamps are faster, but arguably slightly ugly. For fastest stamps (the default) use -Z 0; for partly compressed stamps use -Z 1; for very compressed, but somewhat slow stamps use -Z 2. (Note: due to a late discovered bug, -Z2 is the same as -Z1 for now until I can fix that.) EXAMPLES Creating stamps "hashcash -s" Print timing information about how many collisions the machine can try per second. "hashcash -sv" More accurate but quite slow benchmarking of different processor specific minting cores. "hashcash -s -b default" Print how long it would take the machine to compute a default sized collision (but don't actually compute a collision). "hashcash -s -b 32" Print how long it would take the machine to compute a 32 bit collision (but don't actually compute a collision). "hashcash -m" Mint a stamp. Will prompt for resource name and mint with default value (number of collision bits). "hashcash -m foo" Compute collision on resource foo. Will mint with default value (number of collision bits). "hashcash -m foo -b 10" Compute 10 bit collision on resource foo. "hashcash -a -3d" Subtract a random time of between 0 days and 3 days from the stamp's creation time. This is the same fuzz factor used by mixmaster to reduce risk of timing-correlations. Examining Stamps "hashcash -w 1:24:040806:foo::511801694b4cd6b0:1e7297a" Report the value of the stamp (how many bits of collision) there are. The example is a 24 bit collision, which takes on average 25 seconds to create on a 3Ghz P4. "hashcash -mq -b 10 foo | hashcash -w" Create a stamp in batch mode, pass to hashcash on stdin to verify, have it print how many bits there were. "hashcash -n 1:24:040806:foo::511801694b4cd6b0:1e7297a" Report the resource name from the stamp. The resource name in the example is foo. "hashcash -l -e 30y 1:24:040806:foo::511801694b4cd6b0:1e7297a" Report how long until the stamp expires if it expires in 30 years from its creation date. (Note dates too far into the future run into the 2038 end of Epoch, which is the unix time analog of the y2k bug). Verifying Stamps "hashcash -c 1:24:040806:foo::511801694b4cd6b0:1e7297a" Check if the stamp is valid. Note as we are not checking the stamp in a double spend database, and did not specify a resource name or required number of bits of collision and hashcash will consider the stamp not fully checked, and it will report it as valid but not fully unchecked, or as invalid if there is any problem with the stamp. "hashcash -c -b24 1:24:040806:foo::511801694b4cd6b0:1e7297a" Check that the value of the stamp is greater or equal to 24 bits. This example has 24 bit value. If you increase the requested number of bits or replace the stamp with one with less than 24 bit collision the stamp will be rejected. "hashcash -c -b24 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" As above check if the stamp has sufficient value, but in addition check that the resource name given matches the resource name in the stamp. Double Spending Prevention The examples given in "Verifying Stamps" can be modified to keep a double spend database so that the same stamp will not be accepted twice. Note a stamp will only be checked in and added to the database if it is otherwise valid and fully checked (a required number of bits of collision has been specified and a resource has been specified). "hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Check the stamp and add to double spent database if it's valid (has correct resource name and sufficient value). "hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Try to double spend the stamp. It will be rejected as double spent. Stamp Expiry To prevent the double spend database growing indefinately, the recipient can request that stamps be no older than a specified period. After expiry old stamps can dropped from the double spend database as they will no longer be needed -- expired stamps can be rejected based purely on their old date, so the space taken by expired stamps in the double spend database can be saved without risk of accepting an expired though otherwise valid stamp. The third field of the stamp is the UTC time since 1st January 1970. The default time format is YYMMDD, time rounded down to the nearest day. The default validity period is 28 days. You can provide an alternative validity period with the *-e* option. "hashcash -cd -b 10 -e 2d -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Try verifying an old stamp, the above stamp was created 11 Aug 2002. We gave option *-e 2d* so the stamps expiry date is 2 days after creation, which is now in the past. Note: if the creation time is expressed in the stamp in days, the precise creation date is the begining of the specified day in UTC time (similarly for alternate units the creation time is rounded down to the begining of the unit it is expressed in). For units in days, for example, this may mean depending on your time zone that the stamp appears to be considered invalid in under the specified expiry period in days relative to your relative view of what day it is, as the calculation is based on current time in UTC, and the creation time of the stamp is expressed in UTC time. "hashcash -cd -b 10 -r foo 1:24:040806:foo::511801694b4cd6b0:1e7297a" Test whether the stamp is otherwise valid, apart from having expired. Omitting the *-e* tells hashcash that the stamp will never expire. An expiry period of forever can also be given explitly like this: *-e 0*, where an expiry period of 0 means forever. Purging old stamps If the *-c*, *-d* options are used together, each time a stamp is checked, if it is valid and all of the mandatory aspects of the stamp are verified (collision bits check, resource name check) then the stamp and its expiry period is written to the database file. The default expiry period if an expiry period is not given explicitly with the *-e* option is 28 days (ie stamps expire after 4 weeks). First mint and then add a stamp: "hashcash -m -b 10 foo -e 1m > stamp" Note: we specified an expiry on minting in this example, to ensure that the stamp creation time is given in high enough resolution in the stamp that the stamp will not be considered expired at time of creation. (Recall the default resolution is in days, a stamp created with a creation time rounded down to the beginging of the day is unlikely to be considered valid 1 minute later unless you mint it at midnight UTC time.) "hashcash -cd -e 1m -b 10 -r foo < stamp" The stamp expires in 1 minute. Wait 1 minute and then explicitly request that expired stamps be purged: "hashcash -p now" Then try resubmitting the same stamp: "hashcash -cd -e 1m -b 10 -r foo < stamp" and the stamp will be rejected anyway as it has expired, illustrating why it was not necessary to keep this stamp in the database. With the default database (the sdb format) the database contents are human readable, so you can view their contents by cating them to the terminal: "cat hashcash.sdb" to see that the stamp really is added and then after puring subsequently purged due to expiry. Purging old stamps on Demand As a convenience you can purge at the same time as checking stamps by using the *-p* option with the *-c* option. "hashcash -m -b 10 foo > stamp" "hashcash -cd -p now -e 1 -b 10 -r foo < stamp" It may be inefficient to purge stamps on every use as the entire database has to be scanned for expired stamps. By giving a time period to the *-p* option, you can tell "hashcash" to purge no more frequently than that time period since the previous purge. For example: "hashcash -cd -p 1d -e 1 -b 10 -r foo < stamp" tells "hashcash" to purge any expired stamps no more than once per day. "hashcash -p 1M -j foo" tells "hashcash" to purge only expired stamps matching resource foo once per month. "hashcash -p now -k" tells "hashcash" to purge all stamps (expired and unexpired) now. stamp format (version 1) The current stamp format is version 1. This tool can verify hashcash version 0 stamps also, but version 0 stamps are no longer created as they are being phased out in favor of the more extensible v1 stamp format. *ver*:*bits*:*date*:*resource*:[*ext*]:*rand*:*counter* where *ver* = 1 *bits* = how many bits of partial-collision the stamp is claimed to have *date* = YYMMDD[hhmm[ss]] *resource* = resource string (eg IP address, email address) *ext* = extension -- ignored in the current version Format of extension: [name1[=val1[,val2...]];[name2[=val1[,val2...]]...]] Note the value can also contain =. Example extension (not a real one): name1=2,3;name2;name3=var1=2,var2=3,2,val Which would be extension name1 has values 2 and 3; extension name2 has no values; extension name3 has 3 values "var1=2", "var2=3", "2" and "val". The hashcash extension may interpret the values as it sees fit eg "var1=2" could be the value of an option to the extension name3. *rand* = string of random characters from alphabet a-zA-Z0-9+/= to avoid collisions with other sender's stamps *counter* = to find a stamp with the desired number of collision bits need to try lots of different strings this counter is incremented on each try. The Counter is also composed of characters from the alphabet a-zA-Z0-9+/=. (Note an implementation is not required to count sequentially). FILES hashcash.sdb default double spend database EXIT STATUS "hashcash" returns success (exit code 0) after successfully minting a stamp, after fully checking a stamp and finding it valid, and after a timing test. If when checking a stamp it is found to be invalid (due to being malformed, being expired, having insufficient value, having a date in the future, or being double spent), "hashcash" returns failure (exit code 1). If insufficient options are given to fully check a stamp, if the stamp is otherwise valid return unchecked (exit code 2). If the *-y* flag is given and hashcash would normally return unchecked, exit code success is returned instead. If any exception occurs (file read failure for database checking or corrupted database contents) an exit status of 3 is returned. AUTHOR Written by Adam Back SEE ALSO sha1sum(1), sha1-hashcash(1), sha1(1), http://www.hashcash.org/ hashcash-1.21/hashcash.def0000664000076400007640000000155510411051106014054 0ustar adamadamEXPORTS hashcash_benchtest @1 hashcash_check @2 hashcash_core @3 hashcash_core_name @4 hashcash_count @5 hashcash_db_add @6 hashcash_db_close @7 hashcash_db_in @8 hashcash_db_open @9 hashcash_estimate_time @10 hashcash_expected_tries @11 hashcash_free @12 hashcash_from_utctimestr @13 hashcash_make_header @14 hashcash_mint @15 hashcash_parse @16 hashcash_per_sec @17 hashcash_resource_match @18 hashcash_simple_mint @19 hashcash_to_utctimestr @20 hashcash_use_core @21 hashcash_valid_for @22 hashcash_validity_to_width @23 hashcash_version @24 sdb_add @25 sdb_callbacklookup @26 sdb_callbacklookupnext @27 sdb_close @28 sdb_del @29 sdb_findfirst @30 sdb_findnext @31 sdb_lookup @32 sdb_lookupnext @33 sdb_open @34 sdb_updateiterate @35 hashcash-1.21/sha1-hashcash.10000644000076400007640000002017310351101612014304 0ustar adamadam.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "SHA1-HASHCASH 1" .TH SHA1-HASHCASH 1 "2004-12-17" "1.15" "sha1" .SH "NAME" sha1 \- Secure Hash Algorithm (version 1) hash function .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Sh "hash files:" .IX Subsection "hash files:" \&\fBsha1\fR [ \fIfiles\fR ] .SH "DESCRIPTION" .IX Header "DESCRIPTION" This is an implementation of the Secure Hash Algorithm version 1 as defined in \s-1US\s0 Federal Information Processing Standard \*(L"\s-1FIPS\s0 180\-1\*(R". .PP This was shipped for convenience with the hashcash tool, but also functions similarly to the widely distributed md5sum utility but using \s-1SHA1\s0 instead of \&\s-1MD5\s0 so you may find other uses for it. (Some have argued that \s-1MD5\s0 is too small a hash to use for checking files if the attacker can insert chosen files into your distribution as then a birthday attack becomes possible for the master md5sum with work factor of about 2^64, which is a fairly high cost but not infeasible.) .SH "OTHER IMPLEMENTATIONS" .IX Header "OTHER IMPLEMENTATIONS" This program is fairly compatible with \fBopenssl sha1\fR (\fIsha1\fR\|(1)) and \&\fBsha1sum\fR (\fIsha1sum\fR\|(1)) installed on some unix systems and included with hashcash package so that a sha1 implementation is available for testing and scripting if those packages are missing. .SH "USAGE NOTES" .IX Header "USAGE NOTES" The sha1 program will hash the files given on the command line, or if no files are given it will hash the input fed to it on standard input. The output format is a list of \s-1SHA1\s0 hashes in hex followed by the corresponding filenames, one per line. .SH "EXAMPLES" .IX Header "EXAMPLES" .Sh "Hashing files" .IX Subsection "Hashing files" .ie n .IP """sha1 file1 file2 [...]""" 4 .el .IP "\f(CWsha1 file1 file2 [...]\fR" 4 .IX Item "sha1 file1 file2 [...]" Hashes the files listed on the command line and outputs their \s-1SHA1\s0 message digests as 40 hexadecimal digits, followed by the filename, one per line. .ie n .IP """echo \-n abc | sha1""" 4 .el .IP "\f(CWecho \-n abc | sha1\fR" 4 .IX Item "echo -n abc | sha1" When no files are given, hashes from standard in. The example command hashes the string \*(L"abc\*(R" from standard input. This string is coincidentally one of the test vectors specified in \s-1FIPS\s0 180\-1 and should output: \fIa9993e364706816aba3e25717850c26c9cd0d89d\fR. .ie n .IP """echo \-n abc | sha1 \-""" 4 .el .IP "\f(CWecho \-n abc | sha1 \-\fR" 4 .IX Item "echo -n abc | sha1 -" Equivalent to above. The filename of \fI\-\fR means read from standard input. .ie n .IP """echo \-n abc | sha1 test.txt \-""" 4 .el .IP "\f(CWecho \-n abc | sha1 test.txt \-\fR" 4 .IX Item "echo -n abc | sha1 test.txt -" You can also mix filenames and reading from standard input with the \fI\-\fR pseudo file. The above command first hashes file \f(CW\*(C`test.txt\*(C'\fR then reads the string abc from standard in. .SH "Verifying Hashcash" .IX Header "Verifying Hashcash" The \s-1SHA1\s0 hash function is used by hashcash. You can use this sha1 utility to write shell scripts to verify hashcash tokens without using the hashcash command line tool, or just to verify visually. Say you received the following email: .PP .Vb 5 \& From: Someone \& To: Adam Back \& Subject: test hashcash \& Date: Thu, 15 Aug 2002 11:12:02 +0000 \& X-Hashcash: 0:030626:adam@cypherspace.org:6470e06d773e05a8 .Ve .PP Then the following command would show visually the size of the collision: .ie n .IP """echo \-n 0:030626:adam@cypherspace.org:6470e06d773e05a8 | sha1""" 4 .el .IP "\f(CWecho \-n 0:030626:adam@cypherspace.org:6470e06d773e05a8 | sha1\fR" 4 .IX Item "echo -n 0:030626:adam@cypherspace.org:6470e06d773e05a8 | sha1" .Vb 1 \& 00000000c70db7389f241b8f441fcf068aead3f0 .Ve .PP and you can see the leading hex 0s. .SH "LIMITATIONS" .IX Header "LIMITATIONS" Doesn't have the check option that md5sum has. Perhaps I'll add that in a future version. Also doesn't have the binary / text distinction that md5sum introduced for DOS/Windows benefit, nor the output convention signifying text (* after hash). Can't say I've ever seen anyone use that feature though. .SH "EXIT STATUS" .IX Header "EXIT STATUS" \&\f(CW\*(C`sha1\*(C'\fR returns success (exit code 0) normally. .PP If it can't read any of the files you give it on the comamnd line, it instead returns failure (exit code 1). .SH "AUTHOR" .IX Header "AUTHOR" Written by Adam Back .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fImd5sum\fR\|(1), \fIsha1\fR\|(1), \fIsha1sum\fR\|(1), \fIhashcash\fR\|(1), http://www.hashcash.org/, http://www.itl.nist.gov/fipspubs/fip180\-1.htm hashcash-1.21/hashcash.pod0000664000076400007640000007744310351101612014112 0ustar adamadam=head1 NAME hashcash - hashcash anti-spam / denial of service counter-measure tool =head1 SYNOPSIS =head2 Mint (create) stamps: B I<-m> [ [ I ] [ I ... ] ... ] =head2 Check stamps: B I<-c> [ [ I ] I<-r resource> ... ] [ I ... ] =head2 Speed estimates: B I<-s> [ I ] [ I<-b bits> ] =head2 Purge database: B I<-p now> [ I<-j resource> ] [ I<-k> ] [ I<-t time> ] [ I<-u> ] =head2 Count collision bits: B I<-w> [ I ] [ I ... ] =head2 Get resource name: B I<-n> [ I ] [ I ... ] =head2 Get time remaining until expiry B I<-l> [ I ] I<-e period> [ I ... ] =head1 DESCRIPTION Hashcash is a denial-of-service counter measure tool. Its main current use is to help hashcash users avoid losing email due to content based and blacklist based anti-spam systems. The hashcash tool allows you to create hashcash stamps to attach to emails you send, and to verify hashcash stamps attached to emails you receive. Email senders attach hashcash stamps with the X-Hashcash: header. Vendors and authors of anti-spam tools are encouraged to exempt mail sent with hashcash from their blacklists and content based filtering rules. A hashcash stamp constitutes a proof-of-work which takes a parameterizable amount of work to compute for the sender. The recipient can verify received stamps efficiently. This package also includes a sha1 implementation which is equivalent in functionality to the sha1sum program available on some systems. The hashcash proof-of-work function is to compute partial hash collisions (the SHA1 hash function is used). By choosing the number of bits of collision, the work required to compute a collision can be made arbitrarily expensive -- from fractions of a second to minutes or hours. The verification is efficient requiring the same small cost whatever the collision size. For more detailed discussion of other applications hashcash has been used for see http://www.hashcash.org/ =head1 USAGE NOTES In this man page a resource name is the name of the service or address the stamp is created for. In the case of email, the resource name is the recipient's email address in the form user@domain.com. =head2 Minting stamps The I<-m> flag must be given to mint a stamp. The resource name (recipient's email address) to mint the stamp against can be passed as an argument, or if omitted is read from stdin. If stdin is a tty the user is prompted, if stdin is a pipe the resource name is just silently read. The desired collision size can be specified with the -b option. If no collision size is specified, the default is 20 bits. See also the I<-b default> option. =head2 Checking stamps The I<-c> flag must be given to check a stamps expiry. The stamp to check can be given as an argument to C. If no stamp is given the stamp is read from stdin. If stdin is a tty the user will be prompted, if stdin is a pipe the stamp is just silently read. A resource name (the recipient's email address) can be given with the I<-r> option. If a resource name is given the resource name is compared to the resource name in the stamp, if they do not match, the stamp is rejected. Note: if no resource name is given the stamp is anyway checked to see if it is otherwise valid, but it could be minted for a different resource, which would allow stamps to be reused across different resources, so hashcash will return unchecked exit code on exit. Stamps are by default considered to be valid for 28 days. The validity period can be changed using the I<-e> flag. If the stamp has expired or has a date in the future the stamp is rejected and the program exits immediately. If a required collision size is given with the I<-b> flag, the stamps value is computed and compared, if the stamp has insufficent value it is rejected, and the program exits immediately. If the I<-b> flag is not given, the stamp is checked to see if it is otherwise valid, but hashcash will return unchecked exit code on exit. If the stamp is double spent the stamp is rejected. Double spending protection is discussed in more detail below in L. If double spending protection is not enabled, the stamp could be double spent, so hashcash will return unchecked exit code (exit code 2) on exit. The I<-w> flag can be used to request that the number of bits of the collision are counted and displayed. The I<-n> flag can be used to request that the resource name in the stamp is parsed out and displayed. The I<-l> flag can be used to request the number of seconds until expiry of the stamp is output. The program will only return exit codes valid or invalid if the I<-c> flag is used, the I<-b> flag is used, I<-d>, I<-r resource> are used. These are the minimum set of options necessary to fully check the validty of a stamp. If these criteria are not met, the program will return exit code unchecked (exit code 2) on exit. (See also the I<-y> flag.) =head2 Double Spending Protection If the I<-d> flag is used when checking stamps, a database of spent stamps is kept. By default stamps expire after 28 days, without expiry the database would grow indefinately. You can specify an alternate expiry period with the I<-e> flag. The recommended (and default) expiry period for email is 28 days. After the expiry period amount of time, the stamp is anyway considered expired and may be purged from the database to save space. (See L for how to purge stamps.) For efficiency reasons a stamp is verified before it is checked in the database; if it is otherwise invalid no database activity will occur. Note: The decision about how long the stamp should be considered valid is up to the verifier. If it is too short it is possible for some applications that the stamp will expire before arriving at the recipient (eg with email.) The suggested value of 28 days should be safe for normal email delivery delays. The choice is a trade-off between database size and risk of expiry prior to arrival, and depends on the application. Note: Different stamps in the same database can have different validity periods, so for example stamps for different resources with different validity periods can be stored in the same database, or the recipient may change the validity period for future stamps without affecting the validity of old stamps. =head2 Purging Periodically vs on Next Access To purge old stamps periodically while checking stamps use the I<-p period> option to purge no sooner than the given time period since the last purge. Purging can be used with the I<-k> option to purge unexpired stamps also, and with the I<-j resource> flag to purge only stamps for the given resource. There are circumstances where it may be inconvenient to purge stamps on the next access, for example if there is a large double spend database which takes some time to purge, and the response time of the hashcash checker is important. To avoid this problem, purging can be done separately using just the I<-p now> option to request just the purge operation. On unix for example you could call C in a cron job once per day, or on demand when disk was running low. =head2 Speed Estimates The I<-s> flag requests measurement of how many collisions can be tested per second. No stamp is minted, or verified. If the I<-b> flag is used with this option, instead an estimate of how many seconds it would take to mint a stamp of the given size in bits is computed. To find out how much time it will take to mint a default sized stamp use I<-s -b default>. =head2 Notes All informational output is printed on stderr. Minted stamps, and results of stamp verification and timing are printed on stdout. The quiet flag I<-q> suppresses all informational output. The I<-v> flag requests more informational output. The requested output, which is the only information that is output in quiet mode (when I<-q> is specified) is printed on standard output. If stdout is a pipe, or when quiet mode is in effect the output is printed without description (ie just bits, just seconds, just resource). =head1 OPTIONS =over 4 =item I<-c> Check the expiry information of stamps given as an argument or on stdin. (Use with I<-b>, I<-d> and I<-r resource> to fully check stamps). =item I<-m> Mint stamps with the resources given as arguments or on stdin. =item I<-b bits> When minting a stamp, create a collision of at least this many bits. When verifying a stamp require that it have a collision of at minimum this many bits, otherwise reject it. If omitted the default is used. When checking stamps, require that the stamps have this many bits. The default number of bits can be specified with I<-b default>. Bits relative to the default can also be specified with I<-b +n> for n bits more than the default and I<-b -n> for n bits less than the default. I<-b default>, I<-b +0> and I<-b -0> are all equivalent. When doing the speed test I<-s>, can to measure speed of default token with I<-s -b default>. =item I<-r resource> When minting stamps, the resource name (recipient's email address) to mint the stamp against can be given either with I<-r resource> or as an argument to C. When checking stamps, the resource name (your own email address) is given with the I<-r> option. If the resource name is given it is checked against the resource name in the stamp, and if they do not match the stamp is rejected. Note if the resource name is not given, stamps for other resources would be accepted, and therefore hashcash returns exit code unchecked (exit code 2) on exit. =item I<-o> When verifying stamps multiple resources can be given. By default the resources are just checked one by one until a matching valid resource is found. However when you use wildcards or regular expressions (see I<-E>), it is useful to be able to specify that one resource overrides another. For example this: I<-b15 -r adam@dev.null -o -b10 *@dev.null> states that mail to address I requires 15 bits, but mail to I<*@dev.null> requires only 10 bits. If we omitted the I<-o> override relationship between the two resources, a stamp of 10 bits would be accepted for address I because while it would be rejected as having insufficient bits under the first rule, it would be accepted under the 2nd rule. The I<-o> option allows you avoid this problem. =item I<-e time> Expiry period for spent stamps. While checking stamps (using the I<-c> flag), if the stamp was minted more than the specified amount of time ago, it is considered expired. If this option is not used, by default stamps expire after 28 days. The expiry period is given in seconds by default (an argument of 0 means forever). A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). If used with the I<-d> option, the spent stamp and its expiry period is recorded in the database. See the I<-p> option for description of how to purge stamps from the database. While minting stamps, the I<-e> flag can have an effect on the resolution of time created in the stamp. Without the I<-e> option, the default resolution is days (time format: YYMMDD). Alternate formats based on range of expiry period are as follows: While minting you can also given an explicit time width with the I<-z> option instead. (I<-z> overrides I<-e> if both are given. If neither are given the default is 6 chars (time format: YYMMDD)). The rules for automatically determining appropriate time width from I<-e> if no I<-z> option is given are: =over 8 =item * period E= 2 years then time format YY is used rounded down to the nearest year start; =item * 2 years E period E= 2 months then time format YYMM is used rounded down to the nearest month start; =item * 2 months E period E= 2 days then time format YYMMDD is used rounded down to the begining of the nearest day; =item * 2 days E period E= 2 minutes then time format YYMMDDhhmm is used rounded down to the begining of the nearest minute; =item * period E 2 minutes then time format YYMMDDhhmmss is used in seconds. =back Note the rounding down is based on UTC time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than GMT (UTC = GMT). It may be clearer to understand if you use the I<-u> option. =item I<-z width> The I<-z> option is for use during minting and allows user choice of width of time width field. See also the I<-e> option given in combination with I<-m> to specify an implicit time field width under the description of the I<-e> flag. Valid widths are 6,10 or 12 chars corresponding respectively to: YYMMDD, YYMMDDhhmm, and YYMMDDhhmmss rounded down to the nearest day, or minute respectively. Note the rounding down is based on UTC time, not local time. This can lead to initially suprising results when rounding down to eg days in time zones other than GMT (UTC = GMT). It may be clearer to understand if you use the I<-u> option. =item I<-g period> The I<-g> option is for use when checking hashcash stamps with the I<-c> option and specifies a grace period for clock skew, ie if a hashcash stamp arrives with a date in the future or in the past it will not be rejected as having a futuristic date (or as being expired) unless it is more futuristic (or has been expired for longer) than this period. The default is 2 days, which means as long as the sending system's clock is no more than 2 days ahead (or 2 days behind) of the receiving system's clock, the hashcash stamp will still be accepted. The default units for grace period are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). =item I<-d> Store stamps in a double spend database. If stamp has been seen before it will be rejected even if it is otherwise valid. The default database file is F in the current directory. Only otherwise valid stamps will be stored in the database. Only fully validated stamps will be stored in the database, unless the I<-y> option is given. =item I<-f dbname> Use F instead of default filename for double spend database. =item I<-p period> Purges the database of expired stamps if the given time period has passed since the last time it was purged. As a convenience I<-p now> is equivalent to I<-p 0> both of which mean purge now, regardless of when the database was last purged. If used in combination with I<-j resource> only the stamps minted for the given resource are purged. If used in combination with I<-k> all stamps even un-expired stamps are purged. Can be used in combination with I<-t time> to expire as if the current time were the given time. =item I<-k> Use with option I<-p> to request all stamps are purged rather than just expired ones. =item I<-j resource> Use with option I<-p> to request that just stamps matching the given resource name are to be purged, rather than the default which is to purge all expired stamps. If the resource name is the empty string, all stamps are matched (this is equivalent to omitting the I<-j> option). Note the I<-E>, I<-M> and I<-S> type of match flags also apply to resources given with the I<-j resource> flag. =item I<-s> Print timing information only, and don't proceed to create a stamp. If combined with I<-b bits> flag print estimate of how long the requested collision size would take to compute, if I<-s> given by itself, just prints speed of the collision finder. To print an estimate of how long the default number of bits would take use I<-b default>. =item I<-h> Print short usage information. =item I<-v> Print more verbose informational output about the stamp minting or verification. (If -v is the only argument, prints the tool version number.) =item I<-V> Prints tool version number. =item I<-q> Batch mode. Prints no information other than output. This option overrides the I<-v> option. =item I<-X> When minting, prints the hashcash email X-header 'X-Hashcash: ' before the stamp. Without this option just the bare stamp is printed. When checking, after scanning stamps given as arguments, scans stdin for lines starting with the string 'X-Hashcash:', and uses the rest of the matching line as the stamp. Only the lines up to and ending at the first blank line are scanned (see also I<-i> flag which can be used to override this). A blank line is the separator used to separate the headers from the body of a mail message or USENET article. This is meant to make it convenient to pipe a mail message or USENET article to hashcash on stdin. =item I<-x extension> An extension string composed of name value sets. The extension format is described below in the section on the hashcash stamp format. This allows users to define their own stamp extensions which are hashed into the stamp, verified by recipients that support them, and ignored by recipients that don't support them. Note the extension hook mechanism has not yet been implemented. This will come in a subsequent release. =item I<-i> When checking and using the I<-X> flag, ignore the blank line boundary between headers and body of the message, and check for collision in the body too if one is not found in the headers. =item I<-t time> Pretend the current time is the time given for purposes of minting stamps, verifying stamps and purging old stamps from the database. Time is given in a format based on UTCTIME format YYMMDD[hhmm[ss]]. Time is expressed in local time by default. Use with I<-u> flag to give time in UTC (GMT). You can also give time relative to the current time by prefixing the argument with + or -. The default units for relative time are seconds. A single character suffix can be used to specify alternate units (m = minutes, h = hours, d = days, M = months, y = Y = years, and s = seconds). Note: when time is expressed in local time, if there is daylight savings in your timezone, there are one or two ambiguous hours per year at the time of change from daylight savings time to normal time. =item I<-u> Input and output absolute times in UTC (GMT) instead of local time. =item I<-a period> Add (or subtract if number is negative) a random value from the current time before minting the stamp. This hides the time the stamp was created, which may be useful for anonymous users. Note adding (rather than subtracting) a random time may be risky if the stamp takes less than the added time to arrive as the recipient will reject stamps with time stamps in the future. =item I<-n> Print resource name parsed from stamp being verified. Returns exit code unchecked on exit. =item I<-l> Print number of seconds left before stamp expires. Returns exit code unchecked on exit. Note: the calculation includes the grace period, so can be up to 2 times grace period longer than you might otherwise expect (clock fast but system has to presume it could be slow). If you want to exclude the grace period add I<-g0> to set grace period to 0 for the calculation. =item I<-w> Print number of bits of collision of stamp. Returns exit code unchecked on exit. =item I<-y> Returns success if the stamp is valid even if it is not fully checked. Use with I<-c> where not all of I<-d>, I<-r> are specified to get success exit code on valid but partially checked stamp. Similarly can use with I<-n>, I<-l>, I<-w> with same effect. =item I<-M> When checking stamps, allow wildcard I<*> matching in the resource name to make it simpler to specify multiple email addresses and to allow matching catch-all addresses and addresses including subdomains. This is the default. See also I<-S>, I<-E> and I<-C> =item I<-S> When checking stamps use simple text compare to compare resource names to those in stamps. See also I<-M>, I<-E> and I<-C>. =item I<-E> When checking stamps use regular expressions to specify resource names to make it simpler to specify multiple email addresses, catch-all addresses, classes of extension addresses and addresses including subdomains. Note regular expression syntax is POSIX style: special characters do not need to be quoted to have their special meaning; but they do have to be quoted with \ to that character in the searched string. The regular expression automatically has ^ added at the beginning and $ added at the end, if they are not specified. The special characters ^ matches the beginning of the resouce, and $ matches the end of resource. (Note even if compiled with BSD regular expressions, POSIX style syntax is used; also note BSD regular expressions do not support ranges {}.) =item I<-C> By default resources are canonicalized to lower case on minting and on checking. The I<-C> flag overrides this so that resources are treated as case sensitive on checking, and not canonizalized on minting. =item I<-P> Print progress info (number of iterations, expected iterations, percentage done, best stamp size found so far). =item I<-O core> Select hashcash core with that number. Currently 0-9 are valid cores. Not all cores work on all architectures. Eg some are x86 specific assembler, others PPC specific assembler. If a core is not valid hashcash returns failure and explains what happened. =item I<-Z n> Compress the stamp. This is a time vs space trade off. Larger stamps are faster, but arguably slightly ugly. For fastest stamps (the default) use -Z 0; for partly compressed stamps use -Z 1; for very compressed, but somewhat slow stamps use -Z 2. (Note: due to a late discovered bug, -Z2 is the same as -Z1 for now until I can fix that.) =back =head1 EXAMPLES =head2 Creating stamps =over 4 =item C Print timing information about how many collisions the machine can try per second. =item C More accurate but quite slow benchmarking of different processor specific minting cores. =item C Print how long it would take the machine to compute a default sized collision (but don't actually compute a collision). =item C Print how long it would take the machine to compute a 32 bit collision (but don't actually compute a collision). =item C Mint a stamp. Will prompt for resource name and mint with default value (number of collision bits). =item C Compute collision on resource foo. Will mint with default value (number of collision bits). =item C Compute 10 bit collision on resource foo. =item C Subtract a random time of between 0 days and 3 days from the stamp's creation time. This is the same fuzz factor used by mixmaster to reduce risk of timing-correlations. =back =head2 Examining Stamps =over 4 =item C Report the value of the stamp (how many bits of collision) there are. The example is a 24 bit collision, which takes on average 25 seconds to create on a 3Ghz P4. =item C Create a stamp in batch mode, pass to hashcash on stdin to verify, have it print how many bits there were. =item C Report the resource name from the stamp. The resource name in the example is foo. =item C Report how long until the stamp expires if it expires in 30 years from its creation date. (Note dates too far into the future run into the 2038 end of Epoch, which is the unix time analog of the y2k bug). =back =head2 Verifying Stamps =over 4 =item C Check if the stamp is valid. Note as we are not checking the stamp in a double spend database, and did not specify a resource name or required number of bits of collision and hashcash will consider the stamp not fully checked, and it will report it as valid but not fully unchecked, or as invalid if there is any problem with the stamp. =item C Check that the value of the stamp is greater or equal to 24 bits. This example has 24 bit value. If you increase the requested number of bits or replace the stamp with one with less than 24 bit collision the stamp will be rejected. =item C As above check if the stamp has sufficient value, but in addition check that the resource name given matches the resource name in the stamp. =back =head2 Double Spending Prevention The examples given in L can be modified to keep a double spend database so that the same stamp will not be accepted twice. Note a stamp will only be checked in and added to the database if it is otherwise valid and fully checked (a required number of bits of collision has been specified and a resource has been specified). =over 4 =item C Check the stamp and add to double spent database if it's valid (has correct resource name and sufficient value). =item C Try to double spend the stamp. It will be rejected as double spent. =back =head2 Stamp Expiry To prevent the double spend database growing indefinately, the recipient can request that stamps be no older than a specified period. After expiry old stamps can dropped from the double spend database as they will no longer be needed -- expired stamps can be rejected based purely on their old date, so the space taken by expired stamps in the double spend database can be saved without risk of accepting an expired though otherwise valid stamp. The third field of the stamp is the UTC time since 1st January 1970. The default time format is YYMMDD, time rounded down to the nearest day. The default validity period is 28 days. You can provide an alternative validity period with the I<-e> option. =over 4 =item C Try verifying an old stamp, the above stamp was created 11 Aug 2002. We gave option I<-e 2d> so the stamps expiry date is 2 days after creation, which is now in the past. Note: if the creation time is expressed in the stamp in days, the precise creation date is the begining of the specified day in UTC time (similarly for alternate units the creation time is rounded down to the begining of the unit it is expressed in). For units in days, for example, this may mean depending on your time zone that the stamp appears to be considered invalid in under the specified expiry period in days relative to your relative view of what day it is, as the calculation is based on current time in UTC, and the creation time of the stamp is expressed in UTC time. =item C Test whether the stamp is otherwise valid, apart from having expired. Omitting the I<-e> tells hashcash that the stamp will never expire. An expiry period of forever can also be given explitly like this: I<-e 0>, where an expiry period of 0 means forever. =back =head2 Purging old stamps If the I<-c>, I<-d> options are used together, each time a stamp is checked, if it is valid and all of the mandatory aspects of the stamp are verified (collision bits check, resource name check) then the stamp and its expiry period is written to the database file. The default expiry period if an expiry period is not given explicitly with the I<-e> option is 28 days (ie stamps expire after 4 weeks). First mint and then add a stamp: =over 4 =item C stamp> Note: we specified an expiry on minting in this example, to ensure that the stamp creation time is given in high enough resolution in the stamp that the stamp will not be considered expired at time of creation. (Recall the default resolution is in days, a stamp created with a creation time rounded down to the beginging of the day is unlikely to be considered valid 1 minute later unless you mint it at midnight UTC time.) =item C stamp> The stamp expires in 1 minute. Wait 1 minute and then explicitly request that expired stamps be purged: =item C Then try resubmitting the same stamp: =item C stamp> and the stamp will be rejected anyway as it has expired, illustrating why it was not necessary to keep this stamp in the database. With the default database (the sdb format) the database contents are human readable, so you can view their contents by cating them to the terminal: =item C to see that the stamp really is added and then after puring subsequently purged due to expiry. =back =head2 Purging old stamps on Demand As a convenience you can purge at the same time as checking stamps by using the I<-p> option with the I<-c> option. =over 4 =item C stamp> =item C stamp> It may be inefficient to purge stamps on every use as the entire database has to be scanned for expired stamps. By giving a time period to the I<-p> option, you can tell C to purge no more frequently than that time period since the previous purge. For example: =item C stamp> tells C to purge any expired stamps no more than once per day. =item C tells C to purge only expired stamps matching resource foo once per month. =item C tells C to purge all stamps (expired and unexpired) now. =back =head1 stamp format (version 1) The current stamp format is version 1. This tool can verify hashcash version 0 stamps also, but version 0 stamps are no longer created as they are being phased out in favor of the more extensible v1 stamp format. =over 4 =item I:I:I:I:[I]:I:I =back where =over 4 =item I = 1 =item I = how many bits of partial-collision the stamp is claimed to have =item I = YYMMDD[hhmm[ss]] =item I = resource string (eg IP address, email address) =item I = extension -- ignored in the current version Format of extension: =over 4 =item [name1[=val1[,val2...]];[name2[=val1[,val2...]]...]] Note the value can also contain =. Example extension (not a real one): name1=2,3;name2;name3=var1=2,var2=3,2,val Which would be extension name1 has values 2 and 3; extension name2 has no values; extension name3 has 3 values "var1=2", "var2=3", "2" and "val". The hashcash extension may interpret the values as it sees fit eg "var1=2" could be the value of an option to the extension name3. =back =item I = string of random characters from alphabet a-zA-Z0-9+/= to avoid collisions with other sender's stamps =item I = to find a stamp with the desired number of collision bits need to try lots of different strings this counter is incremented on each try. The Counter is also composed of characters from the alphabet a-zA-Z0-9+/=. (Note an implementation is not required to count sequentially). =back =head1 FILES =over 4 =item F default double spend database =back =head1 EXIT STATUS C returns success (exit code 0) after successfully minting a stamp, after fully checking a stamp and finding it valid, and after a timing test. If when checking a stamp it is found to be invalid (due to being malformed, being expired, having insufficient value, having a date in the future, or being double spent), C returns failure (exit code 1). If insufficient options are given to fully check a stamp, if the stamp is otherwise valid return unchecked (exit code 2). If the I<-y> flag is given and hashcash would normally return unchecked, exit code success is returned instead. If any exception occurs (file read failure for database checking or corrupted database contents) an exit status of 3 is returned. =head1 AUTHOR Written by Adam Back Eadam@cypherspace.orgE =head1 SEE ALSO sha1sum(1), sha1-hashcash(1), sha1(1), http://www.hashcash.org/ hashcash-1.21/fastmint_ansi_compact_1.c0000664000076400007640000001502210351101612016536 0ustar adamadam#include #include #include "libfastmint.h" int minter_ansi_compact_1_test(void) { /* This minter runs on any hardware */ return 1; } /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) #define ROUND(t,A,B,C,D,E,Func,K) \ E += S(5,A) + Func(B,C,D) + W[t] + K; \ B = S(30,B); #define ROUND5( t, Func, K ) \ ROUND( t + 0, A, B, C, D, E, Func, K );\ ROUND( t + 1, E, A, B, C, D, Func, K );\ ROUND( t + 2, D, E, A, B, C, Func, K );\ ROUND( t + 3, C, D, E, A, B, Func, K );\ ROUND( t + 4, B, C, D, E, A, Func, K ) #define ROUND20( t, Func, K )\ ROUND5( t + 0, Func, K );\ ROUND5( t + 5, Func, K );\ ROUND5( t + 10, Func, K );\ ROUND5( t + 15, Func, K ) unsigned long minter_ansi_compact_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { MINTER_CALLBACK_VARS; unsigned long iters = 0 ; int t = 0, gotBits = 0, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low = 0 , bitMask1High = 0 , s = 0 ; uInt32 A = 0 , B = 0 , C = 0 , D = 0 , E = 0 ; uInt32 W[80] = {0}; uInt32 H[5] = {0}, pH[5] = {0}; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X = (unsigned char*) W; int addressMask = 0 ; static const int endTest = 3; unsigned char *output = (unsigned char*) block; *best = 0; /* Work out whether we need to swap bytes during encoding */ addressMask = ( *(char*)&endTest ); /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } maxBits = 0; /* Copy block and IV to internal storage */ for(t=0; t < 16; t++) W[t] = GET_WORD(output + t*4); for(t=0; t < 5; t++) pH[t] = H[t] = IV[t]; /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter; iters++) { /* Encode iteration count into tail */ X[(tailIndex - 1) ^ addressMask] = p[(iters ) & 0x3f]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(tailIndex - 2) ^ addressMask] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(tailIndex - 3) ^ addressMask] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(tailIndex - 4) ^ addressMask] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(tailIndex - 5) ^ addressMask] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(tailIndex - 6) ^ addressMask] = p[(iters >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; for(t=16; t < 32; t++) Wf(t); ROUND( 0, A, B, C, D, E, F1, K1 ); ROUND( 1, E, A, B, C, D, F1, K1 ); ROUND( 2, D, E, A, B, C, F1, K1 ); ROUND( 3, C, D, E, A, B, F1, K1 ); ROUND( 4, B, C, D, E, A, F1, K1 ); ROUND( 5, A, B, C, D, E, F1, K1 ); ROUND( 6, E, A, B, C, D, F1, K1 ); if(tailIndex == 52) { ROUND( 7, D, E, A, B, C, F1, K1 ); ROUND( 8, C, D, E, A, B, F1, K1 ); ROUND( 9, B, C, D, E, A, F1, K1 ); ROUND(10, A, B, C, D, E, F1, K1 ); ROUND(11, E, A, B, C, D, F1, K1 ); } pH[0] = A; pH[1] = B; pH[2] = C; pH[3] = D; pH[4] = E; } /* Fill W buffer */ switch(tailIndex) { default: Wf(16); Wf(17); Wf(18); Wf(19); Wf(22); Wf(25); case 32: /* 21,23 */ Wf(21); /* 24,29,35,37 */ Wf(24); /* 27,... */ Wf(27); /* 30,... */ Wf(30); /* */ case 52: /* 20,26,28 */ Wf(20); /* 23,28,34,36 */ Wf(23); /* 26,31,... */ Wf(26); /* 29,... */ Wf(28); /* 31,... */ Wf(29); /* */ Wf(31); /* */ } for(t=32; t < 80; t++) Wf(t); /* Set up working variables */ A = pH[0]; B = pH[1]; C = pH[2]; D = pH[3]; E = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUND( 0, A, B, C, D, E, F1, K1 ); ROUND( 1, E, A, B, C, D, F1, K1 ); ROUND( 2, D, E, A, B, C, F1, K1 ); ROUND( 3, C, D, E, A, B, F1, K1 ); ROUND( 4, B, C, D, E, A, F1, K1 ); ROUND( 5, A, B, C, D, E, F1, K1 ); ROUND( 6, E, A, B, C, D, F1, K1 ); case 32: ROUND( 7, D, E, A, B, C, F1, K1 ); ROUND( 8, C, D, E, A, B, F1, K1 ); ROUND( 9, B, C, D, E, A, F1, K1 ); ROUND(10, A, B, C, D, E, F1, K1 ); ROUND(11, E, A, B, C, D, F1, K1 ); case 52: ROUND(12, D, E, A, B, C, F1, K1 ); ROUND(13, C, D, E, A, B, F1, K1 ); ROUND(14, B, C, D, E, A, F1, K1 ); ROUND(15, A, B, C, D, E, F1, K1 ); ROUND(16, E, A, B, C, D, F1, K1 ); ROUND(17, D, E, A, B, C, F1, K1 ); ROUND(18, C, D, E, A, B, F1, K1 ); ROUND(19, B, C, D, E, A, F1, K1 ); } ROUND20(20,F2,K2); ROUND20(40,F3,K3); ROUND20(60,F4,K4); /* Mix in the IV again */ A += H[0]; B += H[1]; C += H[2]; D += H[3]; E += H[4]; /* Is this the best bit count so far? */ if(!(A & bitMask1Low) && !(B & bitMask1High)) { /* Count bits */ gotBits = 0; if(A) { s = A; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(B) { s = B; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best=gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } /* Copy this result back to the block buffer */ for(t=0; t < 16; t++) PUT_WORD(output + t*4, W[t]); /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+1; } } MINTER_CALLBACK(); } return iters+1; } hashcash-1.21/sha1-hashcash.txt0000644000076400007640000000713010351101612014761 0ustar adamadamNAME sha1 - Secure Hash Algorithm (version 1) hash function SYNOPSIS hash files: sha1 [ *files* ] DESCRIPTION This is an implementation of the Secure Hash Algorithm version 1 as defined in US Federal Information Processing Standard "FIPS 180-1". This was shipped for convenience with the hashcash tool, but also functions similarly to the widely distributed md5sum utility but using SHA1 instead of MD5 so you may find other uses for it. (Some have argued that MD5 is too small a hash to use for checking files if the attacker can insert chosen files into your distribution as then a birthday attack becomes possible for the master md5sum with work factor of about 2^64, which is a fairly high cost but not infeasible.) OTHER IMPLEMENTATIONS This program is fairly compatible with openssl sha1 (sha1(1)) and sha1sum (sha1sum(1)) installed on some unix systems and included with hashcash package so that a sha1 implementation is available for testing and scripting if those packages are missing. USAGE NOTES The sha1 program will hash the files given on the command line, or if no files are given it will hash the input fed to it on standard input. The output format is a list of SHA1 hashes in hex followed by the corresponding filenames, one per line. EXAMPLES Hashing files "sha1 file1 file2 [...]" Hashes the files listed on the command line and outputs their SHA1 message digests as 40 hexadecimal digits, followed by the filename, one per line. "echo -n abc | sha1" When no files are given, hashes from standard in. The example command hashes the string "abc" from standard input. This string is coincidentally one of the test vectors specified in FIPS 180-1 and should output: *a9993e364706816aba3e25717850c26c9cd0d89d*. "echo -n abc | sha1 -" Equivalent to above. The filename of *-* means read from standard input. "echo -n abc | sha1 test.txt -" You can also mix filenames and reading from standard input with the *-* pseudo file. The above command first hashes file "test.txt" then reads the string abc from standard in. Verifying Hashcash The SHA1 hash function is used by hashcash. You can use this sha1 utility to write shell scripts to verify hashcash tokens without using the hashcash command line tool, or just to verify visually. Say you received the following email: From: Someone To: Adam Back Subject: test hashcash Date: Thu, 15 Aug 2002 11:12:02 +0000 X-Hashcash: 0:030626:adam@cypherspace.org:6470e06d773e05a8 Then the following command would show visually the size of the collision: "echo -n 0:030626:adam@cypherspace.org:6470e06d773e05a8 | sha1" 00000000c70db7389f241b8f441fcf068aead3f0 and you can see the leading hex 0s. LIMITATIONS Doesn't have the check option that md5sum has. Perhaps I'll add that in a future version. Also doesn't have the binary / text distinction that md5sum introduced for DOS/Windows benefit, nor the output convention signifying text (* after hash). Can't say I've ever seen anyone use that feature though. EXIT STATUS "sha1" returns success (exit code 0) normally. If it can't read any of the files you give it on the comamnd line, it instead returns failure (exit code 1). AUTHOR Written by Adam Back SEE ALSO md5sum(1), sha1(1), sha1sum(1), hashcash(1), http://www.hashcash.org/, http://www.itl.nist.gov/fipspubs/fip180-1.htm hashcash-1.21/libsha1.c0000664000076400007640000002375110351101612013304 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ /* * Implementation of Federal Information Processing Standards Publication * FIPS 180-1 (17 Apr 1995) which supersedes FIPS 180 (11 May 1993) * * Speed hack version optimised for speed (see also reference version) * uses macros so you need to recompile, not just relink * * Adam Back * */ /* define VERBOSE to get output as in fip180-1.txt */ #if defined( VERBOSE ) #include #endif #include #include "sha1.h" static int swap_endian32( void*, size_t ); /* A run time endian test. little_endian is the broken one: 80x86s, VAXs big_endian is: most unix machines, RISC chips, 68000, etc The endianess is stored in macros: little_endian and big_endian These boolean values can be checked in your code in C expressions. They should NOT be tested with conditional macro statements (#ifdef etc). */ static const int endian_test = 1; #define little_endian ( *(char*)&endian_test == 1 ) #define big_endian ( ! little_endian ) #define make_big_endian32( data, len ) \ ( little_endian ? swap_endian32( data, len ) : 0 ) #define make_little_endian32( data, len ) \ ( little_endian ? 0 : swap_endian32( data, len ) ) #define make_local_endian32( data, len ) \ ( little_endian ? swap_endian32( data, len ) : 0 ) #if defined( OPENSSL ) void SHA1_Xform( word32* iv, const byte* data ) { SHA1_ctx c; byte d[SHA1_INPUT_BYTES]; c.h0=iv[0]; c.h1=iv[1]; c.h2=iv[2]; c.h3=iv[3]; c.h4=iv[4]; /* openSSL SHA1_Transform is in data order, trying to be helpful */ /* #undef SHA1_Transform */ /* SHA1_Transform( &c, data ); */ /* but they offer a host order version */ /* but they don't export it :-( */ /* sha1_block_asm_host_order( &c, data ); */ /* plan C, copy & convert the data on input */ #undef SHA1_Transform if ( little_endian ) { memcpy( d, data, SHA1_INPUT_BYTES ); make_local_endian32( d, SHA1_INPUT_WORDS ); SHA1_Transform( &c, d ); } else { /* not necessary on big endian */ SHA1_Transform( &c, data ); } iv[0]=c.h0; iv[1]=c.h1; iv[2]=c.h2; iv[3]=c.h3; iv[4]=c.h4; } #else #define min( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) ) /********************* function used for rounds 0..19 ***********/ /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ /* equivalent, one less operation: */ #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) /********************* function used for rounds 20..39 ***********/ #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) /********************* function used for rounds 40..59 ***********/ /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (C) & (D) ) */ /* equivalent, one less operation */ #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) /********************* function used for rounds 60..79 ***********/ #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* magic constants */ #define H0 0x67452301 #define H1 0xEFCDAB89 #define H2 0x98BADCFE #define H3 0x10325476 #define H4 0xC3D2E1F0 word32 SHA1_IV[ 5 ] = { H0, H1, H2, H3, H4 }; /* rotate X n bits left ( X <<< n ) */ #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) #if defined( word64 ) #define SHA1_zero_bitcount( ctx ) \ (ctx)->bits = 0; #else #define SHA1_zero_bitcount( ctx ) \ (ctx)->lbits = 0; \ (ctx)->hbits = 0; #endif void SHA1_Init( SHA1_ctx* ctx ) { SHA1_zero_bitcount( ctx ); memcpy( ctx->H, SHA1_IV, SHA1_DIGEST_BYTES ); } /* this is only used if you want to modify the IV */ /* ignore this function for purposes of the standard */ void SHA1_Init_With_IV( SHA1_ctx* ctx, const byte user_IV[ SHA1_DIGEST_BYTES ] ) { SHA1_zero_bitcount( ctx ); memcpy( ctx->H, user_IV, SHA1_DIGEST_BYTES ); make_local_endian32( ctx->H, SHA1_DIGEST_WORDS ); } void SHA1_Transform( word32 H[ SHA1_DIGEST_WORDS ], const byte M[ SHA1_INPUT_BYTES ] ) { #ifdef COMPACT int t = 0 ; #endif word32 A = H[ 0 ]; word32 B = H[ 1 ]; word32 C = H[ 2 ]; word32 D = H[ 3 ]; word32 E = H[ 4 ]; #if !defined( COMPACT ) word32 W[ 16 ] = {0}; #else word32 W[ 80 ] = {0}; #endif memcpy( W, M, SHA1_INPUT_BYTES ); /* Use method B from FIPS-180 (see fip-180.txt) where the use of temporary array W of 80 word32s is avoided by working in a circular buffer of size 16 word32s. (Chromatix: this is unreasonably slow on x86 due to register pressure - going back to method A) */ /********************* define some macros *********************/ /* Wc = access W as 16 word circular buffer */ #if !defined( COMPACT ) #define Wc( t ) ( W[ (t) & 0x0F ] ) #else #define Wc( t ) ( W[ (t) ] ) #endif /* Calculate access to W array on the fly for entries 16 .. 79 */ #if !defined( COMPACT ) #define Wf( t ) \ ( Wc( t ) = S( 1, Wc( t ) ^ Wc( t - 14 ) ^ Wc( t - 8 ) ^ Wc( t - 3 ) ) ) #else #define Wf( t ) \ ( Wc( t ) = S( 1, Wc( t - 16 ) ^ Wc( t - 14 ) ^ Wc( t - 8 ) ^ Wc( t - 3 ) ) ) #endif /* Calculate access to W virtual array calculating access to W on the fly */ #if !defined( COMPACT ) #define Wfly( t ) ( (t) < 16 ? Wc( (t) ) : Wf( (t) ) ) #else #define Wfly( t ) ( Wc( (t) ) ) #endif #if defined( VERBOSE ) #define REPORT( t, A, B, C, D, E ) \ fprintf( stderr, "t = %2d: %08X %08X %08X %08X %08X\n",\ t, A, B, C, D, E ); #else #define REPORT( t, A, B, C, D, E ) #endif #define ROUND( t, A, B, C, D, E, Func, K ) \ E += S( 5, A ) + Func( B, C, D ) + Wfly( t ) + K;\ B = S( 30, B ); REPORT( t, E, A, B, C, D ) /* Remove rotatation E' = D; D' = C; C' = B; B' = A; A' = E; by completely unrolling and rotating the arguments to the macro ROUND manually so the rotation is compiled in. */ #define ROUND5( t, Func, K ) \ ROUND( t + 0, A, B, C, D, E, Func, K );\ ROUND( t + 1, E, A, B, C, D, Func, K );\ ROUND( t + 2, D, E, A, B, C, Func, K );\ ROUND( t + 3, C, D, E, A, B, Func, K );\ ROUND( t + 4, B, C, D, E, A, Func, K ) #define ROUND20( t, Func, K )\ ROUND5( t + 0, Func, K );\ ROUND5( t + 5, Func, K );\ ROUND5( t + 10, Func, K );\ ROUND5( t + 15, Func, K ) /********************* use the macros *********************/ #if defined( VERBOSE ) && !defined( COMPACT ) for ( t = 0; t < 16; t++ ) { fprintf( stderr, "W[%2d] = %08x\n", t, W[ t ] ); } fprintf( stderr, " A B C D E\n\n" ); #endif #if defined( COMPACT ) /* initialise W buffer */ for ( t = 16; t < 80; t++ ) { Wf( t ); } #endif /* rounds 0..19 */ ROUND20( 0, F1, K1 ); /* rounds 21..39 */ ROUND20( 20, F2, K2 ); /* rounds 40..59 */ ROUND20( 40, F3, K3 ); /* rounds 60..79 */ ROUND20( 60, F4, K4 ); H[ 0 ] += A; H[ 1 ] += B; H[ 2 ] += C; H[ 3 ] += D; H[ 4 ] += E; } void SHA1_Update( SHA1_ctx* ctx, const void* pdata, size_t data_len ) { const byte* data = (const byte*)pdata; unsigned use = 0 ; unsigned mlen = 0 ; #if !defined( word64 ) word32 low_bits = 0 ; #endif /* convert data_len to bits and add to the 64-bit bit count */ #if defined( word64 ) mlen = (unsigned)( ( ctx->bits >> 3 ) % SHA1_INPUT_BYTES ); ctx->bits += ( (word64) data_len ) << 3; #else mlen = (unsigned)( ( ctx->lbits >> 3 ) % SHA1_INPUT_BYTES ); ctx->hbits += data_len >> 29; /* simulate 64 bit addition */ low_bits = data_len << 3; ctx->lbits += low_bits; if ( ctx->lbits < low_bits ) { ctx->hbits++; } #endif /* deal with first block */ use = (unsigned)min( (size_t)(SHA1_INPUT_BYTES - mlen), data_len ); memcpy( ctx->M + mlen, data, use ); mlen += use; data_len -= use; data += use; while ( mlen == SHA1_INPUT_BYTES ) { make_big_endian32( (word32*)ctx->M, SHA1_INPUT_WORDS ); SHA1_Transform( ctx->H, ctx->M ); use = (unsigned)min( SHA1_INPUT_BYTES, data_len ); memcpy( ctx->M, data, use ); mlen = use; data_len -= use; data += use; } } void SHA1_Final( SHA1_ctx* ctx, byte digest[ SHA1_DIGEST_BYTES ] ) { unsigned mlen = 0 ; unsigned padding = 0 ; #if defined( word64 ) word64 temp = 0 ; #endif #if defined( word64 ) mlen = (unsigned)(( ctx->bits >> 3 ) % SHA1_INPUT_BYTES); #else mlen = (unsigned)(( ctx->lbits >> 3 ) % SHA1_INPUT_BYTES); #endif ctx->M[ mlen ] = 0x80; mlen++; /* append a 1 bit */ padding = SHA1_INPUT_BYTES - mlen; #define BIT_COUNT_WORDS 2 #define BIT_COUNT_BYTES ( BIT_COUNT_WORDS * sizeof( word32 ) ) if ( (unsigned)padding >= BIT_COUNT_BYTES ) { memset( ctx->M + mlen, 0x00, padding - BIT_COUNT_BYTES ); make_big_endian32( ctx->M, SHA1_INPUT_WORDS - BIT_COUNT_WORDS ); } else { memset( ctx->M + mlen, 0x00, SHA1_INPUT_BYTES - mlen ); make_big_endian32( ctx->M, SHA1_INPUT_WORDS ); SHA1_Transform( ctx->H, ctx->M ); memset( ctx->M, 0x00, SHA1_INPUT_BYTES - BIT_COUNT_BYTES ); } #if defined( word64 ) if ( little_endian ) { temp = ( ctx->bits << 32 | ctx->bits >> 32 ); } else { temp = ctx->bits; } memcpy( ctx->M + SHA1_INPUT_BYTES - BIT_COUNT_BYTES, &temp, BIT_COUNT_BYTES ); #else memcpy( ctx->M + SHA1_INPUT_BYTES - BIT_COUNT_BYTES, &(ctx->hbits), BIT_COUNT_BYTES ); #endif SHA1_Transform( ctx->H, ctx->M ); memcpy( digest, ctx->H, SHA1_DIGEST_BYTES ); make_big_endian32( digest, SHA1_DIGEST_WORDS ); } #endif static int swap_endian32( void* data, size_t len ) { word32 tmp32 = 0 ; byte* tmp32_as_bytes = (byte*) &tmp32; word32* data_as_word32s = (word32*) data; byte* data_as_bytes = NULL ; size_t i = 0 ; for ( i = 0; i < len; i++ ) { tmp32 = data_as_word32s[ i ]; data_as_bytes = (byte*) &( data_as_word32s[ i ] ); data_as_bytes[ 0 ] = tmp32_as_bytes[ 3 ]; data_as_bytes[ 1 ] = tmp32_as_bytes[ 2 ]; data_as_bytes[ 2 ] = tmp32_as_bytes[ 1 ]; data_as_bytes[ 3 ] = tmp32_as_bytes[ 0 ]; } return 1; } hashcash-1.21/libhc.c0000664000076400007640000004031610351101612013036 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #include #include #include #include #if defined( REGEXP_BSD ) #define _REGEX_RE_COMP #include #elif defined( REGEXP_POSIX ) #include #include #else /* no regular expression support */ #endif #define BUILD_DLL #include "hashcash.h" #include "utct.h" #include "libfastmint.h" #include "sha1.h" #include "random.h" #include "sstring.h" time_t round_off( time_t now_time, int digits ); #if DEBUG /* smaller base for debugging */ #define GROUP_SIZE 255 #define GROUP_DIGITS 2 #define GFFORMAT "%x" #define GFORMAT "%02x" #else #define GROUP_SIZE 0xFFFFFFFFU #define GROUP_DIGITS 8 #define GFFORMAT "%x" #define GFORMAT "%08x" #endif long per_sec = 0; /* cache calculation */ char *strrstr(char *s1,char *s2) { char *sc2 = NULL , *psc1 = NULL , *ps1 = NULL ; if ( *s2 == '\0' ) { return s1; } ps1 = s1 + strlen(s1); while( ps1 != s1 ) { --ps1; for ( psc1 = ps1, sc2 = s2; ; ) { if (*(psc1++) != *(sc2++)) { break; } else if ( *sc2 == '\0' ) { return ps1; } } } return NULL; } int wild_match( char* pat, char* str ) { int num = 1, last = 0, first = 1; char* term = NULL , *ptr = pat, *pos = str; do { term = ptr; ptr = strchr( ptr, '*' ); if ( ptr ) { *ptr = '\0'; ptr++; } else { last = 1; } if ( *term != '\0' ) { if ( first ) { /* begin */ if ( strncmp( pos, term, strlen( term ) ) != 0 ) { return 0; } pos += strlen( term ); } else if ( !first ) { /* middle */ if ( last ) { pos = strrstr( pos, term ); } else { pos = strstr( pos, term ); } if ( pos == 0 ) { return 0; } pos += strlen( term ); } if ( last && *pos != '\0' ) { return 0; } } num++; first = 0; } while ( term && !last ); return 1; } int email_match( const char* email, const char* pattern ) { int len = 0 , ret = 0; char *pat_user = NULL, *pat_dom = NULL; char *em_user = NULL, *em_dom = NULL; char *pat_sub = NULL , *em_sub = NULL , *pat_next = NULL , *em_next = NULL , *state = NULL ; sstrtok( pattern, "@", &pat_user, 0, &len, &state ); sstrtok( NULL, "@", &pat_dom, 0, &len, &state ); sstrtok( email, "@", &em_user, 0, &len, &state ); sstrtok( NULL, "@", &em_dom, 0, &len, &state ); /* if @ in pattern, must have @ sign in email too */ if ( pat_dom && em_dom == NULL ) { goto done; } if ( !wild_match( pat_user, em_user ) ) { goto done; } if ( !pat_dom && !em_dom ) { ret = 1; goto done; } /* no @ in either, ok */ pat_next = pat_dom; em_next = em_dom; do { pat_sub = pat_next; em_sub = em_next; pat_next = strchr( pat_next, '.' ); if ( pat_next ) { *pat_next = '\0'; pat_next++; } em_next = strchr( em_next, '.' ); if ( em_next ) { *em_next = '\0'; em_next++; } if ( !wild_match( pat_sub, em_sub ) ) { goto done; } } while ( pat_next && em_next ); /* different numbers of subdomains, fail */ if ( ( pat_next == NULL && em_next != NULL ) || ( pat_next != NULL && em_next == NULL ) ) { goto done; } ret = 1; done: if ( pat_user ) { free( pat_user ); } if ( pat_dom ) { free( pat_dom ); } if ( em_user ) { free( em_user ); } if ( em_dom ) { free( em_dom ); } return ret; } const char* hashcash_version( void ) { return HASHCASH_VERSION_STRING; } char* hashcash_simple_mint( const char* resource, unsigned bits, long anon_period, char* ext, int compress ) { time_t now_time = time( 0 ); char* stamp = NULL; int ret = hashcash_mint( now_time, 6, resource, bits, anon_period, &stamp, NULL, NULL, ext, compress, NULL, NULL ); if ( ret != HASHCASH_OK ) { return NULL; } return stamp; } int hashcash_mint( time_t now_time, int time_width, const char* resource, unsigned bits, long anon_period, char** new_token, long* anon_random, double* tries_taken, char* ext, int compress, hashcash_callback cb, void* user_arg ) { long rnd = 0 ; char now_utime[ MAX_UTC+1 ] = {0}; /* current time */ char* token = 0; double taken; if ( resource == NULL ) { return HASHCASH_INTERNAL_ERROR; } if ( anon_random == NULL ) { anon_random = &rnd; } *anon_random = 0; if ( bits > SHA1_DIGEST_BYTES * 8 ) { return HASHCASH_INVALID_TOK_LEN; } if ( time_width == 0 ) { time_width = 6; } /* default YYMMDD */ if ( now_time < 0 ) { return HASHCASH_INVALID_TIME; } if ( anon_period != 0 ) { if ( !random_rectangular( (long)anon_period, anon_random ) ) { return HASHCASH_RNG_FAILED; } } now_time += *anon_random; if ( time_width != 12 && time_width != 10 && time_width != 6 ) { return HASHCASH_INVALID_TIME_WIDTH; } now_time = round_off( now_time, 12-time_width ); hashcash_to_utctimestr( now_utime, time_width, now_time ); if ( !ext ) { ext = ""; } token = malloc( MAX_TOK+strlen(ext)+1 ); if ( token == NULL ) { return HASHCASH_OUT_OF_MEMORY; } sprintf( token, "%d:%d:%s:%s:%s:", HASHCASH_FORMAT_VERSION, bits, now_utime, resource, ext ); taken = hashcash_fastmint( bits,token,compress,new_token,cb,user_arg ); if ( taken < 0 ) { free( token ); return HASHCASH_USER_ABORT; } free( token ); if ( tries_taken ) { *tries_taken = taken; } return HASHCASH_OK; } #define X_HASHCASH "X-Hashcash" #define CONT '\t' #define LF "\r\n" char* hashcash_make_header( const char* stamp, int line_len, const char* header, char cont, const char* lf ) { int stamp_len = strlen( stamp ), i, fstep, step, tstep, lf_len; int stamp_left = stamp_len; int lines, header_len, cont_len, max_res; char* res, *resp; char conts[2]; if ( header == NULL ) { header = X_HASHCASH; } if ( cont == '\0' ) { cont = CONT; } if ( lf == NULL ) { lf = LF; } header_len = strlen( header ); cont_len = ( cont == CONT ) ? 8 : 1; conts[0] = cont; conts[1] = '\0'; lines = ((stamp_len+header_len+2) / (line_len-cont_len))+1; lf_len = strlen(lf); max_res = lines*(line_len+lf_len+1); res = malloc( max_res+1 ); res[0]='\0'; resp = res; strncat( resp, header, max_res ); resp += header_len; fstep = line_len - header_len; step = line_len - cont_len; for ( i = 0; i < stamp_len; stamp += tstep, i += tstep ) { tstep = i ? step : fstep; if ( tstep > stamp_left ) { tstep = stamp_left; } if ( i ) { strncat( resp, conts, 1 ); resp++; } strncat( resp, stamp, tstep ); resp += tstep; strncat( resp, lf, lf_len ); resp += lf_len; stamp_left -= tstep; } return res; } time_t round_off( time_t now_time, int digits ) { struct tm* now = NULL ; if ( digits != 2 && digits != 4 && digits != 6 && digits != 8 && digits != 10 ) { return now_time; } now = gmtime( &now_time ); /* still in UTC */ switch ( digits ) { case 10: now->tm_mon = 0; case 8: now->tm_mday = 1; case 6: now->tm_hour = 0; case 4: now->tm_min = 0; case 2: now->tm_sec = 0; } return mk_utctime( now ); } int hashcash_validity_to_width( long validity_period ) { int time_width = 6; /* default YYMMDD */ if ( validity_period < 0 ) { return 0; } if ( validity_period != 0 ) { /* YYMMDDhhmmss or YYMMDDhhmm or YYMMDD */ if ( validity_period < 2*TIME_MINUTE ) { time_width = 12; } else if ( validity_period < 2*TIME_HOUR ) { time_width = 10; } else { time_width = 6; } } return time_width; } /* all chars from ascii(33) to ascii(126) inclusive, minus : */ /* limited v0 backwards compatibility: impose this valid random string * alphabet limitation on v0 also retroactively even tho the internet * draft did not require it. All widely used clients generate hex / * base64 anyway. */ #define VALID_STR_CHARS "/+0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"\ "abcdefghijklmnopqrstuvwxyz=" int hashcash_parse( const char* token, int* vers, int* bits, char* utct, int utct_max, char* token_resource, int res_max, char** ext, int ext_max ) { char ver_arr[MAX_VER+1] = {0}; char bits_arr[3+1] = {0}; char *bits_str = bits_arr, *ver = ver_arr; char *rnd = NULL, *cnt = NULL; char *state = NULL ; int ver_len = 0 , utct_len = 0 , res_len = 0 , bit_len = 0 , rnd_len = 0 , cnt_len = 0 ; /* parse out the resource name component * v1 format: ver:bits:utctime:resource:ext:rand:counter * where utctime is [YYMMDD[hhmm[ss]]] * * v0 format: ver:utctime:resource:rand * * in v0 & v1 the resource may NOT include :s, if it needs to include * :s some encoding such as URL encoding must be used */ if ( ext != NULL ) { *ext = NULL; } if ( !sstrtok( token, ":", &ver, MAX_VER, &ver_len, &state ) ) { return 0; } *vers = atoi( ver ); if ( *vers < 0 ) { return 0; } if ( *vers == 0 ) { *bits = -1; if ( !sstrtok( NULL, ":", &utct, utct_max, &utct_len, &state ) || !sstrtok( NULL, ":", &token_resource, res_max,&res_len,&state ) || !sstrtok( NULL, ":", &rnd, 0, &rnd_len, &state ) ) { return 0; } } else if ( *vers == 1 ) { if ( !sstrtok( NULL, ":", &bits_str, 3, &bit_len, &state ) || !sstrtok( NULL, ":", &utct, utct_max, &utct_len, &state ) || !sstrtok( NULL, ":", &token_resource, res_max,&res_len,&state ) || !sstrtok( NULL, ":", ext, 0, &ext_max, &state ) || !sstrtok( NULL, ":", &rnd, 0, &rnd_len, &state ) || !sstrtok( NULL, ":", &cnt, 0, &cnt_len, &state ) ) { return 0; } *bits = atoi( bits_str ); if ( *bits < 0 ) { return 0; } if ( strspn( cnt, VALID_STR_CHARS ) != cnt_len ) { return 0; } } if ( rnd == NULL || strspn( rnd, VALID_STR_CHARS ) != rnd_len ) { return 0; } return 1; } unsigned hashcash_count( const char* token ) { SHA1_ctx ctx; byte target_digest[ SHA1_DIGEST_BYTES ] = {0}; byte token_digest[ SHA1_DIGEST_BYTES ] = {0}; char ver[MAX_VER+1] = {0}; int vers = 0 ; char* first_colon = NULL; char* second_colon = NULL; int ver_len = 0 ; int i = 0 ; int last = 0 ; int collision_bits = 0 ; first_colon = strchr( token, ':' ); if ( first_colon == NULL ) { return 0; } /* should really fail */ ver_len = (int)(first_colon - token); if ( ver_len > MAX_VER ) { return 0; } sstrncpy( ver, token, ver_len ); vers = atoi( ver ); if ( vers < 0 ) { return 0; } if ( vers > 1 ) { return 0; } /* unsupported version number */ second_colon = strchr( first_colon+1, ':' ); if ( second_colon == NULL ) { return 0; } /* should really fail */ memset( target_digest, 0, SHA1_DIGEST_BYTES ); SHA1_Init( &ctx ); SHA1_Update( &ctx, token, strlen( token ) ); SHA1_Final( &ctx, token_digest ); for ( i = 0; i < SHA1_DIGEST_BYTES && token_digest[ i ] == target_digest[ i ]; i++ ) { } last = i; collision_bits = 8 * i; #define bit( n, c ) (((c) >> (7 - (n))) & 1) for ( i = 0; i < 8; i++ ) { if ( bit( i, token_digest[ last ] ) == bit( i, target_digest[ last ] ) ) { collision_bits++; } else { break; } } return collision_bits; } long hashcash_valid_for( time_t token_time, long validity_period, long grace_period, time_t now_time ) { long expiry_time = 0 ; /* for ever -- return infinity */ if ( validity_period == 0 ) { return HASHCASH_VALID_FOREVER; } /* future date in token */ if ( token_time > now_time + grace_period ) { return HASHCASH_VALID_IN_FUTURE; } expiry_time = token_time + validity_period; if ( expiry_time + grace_period > now_time ) { /* valid return seconds left */ return expiry_time + grace_period - now_time; } return HASHCASH_EXPIRED; /* otherwise expired */ } #define REGEXP_DIFF "(|)" #define REGEXP_UNSUP "{}" #define REGEXP_SAME "\\.?[]*+^$" #define MAX_RE_ERR 256 int regexp_match( const char* str, const char* regexp, void** compile, char** err ) { #if defined( REGEXP_BSD ) char* q = NULL ; const char *r = NULL ; char* quoted_regexp = malloc( strlen( regexp ) * 2 + 3 ); *err = NULL; if ( quoted_regexp == NULL ) { *err = "out of memory"; return 0; } q = quoted_regexp; r = regexp; if ( *r != '^' ) { *q++ = '^'; } for ( ; *r; *q++ = *r++ ) { if ( *r == '\\' ) { if ( strchr( REGEXP_SAME, *(r+1) ) ) { *q++ = *r++; /* copy thru \\ unchanged */ } else { r++; /* skip \c for any c other than \ */ } } else if ( strchr( REGEXP_DIFF, *r ) ) { *q++ = '\\'; } else if ( strchr( REGEXP_UNSUP, *r ) ) { *err = "compiled with BSD regexp, {} not suppored"; free( quoted_regexp ); return 0; } } if ( *(q-1) != '$' ) { *q++ = '$'; } *q = '\0'; if ( ( *err = re_comp( quoted_regexp ) ) != NULL ) { free( quoted_regexp ); return 0; } free( quoted_regexp ); return re_exec( str ); #elif defined( REGEXP_POSIX ) regex_t** comp = (regex_t**) compile; int re_code = 0 ; char* bound_regexp = NULL ; int re_len = 0 , bre_len = 0 ; static char re_err[ MAX_RE_ERR+1 ] = {0}; re_err[0] = '\0'; *err = NULL; if ( *comp == NULL ) { *comp = malloc( sizeof(regex_t) ); if ( *comp == NULL ) { *err = "out of memory"; return 0; } bre_len = re_len = strlen(regexp); if ( regexp[0] != '^' || regexp[re_len-1] != '$' ) { bound_regexp = malloc( re_len+3 ); if ( regexp[0] != '^' ) { bound_regexp[0] = '^'; sstrncpy( (bound_regexp+1), regexp, re_len ); bre_len++; } else { sstrncpy( bound_regexp, regexp, re_len ); } if ( regexp[re_len-1] != '$' ) { bound_regexp[bre_len] = '$'; bound_regexp[bre_len+1] = '\0'; } } else { bound_regexp = (char*)regexp; } if ( ( re_code = regcomp( *comp, bound_regexp, REG_EXTENDED | REG_NOSUB ) ) != 0 ) { regerror( re_code, *comp, re_err, MAX_RE_ERR ); *err = re_err; if ( bound_regexp != regexp ) { free( bound_regexp ); } return 0; } if ( bound_regexp != regexp ) { free( bound_regexp ); } } return regexec( *comp, str, 0, NULL, 0 ) == 0; #else *err = "regexps not supported on your platform, used -W wildcards"; return 0; #endif } int hashcash_resource_match( int type, const char* token_res, const char* res, void** compile, char** err ) { switch ( type ) { case TYPE_STR: if ( strcmp( token_res, res ) != 0 ) { return 0; } break; case TYPE_WILD: if ( !email_match( token_res, res ) ) { return 0; } break; case TYPE_REGEXP: if ( !regexp_match( token_res, res, compile, err ) ) { return 0; } break; default: return 0; } return 1; } int hashcash_check( const char* token, int case_flag, const char* resource, void **compile, char** re_err, int type, time_t now_time, long validity_period, long grace_period, int required_bits, time_t* token_time ) { time_t token_t = 0 ; char token_utime[ MAX_UTC+1 ] = {0}; char token_res[ MAX_RES+1 ] = {0}; int bits = 0, claimed_bits = 0, vers = 0; if ( token_time == NULL ) { token_time = &token_t; } if ( !hashcash_parse( token, &vers, &claimed_bits, token_utime, MAX_UTC, token_res, MAX_RES, NULL, 0 ) ) { return HASHCASH_INVALID; } if ( vers < 0 || vers > 1 ) { return HASHCASH_UNSUPPORTED_VERSION; } *token_time = hashcash_from_utctimestr( token_utime, 1 ); if ( *token_time == -1 ) { return HASHCASH_INVALID; } if ( !case_flag ) { stolower( token_res ); } if ( resource && !hashcash_resource_match( type, token_res, resource, compile, re_err ) ) { if ( *re_err != NULL ) { return HASHCASH_REGEXP_ERROR; } else { return HASHCASH_WRONG_RESOURCE; } } bits = hashcash_count( token ); if ( vers == 1 ) { bits = ( bits < claimed_bits ) ? 0 : claimed_bits; } if ( bits < required_bits ) { return HASHCASH_INSUFFICIENT_BITS; } return hashcash_valid_for( *token_time, validity_period, grace_period, now_time ); } double hashcash_estimate_time( int b ) { return hashcash_expected_tries( b ) / (double)hashcash_per_sec(); } double hashcash_expected_tries( int b ) { double expected_tests = 1; #define CHUNK ( sizeof( unsigned long )*8 - 1 ) for ( ; b > CHUNK; b -= CHUNK ) { expected_tests *= ((unsigned long)1) << CHUNK; } expected_tests *= ((unsigned long)1) << b; return expected_tests; } void hashcash_free( void* ptr ) { if ( ptr != NULL ) { free( ptr ); } } hashcash-1.21/LICENSE0000664000076400007640000000377210351101612012623 0ustar adamadamThis software can be distributed and used under any of the following licenses: CPL public domain BSD (3 clause -- no advertising clause) LGPL (2.1) GPL (2) in rough order of author preference. The CPL is probably unique to this package, and is described here: http://www.cypherspace.org/CPL/; if you can't be bothered to figure it out feel free to use one of the other licenses. Earlier versions where distributed just under CPL. CPL allows you to re-license under a license of your choice, I just include other specific licenses to save people having to read, understand and feel confident in the validity of the CPL. NOTE: on systems without a POSIX getopt this package will be linked with the included getopt.c which is distributed under GPL 2. The following are not licensing requirements, just comments from the author: - If you have to make changes to make this library work for your system or application it would be useful if you could tell me what you had to do, and optionally send me the source changes so I can include them or update the library. The aim is to make a practically useful library. - It would be useful if you could inform me if you use or distribute hashcash. The intent here is to give me feedback and insight into the areas of application which people find useful in practice. - If you are unclear on how to use hashcash there is a FAQ here: http://www.hashcash.org/faq.html. If that doesn't answer your question or doesn't apply to your usage, feel free to discuss in email. - It may help the deployment of hashcash as an anti-spam system if different systems based on hashcash were interoperable as far as that makes sense for your system. To this end the FAQ http://www.hashcash.org/faq.html and Internet-Draft http://www.hashcash.org/draft-hashcash.txt document my thoughts in this area. See also the hashcash home page http://www.hashcash.org and paper there as I update with links to deployed systems which it might be useful for you to interoperate with. hashcash-1.21/vmsbuild.com0000664000076400007640000000034310351101612014132 0ustar adamadam$! VAX VMS script to build HASHCASH.EXE and SHA1.EXE $! $! type @vmsbuild to run $! $ cc hashcash.c,libsha1.c,timer.c,sdb.c,utct.c,random.c,sha1.c,libhc.c $ link hashcash,libsha1,timer,sdb,utct,random,libhc $ link sha1,libsha1 hashcash-1.21/utct.h0000664000076400007640000000213110351101612012732 0ustar adamadam/* -*- Mode: C; c-file-style: "stroustrup" -*- */ #if !defined( _utct_h ) #define _utct_h #include #if defined( __cplusplus ) extern "C" { #endif #if !defined(HCEXPORT) #if !defined(WIN32) || defined(MONOLITHIC) #define HCEXPORT #elif defined(BUILD_DLL) #define HCEXPORT __declspec(dllexport) #else /* USE_DLL */ #define HCEXPORT extern __declspec(dllimport) #endif #endif const char* strtime( time_t* timep, int utc ); /* ctime like with utc arg */ time_t mk_utctime( struct tm* tms ); /* mktime like with utc struct tm */ /* functions to parse and create UTCTime string which is: * YYMMDDhhmm[ss]Z */ /* when creating utc string, if requested len is odd Z is appended, * if even Z is omitted * * when parsing Z or missing Z is tolerated * * parsing has an extension to parse like: * * YYMMDD[hh[mm[ss]]][Z] */ #define MAX_UTC 13 HCEXPORT time_t hashcash_from_utctimestr( const char utct[MAX_UTC+1], int utc ); HCEXPORT int hashcash_to_utctimestr( char utct[MAX_UTC+1], int len, time_t t ); #if defined( __cplusplus ) } #endif #endif hashcash-1.21/Makefile0000664000076400007640000001504310411051064013252 0ustar adamadamHC_VERS = 1.21 CHANGEME1 = 1 # put PACKAGER = EBUILD|RPM etc here CHANGEME2 = 2 CHANGEME3 = 3 INSTALL_PATH = /usr/bin MAN_INSTALL_PATH = /usr/share/man/man1 DOC_INSTALL_PATH = /usr/share/doc/hashcash-$(HC_VERS) MAKEDEPEND = makedepend MSLIB = mslib # here you can choose the regexp style your system has # default is POSIX # REGEXP = -DREGEXP_POSIX # if no POSIX regexp support, try BSD # REGEXP = -DREGEXP_BSD # if no POSIX or BSD, disable, still have builtin basic wildcard support # REGEXP = REGEXP=-DREGEXP_POSIX COPT_DEBUG = -g COPT_GENERIC = -O3 COPT_GNU = -O3 -funroll-loops COPT_X86 = -O3 -funroll-loops -march=pentium-mmx -mmmx \ -D_REENTRANT -D_THREAD_SAFE -fPIC COPT_MINGW = -O3 -funroll-loops -march=pentium-mmx -mmmx \ -D_REENTRANT -D_THREAD_SAFE COPT_G3_OSX = -O3 -funroll-loops -fno-inline -mcpu=750 -faltivec COPT_PPC_LINUX = -O3 -funroll-loops -fno-inline -mcpu=604e -maltivec \ -mabi=altivec LIB=.a # request static link of -lcrypto only LIBCRYPTO=/usr/lib/libcrypto.a EXES = hashcash$(EXE) sha1$(EXE) sha1test$(EXE) INSTALL = install POD2MAN = pod2man POD2HTML = pod2html POD2TEXT = pod2text DELETE = rm -f ETAGS = etags FASTLIBS = libfastmint.o fastmint_mmx_standard_1.o fastmint_mmx_compact_1.o \ fastmint_ansi_compact_1.o fastmint_ansi_standard_1.o \ fastmint_ansi_compact_2.o fastmint_ansi_standard_2.o \ fastmint_altivec_standard_1.o fastmint_altivec_standard_2.o \ fastmint_altivec_compact_2.o fastmint_ansi_ultracompact_1.o \ fastmint_library.o OBJS = libsha1.o libhc.o sdb.o lock.o utct.o random.o sstring.o \ getopt.o $(FASTLIBS) LIBOBJS = libhc.o libsha1.o utct.o sdb.o lock.o sstring.o random.o $(FASTLIBS) EXEOBJS = hashcash.o DIST = ../dist.csh default: help generic help: @echo "make where platform is:" @echo " x86, mingw, mingw-dll, g3-osx, ppc-linux, gnu, generic, debug" @echo "or to link with openSSL for SHA1 rather than builtin:" @echo " x86-openssl, g3-osx-openssl, ppc-linux-openssl, " @echo " gnu-openssl, generic-openssl, debug-openssl" @echo "other make targets are docs, install, clean, distclean, docclean" @echo "" @echo "(doing make generic by default)" @echo "" generic: $(MAKE) "CFLAGS=$(CFLAGS) $(REGEXP) $(COPT_GENERIC) $(COPT)" build debug: $(MAKE) "CFLAGS=$(CFLAGS) $(REGEXP) $(COPT_DEBUG) $(COPT)" build gnu: $(MAKE) "CFLAGS=$(CFLAGS) $(REGEXP) $(COPT_GNU) $(COPT)" "CC=gcc" build x86: $(MAKE) "CFLAGS=$(CFLAGS) $(REGEXP) $(COPT_X86) $(COPT)" build g3-osx: $(MAKE) "CFLAGS=$(CFLAGS) $(REGEXP) $(COPT_G3_OSX) $(COPT)" build ppc-linux: $(MAKE) "CFLAGS=$(CFLAGS) $(REGEXP) $(COPT_PPC_LINUX) $(COPT)" build # mingw windows targets (cross compiler, or native) mingw: $(MAKE) "LIB=.lib" "CC=gcc" "EXE=.exe" "CFLAGS=$(COPT_MINGW) -DMONOLITHIC $(COPT)" build mingw-dll: $(MAKE) "CC=gcc" "EXE=.exe" "CFLAGS=$(COPT_MINGW) $(COPT)" build-dll # openSSL versions of targets x86-openssl: $(MAKE) x86 "CFLAGS=$(CFLAGS) -DOPENSSL" "LDFLAGS=$(LDFLAGS) $(LIBCRYPTO)" g3-osx-openssl: $(MAKE) g3-osx "CFLAGS=$(CFLAGS) -DOPENSSL" "LDFLAGS=$(LDFLAGS) $(LIBCRYPTO)" ppc-linux-openssl: $(MAKE) ppc-linux "CFLAGS=$(CFLAGS) -DOPENSSL" "LDFLAGS=$(LDFLAGS) $(LIBCRYPTO)" gnu-openssl: $(MAKE) gnu "CFLAGS=$(CFLAGS) -DOPENSSL" "LDFLAGS=$(LDFLAGS) $(LIBCRYPTO)" generic-openssl: $(MAKE) generic "CFLAGS=$(CFLAGS) -DOPENSSL" "LDFLAGS=$(LDFLAGS) $(LIBCRYPTO)" debug-openssl: $(MAKE) debug "CFLAGS=$(CFLAGS) -DOPENSSL" "LDFLAGS=$(LDFLAGS) $(LIBCRYPTO)" build: hashcash$(EXE) sha1$(EXE) build-dll: hashcash-dll$(EXE) sha1$(EXE) hashcash$(EXE): hashcash.o getopt.o libhashcash$(LIB) $(CC) hashcash.o getopt.o libhashcash$(LIB) -o $@ $(LDFLAGS) sha1$(EXE): sha1.o libsha1.o $(CC) sha1.o libsha1.o -o $@ $(LDFLAGS) example$(EXE): example.o getopt.o libhashcash$(LIB) $(CC) example.o getopt.o libhashcash$(LIB) -o $@ $(LDFLAGS) hashcash-dll$(EXE): $(EXEOBJS) hashcash.dll $(CC) $(EXEOBJS) hashcash.dll -o $@ $(LDFLAGS) sha1test$(EXE): sha1test.o libsha1.o $(CC) sha1test.o libsha1.o -o $@ $(LDFLAGS) all: $(EXES) libhashcash$(LIB): $(LIBOBJS) $(DELETE) $@ $(AR) rcs $@ $(LIBOBJS) hashcash.dll: $(LIBOBJS) $(CC) -shared -o hashcash.dll $(LIBOBJS) \ -Wl,--output-def,hashcash.def,--out-implib,libhashcash.a $(MSLIB) /machine:x86 /def:hashcash.def docs: hashcash.1 hashcash.html hashcash.txt sha1-hashcash.1 \ sha1-hashcash.html sha1-hashcash.txt hashcash.1: hashcash.pod $(POD2MAN) -s 1 -c hashcash -r $(HC_VERS) $? > $@ hashcash.html: hashcash.pod $(POD2HTML) --title hashcash $? > $@ $(DELETE) pod2htm* hashcash.txt: hashcash.pod $(POD2TEXT) $? > $@ sha1-hashcash.1: sha1-hashcash.pod $(POD2MAN) -s 1 -c sha1 -r $(HC_VERS) $? > $@ sha1-hashcash.html: sha1-hashcash.pod $(POD2HTML) --title sha1 $? > $@ $(DELETE) pod2htm* sha1-hashcash.txt: sha1-hashcash.pod $(POD2TEXT) $? > $@ install: hashcash sha1 hashcash.1 sha1-hashcash.1 $(INSTALL) -d $(INSTALL_PATH) $(INSTALL) hashcash sha1 $(INSTALL_PATH) $(INSTALL) -d $(MAN_INSTALL_PATH) $(INSTALL) -m 644 hashcash.1 sha1-hashcash.1 $(MAN_INSTALL_PATH) $(INSTALL) -d $(DOC_INSTALL_PATH) $(INSTALL) -m 644 README LICENSE CHANGELOG $(DOC_INSTALL_PATH) depend: $(MAKEDEPEND) -- -Y *.c *.h docclean: $(DELETE) hashcash.txt hashcash.1 hashcash.html pod2htm* $(DELETE) sha1-hashcash.txt sha1-hashcash.1 sha1-hashcash.html clean: $(DELETE) *.o *~ distclean: $(DELETE) *.o *~ $(EXES) hashcash-dll.* *.db *.bak TAGS core* $(DELETE) *.bak test/* *.dll *.lib *.exe *.a *.sdb tags: $(ETAGS) *.c *.h dist: $(DIST) # DO NOT DELETE example.o: sstring.h sdb.h hashcash.h getopt.h fastmint_altivec_compact_2.o: libfastmint.h hashcash.h fastmint_altivec_standard_1.o: libfastmint.h hashcash.h fastmint_altivec_standard_2.o: libfastmint.h hashcash.h fastmint_ansi_compact_1.o: libfastmint.h hashcash.h fastmint_ansi_compact_2.o: libfastmint.h hashcash.h fastmint_ansi_standard_1.o: libfastmint.h hashcash.h fastmint_ansi_standard_2.o: libfastmint.h hashcash.h fastmint_ansi_ultracompact_1.o: libfastmint.h hashcash.h fastmint_library.o: sha1.h types.h libfastmint.h hashcash.h fastmint_mmx_compact_1.o: libfastmint.h hashcash.h fastmint_mmx_standard_1.o: libfastmint.h hashcash.h getopt.o: getopt.h hashcash.o: sdb.h utct.h random.h hashcash.h libfastmint.h sstring.h getopt.h hashcash.o: sha1.h types.h libfastmint.o: random.h sha1.h types.h libfastmint.h hashcash.h libhc.o: hashcash.h utct.h libfastmint.h sha1.h types.h random.h sstring.h libsha1.o: sha1.h types.h lock.o: lock.h random.o: random.h sha1.h types.h sdb.o: types.h lock.h sdb.h utct.h sha1.o: sha1.h types.h sha1test.o: sha1.h types.h sstring.o: sstring.h utct.o: sstring.h utct.h libfastmint.o: hashcash.h sha1.o: types.h hashcash-1.21/fastmint_altivec_standard_1.c0000664000076400007640000003476410351101612017423 0ustar adamadam#include "libfastmint.h" #if defined(__POWERPC__) && defined(__ALTIVEC__) #if !defined(__GNUC__) || !defined(__MACH__) #include #endif #endif int minter_altivec_standard_1_test(void) { /* This minter runs only on PowerPC G4 and higher hardware */ #if defined(__POWERPC__) && defined(__ALTIVEC__) return (gProcessorSupportFlags & HC_CPU_SUPPORTS_ALTIVEC) != 0; #endif /* Not a PowerPC, or compiler doesn't support Altivec */ return 0; } /* Define low-level primitives in terms of operations */ /* #define S(n, X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) */ #define S(n,X) ( vec_rl( X, (vector unsigned int) (n) ) ) #define XOR(a,b) ( vec_xor(a,b) ) #define AND(a,b) ( vec_and(a,b) ) #define ANDNOT(a,b) ( vec_andc(a,b) ) #define OR(a,b) ( vec_or(a,b) ) #define ADD(a,b) ( vec_add(a,b) ) /* #define F1( B, C, D ) ( ( (B) & (C) ) | ( ~(B) & (D) ) ) */ /* #define F1( B, C, D ) ( (D) ^ ( (B) & ( (C) ^ (D) ) ) ) */ #define F1( B, C, D ) ( \ F = AND(B,C), \ G = ANDNOT(D,B), \ OR(F,G) ) /* #define F2( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F2( B, C, D ) ( \ F = XOR(B,C), \ XOR(F,D) ) /* #define F3( B, C, D ) ( (B) & (C) ) | ( (C) & (D) ) | ( (B) & (D) ) */ /* #define F3( B, C, D ) ( ( (B) & ( (C) | (D) )) | ( (C) & (D) ) ) */ #define F3( B, C, D ) ( \ F = OR(C,D), \ G = AND(C,D), \ F = AND(B,F), \ OR(F,G) ) /* #define F4( B, C, D ) ( (B) ^ (C) ^ (D) ) */ #define F4(B,C,D) F2(B,C,D) #define K1 0x5A827999 /* constant used for rounds 0..19 */ #define K2 0x6ED9EBA1 /* constant used for rounds 20..39 */ #define K3 0x8F1BBCDC /* constant used for rounds 40..59 */ #define K4 0xCA62C1D6 /* constant used for rounds 60..79 */ /* #define Wf(t) (W[t] = S(1, W[t-16] ^ W[t-14] ^ W[t-8] ^ W[t-3])) */ #define Wf(W,t) ( \ F = XOR((W)[t-16], (W)[t-14]), \ G = XOR((W)[t-8], (W)[t-3]), \ F = XOR(F,G), \ (W)[t] = S(1,F) ) #define Wfly(W,t,u) ( (!(u) || (t) < 16) ? (W)[t] : Wf(W,t) ) #define ROUND(u,t,A,B,C,D,E,Func,K) \ E = ADD(E,K); \ F = S(5,A); \ E = ADD(F,E); \ F = Wfly(W,t,u); \ E = ADD(F,E); \ F = Func(B,C,D); \ E = ADD(F,E); \ B = S(30,B); #define ROUNDu(t,A,B,C,D,E,Func,K) ROUND(1,t,A,B,C,D,E,Func,K) #define ROUNDn(t,A,B,C,D,E,Func,K) ROUND(0,t,A,B,C,D,E,Func,K) #define ROUND5( t, Func, K ) \ ROUNDu( t + 0, A, B, C, D, E, Func, K );\ ROUNDu( t + 1, E, A, B, C, D, Func, K );\ ROUNDu( t + 2, D, E, A, B, C, Func, K );\ ROUNDu( t + 3, C, D, E, A, B, Func, K );\ ROUNDu( t + 4, B, C, D, E, A, Func, K ) #define ROUND20( t, Func, Kn )\ K = vec_splat(Ka, Kn-1);\ ROUND5( t + 0, Func, K );\ ROUND5( t + 5, Func, K );\ ROUND5( t + 10, Func, K );\ ROUND5( t + 15, Func, K ) unsigned long minter_altivec_standard_1(int bits, int* best, unsigned char *block, const uInt32 IV[5], int tailIndex, unsigned long maxIter, MINTER_CALLBACK_ARGS) { #if defined(__POWERPC__) && defined(__ALTIVEC__) MINTER_CALLBACK_VARS; unsigned long iters; int n, t, gotBits, maxBits = (bits > 16) ? 16 : bits; uInt32 bitMask1Low, bitMask1High, s; vector unsigned int vBitMaskHigh, vBitMaskLow; vector unsigned int A,B,C,D,E, F,G; vector unsigned int W[80]; vector unsigned int H[5], pH[5]; vector unsigned int Ka = (vector unsigned int) (K1, K2, K3, K4); uInt32 *Hw = (uInt32*) H; const char *p = encodeAlphabets[EncodeBase64]; unsigned char *X = (unsigned char*) W; unsigned char *output = (unsigned char*) block; *best = 0; /* Work out which bits to mask out for test */ if(maxBits < 32) { if ( bits == 0 ) { bitMask1Low = 0; } else { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); } bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } *((uInt32*) &vBitMaskLow ) = bitMask1Low ; vBitMaskLow = vec_splat(vBitMaskLow , 0); *((uInt32*) &vBitMaskHigh) = bitMask1High; vBitMaskHigh = vec_splat(vBitMaskHigh, 0); maxBits = 0; /* Copy block and IV to vectorised internal storage */ for(t=0; t < 16; t++) { X[t*16+ 0] = X[t*16+ 4] = X[t*16+ 8] = X[t*16+12] = output[t*4+0]; X[t*16+ 1] = X[t*16+ 5] = X[t*16+ 9] = X[t*16+13] = output[t*4+1]; X[t*16+ 2] = X[t*16+ 6] = X[t*16+10] = X[t*16+14] = output[t*4+2]; X[t*16+ 3] = X[t*16+ 7] = X[t*16+11] = X[t*16+15] = output[t*4+3]; } for(t=0; t < 5; t++) { Hw[t*4+0] = Hw[t*4+1] = Hw[t*4+2] = Hw[t*4+3] = IV[t]; pH[t] = H[t]; } /* The Tight Loop - everything in here should be extra efficient */ for(iters=0; iters < maxIter-4; iters += 4) { /* Encode iteration count into tail */ /* Iteration count is always 4-aligned, so only least-significant character needs multiple lookup */ /* Further, we assume we're always big-endian */ X[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 0] = p[(iters & 0x3c) + 0]; X[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 4] = p[(iters & 0x3c) + 1]; X[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 8] = p[(iters & 0x3c) + 2]; X[(((tailIndex - 1) & ~3) << 2) + ((tailIndex - 1) & 3) + 12] = p[(iters & 0x3c) + 3]; if(!(iters & 0x3f)) { if ( iters >> 6 ) { X[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 0] = X[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 4] = X[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 8] = X[(((tailIndex - 2) & ~3) << 2) + ((tailIndex - 2) & 3) + 12] = p[(iters >> 6) & 0x3f]; } if ( iters >> 12 ) { X[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 0] = X[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 4] = X[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 8] = X[(((tailIndex - 3) & ~3) << 2) + ((tailIndex - 3) & 3) + 12] = p[(iters >> 12) & 0x3f]; } if ( iters >> 18 ) { X[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 0] = X[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 4] = X[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 8] = X[(((tailIndex - 4) & ~3) << 2) + ((tailIndex - 4) & 3) + 12] = p[(iters >> 18) & 0x3f]; } if ( iters >> 24 ) { X[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 0] = X[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 4] = X[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 8] = X[(((tailIndex - 5) & ~3) << 2) + ((tailIndex - 5) & 3) + 12] = p[(iters >> 24) & 0x3f]; } if ( iters >> 30 ) { X[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 0] = X[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 4] = X[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 8] = X[(((tailIndex - 6) & ~3) << 2) + ((tailIndex - 6) & 3) + 12] = p[(iters >> 30) & 0x3f]; } } /* Bypass shortcuts below on certain iterations */ if((!(iters & 0xffffff)) && (tailIndex == 52 || tailIndex == 32)) { A = H[0]; B = H[1]; C = H[2]; D = H[3]; E = H[4]; for(t=16; t < 32; t++) Wf(W,t); ROUNDn( 0, A, B, C, D, E, F1, (vector unsigned int) (K1) ); ROUNDn( 1, E, A, B, C, D, F1, (vector unsigned int) (K1) ); ROUNDn( 2, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn( 3, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn( 4, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDn( 5, A, B, C, D, E, F1, (vector unsigned int) (K1) ); ROUNDn( 6, E, A, B, C, D, F1, (vector unsigned int) (K1) ); if(tailIndex == 52) { ROUNDn( 7, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn( 8, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn( 9, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDn(10, A, B, C, D, E, F1, (vector unsigned int) (K1) ); ROUNDn(11, E, A, B, C, D, F1, (vector unsigned int) (K1) ); } pH[0] = A; pH[1] = B; pH[2] = C; pH[3] = D; pH[4] = E; } /* Set up working variables */ A = pH[0]; B = pH[1]; C = pH[2]; D = pH[3]; E = pH[4]; /* Do the rounds */ switch(tailIndex) { default: ROUNDn( 0, A, B, C, D, E, F1, (vector unsigned int) (K1) ); ROUNDn( 1, E, A, B, C, D, F1, (vector unsigned int) (K1) ); ROUNDn( 2, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn( 3, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn( 4, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDn( 5, A, B, C, D, E, F1, (vector unsigned int) (K1) ); ROUNDn( 6, E, A, B, C, D, F1, (vector unsigned int) (K1) ); case 32: ROUNDn( 7, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn( 8, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn( 9, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDn(10, A, B, C, D, E, F1, (vector unsigned int) (K1) ); ROUNDn(11, E, A, B, C, D, F1, (vector unsigned int) (K1) ); case 52: ROUNDn(12, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn(13, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn(14, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDn(15, A, B, C, D, E, F1, (vector unsigned int) (K1) ); } if(tailIndex == 52) { ROUNDn(16, E, A, B, C, D, F1, (vector unsigned int) (K1) ); ROUNDn(17, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn(18, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn(19, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDu(20, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDn(21, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDn(22, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(23, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDn(24, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDn(25, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDu(26, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDn(27, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(28, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(29, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDn(30, A, B, C, D, E, F2, (vector unsigned int) (K2) ); } else if (tailIndex == 32) { ROUNDn(16, E, A, B, C, D, F1, (vector unsigned int) (K1) ); ROUNDn(17, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDn(18, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDn(19, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDn(20, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDu(21, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDn(22, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(23, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(24, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDn(25, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDu(26, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDu(27, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDn(28, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(29, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDu(30, A, B, C, D, E, F2, (vector unsigned int) (K2) ); } else { ROUNDu(16, E, A, B, C, D, F1, (vector unsigned int) (K1) ); ROUNDu(17, D, E, A, B, C, F1, (vector unsigned int) (K1) ); ROUNDu(18, C, D, E, A, B, F1, (vector unsigned int) (K1) ); ROUNDu(19, B, C, D, E, A, F1, (vector unsigned int) (K1) ); ROUNDu(20, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDu(21, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDu(22, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(23, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(24, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDu(25, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDu(26, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDu(27, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(28, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(29, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDu(30, A, B, C, D, E, F2, (vector unsigned int) (K2) ); } ROUNDu(31, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDu(32, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(33, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(34, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUNDu(35, A, B, C, D, E, F2, (vector unsigned int) (K2) ); ROUNDu(36, E, A, B, C, D, F2, (vector unsigned int) (K2) ); ROUNDu(37, D, E, A, B, C, F2, (vector unsigned int) (K2) ); ROUNDu(38, C, D, E, A, B, F2, (vector unsigned int) (K2) ); ROUNDu(39, B, C, D, E, A, F2, (vector unsigned int) (K2) ); ROUND5(40, F3, (vector unsigned int) (K3) ); ROUND5(45, F3, (vector unsigned int) (K3) ); ROUND5(50, F3, (vector unsigned int) (K3) ); ROUND5(55, F3, (vector unsigned int) (K3) ); ROUND5(60, F4, (vector unsigned int) (K4) ); ROUND5(65, F4, (vector unsigned int) (K4) ); ROUND5(70, F4, (vector unsigned int) (K4) ); ROUND5(75, F4, (vector unsigned int) (K4) ); /* Mix in the IV again */ A = vec_add(A, H[0]); B = vec_add(B, H[1]); C = vec_add(C, H[2]); D = vec_add(D, H[3]); E = vec_add(E, H[4]); /* Is this the best bit count so far? */ if(vec_any_ne( vec_and( vec_cmpeq(vec_and(A, vBitMaskLow), (vector unsigned int) (0)), vec_cmpeq(vec_and(A, vBitMaskHigh), (vector unsigned int) (0)) ), (vector unsigned int) (0))) { uInt32 IA, IB; /* Go over each vector element in turn */ for(n=0; n < 4; n++) { /* Extract A and B components */ IA = ((uInt32*) &A)[n]; IB = ((uInt32*) &B)[n]; /* Count bits */ gotBits = 0; if(IA) { s = IA; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 32; if(IB) { s = IB; while(!(s & 0x80000000)) { s <<= 1; gotBits++; } } else { gotBits = 64; } } *best = gotBits; /* Regenerate the bit mask */ maxBits = gotBits+1; if(maxBits < 32) { bitMask1Low = ~((((uInt32) 1) << (32 - maxBits)) - 1); bitMask1High = 0; } else { bitMask1Low = ~0; bitMask1High = ~((((uInt32) 1) << (64 - maxBits)) - 1); } *((uInt32*) &vBitMaskLow ) = bitMask1Low ; vBitMaskLow = vec_splat(vBitMaskLow , 0); *((uInt32*) &vBitMaskHigh) = bitMask1High; vBitMaskHigh = vec_splat(vBitMaskHigh, 0); /* Copy this result back to the block buffer */ for(t=0; t < 16; t++) { output[t*4+0] = X[t*16+0+n*4]; output[t*4+1] = X[t*16+1+n*4]; output[t*4+2] = X[t*16+2+n*4]; output[t*4+3] = X[t*16+3+n*4]; } /* Is it good enough to bail out? */ if(gotBits >= bits) { return iters+4; } } } MINTER_CALLBACK(); } return iters+4; /* For other platforms */ #else return 0; #endif } hashcash-1.21/test.sh0000664000076400007640000003117510351101612013127 0ustar adamadam#!/bin/sh . jdebug hashcash="../hashcash -u -t 040404" sha1="../sha1" mkdir -p test cd test #v=1 test=1 ###################################################################### echo -n "test $test (-mb8) " $hashcash -mqb8 foo@bar.com > stamp.$test echo -n `cat stamp.$test` | $sha1 | sed 's/^\(..\).*/\1/' > res.$test echo 00 > out.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-mb12) " $hashcash -mqb12 foo@bar.com > stamp.$test echo -n `cat stamp.$test` | $sha1 | sed 's/^\(...\).*/\1/' > res.$test echo 000 > out.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-mb8 -mb12) " $hashcash -mqb8 foo@bar.com -b12 taz@bar.com > stamp.$test echo -n `head -1 stamp.$test` | $sha1 | sed 's/^\(..\).*/\1/' > res1.$test echo 00 > out1.$test echo -n `tail -1 stamp.$test` | $sha1 | sed 's/^\(...\).*/\1/' > res2.$test echo 000 > out2.$test diff -q res1.$test out1.$test 1> /dev/null 2>&1 && \ diff -q res2.$test out2.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-b10 partial) " $hashcash -q -b10 < stamps [ $? -eq 2 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-b15 insufficient) " $hashcash -q -b15 < stamps [ $? -eq 1 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-r partial) " $hashcash -q -r '*@foo.com' < stamps [ $? -eq 2 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-r no match) " $hashcash -q -r '*@taz.com' < stamps [ $? -eq 1 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-d not spent) " cat > db.$test < stamp.$test < db.$test < stamp.$test < stamps < out.$test < res.$test" $hashcash -wq < stamps > res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-wb10 one sufficient) " cat > stamp.$test < out.$test < res.$test" $hashcash -wqb10 < stamp.$test > res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-wn) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-n) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-nb10) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-nr three match) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-l) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-nl) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-wl) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-nwl) " cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### # -c with -y ###################################################################### echo -n "test $test (-cy -b10 match) " $hashcash -cqy -b10 -r '*@foo.com' < stamps && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-cy -b15 insufficient) " $hashcash -cqy -b15 -r '*@foo.com' < stamps && echo fail || echo ok test=`expr $test + 1` ###################################################################### echo -n "test $test (-cy insufficient) " $hashcash -cqy -b+0 -r '*@foo.com' < stamps && echo fail || echo ok test=`expr $test + 1` ###################################################################### echo -n "test $test (-cy -b10 no match) " $hashcash -cqy -b10 -r '*@taz.com' < stamps && echo fail || echo ok test=`expr $test + 1` ###################################################################### echo -n "test $test (-cy -b5 expired) " $hashcash -cqy -b5 -r 'fax@foo.com' < stamps && echo fail || echo ok test=`expr $test + 1` ###################################################################### echo -n "test $test (-cy -b5 future) " $hashcash -cqy -b5 -r 'space@foo.com' < stamps && echo fail || echo ok test=`expr $test + 1` ###################################################################### # -c with exit codes ###################################################################### echo -n "test $test (-c -b10 partial) " $hashcash -cq -b10 -r '*@foo.com' < stamps [ $? -eq 2 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-cy -b10 success) " $hashcash -cyq -b10 -r '*@foo.com' < stamps [ $? -eq 0 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-c -b15 insufficient) " $hashcash -cq -b15 -r '*@foo.com' < stamps [ $? -eq 1 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-c -b10 no match) " $hashcash -cq -b10 -r '*@taz.com' < stamps [ $? -eq 1 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-c -b5 expired) " $hashcash -cq -b5 -r 'fax@foo.com' < stamps [ $? -eq 1 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-c -b5 future) " $hashcash -cq -b5 -r 'space@foo.com' < stamps [ $? -eq 1 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### # -cn ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "" > out.$test echo -n "test $test (-cn none sufficient) " $hashcash -cnqb+0 -r '*+bar@foo.com' < stamps > res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### # -cw ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### # -cl ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-c -b10 -r adam+bar@foo.com -o -b5 -r *@foo.com partial) " $hashcash -cqb10 -r 'adam+bar@foo.com' -o -b5 -r '*+bar@foo.com' < stamps [ $? -eq 2 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### echo -n "test $test (-c -b10 -r blah -o -b6 -r blah -r *+xyz@foo.com partial) " $hashcash -cqb10 -r 'adam+bar@foo.com' -o -b6 -r '*+bar@foo.com' \ -r '*+xyz@foo.com' < stamps [ $? -eq 2 ] && echo ok || echo fail test=`expr $test + 1` ###################################################################### cat > out.$test < res.$test diff -q res.$test out.$test 1> /dev/null 2>&1 && echo ok || echo fail test=`expr $test + 1`