趣闻一则:Fermi实验室的神秘密码

    去年5月5日,芝加哥附近的一个全球知名的物理实验室——Fermi国立加速器实验室——收到了一封神秘的来信。收信人只是简单的一个Fermilab,信件上没有留下寄信人地址。信纸上是一些短竖线、数字和怪异的符号。实验室里的所有人都不知道这封信是怎么回事。这究竟是一个玩笑,还是一个恐吓,或者暗示着一个革命性的物理理论?
    不管他是什么玩意儿,第一个读到信件的人Judy Jackson对它产生了极大的兴趣。为了更快地破解密码,今年五月,Jackson的同事把这封信发到了他们的Blog上。他们得到了全球各地好几百人的回复,其中一些人甚至已经解开了密码的一部分。

    信件内容公布不久,很快就有人注意到,最上面的那部分密码实质上是一个三进制编码,最底下那部分实质上是一个二进制编码。有人甚至把两部分的编码都录入了下来:

323233331112132
33323132212331
2111331132312233
333212123213113
311333313331111
211333323232211
232313331121231
33231312

111212112121212121121212121112121121
1121121121211121211211121211211121111
1111212121121121211121212121112111211
2111212112112111211121112111211121112
111211211121112121121112122211121211
1212112111211121112112111212121112111
211211211121121112112111212112111212
112121211

    破译这样的密码并不容易,前段时间某MM写给我的一段密码我至今仍未破译。你需要从有限的密文中寻找各种形式的特征,从密码本身的规律中寻找提示。人们注意到,在后面的二进制编码中,连续的2仅仅出现了一次。这是为什么?人们更倾向于认为,连续的三个2是不应该出现的,这打破了这段编码的模式;很可能中间那个2仅仅是两个画得比较近的1罢了。后来,这一点得到了证实:人们渐渐意识到,数字2起到一个分隔符的作用。连续的1的个数最多不超过3个,这绝对不是一种巧合;如果把二进制编码中的2看成分隔符的话,下面一部分也是一个三进制编码。这样的话,后一部分的密码可以重新编码为:

312111121113123221312312333112213111332312233333332331231231312333231133223232312312112

Read more…

标题党 之 密码学家用PS3成功预测美国2008大选结果

    昨天,一帮密码学家在网上宣称,他们通过一台PS3成功预测出了2008年大选的结果。但为了不干预事件的进程,他们将放出描述大选结果的文档的md5值。大选结束后,密码学家们会放出该文档,证实他们之前确实成功预测到了这个结果。这份电子文档的md5值为:

3D515DEAD7AA16560ABA3E9DF05CBC80

    你能猜出这背后有什么阴谋诡计吗?

    在字谜界有一个非常有趣的故事。美国大选结果出炉的前一天,某报纸上刊登了一则纵横字谜,正中间的那个最长的单词的提示是:美国新一任总统的名字。字谜爱好者纷纷给报社打电话,说大选结果还没出来,我他妈的咋知道新总统是谁,这背后到底隐藏了什么秘密。第二天,报纸上刊登了字谜的答案,中间那个单词果然就是新总统的名字。这家报社“预测”出了大选的结果。你可能已经想到了,这是一个设计得非常巧妙的字谜。这则纵横字谜有两个答案,每个答案都完全符合所有的提示,只是中间的那个人名字不同。纵横字谜中任一个字母的改变都会引起连锁反应导致很大一片字母的改变,因此要想设计出这样一个字谜是非常困难的。但是,就有这么牛的人设计出来了。
    你相信吗?就有那么牛的人,他居然可以构造出两篇文档,hash出来的md5是完全一样的!比起纵横字谜来,这似乎变得更不可思议,因为一篇文档中任何一处微小的改变都会使原来的md5值面目全非。但是,就有这样一种算法,它可以在短时间内构造出md5发生“碰撞”的情况。这就是前几年炒得沸沸扬扬的“山东大学王小云教授成功破解md5”一事。
    当时的新闻很不负责任,没有几个是说清楚了的。和大多数人想的不同,md5被破解并不是真的md5被破解了,你无法把md5还原为原来的信息(因为md5是多对一、不可逆的),也几乎不可能构造一个字符串具有指定的md5值。但是,王小云教授发现,作为一种验证码,md5已经不再可靠了,因为利用他的发现可以很轻易地构造出md5发生冲突的情况。考虑这样一种情况,你需要在未来的某个时间公开一份秘密文档,但到时候你必须证明这份文档确实就是之前说要公开的那一份。比如,你参加CCTV的垃圾娱乐节目,主持人叫你猜一个充气娃娃的价格,你猜是1000元,然后被干冰吓跑,主持人阴笑着宣布这个充气娃娃的真实价格是998。但制作单位如何证明这个价格确实就是998呢?即使是把真实价格藏在一块遮板后面,也有作假的可能。一种不错的方式就是,预先算出998的md5值,由于md5值是不可逆转的,因此即使公开md5别人也拿它没办法;但这个md5可以起一个验证作用,我在报出998的同时你可以验证998的md5值和刚才给的是不是一样,这样才能确保制作单位没有作假。这就需要md5算法具有很低的碰撞概率。现在看来,这种验证方法也不能相信了,因为人为构造md5冲突的两个原始信息变得越来越容易,你猜价格是A我就说是B,你说是B我就说是A,而A和B的md5是一样的。这样的话,我可以做很多坏事,比如说些什么我能预测股票的走向,我五年前就知道我会和你在一起之类的屁话。从这个意义上说,md5不再安全,它已经被破解了。

