格納
Re: 格納
「MH暗号を作る」が課題の全文ですか?ライトニング さんが書きました:課題の内容はMH暗号を作るという課題です。
こういう入力に対して、こういう出力が表示されるように、
などの指定はないんですか?
プログラミング言語の指定もないんですか?
No. 44 のプログラムの 79~88行目と、No.55 のプログラムの 59~67行目で既にライトニング さんが書きました:ほとんど今までも丸投げ質問でしたが最後の復号部分の作り方を教えていただけないでしょうか?
作り方を教えたはずなのですが、見ていないのですか?
分からないところは質問してくれれば、なんでも答えるつもりでいるんですけど。
Re: 格納
Wikipediaの Merkle-Hellmanナップサック暗号 のページの左側に「他言語版」がライトニング さんが書きました: C言語の参考書などを読んでるのですがMH暗号を理解するのも難しく
あって「English」を選ぶと、そこには、小学生でもわかりそうな具体的な例が
載っていて、詳しく解説されています。
Re: 格納
No.44 のプログラムに、間違い発見。
暗号データを 2進 10ビットで表示していますが、これは公開鍵 b の部分和なので
もっと大きな数です。表示を改めます。
また、r の値をユーザが入力するのではなく、内部で求めるようにしました。
10ビットの 2進数をいろいろ入力してみてください。
暗号データを 2進 10ビットで表示していますが、これは公開鍵 b の部分和なので
もっと大きな数です。表示を改めます。
また、r の値をユーザが入力するのではなく、内部で求めるようにしました。
#include <stdio.h>
#include <stdlib.h>
#define N 10
int gcd(int a, int b)
{
int r;
while (b)
r = a % b, a = b, b = r;
return a;
}
int inverse(int a, int 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, int a[N])
{
int i;
printf("%s = [", name);
for (i = 0; i < N; i++) printf(" %d", a[i]);
printf(" ]\n");
}
void print_bin(const char *name, int b)
{
int i;
printf("%s = [", name);
for (i = N; --i >= 0; ) printf(" %d", b >> i & 1);
printf(" ]\n");
}
int input_data(const char *name, int *a)
{
int i = 0;
char buf[256];
printf("\nEnter %d binary digits\n", N);
printf("%.*s\n", N, "123456789.123456789.123456789.12");
if (!fgets(buf, sizeof buf, stdin)) exit(1);
*a = 0;
for (i = 0; i < N; i++)
if (buf[i] == '1') *a |= 1 << (N - i - 1);
else if (buf[i] != '0') break;
return i == N;
}
void generate_keys(int b[N], int w[N], int *q, int *r)
{
int i, sum = 0;
for (i = 0; i < N; i++)
sum += w[i] = sum + 1 + rand() % 10;
*q = sum + 1 + rand() % 10;
print_array(" w", w);
printf(" sum = %d\n", sum);
for (*r = *q /2; gcd(*r, *q) != 1; ++*r) ;
for (i = 0; i < N; i++)
b[i] = (long long)*r * w[i] % *q;
}
int encrypt(int a, int b[N])
{
int i, c = 0;
for (i = 0; i < N; i++)
if (a >> (N - i - 1) & 1) c += b[i];
return c;
}
int decrypt(const int c, const int w[N], int q, int r)
{
int i, s = 0, c1 = (long long)c * inverse(r, q) % q;
for (i = N; --i >= 0; )
if (c1 >= w[i]) {
c1 -= w[i];
s |= 1 << (N - i - 1);
}
return s;
}
int main(void)
{
int w[N], q, r; // private key
int b[N]; // public key
int a; // input data
int c; // encrypted data
int s; // decrypted data
generate_keys(b, w, &q, &r);
printf("Private key\n");
print_array(" w", w);
printf(" q = %d\n r = %d\n", q, r);
printf("Public key\n");
print_array(" b", b);
while (input_data("a", &a)) {
print_bin(" a", a);
c = encrypt(a, b);
printf(" c = %d\n", c);
s = decrypt(c, w, q, r);
print_bin(" s", s);
}
return 0;
}
Re: 格納
プログラムは複数あります。ライトニング さんが書きました:後実行してみたのですが
warning: format string is empty [-Wformat-zero-length]
printf("",sum);
^~
こういうエラーを吐かれてしまったのですがどう直せばいいのでしょうか?
どのプログラムを実行したのか書きましょう。
エラーメッセージは、出たものをそのままコピペしましょう。
ファイル名と「行番号」があるはずです。
英語だからといって、内容を読まないのですか?
「警告: 書式文字列がカラです。printf("",sum);」
どう直せばいいかは、No.44 で回答済みです。見ていないんですか?
Re: 格納
「公開鍵の中から自分で何か部分和を作り」ですか。「何か」はないでしょう。ライトニング さんが書きました: また復号は公開鍵の中から自分で何か部分和を作りその部分和にxmodyの逆元をかけると秘密鍵の数列の要素の部分和になっているみたいです。
暗号化したい 10ビットのデータ(これを平文と言います)のうち、1の立っている
ビットが何番目にあるかで、公開鍵の何番目の数値を使うかを決めるんです。
そうしてできた部分和が暗号化データ(暗号文と言います)です。
ここからが復号です。
「xmodyの逆元」という意味不明の用語を使わないでください。
「x を法とする合同の y の逆元」とか「モジュロ x の y の逆元」とか
「mod x における y の逆元」と言いましょう。
暗号データ(部分和)にその「y の逆元」を掛けて、x で割った余りが
秘密鍵の部分和になります。
0 にはなるんです。もっと重要なのは、何番目が引けたかということで、それにライトニング さんが書きました: その部分和を秘密鍵の大きい値から順番に引けるか判定をしていき最終的に0になればいいみたいです。
よって 10ビットのデータの何番目が 1 なのかが分かり、元の平文が復号できた
ことになります。
[9] [5] [2] とうまく出てるじゃないですか。ライトニング さんが書きました: それをNo.55の回答でうまくだしたいのですがどうすればいいのでしょうか?
0010010001 と表示したいということですか?
No.44 の訂正版である No.64 のプログラムの中の decrypt() や print_bin()
を参考にすればいいだけのことでは?
それとも、全部引き終わったらゼロになったことを確認したいのですか?
それなら、No.64 のプログラムでは復号の最後で if (c1 == 0) printf("OK\n");。
No.55 のプログラムなら、if (xmody == 0) printf("OK\n"); でいいのでは?
Re: 格納
復号結果の[9] [5] [2] はどのような判定をしてこれが出力されたのでしょうか?
また何箇所か理解できない部分があるんですが
1.(long long)kakunou
2.for (i = NUM; --i >= 0; ) {
if (xmody >= kakunou) {
xmody -= kakunou;
printf(" [%d]", i);
3.for (;;)
4.while (b)
r = a % b, a = b, b = r;
5.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;
この辺りのプログラミングの内容がよくわからないのですがどういう事なのでしょうか?
また何箇所か理解できない部分があるんですが
1.(long long)kakunou
2.for (i = NUM; --i >= 0; ) {
if (xmody >= kakunou) {
xmody -= kakunou;
printf(" [%d]", i);
3.for (;;)
4.while (b)
r = a % b, a = b, b = r;
5.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;
この辺りのプログラミングの内容がよくわからないのですがどういう事なのでしょうか?
Re: 格納
No.55 のプログラムですね。ライトニング さんが書きました:復号結果の[9] [5] [2] はどのような判定をしてこれが出力されたのでしょうか?
printf("暗号: %d\n", xmody);
xmody = (long long)xmody * inverse(y, x) % x;
printf("復号:");
for (i = NUM; --i >= 0; ) {
if (xmody >= kakunou[i]) {
xmody -= kakunou[i];
printf(" [%d]", i);
}
}
printf("\n");
これに、(mod x) における y の逆元である inverse(y, x) を掛けて、
その結果を x で割った余りが、秘密鍵である kakunou の部分和です。
ここでは、それをまた xmody に代入しています。
超増加列である kakunou の項の大きいほうから順に、すなわち
kakunou[N-1], kakunou[N-2], ..., kakunou[0] を xmody から、
引けるなら引いて、その位置を表示しています。
これが、MHナップサック暗号の復号手順で、復号した平文の
1の立っているビットの位置だけを表示したものです。
公開鍵を作るところですね。ライトニング さんが書きました:1.(long long)kakunou
NUM が 10 の場合、kakunou や y は 1万未満の値なので kakunou * y は
1億未満の値となり、32ビットの int でオーバーフローしませんが、たとえば、
NUM が 20 の場合、kakunou や y は 1億未満の値なので kakunou * y は
32ビットの int の最大値である 2147483647 を超えてしまいます。
そこで、掛け算をする前に kakunou の値を (long long) すなわち 64ビットの
int に変換して、y を掛けてもオーバーフローしないようにしています。その
掛け算の結果を y で割ると、また 32ビットの int に収まる小さな値になります。
ライトニング さんが書きました:2.for (i = NUM; --i >= 0; ) {
if (xmody >= kakunou) {
xmody -= kakunou;
printf(" [%d]", i);
既に説明しました。
ライトニング さんが書きました:3.for (;;)
int get_prime(int sum)
{
int x;
printf("数列の項の合計 = %d\n", sum);
for (;;)
{
printf("x の値は? ");
if (scanf("%d",&x) != 1)
{
exit(1);
}
if (x >= sum)
return x;
else
puts("数列の合計値よりxが小さいです。");
}
}
その無限ループからは、return文や break文や exit関数の呼び出しなどで
抜けだします。
最大公約数を求めるところですね。ライトニング さんが書きました:4.while (b)
r = a % b, a = b, b = r;
次のように書いても同じです。これは説明不要ですよね。
ライトニング さんが書きました:5.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;
int inverse(int a, int 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;
}
#define STEP(a, b) は関数型マクロと言って、これ以降に、STEP という名前が
出てくるところはすべて (t = a - q * b, a = b, b = t) の文字列に置き換え
ますが、引数の a, b は与えられた引数に置き換えます。
ということで次のようになります。
int inverse(int a, int b)
{
int x1 = 1, y1 = 0, z1 = a, x2 = 0, y2 = 1, z2 = b, q, t;
while (z2 > 1)
q = z1 / z2,
(t = x1 - q * x2, x1 = x2, x2 = t),
(t = y1 - q * y2, y1 = y2, y2 = t),
(t = z1 - q * z2, z1 = z2, z2 = t);
return x2 < 0 ? x2 + b : x2;
}
Re: 格納
かずま さんが書きました: 公開鍵を作るところですね。
NUM が 10 の場合、kakunou や y は 1万未満の値なので kakunou * y は
1億未満の値となり、32ビットの int でオーバーフローしませんが、たとえば、
NUM が 20 の場合、kakunou や y は 1億未満の値なので kakunou * y は
32ビットの int の最大値である 2147483647 を超えてしまいます。
そこで、掛け算をする前に kakunou の値を (long long) すなわち 64ビットの
int に変換して、y を掛けてもオーバーフローしないようにしています。その
掛け算の結果を y で割ると、また 32ビットの int に収まる小さな値になります。
最後の文を次のように訂正します。
「その掛け算の結果を x で割ると、その余りは 32ビットの int に収まる小さな値になります。」
ところで、このトピックは解決にはならないんでしょうか?
少なくとも次のトピックは解決になっていいと思うんですが。
数列の作成 と mody逆演算
Re: 格納
C言語の識別子として有効な名前で、他の名前と被らなければいいでしょう。ライトニング さんが書きました:intget_primeやintgcdやint inverse のintの後の部分はどのような名前に変えてもいいんですかね?
はい。ライトニング さんが書きました:同様に(long long)xmodyのlong longの部分
void print_suretu(const char *title, const int *kakunou, int num)
const title const int
なども問題ないのでしょうか?
typedefを用いてC言語の識別子として許される範囲で好きな名前をつけることができます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 格納
NO: 55のコードなら、代わりにmain関数を一番下に持ってくれば、消してもいいでしょう。ライトニング さんが書きました:int get_rand(void);
int get_prime(int sum);
void print_suretu(const char *title, const int *kakunou, int num);
int gcd(int a, int b);
int inverse(int a, int b);
ちなみに最初のこの部分を全部消しても動いたのですが
この部分自体も別に分かりやすく書いてるだけでいらないということでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 格納
intget_prime ではなく、int get_prime ですね。質問は正確に書きましょう。ライトニング さんが書きました: intget_primeやintgcdやint inverse のintの後の部分はどのような名前に変えてもいいんですかね?
関数?との見分けが出来てない部分が何個かありまして...
これは int に関連する名前を宣言しているところです。
名前は、英字で始まり英数字が続くという規則に従えば何でも構いません。
英字には、下線を含みます。だから、get_prime は OK。
int get_prime; と書けば、get_prime は int型の変数です。
int get_prime(int sum); と書けば、get_prime は、int型の値を返す関数です。
これらの宣言で、名前が変数名か関数名か、が分かります。
名前の後に ( がついている宣言の時は、その名前は関数名です。
見分けができてない部分とは具体的にどこでしょうか?
同様にというのは、long long の部分をどのような名前に書き換えてもよいか、ライトニング さんが書きました: 同様に(long long)xmodyのlong longの部分
ということでしょうか?
それはダメです。long long は C のキーワードであり、型名です。
No.55 のプログラムでは、int が 32ビット、long long が 64ビットであるもの
と仮定しています。たいていのコンパイラは、そのようになっていますが、
int が 16ビットのものや、long が 64ビットのものもあります。
これは、質問が不明確です。const title const int って何ですか?ライトニング さんが書きました: void print_suretu(const char *title, const int *kakunou, int num)
const title const int
なども問題ないのでしょうか?
const char *title の const char * は型を表すので変更できません。
title は名前ですから、変更してかまいません。
Re: 格納
動きますが、コンパイル時に警告が出るでしょう。ライトニング さんが書きました:int get_rand(void);
int get_prime(int sum);
void print_suretu(const char *title, const int *kakunou, int num);
int gcd(int a, int b);
int inverse(int a, int b);
ちなみに最初のこの部分を全部消しても動いたのですが
この部分自体も別に分かりやすく書いてるだけでいらないということでしょうか?
VC++ なら、
C:\tmp>cl hm2.c
Microsoft(R) C/C++ Optimizing Compiler Version 18.00.31101 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
hm2.c
hm2.c(97) : error C2371: 'print_suretu' : 再定義されています。異なる基本型です。
C:\tmp>gcc hm2.c
hm2.c:96:6: 警告: 'print_suretu' と型が競合しています
void print_suretu(const char *title, const int *kakunou, int num)
^
hm2.c:51:5: 備考: 前の 'print_suretu' の暗黙的な宣言はここです
print_suretu("秘密鍵: ", kakunou, NUM);
あなたは,他にも、回答者からの多くの質問を無視して答えていませんね。
できるだけ答えてください。
それから、No.64 のプログラムは、ちゃんと見ていますか?
Wikipediaの Merkle-Hellmanナップサック暗号に書いてる通りに
実装しているつもりなので、分かりやすいはずです。
No.55 のプログラムは、変数名が kakunou、newkakunou、x、y、xmody と
意味不明のものばかりで、よくないと思いませんか?
暗号化する平文がプログラムの中に埋め込まれているし、
その埋め込み方も結果の表示もビット列ではなく、
1のビットの番号だけというのは変だと思いませんか?
Re: 格納
答えはそれだけですか?ライトニング さんが書きました: コンパイラはC言語のフリーソフト?みたいなのと
gcc でターミナルでやっています。
あなたは、No.55 のプログラムで、先頭の「関数の宣言」を削除しても
動いたので、これは要らないということか、と聞いてきました。
でも削除するとコンパイラが警告を出すはずなのに、それは書いていません。
だから、どんなコンパイラを使っているのかと尋ねたのです。
そしたら、gcc だという。
質問です。警告は出なかったのですか?
警告は出たけれど、動けばいいと判断したのでしょうか?
コンパイルした後は、実行でしょう。ライトニング さんが書きました:No64の回答していただいのは自分の理解力がなくてどの部分がどういう動きになってるのかよくわからなくて
またコンパイルした後何をしたらいいのかもよくわからなくて...
実行例
w = [ 2 10 17 30 69 133 270 540 1074 2150 ]
sum = 4295
Private key
w = [ 2 10 17 30 69 133 270 540 1074 2150 ]
q = 4301
r = 2150
Public key
b = [ 4300 4296 2142 4286 2116 2084 4166 4031 3764 3226 ]
Enter 10 binary digits
123456789.
0011010110
a = [ 0 0 1 1 0 1 0 1 1 0 ]
c = 16307
s = [ 0 0 1 1 0 1 0 1 1 0 ]
Enter 10 binary digits
123456789.
0000101101
a = [ 0 0 0 0 1 0 1 1 0 1 ]
c = 13539
s = [ 0 0 0 0 1 0 1 1 0 1 ]
Enter 10 binary digits
123456789.
.
さらに秘密鍵と公開鍵を生成して表示します。そして
Enter 10 binary digits
123456789.
と表示して、10個の2進数字を入力せよと言ってきます。
123456789. は間違いなく 10個の数字(0 か 1) を入力するためのガイドです。
10ビットの 2進数を入力すると、それを平文 a として表示し、
暗号化して、c の値として表示し、復号して s を表示します。
a から c へは公開鍵を使って暗号化され、
c から s へは秘密鍵を使って復号されています。
で、質問です。No.64 のプログラムは見てもよくわからないから、
実行してみなかったのですか?
これはどのプログラムのことを言っていますか?ライトニング さんが書きました:後一つ数列の合計値は?までに出てくる最初の数列と
xの値とyの値を入力した後に出てくる秘密鍵の数列が変わってしまうのですがどうすれば良いのでしょうか?
No.55 も No.64 も「数列の合計値は?」とは表示しません。
最初の数列とは何ですか?
新しいプログラムなら、それを提示してください。また実行経過を
[ code=text] と [ /code] で挟んで提示してください。
具体的なデータを示してもらわないと、数列が変わってしまうといわれても
何のことかわかりません。
Re: 格納
No.64 のプログラムが理解されないのは、ライトニング さんが書きました: No64の回答していただいのは自分の理解力がなくてどの部分がどういう動きになってるのかよくわからなくて
入力データである 10個の 1 と 0 を本当の 10ビットのデータに変換して
int の a に入れるビット操作が分かりにくいのだと考えました。
そこで、int a[N]; と配列にして、その要素に 1 か 0 を入れることにしました。
以後は、Wikipediaの Merkle-Hellmanナップサック暗号 を参照しながら、
次のプログラムをよく読んでみてください。
#include <stdio.h> // fgets, printf
#include <stdlib.h> // rand, srand
#include <time.h> // time
#define N 10
int gcd(int a, int b) // 最大公約数
{
int r;
while (b)
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[N]) // 配列の表示
{
int i;
printf("%s = [", name);
for (i = 0; i < N; i++) printf(" %d", a[i]);
printf(" ]\n");
}
int input_data(int a[N]) // データの入力
{
int i;
char buf[256];
printf("\nEnter %d binary digits\n", N);
printf("%.*s\n", N, "123456789.123456789.123456789.12");
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 == N; // i が N だったら 1 を、そうでなかったら 0 を返す
}
void generate_keys(int b[N], int w[N], int *q, int *r) // 鍵の生成
{
int i, sum = 0;
for (i = 0; i < N; i++) // 超増加列(秘密鍵)の生成
sum += w[i] = sum + 1 + rand() % 10;
print_array(" w", w);
printf(" sum = %d\n", sum);
*q = sum + 1 + rand() % 10; // 秘密鍵 q の生成
for (*r = *q /2; gcd(*r, *q) != 1; ++*r) ; // 秘密鍵 r の生成
for (i = 0; i < N; i++) // 公開鍵の生成
b[i] = (long long)*r * w[i] % *q;
}
int encrypt(const int a[N], const int b[N]) // 暗号化
{
int i, c = 0;
for (i = 0; i < N; i++)
if (a[i] == 1) c += b[i];
return c;
}
int decrypt(int c, const int w[N], int q, int r, int s[N]) // 復号
{
int i, sum = (long long)c * inverse(r, q) % q;
for (i = N; --i >= 0; ) {
if (sum >= w[i])
sum -= w[i], s[i] = 1;
else
s[i] = 0;
}
return sum; // 復号できれば 0、そうでなければ正の値を返す
}
int main(void)
{
int w[N], q, r; // 秘密鍵
int b[N]; // 公開鍵
int a[N]; // 暗号化する前のデータ
int c; // 暗号化されたデータ
int s[N]; // 復号されたデータ
srand(time(0));
generate_keys(b, w, &q, &r); // 鍵の生成
printf("Private key\n");
print_array(" w", w);
printf(" q = %d\n r = %d\n", q, r);
printf("Public key\n");
print_array(" b", b);
while (input_data(a)) { // データの入力
print_array(" a", a); // 入力データの表示
c = encrypt(a, b); // 暗号化
printf(" c = %d\n", c); // 暗号化されたデータの表示
decrypt(c, w, q, r, s); // 復号
print_array(" s", s); // 復号されたデータの表示
}
return 0;
}
inverse() の中身は分からなくてかまいません。
Re: 格納
#include<NTL/zz.h>
using namespace std;
using namespace NTL;
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM 10
zz get_rand(void);
zz get_prime(zz sum);
void print_suretu(const char *title, const int *kakunou, int num);
zz gcd(int a, int b);
zz inverse(int a, int b);
void main()
{
zz i, sum;
zz kakunou[NUM], newkakunou[NUM], suretu;
zz x, y, xmody;
srand((unsigned int)time(NULL));
sum = 0;
for (i = 0; i < NUM; i++)
{
suretu = sum + get_rand();
sum = sum + suretu;
kakunou[i] = suretu;
}
x = get_prime(sum);
while (1) {
printf("y の値は? "); scanf("%d", &y);
if (gcd(x, y) == 1) break;
printf("x と y が互いに素ではありません。\n");
}
for (i = 0; i < NUM; i++)
{
newkakunou[i] = kakunou[i] * xmody;
newkakunou[i] = (long long)kakunou[i] * y % x;
}
print_suretu("秘密鍵: ", kakunou, NUM);
printf("合計 = %d, x = %d, y = %d\n", sum, x, y);
print_suretu("公開鍵: ", newkakunou, NUM);
printf("暗号%d",newkakunou[2]+newkakunou[5]+newkakunou[9]);
xmody = newkakunou[2]+newkakunou[5]+newkakunou[9];
printf("暗号: %d\n", xmody);
xmody = (long long)xmody * inverse(y, x) % x;
printf("復号:");
for (i = NUM; --i >= 0; ) {
if (xmody >= kakunou[i]) {
xmody -= kakunou[i];
printf(" [%d]", i);
}
}
printf("\n");
return 0;
}
zz get_rand(void)
{
return rand() % 10 + 1;
}
zz get_prime(int sum)
{
zz x;
printf("数列の項の合計 = %d\n", sum);
for (;;)
{
printf("x の値は? ");
if (scanf("%d",&x) != 1)
{
exit(1);
}
if (x >= sum)
return x;
else
puts("数列の合計値よりxが小さいです。");
}
}
void print_suretu(const char *title, const int *kakunou, int num)
{
zz i;
for (i = 0; i < num; i++)
{
printf("%s%d", i == 0 ? title : ", ", kakunou[i]);
}
putchar('\n');
}
zz gcd(int a, int b)
{
int r;
while (b)
r = a % b, a = b, b = r;
return a;
}
zz inverse(zz a, zz b)
{
zz 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;
}
これをNTLライブラリを使って実行したのですが
zz get_rand(void);
^
mhangou.cpp:12:1: error: unknown type name 'zz'
zz get_prime(zz sum);
^
mhangou.cpp:12:14: error: unknown type name 'zz'
zz get_prime(zz sum);
^
mhangou.cpp:15:1: error: unknown type name 'zz'
zz gcd(int a, int b);
^
mhangou.cpp:16:1: error: unknown type name 'zz'
zz inverse(int a, int b);
^
mhangou.cpp:19:1: error: 'main' must return 'int'
void main()
^~~~
int
mhangou.cpp:21:5: error: unknown type name 'zz'
zz i, sum;
^
mhangou.cpp:22:5: error: unknown type name 'zz'
zz kakunou[NUM], newkakunou[NUM], suretu;
^
mhangou.cpp:23:5: error: unknown type name 'zz'
zz x, y, xmody;
^
mhangou.cpp:27:5: error: use of undeclared identifier 'sum'
sum = 0;
^
mhangou.cpp:30:9: error: use of undeclared identifier 'suretu'
suretu = sum + get_rand();
^
mhangou.cpp:30:18: error: use of undeclared identifier 'sum'; did you mean
'sub'?
suretu = sum + get_rand();
^~~
sub
/usr/local/include/NTL/zz.h:322:13: note: 'sub' declared here
inline void sub(ZZ& x, const ZZ& a, const ZZ& b)
^
mhangou.cpp:30:22: error: arithmetic on a pointer to the function type 'void
(NTL::ZZ &, const NTL::ZZ &, const NTL::ZZ &)'
suretu = sum + get_rand();
~~~ ^
mhangou.cpp:31:9: error: use of undeclared identifier 'sum'
sum = sum + suretu;
^
mhangou.cpp:31:15: error: use of undeclared identifier 'sum'; did you mean
'sub'?
sum = sum + suretu;
^~~
sub
/usr/local/include/NTL/zz.h:322:13: note: 'sub' declared here
inline void sub(ZZ& x, const ZZ& a, const ZZ& b)
^
mhangou.cpp:31:21: error: use of undeclared identifier 'suretu'
sum = sum + suretu;
^
mhangou.cpp:32:22: error: use of undeclared identifier 'suretu'
kakunou = suretu;
^
mhangou.cpp:34:20: error: use of undeclared identifier 'sum'; did you mean
'sub'?
x = get_prime(sum);
^~~
sub
/usr/local/include/NTL/zz.h:322:13: note: 'sub' declared here
inline void sub(ZZ& x, const ZZ& a, const ZZ& b)
^
mhangou.cpp:34:10: error: no matching function for call to 'get_prime'
x = get_prime(sum);
^~~~~~~~~
mhangou.cpp:12:4: note: candidate function not viable: no known conversion from
'void (NTL::ZZ &, const NTL::ZZ &, const NTL::ZZ &)' to 'int' for 1st
argument
zz get_prime(zz sum);
このようなエラーが出ました。C++との違いがよくわからないのですがzzという型数を宣言したのですがうまくいきません。
大きい桁というとはとりあえず50ビット?くらいで試してみたい状況です。
Re: 格納
#include <iostream> // cin, cout
#include <cstdlib> // rand, srand
#include <ctime> // time
#include <NTL/ZZ.h>
using namespace std;
using namespace NTL;
#define N 128
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) // b を法とする整数の合同における a の逆元
{
ZZ x1, y1, z1 = a, x2, y2, z2 = b, q, t;
x1 = 1, y1 = 0, x2 = 0, y2 = 1;
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 << " = [";
if (n > 10) n = 10;
for (int i = 0; i < n; i++) cout << ' ' << a[i];
cout << " ... ]\n";
}
void print_bin(const char *name, const int *a, int n) // 配列の表示
{
cout << name << " = [";
for (int i = 0; i < n; i++) cout << a[i];
cout << " ]\n";
}
int input_data(int *a) // データの入力
{
string buf;
cout << "\nEnter less than " << N << " binary digits\n";
if (!getline(cin, buf)) return 0;
size_t i, n = buf.size();
if (n >= N) 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;
if (i != n) return 0;
return n; // ビット数を返す
}
void generate_keys(ZZ *b, ZZ *w, ZZ& q, ZZ& r) // 鍵の生成
{
ZZ sum;
sum = 0;
for (int i = 0; i < N; i++) // 超増加列(秘密鍵)の生成
sum += w[i] = sum + 1 + rand() % 10;
print_array(" w", w, N);
cout << " sum = " << sum << endl;
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;
}
ZZ encrypt(int n, const int *a, const ZZ *b) // 暗号化
{
ZZ c;
c = 0;
for (int i = 0; i < n; i++)
if (a[i] == 1) c += b[i];
return c;
}
int decrypt(int n, ZZ c, const ZZ *w, ZZ q, ZZ r, int *s) // 復号
{
ZZ sum = c * inverse(r, q) % q;
for (int i = n; --i >= 0; ) {
if (sum >= w[i])
sum -= w[i], s[i] = 1;
else
s[i] = 0;
}
return sum != 0; // 復号できれば 0、そうでなければ 1 を返す
}
int main(void)
{
ZZ w[N], q, r; // 秘密鍵
ZZ b[N]; // 公開鍵
int a[N]; // 暗号化する前のデータ
ZZ c; // 暗号化されたデータ
int s[N]; // 復号されたデータ
int n; // ビット数
srand(time(0));
generate_keys(b, w, q, r); // 鍵の生成
cout << "Private key\n";
print_array(" w", w, N);
cout << " q = " << q << "\n r = " << r << endl;
cout << "Public key\n";
print_array(" b", b, N);
while (n = input_data(a)) { // データの入力
print_bin(" a", a, n); // 入力データの表示
c = encrypt(n, a, b); // 暗号化
cout << " c = " << c << endl; // 暗号化されたデータの表示
decrypt(n, c, w, q, r, s); // 復号
print_bin(" s", s, n); // 復号されたデータの表示
}
return 0;
}
w = [ 8 14 31 57 114 233 458 917 1839 3672 ... ]
sum = 2442393501432829254771146219802651424984
Private key
w = [ 8 14 31 57 114 233 458 917 1839 3672 ... ]
q = 2442393501432829254771146219802651424992
r = 1221196750716414627385573109901325712497
Public key
b = [ 8 14 1221196750716414627385573109901325712527 1221196750716414627385573109901325712553 114 1221196750716414627385573109901325712729 458 1221196750716414627385573109901325713413 1221196750716414627385573109901325714335 3672 ... ]
Enter less than 128 binary digits
0011010100001111010101010011
a = [0011010100001111010101010011 ]
c = 10990770756447731646470157989113456852191
s = [0011010100001111010101010011 ]
Enter less than 128 binary digits
1111111111111111000000000000000000010101010
a = [1111111111111111000000000000000000010101010 ]
c = 14654361008596975528626877339778500203434
s = [1111111111111111000000000000000000010101010 ]
Enter less than 128 binary digits
.
これ以上の質問を受け付けません。
また、以前のトピックを解決にするかどうかも教えてください。
さらに、NTL に関する質問をするなら、
「NTL を使用する Merkle-Hellmanナップサック暗号」という件名の
新しいトピックを立ててください。
Re: 格納
自宅ではEasyIDECと言うのを使用しておりそれで先頭の「関数の宣言」を消しては実行できました。
学校ので使ってるのEmacsをターミナルでコンパイルするとかずまんの言われたとおり警告が出てしまいました。
なので先頭の「関数の宣言」は消さない事にしました。報告を忘れていました。
No64の実行をしてみたのですがその後どのような値を入力するのか分からないのが謎でした。
けれど10bitを何かうつというのをようやく理解でき実行部分までは理解することができました。
実行したのはこのプログラミングです
以前に質問した29行目のprintf("",sum)だとエラーをはかれてしまったのでprintf("%d\n",sum)を治したらこのようになってしまい
[ code=text]
6
17
42
92
193
394
791
1589
3185
6379
数列の合計 = 6379
xの値は? 7000
yの値は? 699
秘密鍵 6,11,25,50,101,397,798,1596,3194
合計=6379 x = 7000 y=6999
公開鍵 6994,6989,6975,6950,6899 6799 6603 6202 5450 3806
暗号 17580 暗号:17580
復号:[9][5][2]
[ /code]
のように少し変になってしました。
すいません。NTLのことも色々聞きたいので新しいトピックを作らせてもらいます。そちらでよろしくお願いします。
学校ので使ってるのEmacsをターミナルでコンパイルするとかずまんの言われたとおり警告が出てしまいました。
なので先頭の「関数の宣言」は消さない事にしました。報告を忘れていました。
No64の実行をしてみたのですがその後どのような値を入力するのか分からないのが謎でした。
けれど10bitを何かうつというのをようやく理解でき実行部分までは理解することができました。
実行したのはこのプログラミングです
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM 10
int get_rand(void);
int get_prime(int sum);
void print_suretu(const char *title, const int *kakunou, int num);
int gcd(int a, int b);
int inverse(int a, int b);
int main(void)
{
int i, sum;
int kakunou[NUM], newkakunou[NUM], suretu;
int x, y, xmody;
srand((unsigned int)time(NULL));
sum = 0;
for (i = 0; i < NUM; i++)
{
suretu = sum + get_rand();
sum = sum + suretu;
kakunou[i] = suretu;
printf("%d\n",sum);
}
x = get_prime(sum);
while (1) {
printf("y の値は? "); scanf("%d", &y);
if (gcd(x, y) == 1) break;
printf("x と y が互いに素ではありません。\n");
}
for (i = 0; i < NUM; i++)
{
newkakunou[i] = kakunou[i] * xmody;
newkakunou[i] = (long long)kakunou[i] * y % x;
}
print_suretu("秘密鍵: ", kakunou, NUM);
printf("合計 = %d, x = %d, y = %d\n", sum, x, y);
print_suretu("公開鍵: ", newkakunou, NUM);
printf("暗号%d",newkakunou[2]+newkakunou[5]+newkakunou[9]);
xmody = newkakunou[2]+newkakunou[5]+newkakunou[9];
printf("暗号: %d\n", xmody);
xmody = (long long)xmody * inverse(y, x) % x;
printf("復号:");
for (i = NUM; --i >= 0; ) {
if (xmody >= kakunou[i]) {
xmody -= kakunou[i];
printf(" [%d]", i);
}
}
printf("\n");
return 0;
}
int get_rand(void)
{
return rand() % 10 + 1;
}
int get_prime(int sum)
{
int x;
printf("数列の項の合計 = %d\n", sum);
for (;;)
{
printf("x の値は? ");
if (scanf("%d",&x) != 1)
{
exit(1);
}
if (x >= sum)
return x;
else
puts("数列の合計値よりxが小さいです。");
}
}
void print_suretu(const char *title, const int *kakunou, int num)
{
int i;
for (i = 0; i < num; i++)
{
printf("%s%d", i == 0 ? title : ", ", kakunou[i]);
}
putchar('\n');
}
int gcd(int a, int b)
{
int r;
while (b)
r = a % b, a = b, b = r;
return a;
}
int inverse(int a, int 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;
}
[ code=text]
6
17
42
92
193
394
791
1589
3185
6379
数列の合計 = 6379
xの値は? 7000
yの値は? 699
秘密鍵 6,11,25,50,101,397,798,1596,3194
合計=6379 x = 7000 y=6999
公開鍵 6994,6989,6975,6950,6899 6799 6603 6202 5450 3806
暗号 17580 暗号:17580
復号:[9][5][2]
[ /code]
のように少し変になってしました。
すいません。NTLのことも色々聞きたいので新しいトピックを作らせてもらいます。そちらでよろしくお願いします。
Re: 格納
オフトピック
Emacsをコンパイルした…?どうして…?ライトニング さんが書きました:学校ので使ってるのEmacsをターミナルでコンパイルするとかずまんの言われたとおり警告が出てしまいました。
これはプログラミングではありません。プログラムです。ライトニング さんが書きました:実行したのはこのプログラミングです
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 格納
私のところで、EasyIDEC Ver 0.0.9.0 を起動し、No.90 のプログラムをコピペして、ライトニング さんが書きました:自宅ではEasyIDECと言うのを使用しておりそれで先頭の「関数の宣言」を消しては実行できました。
「プログラム実行」を押すと、次のようにエラーになりました。
ファイル「C:/EasyIDEC/project/default/main.c」の
「89行目」で記述エラーを発見しました。
「print_suretu」が再定義されています。以前と同じ名前を使っていないか、関数の記述順番は正しいかどうかを確認してください。
EasyIDEC のバージョンが違うのでしょうか?
No.64 の改良版の No.86 なら中身を理解できますか?ライトニング さんが書きました:No64の実行をしてみたのですがその後どのような値を入力するのか分からないのが謎でした。
けれど10bitを何かうつというのをようやく理解でき実行部分までは理解することができました。
分からないところは質問してください。
No.89 は、これの NTL版です。
何も変ではありません。ライトニング さんが書きました:実行したのはこのプログラミングです
以前に質問した29行目のprintf("",sum)だとエラーをはかれてしまったのでprintf("%d\n",sum)を治したらこのようになってしまい6 17 42 92 193 394 791 1589 3185 6379 数列の合計 = 6379 xの値は? 7000 yの値は? 699 秘密鍵 6,11,25,50,101,397,798,1596,3194 合計=6379 x = 7000 y=6999 公開鍵 6994,6989,6975,6950,6899 6799 6603 6202 5450 3806 暗号 17580 暗号:17580 復号:[9][5][2]
のように少し変になってしました。
6 17 42 92 というのは sum の値です。
6 11 25 50 というのは、kakunou の値です。
17 = 6 + 11
42 = 6 + 11 + 25
92 = 6 + 11 + 25 + 50
となるのは当然です。プログラムをそうなるように書いているのですから。
Re: 格納
No.94 の「EasyIDEC のバージョンが違うのでしょうか?」という質問にライトニング さんが書きました:自分ももう一度改めて実行してみたところかずまさんと同じようなエラーをはかれてしまいました
自分でも何故以前実行できたのか分からない状態です....
答えてもらっていません。
No.94 の「No.64 の改良版の No.86 なら中身を理解できますか?」にライトニング さんが書きました:またNo.89のプログラムで質問したいことがあるのですがここで質問していいでしょうか?
答えてもらっていません。
No.89 のプログラムは、No.86 を元に NTL を使うようにしたものなので、
そちらの理解が先です。
No.89 で、「さらに、NTL に関する質問をするなら、ライトニング さんが書きました:一応NTLトピックをたてたのですが...
「NTL を使用する Merkle-Hellmanナップサック暗号」という件名の
新しいトピックを立ててください。」とお願いしたのに、そうなっていません。
何度同じ間違いをしたら気が済むのですか?
10/04~10/07 「mod計算?」
10/08~10/14 「数列の作成」
10/23~現在 「格納」
10/29~10/31 「mody逆元計算」
どれも、不明確な質問をし、回答者が疑問を持ってから、Wikipedia の
「Merkle-Hellmanナップサック暗号」へのリンクを持ち出しています。
12/06 「NTLの使い方」
これを見た人はどう思いますか?ライトニング さんが書きました: これをNTLでうまく実装させたいのですがどの部分を直せばいいでしょうか?
うか?NTLのオリジナル?の関数なのでしょうか?
・このプログラムは何だろうか?
・NTL って何だろうか?
・なぜ直さないといけないんだろうか?
と疑問がいっぱいです。
ちゃんとこれまでの経緯を説明し、少なくとも「格納」へのリンクを張るべきです。
C++ を全く学習していないことが分かります。ライトニング さんが書きました: またCとC++の違いがよくわからないのですがcoutと言うのはC++関数なのでしょうか?NTLのオリジナル?の関数なのでしょうか?
せめて 30分だけでも C++ の入門ページを見てください。
Re: 格納
どこでそんなことを知ったのですか?coutは関数ではありません。ライトニング さんが書きました:C++の入門を少し拝見しcoutがC++の関数ということも分かりました。
c++ - cout and cin are not functions, so what are they? - Stack Overflow
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 格納
はい、聞かれたことはなんでも答えるつもりです。ライトニング さんが書きました:No.86のプログラムを理解できていない状態なのでそちらを聞いてもいいでしょうか?
そうです。最初にリンクを示し、自分がどんな問題で困っているのかを説明するべきでした。ライトニング さんが書きました:どれも自分の説明不足で結果的にナップサック暗号のリンクを持ち出してしまってる形になっているのはすいません。
NTL は、どこで知ったのですか?ライトニング さんが書きました:確かにまたNTLのトピックも同じように自分の説明不足でNTLが何のかなども分からない事だらけだと思います。
前の課題は、「C言語で MH暗号を作れ」だったと思いますが、
今度の課題は、「C++ で、NTL を使って MH暗号を作れ」になったんですか?
「NTLの使い方」にそのトピックができたいきさつを説明し、リンクを貼ってください。ライトニング さんが書きました:格納のリンクを貼るべきでした
cout は、オブジェクトです。ostreamクラスのインスタンスです。ライトニング さんが書きました:C++の入門を少し拝見しcoutがC++の関数ということも分かりました。
と言っても、わからないでしょう。cout は、変数だと思ってください。
cout << "abc" は、operator<<(cout, "abc") のことで、operator<< が関数です。
これは C の fputs("abc", stdout) に相当します。
Re: 格納
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[N]) // 配列の表示
{
int i;
printf("%s = [", name);
for (i = 0; i < N; i++) printf(" %d", a[i]);
printf(" ]\n");
int i;
char buf[256];
printf("\nEnter %d binary digits\n", N);
printf("%.*s\n", N, "123456789.123456789.123456789.12");
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;
print_array(" w", w);
for (*r = *q /2; gcd(*r, *q) != 1; ++*r) ;
b[i] = (long long)*r * w[i] % *q;
後最後のほうの while (input_data(a))
input_data(a)になるまで続ける?と言うのがよく分からないです。
この部分のどこを変換したらbit数をかえれるのでしょうか?
ご回答よろしくお願いします。
またNTLをしったのはちゃんと暗号を使える?大きいbit数でもできるように新たなNTLでも動くようにと課題が与えられました。
これのNTLを使って実装の後にもまた別の最後の課題が与えられまずはこっちを理解しなければならない状態です。
Re: 格納
Wikipedia とプログラムに書いてありますが、それでも w や b が何か分かりませんか?
b は beta、すなわち β です。
int main(void)
{
int w[N], q, r; // 秘密鍵
int b[N]; // 公開鍵
int a[N]; // 暗号化する前のデータ
int c; // 暗号化されたデータ
int s[N]; // 復号されたデータ
srand(time(0));
generate_keys(b, w, &q, &r); // 鍵の生成
void generate_keys(int b[N], int w[N], int *q, int *r) // 鍵の生成
{
int i, sum = 0;
for (i = 0; i < N; i++) // 超増加列(秘密鍵)の生成
sum += w[i] = sum + 1 + rand() % 10;
print_array(" w", w);
printf(" sum = %d\n", sum);
*q = sum + 1 + rand() % 10; // 秘密鍵 q の生成
for (*r = *q /2; gcd(*r, *q) != 1; ++*r) ; // 秘密鍵 r の生成
for (i = 0; i < N; i++) // 公開鍵の生成
b[i] = (long long)*r * w[i] % *q;
}
generate_keys() からそれらを参照するために *q や *r となっています。
つづく
Re: 格納
for文ですが、これは右側にあるコメントのように秘密鍵 r の生成です。かずま さんが書きました:つづく
Wikipedia には、「整数 r を gcd(r,q) = 1 となるようにランダムに選ぶ」とあります。
ランダムといっても 0 と 1 はダメです。
0 だと、公開鍵が全部 0 になってしまいます。
1 だと、公開鍵が秘密鍵と同じになってしまいます。
2, 3, 4, ... といった小さい数だと r * w が q を超えず、
b[0], b[1], b[2], ... が超増加列なって、秘密鍵がばれやすくなりますから、
ある程度の大きさが必要です。
r が大きすぎると、r * w[N-1] などがオーバーフローする恐れがあります。
そこで、私は、r を q/2 と q の間と考え、q/2 にしました。
r は q と互いに素でなければなりませんから、gdc(r, q) == 1 になるまで、
r を 1ずつ増やしていって、適切な値を求めています。
Re: 格納
No.86のプログラムを実行すると、次のような表示が出ます。
w = [ 5 13 28 48 96 196 390 779 1557 3117 ]
sum = 6229
Private key
w = [ 5 13 28 48 96 196 390 779 1557 3117 ]
q = 6233
r = 3116
Public key
b = [ 3114 3110 6219 6209 6185 6135 6038 2727 2338 1558 ]
Enter 10 binary digits
123456789.
関数呼び出しにより表示されています。
name が、" w" を表していることに何の疑問があるのでしょうか?
関数 print_array() の中の
一つ目の printf で、" w = [" を表示し、
二つ目の printf で、" 5 13 28 48 96 196 390 779 1557 3117" を表示し、
三つ目の printf で、"]\n" を表示しています。
a と a[N] の違いですか?
a は、配列 a の i番目の要素を参照しています。
a[N] は、引数の宣言で、a が配列であることを示しています。
実は、この引数の const int a[N] は、
const int a[] と書いてもよく、
const int *a と書いてもよいのです。
ここでの配列の実体は、main() の int w[N] で、それが generate_keys()
を経由して、print_array() から a で参照できるようになっています。
Re: 格納
関数の頭書きと、最後の return 文を省略しないでください。ライトニング さんが書きました: この部分は何をやってるかが全くわからない状態です。 char buf[256]は256bit分箱があると考えればいいですか?
int input_data(int a[N]) // データの入力
{
int i;
char buf[256];
printf("\nEnter %d binary digits\n", N);
printf("%.*s\n", N, "123456789.123456789.123456789.12");
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 == N; // i が N だったら 1 を、そうでなかったら 0 を返す
}
10個の '0' と '1'、改行コードの '\n'、文字列の終わりの '\0' を入れるため
に最低 12個の char の領域が必要です。
N はもう少し大きくできますし、ユーザが間違って余計な文字を入れてしまうかも
しれないので、かなり大きめに 256個にしました。
次の printf は、Enter 10 binary digits と表示します。
その次の printfは、"123456789.123456789.123456789.12"という文字列の_うち
先頭の N 個、すなわち 10個を表示します。
fgets は、行入力関数です。標準入力 stdin(今はキーボード)から 1行分の
文字列を buf に読み込みます。
ユーザが Linux で Ctrl-D、Windows で Ctrl-Z を入力すると、それは入力終了の
意味になるので、fgets は読込みに失敗し、NULL を返します。
fgets() の前の単項演算子 ! はそれを検出して、演算結果を 1 にします。
if (1) になるので、return 0; が実行され、input_data()関数は呼び出し元に
0 を返します。これは、データが読み込めなかったという合図です。
通常は、ユーザが 0011010111 Enter などと入力するので、fgets() は成功し、
NULL 以外の値を返すので、! による演算結果が 0 になり、if (0) ですから
return 文は実行されません。buf には入力文字列が入っています。
その buf の先頭から N個の文字を、for文で順番に見ていき、
'1' だったら数値の 1 を、
'0' だったら数値の 0 を a に代入します。
'1' と '0' 以外の文字があったら、break; で forループを中断します。
最後の return文の意味はコメント通りです。
これで、main() の int a[N]; にデータの入力完了です。
さて、main() の while (input_data(a)) { ですが、正しく入力できた場合、
input_data() が 1 を返すので、while (1) { となり、{ から } までの文が実行され、
その後、input_data(a) の呼び出しに戻ってきます。
入力の終了や入力エラーの場合、input_data(a) は 0 を返すので、
while (0) { となり、{ から } までの文は実行されずにその次の return 0; に進みます。
main() 終了します。
Re: 格納
「拡張ユークリッド 互除法 逆元」でググってください。
Re: 格納
wやbの単体の意味は分かるのですが中で何が行われてるのかがよく分からないのがほとんどの状態です....
(long long)*rのlong longを以前に説明してもらいましたが
これにrをかけてるのは何故でしょうか?またなぜlong longなのでしょうか?
print_arrayがいまいちどういうものかピンとこないのですが( " w",w)とか (" sum = %d\n", sum)のwやsumの手前の空白部分などが
よくわからないのですがこれは何を表示させてるのでしょうか?("%d",w)とかはだめなのでしょうか?
int*q int*rなどの*はintにqをかけるという意味でしょうか?それとも只*qというのを宣言してるだけでしょうか?
(long long)*rのlong longを以前に説明してもらいましたが
これにrをかけてるのは何故でしょうか?またなぜlong longなのでしょうか?
print_arrayがいまいちどういうものかピンとこないのですが( " w",w)とか (" sum = %d\n", sum)のwやsumの手前の空白部分などが
よくわからないのですがこれは何を表示させてるのでしょうか?("%d",w)とかはだめなのでしょうか?
int*q int*rなどの*はintにqをかけるという意味でしょうか?それとも只*qというのを宣言してるだけでしょうか?
Re: 格納
long longにrを掛けるなどという意味の分からない処理はありません。ライトニング さんが書きました:(long long)*rのlong longを以前に説明してもらいましたが
これにrをかけてるのは何故でしょうか?
これはポインタrが指す先のデータ(int型)を読み取り、その値をlong long型に変換しているだけですね。
int型の値同士を掛け算するとオーバーフローしてundefined behaviorになってしまうリスクがありますが、ライトニング さんが書きました:またなぜlong longなのでしょうか?
もしもlong long型のサイズがint型の2倍であれば、int型の範囲の値をlong longにして掛け算をすることでオーバーフローを回避できるからでしょう。
文字列リテラル中の空白部分は、標準出力の内容を表示するアプリケーションに空白を表示させているのでしょう。ライトニング さんが書きました:( " w",w)とか (" sum = %d\n", sum)のwやsumの手前の空白部分などが
よくわからないのですがこれは何を表示させてるのでしょうか?
ソースコード中の文字列リテラル以外の空白部分は、ソースコードを編集するテキストエディタに空白を表示されているのでしょう。
No: 64のコードの559行目のprint_array(" w", w);をprint_array("%d",w);にすることは、出力の意味がわかりにくくなるだけで問題はないでしょう。ライトニング さんが書きました:("%d",w)とかはだめなのでしょうか?
いいえ。ライトニング さんが書きました:int*q int*rなどの*はintにqをかけるという意味でしょうか?
いいえ。int*型(int型のデータを指すポインタ)の引数qやrを宣言しています。ライトニング さんが書きました:それとも只*qというのを宣言してるだけでしょうか?
オフトピック
C言語の文法的には「int*」と「q」ではなく「int」(declaration-specifiers)と「*q」(declarator)なんですけどね…
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 格納
*r は、main の r を参照するため、と説明したはずです。掛け算ではありません。ライトニング さんが書きました:wやbの単体の意味は分かるのですが中で何が行われてるのかがよく分からないのがほとんどの状態です....
(long long)*rのlong longを以前に説明してもらいましたが
これにrをかけてるのは何故でしょうか?またなぜlong longなのでしょうか?
(long long) は #define N 20 のときに掛け算がオーバーフローしないようにするものです。
今は、#define N 10 ですから、不要です。削除してかまいません。
現在の表示は次の通り。ライトニング さんが書きました: print_arrayがいまいちどういうものかピンとこないのですが( " w",w)とか (" sum = %d\n", sum)のwやsumの手前の空白部分などが
よくわからないのですがこれは何を表示させてるのでしょうか?("%d",w)とかはだめなのでしょうか?
w = [ 5 13 28 48 96 196 390 779 1557 3117 ]
sum = 6229
Private key
w = [ 5 13 28 48 96 196 390 779 1557 3117 ]
q = 6233
r = 3116
Public key
b = [ 3114 3110 6219 6209 6185 6135 6038 2727 2338 1558 ]
Enter 10 binary digits
123456789.
w = [ 5 13 28 48 96 196 390 779 1557 3117 ]
sum = 6229
Private key
w = [ 5 13 28 48 96 196 390 779 1557 3117 ]
q = 6233
r = 3116
Public key
b = [ 3114 3110 6219 6209 6185 6135 6038 2727 2338 1558 ]
Enter 10 binary digits
123456789.
* はポインタです。ライトニング さんが書きました: int*q int*rなどの*はintにqをかけるという意味でしょうか?それとも只*qというのを宣言してるだけでしょうか?
int *q は、q がポインタで、そのポインタは int を指しますよ。という意味です。
q は int ではありません。*q が int で、関数 generate_key() の呼出し元である main() の q を参照します。
鍵の生成を genearate_key() という別関数にしたため、混乱しているようですね。
では、関数にせず、main() に戻します。次のプログラムなら理解できますか?
#include <stdio.h> // fgets, printf
#include <stdlib.h> // rand, srand
#include <time.h> // time
#define N 10
int gcd(int a, int b) // 最大公約数
{
int r;
while (b)
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[N]) // 配列の表示
{
int i;
printf("%s = [", name);
for (i = 0; i < N; i++) printf(" %d", a[i]);
printf(" ]\n");
}
int input_data(int a[N]) // データの入力
{
int i;
char buf[256];
printf("\nEnter %d binary digits\n", N);
printf("%.*s\n", N, "123456789.123456789.123456789.12");
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 == N; // i が N だったら 1 を、そうでなかったら 0 を返す
}
int encrypt(const int a[N], const int b[N]) // 暗号化
{
int i, c = 0;
for (i = 0; i < N; i++)
if (a[i] == 1) c += b[i];
return c;
}
int decrypt(int c, const int w[N], int q, int r, int s[N]) // 復号
{
int i, sum = c * inverse(r, q) % q;
for (i = N; --i >= 0; ) {
if (sum >= w[i])
sum -= w[i], s[i] = 1;
else
s[i] = 0;
}
return sum; // 復号できれば 0、そうでなければ正の値を返す
}
int main(void)
{
int w[N], q, r; // 秘密鍵
int b[N]; // 公開鍵
int a[N]; // 暗号化する前のデータ
int c; // 暗号化されたデータ
int s[N]; // 復号されたデータ
int i, sum = 0;
srand(time(0));
for (i = 0; i < N; i++) // 超増加列(秘密鍵)の生成
sum += w[i] = sum + 1 + rand() % 10;
print_array(" w", w);
printf(" sum = %d\n", sum);
q = sum + 1 + rand() % 10; // 秘密鍵 q の生成
for (r = q /2; gcd(r, q) != 1; ++r) ; // 秘密鍵 r の生成
for (i = 0; i < N; i++) // 公開鍵の生成
b[i] = r * w[i] % q;
printf("Private key\n");
print_array(" w", w);
printf(" q = %d\n r = %d\n", q, r);
printf("Public key\n");
print_array(" b", b);
while (input_data(a)) { // データの入力
print_array(" a", a); // 入力データの表示
c = encrypt(a, b); // 暗号化
printf(" c = %d\n", c); // 暗号化されたデータの表示
decrypt(c, w, q, r, s); // 復号
print_array(" s", s); // 復号されたデータの表示
}
return 0;
}
Re: 格納
オフトピック
確かにそうですね。かずま さんが書きました:* はポインタです。
(http://www.open-std.org/jtc1/sc22/wg14/ ... /n1256.pdf)N1256 6.7.5 Declarators さんが書きました: declarator:
[tab=30]pointeropt direct-declarator
direct-declarator:
[tab=30]identifier
[tab=30](略)
pointer:
[tab=30]* type-qualifier-listopt
[tab=30]* type-qualifier-listopt pointer
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 格納
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;
またbufは配列をいれる箱?なのは分かりますがbuf==1ならa=は1ってのは何故ですか?
aは秘密鍵の配列要素ですよね?
28行目のfor (i = 0; i < N; i++) printf(" %d", a);
ここでaを表示させるようにしてますが。aの計算はこれより後に計算してるのに何故先に表示するのを持ってきて実行できるのでしょうか?
MH暗号のWikiはみながら確認しています。
Re: 格納
No.105 に詳しく書いたのに分からないのですか?
No.105 の fgets() や ! の説明のどこが分からないのですか?
buf は文字列を入れる配列。ライトニング さんが書きました:またbufは配列をいれる箱?なのは分かりますが
buf には、fgets() によりキーボードから文字列が入ります。
配列の各要素 buf には文字が入ります。
ライトニング さんが書きました:buf==1ならa=は1ってのは何故ですか?
buf == '1' です。
キーボードからの入力は文字列ですね。
buf には文字が入っています。
a には数値を入れなければなりません。
buf が文字の '1' なら a には数値の 1 を入れます。
ライトニング さんが書きました:aは秘密鍵の配列要素ですよね?
なんでそうなるの? main() は無視ですか?
int main(void)
{
int w[N], q, r; // 秘密鍵
int b[N]; // 公開鍵
int a[N]; // 暗号化する前のデータ
int c; // 暗号化されたデータ
int s[N]; // 復号されたデータ
a は暗号する前のデータとはっきり書いてあります。
Wikipedia は無視ですか?
a は alpha、すなわち α。
b は beta、すなわち β。
ライトニング さんが書きました:28行目のfor (i = 0; i < N; i++) printf(" %d", a);
ここでaを表示させるようにしてますが。aの計算はこれより後に計算してるのに何故先に表示するのを持ってきて実行できるのでしょうか?
これは print_array() の中ですね。
ここの a は暗号化する前のデータではありません。
print_array(" w", w) で呼び出されたときは w。
print_array(" b", b) で呼び出されたときは b。
だから、まだ計算していない a や s ではありません。
関数の引数は、呼出し元の引数を引き継ぎます。
Re: 格納
すみません。
b は公開鍵でした。秘密鍵は w(と q と r)。
しかし、input_data(a) で呼び出された input_data() の中の a が、
暗号化する前のデータであることは確かです。
print_array() の中の a は、呼出しごとに変わります。
Re: 格納
そのひとつ上の行で、q の値を決めています。
No.86 のプログラム, 53行目 (generat_keys() の中から main() の q と r を参照するので、*q、*r )
*q = sum + 1 + rand() % 10; // 秘密鍵 q の生成
for (*r = *q /2; gcd(*r, *q) != 1; ++*r) ; // 秘密鍵 r の生成
No.103 の説明は読んでいないのですか?ライトニング さんが書きました:またqの値を決めたらそれを2で割りその値を+1ずつしていき最初のqの値と素になればその値がrとなるという解釈でいいでしょうか?
Re: 格納
そこで、私は、r を q/2 と q の間と考え、q/2 にしました。
r は q と互いに素でなければなりませんから、gdc(r, q) == 1 になるまで、
r を 1ずつ増やしていって、適切な値を求めています。
この文はそういう意味じゃないのでしょうか?qを求め2/qを計算しそこからgdc(r, q) == 1 になるようなqを+1増やしていき
gdc(r, q) == 1 になった時その値がrになるってことではないのでしょうか?
それとも2/qをした時点でその値がrになりそのrがgdc(r, q) == 1になるまで+1していくという事でしょうか?
r は q と互いに素でなければなりませんから、gdc(r, q) == 1 になるまで、
r を 1ずつ増やしていって、適切な値を求めています。
この文はそういう意味じゃないのでしょうか?qを求め2/qを計算しそこからgdc(r, q) == 1 になるようなqを+1増やしていき
gdc(r, q) == 1 になった時その値がrになるってことではないのでしょうか?
それとも2/qをした時点でその値がrになりそのrがgdc(r, q) == 1になるまで+1していくという事でしょうか?
Re: 格納
まず、引用は、quoteタグを使うとか、行頭に > を付けるとかして、
引用と自分の文章との区別がつくように書いてください。
> この文はそういう意味じゃないのでしょうか?
「この文」というのは、No.103 の文ですね。
「そういう意味」というのは、No.115 のライトニングさんの解釈でしょうか?
それは間違い。互いに素であるかどうかのチェックが先です。
「qを+1増やしていき」は、「r を 1ずつ増やしていき」の間違い。
q/2 を計算した時点でそれを仮の r にしないと、gcd(r, q) != 1 のチェックができません。
最初から、gcd(r, q) == 1 なら、その r が決定した値です。
gcd(r, q) が 1 でなければ、r を 1 増やし、また gcd(r, q) のチェックに行きます。
そして、gcd(r, q) == 1 なら、求める r が決定します。
引用と自分の文章との区別がつくように書いてください。
gdc は gcd の間違いでした。申し訳ありません。No.103 で、かずま さんが書きました: そこで、私は、r を q/2 と q の間と考え、q/2 にしました。
r は q と互いに素でなければなりませんから、gdc(r, q) == 1 になるまで、
r を 1ずつ増やしていって、適切な値を求めています。
> この文はそういう意味じゃないのでしょうか?
「この文」というのは、No.103 の文ですね。
「そういう意味」というのは、No.115 のライトニングさんの解釈でしょうか?
この解釈では、+1 してから、互いに素のチェックをしているようにも取れます。NO.115 で、ライトニング さんが書きました: qの値を決めたらそれを2で割りその値を+1ずつしていき最初のqの値と素になればその値がrとなるという解釈
それは間違い。互いに素であるかどうかのチェックが先です。
「2/q」は「q/2」の間違い。NO.117 ライトニング さんが書きました: qを求め2/qを計算しそこからgdc(r, q) == 1 になるようなqを+1増やしていき
gdc(r, q) == 1 になった時その値がrになるってことではないのでしょうか?
「qを+1増やしていき」は、「r を 1ずつ増やしていき」の間違い。
「2/q」は「q/2」の間違いですが、「+1していく」が r のことなら、その通りです。NO.117 ライトニング さんが書きました: それとも2/qをした時点でその値がrになりそのrがgdc(r, q) == 1になるまで+1していくという事でしょうか?
q/2 を計算した時点でそれを仮の r にしないと、gcd(r, q) != 1 のチェックができません。
最初から、gcd(r, q) == 1 なら、その r が決定した値です。
gcd(r, q) が 1 でなければ、r を 1 増やし、また gcd(r, q) のチェックに行きます。
そして、gcd(r, q) == 1 なら、求める r が決定します。
- Dixq (管理人)
- 管理人
- 記事: 1662
- 登録日時: 14年前
- 住所: 北海道札幌市
- 連絡を取る:
Re: 格納
一つのトピックが長くなりすぎです。
1トピック1質問としてください。
また、トピックタイトルは質問内容が分かるように名前を付け、内容が変わったら新しくトピックを立ててください。
現在過去ログとして全く意味をなさないものになっています。
1トピック1質問としてください。
また、トピックタイトルは質問内容が分かるように名前を付け、内容が変わったら新しくトピックを立ててください。
現在過去ログとして全く意味をなさないものになっています。