Stanford - Cryptography I - Week 1-1 Programming Assignment

来源:互联网 发布:windows.old 1 删不掉 编辑:程序博客网 时间:2024/06/06 00:17

题注

今天我的留学生朋友伊万发给我微信,说Coursera的课程太棒了!我还想呢,这是感慨哪门课程啊。一问才知道,原来是Dan Boneh的Cryptography I又开始新的学期了。而且,他做了一些Programming Assignment,并且说从我以前的技术博客中学到了很多的东西。这我才想起来,我本来很早之间就计划把Cryptography I的Programming Assignments答案更新一遍,方便让更多的朋友们顺利地完成Cryptography I的学习。这正好是一个机会,就开始更新吧~!

在更新前,首先介绍一个Dan Boneh这个人。这个人呀,刚开始我还不知道是个什么水平的,只知道是Stanford的一个教授。结果我直博第一年阅读辅导我导师提供的三篇论文,发现竟然有两篇论文都是Dan Boneh写的,一篇发表在《Journal of Cryptography》,一篇发表在会议Asiacrypt中。要知道,这可都是密码学顶级的会议和期刊啊!随着研究的进一步发展,尤其自己是研究双线性对相关的密码学算法,才知道Dan Boneh是一个多么恐怖的人。

有兴趣的朋友可以浏览下下面的链接,来看看Dan Boneh的研究成果。

http://www.informatik.uni-trier.de/~ley/pers/hd/b/Boneh:Dan

值得一提的是,一些现在知名的密码学家,如Craig Gentry,Brent Waters, Ben Lynn等等都是Boneh的学生,特别要注意的是,现在密码学中广泛使用的Pairing Based Cryptography Library就是Ben Lynn编写并维护的。

好了,废话就说到这里,开始Week 1的Assignment吧!第一周的Assignment有两个题目,我分两个Blog来写。

问题

Many Time Pad

Let us see what goes wrong when a stream cipher key is used more than once. Below are eleven hex-encoded ciphertexts that are the result of encrypting eleven plaintexts with a stream cipher, all with the same stream cipher key. Your goal is to decrypt the last ciphertext, and submit the secret message within it as solution.

Hint: XOR the ciphertexts together, and consider what happens when a space is XORed with a character in [a-zA-Z].

ciphertext #1:

315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e

ciphertext #2:
234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f

ciphertext #3:
32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb

ciphertext #4:
32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa

ciphertext #5:
3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070

ciphertext #6:
32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4

ciphertext #7:
32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce

ciphertext #8:
315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3

ciphertext #9:
271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027

ciphertext #10:
466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83

target ciphertext (decrypt this one):
32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904


For completeness, here is the python script used to generate the ciphertexts.(it doesn't matter if you can't read this)

import sysMSGS = ( ---  11 secret messages  --- )def strxor(a, b):     # xor two strings of different lengths    if len(a) > len(b):        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])    else:        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])def random(size=16):    return open("/dev/urandom").read(size)def encrypt(key, msg):    c = strxor(key, msg)    print    print c.encode('hex')    return cdef main():    key = random(1024)    ciphertexts = [encrypt(key, msg) for msg in MSGS]

分析

这道题如果不刻意做的话,似乎是没办法写成一个程序,然后处理并等待结果的。为什么呢?因为这道题中间的几个步骤需要我们用“眼睛”来看。做出这道题,核心点就是理解hint中的提示。

首先,我们需要查阅一下space,即空格的ASCII码,以及字母[a-z]和[A-Z]的ASCII码,并寻找一下他们之间的关系。下面是整个ASCII码表,为了后面方便,我们将ASCII码用8位2进制来表示:

 000000010010001101000101011001110000NULDLESP0@P·p0001SOHDC1!1AQaq0010STXDC2"2BRbr0011ETXDC3#3CScs0100EOTDC4$4DTdt0101ENQNAK%5EUeu0110ACKSYN&6FVfv0111BELETB'7GWgw1000BSCAN(8HXhx1001HTEM)9IYiy1010LFSUB*:JZjz1011VTESC+;K[k{1100FFFS,<L、l|1101CRGS-=M]m}1110SORS.>N^n~1111SIUS/?O_oDEL

我们来观察一下这个码表。[A-Z]用[0x41-0x5a]表示。相应地,[a-z]用[0x61-0x7a]表示。而空格,则用0x20表示,也就是表格中的SP。空格这个表示带来了一个很巧妙的转换:如果一个大写字母与空格与或,那么结果为一个对应的小写字母;如果一个小写字母与空格与或,那么结果为一个对应的大写字母!举两个例子:

a ^ SP = 01100001 ^ 00100000 = 01000001 = A

A ^ SP = a ^ SP ^ SP = a

同时,解密过程中也用到了xor的另一个性质:对于一个数,连续与或两次任意相同的数,其结果与原数相同。用公式表示就是,对于任意的x和y:

x ^ y ^ y = x

根据提示,每一个ciphertext都是用相同的streamcipher加密的。因此,假设plaintext分别为m1、m2,那么c1 = m1 ^ k, c2 = m2 ^ k,于是c1 ^ c2= m1 ^ k ^ m2 ^ k = m1 ^ m2。这样我们就把k消去,只剩下了m1和m2。

那么,我们的解法就很显然了:随便找任意给定的m_i,m_j相与或,如果发现了有意义的英文字母,那么对应位上很可能一个是空格,另一个是英文字母。这个说起来比较绕口,我们还是举个例子:

m_1a b c d e f g hm_2 i j k l m n o 

