// ============================================================================ // C servlet sample for the G-WAN Web Application Server (http://trustleap.ch/) // ---------------------------------------------------------------------------- // captcha.c: generate an in-memory dynamically generated GIF image served // "inline" as a Data URI in the HTML code of a page (instead of // as a GIF image sent to the client). CSS could be embedded too. // // As text is rendered as a bitmap, it requires OCR to break the // captcha (a question of basic arithmetic which gets trickier // for robots because the colors used to draw in the bitmap have // the same arithmetic contrast: humans will only see one view // rathan the other - due to the HTML background color). // // ============================================================================ #include "gwan.h" // G-WAN exported functions #include #include // ---------------------------------------------------------------------------- // render random chars into a bitmap, with a char per line painted in green // ---------------------------------------------------------------------------- #define NBR_COLUMNS 3 // try '16' to get on the nerves of your users... #define CHAR_WIDTH 8 #define BMP_WIDTH (NBR_COLUMNS * CHAR_WIDTH) #define BMP_HEIGHT (NBR_COLUMNS * CHAR_WIDTH + 1) u32 captcha(bmp_t *img, prnd_t *rnd) { u8 *img_p = img->p; u32 sum = 0; for(;;) // loop until we have different results for the two sums { char l[] = { 0, 0 }; u8 color[] = { 1, 2 }, dic[] = "ABCDEF0123456789"; u32 j = 0, r = sw_rand(rnd), c = r % NBR_COLUMNS; u8 *old_p; img->p = img_p + img->w + 1; // top position while(j < NBR_COLUMNS) { int i = 0; // number of characters per line to generate while(i < NBR_COLUMNS) { *l = dic[r % (sizeof(dic) - 1)]; // pick a character sum += ((*l < '0' || *l > '9')) ? 0 : (i != c) ? (*l - '0') : (*l - '0') << 16; old_p = img->p; // remember last position in bitmap img->pen = color[i == c]; // select pen color dr_text(img, 0, l); // 0:default font //printf("%s ", l); // debugging // turn proportional spacing into fixed spacing (CHAR_WIDTH) img->p += CHAR_WIDTH - (img->p - old_p); r = sw_rand(rnd); i++; } //printf("\n"); // debugging c = r % NBR_COLUMNS; // pick the next random GREEN column img->p += (CHAR_WIDTH-1) * img->w; // new line position in buffer j++; } if(((sum & 0xffff0000) >> 16) != (sum & 0x0000ffff)) break; img->p = img_p; // reset bitmap and retry until we get <> sums memset(img_p, 0, CHAR_WIDTH * img->w * img->h); } //printf("\n"); // debugging return sum; // return the SUM of all the GREEN figures } // ---------------------------------------------------------------------------- // imported functions: // get_reply(): get a pointer on the 'reply' dynamic buffer from the server // get_env(): get connection's 'environment' variables from the server // xbuf_cat(): like strcat(), but it works in the specified dynamic buffer // gif_build(): build an in-memory GIF image from a bitmap and palette // ---------------------------------------------------------------------------- int main(int argc, char *argv[]) { // ------------------------------------------------------------------------- // build the top of our HTML page // ------------------------------------------------------------------------- static char top[]= "" "Captcha" "" "

Captcha for Humans

" "

Please enter the SUM of all the GREEN FIGURES (not letters) below " "(that's twice the same image, just with a different HTML background " "- the Data-URI-inlined GIF background is transparent):


\r\n"; xbuf_t *reply = get_reply(argv); xbuf_ncat(reply, top, sizeof(top) - 1); // ------------------------------------------------------------------------- // allocate memory for a raw bitmap // ------------------------------------------------------------------------- const int w = BMP_WIDTH, h = BMP_HEIGHT, wXh = w * h; u8 *bmp = (u8*)calloc(CHAR_WIDTH * w, h); if(!bmp) return 503; // service unavailable // ------------------------------------------------------------------------- // render the captcha in our bitmap // ------------------------------------------------------------------------- u32 seed = (u32)getns(); prnd_t rnd; // pseudo-random generator (period: 1 << 158) sw_init(&rnd, seed); // EPOCH time in nano-seconds // structure needed by G-WAN's frame buffer routines like dr_text() bmp_t img ={ .bmp = bmp, .p = bmp, .bbp = 8, .pen = 1, .bgd = 0, .rect = {0,0, w,h}, .flags = 0, .w = w, .h = h, .x = 0, .y = 0 }; u32 sum = captcha(&img, &rnd); // ------------------------------------------------------------------------- // build the GIF image, gif_build(0:transparent color index, 0: no comment) // ------------------------------------------------------------------------- u8 pal[] = { 255, 255, 255, 223, 255, 191, 132, 164, 100, 0, 0, 0 }; const int nbcolors = (sizeof(pal) / sizeof(u8)) / 3; // RGB values u8 *gif = (u8*)malloc(CHAR_WIDTH * wXh); if(!gif) { free(bmp); return 503; } // service unavailable int gln = gif_build(gif, bmp, w, h, pal, nbcolors, 0, 0); // ------------------------------------------------------------------------- // store the base64 encoded GIF in the 'reply' buffer // ------------------------------------------------------------------------- if(gln > 0) // (gln == -1) if gif_build() failed { // a real captcha test would only display the first of those two views: // (they are shown side-by-side to visualize the background trick) xbuf_cat(reply, "\r\n" "\r\n", gln, gif, w + w, h + h); // scale picture xbuf_xcat(reply, "\r\n
\r\n"); u32 img_pos = reply->len; xbuf_xcat(reply, "\"A%.*s
\n\r", reply->len - img_pos, reply->ptr + img_pos); } free(gif); free(bmp); // ------------------------------------------------------------------------- // close our HTML page // ------------------------------------------------------------------------- xbuf_xcat(reply, "
The two sums are: %u and %u... " "for the same Captcha image!

" "By just changing the HTML background color (mouse cursor " "hovering, previous state or input or shared secret) used for " "the transparent GIF Captcha image we can make something simple " "for humans become difficult or even completely impossible " "for robots.

" "HTML and GIF are served with one single request: the picture" " is generated on-the-fly and embedded into the HTML code by " " using the base64 encoding (look at the HTML source code)." "
", (sum & 0xffff0000) >> 16, sum & 0x0000ffff); return 200; // return an HTTP code (200:'OK') } // ============================================================================ // End of Source Code // ============================================================================