消息来源:http://blog.wired.com/27bstroke6/2007/11/cryptographers.html
查看更多:http://www.win.tue.nl/hashclash/Nostradamus/
做人要厚道,转贴请注明出处

网上出现340-cipher破解系统 邀请大家一起破译Zodiac密码

    69年7月31日,三家报社各自收到了连环杀手Zodiac写的一封密文的三分之一。Zodiac要求这三家报社把密文发表在报纸上,否则他将在这周末再次杀人。Vallejo Times-Herald得到的是整个密文的头三分之一(图1),另外两家报社则是San Francisco Chronicle和San Francisco Examiner。这个密文共有408个符号,以后大家都习惯称它为408-密文(408-cipher)。408-密文是Zodiac的第一封密信,是Zodiac事件中极其重要的一环,David Fincher的电影Zodiac就完整地记述了这一事件。
    一个星期后,一位教师和他的妻子破解了这篇密文。他们发现,这篇密文用的是最简单的字母替换法,所不同的是一个字母可能对应多个符号。通常这种一对多的替换加密叫做同音替换法(Homophonic Substitution Cipher)。同音替换密码可以很好地防止字频破解法,因为你可以让常用的字母对应更多的符号,保证每个符号出现的次数大致相等。破解同音密码的常见方法是利用“字母Q后面一定是U”这一类的英文特性,因此你可以特别注意一个符号后面总是跟着那几个符号的情况(Q是不常用的字母,一般只对应一个符号)。但这篇密文太短,可以获取的信息有限,因此可能的破解方法只有一个:不断尝试,不断猜测,不断改进。不管怎样,那位教师和他的妻子解开了Zodiac的密码:

I LIKE KILLING PEOPLE BECAUSE IT IS SO MUCH FUN

IT IS MORE FUN THAN KILLING WILD GAME IN THE FORREST BECAUSE MAN IS THE MOST DANGEROUE ANIMAL OF ALL TO KILL SOMETHING GIVES ME THE MOST THRILLING EXPERENCE

IT IS EVEN BETTER THAN GETTING YOUR ROCKS OFF WITH A GIRL

THE BEST PART IS THAE WHEN I DIE I WILL BE REBORN IN PARADICE AND ALL THE I HAVE KILLED WILL BECOME MY SLAVES