我们将其用一个随机的key来加密,加密结果为c_1和c_2。我们进行与或运算,即c_1 ^ c_2,则有:

c_1 ^ c_2 = m_1 ^ key ^ m_2 ^ key = m_1 ^ m_2

m_1a b c d e f g hm_2 i j k l m n o c_1 ^ c_2AIBJCKDIEMFNGOH这样,c_1^c_2就透露了很多其他的信息。那么,怎么确定谁是空格,谁是字母呢?我们还有其他8个ciphertext嘛。重复进行这样的操作,甚至最多进行10*9/2=45次与或计算,计算所有c_i与c_j的与或结果,我们就能够恢复出大部分的信息了。最后,根据英文使用习惯,能够得到10个密文所对应的明文了。

实际上,如果我们找到了某一位对应的明文,那么我们通过计算c_i ^ m_i = m_I ^ key ^ m_i = key,能够把对应的key都恢复出来,这样更能加快我们的计算速度了。

这道题我没有刻意弄出一个运行后直接得到结果的程序,在此我给出一个验证程序,验证程序中包括了Java中与或运算等这道题涉及到的Java相关使用,大家可以参考我的代码来实现自己的代码,最终解出这道题。

代码

W1Solution.java

import java.io.UnsupportedEncodingException;import java.math.BigInteger;public class W1Solution {    //给定的10个ciphertext以及target ciphertext    private final String string_cipher_text01 = "315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b1" +   "6349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba50";     private final String string_cipher_text02 = "234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df" +   "44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb741";     private final String string_cipher_text03 = "32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df" +   "44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de812";     private final String string_cipher_text04 = "32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a0290" +   "56f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee41";     private final String string_cipher_text05 = "3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c2" +   "13f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de812";     private final String string_cipher_text06 = "32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d8" +   "4aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d";     private final String string_cipher_text07 = "32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c9" +   "09ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af513";     private final String string_cipher_text08 = "315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d9" +   "43ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e941";     private final String string_cipher_text09 = "271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d5" +   "13e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f404";     private final String string_cipher_text10 = "466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed395980" +   "05b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d";     private final String target_text          = "32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e90" +   "52ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904";     //这里是猜想到的key,我在这已经给出最终key的结果    private final String guest_key            = "66396e89c9dbd8cc9874352acd6395102eafce78aa7fed28a07f6bc98d29c50b69b0" +   "339a19f8aa401a9c6d708f80c066c763fef0123148cdd8e802d05ba98777335daefcecd59c433a6b268b60bf4eF03C9A61";    //将每一个ciphertext转换成为BigInteger    private BigInteger bigIntegerText01 = new BigInteger(string_cipher_text01,16);    private BigInteger bigIntegerText02 = new BigInteger(string_cipher_text02,16);    private BigInteger bigIntegerText03 = new BigInteger(string_cipher_text03,16);    private BigInteger bigIntegerText04 = new BigInteger(string_cipher_text04,16);    private BigInteger bigIntegerText05 = new BigInteger(string_cipher_text05,16);    private BigInteger bigIntegerText06 = new BigInteger(string_cipher_text06,16);    private BigInteger bigIntegerText07 = new BigInteger(string_cipher_text07,16);    private BigInteger bigIntegerText08 = new BigInteger(string_cipher_text08,16);    private BigInteger bigIntegerText09 = new BigInteger(string_cipher_text09,16);    private BigInteger bigIntegerText10 = new BigInteger(string_cipher_text10,16);    private BigInteger bigIntgerTargetText = new BigInteger(target_text,16);    private BigInteger bigIntegerGuestKey = new BigInteger(guest_key,16);    //计算过程如下    public void calculateAndPrint(){        try {            System.out.println("Plaintext 01: " + new String(bigIntegerGuestKey.xor(bigIntegerText01).toByteArray(), "GBK"));            System.out.println("Plaintext 02: " + new String(bigIntegerGuestKey.xor(bigIntegerText02).toByteArray(), "GBK"));            System.out.println("Plaintext 03: " + new String(bigIntegerGuestKey.xor(bigIntegerText03).toByteArray(), "GBK"));            System.out.println("Plaintext 04: " + new String(bigIntegerGuestKey.xor(bigIntegerText04).toByteArray(), "GBK"));            System.out.println("Plaintext 05: " + new String(bigIntegerGuestKey.xor(bigIntegerText05).toByteArray(), "GBK"));            System.out.println("Plaintext 06: " + new String(bigIntegerGuestKey.xor(bigIntegerText06).toByteArray(), "GBK"));            System.out.println("Plaintext 07: " + new String(bigIntegerGuestKey.xor(bigIntegerText07).toByteArray(), "GBK"));            System.out.println("Plaintext 08: " + new String(bigIntegerGuestKey.xor(bigIntegerText08).toByteArray(), "GBK"));            System.out.println("Plaintext 09: " + new String(bigIntegerGuestKey.xor(bigIntegerText09).toByteArray(), "GBK"));            System.out.println("Plaintext 10: " + new String(bigIntegerGuestKey.xor(bigIntegerText10).toByteArray(), "GBK"));            System.out.println("Target Text : " + new String(bigIntegerGuestKey.xor(bigIntgerTargetText).toByteArray(), "GBK"));        }catch (UnsupportedEncodingException e) {            e.printStackTrace();        }    }    public static void main(String[] args){        new W1Solution().calculateAndPrint();    }}

答案

程序运行后的截图如图。

因此我们可以得到正确的Target Text: The secret message is: when using a stream cipher, never use the key more than once


0 0