LibSignalProtocolSwift
Signal 协议的 Swift 实现。Signal 协议可用于在同步和异步环境中进行安全、端到端的加密消息传递。它具有许多理想的加密功能,可以处理缺失和顺序错误的消息。Signal 协议被 Signal Messenger 以及 WhatsApp、Facebook、Skype 等应用使用。更多信息请参阅 此处。
目的
此 Swift 库仅用于教学目的,以展示 Signal 协议的工作方式。它某种程度上模仿了Signal 协议 C 实现的功能性和结构。
安装
您可以通过Cocoapods来安装 LibSignalProtocolSwift
,只需在您的 Podfile
中添加以下内容
pod 'LibSignalProtocolSwift', '~> 1.3'
安装后,可以通过导入来访问 Framework
import SignalProtocol
先决条件
本地存储
Signal 协议需要本地存储消息密钥、身份和其他状态信息。您可以通过实现协议 KeyStore
来提供此功能,该协议需要为单个数据存储实现四个代理。
IdentityKeyStore
用于存储和检索身份密钥PreKeyStore
用于存储和检索预密钥SessionStore
用于存储和检索会话SignedPreKeyStore
用于存储和检索签名预密钥
可选
存在一个用于群组更新的功能,其中只有一名管理员可以发送消息,而其他人只能接收。如果您想使用此功能,则需实现 GroupKeyStore
协议,并增加代理 SenderKeyStore
以存储和检索发送者密钥。
示例实现
您可以通过查看 测试实现 获取灵感。
消息交付服务器
存储消息以便检索的服务器需要为每个 SignalAddress
存储以下数据:
公钥身份密钥
:设备身份密钥的公钥部分签名预密钥
:当前的签名预密钥预密钥
:一组未签名的预密钥消息
:要发送到该地址的消息,包括发送者
使用方式
在两个设备(两个不同的 SignalAddress
)之间建立加密会话的标准流程通常如下:
- Alice 将她的
Identity
和一个SignedPreKey
以及一系列未签名的PreKey
上传到服务器。 - Bob 从服务器获取一个
PreKeyBundle
,其中包含 Alice 的Identity
、SignedPreKey
和一个PreKey
(然后从服务器上删除)。 - Bob 通过处理
PreKeyBundle
并加密一个上传到服务器的PreKeyMessage
来创建一个会话。 - Alice 从服务器接收 Bob 的
PreKeyMessage
并解密信息。 - Alice 和 Bob 都建立了加密会话。
创建标识和密钥
在安全通信发生之前,至少需要一个用户将创建 PreKeyBundle
所需的所有必要组件上传到服务器。
// Create the identity key ata install time
let identity = try SignalCrypto.generateIdentityKeyPair()
// Store the data in the key store
// Get the public key from the store
let publicKey: Data = try bobStore.getPublicIdentityKey()
// Create pre keys and save them in the store
let preKeys: [Data] = try bobStore.createPreKeys(count: 10)
// Create a signed pre key and save it in the store
let signedPreKey: Data = try bobStore.updateSignedPrekey()
// Upload publicKey, preKeys, and signedPreKey to the server
从 PreKeyBundle 创建会话
假设 Alice(拥有 SignalAddress
aliceAddress)想与 Bob(下面有 SignalAddress
bobAddress)建立一个会话
// Download Bob's identity, current signedPreKey and one of the preKeys from the server
// Create PreKeyBundle
let preKeyBundle = try SessionPreKeyBundle(
preKey: preKey,
signedPreKey: signedPreKey,
identityKey: identity)
// Create a new session by processing the PreKeyBundle
let session = SessionCipher(store: aliceStore, remoteAddress: bobAddress)
try session.process(preKeyBundle: preKeyBundle)
// The message to encrypt
let message = "Hello Bob, it's Alice".data(using: .utf8)!
// Here Alice can send messages to Bob
let encryptedMessage = try session.encrypt(message)
// Upload the message to the server
从接收到的 PreKeySignalMessage 创建会话
我们继续上面的例子,假设 Bob 收到了来自 Alice 的消息。然后 Bob 可以建立会话
// Get the message from the server
// Create the session
let session = SessionCipher(store: bobStore, remoteAddress: aliceAddress)
// Process the message
let decryptedMessage = try session.decrypt(preKeyMessage)
使用已建立的会话
现在 Alice 和 Bob 都可以随意发送和接收消息。
发送
// Compose a message
let message = "Hello there".data(using: .utf8)!
// Send message to Bob
let session = SessionCipher(store: aliceStore, remoteAddress: bobAddress)
// Encrypt
let encryptedMessage = try session.encrypt(message)
接收
// Get message from the server
// Receive message from Alice
let session = SessionCipher(store: bobStore, remoteAddress: aliceAddress)
// Decrypt
let decryptedMessage = try session.decrypt(message)
验证身份密钥
为了防止中间人攻击,通过手动比较指纹或者扫描某种代码(例如QR码)来比较身份密钥可能是有益的。库提供了一种方便的方法来完成这个任务。
// Create the fingerprint
let aliceFP = try aliceStore.fingerprint(for: bobAddress, localAddress: aliceAddress)
// Display the string...
let display = fingerprint.displayText
// ... or transmit the scannable data to the other client...
let scanData = try fingerprint.scannable.protoData()
// ... or compare to a received fingerprint
fingerprint.matches(scannedFingerprint)
杂项
客户端标识符
库被设计为允许不同的标识符区分不同用户。《信号地址》结构体被用于此目的,它由一个《String》(例如电话号码)和一个《Int》,设备ID组成。然而,只要有符合《Hashable》、《Equatable》和《CustomStringConvertible》协议的结构体、类或类型,就可以使用不同的结构体。例如,可以使用简单的字符串。
class MyCustomKeyStore: KeyStore {
typealias Address = String
...
}
现在,可以使用《MyCustomKeyStore》
let aliceStore = MyCustomKeyStore()
let session = SessionCipher(store: aliceStore, remoteAddress: "Bob")
提供一个自定义加密实现
任何《SignalCryptoProvider》协议的自定义实现都可以作为协议的加密骨干。这可以通过设置《SignalCrypto》类的静态变量《provider》来实现。
SignalCrypto.provider = MyCustomCryptoProvider()
椭圆曲线函数由部署在libsignal-protocol-c中的相同C代码处理,并由Curve25519框架打包以使函数在Swift中可用。
文档
该项目文档丰富,因为这有助于其他人理解代码。文档是用jazzy(网址:https://github.com/realm/jazzy)创建的,它可生成类似苹果的出色文档。
您可以在项目目录中运行以下命令来(重新)生成文档:
jazzy --min-acl private -a 'Christoph Hagen' -u 'https://github.com/christophhagen' -g 'https://github.com/christophhagen/LibSignalProtocolSwift' -e 'Sources/ProtocolBuffers/*' -o 'Documentation'
免责声明
此代码**不适用于生产使用**!此代码未经过错误检查,也未由专家编写。如果您不完全清楚自己在做什么,请不要实施自己的加密软件。