バーナム暗号プログラム



[ 簡単な説明 ]

バーナム暗号を用いたファイル暗号化/復号化プログラムです。
バーナム暗号は、メッセージと乱数列との排他的論理和をとる単純なアルゴリズムですが、
暗号文のみの情報からは解読不可能な暗号です。
本プログラム・ソースでは、乱数ライブラリにあるM系列乱数を用いています。


プログラム・ソース("vernam.c")           top (トップに戻る)
/*			vernam.c			*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <mem.h>

#define		BUFFER_SIZE		51200

typedef		unsigned long	ULONG;
typedef		union
			{
				ULONG a;
				char b[4];
			}				LONGCHAR;

int (*rfunc)();
void (*wfunc)();
void (*ini_s)();
void (*rot_i)();

FILE *fp1, *fp2;

LONGCHAR m;
char *mb3 = m.b + 3;

ULONG rx[521];
ULONG *prx, *prx_end = rx + 521;
ULONG seed, bufsize1;

int count, llen, lshift, rshift;

char buf[8], rndkey[8], keybuf[16];
char *buffer, *blast, *bp;

static int deflag = -1, blen = 8, blena = 0, nwarm1 = 3, nwarm2 = 0;

static char initial[8];
char *initial4 = initial + 4;
 
/* prototype declaration */
void init_mrnd(void);
void rnd521(void);
ULONG mrnd(void);
void xor(char *in, char *out, int n);
ULONG char_to_long(char *a);
void long_to_char(ULONG m1, ULONG m2);
void rot_initial1(char *set);
void rot_initial2(char *set);
void rot_initial3(char *set);
void rot_initial4(char *set);
void init_crypt(void);
void make_rndkey(void);
void ini_shift1(void);
void ini_shift2(void);
void ini_shift3(void);
void ini_shift4(void);
void vernam(void);
int readdata1(void);
int readdata2(void);
void writedata1(char *data);
void writedata2(char *data);
void openerr(char *kind, char *file);
void usage(char *prog);

void init_mrnd(void)
{
	int i, j;
	ULONG *p1 = rx, *p2 = rx + 1, *p3, u;

	if(seed == 0)	seed = 1;
	u = 0;
	prx = rx;
	i = 17;
	do
	{
		j = 32;
		do
		{
			seed *= 1566083941L;
			u = (u >> 1) | (++seed & (1UL << 31));
		} while(--j);
		*prx++ = u;
	} while(--i);

	*(--prx) = (0xffffffff & (*prx << 23)) ^ (*rx >> 9) ^ *(rx + 15);
	p3 = prx++;
	while(prx != prx_end)
		*prx++ = (0xffffffff & (*p1++ << 23)) ^ (*p2++ >> 9) ^ *p3++;

	while(nwarm1--)	rnd521();		/* wram up */
	prx = rx + 520;
	while(nwarm2--)	mrnd();
}

void rnd521(void)
{
	ULONG *p = rx, *q = rx + 489, *r = rx;

	while(q != prx_end)	*p++ ^= *q++;
	while(p != prx_end)	*p++ ^= *r++;
	prx = rx;
}

ULONG mrnd(void)
{
	if(++prx == prx_end)	rnd521();
	return *prx;
}

void xor(char *in, char *out, int n)
{
	char *p = in, *q = out;

	do
	{
		*q++ ^= *p++;
	} while(--n);
}

ULONG char_to_long(char *a)
{
	char *p = a, *q = mb3;

	*q-- = *p++;
	*q-- = *p++;
	*q-- = *p++;
	*q   = *p;
	return m.a;
}

void long_to_char(ULONG m1, ULONG m2)
{
	char *p = initial, *q = mb3, *r = mb3;

	m.a = m1;
	*p++ = *q--;
	*p++ = *q--;
	*p++ = *q--;
	*p++ = *q;
	m.a = m2;
	*p++ = *r--;
	*p++ = *r--;
	*p++ = *r--;
	*p   = *r;
}

void rot_initial1(char *set)
{
	memcpy(initial, initial4, 4);
	memcpy(initial4, set, 4);
}

void rot_initial2(char *set)
{
	memcpy(initial, set, 8);
}

void rot_initial3(char *set)
{
	ULONG m2;

	m2 = char_to_long(initial4);
	long_to_char((char_to_long(initial) << lshift) + (m2 >> rshift),
				 (m2 << lshift) + (char_to_long(set) >> rshift));
}

void rot_initial4(char *set)
{
	ULONG s1;

	s1 = char_to_long(set);
	long_to_char((char_to_long(initial4) << lshift) + (s1 >> rshift),
				 (s1 << lshift) + (char_to_long(set + 4) >> rshift));
}

void init_crypt(void)
{
	char *p, *q;
	int i, j, k;

	buffer = (char *)malloc(sizeof(char) * BUFFER_SIZE);
	if(buffer != NULL)
	{
		rfunc = readdata1;
		wfunc = writedata1;
		bp = buffer;
		bufsize1 = (BUFFER_SIZE / blen) * blen;
	}
	else
	{
		rfunc = readdata2;
		wfunc = writedata2;
		printf("abort buffer allocation (%ldKB)\n", BUFFER_SIZE / 1024);
	}

	p = keybuf;
	i = 16;
	do
	{
		if(*p >= '0' && *p <= '9')		*p -= '0';
		else if(*p >= 'a' && *p <= 'f')	*p = *p - 'a' + 10;
		else if(*p >= 'A' && *p <= 'F')	*p = *p - 'A' + 10;
		else *p &= 0x0f;
		p++;
	} while(--i);

	p = keybuf;
	q = initial;
	i = 8;
	do
	{
		j = *p++;
		k = (j & 2) * 1024 + (j & 4) * 256 + (j & 1) * 512 + (j & 8) * 32;
		j = *p++;
		*q++ = k + ((j & 2) * 4 + (j & 4) + (j & 1) * 2 + (j >> 3) & 1);
	} while(--i);

	p = keybuf;
	seed = *p++;
	i = 7;
	do
	{
		seed *= 16;
		seed += *p++;
	} while(--i);  
	nwarm1 += (keybuf[8] ^ keybuf[15]);
	nwarm2 += (keybuf[9] ^ keybuf[14]);

	llen = blen * 8 + blena * (keybuf[10] ^ keybuf[13])/ 2;

	init_mrnd();

	j = 1 + (keybuf[11] ^ keybuf[12]);
	do
	{
		make_rndkey();
		xor(rndkey, initial, 8);
	} while(--j);
}

void make_rndkey(void)
{
	ULONG mm[2];

	mm[0] = mrnd();
	mm[1] = mrnd();
	memcpy(rndkey, mm, 8);
}

void ini_shift1(void)
{
	memcpy(initial4, initial, 4);
	setmem(initial, 4, 0);
}

void ini_shift2(void)
{
	setmem(initial, 8, 0);
}

void ini_shift3(void)
{
	long_to_char(0, (char_to_long(initial) >> rshift));
}

void ini_shift4(void)
{
	ULONG m1;

	m1 = char_to_long(initial);
	long_to_char((m1 >> rshift), ((m1 << lshift) + (char_to_long(initial4) >> rshift)));
}

void vernam(void)
{
	while((count = rfunc()) != 0)
	{
		if(count != blen)	setmem(buf + count, blen - count, 0);
		make_rndkey();
		xor(initial, rndkey, 8);
		xor(rndkey, buf, blen);
		wfunc(buf);
		rot_i(rndkey);
	}
}

int readdata1(void)
{
	ULONG i;

	if(bp == buffer)
	{
		i = fread(buffer, sizeof(char), bufsize1, fp1);
		blast = buffer + i;
	}
	else	i = blast - bp;
	memcpy(buf, bp, blen);
	return (i >= blen)? blen: i;
}

int readdata2(void)
{
	return fread(buf, sizeof(char), blen, fp1);
}

void writedata1(char *data)
{
	memcpy(bp, data, blen);
	if((bp += blen) >= blast)
	{
		fwrite(buffer, sizeof(char), bp - buffer, fp2);
		bp = buffer;
	}
}

void writedata2(char *data)
{
	fwrite(data, sizeof(char), blen, fp2);
}

void openerr(char *kind, char *file)
{
	fprintf(stderr, "Error : %s file(%s) can't open.\n", kind, file);
	exit(-1);
}

void usage(char *prog)
{
	fprintf(stderr, "Usage : %s [-Ffile] [-Kkey] [-Bn] <-E|-D> file1 file2\n", prog);
	fprintf(stderr, "   -F : key file definition\n");
	fprintf(stderr, "        file : key file name\n");
	fprintf(stderr, "   -K : key definition\n");
	fprintf(stderr, "        key : 8byte Hexa-decimal code = 16 characters\n");
	fprintf(stderr, "   -B : Block length direction\n");
	fprintf(stderr, "        n : Bleck length(1 - 8 Bytes, default = 8)\n");
	fprintf(stderr, "   -E : Encryption mode\n");
	fprintf(stderr, "   -D : Decryption mode\n");
	fprintf(stderr, "  file1 : source file\n");
	fprintf(stderr, "  file2 : destination file\n\n");
	exit(0);
}

int main(int argc, char *argv[])
{
	int k, kf = 0, kk = 0;
	char *file1, *file2, *p, *q;

	if(argc < 2 || *argv[1] != '-')	usage(argv[0]);
	k = 1;
	do
	{
		switch(tolower(*(argv[k] + 1)))
		{
		case 'f':	kf = k;
					break;
		case 'k':	kk = k;
					break;
		case 'b':	blen = atoi(argv[k] + 2);
					if(blen < 1 || blen > 8)	usage(argv[0]);
					blena = 8 - blen;
					break;
		case 'd':	if(deflag == 0)	usage(argv[0]);
					deflag = 1;
					break;
		case 'e':	if(deflag == 1)	usage(argv[0]);
					deflag = 0;
					break;
		default:	usage(argv[0]);
		}
	} while(*argv[++k] == '-');

	if(deflag == -1 || k >= argc)	usage(argv[0]);
	file1 = argv[k++];
	if(k >= argc)	usage(argv[0]);
	file2 = argv[k];

	if(kk != 0)
	{
		p = argv[kk] + 2;
		q = keybuf;
		k = 0;
		while(k++ < 16 && *p != '\0')	*q++ = *p++;
	}
	else if(kf != 0)
	{
		fp1 = fopen(argv[kf] + 2, "rb");
		if(fp1 == NULL)	openerr("key", argv[kf] + 2);
		fread(keybuf, sizeof(char), 16, fp1);
		fclose(fp1);
	}

	init_crypt();

	if(llen == 32)
	{
		ini_s = ini_shift1;
		rot_i = rot_initial1;
	}
	else if(llen == 64)
	{
		ini_s = ini_shift2;
		rot_i = rot_initial2;
	}
	else if(llen < 32)
	{
		ini_s = ini_shift3;
		rot_i = rot_initial3;
		lshift = llen;
		rshift = 32 - lshift;
	}
	else
	{
		ini_s = ini_shift4;
		rot_i = rot_initial4;
		lshift = llen - 32;
		rshift = 32 - lshift;
	}

	fp1 = fopen(file1, "rb");
	if(fp1 == NULL)	openerr("source", file1);
	fp2 = fopen(file2, "rb");
	if(fp2 != NULL)
	{
		fprintf(stderr, "Error : destination file(%s) already exist.\n", file2);
		exit(-1);
	}
	fp2 = fopen(file2, "wb");

	vernam();

	if(wfunc == writedata1)	fwrite(buffer, sizeof(char), bp - buffer, fp2);

	fclose(fp1);
	fclose(fp2);

	return 0;
}

マニュアル           top (トップに戻る)

高速ファイル暗号化ツール(バーナム暗号)

  1. 使用法
    書式 : vernam [-Ffile] [-Kkey] [-Bn] <-E|-D> in-file out-file

    [ オプション ]
    -Ffile : 暗号鍵ファイル指定
    -Kkey : 暗号鍵指定
      暗号鍵 : 8バイトの16進数(16桁)
        (ファイル指定優先,無指定時は暗号鍵=all 0と設定)
    -Bn : 処理ブロック長指定(n は1〜8)(無指定時 n = 8)
    -E : 暗号化指定
    -D : 復号化指定
    in-file : 処理対象ファイル名
    out-file : 結果ファイル名
    [ 注意 ]
        
    • -E,-D オプションは必ずどちらかを指定する。
    • 処理対象ファイル名、結果ファイル名は必ず指定する。
    • 結果ファイルは新規作成する為、既存ファイル名を指定するとエラー。
    • 復号化は、必ず暗号化と同じ暗号鍵、処理ブロック長で行う。
      (同じ暗号鍵でも、処理ブロック長が異なると結果ファイルは異なる。)
    • 暗号鍵は8バイト(16桁)の16進数(0〜9、A〜F)で指定するが、
      16進数以外の文字セットで指定しても、各文字の下位4ビットを16進数
      と見なして判断する。
    • 暗号鍵ファイル指定の場合は、ファイル先頭の16文字を暗号鍵とする。
      従って、任意のファイル(EXEファイルも可)を指定できる。

  2. 参考文献
    1)現代暗号理論  池野伸一,小山謙二 共著  電子通信学会編
    2)ネットワーク・セキュリティ  D.W.Davies,W.L.Price 共著 上園忠弘 監訳  日経マグロウヒル社
    3)コンピュータ アルゴリズム事典  奥村晴彦 著  技術評論社

  3. その他
      
    • 本暗号の基本アルゴリズムは、原文と非常に長い周期の乱数列の排他的論理和を
      とるバーナム暗号である。暗号文だけの情報から原文を再現するのは、まず不可能
      である。

    • バーナム暗号を解読する手段は、
          
      1. 暗号鍵を入手し、暗号化プログラムで復号処理し、解読する。
      2. 原文と暗号文を入手し、両者の排他的論理和をとり、乱数列を解読する。
        以後、同じ乱数列を用いたものは全て解読される。
        また、乱数列と使用した乱数アルゴリズムが判明していれば、
        暗号鍵(乱数初期値)が判明する。

    • 従ってバーナム暗号の解読を防止する手段としては、下記があげられる。
          
      1. 原文及び暗号鍵は、非公開とする。
      2. 暗号鍵を使用するたびに変更する。
      3. 乱数アルゴリズムを非公開とする。
        (本プログラム・ソースの乱数ルーチンを任意の他のルーチンに交換した方がより安全である。)