原问题地址:http://www.zhihu.com/question/36989779
问题描述
下了个APP,表面是个普通计算器,输入特定数字后,会显示自己保存的秘密图片和文件。怎么尽量避免熊孩子使用这个计算器时刚按到这个数字或答案是这个数字?
注:小于15位,且不得以0开头的自然数。不含字母、符号、小数点。
我的回答
根据题主的需求描述,这是一个口令的生成和管理问题。那么我们就需要遵循以下原则:
- Confidential 不能使用过于简单容易被猜到的口令,长度不能过短其中的数字重复不能太多,数字不能太有意义(比如直接用题主的生日,工号/学号,甚至身份证号),需要体现出一定的随机性,否则容易被拥有良好的字典的攻击者破解。应对暴力破解,最有效的方法就是增加口令长度,所以这里取题主说的上限14位作为目标口令的长度。
- Accessible 必须能随手找到工具容易生成,而且根据confidential原则,生成以后也不能随手用明文抄白纸上或者系统自带的备忘录上,否则熊孩子分分钟找的到然后就玩脱了,但是14位直接记住难度太高,所以我们可以考虑把口令加密以后进行记录,自己记住加密算法和密钥,或者反其道而行之,把某个或几个能记住的object作为密钥输入,加密结果直接作为生成的口令。
综上所述,有一个可行的思路就是生成伪随机数作为口令。
方案一
取一个能记住的字符串或者数字,用散列函数计算hash
值,取前14位作为输出
比如这个就可以取60354186872526
再比如这个可以取32993841519355
觉得生成的数字太凑巧的话也可以换个熟悉的单词短语再算一遍,直到生产满意的为止。
方案二
找一张图片,比如你喜欢的CP的同人图,用MD2
编码生成数字指纹作为随机种子,用这个种子生成一个14位的随机数作为口令。
如果需要得到这个口令,再用原来的图像按这个流程走一遍就行。
我们可以事先定义一个函数:
ApplyForPassword[x_] := {
SeedRandom[Hash[Import[x], "MD2"]];
RandomInteger[{10^14, 10^15 - 1}]
}
这样需要口令的时候调用ApplyForPassword["path"]
就可以得到这个14位伪随机口令(其中path
为图片路径字符串)
方案三
随机生成一个14位自然数作为密码,使用隐写术(Steganography)将密码伪装成看上去非常普通的图片,需要再取用这个密码的时候恢复其中的信息。比如这里有个已经完成的隐写术代码方案可以参考一下:
伪装过程:
恢复过程:
这些关于隐写术的操作也可以被封装为两个函数。
伪装函数(第一个参数为载体图片,第二个是密文):
StegCover[Carrier_Image, text_] :=
Block[{CarrierData, TruncatedCarrier, pixelChannels, SecretBits,
LifeLength, SecretData},
CarrierData = ImageData[Carrier, "Byte"];
TruncatedCarrier = BitAnd[CarrierData, 2^^11111110];
pixelChannels = Apply[Times, Dimensions[CarrierData]];
SecretBits =
Flatten[IntegerDigits[
ToCharacterCode[
ToString[text, InputForm, CharacterEncoding -> "ASCII"]], 2,
8]]; LifeLength = IntegerDigits[Length[SecretBits], 2, 48];
SecretData =
Fold[Partition,
PadRight[Join[LifeLength, SecretBits], pixelChannels],
Reverse@Rest[Dimensions[CarrierData]]];
Image[TruncatedCarrier + SecretData, "Byte"]]
恢复函数(参数为伪装图片):
StegUncover[CoverImage_Image] :=
Block[{SecretData, LifeLength, secretBytes},
SecretData = Flatten[BitAnd[ImageData[CoverImage, "Byte"], 1]];
LifeLength = FromDigits[Take[SecretData, 48], 2];
secretBytes = Partition[Take[Drop[SecretData, 48], LifeLength], 8];
ToExpression[
FromCharacterCode[FromDigits[#, 2] & /@ Take[secretBytes]]]
]
这样需要使用的时候就可以直接调用StegCover
和StegUncover
了,就可以把任意文本(甚至其他形式的内容)隐藏在任意图片中了。
当然这个方案也有很大缺陷,就是降低了资源的利用率。比如苏教版语文第七册一篇课文的彩色插图,508*715
像素,有RGB三个色彩通道,以我们的方法完全可以存储508*715*3/8=136207
个字符,而这里如果仅仅用来存储14位密码(+字符串终止标识‘\0’
,一共15个char
空间的密文)是严重浪费的。中华人民共和国香港特别行政区基本法 (豆瓣)全书一共55983字,即时假设全部为2个char长度的中文字符(ASCII编码
),那么也只需要111966个字符的空间,再用于存储长度的6个字节,还剩24235字节是空余的,我们的方案这时就显得有些杀鸡用宰牛刀了。
那不妨偏个题,这样有个好处就是这样的方案可以隐藏非常大的信息量,一篇GettysburgAddress放进图片里简直毫无感觉。生成的伪装图片也可以通过存储为png或其他无损格式的图片在网络或者别的平台交换信息。
彩蛋,猜猜这张图里藏了什么?
(未完待续)