/*
* blowfish.c -- part of blowfish.mod
* encryption and decryption of passwords
*
* $Id: blowfish.c,v 1.32 2006-03-28 02:35:50 wcc Exp $
*/
/*
* Copyright (C) 1997 Robey Pointer
* Copyright (C) 1999 - 2006 Eggheads Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* This code was originally in the public domain.
*/
#define MODULE_NAME "encryption"
#define MAKING_ENCRYPTION
#include "src/mod/module.h"
#include "blowfish.h"
#include "bf_tab.h" /* P-box P-array, S-box */
#undef global
static Function *global = NULL;
/* Each box takes up 4k so be very careful here */
#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 {
u_32bit_t *P;
u_32bit_t **S;
char key[81];
char keybytes;
time_t lastuse;
} box[BOXES];
/* static u_32bit_t bf_P[bf_N+2]; */
/* static u_32bit_t bf_S[4][256]; */
static u_32bit_t *bf_P;
static u_32bit_t **bf_S;
static int blowfish_expmem()
{
int i, tot = 0;
for (i = 0; i < BOXES; i++)
if (box[i].P != NULL) {
tot += ((bf_N + 2) * sizeof(u_32bit_t));
tot += (4 * sizeof(u_32bit_t *));
tot += (4 * 256 * sizeof(u_32bit_t));
}
return tot;
}
static void blowfish_encipher(u_32bit_t *xl, u_32bit_t *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(u_32bit_t *xl, u_32bit_t *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_report(int idx, int details)
{
if (details) {
int i, tot = 0, size = blowfish_expmem();
for (i = 0; i < BOXES; i++)
if (box[i].P != NULL)
tot++;
dprintf(idx, " Blowfish encryption module:\n");
dprintf(idx, " %d of %d boxes in use: ", tot, BOXES);
for (i = 0; i < BOXES; i++)
if (box[i].P != NULL) {
dprintf(idx, "(age: %d) ", now - box[i].lastuse);
}
dprintf(idx, "\n");
dprintf(idx, " Using %d byte%s of memory\n", size,
(size != 1) ? "s" : "");
}
}
static void blowfish_init(u_8bit_t *key, int keybytes)
{
int i, j, bx;
time_t lowest;
u_32bit_t data;
u_32bit_t datal;
u_32bit_t datar;
union aword temp;
/* drummer: Fixes crash if key is longer than 80 char. This may cause the key
* to not end with \00 but that's no problem.
*/
if (keybytes > 80)
keybytes = 80;
/* Is buffer already allocated for this? */
for (i = 0; i < BOXES; i++)
if (box[i].P != NULL) {
if ((box[i].keybytes == keybytes) &&
(!strncmp((char *) (box[i].key), (char *) key, keybytes))) {
/* Match! */
box[i].lastuse = now;
bf_P = box[i].P;
bf_S = box[i].S;
return;
}
}
/* No pre-allocated buffer: make new one */
/* Set 'bx' to empty buffer */
bx = -1;
for (i = 0; i < BOXES; i++) {
if (box[i].P == NULL) {
bx = i;
i = BOXES + 1;
}
}
if (bx < 0) {
/* Find oldest */
lowest = now;
for (i = 0; i < BOXES; i++)
if (box[i].lastuse <= lowest) {
lowest = box[i].lastuse;
bx = i;
}
nfree(box[bx].P);
for (i = 0; i < 4; i++)
nfree(box[bx].S[i]);
nfree(box[bx].S);
}
/* Initialize new buffer */
/* uh... this is over 4k */
box[bx].P = nmalloc((bf_N + 2) * sizeof(u_32bit_t));
box[bx].S = nmalloc(4 * sizeof(u_32bit_t *));
for (i = 0; i < 4; i++)
box[bx].S[i] = nmalloc(256 * sizeof(u_32bit_t));
bf_P = box[bx].P;
bf_S = box[bx].S;
box[bx].keybytes = keybytes;
strncpy(box[bx].key, key, keybytes);
box[bx].key[keybytes] = 0;
box[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;
if (keybytes > 0) { /* drummer: fixes crash if key=="" */
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;
}
}
}
/* Of course, if you change either of these, then your userfile will
* no longer be able to be shared. :)
*/
#define SALT1 0xdeadd061
#define SALT2 0x23f6b095
/* 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;
}
static void blowfish_encrypt_pass(char *text, char *new)
{
u_32bit_t left, right;
int n;
char *p;
blowfish_init((unsigned char *) text, strlen(text));
left = SALT1;
right = SALT2;
blowfish_encipher(&left, &right);
p = new;
*p++ = '+'; /* + means encrypted pass */
n = 32;
while (n > 0) {
*p++ = base64[right & 0x3f];
right = (right >> 6);
n -= 6;
}
n = 32;
while (n > 0) {
*p++ = base64[left & 0x3f];
left = (left >> 6);
n -= 6;
}
*p = 0;
}
/* Returned string must be freed when done with it!
*/
static char *encrypt_string(char *key, char *str)
{
u_32bit_t left, right;
unsigned char *p;
char *s, *dest, *d;
int i;
/* Pad fake string with 8 bytes to make sure there's enough */
s = nmalloc(strlen(str) + 9);
strcpy(s, str);
if ((!key) || (!key[0]))
return s;
p = s;
dest = nmalloc((strlen(str) + 9) * 2);
while (*p)
p++;
for (i = 0; i < 8; i++)
*p++ = 0;
blowfish_init((unsigned char *) 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;
nfree(s);
return dest;
}
/* Returned string must be freed when done with it!
*/
static char *decrypt_string(char *key, char *str)
{
u_32bit_t left, right;
char *p, *s, *dest, *d;
int i;
/* Pad encoded string with 0 bits in case it's bogus */
s = nmalloc(strlen(str) + 12);
strcpy(s, str);
if ((!key) || (!key[0]))
return s;
p = s;
dest = nmalloc(strlen(str) + 12);
while (*p)
p++;
for (i = 0; i < 12; i++)
*p++ = 0;
blowfish_init((unsigned char *) 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;
nfree(s);
return dest;
}
static int tcl_encrypt STDVAR
{
char *p;
BADARGS(3, 3, " key string");
p = encrypt_string(argv[1], argv[2]);
Tcl_AppendResult(irp, p, NULL);
nfree(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);
nfree(p);
return TCL_OK;
}
static int tcl_encpass STDVAR
{
BADARGS(2, 2, " string");
if (strlen(argv[1]) > 0) {
char p[16];
blowfish_encrypt_pass(argv[1], p);
Tcl_AppendResult(irp, p, NULL);
} else
Tcl_AppendResult(irp, "", NULL);
return TCL_OK;
}
static tcl_cmds mytcls[] = {
{"encrypt", tcl_encrypt},
{"decrypt", tcl_decrypt},
{"encpass", tcl_encpass},
{NULL, NULL}
};
static char *blowfish_close()
{
return "You can't unload the encryption module";
}
EXPORT_SCOPE char *blowfish_start(Function *);
static Function blowfish_table[] = {
/* 0 - 3 */
(Function) blowfish_start,
(Function) blowfish_close,
(Function) blowfish_expmem,
(Function) blowfish_report,
/* 4 - 7 */
(Function) encrypt_string,
(Function) decrypt_string,
};
char *blowfish_start(Function *global_funcs)
{
int i;
/* `global_funcs' is NULL if eggdrop is recovering from a restart.
*
* As the encryption module is never unloaded, only initialise stuff
* that got reset during restart, e.g. the tcl bindings.
*/
if (global_funcs) {
global = global_funcs;
if (!module_rename("blowfish", MODULE_NAME))
return "Already loaded.";
/* Initialize buffered boxes */
for (i = 0; i < BOXES; i++) {
box[i].P = NULL;
box[i].S = NULL;
box[i].key[0] = 0;
box[i].lastuse = 0L;
}
module_register(MODULE_NAME, blowfish_table, 2, 1);
if (!module_depend(MODULE_NAME, "eggdrop", 106, 3)) {
module_undepend(MODULE_NAME);
return "This module requires Eggdrop 1.6.3 or later.";
}
add_hook(HOOK_ENCRYPT_PASS, (Function) blowfish_encrypt_pass);
add_hook(HOOK_ENCRYPT_STRING, (Function) encrypt_string);
add_hook(HOOK_DECRYPT_STRING, (Function) decrypt_string);
}
add_tcl_commands(mytcls);
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1