/* ** Implementation of an RC4 codec for TCL. */ const char rc4_c_version[] = "$Header: /readi/code/tobe/rc4.c,v 1.6 2007/05/08 21:53:56 drh Exp $"; #include #include #include #include char *getPwdKey(char *keybuf); //#include /* ** An RC4 codec is an instance of the following structure. */ typedef struct Rc4Codec Rc4Codec; struct Rc4Codec { unsigned char i, j; unsigned char s[256]; }; static Tcl_WideInt next_random_number = 1; /* ** Initialize an RC4 codec with the given key sequence. */ static void rc4_init(Rc4Codec *p, int nByte, unsigned char *pKey){ int k, l; unsigned char i, j, t, *s; i = j = p->i = p->j = 0; s = p->s; for(k=0; k<256; k++){ s[k] = k; } l = 0; for(k=0; k<256; k++){ t = s[k]; j += t + pKey[l++]; if( l>=nByte ) l = 0; s[k] = s[j]; s[j] = t; } /* Discard the first 1024 bytes of key stream to thwart the ** Fluhrer-Mantin-Shamir attack. */ for(k=0; k<1024; k++){ t = s[++i]; j += t; s[i] = s[j]; s[j] = t; } p->j = j; } /* ** Encode/Decode nBytes bytes of traffic using the given codec. */ static void rc4_coder(Rc4Codec *p, int nByte, unsigned char *pData){ register unsigned char ti, tj, i, j, *s; s = p->s; i = p->i; j = p->j; while( nByte-->0 ){ ti = s[++i]; j += ti; tj = s[i] = s[j]; s[j] = ti; tj += ti; *(pData++) ^= s[tj]; } p->i = i; p->j = j; } /* ** Usage: NAME TEXT ** ** There is a separate TCL command created for each rc4 codec instance. ** This is the implementation of that command. Apply the codec to the ** input and return the results. */ static int CodecObjCmd( void *pCodec, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ unsigned char *data; int nData; Tcl_Obj *pResult; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, "TEXT"); return TCL_ERROR; } data = Tcl_GetByteArrayFromObj(objv[1], &nData); pResult = Tcl_NewByteArrayObj(data, nData); data = Tcl_GetByteArrayFromObj(pResult, 0); rc4_coder((Rc4Codec*)pCodec, nData, data); Tcl_SetObjResult(interp, pResult); return TCL_OK; } /* ** Destructor for codec. */ static void CodecDestructor(void *pCodec){ Tcl_Free(pCodec); } /* ** Usage: rc4 NAME PASSWORD ** ** Create a new rc4 codec called NAME and initialized using PASSWORD. */ static int Rc4ObjCmd( void *NotUsed, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Rc4Codec *pCodec; const char *zName; unsigned char *pKey; int nKey; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "NAME PASSWORD"); return TCL_ERROR; } zName = Tcl_GetStringFromObj(objv[1], 0); pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); pCodec = (Rc4Codec*)Tcl_Alloc( sizeof(*pCodec) ); rc4_init(pCodec, nKey, pKey); Tcl_CreateObjCommand(interp, zName, CodecObjCmd, pCodec, CodecDestructor); return TCL_OK; } /* ** The characters used for HTTP base64 encoding. */ static const unsigned char zBase[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"; /* ** Encode a string using a base-64 encoding. ** The encoding can be reversed using the decode64 function. ** ** Space to hold the result comes from Tcl_Alloc(). */ static char *encode64(const char *zData, int nData, int *pnOut){ char *z64; int i, n; if( nData<=0 ){ nData = strlen(zData); } z64 = Tcl_Alloc( (nData*4)/3 + 6 ); for(i=n=0; i+2>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; z64[n++] = zBase[ zData[i+2] & 0x3f ]; } if( i+1>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) ]; }else if( i>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) ]; } z64[n] = 0; if( pnOut ) *pnOut = n; return z64; } /* ** This function treats its input as a base-64 string and returns the ** decoded value of that string. Characters of input that are not ** valid base-64 characters (such as spaces and newlines) are ignored. ** ** Space to hold the decoded string is obtained from Tcl_Alloc(). */ char *decode64(const char *z64, int n64, int *pnOut){ char *zData; int i, j; int a, b, c, d; static int isInit = 0; static int trans[128]; if( !isInit ){ for(i=0; i<128; i++){ trans[i] = 0; } for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } isInit = 1; } if( n64<0 ){ n64 = strlen(z64); } while( n64>0 && z64[n64-1]=='=' ) n64--; zData = Tcl_Alloc( (n64*3)/4 + 4 ); for(i=j=0; i+3>4) & 0x03); zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); zData[j++] = ((c<<6) & 0xc0) | (d & 0x3f); } if( i+2>4) & 0x03); zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); }else if( i+1>4) & 0x03); } zData[j] = 0; if( pnOut ) *pnOut = j; return zData; } static unsigned char randomByte(void) { char i; /* RAND_MAX assumed to be 256 */ char repeat=(next_random_number % 10)+2; for(i=0;i252 ) nPasswd = 252; memcpy(&zKey[4], zPasswd, nPasswd); rc4_init(&codec, nPasswd+4, (unsigned char*)zKey); zIn = Tcl_GetStringFromObj(objv[2], &nIn); zBuf = Tcl_Alloc( nIn + 5 ); memcpy(zBuf, zKey, 4); memcpy(&zBuf[4], zIn, nIn); rc4_coder(&codec, nIn, (unsigned char*)&zBuf[4]); zOut = encode64(zBuf, nIn+4, &nOut); Tcl_SetObjResult(interp, Tcl_NewStringObj(zOut, nOut)); Tcl_Free((char *)zOut); Tcl_Free((char *)zBuf); return TCL_OK; } /* ** Usage: rc4decrypt PASSWORD CYPHERTEXT ** ** Decrypt CYPHERTEXT using PASSWORD and a nonce found at the beginning of ** the cyphertext. The cyphertext is base64 encoded. */ static int Rc4DecryptObjCmd( void *NotUsed, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zPasswd; int nPasswd; char *zIn; int nIn; char *zOut; int nOut; char zKey[256]; Rc4Codec codec; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "PASSWORD TEXT"); return TCL_ERROR; } zPasswd = Tcl_GetStringFromObj(objv[1], &nPasswd); zIn = Tcl_GetStringFromObj(objv[2], &nIn); zOut = decode64(zIn, nIn, &nOut); if( nOut<4 ){ return TCL_OK; } memcpy(zKey, zOut, 4); if( nPasswd>252 ) nPasswd = 252; memcpy(&zKey[4], zPasswd, nPasswd); rc4_init(&codec, nPasswd+4, (unsigned char*)zKey); rc4_coder(&codec, nOut-4, (unsigned char*)&zOut[4]); Tcl_SetObjResult(interp, Tcl_NewStringObj(&zOut[4], nOut-4)); Tcl_Free(zOut); return TCL_OK; } /* ** Usage: source_encrypt TEXT ** ** Encrypt TEXT using compiled in PASSWORD and a random nonce. Encode the result ** as a single token using base64. */ static int Rc4EncryptSourceObjCmd( void *NotUsed, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ char zPasswd[32]; int nPasswd; char *zIn; int nIn; char *zBuf; char *zOut; int nOut; char zKey[256]; Rc4Codec codec; extern void sqliteRandomness(int,void*); if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, "TEXT"); return TCL_ERROR; } getPwdKey(zPasswd); nPasswd=strlen(zPasswd); rc4_randomness(4, zKey); if( nPasswd>252 ) nPasswd = 252; memcpy(&zKey[4], zPasswd, nPasswd); rc4_init(&codec, nPasswd+4, (unsigned char*)zKey); zIn = Tcl_GetStringFromObj(objv[1], &nIn); zBuf = Tcl_Alloc( nIn + 5 ); memcpy(zBuf, zKey, 4); memcpy(&zBuf[4], zIn, nIn); rc4_coder(&codec, nIn, (unsigned char*)&zBuf[4]); zOut = encode64(zBuf, nIn+4, &nOut); Tcl_SetObjResult(interp, Tcl_NewStringObj(zOut, nOut)); Tcl_Free((char *)zOut); Tcl_Free((char *)zBuf); return TCL_OK; } /* ** Usage: source_decrypt CYPHERTEXT ** ** Decrypt CYPHERTEXT using compiled in PASSWORD and a nonce ** found at the beginning of ** the cyphertext. The cyphertext is base64 encoded. */ static int Rc4DecryptSourceObjCmd( void *NotUsed, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ char zPasswd[32]; int nPasswd; char *zIn; int nIn; char *zOut; int nOut; char zKey[256]; Rc4Codec codec; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, "TEXT"); return TCL_ERROR; } getPwdKey(zPasswd); nPasswd=strlen(zPasswd); zIn = Tcl_GetStringFromObj(objv[1], &nIn); zOut = decode64(zIn, nIn, &nOut); if( nOut<4 ){ return TCL_OK; } memcpy(zKey, zOut, 4); if( nPasswd>252 ) nPasswd = 252; memcpy(&zKey[4], zPasswd, nPasswd); rc4_init(&codec, nPasswd+4, (unsigned char*)zKey); rc4_coder(&codec, nOut-4, (unsigned char*)&zOut[4]); Tcl_SetObjResult(interp, Tcl_NewStringObj(&zOut[4], nOut-4)); Tcl_Free(zOut); return TCL_OK; } /* ** Usage: eval_decrypt CYPHERTEXT ** ** Decrypt CYPHERTEXT using compiled in PASSWORD and a nonce ** found at the beginning of ** the cyphertext. The cyphertext is base64 encoded. */ static int Rc4DecryptEvalObjCmd( void *NotUsed, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ char zPasswd[32]; int nPasswd; char *zIn; int nIn; char *zOut; int nOut; char zKey[256]; Rc4Codec codec; Tcl_Obj *cleartext; int code=TCL_OK; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, "TEXT"); return TCL_ERROR; } getPwdKey(zPasswd); nPasswd=strlen(zPasswd); zIn = Tcl_GetStringFromObj(objv[1], &nIn); zOut = decode64(zIn, nIn, &nOut); if( nOut<4 ){ return TCL_OK; } memcpy(zKey, zOut, 4); if( nPasswd>252 ) nPasswd = 252; memcpy(&zKey[4], zPasswd, nPasswd); rc4_init(&codec, nPasswd+4, (unsigned char*)zKey); rc4_coder(&codec, nOut-4, (unsigned char*)&zOut[4]); cleartext=Tcl_NewStringObj(&zOut[4], nOut-4); Tcl_IncrRefCount(cleartext); code=Tcl_EvalObjEx(interp,cleartext,NULL); Tcl_DecrRefCount(cleartext); Tcl_Free(zOut); return code; } /* ** Initialize the rc4 codec subsystem. */ DLLEXPORT int Rc4_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "rc4", Rc4ObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "rc4seed", Rc4SeedObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "rc4encrypt", Rc4EncryptObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "rc4decrypt", Rc4DecryptObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "source_encrypt", Rc4EncryptSourceObjCmd, 0, 0); //Tcl_CreateObjCommand(interp, "source_decrypt", Rc4DecryptSourceObjCmd, 0, 0); Tcl_CreateObjCommand(interp, "eval_decrypt", Rc4DecryptEvalObjCmd, 0, 0); return TCL_OK; }