/*
** 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <tcl.h>
char *getPwdKey(char *keybuf);
//#include <sqlite3.h>
/*
** 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 <b>decode64</b> 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<nData; i+=3){
z64[n++] = zBase[ (zData[i]>>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<nData ){
z64[n++] = zBase[ (zData[i]>>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<nData ){
z64[n++] = zBase[ (zData[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<n64; i+=4){
a = trans[z64[i] & 0x7f];
b = trans[z64[i+1] & 0x7f];
c = trans[z64[i+2] & 0x7f];
d = trans[z64[i+3] & 0x7f];
zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03);
zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f);
zData[j++] = ((c<<6) & 0xc0) | (d & 0x3f);
}
if( i+2<n64 ){
a = trans[z64[i] & 0x7f];
b = trans[z64[i+1] & 0x7f];
c = trans[z64[i+2] & 0x7f];
zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03);
zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f);
}else if( i+1<n64 ){
a = trans[z64[i] & 0x7f];
b = trans[z64[i+1] & 0x7f];
zData[j++] = ((a<<2) & 0xfc) | ((b>>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;i<repeat;i++) {
next_random_number = next_random_number * 1103515245 + 12345;
}
return ((unsigned)(next_random_number/256) % 256);
}
static void rc4_randomness(int N, void *pBuf) {
unsigned char *zBuf = pBuf;
while( N-- ){
*(zBuf++) = randomByte();
}
}
static int Rc4SeedObjCmd(
void *NotUsed,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 2, objv, "SEEDINT");
return TCL_ERROR;
}
if(Tcl_GetWideIntFromObj(interp,objv[1],&next_random_number)) {
return TCL_ERROR;
}
return TCL_OK;
}
/*
** Usage: rc4encrypt PASSWORD TEXT
**
** Encrypt TEXT using PASSWORD and a random nonce. Encode the result
** as a single token using base64.
*/
static int Rc4EncryptObjCmd(
void *NotUsed,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zPasswd;
int nPasswd;
char *zIn;
int nIn;
char *zBuf;
char *zOut;
int nOut;
char zKey[256];
Rc4Codec codec;
extern void sqliteRandomness(int,void*);
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 2, objv, "PASSWORD TEXT");
return TCL_ERROR;
}
zPasswd = Tcl_GetStringFromObj(objv[1], &nPasswd);
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[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;
}