题目
个人见解
这题其实很简单,但是加了几个条件就变难了,首先如果p,q,e组成的RSA加密太弱了的话算错(我也感觉很迷惑,安全性弱也不能算错吧…),所以除了验算这几个参数之间的关系是否正确之外还要验证他们的安全性,即容不容易被破解。
- e不能太小,很容易理解,e的值越大,计算的难度越高嘛,但是什么样算大什么样算小这就很…感性(?)了。不过根据这道题所有检测数据点的输入来看,只要比65536大就可以了。
- p和q不能间隔太小,更不能相等(废话)。但是什么样算小呢,这个如果不听老师的话自己测的话,只能自己慢慢摸索测试方法,慢慢改变范围来找到这个具体的值(有点难)。我一开始是直接用|p-q|与p和q的比值来确定大小的,后来知道正确的做法是计算(pq)^1/2与p和q的差值(这我自己一个人想不到啊,orz)。然后这个差值最好是小于2的31次方,不过如果过不了可以试试改成30次方或32次方。
- (p-1)和(q-1)不能太平滑。我一开始看到是懵逼的,因为什么叫平滑啊…后来听老师讲解知道了就是看(p-1)和(q-1)两者最大公因数的大小。一开始的我很蠢,想着应该比65536小都算小吧,毕竟这p和q都是快2的1000次方量级的东西…后来怎么都过不去,问了同学知道了原来超过20就算大了…20…20…20…。好吧我知道我数学很差,到现在也觉得这也太小了…感性上无法接受,然而这是事实,甚至调成16都没什么问题
上面是难点,也是这题的精髓吧,p和q的间隔,还有(p-1)和(q-1)的最大公因数大小这些自己一个人摸索是很耗时很困难的,orz。
下面就是这题的坑点了:
因为我们老师说计算模逆,判断素数,计算最大公因数的现成函数都不给用!裂开,只能自己写,好麻烦…
代码
#include <stdio.h>
#include <gmp.h>
#include <iostream>
#include<time.h>
#include<stdlib.h>
#define MAX 1<<31
using namespace std;
int test_num[] = {
2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,
107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199 };
void diygcd(mpz_t a, mpz_t b, mpz_t max)
{
mpz_t rt, at, bt;
mpz_init(rt);
mpz_init_set(at, a);
mpz_init_set(bt, b);
if (mpz_cmp(at, bt) < 0)
mpz_swap(at, bt);
mpz_mod(rt, at, bt);
while (mpz_cmp_ui(rt, 0) != 0)
{
mpz_set(at, bt);
mpz_set(bt, rt);
mpz_mod(rt, at, bt);
}
mpz_set(max, bt);
}
void expmod(mpz_t e, mpz_t m, mpz_t N, mpz_t b)
{
mpz_t i, r, result;
mpz_init(i); mpz_init(r); mpz_init(result);
mpz_set(i, e);
mpz_set_ui(result, 1);
while (mpz_cmp_ui(i, 1) != 0)
{
mpz_cdiv_r_ui(r, i, 2);
if (mpz_cmp_ui(r, 0) == 0)
{
mpz_mul(m, m, m);
mpz_cdiv_q_ui(i, i, 2);
}
else
{
mpz_mul(result, result, m);
mpz_sub_ui(i, i, 1);
}
mpz_mod(m, m, N);
mpz_mod(result, result, N);
}
mpz_mul(b, m, result);
mpz_mod(b, b, N);
}
int PrimeJudge(mpz_t n, int limit)
{
if (mpz_cmp_ui(n, 1) == 0)
return 0;
int test_len = sizeof(test_num) / sizeof(int);
int sign;
int k = 0;
int i, t;
mpz_t test;
mpz_init(test);
sign = 0;
for (i = 0; i < test_len; i++)
{
if (mpz_cmp_ui(n, test_num[i]) == 0)
sign = 1;
}
if (sign)
return sign;
sign = 1;
for (i = 0; i < test_len; i++)
{
mpz_mod_ui(test, n, test_num[i]);
if (mpz_cmp_ui(test, 0) == 0)
sign = 0;
}
if (sign == 0)
return 0;
sign = 0;
mpz_t m, a, b, j, r;
mpz_init(m); mpz_init(a); mpz_init(b); mpz_init(j); mpz_init(r);
mpz_sub_ui(m, n, 1);
mpz_sub_ui(r, n, 1);
while (1)
{
if (mpz_tstbit(m, 0))
break;
k++;
mpz_cdiv_q_ui(m, m, 2);
}
gmp_randstate_t state;
for (mpz_set_ui(a, 2); mpz_cmp_ui(a, limit) <= 0 && mpz_cmp(a, r) <= 0; mpz_add_ui(a, a, 1)) {
if (mpz_cmp_ui(a, 0) == 0) {
continue;
}
expmod(m, a, n, b);
sign = 0;
if (mpz_cmp_ui(b, 1) == 0) {
sign = 1;
}
for (t = 0; t < k; t++) {
if (mpz_cmp(b, r) == 0) {
sign = 1;
}
else {
mpz_mul(b, b, b);
mpz_mod(b, b, n);
}
}
if (sign == 0)break;
}
mpz_clear(m); mpz_clear(a); mpz_clear(b); mpz_clear(j); mpz_clear(r);
return sign;
}
int invert(mpz_t d, mpz_t e, mpz_t yN)
{
mpz_t x1, x2, x3, y1, y2, y3, z1, z2, z3, k, p, temp;
mpz_init(x1);
mpz_init(x2);
mpz_init(x3);
mpz_init(y1);
mpz_init(y2);
mpz_init(y3);
mpz_init(z1);
mpz_init(z2);
mpz_init(z3);
mpz_init(k);
mpz_init(p);
diygcd(e, yN, temp);
if (mpz_cmp_ui(temp, 1) != 0)
return false;
mpz_init_set(temp, yN);
if (mpz_cmp(e, yN) > 0)
mpz_swap(e, yN);
mpz_set_ui(x1, 1); mpz_set_ui(x2, 0); mpz_set(x3, yN);
mpz_set_ui(y1, 0); mpz_set_ui(y2, 1); mpz_set(y3, e);
while (1)
{
if (mpz_cmp_ui(y3, 0) == 0)
{
mpz_set_ui(d, 0);
break;
}
if (mpz_cmp_ui(y3, 1) == 0)
{
mpz_set(d, y2);
break;
}
mpz_mod(p, x3, y3);
mpz_sub(k, x3, p);
mpz_cdiv_q(k, k, y3);
mpz_mul(z1, k, y1);
mpz_sub(z1, x1, z1);
mpz_mul(z2, k, y2);
mpz_sub(z2, x2, z2);
mpz_mul(z3, k, y3);
mpz_sub(z3, x3, z3);
mpz_set(x1, y1);
mpz_set(x2, y2);
mpz_set(x3, y3);
mpz_set(y1, z1);
mpz_set(y2, z2);
mpz_set(y3, z3);
}
if (mpz_cmp_ui(d, 0) <= 0)
mpz_add(d, d, temp);
return true;
}
int main()
{
//问题的个数
int n;
cin >> n;
//存储大整数
char temp[1024];
mpz_t e, p, q, maxdiv;
mpz_init(e);
mpz_init(p);
mpz_init(q);
mpz_init(maxdiv);
for (int i = 0; i < n; i++)
{
int sign = 0;
scanf("%s", temp);
mpz_set_str(e, temp, 10);
scanf("%s", temp);
mpz_set_str(p, temp, 10);
scanf("%s", temp);
mpz_set_str(q, temp, 10);
//判断e是否足够大
int compare = mpz_cmp_ui(e, 65536);
if (compare <= 0)
{
cout << "ERROR" << endl;
continue;
}
//判断素数
sign = PrimeJudge(p, 20) + PrimeJudge(q, 20);
if (sign < 2)
{
cout << "ERROR" << endl;
continue;
}
//判断p,q间隔
if (mpz_cmp(p, q) == 0)
{
cout << "ERROR" << endl;
continue;
}
mpz_t tool, N;
mpz_init(N);
mpz_init(tool);
mpz_mul(N, p, q);
mpz_sqrt(N, N);
mpz_sub(tool, p, N);
mpz_abs(tool, tool);
if (mpz_cmp_ui(tool, MAX) <= 0)
{
cout << "ERROR" << endl;
continue;
}
mpz_sub(tool, q, N);
mpz_abs(tool, tool);
if (mpz_cmp_ui(tool, MAX) <= 0)
{
cout << "ERROR" << endl;
continue;
}
mpz_t yN, d, p1, q1;
mpz_init(yN);
mpz_init(d);
mpz_init(p1);
mpz_init(q1);
mpz_sub_ui(p1, p, 1);
mpz_sub_ui(q1, q, 1);
mpz_mul(yN, p1, q1);
//判断p1和q1的最大公因子的大小
diygcd(q1, p1, maxdiv);
if (mpz_cmp_ui(maxdiv, 16) > 0)
{
cout << "ERROR" << endl;
continue;
}
//求d
int choice = invert(d, e, yN);
if (!choice)
{
cout << "ERROR" << endl;
continue;
}
gmp_printf("%Zd\n", d);
mpz_clear(yN);
mpz_clear(d);
mpz_clear(p1);
mpz_clear(q1);
}
mpz_clear(e);
mpz_clear(p);
mpz_clear(q);
}