388 lines
8.3 KiB
C
388 lines
8.3 KiB
C
/*
|
|
blowfish.c -- handles:
|
|
encryption and decryption of passwords
|
|
|
|
The first half of this is very lightly edited from public domain
|
|
sourcecode. For simplicity, this entire module will remain public
|
|
domain.
|
|
*/
|
|
|
|
#include "blowfish.h"
|
|
#include "bf_tab.h" /* P-box P-array, S-box */
|
|
|
|
#define BOXES 3
|
|
|
|
/* #define S(x,i) (bf_S[i][x.w.byte##i]) */
|
|
#define S0(x) (bf_S[0][x.w.byte0])
|
|
#define S1(x) (bf_S[1][x.w.byte1])
|
|
#define S2(x) (bf_S[2][x.w.byte2])
|
|
#define S3(x) (bf_S[3][x.w.byte3])
|
|
#define bf_F(x) (((S0(x) + S1(x)) ^ S2(x)) + S3(x))
|
|
#define ROUND(a,b,n) (a.word ^= bf_F(b) ^ bf_P[n])
|
|
|
|
/* keep a set of rotating P & S boxes */
|
|
static struct box_t {
|
|
UWORD_32bits *P;
|
|
UWORD_32bits **S;
|
|
char key[81];
|
|
char keybytes;
|
|
time_t lastuse;
|
|
} blowbox[BOXES];
|
|
|
|
/*
|
|
static UWORD_32bits bf_P[bf_N+2];
|
|
static UWORD_32bits bf_S[4][256];
|
|
*/
|
|
|
|
static UWORD_32bits *bf_P;
|
|
static UWORD_32bits **bf_S;
|
|
|
|
static char blowfish_version[] = "BitchX blowfish encryption module v1.0";
|
|
|
|
static void blowfish_encipher (UWORD_32bits * xl, UWORD_32bits * xr)
|
|
{
|
|
union aword Xl;
|
|
union aword Xr;
|
|
|
|
Xl.word = *xl;
|
|
Xr.word = *xr;
|
|
|
|
Xl.word ^= bf_P[0];
|
|
ROUND(Xr, Xl, 1);
|
|
ROUND(Xl, Xr, 2);
|
|
ROUND(Xr, Xl, 3);
|
|
ROUND(Xl, Xr, 4);
|
|
ROUND(Xr, Xl, 5);
|
|
ROUND(Xl, Xr, 6);
|
|
ROUND(Xr, Xl, 7);
|
|
ROUND(Xl, Xr, 8);
|
|
ROUND(Xr, Xl, 9);
|
|
ROUND(Xl, Xr, 10);
|
|
ROUND(Xr, Xl, 11);
|
|
ROUND(Xl, Xr, 12);
|
|
ROUND(Xr, Xl, 13);
|
|
ROUND(Xl, Xr, 14);
|
|
ROUND(Xr, Xl, 15);
|
|
ROUND(Xl, Xr, 16);
|
|
Xr.word ^= bf_P[17];
|
|
|
|
*xr = Xl.word;
|
|
*xl = Xr.word;
|
|
}
|
|
|
|
static void blowfish_decipher (UWORD_32bits * xl, UWORD_32bits * xr)
|
|
{
|
|
union aword Xl;
|
|
union aword Xr;
|
|
|
|
Xl.word = *xl;
|
|
Xr.word = *xr;
|
|
|
|
Xl.word ^= bf_P[17];
|
|
ROUND(Xr, Xl, 16);
|
|
ROUND(Xl, Xr, 15);
|
|
ROUND(Xr, Xl, 14);
|
|
ROUND(Xl, Xr, 13);
|
|
ROUND(Xr, Xl, 12);
|
|
ROUND(Xl, Xr, 11);
|
|
ROUND(Xr, Xl, 10);
|
|
ROUND(Xl, Xr, 9);
|
|
ROUND(Xr, Xl, 8);
|
|
ROUND(Xl, Xr, 7);
|
|
ROUND(Xr, Xl, 6);
|
|
ROUND(Xl, Xr, 5);
|
|
ROUND(Xr, Xl, 4);
|
|
ROUND(Xl, Xr, 3);
|
|
ROUND(Xr, Xl, 2);
|
|
ROUND(Xl, Xr, 1);
|
|
Xr.word ^= bf_P[0];
|
|
|
|
*xl = Xr.word;
|
|
*xr = Xl.word;
|
|
}
|
|
|
|
static void blowfish_init (const char *key, short keybytes)
|
|
{
|
|
int i, j, bx;
|
|
time_t lowest;
|
|
UWORD_32bits data;
|
|
UWORD_32bits datal;
|
|
UWORD_32bits datar;
|
|
union aword temp;
|
|
|
|
/* is buffer already allocated for this? */
|
|
for (i = 0; i < BOXES; i++)
|
|
if (blowbox[i].P != NULL)
|
|
{
|
|
if ((blowbox[i].keybytes == keybytes) &&
|
|
(strncmp((char *) (blowbox[i].key), key, keybytes) == 0))
|
|
{
|
|
blowbox[i].lastuse = now;
|
|
bf_P = blowbox[i].P;
|
|
bf_S = blowbox[i].S;
|
|
return;
|
|
}
|
|
}
|
|
/* no pre-allocated buffer: make new one */
|
|
/* set 'bx' to empty buffer */
|
|
bx = (-1);
|
|
for (i = 0; i < BOXES; i++)
|
|
{
|
|
if (blowbox[i].P == NULL)
|
|
{
|
|
bx = i;
|
|
i = BOXES + 1;
|
|
}
|
|
}
|
|
if (bx < 0)
|
|
{
|
|
/* find oldest */
|
|
lowest = now;
|
|
for (i = 0; i < BOXES; i++)
|
|
if (blowbox[i].lastuse <= lowest)
|
|
{
|
|
lowest = blowbox[i].lastuse;
|
|
bx = i;
|
|
}
|
|
new_free(&blowbox[bx].P);
|
|
for (i = 0; i < 4; i++)
|
|
new_free(&blowbox[bx].S[i]);
|
|
new_free(&blowbox[bx].S);
|
|
}
|
|
/* initialize new buffer */
|
|
/* uh... this is over 4k */
|
|
blowbox[bx].P = (UWORD_32bits *) new_malloc((bf_N + 2) * sizeof(UWORD_32bits));
|
|
blowbox[bx].S = (UWORD_32bits **) new_malloc(4 * sizeof(UWORD_32bits *));
|
|
for (i = 0; i < 4; i++)
|
|
blowbox[bx].S[i] = (UWORD_32bits *) new_malloc(256 * sizeof(UWORD_32bits));
|
|
bf_P = blowbox[bx].P;
|
|
bf_S = blowbox[bx].S;
|
|
blowbox[bx].keybytes = keybytes;
|
|
strncpy(blowbox[bx].key, key, keybytes);
|
|
blowbox[bx].lastuse = now;
|
|
|
|
/* robey: reset blowfish boxes to initial state */
|
|
/* (i guess normally it just keeps scrambling them, but here it's */
|
|
/* important to get the same encrypted result each time) */
|
|
for (i = 0; i < bf_N + 2; i++)
|
|
bf_P[i] = initbf_P[i];
|
|
for (i = 0; i < 4; i++)
|
|
for (j = 0; j < 256; j++)
|
|
bf_S[i][j] = initbf_S[i][j];
|
|
|
|
j = 0;
|
|
for (i = 0; i < bf_N + 2; ++i)
|
|
{
|
|
temp.word = 0;
|
|
temp.w.byte0 = key[j];
|
|
temp.w.byte1 = key[(j + 1) % keybytes];
|
|
temp.w.byte2 = key[(j + 2) % keybytes];
|
|
temp.w.byte3 = key[(j + 3) % keybytes];
|
|
data = temp.word;
|
|
bf_P[i] = bf_P[i] ^ data;
|
|
j = (j + 4) % keybytes;
|
|
}
|
|
|
|
datal = 0x00000000;
|
|
datar = 0x00000000;
|
|
|
|
for (i = 0; i < bf_N + 2; i += 2)
|
|
{
|
|
blowfish_encipher(&datal, &datar);
|
|
bf_P[i] = datal;
|
|
bf_P[i + 1] = datar;
|
|
}
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
{
|
|
for (j = 0; j < 256; j += 2)
|
|
{
|
|
blowfish_encipher(&datal, &datar);
|
|
bf_S[i][j] = datal;
|
|
bf_S[i][j + 1] = datar;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* stuff below this line was written by robey for eggdrop use */
|
|
|
|
/* convert 64-bit encrypted password to text for userfile */
|
|
static char *base64 = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
static int base64dec (char c)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 64; i++)
|
|
if (base64[i] == c)
|
|
return i;
|
|
return 0;
|
|
}
|
|
|
|
/* returned string must be freed when done with it! */
|
|
static char *encrypt_string (char * key, char * str)
|
|
{
|
|
UWORD_32bits left, right;
|
|
char *p, *s, *dest, *d;
|
|
int i;
|
|
dest = (char *) new_malloc((strlen(str) + 9) * 2);
|
|
/* pad fake string with 8 bytes to make sure there's enough */
|
|
s = (char *) new_malloc(strlen(str) + 9);
|
|
strcpy(s, str);
|
|
p = s;
|
|
while (*p)
|
|
p++;
|
|
for (i = 0; i < 8; i++)
|
|
*p++ = 0;
|
|
blowfish_init(key, strlen(key));
|
|
p = s;
|
|
d = dest;
|
|
while (*p)
|
|
{
|
|
left = ((*p++) << 24);
|
|
left += ((*p++) << 16);
|
|
left += ((*p++) << 8);
|
|
left += (*p++);
|
|
right = ((*p++) << 24);
|
|
right += ((*p++) << 16);
|
|
right += ((*p++) << 8);
|
|
right += (*p++);
|
|
blowfish_encipher(&left, &right);
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
*d++ = base64[right & 0x3f];
|
|
right = (right >> 6);
|
|
}
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
*d++ = base64[left & 0x3f];
|
|
left = (left >> 6);
|
|
}
|
|
}
|
|
*d = 0;
|
|
new_free(&s);
|
|
return dest;
|
|
}
|
|
|
|
/* returned string must be freed when done with it! */
|
|
static char *decrypt_string (char * key, char * str)
|
|
{
|
|
UWORD_32bits left, right;
|
|
char *p, *s, *dest, *d;
|
|
int i;
|
|
dest = (char *) new_malloc(strlen(str) + 12);
|
|
/* pad encoded string with 0 bits in case it's bogus */
|
|
s = (char *) new_malloc(strlen(str) + 12);
|
|
strcpy(s, str);
|
|
p = s;
|
|
while (*p)
|
|
p++;
|
|
for (i = 0; i < 12; i++)
|
|
*p++ = 0;
|
|
blowfish_init(key, strlen(key));
|
|
p = s;
|
|
d = dest;
|
|
while (*p) {
|
|
right = 0L;
|
|
left = 0L;
|
|
for (i = 0; i < 6; i++)
|
|
right |= (base64dec(*p++)) << (i * 6);
|
|
for (i = 0; i < 6; i++)
|
|
left |= (base64dec(*p++)) << (i * 6);
|
|
blowfish_decipher(&left, &right);
|
|
for (i = 0; i < 4; i++)
|
|
*d++ = (left & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8);
|
|
for (i = 0; i < 4; i++)
|
|
*d++ = (right & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8);
|
|
}
|
|
*d = 0;
|
|
new_free(&s);
|
|
return dest;
|
|
}
|
|
|
|
#ifdef WANT_TCL
|
|
static int tcl_encrypt STDVAR
|
|
{
|
|
char *p;
|
|
BADARGS(3, 3, " key string");
|
|
p = encrypt_string(argv[1], argv[2]);
|
|
Tcl_AppendResult(irp, p, NULL);
|
|
new_free(&p);
|
|
return TCL_OK;
|
|
}
|
|
|
|
static int tcl_decrypt STDVAR
|
|
{
|
|
char *p;
|
|
BADARGS(3, 3, " key string");
|
|
p = decrypt_string(argv[1], argv[2]);
|
|
Tcl_AppendResult(irp, p, NULL);
|
|
new_free(&p);
|
|
return TCL_OK;
|
|
}
|
|
|
|
#endif /* WANT_TCL */
|
|
|
|
BUILT_IN_FUNCTION(ircii_encrypt)
|
|
{
|
|
char *p, *q, *r;
|
|
if (!input)
|
|
return m_strdup("1");
|
|
p = input;
|
|
if ((q = strchr(input, ' ')))
|
|
{
|
|
*q++ = 0;
|
|
r = encrypt_string(p, q);
|
|
return r;
|
|
}
|
|
return m_strdup(empty_string);
|
|
}
|
|
|
|
BUILT_IN_FUNCTION(ircii_decrypt)
|
|
{
|
|
char *p, *q, *r;
|
|
if (!input)
|
|
return m_strdup("1");
|
|
p = input;
|
|
if ((q = strchr(input, ' ')))
|
|
{
|
|
*q++ = 0;
|
|
r = decrypt_string(p, q);
|
|
return r;
|
|
}
|
|
return m_strdup(empty_string);
|
|
}
|
|
|
|
int Blowfish_Init(IrcCommandDll **intp, Function_ptr *global_table)
|
|
{
|
|
int i;
|
|
initialize_module("Blowfish");
|
|
|
|
for (i = 0; i < BOXES; i++) {
|
|
blowbox[i].P = NULL;
|
|
blowbox[i].S = NULL;
|
|
blowbox[i].key[0] = 0;
|
|
blowbox[i].lastuse = 0L;
|
|
}
|
|
#ifdef WANT_TCL
|
|
Tcl_CreateCommand(tcl_interp, "encrypt", tcl_encrypt, NULL, NULL);
|
|
Tcl_CreateCommand(tcl_interp, "decrypt", tcl_decrypt, NULL, NULL);
|
|
Tcl_SetVar(tcl_interp, "blowfish_version", blowfish_version, TCL_GLOBAL_ONLY);
|
|
#endif
|
|
add_module_proc(ALIAS_PROC, "blowfish", "encrypt", "Blowfish Encryption", 0, 0, ircii_encrypt, NULL);
|
|
add_module_proc(ALIAS_PROC, "blowfish", "decrypt", "Blowfish Decryption", 0, 0, ircii_decrypt, NULL);
|
|
put_it("%s loaded.", blowfish_version);
|
|
put_it("Adapted from eggdrop by By-Tor");
|
|
return 0;
|
|
}
|
|
|
|
int Blowfish_Cleanup(IrcCommandDll **intp)
|
|
{
|
|
#ifdef WANT_TCL
|
|
Tcl_DeleteCommand(tcl_interp, "encrypt");
|
|
Tcl_DeleteCommand(tcl_interp, "decrypt");
|
|
Tcl_UnsetVar(tcl_interp, "blowfish_version", TCL_GLOBAL_ONLY);
|
|
#endif
|
|
return 1;
|
|
}
|