integrating and implementing hashcash
new format version
The 1.02 code was released 11 Aug 2004 (and is the same bar minor doc cleaup to the 1.01 release of 08 Aug 2004). The verison 1 format is described in the man page from the docs. (See section on stamp format).
The purpose of the format change that comes with 1.x is to allow people such as yourself to add their own extensions to hashcash in a general fashion such that different hashcash using systems can use the extensions they understand and ignore the extensions they don't without needing to revise the main hashcash format version number each time. This I hope should lead to people thinking of new and useful ways to extend and integrate hashcash into other anti-spam systems, and find entirely new uses for it.
The old current version 0 format is described in the internet-draft. Note there is one small simplifying retro-active revision of what is stated in that document: the resource string MUST NOT contain a : (colon) character.
I will make a new version 1 format internet-draft over the coming weeks. In the mean time it is described in adequate working detail in the man page. Send any questions or clarifications to the mailing list.
v1 format implementation notesNote the following value definition change:
version 0 format: value is number of leading 0 bits
version 1 format:
the v1 format has an explicit version field which I'll call $claimed_value. Then $measured_value is the number of leading 0 bits.
But the actual value used should be calculated based on those two inputs as follows:
ie the stamp never has value higher than the claimed value, if you got lucky and there are more than the targetted number of 0 bits, you do not get the benefit of that, the stamp is just worth the claimed value. On the other hand if it has less bits than claimed it is worthless (0 value).
(This works because you can't change the claimed_value bits field without re-computing the stamp from scratch).
And here is some perl code for parsing stamps:
ie for non perl programmers each field is separated by a ":" and each of the 0 or more extensions is separated by a ";" and the extension format is a series of name, value sets with the name separated from the value set by an "=". (Note the values can contain "=" hence the 2 argument to split; this means (for non-perl programmers) that only the first "=" in an extension should be used to separate the extension-name from the extension-value set. The 0 or more values in an extension are separated by ",". The values of an extension can be interpreted arbitrarily by the extension implementation. Note also that it is legal for there to be only an extension name, with no values (and no "=" separating name from non-existant value set).
Note the extension value should be limited to printable 7-bit clean, no whitespace.
But you may just want to ignore the extension until you need to handle it in which case it is just:
ie for non-perl programmers each field is just separated by a ":".
There is C library source code available under a non-restrictive open source license which you can use. In some circumstances using the command line tool may be sufficient (same source).
In any case verifying hashcash is very simple, so you may choose to implement this yourself using shell scripting or the language of your choice. The only complicated part is the SHA1 hash function which is very widely used and available in many scripting environments and as a library function in many languages.
A stamp is verified by counting how many most significant 0 bits are output by the SHA1 function. There is a sha1 command included with the command line source. On some systems sha1 is installed as sha1sum, and in addition the openssl sha1 sub-program also works in this function if you have the openssl executable installed.
Taking the example stamp: 1:20:040806:foo::65f460d0726f420d:13a6b8 one could verify its collision using the following:
In addition it is necessary to avoid accepting the same stamp twice which you would do by storing the spent hashcash stamps after accepting. There is also a creation date, and expired stamps can be purged from the database after this date. Expired stamps should be rejected without reference to the double-spend database, and futuristic stamps should be rejected. Recommended default collision sizes are 20-bits. (Note a hashcash extension may be designed to do something to standardize and communicate collision sizes; currently the recommended is 20-bits fixed, there is no standardized way to update the required bits, nor to communicate non-standard amounts of bits. SpamAssassin accepts 16-25 bits but assignes more +ve score for more bits (>25 bits also accepted but with score equal to a 25 bit stamp)). Recommended expiry time is 28 days. Recommended tolerance for clock skew is 48 hours. As each recipient will have a separate X-Hashcash: header it is necessary to scan the headers until a valid stamp is found. (Note also that a recipient can have multiple addresses he is willing to receive mail for, and may as a result of that, or anyway by being on the recipient list multiple times receive a mail multiple times.)
It has been suggested by one of the mail server authors who implemented this that one should take the received date stamped by the mail server to compare against the date stamp in the hashcash stamp for expiry purposes. (Rather than "today" above). The clear advantage of this is that it avoids problems where the user downloads email much later than receipt, eg after a holiday; and also for example uses POP with "keep on server" and then loads the same mails again much later on a different machine. The server conveniently keeps track of the date (from it's point of view) at time of receipt in the received header. Another positive side effect is that it means correct hashcash expiry relies only on the clock on the MX/POP server rather than on the clock on the mail client machine.