I WILL NOT GIVE YOU MY NAME BECAUSE YOU WILL TRY TO SLOI DOWN or STOP MY COLLECTING OF SLAVES FOR MY AFTERLIFE EBEORIETEMETHHPITI

    最后这个EBEORIETEMETHHPITI是什么意思现在还没搞清楚。

    11月8日,Zodiac又寄出了一篇密文。这篇密文有340个字符,被称作340-密文。与408-密文不同的是,虽然大家都相信340-密文同样使用的是同音替换加密,但直到现在340-密文也没有解开。
    时至今日,David Fincher电影Zodiac又掀起了一次破解未解之谜的热潮,很多人都开始尝试破解340-密文并在网络上分享他们的新发现。最近,网络上出现了一个专门用于破解340-密文的网页。这个网页假设340-密文和408-密文一样也是用的同音替换法,你可以方便地对符号进行替换,同时程序可以告诉你替换后可以得到哪些单词。你也可以随机建立替换表,或者查看一些有趣的替换结果。目前最好的替换表可以产生halloween, killing, you, next, die, zodiac等单词。看过很多侦探小说和电影?对密码破译很有兴趣?340-密文是货真价实的“侦探小说式”密码,有兴趣的话不妨试一试。

位运算简介及实用技巧(一):基础篇

    去年年底写的关于位运算的日志是这个Blog里少数大受欢迎的文章之一,很多人都希望我能不断完善那篇文章。后来我看到了不少其它的资料,学习到了更多关于位运算的知识,有了重新整理位运算技巧的想法。从今天起我就开始写这一系列位运算讲解文章,与其说是原来那篇文章的follow-up,不如说是一个remake。当然首先我还是从最基础的东西说起。

什么是位运算?
    程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理):
     110
AND 1011
———-
    0010  –>  2

    由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。当然有人会说,这个快了有什么用,计算6 and 11没有什么实际意义啊。这一系列的文章就将告诉你,位运算到底可以干什么,有些什么经典应用,以及如何用位运算优化你的程序。

Pascal和C中的位运算符号
    下面的a和b都是整数类型,则:
C语言  |  Pascal语言
——-+————-
a & b  |  a and b
a | b  |  a or b
a ^ b  |  a xor b
  ~a   |   not a
a << b |  a shl b
a >> b |  a shr b

    注意C中的逻辑运算和位运算符号是不同的。520|1314=1834,但520||1314=1,因为逻辑运算时520和1314都相当于True。同样的,!a和~a也是有区别的。

各种位运算的使用
    === 1. and运算 ===
    and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数.

    === 2. or运算 ===
    or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。

    === 3. xor运算 ===
    xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
    xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。
    下面我们看另外一个东西。定义两个符号#和@(我怎么找不到那个圈里有个叉的字符),这两个符号互为逆运算,也就是说(x # y) @ y = x。现在依次执行下面三条命令,结果是什么?
x <- x # y
y <- x @ y
x <- x @ y

    执行了第一句后x变成了x # y。那么第二句实质就是y <- x # y @ y,由于#和@互为逆运算,那么此时的y变成了原来的x。第三句中x实际上被赋值为(x # y) @ x,如果#运算具有交换律,那么赋值后x就变成最初的y了。这三句话的结果是,x和y的位置互换了。
    加法和减法互为逆运算,并且加法满足交换律。把#换成+,把@换成-,我们可以写出一个不需要临时变量的swap过程(Pascal)。
procedure swap(var a,b:longint);
begin
   a:=a + b;
   b:=a - b;
   a:=a - b;
end;

    好了,刚才不是说xor的逆运算是它本身吗?于是我们就有了一个看起来非常诡异的swap过程:
procedure swap(var a,b:longint);
begin
   a:=a xor b;
   b:=a xor b;
   a:=a xor b;
end;

    === 4. not运算 ===
    not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用$0000到$FFFF依次表示的。下面的两个程序(仅语言不同)均返回65435。
var
   a:word;
begin
   a:=100;
   a:=not a;
   writeln(a);
end.

#include <stdio.h>
int main()
{
    unsigned short a=100;
    a = ~a;
    printf( "%dn", a );    
    return 0;
}

    如果not的对象是有符号的整数,情况就不一样了,稍后我们会在“整数类型的储存”小节中提到。

    === 5. shl运算 ===
    a shl b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
    通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。
    定义一些常量可能会用到shl运算。你可以方便地用1 shl 16 – 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。

    === 6. shr运算 ===
    和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。

