1
zhuang 2015-08-13 08:34:47 +08:00 2
Crypto 相关的代码审计是非常专业的工作,这里可能没有人有相关资质,即使有也可能没有精力关注你的个人项目。
想了解密码学相关的审计工作可以参考 OCAP 对 TrueCypte 做的审计报告。 https://opencryptoaudit.org/reports/TrueCrypt_Phase_II_NCC_OCAP_final.pdf 某种意义上说,最不能重复造的轮子就是密码学相关的轮子。 我简单看了一下你的项目,与 iMessage/1Password 的设计基本无关。二者都是项目意义上的流程设计,而非你实现的密码学方案的设计。 如果让我来描述你的项目,会是“重新封装的 RSA/AES/SHA 接口,基于 CommonCrypto”。 算法层面: 本身是源自 CommonCrypto 的 PBKDF/AES/RSA/SHA 算法,我想没有什么问题。 实现层面: 1. 随机化问题:使用 arc4random_uniform() 仅能保证随机分布,String.RandomString() 依赖外部初始化,但没有严格的 RNG(随机数生成器)。这一点几乎可以从密码学意义上判整个实现死刑了。 2. 数据暴露问题:关键信息的持久化策略,以及关键信息的运行时状态,都可以从外部轻易访问到。在敏感项目上直接使用 CommonCryto 是有原因的,这是因为它不会由于二次封装而造成信息泄漏。 其它层面: 请使用密码学术语。 这仅仅是一个非专业人员花十分钟读了下代码结构发现的问题,如果深入到代码本身上,可能还会有更多问题。 PS 作为 CommonCrypto 的 Swift 封装,这个项目还是有意义的。 |
2
remaerd OP 谢谢 @zhuang 的回复。
我本身是学设计的,项目需求需要学密码学的东西,很不容易才学到一些东西。所以想提供一些简单的接口,简化使用。开源亦是为了找到像你这样的朋友,对项目的专业性进行评估。 提到 1Password / iMessage,主要是日后可能会提供一个 Crypto 的 Singleton 让大家直接使用,设计上会参考两者的设计。 使用非技术词汇去描述项目。主要是想和不懂这些东西的朋友用一种浅显易懂的方式去了解 Crypto,要真的搞明白这些东西还是挺难的。而且 CommonCrypto 本身自带奇葩属性。没有 Diffie-Hellman 的 Header,没有 RSA 接口,要通过 Security 框架下的其它接口实现,非常让人困恼。 关于数据暴露问题,我的看法是受 1Password 影响的。他直接在互联网上公布技术实现细节,其实不会影响软件的数据泄漏。如果黑客真的攻击成功,这只能是你的软件问题,而不是二次封装导致的问题。 随机化是我没有学习到的一部分,项目里面可能还会有很多很天真幼稚的实现。希望能够得到你们的指教,我只是起一个头而已。 多谢了。 |
3
zhuang 2015-08-13 11:32:44 +08:00 1
@remaerd
数据暴露(Data Exposure)指代的不是开源或者公开设计导致的“实现方法”泄漏,而是指具体的代码实现在使用时的敏感信息,如密钥泄漏。 密码学意义上有一类叫做 Side-channel attack 的攻击方式,很重要的一环就是根据 Data Remanence 提取敏感信息。 为什么二次封装会导致数据暴露? 以 CommonCrypto 或者 libssl(openssl 实现)为例,二者都自己实现了内存分配和回收,所有的密码学运算都是在它自己的内存空间中实现的。其它应用在调用的时候只会通过 IPC 机制获取结果,而不会,同时也不能,获取任何密码学运算的中间状态信息。即使通过 root 权限访问到相应的内存空间,也无法获知其数据结构在内存中的映射方式,因而就保证了不会有数据暴露的问题。 二次封装 Wrapper 并没有这一机制,当调用 Warpper 的时候,敏感信息在 Wrapper 的内存空间中存在,可以被多种方式获取。 |
4
remaerd OP @zhuang
感谢解疑。虽然还是一知半解。 我尝试理解你解说的关于 “自己实现了内存分配和回收” ,是不是相当于使用 alloc malloc release 这些非 ARC 的方式管理内存?当用户输入密码后,因为我不是手动管理内存,所以用户密码会存留在内存里,而且黑客能够通过 root 权限访问这些数据。 还是举例 1Password 吧?如果用户输入密码,通过 CCKeyDerivationPBKDF 获得密钥后,1Password 会将这个数据缓存在内存里。当设备休眠/锁屏时,敏感数据就会从内存中 nil 掉。 我的问题是。如果黑客直接用类似 Hooper 这些软件读取你的 App 的数据,而不是获取你的 Wrapper 的数据,那你所说的内存问题还是存在的呀?如果你打开 1Password 查看里面的包,你会发现其实 1Password 里面也有一个类似与 Keys 一样的 Core Framework。你所说的 Side-channel attack,不管用不用 Wrapper,问题还是存在的呀? |
5
zhuang 2015-08-13 12:58:29 +08:00 1
@remaerd
Keys 和 1Password 都用了 Core Framework,不代表两者在关键信息的内存管理方式上就是一样的。 1Password 不一定不存在数据暴露的问题。 直接使用 CommonCrypto 也可能存在数据暴露的问题,但可能性远远小于二次封装的使用方式。 内存的分配和回收不是重点,重点是数据在内存中的映射方式。有太多的方式获得内存的 Raw 数据,但这样的数据并没有实际价值,只有在其基础上获得每一块内存的数据定义才有意义。 比如 Wrapper A 的实现,某个 key 在内存中是连续 32 Bytes 存储的,应用访问它的方式是通过某个指针获得起始内存地址。对于该应用本身而言,它是不关心 key 到底在内存的哪个位置,它只要知道访问它的指针即可。 对于另一应用 B 来说,如果通过某种手段获得了对 A 内存空间的访问权限,它获取的是某个内存页面的完整信息,但并不知道该 key 到底在哪个地址上。连续 32 Bytes 有太多可能性。 对于依赖操作系统进行内存管理的应用来说,对于确定的执行流程,数据在内存中的分布有很大可能性是“可预测的”。 更大的漏洞来源于非人工内存管理带来的多重副本问题。原则上如 key 这样的敏感信息,在内存中应当仅存在一份副本,依赖操作系统对应用内存进行管理时,很可能会造成该 key 在内存中存在多个副本。攻击方寻找一份 32 Bytes 的内容很困难,但是当内存快照中存在多个相同 32 Bytes 连续内容时,提取出该 key 的可能性就非常大了。 (PBKDF 的白皮书就推荐 pbkdf() 只通过引用传递而不是值拷贝的方式来传入 passphrase,目的是保证敏感信息在内存中只存在一份) CommonCrypto/libssl 等底层实现在内存管理上都有特殊之处,就是为了避免当运行时内存被快照化提取后,关键信息不会(轻易)泄漏。理论上内存信息泄漏了,关键信息也随之泄漏了,改变关键信息在内存中分布的目的就是:即使攻击者可以获取内存,也知道关键信息一定在这段内存里,但就是不知道具体哪一部分才是关键信息。 |