ライトニング さんが書きました:自分の中でもあまり理解出来ておらず説明が難しいのですが
SO=超増加数列で
超増加数列でも1bit,3bit,5bitと奇数番目のみ超増加していく数列です。
SO は、Shift-Odd Sequence で、奇数シフト数列です。
SI は、Super-Increasing Sequence で、超増加数列です。
超増加数列は、(3, 7, 12, 25, 48, 99) のように、ある項が、直前までのすべて
の項の和より大きい数列です。
この数列の特徴は、部分和が与えられると、どの項の和であるかが分かることです。
例えば、67 になるのは 7 + 12 + 48 だけです。
(1, 3, 5, 7, 9, 11) は増加数列ですが、超増加ではないので、12 が与えられて
も、それが 3 + 9 なのか、5 + 7 なのか分かりません。
67 は、48 の直前まで総和が 48 より小さいので 48 を含むことが分かります。
残りは、67 - 48 = 19 ですが、これは 25 を含まない。
12 を含み、残りが 7 だと分かります。
入力の平文のビット列から、どの項の部分和かが決まり、その部分和が暗号文です。
ただ、この超増加数列をそのまま、公開すると、だれでも暗号化も復号もできて
しまいます。
だから、互いに素である 2つの整数 a, b を使い、数列の各項を a倍して b で
割った余りを公開鍵にするのです。公開鍵からは a と b を求めることはできない
ので安全になります。a と b を知っていると mod b における a の逆元を使い、
暗号文を部分和に戻すことができます。
ここまでが、Marcle-Hellmanナップサック暗号でした。
奇数シフト数列は、奇数数列の各項を、(項番号-1)ビット左にシフトしたものです。
例えば、(1, 5, 3, 7, 9, 3) という奇数数列から、
(1<<0. 5<<1, 3<<2, 7<<3, 9<<4, 3<<5)、すなわち
(1*1, 5*2, 3*4, 7*8, 9*16, 3*32)、すなわち
(1, 10, 12, 56, 144, 96) という奇数シフト数列が作れます。
各項を 2進で表すと、
(00000001, 00001010, 00001100, 00111000, 10010000, 01100000)
最下位の 1 が右(LSB) から何ビット目にあるかで、第何項かが分かります。
部分和が与えられると、最下位の 1 が何ビット目にあるかで、それがどの項を
含むかが分かり、その値を引くことで、残りの部分和の項も分かります。
この 2つの数列を組み合わせることで、複雑な暗号ができます。
例えば、偶数番目の項は SI を、奇数番目の項は SO を使うとします。
ここで注意しなければならないのは、SO は下位のビットから処理しなければ
ならないので、数列の初項から見ていきますが、SI は大きな値から処理して
いかなければならないので、数列の末項から見ていきます。
そこで、数列を逆順にしておきます。
SI = (99, 48, 25, 12, 7, 3)
SO = (1, 10, 12, 56, 144, 96)
SI は偶数番目だけ、SO は奇数番目だけとすると、
SI = (xx, 48, xx, 12, xx, 3)
SO = (1, xx, 12, xx, 144, xx)
xx は使わないので何でもよいことになります。
SI の xx を小さな値にすることによって、値の増加を少なくすることができます。
SI の総和より大きい値を X とすると、
SOSI
= SO * X + SI で、2つの数列を合成でき、
SOSI の部分和 M を作ると、
Q = M / X
R = M % X
で、SO の部分和 Q と、SI の部分和 R に分離することができます。
(a1, a2, ..., a6) という数列は、
C のプログラムでは、a[0], a[1], ..., a[5] という配列になって、
添え字の奇数偶数が入れ替わることに注意が必要です。
このように考えてできたのが次のプログラムです。
コード:
#include <stdio.h> // printf, putchar, fgets, stdin
#include <stdlib.h> // srand, rand
#include <time.h> // time
#define N 12
int gcd(int a, int b) // 最大公約数
{
int r;
while (b != 0)
r = a % b, a = b, b = r;
return a;
}
int inverse(int a, int b) // a の逆元 (mod b)
{
int x1 = 1, y1 = 0, z1 = a, x2 = 0, y2 = 1, z2 = b, q, t;
while (z2 > 1)
#define STEP(a, b) (t = a - q * b, a = b, b = t)
q = z1 / z2, STEP(x1, x2), STEP(y1, y2), STEP(z1, z2);
return x2 < 0 ? x2 + b : x2;
}
void print_array(const char *name, const int *a, int n)
{
int i;
printf(" %s = [ %d", name, a[0]);
for (i = 1; i < n; i++) printf(" %d", a[i]);
printf(" ]\n");
}
int input_message(int *m)
{
int i; char buf[256];
printf(">> ");
if (!fgets(buf, sizeof buf, stdin)) return 0;
for (i = 0; i < N && (buf[i] | 1) == '1'; i++)
m[i] = buf[i] - '0';
return i;
}
void generate_keys(int *si, int *so, int *pub, int *pX, int *pP, int *pw)
{
int sosi[N], i = N, X = 0, P, w;
while (--i >= 0)
if (i & 1)
X += si[i] = X + 1 + rand() % 10; // 秘密鍵 si の生成
else
X += si[i] = 1 + rand() % 10; // ダミーの si の生成
X += 1 + rand() % 10; // 秘密鍵 X の生成
for (i = 0; i < N; i++)
so[i] = (rand() % 10 | 1) << i; // 秘密鍵 so の生成
for (P = i = 0; i < N; i++)
P += sosi[i] = so[i] * X + si[i];
P += 1 + rand() % 10; // 秘密鍵 P の生成
for (w = P / 2; gcd(w, P) != 1; w++) ; // 秘密鍵 w の生成
for (i = 0; i < N; i++)
pub[i] = (long long)w * sosi[i] % P; // 公開鍵 pub の生成
*pX = X, *pP = P, *pw = w;
}
int encrypt(const int *m, int *pub, int n)
{
int M = 0, i;
for (i = 0; i < n; i++)
if (m[i]) M += pub[i];
return M;
}
int decrypt(int C, const int *si, const int *so,
int X, int P, int inv_w, int *m, int n)
{
int M = (long long)C * inv_w % P;
int Q = M / X, R = M % X, i;
for (i = 0; i < n; i++)
if (i & 1)
if (R >= si[i])
m[i] = 1, R -= si[i], Q -= so[i];
else
m[i] = 0;
else
if (Q >> i & 1) // if (Q % (1 << i+1) == 1 << i)
m[i] = 1, R -= si[i], Q -= so[i];
else
m[i] = 0;
return R | Q; // 0:成功, 0以外:失敗
}
int main(void)
{
int si[N], so[N]; // 秘密鍵
int X, P, w, inv_w; // 秘密鍵
int pub[N]; // 公開鍵
int m[N]; // 入力メッセージ
int n; // 入力ビット長
int C; // 暗号文
int d[N]; // 復号データ
srand(time(0));
generate_keys(si, so, pub, &X, &P, &w);
inv_w = inverse(w, P);
print_array("si", si, N);
print_array("so", so, N);
printf(" X = %d\n", X);
printf(" P = %d\n", P);
printf(" w = %d\n", w);
printf(" inv_w = %d\n", inv_w);
print_array("public key", pub, N);
while ((n = input_message(m)) != 0) {
printf(" length = %d\n", n);
print_array("message input ", m, n);
C = encrypt(m, pub, n);
printf(" cypher text = %d\n", C);
decrypt(C, si, so, X, P, inv_w, d, n);
print_array("decrypted data", d, n);
}
return 0;
}
NTL を使うため C++ に書き直すと、
コード:
#include <NTL/ZZ.h>
#include <stdlib.h> // srand, rand
#include <time.h> // time
using namespace std;
using namespace NTL;
#define N 50
#define DISP_N 15
ZZ gcd(ZZ a, ZZ b) // 最大公約数
{
ZZ r;
while (b != 0)
r = a % b, a = b, b = r;
return a;
}
ZZ inverse(ZZ a, ZZ b) // a の逆元 (mod b)
{
ZZ x1, y1, z1, x2, y2, z2, q, t;
x1 = 1, y1 = 0, z1 = a, x2 = 0, y2 = 1, z2 = b;
while (z2 > 1)
#define STEP(a, b) (t = a - q * b, a = b, b = t)
q = z1 / z2, STEP(x1, x2), STEP(y1, y2), STEP(z1, z2);
return x2 < 0 ? x2 + b : x2;
}
void print_array(const char *name, const ZZ *a, int n)
{
cout << " " << name << " = [ " << a[0];
if (n > DISP_N) n = DISP_N;
for (int i = 1; i < n; i++) cout << " " << a[i];
cout << " ]\n";
}
void print_bin(const char *name, const int *a, int n)
{
cout << " " << name << " = [ " << a[0];
for (int i = 1; i < n; i++) cout << a[i];
cout << " ]\n";
}
int input_message(int *m)
{
int i;
string buf;
cout << ">> ";
if (!getline(cin, buf)) return 0;
for (i = 0; i < N && (buf[i] | 1) == '1'; i++)
m[i] = buf[i] - '0';
return i;
}
void generate_keys(ZZ *si, ZZ *so, ZZ *pub, ZZ *pX, ZZ *pP, ZZ *pw)
{
ZZ sosi[N], X, P, w, t;
int i = N;
X = 0;
while (--i >= 0)
if (i & 1)
X += si[i] = X + 1 + rand() % 10; // 秘密鍵 si の生成
else
X += si[i] = 1 + rand() % 10; // ダミーの si の生成
X += 1 + rand() % 10; // 秘密鍵 X の生成
for (i = 0; i < N; i++) {
t = rand() % 10 | 1;
so[i] = t << i; // 秘密鍵 so の生成
}
for (P = i = 0; i < N; i++)
P += sosi[i] = so[i] * X + si[i];
P += 1 + rand() % 10; // 秘密鍵 P の生成
for (w = P / 2; gcd(w, P) != 1; w++) ; // 秘密鍵 w の生成
for (i = 0; i < N; i++)
pub[i] = w * sosi[i] % P; // 公開鍵 pub の生成
*pX = X, *pP = P, *pw = w;
}
ZZ encrypt(const int *m, ZZ *pub, int n)
{
ZZ M;
M = 0;
for (int i = 0; i < n; i++)
if (m[i]) M += pub[i];
return M;
}
int decrypt(ZZ C, const ZZ *si, const ZZ *so,
ZZ X, ZZ P, ZZ inv_w, int *m, int n)
{
ZZ M = C * inv_w % P;
ZZ Q = M / X, R = M % X;
for (int i = 0; i < n; i++)
if (i & 1)
if (R >= si[i])
m[i] = 1, R -= si[i], Q -= so[i];
else
m[i] = 0;
else
if ((Q >> i) % 2 == 1) // if (Q % (1 << i+1) == 1 << i)
m[i] = 1, R -= si[i], Q -= so[i];
else
m[i] = 0;
return R != 0 || Q != 0; // 0:成功, 0以外:失敗
}
int main(void)
{
ZZ si[N], so[N]; // 秘密鍵
ZZ X, P, w, inv_w; // 秘密鍵
ZZ pub[N]; // 公開鍵
int m[N]; // 入力メッセージ
int n; // 入力ビット長
ZZ C; // 暗号文
int d[N]; // 復号データ
srand(time(0));
generate_keys(si, so, pub, &X, &P, &w);
inv_w = inverse(w, P);
print_array("si", si, N);
print_array("so", so, N);
cout << " X = " << X << "\n";
cout << " P = " << P << "\n";
cout << " w = " << w << "\n";
cout << " inv_w = " << inv_w << "\n";
print_array("public key", pub, N);
while ((n = input_message(m)) != 0) {
cout << " length = " << n << "\n";
print_bin("message input ", m, n);
C = encrypt(m, pub, n);
cout << " cypher text = " << C << "\n";
decrypt(C, si, so, X, P, inv_w, d, n);
print_bin("decrypted data", d, n);
}
return 0;
}