V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
remaerd
V2EX  ›  iDev

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

  •  
  •   remaerd ·
    remaerd · 2015-08-13 03:17:23 +08:00 · 3437 次点击
    这是一个创建于 3392 天前的主题,其中的信息可能已经有所发展或是发生改变。
    https://github.com/remaerd/Keys

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

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

    谢谢了!
    6 条回复    2015-08-13 13:26:21 +08:00
    zhuang
        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 封装,这个项目还是有意义的。
    remaerd
        2
    remaerd  
    OP
       2015-08-13 10:45:18 +08:00 via iPad
    谢谢 @zhuang 的回复。

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

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

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

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

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

    多谢了。
    zhuang
        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 的内存空间中存在,可以被多种方式获取。
    remaerd
        4
    remaerd  
    OP
       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
        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 等底层实现在内存管理上都有特殊之处,就是为了避免当运行时内存被快照化提取后,关键信息不会(轻易)泄漏。理论上内存信息泄漏了,关键信息也随之泄漏了,改变关键信息在内存中分布的目的就是:即使攻击者可以获取内存,也知道关键信息一定在这段内存里,但就是不知道具体哪一部分才是关键信息。
    remaerd
        6
    remaerd  
    OP
       2015-08-13 13:26:21 +08:00
    @zhuang

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

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

    再次感谢。🙏
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2523 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 00:29 · PVG 08:29 · LAX 16:29 · JFK 19:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.