位运算的简单应用
    有时我们的程序需要一个规模不大的Hash表来记录状态。比如,做数独时我们需要27个Hash表来统计每一行、每一列和每一个小九宫格里已经有哪些数了。此时,我们可以用27个小于2^9的整数进行记录。例如,一个只填了2和5的小九宫格就用数字18表示(二进制为000010010),而某一行的状态为511则表示这一行已经填满。需要改变状态时我们不需要把这个数转成二进制修改后再转回去,而是直接进行位操作。在搜索时,把状态表示成整数可以更好地进行判重等操作。这道题是在搜索中使用位运算加速的经典例子。以后我们会看到更多的例子。
    下面列举了一些常见的二进制位的变换操作。

    功能              |           示例            |    位运算
———————-+—————————+——————–
去掉最后一位          | (101101->10110)           | x shr 1
在最后加一个0         | (101101->1011010)         | x shl 1
在最后加一个1         | (101101->1011011)         | x shl 1+1
把最后一位变成1       | (101100->101101)          | x or 1
把最后一位变成0       | (101101->101100)          | x or 1-1
最后一位取反          | (101101->101100)          | x xor 1
把右数第k位变成1      | (101001->101101,k=3)      | x or (1 shl (k-1))
把右数第k位变成0      | (101101->101001,k=3)      | x and not (1 shl (k-1))
右数第k位取反         | (101001->101101,k=3)      | x xor (1 shl (k-1))
取末三位              | (1101101->101)            | x and 7
取末k位               | (1101101->1101,k=5)       | x and (1 shl k-1)
取右数第k位           | (1101101->1,k=4)          | x shr (k-1) and 1
把末k位变成1          | (101001->101111,k=4)      | x or (1 shl k-1)
末k位取反             | (101001->100110,k=4)      | x xor (1 shl k-1)
把右边连续的1变成0    | (100101111->100100000)    | x and (x+1)
把右起第一个0变成1    | (100101111->100111111)    | x or (x+1)
把右边连续的0变成1    | (11011000->11011111)      | x or (x-1)
取右边连续的1         | (100101111->1111)         | (x xor (x+1)) shr 1
去掉右起第一个1的左边 | (100101000->1000)         | x and (x xor (x-1))

    最后这一个在树状数组中会用到。

Pascal和C中的16进制表示
    Pascal中需要在16进制数前加$符号表示,C中需要在前面加0x来表示。这个以后我们会经常用到。

整数类型的储存
    我们前面所说的位运算都没有涉及负数,都假设这些运算是在unsigned/word类型(只能表示正数的整型)上进行操作。但计算机如何处理有正负符号的整数类型呢?下面两个程序都是考察16位整数的储存方式(只是语言不同)。
var
   a,b:integer;
begin
   a:=$0000;
   b:=$0001;
   write(a,' ',b,' ');
   a:=$FFFE;
   b:=$FFFF;
   write(a,' ',b,' ');
   a:=$7FFF;
   b:=$8000;
   writeln(a,' ',b);
end.

#include <stdio.h>
int main()
{
    short int a, b;
    a = 0x0000;
    b = 0x0001;
    printf( "%d %d ", a, b );
    a = 0xFFFE;
    b = 0xFFFF;
    printf( "%d %d ", a, b );
    a = 0x7FFF;
    b = 0x8000;
    printf( "%d %dn", a, b );
    return 0;
}

    两个程序的输出均为0 1 -2 -1 32767 -32768。其中前两个数是内存值最小的时候,中间两个数则是内存值最大的时候,最后输出的两个数是正数与负数的分界处。由此你可以清楚地看到计算机是如何储存一个整数的:计算机用$0000到$7FFF依次表示0到32767的数,剩下的$8000到$FFFF依次表示-32768到-1的数。32位有符号整数的储存方式也是类似的。稍加注意你会发现,二进制的第一位是用来表示正负号的,0表示正,1表示负。这里有一个问题:0本来既不是正数,也不是负数,但它占用了$0000的位置,因此有符号的整数类型范围中正数个数比负数少一个。对一个有符号的数进行not运算后,最高位的变化将导致正负颠倒,并且数的绝对值会差1。也就是说,not a实际上等于-a-1。这种整数储存方式叫做“补码”。

最后还有两句话
    Matrix67原创
    转贴请注明出处