rotormodule.c 14.3 KB
Newer Older
1
/***********************************************************
2 3
Copyright 1994 by Lance Ellinghouse,
Cathedral City, California Republic, United States of America.
4 5 6 7 8 9 10

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
11 12 13
supporting documentation, and that the name of Lance Ellinghouse
not be used in advertising or publicity pertaining to distribution 
of the software without specific, written prior permission.
14

15
LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
16
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 18 19 20 21
FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE BE LIABLE FOR ANY SPECIAL, 
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 23 24 25

******************************************************************/

/* This creates an encryption and decryption engine I am calling
26
   a rotor due to the original design was a hardware rotor with
27 28 29 30 31 32 33 34 35
   contacts used in Germany during WWII.

Rotor Module:

-  rotor.newrotor('key') -> rotorobject  (default of 6 rotors)
-  rotor.newrotor('key', num_rotors) -> rotorobject

Rotor Objects:

36
-  ro.setkey('string') -> None (resets the key as defined in newrotor().
37 38 39
-  ro.encrypt('string') -> encrypted string
-  ro.decrypt('encrypted string') -> unencrypted string

40 41 42 43 44 45
-  ro.encryptmore('string') -> encrypted string
-  ro.decryptmore('encrypted string') -> unencrypted string

NOTE: the {en,de}cryptmore() methods use the setup that was
      established via the {en,de}crypt calls. They will NOT
      re-initalize the rotors unless: 1) They have not been
46
      initialized with {en,de}crypt since the last setkey() call;
47 48
      2) {en,de}crypt has not been called for this rotor yet.

49 50 51 52 53 54 55 56 57
NOTE: you MUST use the SAME key in rotor.newrotor()
      if you wish to decrypt an encrypted string.
      Also, the encrypted string is NOT 0-127 ASCII. 
      It is considered BINARY data.

*/

/* Rotor objects */

58
#include "Python.h"
59

60
#ifndef TRUE
61
#define TRUE	1
62 63
#endif
#ifndef FALSE
64
#define FALSE	0
65
#endif
66 67

typedef struct {
68
	PyObject_HEAD
69 70
	int seed[3];
    	short key[5];
71
	int  isinited;
72 73 74
	int  size;
	int  size_mask;
    	int  rotors;
75 76 77 78 79
	unsigned char *e_rotor;		     /* [num_rotors][size] */
	unsigned char *d_rotor;		     /* [num_rotors][size] */
	unsigned char *positions;	     /* [num_rotors] */
	unsigned char *advances;	     /* [num_rotors] */
} Rotorobj;
80

81
staticforward PyTypeObject Rotor_Type;
82

83
#define is_rotor(v)		((v)->ob_type == &Rotor_Type)
84

85 86

/* This defines the necessary routines to manage rotor objects */
87

88
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
89
set_seed(Rotorobj *r)
90 91 92 93
{
	r->seed[0] = r->key[0];
	r->seed[1] = r->key[1];
	r->seed[2] = r->key[2];
94
	r->isinited = FALSE;
95 96 97
}
	
/* Return the next random number in the range [0.0 .. 1.0) */
98
static double
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
99
r_random(Rotorobj *r)
100 101
{
	int x, y, z;
102
	double val, term;
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

	x = r->seed[0];
	y = r->seed[1];
	z = r->seed[2];

	x = 171 * (x % 177) - 2 * (x/177);
	y = 172 * (y % 176) - 35 * (y/176);
	z = 170 * (z % 178) - 63 * (z/178);
	
	if (x < 0) x = x + 30269;
	if (y < 0) y = y + 30307;
	if (z < 0) z = z + 30323;
	
	r->seed[0] = x;
	r->seed[1] = y;
	r->seed[2] = z;

120 121 122 123
	term = (double)(
		(((double)x)/(double)30269.0) + 
		(((double)y)/(double)30307.0) + 
		(((double)z)/(double)30323.0)
124
		);
125
	val = term - (double)floor((double)term);
126

127 128
	if (val >= 1.0)
		val = 0.0;
129 130 131 132

	return val;
}

133
static short
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
134
r_rand(Rotorobj *r, short s)
135
{
136
	return (short)((short)(r_random(r) * (double)s) % s);
137 138
}

