Index: include/vend.h =================================================================== --- include/vend.h (revision 5484) +++ include/vend.h (working copy) @@ -119,8 +119,6 @@ vbe32enc(p + 4, (uint32_t)(u & 0xffffffffU)); } -#if 0 - static __inline void vle16enc(void *pp, uint16_t u) { @@ -149,6 +147,5 @@ vle32enc(p, (uint32_t)(u & 0xffffffffU)); vle32enc(p + 4, (uint32_t)(u >> 32)); } -#endif #endif Index: include/varnishapi.h =================================================================== --- include/varnishapi.h (revision 5484) +++ include/varnishapi.h (working copy) @@ -33,6 +33,7 @@ #define VARNISHAPI_H_INCLUDED #include +#include #include "vsl.h" @@ -263,5 +264,11 @@ /* base64.c */ void base64_init(void); int base64_decode(char *d, unsigned dlen, const char *s); +int base64_encode(char *e, unsigned elen, const char *s, unsigned n); +int base64_encode_string(char *e, unsigned elen, const void *term, const char *s, va_list ap); +/* safe estimate of length of de/encoded string including terminal NULL */ +#define base64_decode_l(l) ((l * 3 / 4) + 1) +#define base64_encode_l(l) (((((l * 4 / 3) + 3) / 4) * 4) + 1) + #endif Index: configure.ac =================================================================== --- configure.ac (revision 5484) +++ configure.ac (working copy) @@ -483,6 +484,7 @@ lib/libvarnishcompat/Makefile lib/libvcl/Makefile lib/libvmod_std/Makefile + lib/libvmod_digest/Makefile lib/libjemalloc/Makefile man/Makefile redhat/Makefile Index: doc/sphinx/reference/vmod_digest.rst =================================================================== --- doc/sphinx/reference/vmod_digest.rst (revision 0) +++ doc/sphinx/reference/vmod_digest.rst (revision 0) @@ -0,0 +1,159 @@ + +.. _reference-vmod_digest: + +=========== +vmod_digest +=========== + +--------------------------------------------- +Message digests (hashes) and encoders for VCL +--------------------------------------------- + +:Author: Nils Goroll +:Date: 2010-10-28 +:Version: 0.1 +:Manual section: 7 + +SYNOPSIS +======== + + import digest; + + digest.DIGEST_ENCODER(STRING) + + digest.encode_ENCODER(STRING) + +Common use cases: + + digest.sha256_hexlc(STRING) + + digest.md5_hexlc(STRING) + + digest.encode_base64(STRING) + +DESCRIPTION +=========== + +The digest module makes message digest (hashing) functions available +for use from VCLs. As a side-product, it also provides various string +encoders. + +The provided functions are named according to the scheme given in the +synopsis. + +DIGESTS +======= + +DIGEST can be one of + +* ``default``: Some efficient default digest, subject to change + (currently ``crc32``) + +* ``crc32``: "vanilla" 32bit Cyclic redundancy checksum (CRC32) with + polynomial 0x04C11DB7 according to + http://regregex.bbcmicro.net/crc-catalogue.htm in big endian + representation (network byte order) + +* ``sha256``: 256 bit Secure Hash Algorithm 2 (SHA-2) cryptographic + hash function as used internally by Varnish + +* ``md5``: 128 bit Message-Digest algorithm 5 (MD5) cryptographic hash + function + +* ``plain``: 32 bit plain check sum (sum of all bytes modulo 2^32) in + network byte order + +* ``rs``: 32 bit rshash: Simple, efficient hash function from Robert + Sedgwicks *Algorithms in C* book in network byte order + +ENCODERS +======== + +ENCODER can be one of + +* ``base64``: Base64 encoder according to + http://www.faqs.org/rfcs/rfc1113.html section 4.3.2.4, but without + line split + +* ``hex``: Hexadecimal representation with uppercase characters + +* ``hexlc``: Hexadecimal representation with lowercase characters + +RETURN VALUES +============= + +All functions will return the string representation of the encoded +input or encoded digest of the imput, or the NULL (empty) string upon +error. + +EXAMPLES +======== + +The following example shows how to generate a Base64 encoding of a +clear-text username/passwort to check Basic HTTP authentication and +how to check an md5 hash of a shared secret and the IP Address of the +client in order to validate a request. + +While the pratical use of the former example is probably limited, a +more elaborate version of the latter example is actually used by a +well known Content Delivery Network. + +:: + + import digest; + + sub vcl_recv { + # Simplistic check for basic HTTP authentication in Varnish + + if (req.http.Authorization != ("Basic " + + digest.encode_base64("user:password"))) { + error (401, "don't know the magic credentials?"); + } + + # Check a digest of something else + if (req.http.X-Identity != + digest.md5_hexlc("secret" + client.ip)) { + error (403, "You are not on my guest list"); + } + } + +SEE ALSO +======== + +* VCL(7) +* vmod(7) + +LIMITATIONS +=========== + +Due to the fact that a data type to store the respective state does +not currently exist in VCL, updating a hash is not possible from +VCL. All components to be hashed need to be passed at once. + +Because VCL does not provide a data type for binary data, digests can +not be accessed in their binary form. + +Currently, only strings can be hashed or encoded from VCL. Directly +hashing or encoding other data types is not currently supported by the +VMOD framework. + +Inline C might help to avoid these limitations. + +TODO +==== + +* more hashes +* decoding functions + +HISTORY +======= + +The digest vmod and this manual were written by Nils Goroll. + +COPYRIGHT +========= + +This document is licensed under the same licence as Varnish +itself. See LICENCE for details. + +* Copyright (c) 2010 UPLEX Nils Goroll Index: doc/sphinx/reference/index.rst =================================================================== --- doc/sphinx/reference/index.rst (revision 5484) +++ doc/sphinx/reference/index.rst (working copy) @@ -19,6 +19,7 @@ varnishtop.rst shmem.rst vmod.rst + vmod_digest.rst .. todo:: The programs: Index: lib/Makefile.am =================================================================== --- lib/Makefile.am (revision 5484) +++ lib/Makefile.am (working copy) @@ -6,6 +6,7 @@ libvarnishapi \ libvcl \ libvmod_std \ + libvmod_digest \ @JEMALLOC_SUBDIR@ DIST_SUBDIRS = \ @@ -14,4 +15,5 @@ libvarnishapi \ libvcl \ libvmod_std \ + libvmod_digest \ libjemalloc Index: lib/libvarnishapi/base64.c =================================================================== --- lib/libvarnishapi/base64.c (revision 5484) +++ lib/libvarnishapi/base64.c (working copy) @@ -1,5 +1,16 @@ /* + * Utility functions for base64 encoding / decoding according to + * http://www.faqs.org/rfcs/rfc1113.html section 4.3.2.4 + * + * Limits: + * + * - '*' (not encrypted) is unimplemented + * - Encoding line width limitation not implemented, the base64 encoded string + * will not be split up into multiple lines + * - Decoding of multi line input is not implemented + * * Written by Poul-Henning Kamp + * and Nils Goroll * * This file is in the public domain. */ @@ -10,12 +21,15 @@ SVNID("$Id$") #include +#include #include "varnishapi.h" +#include "vas.h" static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static char i64[256]; +static int init_done = 0; void base64_init(void) @@ -23,32 +37,63 @@ int i; const char *p; + /* can we race? Yes, we can. Do we care? No! */ + if (init_done) + return; + for (i = 0; i < 256; i++) i64[i] = -1; for (p = b64, i = 0; *p; p++, i++) i64[(int)*p] = (char)i; i64['='] = 0; + + init_done = 1; } +/* + * Args: destination buffer, buffer length + * source data + * + * source data must be a null-terminated string + * + * return length of decoded data in d or -1 upon error + * + * to facilitate decoding of strings, a final null byte will be appended, but + * not returned as the length + * + * d must be large enough to store the null byte + * + * implementation note: When encountering the N/A char '=', we finish decoding + * the block, but limit the number of bytes being output + */ + int base64_decode(char *d, unsigned dlen, const char *s) { - unsigned u, v, l; + unsigned u, v, w, l; int i; u = 0; l = 0; + while (*s) { + w = 3; for (v = 0; v < 4; v++) { if (!*s) break; - i = i64[(int)*s++]; + i = i64[(int)*s]; if (i < 0) return (-1); u <<= 6; u |= i; + if ((*s++ == '=') && (w == 3)) { + if (v == 3) + w = 2; + else if (v == 2) + w = 1; + } } - for (v = 0; v < 3; v++) { + for (v = 0; v < w; v++) { if (l >= dlen - 1) return (-1); *d = (u >> 16) & 0xff; @@ -58,33 +103,421 @@ } } *d = '\0'; - return (0); + return (l); } +/* + * "left" and "right" part of the respective input bytes, thinking big endian, + * shifted to the right base64 output position + */ + +#define b1_l(s) ((s[0] & 0xfc) >> 2) +#define b1_r(s) ((s[0] & 0x03) << 4) + +#define b2_l(s) ((s[1] & 0xf0) >> 4) +#define b2_r(s) ((s[1] & 0x0f) << 2) + +#define b3_l(s) ((s[2] & 0xc0) >> 6) +#define b3_r(s) ((s[2] & 0x3f)) + +/* + * write one 4-byte block into e (must be large enough), + * encoding b = (1..3) bytes from s + * + * performance note: Should be compiler-optimizable when being fed a constant b + */ + +static inline void +_b64block(char *e, const char *s, char b) +{ + switch (b) { + case 3: + e[3] = b64[ b3_r(s) ]; + e[2] = b64[ b3_l(s) | b2_r(s) ]; + case 2: + e[1] = b64[ b2_l(s) | b1_r(s) ]; + case 1: + e[0] = b64[ b1_l(s) ]; + } + switch (b) { + case 1: + e[1] = b64[ b1_r(s) ]; + e[2] = '='; + e[3] = '='; + break; + case 2: + e[2] = b64[ b2_r(s) ]; + e[3] = '='; + } +} + +/* + * Args: destination buffer, buffer length + * source data, source length + * + * return length of encoded data in d or -1 upon error + */ + +int +base64_encode(char *e, unsigned elen, const char *s, unsigned n) +{ + char b; + int l; + + l = 0; + + while (n) { + if (elen < (l + 5)) + return (-1); + + b = (n < 3) ? n : 3; + + _b64block(e, s, b); + + l += 4; + e += 4; + s += 3; + n -= b; + } + *e='\0'; + return (++l); +} + +/* + * same as base64_encode, but encode strings passed as varargs as if they were + * one concatenated string. + * + * term is the terminator for the string list, this will usually be + * vrt_magic_string_end when called from VRT, VMOD etc. When a non-NULL + * terminator is passed, null arguments will be skipped over. + * + * if NULL is passed as a terminator, varargs processing will stop on a NULL + * pointer + */ + +#if 0 +#define b64assert(x) assert(x) +#else +#define b64assert(x) ((void)(x)) +#endif + +int +base64_encode_string(char *e, unsigned elen, const void *term, const char *s, va_list ap) +{ + const char *p; + char b; + int l; + /* temp space to carry over three bytes from one vararg to the next */ + char carry[2]; + int carry_l; + + l = carry_l = 0; + + if (elen == 0) + return -1; + + p = s; + while (p != term) { + if (p == NULL) + goto nextp; + + /* + * fill up the array carrying the last bytes from the previous + * string until this string ends or carry is full + */ + if (carry_l) { + while (*p && (carry_l < 3)) + carry[carry_l++] = *p++; + + if (carry_l < 3) + goto nextp; + + b64assert(carry_l == 3); + + if (elen < (l + 5)) + return -1; + + _b64block(e, carry, 3); + carry_l = 0; + l += 4; + e += 4; + } + + b64assert(carry_l == 0); + + while (*p) { + if (elen < (l + 5)) + return -1; + + if (*(p+1)) + if (*(p+2)) + b = 3; + else + b = 2; + else + b = 1; + + if (b == 3) { + _b64block(e, p, 3); + + l += 4; + e += 4; + p += 3; + } else { + b64assert (b > 0); + + if (carry_l == 2) { + carry[carry_l] = *p; + p++; + b--; + _b64block(e, carry, 3); + l += 4; + e += 4; + carry_l = 0; + } + + b64assert((carry_l + b) <= 3); + + switch (b) { + case 2: + carry[carry_l + 1] = *(p+1); + case 1: + carry[carry_l] = *p; + } + carry_l += b; + p += b; + } + } + nextp: + p = va_arg(ap, const char *); + } + + /* Anything left ? */ + + if (carry_l) { + if (elen < (l + 5)) + return -1; + + _b64block(e, carry, carry_l); + l += 4; + e += 4; + } + + *e='\0'; + return (++l); +} + #ifdef TEST_DRIVER + +/* gcc -DTEST_DRIVER -I ../.. -I ../../include ../libvarnish/assert.c base64.c */ + #include +#include -const char *test1 = -"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" -"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" -"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" -"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" -"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="; +#define MAX_PRINTABLE 5 +#define MAX_TEST 11 +const void * const magic_string_end = &magic_string_end; + +const char *test[MAX_TEST+1] = { + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" + "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" + "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" + "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", /* 0 */ + "", /* 1 */ + /* all following tests encoded using GNU coreutils base64 */ + "YQ==", /* 2: "a" */ + "YWI=", /* 3: "ab" */ + "YWJj", /* 4: "abc" */ + "UGhrIGFuZCBWYXJuaXNoIHNob3VsZCByZWFsbHkgYmUgYXdhcmRlZCBhIHByaXplIGZvciBwcm90" + "ZWN0aW5nIG91ciBlbnZpcm9ubWVudCBieQpzaWduaWZpY2FudGx5IGxvd2VyaW5nIHRoZSBDTzIg" + "Zm9vdHByaW50IC8gZW5lcmd5IGNvbnN1bXB0aW9uIGJ5IHRvZGF5J3MgaW50ZXJuZXQKaW5kdXN0" + "cnkuIFRoZSBvbmx5IHF1ZXN0aW9uIGlzOiBEb2VzIHN1Y2ggYSBwcml6ZSBleGlzdD8K", /* 5 */ + /* + * 6: just some random data from: + * export LC_CTYPE=C + * dd if=/dev/random bs=256 count=1 | tr '\000' '0' | base64 + */ + "qZzhtC90adeYmJSbUUIXti2e6ob7Ru+QeVl9UiiqM9xtsRFh6gnWIFGDCksSmhZ74FkIPj34UawD" + "U7Db/5cWWMclJf+B55jmttozMvDW9IumypB/XjMr1W9e9gyiOOoRc1snjIGhyEdiRwvsOl2V0mX0" + "7e7StefOtImNz80gIWtGxq+e5KKW5GiJmVamla2zGNj2Sdvo1LV1CGOh3PhwGwJwmHOn9R1/zZ5x" + "XWosmz90Kp5RLDbsdDmc/jmJvGzZkE66p0554Xl4rFdu7jSWEqoQKGYboCa8YDAGzKhB0C+xth/J" + "F9T42OINv9VXkIjB0Vs+0geY3GifRqbjFPmk", + /* + * 7: random data with nulls + */ + "KN3KvtjkNP70IBlGqYzRMbUCAJdpux0VXTVGNGVP3iGbTr4fYJ/S78iKQNPVf1TWWhJPLT0ENZVI" + "OP4UYWP9Laja4hsPn3d9iunTzG8fDovDXwRlmyTL5xzI1OC34MNwA0nVxoSsHCDHvIBT1uz3ZEaM" + "k4N6A8Uwifk5IlLsoeWOU83ao1+jqNXkhrLcuZOaccIr5GfI7KU+igswmH22+ncaIIuAhc8fIzSv" + "n01grCHeB5H3O/+Yt8ycQIoUmf+HkbpIm4v//AWIZVivgzeI2dMoJ0a8xWV+MsgKg+2NwnknIVLB" + "dfukgA32FjiuJ0lWZAUVsakmk0QChtTLQpkDqg==", + /* various lengths of zero buffers */ + "AA==", /* 8 */ + "AAA=", /* 9 */ + "AAAA", /* 10 */ + "AAAAAA==" /* 11 */ +}; + int +base64_encode_s(char *e, unsigned elen, const char *s, ...) +{ + va_list ap; + int r; + + va_start(ap, s); + r = base64_encode_string(e, elen, magic_string_end, s, ap); + va_end(ap); + return (r); +} + +void +print_varargs(const char *s, ...) +{ + va_list ap; + const char *p; + + va_start(ap, s); + p = s; + while (p != magic_string_end) { + if (p) + printf(">%s< ", p); + else + printf("(null) "); + p = va_arg(ap, const char *); + } + printf("\n"); + va_end(ap); +} + +int main(int argc, char **argv) { - int i; - char buf[BUFSIZ]; - unsigned l; + int i, j, k, l; + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; + int l_dec, l_dec_est, l_dec_str; + int l_enc, l_enc_est, l_enc_str; + + /* split for varargish encode */ +#define CHUNK_LEN 9 +#define NCHUNK 8 + char chunk[NCHUNK][CHUNK_LEN]; + const char *cp; + + (void)argc; (void)argv; base64_init(); - l = sizeof buf; - base64_decode(buf, &l, test1); - printf("%s\n", buf); + + + for (i = 0; i <= MAX_TEST; i++) { + printf("\nTest %d:\n-------\n", i); + + /* decode */ + + if ((l_dec = base64_decode(buf1, sizeof(buf1), test[i])) < 0) { + fprintf(stderr, "decode(test[%d]) failed\n", i); + return (1); + } + l_dec_est = base64_decode_l(strlen(test[i])); + + if (i <= MAX_PRINTABLE) { + printf("%s\n", buf1); + l_dec_str = strlen(buf1) + 1; + printf("decode length estimated: %d, actual %d, strlen+1 %d\n", + l_dec_est, l_dec, l_dec_str); + } else { + printf("decode length estimated: %d, actual %d\n", + l_dec_est, l_dec); + } + + if (l_dec_est < l_dec) { + fprintf(stderr, "decode length estimate to small for test %d\n", i); + } + + + /* encode */ + + if ((l_enc = base64_encode(buf2, sizeof(buf2), buf1, l_dec)) < 0) { + fprintf(stderr, "encode(test[%d]) failed\n", i); + return (1); + } + + l_enc_est = base64_encode_l(l_dec); + if (i <= MAX_PRINTABLE) { + l_enc_str = strlen(buf2) + 1; + printf("encode length estimated: %d, actual %d, strlen+1 %d\n", + l_enc_est, l_enc, l_enc_str); + } else { + printf("encode length estimated: %d, actual %d\n", + l_enc_est, l_enc); + } + + if (l_enc_est < l_enc) { + fprintf(stderr, "encode length estimate to small for test %d\n", i); + } + + + printf("%s\n", test[i]); + printf("%s\n", buf2); + + if (strcmp(test[i], buf2) != 0) { + fprintf(stderr, "encode(decode(test[%d])) != test[%d]\n", i, i); + return (1); + } + + if ((i > MAX_PRINTABLE) || (l_dec < (NCHUNK * CHUNK_LEN / 2))) + continue; + + /* varargisch encode: split input into chunks */ + for (j = 0; j < CHUNK_LEN; j++) { + cp = buf1; + + /* permutate lengths of chunks */ + for (k = 0; k < NCHUNK && *cp; k++) { + l = strnlen(cp, (j + k + 1) % CHUNK_LEN); + strncpy(chunk[k], cp, l); + cp += l; + chunk[k][l] = '\0'; + } + + l_enc = base64_encode_s(buf2, sizeof(buf2), + chunk[0], NULL, "", + chunk[1], chunk[2], chunk[3], + "", "", "", "", NULL, "", + chunk[4], chunk[5], chunk[6], + chunk[7], cp, magic_string_end); + if (l_enc < 0) { + fprintf(stderr, "encode(test[%d]) varargs %d" + "failed\n", i, j); + return (1); + } + + if (strcmp(test[i], buf2) != 0) { + fprintf(stderr, "encode(decode(test[%d])) " + "varargs %d != test[%d]:\n", i, j, i); + + print_varargs( + chunk[0], NULL, "", + chunk[1], chunk[2], chunk[3], + "", "", "", "", NULL, "", + chunk[4], chunk[5], chunk[6], + chunk[7], cp, magic_string_end); + printf("%s\n", buf2); + + return (1); + } + } + } + return (0); } + +#undef MAX_PRINTABLE +#undef MAX_TEST +#undef CHUNK_LEN +#undef NCHUNK #endif Property changes on: lib/libvmod_digest ___________________________________________________________________ Added: svn:ignore + vcc_if.h vmod.vcc Makefile.in vmod_digest.h vtc_snippet vmod_digest_if.c .deps vcc_if.c Makefile Index: lib/libvmod_digest/vmd5.h =================================================================== --- lib/libvmod_digest/vmd5.h (revision 0) +++ lib/libvmod_digest/vmd5.h (revision 0) @@ -0,0 +1,51 @@ +/*- + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +Taken from: $FreeBSD: head/sys/sys/md5.h 156752 2006-03-15 19:47:12Z andre $ + */ + +#ifndef _VMD5_H_ +#define _VMD5_H_ + +#include + +#define MD5_BLOCK_LENGTH 64 +#define MD5_DIGEST_LENGTH 16 +#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) + +/* MD5 context. */ +typedef struct MD5Context { + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init (MD5_CTX *); +void MD5Update (MD5_CTX *, const void *, unsigned int); +void MD5Final (unsigned char [16], MD5_CTX *); +char * MD5End(MD5_CTX *, char *); +char * MD5File(const char *, char *); +char * MD5FileChunk(const char *, char *, off_t, off_t); +char * MD5Data(const void *, unsigned int, char *); +void MD5Test(void); + +#endif /* !_VMD5_H_ */ Index: lib/libvmod_digest/vmod_digest.c =================================================================== --- lib/libvmod_digest/vmod_digest.c (revision 0) +++ lib/libvmod_digest/vmod_digest.c (revision 0) @@ -0,0 +1,435 @@ +/*- + * + * VCL module (vmod) providing some hash / message digest functions + * + * Copyright (c) 2010 UPLEX, Nils Goroll + * All rights reserved. + * + * initially based upon vmod_std.c by Poul-Henning Kamp + * + * Author: Nils Goroll + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include "vrt.h" +#include "vsha256.h" +#include "vmd5.h" +#include "varnishapi.h" +#include "vmod_digest.h" + +/* + * common structure to keep results from all digests + * + * result must get filled up from lowest to highest bits + * thinking big-endian (rightmost) + * + */ + +#define DIGEST_RESULT_SIZE 32 +#define DIGEST_RESULT_SIZE_UINT32 (DIGEST_RESULT_SIZE * \ + sizeof(unsigned char) / sizeof(uint32_t)) + +struct digest_data { + unsigned magic; +#define DIGEST_MAGIC 0x0d12a0b2 + enum digest_type type; + void *state; + + union { + unsigned char uchar[DIGEST_RESULT_SIZE]; + uint32_t uint32[DIGEST_RESULT_SIZE_UINT32]; + } result; + size_t result_size; + + /* how to free state and myself */ + vmod_priv_free_f *free; +}; + +/* set to the largest context of all hashes */ + +#if 0 /* (sizeof(MD5_CTX) > sizeof(struct SHA256Context)) */ +#define DIGEST_DATA_SIZE \ + (sizeof(struct digest_data) + sizeof(MD5_CTX)) +#else +#define DIGEST_DATA_SIZE \ + (sizeof(struct digest_data) + sizeof(struct SHA256Context)) +#endif + +/* + * space will be allocated from sp->wrk->ws, unless buf is passed, + * which must be at least of size DIGEST_DATA_SIZE + * + */ +static struct digest_data * +digest_init(const enum digest_type type, struct sess *sp, void *buf) +{ + struct digest_data *data; + size_t sz; + + if (buf) { + data = buf; + buf += sizeof(struct digest_data); + } else { + CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); + data = (void *)WS_Alloc(sp->wrk->ws, sizeof(struct digest_data)); + } + + AN(data); + memset(data, 0, sizeof(struct digest_data)); + + switch (type) { + case DEFAULT: + case CRC32: + case PLAIN: + data->result_size = sizeof(uint32_t); + /* no data->state */ + goto done; + case RS: + sz = sizeof(uint32_t); + data->result_size = sz; + goto alloc_state; + case SHA256: + sz = sizeof(struct SHA256Context); + data->result_size = 32; + goto alloc_state; + case MD5: + sz = sizeof(MD5_CTX); + data->result_size = MD5_DIGEST_LENGTH; + goto alloc_state; + default: + INCOMPL(); + return (0); + } + + alloc_state: + if (buf) { + data->state = buf; + buf += sz; + } else { + data->state = WS_Alloc(sp->wrk->ws, sz); + } + AN(data->state); + memset(data->state, 0, sz); + + done: + data->magic = DIGEST_MAGIC; + return (data); +} + + +static void +digest_free(struct digest_data *d) +{ + AN(d); + + if (!d->free) + return; + + if (d->state) + d->free(d->state); + + d->free(d); +} + +#define VA_VSTRING_FOREACH(p, s, ap) \ + for (p = s; p != vrt_magic_string_end; p = va_arg(ap, const char *)) + +/* + * digest: run varargs trough digest alg and return digest_data + * + * + * data must have been intialized with digest_init + * + * We store the uint32_t based digest in network byte order, to allow using + * these checksums cross-platform (and big endian is "network byte order", so + * this seems most natural). + * + * see also: http://www.boost.org/doc/libs/1_39_0/libs/crc/crc.html: + * + * Note that the CRC has to be stored with the more-significant bytes first + * (big-endian). + * + * checked with http://www.lammertbies.nl/comm/info/crc-calculation.html + * http://www.pvlteam.com/doc/crc/testonline.aspx + */ + +static struct digest_data * +digest(const enum digest_type type, struct digest_data *data, + const char *s, va_list ap) +{ + const char *p; + uint32_t res; + + CHECK_OBJ_NOTNULL(data, DIGEST_MAGIC); + + switch (type) { + case DEFAULT: + case CRC32: + res = ntohl(data->result.uint32[0] ^ ~0U); + + VA_VSTRING_FOREACH(p, s, ap) + res = crc32(res, p, strlen(p)); + + data->result.uint32[0] = htonl(res ^ ~0U); + break; + case PLAIN: + res = ntohl(data->result.uint32[0]); + + VA_VSTRING_FOREACH(p, s, ap) + while (*p) + res += *p++; + + data->result.uint32[0] = htonl(res); + break; + case RS: + { + /* hash function from Robert Sedgwicks 'Algorithms in C' book */ + const uint32_t b = 378551; + uint32_t a = 63689; + + res = ntohl(data->result.uint32[0]); + + VA_VSTRING_FOREACH(p, s, ap) + while (*p) { + res = res * a + (*p++); + a *= b; + } + + data->result.uint32[0] = htonl(res); + break; + } + case SHA256: + { + struct SHA256Context *sha256 = data->state; + + SHA256_Init(sha256); + VA_VSTRING_FOREACH(p, s, ap) + SHA256_Update(sha256, p, strlen(p)); + SHA256_Final(data->result.uchar, sha256); + break; + } + case MD5: + { + MD5_CTX *md5 = data->state; + + MD5Init(md5); + VA_VSTRING_FOREACH(p, s, ap) + MD5Update(md5, p, strlen(p)); + MD5Final(data->result.uchar, md5); + break; + } + default: + INCOMPL(); + return (0); + } + return (data); +} + +static inline char +hexnibble(char c) +{ + return ((c > 9) ? ('A' + (c - 0xa)) : ('0' + c)); +} + +static inline char +hexnibblelc(char c) +{ + return ((c > 9) ? ('a' + (c - 0xa)) : ('0' + c)); +} + +/* get size for specified encoding */ + +static inline size_t +encode_l(const enum encoding_type enc, size_t src_size) +{ + switch (enc) { + case BASE64: + return (base64_encode_l(src_size)); + case HEX: + case HEXLC: + return (src_size * 2 + 1); + default: + INCOMPL(); + return (0); + } +} + +/* encode data with the specified encoding */ + +static char * +encode(const enum encoding_type enc, char *dst, size_t dst_size, + char *src, size_t src_size) +{ + char *p; + int i; + + switch (enc) { + case BASE64: + base64_encode(dst, dst_size, src, src_size); + break; + case HEX: + for (i = 0, p = dst; i < src_size; i++, p += 2) { + /* sprintf(p, "%02X", src[i]); */ + *p = hexnibble((src[i] & 0xf0) >> 4); + *(p+1) = hexnibble(src[i] & 0x0f); + } + *p = '\0'; + break; + case HEXLC: + for (i = 0, p = dst; i < src_size; i++, p += 2) { + /* sprintf(p, "%02x", src[i]); */ + *p = hexnibblelc((src[i] & 0xf0) >> 4); + *(p+1) = hexnibblelc(src[i] & 0x0f); + } + *p = '\0'; + break; + default: + INCOMPL(); + return (NULL); + } + return (dst); +} + + +/* + * digest_string, encode_string: + * + * Functions called from auto-generated vmod wrappers (see vmod_digest.py) + */ + + +char * +digest_string(const enum digest_type type, const enum encoding_type enc, + struct sess *sp, const char *s, va_list ap) +{ + /* alloc scratch for digest_data on stack */ + char buf[DIGEST_DATA_SIZE]; + struct digest_data *data = digest_init(type, sp, buf); + char *res; + size_t l; + + AN(data); + digest(type, data, s, ap); + l = encode_l(enc, data->result_size); + res = WS_Alloc(sp->wrk->ws, l); + if (!res) + return (NULL); + if (!encode(enc, res, l, data->result.uchar, data->result_size)) + return (NULL); + + /* + * not strictly needed because alloc'd from stack, calling free for + * completeness + */ + digest_free(data); + + return (res); +} + +char * +encode_string(const enum encoding_type enc, + struct sess *sp, const char *s, va_list ap) +{ + const char *p; + size_t l; + unsigned u; + char *b, *e; + + + /* get memory. skeleton taken from vmoc_std.c:vmod_updown() */ + u = WS_Reserve(sp->wrk->ws, 0); + e = b = sp->wrk->ws->f; + e += u; + + switch (enc) { + case BASE64: + l = base64_encode_string(b, u, vrt_magic_string_end, s, ap); + if (l < 0) + return (NULL); + b += l; + break; + case HEX: + case HEXLC: + p = s; + while (p != vrt_magic_string_end && b < e) { + if (p != NULL) { + for (; (b < e - 1) && *p != '\0'; p++) { + *b++ = (enc == HEX) ? + hexnibble((*p & 0xf0) >> 4) : + hexnibblelc((*p & 0xf0) >> 4); + *b++ = (enc == HEX) ? + hexnibble(*p & 0x0f) : + hexnibblelc(*p & 0x0f); + } + } + p = va_arg(ap, const char *); + } + if (b < e) + *b = '\0'; + b++; + break; + default: + INCOMPL(); + return (NULL); + } + + if (b > e) { + WS_Release(sp->wrk->ws, 0); + return (NULL); + } else { + e = b; + b = sp->wrk->ws->f; + WS_Release(sp->wrk->ws, e - b); + return (b); + } +} + + +int +init_function(struct vmod_priv *priv, const struct VCL_conf *cfg) +{ + base64_init(); + MD5Test(); + + /* for hexnibble */ + assert(('b' - 'a' == 1) && ('B' - 'A' == 1) && ('2' - '1' == 1)); + + /* for digest (result) size */ + assert((DIGEST_RESULT_SIZE >= SHA256_LEN) && + (DIGEST_RESULT_SIZE >= MD5_DIGEST_LENGTH) && + (DIGEST_RESULT_SIZE >= sizeof(uint32_t))); + + /* for context size allocation */ + assert((DIGEST_DATA_SIZE >= (sizeof(struct digest_data) + sizeof(struct SHA256Context))) && + (DIGEST_DATA_SIZE >= (sizeof(struct digest_data) + sizeof(struct MD5Context))) && + (DIGEST_DATA_SIZE >= (sizeof(struct digest_data) + sizeof(uint32_t)))); + + return (0); +} Index: lib/libvmod_digest/Makefile.am =================================================================== --- lib/libvmod_digest/Makefile.am (revision 0) +++ lib/libvmod_digest/Makefile.am (revision 0) @@ -0,0 +1,28 @@ +# $Id$ + +INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include + +lib_LTLIBRARIES = libvmod_digest.la + +libvmod_digest_la_LDFLAGS = -version-info 1:0:0 + +libvmod_digest_la_LIBADD = $(top_builddir)/lib/libvarnishapi/libvarnishapi.la + +libvmod_digest_la_SOURCES = \ + crc32.c \ + vmd5.h \ + vmd5.c \ + vcc_if.c \ + vmod_digest.h \ + vmod_digest.c \ + vmod_digest_if.c + +vcc_if.c vcc_if.h: vmod.py vmod_digest.h vmod.vcc + @PYTHON@ vmod.py vmod.vcc + +vmod_digest.h vmod_digest_if.c vmod.vcc: vmod_digest.py + @PYTHON@ vmod_digest.py + +EXTRA_DIST = vmod.py vmod_digest.py + +CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h $(builddir)/vmod_digest.h $(builddir)/vmod_digest_if.c $(builddir)/vmod.vcc Index: lib/libvmod_digest/vmod_digest.py =================================================================== --- lib/libvmod_digest/vmod_digest.py (revision 0) +++ lib/libvmod_digest/vmod_digest.py (revision 0) @@ -0,0 +1,165 @@ +#!/usr/local/bin/python +#- +# Copyright (c) 2010 UPLEX, Nils Goroll +# All rights reserved. +# +# Author: Nils Goroll +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# Read the vmod.spec file and produce the vmod.h and vmod.c files. +# +# vmod.h contains the prototypes for the published functions, the module +# C-code should include this file to ensure type-consistency. +# +# vmod.c contains the symbols which VCC and varnishd will use to access +# the module: A structure of properly typed function pointers, the +# size of this structure in bytes, and the definition of the structure +# as a string, suitable for inclusion in the C-source of the compile VCL +# program. +# +# $Id$ + +digest_types = [ + 'DEFAULT', + 'CRC32', + 'SHA256', + 'PLAIN', + 'RS', + 'MD5' +] + +encoding_types = [ + 'BASE64', + 'HEX', + 'HEXLC' +] + +h = open("vmod_digest.h", "w") +c = open("vmod_digest_if.c", "w") +vcc = open("vmod.vcc", "w") +vtc = open("vtc_snippet","w") + +h.write("/* THIS FILE IS AUTO-GENERATED. don't edit */\n") + +c.write("/* THIS FILE IS AUTO-GENERATED. don't edit */\n") +c.write("#include \"vmod_digest.h\"\n") + +vcc.write("# THIS FILE IS AUTO-GENERATED. don't edit\n") +vtc.write("# THIS FILE IS AUTO-GENERATED. don't edit\n") +vtc.write("# use this code snippet for the vtc test\n") + +h.write(""" + +#include +#include "../../bin/varnishd/cache.h" + +/* from libvarnish/crc32.c */ +uint32_t crc32(uint32_t crc, const void *p1, unsigned l); +uint32_t crc32_l(const void *p1, unsigned l); +""") + +h.write("enum digest_type {\n\t" + + ",\n\t".join(digest_types) + + "\n};\n") +h.write("enum encoding_type {\n\t" + + ",\n\t".join(encoding_types) + + "\n};\n") +h.write("\n") +h.write(""" +char *digest_string(const enum digest_type type, const enum encoding_type enc, + struct sess *sp, const char *s, va_list ap); +char *encode_string(const enum encoding_type enc, + struct sess *sp, const char *s, va_list ap); +""") +h.write("\n") + +vcc.write(""" +Module digest +Init init_function +""") + +# output wrappers for "digest&encode" functions +# +# Function STRING digest_encoder(STRING_LIST) +# const char *vmod_digest_encoder_(sp ...) + +for d in digest_types: + for e in encoding_types: + vcc.write("Function STRING " + + d.lower() + "_" + e.lower() + + "(STRING_LIST)\n") + h.write("const char *" + +"vmod_" + d.lower() + "_" + e.lower() + "(struct sess *sp, const char *s, ...);\n") + c.write(""" +const char * +vmod_""" + d.lower() + "_" + e.lower() + """(struct sess *sp, const char *s, ...) +{ + const char *p; + va_list ap; + + va_start(ap, s); + p = digest_string(""" + ", ".join([d, e, "sp", "s", "ap"]) + """); + va_end(ap); + return (p); +} +""") + vtc.write("\t\tset resp.http." + + d.lower() + "-" + e.lower() + + " = digest." + + d.lower() + "_" + e.lower() + + """("123" + "456");"""+ "\n") + +# output wrappers for "encode" functions +# +# Function STRING digest_encoder(STRING_LIST) +# const char *vmod_digest_encoder_(sp ...) + +vtc.write("\n#\t\tencodings (no digest)\n") + +for e in encoding_types: + vcc.write("Function STRING encode_" + e.lower() + "(STRING_LIST)\n") + h.write("const char *" + + "vmod_encode_" + e.lower() + "(struct sess *sp, const char *s, ...);\n") + c.write(""" +const char * +vmod_encode_""" + e.lower() + """(struct sess *sp, const char *s, ...) +{ + const char *p; + va_list ap; + + va_start(ap, s); + p = encode_string(""" + ", ".join([e, "sp", "s", "ap"]) + """); + va_end(ap); + return (p); +} +""") + vtc.write("\t\tset resp.http." + + e.lower() + + " = digest.encode_" + + e.lower() + + """("123" + "456");"""+ "\n") + +h.close +c.close +vcc.close +vtc.close Index: lib/libvmod_digest/vmd5.c =================================================================== --- lib/libvmod_digest/vmd5.c (revision 0) +++ lib/libvmod_digest/vmd5.c (revision 0) @@ -0,0 +1,387 @@ +/* + * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + * + * This code is the same as the code published by RSA Inc. It has been + * edited for clarity and style only. + * + * From: $FreeBSD: head/lib/libmd/md5c.c head/lib/libmd/sha256c.c 154479 2006-01-17 15:35:57Z phk $ + */ + +#include "config.h" + +#include "svnid.h" +SVNID("$Id$") + +#include +#include +#include + +#ifdef HAVE_ENDIAN_H +#include +#define VBYTE_ORDER __BYTE_ORDER +#define VBIG_ENDIAN __BIG_ENDIAN +#endif +#ifdef HAVE_SYS_ENDIAN_H +#include +#define VBYTE_ORDER _BYTE_ORDER +#define VBIG_ENDIAN _BIG_ENDIAN +#endif + +#include "libvarnish.h" +#include "vmd5.h" + +static void MD5Transform(uint32_t [4], const unsigned char [64]); + +#if !defined(VBYTE_ORDER) || (VBYTE_ORDER == LITTLE_ENDIAN) + +/* assume little endian if not defined */ + +#define Encode memcpy +#define Decode memcpy + +#else + +/* + * Encodes input (uint32_t) into output (unsigned char). Assumes len is + * a multiple of 4. + */ + +static void +Encode (unsigned char *output, uint32_t *input, unsigned int len) +{ + unsigned int i; + uint32_t *op = (uint32_t *)output; + + for (i = 0; i < len / 4; i++) + vle32enc(op[i], input[i]); +} + +/* + * Decodes input (unsigned char) into output (uint32_t). Assumes len is + * a multiple of 4. + */ + +static void +Decode (uint32_t *output, const unsigned char *input, unsigned int len) +{ + unsigned int i; + const uint32_t *ip = (const uint32_t *)input; + + for (i = 0; i < len / 4; i++) + output[i] = vle32dec(ip[i]); +} +#endif + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* + * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ + +void +MD5Init (context) + MD5_CTX *context; +{ + + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* + * MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ + +void +MD5Update (context, in, inputLen) + MD5_CTX *context; + const void *in; + unsigned int inputLen; +{ + unsigned int i, idx, partLen; + const unsigned char *input = in; + + /* Compute number of bytes mod 64 */ + idx = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((uint32_t)inputLen << 3)) + < ((uint32_t)inputLen << 3)) + context->count[1]++; + context->count[1] += ((uint32_t)inputLen >> 29); + + partLen = 64 - idx; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + memcpy((void *)&context->buffer[idx], (const void *)input, + partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + idx = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy ((void *)&context->buffer[idx], (const void *)&input[i], + inputLen-i); +} + +/* + * MD5 padding. Adds padding followed by original length. + */ + +void +MD5Pad (context) + MD5_CTX *context; +{ + unsigned char bits[8]; + unsigned int idx, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + idx = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (idx < 56) ? (56 - idx) : (120 - idx); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); +} + +/* + * MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ + +void +MD5Final (digest, context) + unsigned char digest[16]; + MD5_CTX *context; +{ + /* Do padding. */ + MD5Pad (context); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset ((void *)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ + +static void +MD5Transform (state, block) + uint32_t state[4]; + const unsigned char block[64]; +{ + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset ((void *)x, 0, sizeof (x)); +} + +/* + * A few test-vectors, just in case - taken from vsha256.c and modified + */ + +static const struct md5test { + const char *input; + const unsigned char output[16]; +} md5test[] = { + { "", + { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e + } + }, + { "message digest", + { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, + 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 + } + }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, + 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f + } + }, + { NULL } +}; + +void +MD5Test(void) +{ + MD5_CTX c; + const struct md5test *p; + unsigned char o[MD5_DIGEST_LENGTH]; + + for (p = md5test; p->input != NULL; p++) { + MD5Init(&c); + MD5Update(&c, p->input, strlen(p->input)); + MD5Final(o, &c); + assert(!memcmp(o, p->output, MD5_DIGEST_LENGTH)); + } +} Index: lib/libvmod_digest/crc32.c =================================================================== --- lib/libvmod_digest/crc32.c (revision 0) +++ lib/libvmod_digest/crc32.c (revision 0) @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006-2008 Linpro AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + * + * This CRC32 implementation is in the public domain. + * + * This implementation seems to be a "vanilla" CRC32 with poly 04C11DB7 + * according to http://regregex.bbcmicro.net/crc-catalogue.htm + * + * Note that the result needs to be converted to network byte order for cross + * platform use! + */ + +#include "config.h" + +#include "libvarnish.h" + + +/*--------------------------------------------------------------------*/ + +static const uint32_t crc32bits[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t +crc32(uint32_t crc, const void *p1, unsigned l) +{ + const unsigned char *p; + + for (p = (const unsigned char*)p1; l-- > 0; p++) + crc = (crc >> 8) ^ crc32bits[(crc ^ *p) & 0xff]; + return (crc); +} + +uint32_t +crc32_l(const void *p1, unsigned l) +{ + uint32_t crc; + + crc = crc32(~0U, p1, l); + return (crc ^ ~0U); +} Index: bin/varnishtest/tests/m00003.vtc =================================================================== --- bin/varnishtest/tests/m00003.vtc (revision 0) +++ bin/varnishtest/tests/m00003.vtc (revision 0) @@ -0,0 +1,77 @@ +# $Id$ + +test "Test digest vmod" + +server s1 { + rxreq + txresp +} -start + +varnish v1 -vcl+backend { + import digest from "${topbuild}/lib/libvmod_digest/.libs/libvmod_digest.so.1" ; + + sub vcl_deliver { + set resp.http.1base64 = digest.encode_base64("123" + "456" + "" + "Z"); + set resp.http.1hex = digest.encode_hex("123" + "456" + "" + "Z"); + set resp.http.1hexlc = digest.encode_hexlc("123" + "456" + "" + "Z"); + + + ### contents of lib/libvmod_digest/vtc_snippet + set resp.http.default-base64 = digest.default_base64("123" + "456"); + set resp.http.default-hex = digest.default_hex("123" + "456"); + set resp.http.default-hexlc = digest.default_hexlc("123" + "456"); + set resp.http.crc32-base64 = digest.crc32_base64("123" + "456"); + set resp.http.crc32-hex = digest.crc32_hex("123" + "456"); + set resp.http.crc32-hexlc = digest.crc32_hexlc("123" + "456"); + set resp.http.sha256-base64 = digest.sha256_base64("123" + "456"); + set resp.http.sha256-hex = digest.sha256_hex("123" + "456"); + set resp.http.sha256-hexlc = digest.sha256_hexlc("123" + "456"); + set resp.http.plain-base64 = digest.plain_base64("123" + "456"); + set resp.http.plain-hex = digest.plain_hex("123" + "456"); + set resp.http.plain-hexlc = digest.plain_hexlc("123" + "456"); + set resp.http.rs-base64 = digest.rs_base64("123" + "456"); + set resp.http.rs-hex = digest.rs_hex("123" + "456"); + set resp.http.rs-hexlc = digest.rs_hexlc("123" + "456"); + set resp.http.md5-base64 = digest.md5_base64("123" + "456"); + set resp.http.md5-hex = digest.md5_hex("123" + "456"); + set resp.http.md5-hexlc = digest.md5_hexlc("123" + "456"); + +# encodings (no digest) + set resp.http.base64 = digest.encode_base64("123" + "456"); + set resp.http.hex = digest.encode_hex("123" + "456"); + set resp.http.hexlc = digest.encode_hexlc("123" + "456"); + } +} -start + +client c1 { + txreq + rxresp + expect resp.status == 200 + + expect resp.http.1base64 == "MTIzNDU2Wg==" + expect resp.http.1hex == "3132333435365A" + expect resp.http.1hexlc == "3132333435365a" + + expect resp.http.default-base64 == "CXLTYQ==" + expect resp.http.default-hex == "0972D361" + expect resp.http.default-hexlc == "0972d361" + expect resp.http.crc32-base64 == "CXLTYQ==" + expect resp.http.crc32-hex == "0972D361" + expect resp.http.crc32-hexlc == "0972d361" + expect resp.http.sha256-base64 == "jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=" + expect resp.http.sha256-hex == "8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92" + expect resp.http.sha256-hexlc == "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92" + expect resp.http.plain-base64 == "AAABNQ==" + expect resp.http.plain-hex == "00000135" + expect resp.http.plain-hexlc == "00000135" + expect resp.http.rs-base64 == "OPSd2Q==" + expect resp.http.rs-hex == "38F49DD9" + expect resp.http.rs-hexlc == "38f49dd9" + expect resp.http.md5-base64 == "4QrcOUm6Wau+VuBX8g+IPg==" + expect resp.http.md5-hex == "E10ADC3949BA59ABBE56E057F20F883E" + expect resp.http.md5-hexlc == "e10adc3949ba59abbe56e057f20f883e" + expect resp.http.base64 == "MTIzNDU2" + expect resp.http.hex == "313233343536" + expect resp.http.hexlc == "313233343536" + +} -run Index: bin/varnishtest/tests/m00004.vtc =================================================================== --- bin/varnishtest/tests/m00004.vtc (revision 0) +++ bin/varnishtest/tests/m00004.vtc (revision 0) @@ -0,0 +1,49 @@ +# $Id$ + +test "Test digest vmod rst examples" + +server s1 { + rxreq + txresp +} -start + +varnish v1 -vcl+backend { + import digest from "${topbuild}/lib/libvmod_digest/.libs/libvmod_digest.so.1" ; + + sub vcl_recv { + # Simplistic check for basic HTTP authentication in Varnish + + if (req.http.Authorization != ("Basic " + + digest.encode_base64("user:password"))) { + error (401, "don't know the magic credentials?"); + } + + # Check a digest of something else + if (req.http.X-Identity != + digest.md5_hexlc("secret" + client.ip)) { + error (403, "You are not on my guest list"); + } + } +} -start + +client c1 { + txreq + rxresp + expect resp.status == 401 +} -run + +client c2 { + txreq \ + -hdr "Authorization: Basic dXNlcjpwYXNzd29yZA==" + rxresp + expect resp.status == 403 +} -run + +client c3 { + # identity is md5("secret127.0.0.1") + txreq \ + -hdr "Authorization: Basic dXNlcjpwYXNzd29yZA==" \ + -hdr "X-Identity: 44fad189cc8fb13f321ba835aeb8d579" + rxresp + expect resp.status == 200 +} -run