ライトニングさんが「C で MH暗号を作れ」という課題を出され、
かずまが、Wikipediaの Merkle-Hellmanナップサック暗号 をコーディングしたものです。
以下、次のプログラムをもとに MH暗号について議論します。
#include <stdio.h> // cin, cout
#include <stdlib.h> // rand, srand
#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) // b を法とする整数の合同における a の逆元
{
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) // 配列の表示
{
printf("%s = [", name);
for (int i = 0; i < n; i++) printf(" %d", a[i]);
printf(" ]\n");
}
void print_bin(const char *name, const int *a, int n) // 配列の表示
{
printf("%s = [ ", name);
for (int i = 0; i < n; i++) printf("%d", a[i]);
printf(" ]\n");
}
int input_data(int *a) // データの入力
{
char buf[256]; int i;
printf("\nEnter less than %d binary digits\n", N);
if (!fgets(buf, sizeof buf, stdin)) return 0;
for (i = 0; i < N; i++)
if (buf[i] == '1') a[i] = 1;
else if (buf[i] == '0') a[i] = 0;
else break;
return i; // ビット数を返す
}
void generate_keys(int *b, int *w, int *q, int *r) // 鍵の生成
{
int sum;
sum = 0;
for (int i = 0; i < N; i++) // 超増加列(秘密鍵 w)の生成
sum += w[i] = sum + 1 + rand() % 10;
print_array(" w", w, N);
printf(" sum = %d\n", sum);
*q = sum + 1 + rand() % 10; // 秘密鍵 q の生成
for (*r = *q / 2; gcd(*r, *q) != 1; ++*r) ; // 秘密鍵 r の生成
for (int i = 0; i < N; i++) b[i] = *r * w[i] % *q; // 公開鍵 b の生成
}
int encrypt(int n, const int *a, const int *b) // 暗号化
{
int c = 0, i;
for (i = 0; i < n; i++)
if (a[i] == 1) c += b[i];
return c;
}
int decrypt(int n, int c, const int *w, int q, int r, int *s) // 復号
{
int i, sum = c * inverse(r, q) % q;
for (i = n; --i >= 0; )
if (sum < w[i]) s[i] = 0;
else sum -= w[i], s[i] = 1;
return sum != 0; // 復号できれば 0、そうでなければ 1 を返す
}
int main(void)
{
int w[N], q, r; // 秘密鍵
int b[N]; // 公開鍵
int a[N]; // 暗号化する前のデータ
int c; // 暗号化されたデータ
int s[N]; // 復号されたデータ
int n; // ビット数
srand(time(0));
generate_keys(b, w, &q, &r); // 鍵の生成
printf("Private key\n");
print_array(" w", w, N);
printf(" q = %d\n r = %d\n", q, r);
printf("Public key\n");
print_array(" b", b, N);
while (n = input_data(a)) { // データの入力
print_bin(" a", a, n); // 入力データの表示
c = encrypt(n, a, b); // 暗号化
printf(" c = %d\n", c); // 暗号化されたデータの表示
decrypt(n, c, w, q, r, s); // 復号
print_bin(" s", s, n); // 復号されたデータの表示
}
return 0;
}
w = [ 8 15 32 65 126 250 504 1010 2012 4032 8059 16117 ]
sum = 32230
Private key
w = [ 8 15 32 65 126 250 504 1010 2012 4032 8059 16117 ]
q = 32240
r = 16121
Public key
b = [ 8 16135 32 16185 126 250 504 1010 2012 4032 24179 32237 ]
Enter less than 12 binary digits
101011001111
a = [ 101011001111 ]
c = 62876
s = [ 101011001111 ]
Enter less than 12 binary digits
111
a = [ 111 ]
c = 16175
s = [ 111 ]
Enter less than 12 binary digits
1010110011
a = [ 1010110011 ]
c = 6460
s = [ 1010110011 ]
Enter less than 12 binary digits
.