139
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
140
set_key(Rotorobj *r, char *key)
141
{
142
	unsigned long k1=995, k2=576, k3=767, k4=671, k5=463;
143 144
	size_t i;
	size_t len = strlen(key);
145

146
	for (i = 0; i < len; i++) {
147 148 149 150 151 152 153
		unsigned short ki = Py_CHARMASK(key[i]);

		k1 = (((k1<<3 | k1>>13) + ki) & 65535);
		k2 = (((k2<<3 | k2>>13) ^ ki) & 65535);
		k3 = (((k3<<3 | k3>>13) - ki) & 65535);
		k4 = ((ki - (k4<<3 | k4>>13)) & 65535);
		k5 = (((k5<<3 | k5>>13) ^ ~ki) & 65535);
154 155 156 157 158 159 160 161 162 163
	}
	r->key[0] = (short)k1;
	r->key[1] = (short)(k2|1);
	r->key[2] = (short)k3;
	r->key[3] = (short)k4;
	r->key[4] = (short)k5;

	set_seed(r);
}

164 165


166
/* These define the interface to a rotor object */
167
static Rotorobj *
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
168
rotorobj_new(int num_rotors, char *key)
169
{
170 171
	Rotorobj *xp;

172
	xp = PyObject_New(Rotorobj, &Rotor_Type);
173 174
	if (xp == NULL)
		return NULL;
175
	set_key(xp, key);
176 177 178 179 180

	xp->size = 256;
	xp->size_mask = xp->size - 1;
	xp->size_mask = 0;
	xp->rotors = num_rotors;
181 182 183 184 185
	xp->e_rotor = NULL;
	xp->d_rotor = NULL;
	xp->positions = NULL;
	xp->advances = NULL;

186 187 188 189 190 191 192 193 194
	if (!(xp->e_rotor = PyMem_NEW(unsigned char, num_rotors * xp->size)))
		goto finally;
	if (!(xp->d_rotor = PyMem_NEW(unsigned char, num_rotors * xp->size)))
		goto finally;
	if (!(xp->positions = PyMem_NEW(unsigned char, num_rotors)))
		goto finally;
	if (!(xp->advances = PyMem_NEW(unsigned char, num_rotors)))
		goto finally;

195
	return xp;
196 197

  finally:
198 199 200 201 202 203 204 205
	if (xp->e_rotor)
		PyMem_DEL(xp->e_rotor);
	if (xp->d_rotor)
		PyMem_DEL(xp->d_rotor);
	if (xp->positions)
		PyMem_DEL(xp->positions);
	if (xp->advances)
		PyMem_DEL(xp->advances);
206
	Py_DECREF(xp);
207
	return (Rotorobj*)PyErr_NoMemory();
208 209
}

210

211
/* These routines implement the rotor itself */
212

213 214
/*  Here is a fairly sophisticated {en,de}cryption system.  It is based on
    the idea of a "rotor" machine.  A bunch of rotors, each with a
215 216 217
    different permutation of the alphabet, rotate around a different amount
    after encrypting one character.  The current state of the rotors is
    used to encrypt one character.
218

219
    The code is smart enough to tell if your alphabet has a number of
220 221
    characters equal to a power of two.  If it does, it uses logical
    operations, if not it uses div and mod (both require a division).
222

223 224 225 226
    You will need to make two changes to the code 1) convert to c, and
    customize for an alphabet of 255 chars 2) add a filter at the begining,
    and end, which subtracts one on the way in, and adds one on the way
    out.
227

228 229 230
    You might wish to do some timing studies.  Another viable alternative
    is to "byte stuff" the encrypted data of a normal (perhaps this one)
    encryption routine.
231

232
    j'
233

234 235 236 237 238 239 240 241 242 243
 */

/* Note: the C code here is a fairly straightforward transliteration of a
 * rotor implemented in lisp.  The original lisp code has been removed from
 * this file to for simplification, but I've kept the docstrings as
 * comments in front of the functions.
 */


/* Set ROTOR to the identity permutation */
244
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
245
RTR_make_id_rotor(Rotorobj *r, unsigned char *rtr)
246 247 248
{
	register int j;
	register int size = r->size;
249
	for (j = 0; j < size; j++) {
250 251 252 253 254
		rtr[j] = (unsigned char)j;
	}
}


