[Swift] 开源我的数据加密用框架 - Keys

2015-08-13 03:17:23 +08:00
 remaerd
https://github.com/remaerd/Keys

跟大家分享一下我重新的 Swift 数据加密框架 - Keys. 参考了一些 iMessage 和 1Password 的设计原理,将大多数 CommonCrypto 的东西简化掉了。

希望有朋友能够帮忙看看翻译,懂行的朋友帮忙看看有啥漏洞。

谢谢了!
3268 次点击
所在节点    iDev
6 条回复
zhuang
2015-08-13 08:34:47 +08:00
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 封装,这个项目还是有意义的。
remaerd
2015-08-13 10:45:18 +08:00
谢谢 @zhuang 的回复。

我本身是学设计的,项目需求需要学密码学的东西,很不容易才学到一些东西。所以想提供一些简单的接口,简化使用。开源亦是为了找到像你这样的朋友,对项目的专业性进行评估。

提到 1Password / iMessage,主要是日后可能会提供一个 Crypto 的 Singleton 让大家直接使用,设计上会参考两者的设计。

使用非技术词汇去描述项目。主要是想和不懂这些东西的朋友用一种浅显易懂的方式去了解 Crypto,要真的搞明白这些东西还是挺难的。而且 CommonCrypto 本身自带奇葩属性。没有 Diffie-Hellman 的 Header,没有 RSA 接口,要通过 Security 框架下的其它接口实现,非常让人困恼。

关于数据暴露问题,我的看法是受 1Password 影响的。他直接在互联网上公布技术实现细节,其实不会影响软件的数据泄漏。如果黑客真的攻击成功,这只能是你的软件问题,而不是二次封装导致的问题。

随机化是我没有学习到的一部分,项目里面可能还会有很多很天真幼稚的实现。希望能够得到你们的指教,我只是起一个头而已。

多谢了。
zhuang
2015-08-13 11:32:44 +08:00
@remaerd

数据暴露(Data Exposure)指代的不是开源或者公开设计导致的“实现方法”泄漏,而是指具体的代码实现在使用时的敏感信息,如密钥泄漏。

密码学意义上有一类叫做 Side-channel attack 的攻击方式,很重要的一环就是根据 Data Remanence 提取敏感信息。

为什么二次封装会导致数据暴露?

以 CommonCrypto 或者 libssl(openssl 实现)为例,二者都自己实现了内存分配和回收,所有的密码学运算都是在它自己的内存空间中实现的。其它应用在调用的时候只会通过 IPC 机制获取结果,而不会,同时也不能,获取任何密码学运算的中间状态信息。即使通过 root 权限访问到相应的内存空间,也无法获知其数据结构在内存中的映射方式,因而就保证了不会有数据暴露的问题。

二次封装 Wrapper 并没有这一机制,当调用 Warpper 的时候,敏感信息在 Wrapper 的内存空间中存在,可以被多种方式获取。
remaerd
2015-08-13 12:13:56 +08:00
@zhuang

感谢解疑。虽然还是一知半解。

我尝试理解你解说的关于 “自己实现了内存分配和回收” ,是不是相当于使用 alloc malloc release 这些非 ARC 的方式管理内存?当用户输入密码后,因为我不是手动管理内存,所以用户密码会存留在内存里,而且黑客能够通过 root 权限访问这些数据。

还是举例 1Password 吧?如果用户输入密码,通过 CCKeyDerivationPBKDF 获得密钥后,1Password 会将这个数据缓存在内存里。当设备休眠/锁屏时,敏感数据就会从内存中 nil 掉。

我的问题是。如果黑客直接用类似 Hooper 这些软件读取你的 App 的数据,而不是获取你的 Wrapper 的数据,那你所说的内存问题还是存在的呀?如果你打开 1Password 查看里面的包,你会发现其实 1Password 里面也有一个类似与 Keys 一样的 Core Framework。你所说的 Side-channel attack,不管用不用 Wrapper,问题还是存在的呀?
zhuang
2015-08-13 12:58:29 +08:00
@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 等底层实现在内存管理上都有特殊之处,就是为了避免当运行时内存被快照化提取后,关键信息不会(轻易)泄漏。理论上内存信息泄漏了,关键信息也随之泄漏了,改变关键信息在内存中分布的目的就是:即使攻击者可以获取内存,也知道关键信息一定在这段内存里,但就是不知道具体哪一部分才是关键信息。
remaerd
2015-08-13 13:26:21 +08:00
@zhuang

感谢回复。真的是非常详细,多谢。

若是内存管理层面上的,我觉得都不是不能解决的问题。问题肯定是有的,OpenSSL 还有 Heartbleed 呢,但项目还是要做的,因为要用到。总之注意内存管理就好了,或许在 README 中添加一些关于内存管理的文字会是一个好的开始。

再次感谢。🙏

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/212827

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX