/*
firepay.c - Generate a HashCash/MIME envelope around a message
Copyright (C) 2003 Ian Gulliver
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
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
*/
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "firemake.h"
#include "sha1.h"
#define MAX_TOKEN_LEN 256
#define MAXBITS (SHA1_DIGEST_BYTES * 8)
#define bit(c,n) (((c) >> (7 - (n))) & 1)
static char charset[] = "01235456789abcdef";
struct firestring_estr_t subheaders = {0};
int gargc;
char **gargv;
struct token_info {
struct firestring_estr_t token;
struct firestring_estr_t minthash;
};
int bitcmp(char *a, char *b) {
int bits = 0;
while (bits < MAXBITS) {
if (a[0] != b[0])
break;
bits += 8;
a++;
b++;
}
while (bits < MAXBITS) {
if (bit(a[0],bits % 8) != bit(b[0],bits % 8))
break;
bits++;
}
return bits;
}
int nextletter(char *moutput, SHA1_ctx *template, struct firestring_estr_t *token, int letter, int len) {
SHA1_ctx chash;
char coutput[SHA1_DIGEST_BYTES];
int i;
for (i = 0; i < 16; i++) {
token->s[letter] = charset[i];
if (token->l - 1 == letter) {
memcpy(&chash,template,sizeof(chash));
SHA1_Update(&chash,token->s,token->l);
SHA1_Final(&chash,(byte *)coutput);
if (bitcmp(moutput,coutput) >= len)
return 0;
} else {
int j;
j = nextletter(moutput,template,token,letter + 1,len);
if (j == 0)
return 0;
}
}
return 1;
}
struct token_info *gen_token(struct firestring_estr_t *mint, int len) {
SHA1_ctx mhash, ctemplate;
static struct token_info info = {{0},{0}};
int i;
firestring_estr_expand(&info.minthash,SHA1_DIGEST_BYTES);
SHA1_Init(&mhash);
SHA1_Update(&mhash,mint->s,mint->l);
memcpy(&ctemplate,&mhash,sizeof(ctemplate));
SHA1_Final(&mhash,(byte *)info.minthash.s);
info.minthash.l = SHA1_DIGEST_BYTES;
firestring_estr_expand(&info.token,MAX_TOKEN_LEN);
info.token.l = 1;
while (info.token.l < MAX_TOKEN_LEN) {
i = nextletter(info.minthash.s,&ctemplate,&info.token,0,len);
if (i == 0)
return &info;
info.token.l++;
}
firestring_estr_free(&info.token);
firestring_estr_free(&info.minthash);
return NULL;
}
int foldback(struct firestring_estr_t *headers, struct firestring_estr_t *name) {
struct firestring_estr_t *fullheader;
fullheader = firemime_get_full_header(headers,name,&ESTR_S("\n"));
if (fullheader == NULL)
return -1;
firestring_estr_aestrcat(&subheaders,fullheader,0);
memmove(fullheader->s,fullheader->s + fullheader->l,headers->l - (fullheader->s - headers->s + fullheader->l));
headers->l -= fullheader->l;
return 0;
}
int handler(struct firemime_part *stack, int depth, void *opaque) {
struct firestring_estr_t addheader = {0}, *tempheader, delim;
struct firestring_estr_t mint = {0}, wirebody = {0};
struct firestring_estr_t minthash_readable;
struct token_info *info;
time_t t;
int l;
if (foldback(&stack[depth].headers,&ESTR_S("Content-Type")) == -1)
firestring_estr_aestrcat(&subheaders,&ESTR_S("Content-Type: text/plain\n"),0);
foldback(&stack[depth].headers,&ESTR_S("Content-Transfer-Encoding"));
foldback(&stack[depth].headers,&ESTR_S("Content-Dispostion"));
foldback(&stack[depth].headers,&ESTR_S("Content-ID"));
tempheader = firemime_get_header(&stack[depth].headers,&ESTR_S("MIME-Version"),&ESTR_S("\n"));
if (tempheader == NULL)
firestring_estr_aestrcat(&addheader,&ESTR_S("MIME-Version: 1.0\n"),0);
putenv("TZ=UTC");
time(&t);
srand(t);
firestring_estr_alloc(&mint,strlen(gargv[1]) + 21);
firestring_estr_sprintf(&mint,"%s\r\n%t\r\n",gargv[1],t);
firestring_estr_areplace(&wirebody,&stack[depth].body,&ESTR_S("\r\n"),&ESTR_S("\n"));
firestring_estr_aestrcat(&mint,&wirebody,0);
info = gen_token(&mint,atoi(gargv[2]));
firestring_estr_free(&mint);
firestring_estr_alloc(&delim,32);
for (l = 0; l < 32; l++)
delim.s[l] = charset[rand() % 16];
delim.l = 32;
firestring_estr_alloc(&minthash_readable,info->minthash.l * 4 / 3 + 4);
firestring_estr_base64_encode(&minthash_readable,&info->minthash);
firestring_fprintf(stdout,"%e%eContent-Type: multipart/postage; boundary=\"%e\"\n"
"\n"
"--%e\n"
"%e\n"
"%e\n"
"--%e\n"
"Content-Type: application/postage-hashcash\n"
"Version: 1.1\n"
"Mint-Hash: %e\n"
"To: %s\n"
"Date: %t\n"
"Amount: %d\n"
"\n"
"%e\n"
"--%e--\n",&stack[depth].headers,&addheader,&delim,&delim,&subheaders,&stack[depth].body,&delim,
&minthash_readable,gargv[1],t,atoi(gargv[2]),&info->token,&delim);
firestring_estr_free(&addheader);
firestring_estr_free(&wirebody);
firestring_estr_free(&info->token);
firestring_estr_free(&info->minthash);
firestring_estr_free(&delim);
firestring_estr_free(&minthash_readable);
return 2;
}
int main(int argc, char *argv[]) {
struct firestring_estr_t message;
int l;
int r;
if (argc != 3) {
firestring_fprintf(stderr,"Usage: %s <recipient> <amount>\n",argv[0]);
exit(1);
}
gargc = argc;
gargv = argv;
firestring_estr_alloc(&message,4096);
while ((l = read(0,&message.s[message.l],message.a - message.l)) > 0) {
message.l += l;
firestring_estr_expand(&message,message.l + 4096);
}
r = firemime_parse(&message,handler,&ESTR_S("\n"),NULL);
firestring_estr_free(&subheaders);
firestring_estr_free(&message);
/* we expect 2 back from the handler cancelling out */
if (r != 2) {
firestring_fprintf(stderr,"Error parsing MIME structure: %s\n",firemime_strerror(r));
exit(1);
}
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1