255
/* The current set of encryption rotors */
256
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
257
RTR_e_rotors(Rotorobj *r)
258 259
{
	int i;
260 261
	for (i = 0; i < r->rotors; i++) {
		RTR_make_id_rotor(r, &(r->e_rotor[(i*r->size)]));
262 263 264
	}
}

265
/* The current set of decryption rotors */
266
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
267
RTR_d_rotors(Rotorobj *r)
268 269
{
	register int i, j;
270 271
	for (i = 0; i < r->rotors; i++) {
		for (j = 0; j < r->size; j++) {
272 273 274 275 276
			r->d_rotor[((i*r->size)+j)] = (unsigned char)j;
		}
	}
}

277
/* The positions of the rotors at this time */
278
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
279
RTR_positions(Rotorobj *r)
280 281
{
	int i;
282
	for (i = 0; i < r->rotors; i++) {
283 284 285 286
		r->positions[i] = 1;
	}
}

287
/* The number of positions to advance the rotors at a time */
288
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
289
RTR_advances(Rotorobj *r)
290 291
{
	int i;
292
	for (i = 0; i < r->rotors; i++) {
293 294 295 296
		r->advances[i] = 1;
	}
}

297 298 299
/* Permute the E rotor, and make the D rotor its inverse
 * see Knuth for explanation of algorithm.
 */
300
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
301
RTR_permute_rotor(Rotorobj *r, unsigned char *e, unsigned char *d)
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
{
	short i = r->size;
	short q;
	unsigned char j;
	RTR_make_id_rotor(r,e);
	while (2 <= i) {
		q = r_rand(r,i);
		i--;
		j = e[q];
		e[q] = (unsigned char)e[i];
		e[i] = (unsigned char)j;
		d[j] = (unsigned char)i;
	}
	e[0] = (unsigned char)e[0];
	d[(e[0])] = (unsigned char)0;
}

319 320 321
/* Given KEY (a list of 5 16 bit numbers), initialize the rotor machine.
 * Set the advancement, position, and permutation of the rotors
 */
322
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
323
RTR_init(Rotorobj *r)
324 325 326 327 328 329 330
{
	int i;
	set_seed(r);
	RTR_positions(r);
	RTR_advances(r);
	RTR_e_rotors(r);
	RTR_d_rotors(r);
331
	for (i = 0; i < r->rotors; i++) {
332 333
		r->positions[i] = (unsigned char) r_rand(r, (short)r->size);
		r->advances[i] = (1+(2*(r_rand(r, (short)(r->size/2)))));
334 335 336
		RTR_permute_rotor(r,
				  &(r->e_rotor[(i*r->size)]),
				  &(r->d_rotor[(i*r->size)]));
337
	}
338
	r->isinited = TRUE;
339 340
}

341
/* Change the RTR-positions vector, using the RTR-advances vector */
342
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
343
RTR_advance(Rotorobj *r)
344 345 346
{
	register int i=0, temp=0;
	if (r->size_mask) {
347
		while (i < r->rotors) {
348 349 350 351 352 353 354 355
			temp = r->positions[i] + r->advances[i];
			r->positions[i] = temp & r->size_mask;
			if ((temp >= r->size) && (i < (r->rotors - 1))) {
				r->positions[(i+1)] = 1 + r->positions[(i+1)];
			}
			i++;
		}
	} else {
356
		while (i < r->rotors) {
357 358 359 360 361 362 363 364 365 366
			temp = r->positions[i] + r->advances[i];
			r->positions[i] = temp%r->size;
			if ((temp >= r->size) && (i < (r->rotors - 1))) {
				r->positions[(i+1)] = 1 + r->positions[(i+1)];
			}
			i++;
		}
	}
}

367
/* Encrypt the character P with the current rotor machine */
368
static unsigned char
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
369
RTR_e_char(Rotorobj *r, unsigned char p)
370 371 372 373 374
{
	register int i=0;
	register unsigned char tp=p;
	if (r->size_mask) {
		while (i < r->rotors) {
375 376 377
			tp = r->e_rotor[(i*r->size) +
				       (((r->positions[i] ^ tp) &
					 r->size_mask))];
378 379 380 381
			i++;
		}
	} else {
		while (i < r->rotors) {
382 383 384
			tp = r->e_rotor[(i*r->size) +
				       (((r->positions[i] ^ tp) %
					 (unsigned int) r->size))];
385 386 387 388 389 390 391
			i++;
		}
	}
	RTR_advance(r);
	return ((unsigned char)tp);
}

