SipHash
SipHash
是由 Jean-Philippe Aumasson 和 Daniel J. Bernstein 在 2012 年设计的 SipHash 哈希算法的纯 Swift 实现
SipHash 是一族针对短消息优化速度的伪随机函数(又称键控哈希函数)。
目标应用包括网络流量身份验证和对抗哈希洪水 DoS 攻击。
SipHash 实际上安全、快速且简单
- SipHash 比前一代加密算法(例如基于通用哈希的 MAC)更简单、更快
- SipHash 在性能上可以与非加密算法(例如 MurmurHash)相媲美
-- 131002.net
SipHash 有多种版本;此包实现了被称为 SipHash-2-4 的版本。
请注意,Swift 标准库 已经包含了 SipHash-2-4 和 SipHash-1-3 的实现;然而,API 当前是私有的,且在 stdlib 之外不可用。此包提供了一个可用于第三方代码的独立实现。
SipHash 的当前版本需要 Swift 4。
示例代码
import SipHash
// `SipHashable` is like `Hashable`, but simpler.
struct Book: SipHashable {
let title: String
let pageCount: Int
// You need to implement this method instead of `hashValue`.
func appendHashes(to hasher: inout SipHasher) {
// Simply append the fields you want to include in the hash.
hasher.append(title)
hasher.append(pageCount)
}
static func ==(left: Book, right: Book) -> Bool {
return left.title == right.title && left.pageCount == right.pageCount
}
}
// You can now use Books in sets or as dictionary keys.
let book = Book(title: "The Colour of Magic", pageCount: 206)
let books: Set<Book> = [book]
// If you prefer to do so, you may also create & use hashers directly.
var hasher = SipHasher()
hasher.add(book)
hasher.add(42)
// Finalizing the hasher extracts the hash value and invalidates it.
let hash = hasher.finalize()
我会为什么要使用 SipHash?
实现一个良好的hashValue
功能是一项挑战,即使我们只需要组合几个字段的值。我们需要设计一个确定的函数,将这些字段的值完美融合,并生成一个固定宽度的结果,同时在典型输入上碰撞很少。但是,有多少碰撞才算“太多”?我们甚至知道我们的“典型输入”是什么样的吗?对我来说,这两个问题的答案通常都是“我一点都不知道”,我相信你也有同样的问题。
因此,验证我们的hashValue
实现是否良好是一项令人沮丧的任务。
我们需要查看不同输入下哈希函数的行为来检查哈希函数的性质。虽然很容易编写测试来验证相同值的hashValues
相等,但要验证哈希值很少发生碰撞就需要对“典型”输入的统计特性做出某些假设——即使我们对此有足够的信心,编写代码来实现也是非常复杂的。
为什么不使用专门设计用于将数据混合成哈希值的算法呢?使用标准化的算法意味着我们不再需要担心碰撞行为:如果算法设计得好,我们将始终得到良好的结果。
SipHash算法是哈希的一个特别好的选择。它实现了一个从128位密钥(通常在每次程序的执行中随机生成)初始化的256位内部状态的64位加密消息认证码(MAC)。SipHash旨在防御哈希碰撞攻击,同时使用简单且快速。它已经被Perl、Python、Ruby、Rust甚至Swift自身使用——这就是为什么Hashable
的文档明确警告,hashValue
的值在执行之间可能会有所不同。
标准库已经实现了SipHash,但该实现是私有的。(技术上可供使用,但不是stdlib API的正式部分,并且即使在一些点版本发布中也会发生变化/删除。)我预计stdlib的SipHash重构版本将在未来的Swift版本中作为公共API提供。但在我们等待这个版本时,这个包提供了一个今天即可使用的替代实现。
这段代码满是错误吗?
当然。请报告你发现的所有错误!
该包有100%的单元测试覆盖率。不幸的是,这并不能告诉你它在实际应用中的可靠性多少。
测试套件验证包生成的值与SipHash原始作者提供的测试向量匹配,这使我合理地相信这个包正确实现了SipHash。当然,结果可能会有所不同。
参考文档
由Jazzy提供,《格式优雅的参考文档可供使用。
安装
CocoaPods
如果你使用CocoaPods,可以在你的Podfile
中将其作为依赖项开始使用SipHash
。
pod 'SipHash', '~> 1.2'
Carthage
对于Carthage,将以下行添加到你的Cartfile
中。
github "attaswift/SipHash" ~> 1.2
Swift包管理器
对于Swift Package Manager,将SipHash
添加到你的Package.swift
文件中的依赖项列表中。
import PackageDescription
let package = Package(
name: "MyPackage",
dependencies: [
.Package(url: "https://github.com/attaswift/SipHash.git", from: "1.2.1")
]
)
独立开发
如果你不使用依赖管理器,需要将此仓库克隆到项目附近的位置,并将SipHash.xcodeproj
的引用添加到项目的xcworkspace
中。你可以将SipHash的克隆版本放在磁盘上的任何位置,但将其设置为应用程序顶层Git仓库的子模块是个好主意。
要将您的应用程序的二进制文件与SipHash连接起来,只需将 SipHash 项目的 SipHash.framework
添加到 Xcode 中应用程序目标“通用”页面上的“嵌入的二进制文件”部分。只要您的工作区中引用了 SipHash 项目文件,当您点击“嵌入的二进制文件”列表中的“+”按钮时,此框架就会在“选择要添加的项目”表单中列出。
除将框架目标添加到嵌入二进制文件之外,无需进行任何其他设置。