您的位置:首页 > 编程语言 > Java开发

Stanford - Cryptography I - Week 1-2 Programming Assignment

2014-02-26 03:21 399 查看

题注

在整理我自己Cryptography I答案的时候,我发现以前旧博客里面很多的代码都是不能用的… 这实在是对不起广大朋友们啊!而且旧答案真正的源代码已经基本都找不到了,因此我有的代码就干脆重新整理甚至重写,以保证答案的正确性。这次应该是没问题了~

题目

Weak PRG

The PRG described below uses a 56-bit secret seed. Running the program generates the following first nine outputs of the PRG:

output #1: 210205973

output #2: 22795300

output #3: 58776750

output #4: 121262470

output #5: 264731963

output #6: 140842553

output #7: 242590528

output #8: 195244728

output #9: 86752752

Show that this PRG is insecure by computing the next output. What is the next output (output #10) of the PRG? Note that you are not given the seed.

Hint: there is an algorithm that takes time approximately 2^28 to predict the next output.

Here is the Python script that implements the PRG:

import random

P = 295075153L   # about 2^28

class WeakPrng(object):
def __init__(self, p):   # generate seed with 56 bits of entropy
self.p = p
self.x = random.randint(0, p)
self.y = random.randint(0, p)

def next(self):
# x_{i+1} = 2*x_{i}+5  (mod p)
self.x = (2*self.x + 5) % self.p

# y_{i+1} = 3*y_{i}+7 (mod p)
self.y = (3*self.y + 7) % self.p

# z_{i+1} = x_{i+1} xor y_{i+1}
return (self.x ^ self.y)

prng = WeakPrng(P)
for i in range(1, 10):
print "output #%d: %d" % (i, prng.next())


分析

PRG

PRG(伪随机数生成器)是密码学中非常重要的一个原型函数。实际上,当今的计算机是没有办法获得真正的随机数的,计算机产生随机数的方法无非两种:(1)用一些可以预估,但是几乎是随机的事件来作为随机数种子,生成对应的随机数,类似的如:用毫秒级系统时间作为种子,用用户的输入频率作为种子等等。(2)预存一个随机数生成种子,统一用此种子生成随机数。不管用什么样的方法,如果我们能通过一种方法得到随机数生成器的种子,那么我们就相当于控制了这个计算机中几乎所有密码学系统的随机数产生。Intel最近闹出的安全芯片后门,实际上就是这么来的。在此我们摘录新闻中的一些解释:

原文:http://tech.qq.com/a/20131221/004907.htm

据国外媒体最新报道显示,美国国安局(NSA)曾与业内影响力巨大的电脑安全公司RSA达成了一个价格高达1000万美元的秘密协议,NSA要求后者在被广泛使用的电脑加密算法中安置后门。

根据由CIA前探员爱德华-斯诺登(Edward Snowden)泄露出的机密文档显示,NSA要求将自己提供的方程式作为BSafe安全软件设计的优先或默认随机数生成算法。尽管1000万美元的金额看上去不多,但这实际上已经相当于RSA公司相关部门此前一年年收入的三分之一。而事实上,美国《纽约时报》早在今年9月就曾经报道了这一消息。

在这一消息一经披露后,许多计算机安全领域专家都感到十分震惊。因为RSA在保密用户隐私和安全方面一直表现出众,并且是上世纪90年代反对当时的NSA要求在电脑和通讯产品中安置一块特殊芯片以进行监控的主要公司之一。目前,RSA是电脑存储巨头厂商EMC的子公司,并曾经在斯诺登事件爆发后敦促消费者不要继续使用由NSA提供的数字加密安全算法。

NSA和EMC均拒绝就此发表置评,但RSA在日前的一份声明中指出:“RSA一直都为消费者的最佳利益着想,RSA从来没有设计或者有意在产品中放置任何后门,RSA所做出的产品特性和功能性决定都是独立完成的。”

应该说,此次RSA后门事件的曝光恰恰应证了斯诺登此前有关“NSA希望通过利用自己在商业领域的影响力来系统性的入侵安全工具”的策略,但当时流出的文件并没有明确指出哪家企业同NSA达成了合作关系。

对此,许多前任和现任RSA雇员均表示,公司与NSA达成这一协议事实上已经偏离了自己的公司目标。与此同时,也有部分人士认为,RSA是在一定程度上被政府官方所误导,因为后者一直将自己的这一数字生成算法称为是一种更加先进的安全技术。

“他们(RSA)并不知道事情的全部情况,因为政府官员并没有告诉他们这一点。”一名匿名分析人士说道。

RSA具体是什么,留给Boneh的课上由Boneh解释吧。值得一提的是,现在的公钥密码学加密算法,是根据计算困难问题构造的,能够通过自动机原理严格证明算法是安全的。其根据是:假定存在能够在多项式时间内破解此加密方案的算法,那么我们能够构造出一个多项式时间算法,用于解决一个数学上公认的计算困难问题(即NP-hard问题)。因此,只要公钥密码学算法的安全常数设置的足够大(或者说,算法使用的秘钥够长),那么用计算机破解此算法的时间是极长的,以至于计算代价大于信息本身的价值了。但是,如果我们知道加密过程中产生并的随机数,那么算法的破解将变得非常容易。这也是现在密码学算法破解的一种很常见的思路。

给定PRG的Java转换

作为一个Java脑残控(或者说,一个很喜欢用Java的人),看Python代码虽然能看懂,但是我们还是用Java比较好嘛,首先把给定的算法修改为Java形式。当然,给定的PRG算法也将用于问题的求解过程中。

PRG.java

import java.math.BigInteger;

public class PRG {

// 题目中给出的常数p
private static final BigInteger bigInteger_p = new BigInteger("295075153",
10);
private static final BigInteger bigInteger_two = BigInteger.valueOf(2);
private static final BigInteger bigInteger_five = BigInteger.valueOf(5);

private static final BigInteger bigInteger_three = BigInteger.valueOf(3);
private static final BigInteger bigInteger_seven = BigInteger.valueOf(7);

private BigInteger x;
private BigInteger y;

// x(i+1) = 2x(i) + 5 mod p
public BigInteger next() {
x = x.multiply(bigInteger_two);
x = x.add(bigInteger_five);
x = x.mod(bigInteger_p);

// y(i+1) = 3x(i) + 7 mod p
y = y.multiply(bigInteger_three);
y = y.add(bigInteger_seven);
y = y.mod(bigInteger_p);

return x.xor(y);
}

public void setSeed(BigInteger x, BigInteger y) {
this.x = x;
this.y = y;
}
}


分析与解答

我们来分析一下这个PRG。算法中涉及到两个种子:x和y,每个种子的长度为28bit,因此题目说这个PRG使用了56bit长的种子。我们注意到每轮输出的结果为x ^ y,并且x和y会留到下一轮继续带入计算。因此,我们的思路就比较清楚了:

(1) 猜x的值,然后用第一轮的结果与或x,得到y;

(2) 将x和y按照PRG的算法计算,得到下一轮的结果,与给定的结果比较。如果不正确,则x猜测错误,重新猜测;

(3) 如果正确,分别再产生第三轮,第四轮…第九轮的结果,与给定结果对比,如果都正确,那么猜测的x值正确,第十轮输出的结果就是我们需要的结果。

这个计算过程很简单,但是给我们一个非常重要的结论:一个安全算法的安全常数并不是所有涉及到的秘钥、随机数等的总长度,而是涉及到的秘钥、随机数等的最短长度。举个例子:我们加密时候使用两个128bit的私钥,交替用AES算法加密,我们不能说我们的秘钥是128*2=256bit的,这只是两个128bit AES加密的合并而已…

代码

import java.math.BigInteger;

public class Solution {
public static BigInteger bigInteger_round1 = new BigInteger("210205973", 10);
public static BigInteger bigInteger_round2 = new BigInteger("22795300", 10);
public static BigInteger bigInteger_round3 = new BigInteger("58776750", 10);
public static BigInteger bigInteger_round4 = new BigInteger("121262470", 10);
public static BigInteger bigInteger_round5 = new BigInteger("264731963", 10);
public static BigInteger bigInteger_round6 = new BigInteger("140842553", 10);
public static BigInteger bigInteger_round7 = new BigInteger("242590528", 10);
public static BigInteger bigInteger_round8 = new BigInteger("195244728", 10);
public static BigInteger bigInteger_round9 = new BigInteger("86752752", 10);

BigInteger bigInteger_x = BigInteger.ONE;
BigInteger bigInteger_y = bigInteger_x.xor(bigInteger_round1);

private void changeSeed(){
bigInteger_x = bigInteger_x.add(BigInteger.ONE);
bigInteger_y = bigInteger_x.xor(bigInteger_round1);
}

public void solute(){
while(true){
PRG mprg = new PRG();
//打印正在检索的x值
System.out.println("Test x = " + bigInteger_x);
mprg.setSeed(bigInteger_x, bigInteger_y);

if (!mprg.next().equals(bigInteger_round2)){
//第2个随机数与给定的第2个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round3)){
//第3个随机数与给定的第3个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round4)){
//第4个随机数与给定的第4个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round5)){
//第5个随机数与给定的第5个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round6)){
//第6个随机数与给定的第6个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round7)){
//第7个随机数与给定的第7个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round8)){
//第8个随机数与给定的第8个不等,修改种子继续搜索
changeSeed();
continue;
} else if (!mprg.next().equals(bigInteger_round9)){
//第9个随机数与给定的第9个不等,修改种子继续搜索
changeSeed();
continue;
} else {
//前10轮答案都是正确的,证明找到了正确的x和y,结束求解过程
break;
}
}
}

public void showResult(){
PRG mprg = new PRG();
mprg.setSeed(bigInteger_x, bigInteger_y);
//因为我们猜的是round1时x和y的值,因此在此直接输出
System.out.println("round " + 1 + ": " + bigInteger_round1);
for (int i = 2; i <= 10; i++) {
System.out.println("round " + i + ": " + mprg.next());
}
}

public static void main(String args[]) {
Solution solution = new Solution();
solution.solute();
solution.showResult();
}
}


答案

经过一晚上的运行(具体我也不知道多长时间,因为运行的时候我就直接睡觉去了…醒来以后就看到了结果),程序给出了正确的结果,x = 89059908。我们在此给出正确结果的PRG运行结果截图:



答案提交如图。



因此,下一轮输出的结果为图片中显示的231886864。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Coursera Cryptography Java