392
/* Decrypt the character C with the current rotor machine */
393
static unsigned char
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
394
RTR_d_char(Rotorobj *r, unsigned char c)
395
{
396 397 398
	register int i = r->rotors - 1;
	register unsigned char tc = c;

399 400
	if (r->size_mask) {
		while (0 <= i) {
401 402
			tc = (r->positions[i] ^
			      r->d_rotor[(i*r->size)+tc]) & r->size_mask;
403 404 405 406
			i--;
		}
	} else {
		while (0 <= i) {
407 408 409
			tc = (r->positions[i] ^
			      r->d_rotor[(i*r->size)+tc]) %
				(unsigned int) r->size;
410 411 412 413 414 415 416
			i--;
		}
	}
	RTR_advance(r);
	return(tc);
}

417
/* Perform a rotor encryption of the region from BEG to END by KEY */
418
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
419
RTR_e_region(Rotorobj *r, unsigned char *beg, int len, int doinit)
420 421
{
	register int i;
422 423
	if (doinit || r->isinited == FALSE)
		RTR_init(r);
424 425
	for (i = 0; i < len; i++) {
		beg[i] = RTR_e_char(r, beg[i]);
426 427 428
	}
}

429
/* Perform a rotor decryption of the region from BEG to END by KEY */
430
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
431
RTR_d_region(Rotorobj *r, unsigned char *beg, int len, int doinit)
432 433
{
	register int i;
434 435
	if (doinit || r->isinited == FALSE)
		RTR_init(r);
436 437
	for (i = 0; i < len; i++) {
		beg[i] = RTR_d_char(r, beg[i]);
438 439 440 441
	}
}


442

443 444
/* Rotor methods */
static void
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
445
rotor_dealloc(Rotorobj *xp)
446
{
447 448 449 450 451 452 453 454 455
	if (xp->e_rotor)
		PyMem_DEL(xp->e_rotor);
	if (xp->d_rotor)
		PyMem_DEL(xp->d_rotor);
	if (xp->positions)
		PyMem_DEL(xp->positions);
	if (xp->advances)
		PyMem_DEL(xp->advances);
	PyObject_Del(xp);
456 457
}

458
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
459
rotorobj_encrypt(Rotorobj *self, PyObject *args)
460
{
461
	char *string = NULL;
462
	int len = 0;
463
	PyObject *rtn = NULL;
464 465
	char *tmp;

466
	if (!PyArg_Parse(args, "s#", &string, &len))
467
		return NULL;
468
	if (!(tmp = PyMem_NEW(char, len+5))) {
469
		PyErr_NoMemory();
470 471
		return NULL;
	}
472 473 474 475 476
	memset(tmp, '\0', len+1);
	memcpy(tmp, string, len);
	RTR_e_region(self, (unsigned char *)tmp, len, TRUE);
	rtn = PyString_FromStringAndSize(tmp, len);
	PyMem_DEL(tmp);
477 478 479
	return(rtn);
}

480
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
481
rotorobj_encrypt_more(Rotorobj *self, PyObject *args)
482
{
483
	char *string = NULL;
484
	int len = 0;
485
	PyObject *rtn = NULL;
486 487
	char *tmp;

488
	if (!PyArg_Parse(args, "s#", &string, &len))
489
		return NULL;
490
	if (!(tmp = PyMem_NEW(char, len+5))) {
491
		PyErr_NoMemory();
492 493
		return NULL;
	}
494 495 496 497 498
	memset(tmp, '\0', len+1);
	memcpy(tmp, string, len);
	RTR_e_region(self, (unsigned char *)tmp, len, FALSE);
	rtn = PyString_FromStringAndSize(tmp, len);
	PyMem_DEL(tmp);
499 500 501
	return(rtn);
}

502
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
503
rotorobj_decrypt(Rotorobj *self, PyObject *args)
504
{
505
	char *string = NULL;
506
	int len = 0;
507
	PyObject *rtn = NULL;
508 509
	char *tmp;

510
	if (!PyArg_Parse(args, "s#", &string, &len))
511
		return NULL;
512
	if (!(tmp = PyMem_NEW(char, len+5))) {
513
		PyErr_NoMemory();
514 515
		return NULL;
	}
516 517 518 519 520
	memset(tmp, '\0', len+1);
	memcpy(tmp, string, len);
	RTR_d_region(self, (unsigned char *)tmp, len, TRUE);
	rtn = PyString_FromStringAndSize(tmp, len);
	PyMem_DEL(tmp);
521 522 523
	return(rtn);
}

524
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
525
rotorobj_decrypt_more(Rotorobj *self, PyObject *args)
526
{
527
	char *string = NULL;
528
	int len = 0;
529
	PyObject *rtn = NULL;
530 531
	char *tmp;

532
	if (!PyArg_Parse(args, "s#", &string, &len))
533
		return NULL;
534
	if (!(tmp = PyMem_NEW(char, len+5))) {
535
		PyErr_NoMemory();
536 537
		return NULL;
	}
538 539 540 541 542
	memset(tmp, '\0', len+1);
	memcpy(tmp, string, len);
	RTR_d_region(self, (unsigned char *)tmp, len, FALSE);
	rtn = PyString_FromStringAndSize(tmp, len);
	PyMem_DEL(tmp);
543 544 545
	return(rtn);
}

546
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
547
rotorobj_setkey(Rotorobj *self, PyObject *args)
548
{
549
	char *key;
550

551
	if (!PyArg_ParseTuple(args, "s:setkey", &key))
552 553
		return NULL;

554
	set_key(self, key);
555 556
	Py_INCREF(Py_None);
	return Py_None;
557 558
}

559 560 561 562 563 564 565
static struct PyMethodDef
rotorobj_methods[] = {
	{"encrypt",	(PyCFunction)rotorobj_encrypt},
	{"encryptmore",	(PyCFunction)rotorobj_encrypt_more},
	{"decrypt",	(PyCFunction)rotorobj_decrypt},
	{"decryptmore",	(PyCFunction)rotorobj_decrypt_more},
	{"setkey",	(PyCFunction)rotorobj_setkey, 1},
566 567 568 569 570
	{NULL,		NULL}		/* sentinel */
};


/* Return a rotor object's named attribute. */
571
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
572
rotorobj_getattr(Rotorobj *s, char *name)
573
{
574
	return Py_FindMethod(rotorobj_methods, (PyObject*)s, name);
575 576
}

577 578

statichere PyTypeObject Rotor_Type = {
579
	PyObject_HEAD_INIT(NULL)
580
	0,				/*ob_size*/
581
	"rotor",			/*tp_name*/
582
	sizeof(Rotorobj),		/*tp_size*/
583
	0,				/*tp_itemsize*/
584
	/* methods */
585
	(destructor)rotor_dealloc,	/*tp_dealloc*/
586
	0,				/*tp_print*/
587
	(getattrfunc)rotorobj_getattr,	/*tp_getattr*/
588 589 590
	0,				/*tp_setattr*/
	0,				/*tp_compare*/
	0,				/*tp_repr*/
591
	0,                              /*tp_hash*/
592 593 594
};


595
static PyObject * 
Peter Schneider-Kamp's avatar
Peter Schneider-Kamp committed
596
rotor_rotor(PyObject *self, PyObject *args)
597
{
598
	Rotorobj *r;
599 600
	char *string;
	int len;
601
	int num_rotors = 6;
602

603
	if (!PyArg_ParseTuple(args, "s#|i:newrotor", &string, &len, &num_rotors))
604 605 606 607
		return NULL;

	r = rotorobj_new(num_rotors, string);
	return (PyObject *)r;
608 609
}

610

611

612 613 614 615
static struct PyMethodDef
rotor_methods[] = {
	{"newrotor",  rotor_rotor, 1},
	{NULL,        NULL}		     /* sentinel */
616 617 618
};


619
DL_EXPORT(void)
620
initrotor(void)
621
{
622
	Rotor_Type.ob_type = &PyType_Type;
623
	(void)Py_InitModule("rotor", rotor_methods);
624
}