Compare commits

...

12 Commits

Author SHA1 Message Date
Jordan Rose
ee6d35ed05 Point folks to libsignal-client instead 2020-11-09 13:52:12 -08:00
Jordan Rose
59297b9c09 PR testing: update for changes to libsignal-ffi's install name 2020-10-14 17:48:07 -07:00
Jordan Rose
fa5657c051
Merge pull request #3 from signalapp/jrose/pr-testing
GitHub: run tests on PRs
2020-10-14 11:48:51 -07:00
Jordan Rose
a409ed3815 Generate a coverage report for each PR test 2020-10-14 11:43:20 -07:00
Jordan Rose
fb56458e41 GitHub: add swiftlint action as well 2020-10-14 11:43:20 -07:00
Jordan Rose
4e9f13067a GitHub: run tests on PRs 2020-10-14 11:43:20 -07:00
Jordan Rose
3c1da026f4
Merge pull request #19 from signalapp/jrose/stray-handle-property
Fix PreKeyRecord having two 'handle' properties
2020-10-12 14:29:46 -07:00
Jordan Rose
eba1dc9282 Fix PreKeyRecord having two 'handle' properties
PreKeyRecord is a ClonableHandleOwner, so the superclass was handling
all the state management, but the subclass had its own 'handle' that
was never initialized.
2020-10-12 14:17:29 -07:00
Jordan Rose
555c9a358d
Merge pull request #16 from signalapp/jrose/ContiguousBytes
Make all byte buffer inputs accept any ContiguousBytes type
2020-10-12 11:10:01 -07:00
Jordan Rose
f139a56bca Make all byte buffer inputs accept any ContiguousBytes type
This helps avoid unnecessary conversions on the client side.
2020-10-12 11:09:30 -07:00
Jordan Rose
63db637083
Merge pull request #15 from signalapp/jrose/fdkh
Fix order of arguments for hkdf
2020-10-09 10:09:10 -07:00
Jordan Rose
a54dcfebe4 Fix order of arguments for hkdf 2020-10-08 16:13:48 -07:00
18 changed files with 369 additions and 199 deletions

64
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: CI
on: [pull_request]
jobs:
build_and_test:
name: Build and Test
runs-on: macOS-latest
env:
PKG_CONFIG_PATH: ${{ github.workspace }}/libsignal-ffi/target/debug
steps:
- name: Install nightly rust (needed for libsignal-ffi)
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
profile: minimal
- name: Configure git to use HTTPS
run: git config --global url."https://github.com".insteadOf ssh://git@github.com
- name: Check out libsignal-protocol-swift
uses: actions/checkout@v2
- name: Check out libsignal-ffi
uses: actions/checkout@v2
with:
repository: signalapp/libsignal-ffi
path: libsignal-ffi
- name: Build libsignal-ffi
run: make -C libsignal-ffi
- name: Build libsignal-protocol-swift and run tests
run: swift test -v --enable-code-coverage -Xlinker -rpath -Xlinker $PKG_CONFIG_PATH
- name: Generate coverage report
run: xcrun llvm-cov show --format=html --instr-profile .build/x86_64-apple-macosx/debug/codecov/default.profdata .build/x86_64-apple-macosx/debug/*.xctest/Contents/MacOS/* Sources --output-dir coverage-report
- name: Upload coverage report
uses: actions/upload-artifact@v2
with:
name: Coverage
path: coverage-report
- name: Summarize coverage
run: xcrun llvm-cov report --instr-profile .build/x86_64-apple-macosx/debug/codecov/default.profdata .build/x86_64-apple-macosx/debug/*.xctest/Contents/MacOS/* Sources
lint:
name: Lint
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: brew install swiftlint
- name: Run lint
run: swiftlint lint --reporter github-actions-logging

View File

@ -1,8 +1,6 @@
# Overview
libsignal-protocol-swift is a Swift binding to libsignal-protocol-rust via libsignal-ffi
Work in progress. Subject to change without notice, use outside Signal not yet recommended.
**This repository has been folded into [libsignal-client](https://github.com/signalapp/libsignal-client).**
# Legal things
## Cryptography Notice

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public struct DisplayableFingerprint {
public let formatted: String
@ -40,18 +41,22 @@ public struct NumericFingerprintGenerator {
self.iterations = iterations
}
public func create(version: Int,
localIdentifier: [UInt8],
localKey: PublicKey,
remoteIdentifier: [UInt8],
remoteKey: PublicKey) throws -> Fingerprint {
public func create<LocalBytes, RemoteBytes>(version: Int,
localIdentifier: LocalBytes,
localKey: PublicKey,
remoteIdentifier: RemoteBytes,
remoteKey: PublicKey) throws -> Fingerprint
where LocalBytes: ContiguousBytes, RemoteBytes: ContiguousBytes {
var obj: OpaquePointer?
try checkError(signal_fingerprint_new(&obj, UInt32(iterations), UInt32(version),
localIdentifier, localIdentifier.count,
localKey.nativeHandle,
remoteIdentifier, remoteIdentifier.count,
remoteKey.nativeHandle))
try localIdentifier.withUnsafeBytes { localBytes in
try remoteIdentifier.withUnsafeBytes { remoteBytes in
try checkError(signal_fingerprint_new(&obj, UInt32(iterations), UInt32(version),
localBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), localBytes.count,
localKey.nativeHandle,
remoteBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), remoteBytes.count,
remoteKey.nativeHandle))
}
}
let fprintStr = try invokeFnReturningString {
signal_fingerprint_display_string(obj, $0)

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public struct IdentityKey: Equatable {
public let publicKey: PublicKey
@ -7,7 +8,7 @@ public struct IdentityKey: Equatable {
self.publicKey = publicKey
}
public init(bytes: [UInt8]) throws {
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
publicKey = try PublicKey(bytes)
}
@ -26,10 +27,12 @@ public struct IdentityKeyPair {
return IdentityKeyPair(publicKey: publicKey, privateKey: privateKey)
}
public init(bytes: [UInt8]) throws {
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
var pubkeyPtr: OpaquePointer?
var privkeyPtr: OpaquePointer?
try checkError(signal_identitykeypair_deserialize(&pubkeyPtr, &privkeyPtr, bytes, bytes.count))
try bytes.withUnsafeBytes {
try checkError(signal_identitykeypair_deserialize(&pubkeyPtr, &privkeyPtr, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
}
publicKey = PublicKey(owned: pubkeyPtr!)
privateKey = PrivateKey(owned: privkeyPtr!)

View File

@ -1,24 +1,26 @@
import SignalFfi
import Foundation
public func hkdf(outputLength: Int,
version: UInt32,
inputKeyMaterial: [UInt8],
salt: [UInt8],
info: [UInt8]) throws -> [UInt8] {
public func hkdf<InputBytes, SaltBytes, InfoBytes>(outputLength: Int,
version: UInt32,
inputKeyMaterial: InputBytes,
salt: SaltBytes,
info: InfoBytes) throws -> [UInt8]
where InputBytes: ContiguousBytes, SaltBytes: ContiguousBytes, InfoBytes: ContiguousBytes {
var output = Array(repeating: UInt8(0x00), count: outputLength)
let error = signal_hkdf_derive(&output,
outputLength,
Int32(version),
inputKeyMaterial,
inputKeyMaterial.count,
info,
info.count,
salt,
salt.count)
try checkError(error)
try inputKeyMaterial.withUnsafeBytes { inputBytes in
try salt.withUnsafeBytes { saltBytes in
try info.withUnsafeBytes { infoBytes in
try checkError(signal_hkdf_derive(&output,
outputLength,
Int32(version),
inputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), inputBytes.count,
saltBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), saltBytes.count,
infoBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), infoBytes.count))
}
}
}
return output
}

View File

@ -1,9 +1,13 @@
import SignalFfi
import Foundation
public class PrivateKey: ClonableHandleOwner {
public init(_ bytes: [UInt8]) throws {
var handle: OpaquePointer?
try checkError(signal_privatekey_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(_ bytes: Bytes) throws {
let handle: OpaquePointer? = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_privatekey_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}
@ -31,9 +35,11 @@ public class PrivateKey: ClonableHandleOwner {
}
}
public func generateSignature(message: [UInt8]) throws -> [UInt8] {
return try invokeFnReturningArray {
signal_privatekey_sign($0, $1, nativeHandle, message, message.count)
public func generateSignature<Bytes: ContiguousBytes>(message: Bytes) throws -> [UInt8] {
return try message.withUnsafeBytes { messageBytes in
try invokeFnReturningArray {
signal_privatekey_sign($0, $1, nativeHandle, messageBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), messageBytes.count)
}
}
}

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
/*
SignalFfiError *signal_process_prekey_bundle(PreKeyBundle *bundle,
@ -36,15 +37,17 @@ SignalFfiError *signal_decrypt_pre_key_message(const unsigned char **result,
*/
public func signalEncrypt(message: [UInt8],
for address: ProtocolAddress,
sessionStore: SessionStore,
identityStore: IdentityKeyStore,
context: UnsafeMutableRawPointer?) throws -> CiphertextMessage {
return try withSessionStore(sessionStore) { ffiSessionStore in
try withIdentityKeyStore(identityStore) { ffiIdentityStore in
try invokeFnReturningCiphertextMessage {
signal_encrypt_message($0, message, message.count, address.nativeHandle, ffiSessionStore, ffiIdentityStore, context)
public func signalEncrypt<Bytes: ContiguousBytes>(message: Bytes,
for address: ProtocolAddress,
sessionStore: SessionStore,
identityStore: IdentityKeyStore,
context: UnsafeMutableRawPointer?) throws -> CiphertextMessage {
return try message.withUnsafeBytes { messageBytes in
try withSessionStore(sessionStore) { ffiSessionStore in
try withIdentityKeyStore(identityStore) { ffiIdentityStore in
try invokeFnReturningCiphertextMessage {
signal_encrypt_message($0, messageBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), messageBytes.count, address.nativeHandle, ffiSessionStore, ffiIdentityStore, context)
}
}
}
}
@ -96,24 +99,28 @@ public func processPreKeyBundle(_ bundle: PreKeyBundle,
}
}
public func groupEncrypt(groupId: SenderKeyName,
message: [UInt8],
store: SenderKeyStore,
context: UnsafeMutableRawPointer?) throws -> [UInt8] {
return try withSenderKeyStore(store) { ffiStore in
return try invokeFnReturningArray {
signal_group_encrypt_message($0, $1, groupId.nativeHandle, message, message.count, ffiStore, context)
public func groupEncrypt<Bytes: ContiguousBytes>(groupId: SenderKeyName,
message: Bytes,
store: SenderKeyStore,
context: UnsafeMutableRawPointer?) throws -> [UInt8] {
return try message.withUnsafeBytes { messageBytes in
return try withSenderKeyStore(store) { ffiStore in
return try invokeFnReturningArray {
signal_group_encrypt_message($0, $1, groupId.nativeHandle, messageBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), messageBytes.count, ffiStore, context)
}
}
}
}
public func groupDecrypt(groupId: SenderKeyName,
message: [UInt8],
store: SenderKeyStore,
context: UnsafeMutableRawPointer?) throws -> [UInt8] {
return try withSenderKeyStore(store) { ffiStore in
return try invokeFnReturningArray {
signal_group_decrypt_message($0, $1, groupId.nativeHandle, message, message.count, ffiStore, context)
public func groupDecrypt<Bytes: ContiguousBytes>(groupId: SenderKeyName,
message: Bytes,
store: SenderKeyStore,
context: UnsafeMutableRawPointer?) throws -> [UInt8] {
return try message.withUnsafeBytes { messageBytes in
return try withSenderKeyStore(store) { ffiStore in
return try invokeFnReturningArray {
signal_group_decrypt_message($0, $1, groupId.nativeHandle, messageBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), messageBytes.count, ffiStore, context)
}
}
}
}

View File

@ -1,9 +1,13 @@
import SignalFfi
import Foundation
public class PublicKey: ClonableHandleOwner {
public init(_ bytes: [UInt8]) throws {
var handle: OpaquePointer?
try checkError(signal_publickey_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(_ bytes: Bytes) throws {
let handle: OpaquePointer? = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_publickey_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}
@ -29,9 +33,14 @@ public class PublicKey: ClonableHandleOwner {
}
}
public func verifySignature(message: [UInt8], signature: [UInt8]) throws -> Bool {
public func verifySignature<MessageBytes, SignatureBytes>(message: MessageBytes, signature: SignatureBytes) throws -> Bool
where MessageBytes: ContiguousBytes, SignatureBytes: ContiguousBytes {
var result: Bool = false
try checkError(signal_publickey_verify(nativeHandle, &result, message, message.count, signature, signature.count))
try message.withUnsafeBytes { messageBytes in
try signature.withUnsafeBytes { signatureBytes in
try checkError(signal_publickey_verify(nativeHandle, &result, messageBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), messageBytes.count, signatureBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), signatureBytes.count))
}
}
return result
}

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class PreKeySignalMessage {
private var handle: OpaquePointer?
@ -7,8 +8,12 @@ public class PreKeySignalMessage {
signal_pre_key_signal_message_destroy(handle)
}
public init(bytes: [UInt8]) throws {
try checkError(signal_pre_key_signal_message_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
handle = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_pre_key_signal_message_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
}
public init(version: UInt8,

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class SenderKeyDistributionMessage {
private var handle: OpaquePointer?
@ -18,17 +19,20 @@ public class SenderKeyDistributionMessage {
}
}
public init(keyId: UInt32,
iteration: UInt32,
chainKey: [UInt8],
publicKey: PublicKey) throws {
try checkError(signal_sender_key_distribution_message_new(&handle,
keyId,
iteration,
chainKey,
chainKey.count,
publicKey.nativeHandle))
public init<Bytes: ContiguousBytes>(keyId: UInt32,
iteration: UInt32,
chainKey: Bytes,
publicKey: PublicKey) throws {
handle = try chainKey.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_sender_key_distribution_message_new(&result,
keyId,
iteration,
$0.baseAddress?.assumingMemoryBound(to: UInt8.self),
$0.count,
publicKey.nativeHandle))
return result
}
}
public init(bytes: [UInt8]) throws {

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class SenderKeyMessage {
private var handle: OpaquePointer?
@ -7,21 +8,28 @@ public class SenderKeyMessage {
signal_sender_key_message_destroy(handle)
}
public init(keyId: UInt32,
iteration: UInt32,
ciphertext: [UInt8],
privateKey: PrivateKey) throws {
try checkError(signal_sender_key_message_new(&handle,
keyId,
iteration,
ciphertext,
ciphertext.count,
privateKey.nativeHandle))
public init<Bytes: ContiguousBytes>(keyId: UInt32,
iteration: UInt32,
ciphertext: Bytes,
privateKey: PrivateKey) throws {
handle = try ciphertext.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_sender_key_message_new(&result,
keyId,
iteration,
$0.baseAddress?.assumingMemoryBound(to: UInt8.self),
$0.count,
privateKey.nativeHandle))
return result
}
}
public init(bytes: [UInt8]) throws {
try checkError(signal_sender_key_message_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
handle = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_sender_key_message_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
}
public func keyId() throws -> UInt32 {

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class SignalMessage {
private var handle: OpaquePointer?
@ -11,29 +12,40 @@ public class SignalMessage {
handle = rawPtr
}
public init(bytes: [UInt8]) throws {
try checkError(signal_message_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
handle = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_message_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
}
public init(version: UInt8,
macKey: [UInt8],
senderRatchetKey: PublicKey,
counter: UInt32,
previousCounter: UInt32,
ciphertext: [UInt8],
sender senderIdentityKey: PublicKey,
receiver receiverIdentityKey: PublicKey) throws {
try checkError(signal_message_new(&handle,
version,
macKey,
macKey.count,
senderRatchetKey.nativeHandle,
counter,
previousCounter,
ciphertext,
ciphertext.count,
senderIdentityKey.nativeHandle,
receiverIdentityKey.nativeHandle))
public init<MacBytes, CiphertextBytes>(version: UInt8,
macKey: MacBytes,
senderRatchetKey: PublicKey,
counter: UInt32,
previousCounter: UInt32,
ciphertext: CiphertextBytes,
sender senderIdentityKey: PublicKey,
receiver receiverIdentityKey: PublicKey) throws
where MacBytes: ContiguousBytes, CiphertextBytes: ContiguousBytes {
handle = try macKey.withUnsafeBytes { macBytes in
try ciphertext.withUnsafeBytes { ciphertextBytes in
var result: OpaquePointer?
try checkError(signal_message_new(&result,
version,
macBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
macBytes.count,
senderRatchetKey.nativeHandle,
counter,
previousCounter,
ciphertextBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
ciphertextBytes.count,
senderIdentityKey.nativeHandle,
receiverIdentityKey.nativeHandle))
return result
}
}
}
public func senderRatchetKey() throws -> PublicKey {
@ -66,17 +78,19 @@ public class SignalMessage {
}
}
public func verifyMac(sender: PublicKey,
receiver: PublicKey,
macKey: [UInt8]) throws -> Bool {
var result: Bool = false
try checkError(signal_message_verify_mac(&result,
handle,
sender.nativeHandle,
receiver.nativeHandle,
macKey,
macKey.count))
return result
public func verifyMac<Bytes: ContiguousBytes>(sender: PublicKey,
receiver: PublicKey,
macKey: Bytes) throws -> Bool {
return try macKey.withUnsafeBytes {
var result: Bool = false
try checkError(signal_message_verify_mac(&result,
handle,
sender.nativeHandle,
receiver.nativeHandle,
$0.baseAddress?.assumingMemoryBound(to: UInt8.self),
$0.count))
return result
}
}
internal var nativeHandle: OpaquePointer? {

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class PreKeyBundle {
private var handle: OpaquePointer?
@ -12,46 +13,52 @@ public class PreKeyBundle {
}
// with a prekey
public init(registrationId: UInt32,
deviceId: UInt32,
prekeyId: UInt32,
prekey: PublicKey,
signedPrekeyId: UInt32,
signedPrekey: PublicKey,
signedPrekeySignature: [UInt8],
identity identityKey: IdentityKey) throws {
var prekeyId = prekeyId
try checkError(signal_pre_key_bundle_new(&handle,
registrationId,
deviceId,
&prekeyId,
prekey.nativeHandle,
signedPrekeyId,
signedPrekey.nativeHandle,
signedPrekeySignature,
signedPrekeySignature.count,
identityKey.publicKey.nativeHandle))
public init<Bytes: ContiguousBytes>(registrationId: UInt32,
deviceId: UInt32,
prekeyId: UInt32,
prekey: PublicKey,
signedPrekeyId: UInt32,
signedPrekey: PublicKey,
signedPrekeySignature: Bytes,
identity identityKey: IdentityKey) throws {
handle = try signedPrekeySignature.withUnsafeBytes {
var prekeyId = prekeyId
var result: OpaquePointer?
try checkError(signal_pre_key_bundle_new(&result,
registrationId,
deviceId,
&prekeyId,
prekey.nativeHandle,
signedPrekeyId,
signedPrekey.nativeHandle,
$0.baseAddress?.assumingMemoryBound(to: UInt8.self),
$0.count,
identityKey.publicKey.nativeHandle))
return result
}
}
// without a prekey
public init(registrationId: UInt32,
deviceId: UInt32,
signedPrekeyId: UInt32,
signedPrekey: PublicKey,
signedPrekeySignature: [UInt8],
identity identityKey: IdentityKey) throws {
try checkError(signal_pre_key_bundle_new(&handle,
registrationId,
deviceId,
nil,
nil,
signedPrekeyId,
signedPrekey.nativeHandle,
signedPrekeySignature,
signedPrekeySignature.count,
identityKey.publicKey.nativeHandle))
public init<Bytes: ContiguousBytes>(registrationId: UInt32,
deviceId: UInt32,
signedPrekeyId: UInt32,
signedPrekey: PublicKey,
signedPrekeySignature: Bytes,
identity identityKey: IdentityKey) throws {
handle = try signedPrekeySignature.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_pre_key_bundle_new(&result,
registrationId,
deviceId,
nil,
nil,
signedPrekeyId,
signedPrekey.nativeHandle,
$0.baseAddress?.assumingMemoryBound(to: UInt8.self),
$0.count,
identityKey.publicKey.nativeHandle))
return result
}
}
public func registrationId() throws -> UInt32 {

View File

@ -1,8 +1,7 @@
import SignalFfi
import Foundation
public class PreKeyRecord: ClonableHandleOwner {
private var handle: OpaquePointer?
internal override class func destroyNativeHandle(_ handle: OpaquePointer) {
signal_pre_key_record_destroy(handle)
}
@ -11,9 +10,12 @@ public class PreKeyRecord: ClonableHandleOwner {
return signal_pre_key_record_clone(&newHandle, currentHandle)
}
public init(bytes: [UInt8]) throws {
var handle: OpaquePointer?
try checkError(signal_pre_key_record_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
let handle: OpaquePointer? = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_pre_key_record_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}
@ -38,25 +40,25 @@ public class PreKeyRecord: ClonableHandleOwner {
public func serialize() throws -> [UInt8] {
return try invokeFnReturningArray {
signal_pre_key_record_serialize(handle, $0, $1)
signal_pre_key_record_serialize(nativeHandle, $0, $1)
}
}
public func id() throws -> UInt32 {
return try invokeFnReturningInteger {
signal_pre_key_record_get_id(handle, $0)
signal_pre_key_record_get_id(nativeHandle, $0)
}
}
public func publicKey() throws -> PublicKey {
return try invokeFnReturningPublicKey {
signal_pre_key_record_get_public_key($0, handle)
signal_pre_key_record_get_public_key($0, nativeHandle)
}
}
public func privateKey() throws -> PrivateKey {
return try invokeFnReturningPrivateKey {
signal_pre_key_record_get_private_key($0, handle)
signal_pre_key_record_get_private_key($0, nativeHandle)
}
}
}

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class SenderKeyRecord: ClonableHandleOwner {
internal override class func destroyNativeHandle(_ handle: OpaquePointer) {
@ -11,9 +12,12 @@ public class SenderKeyRecord: ClonableHandleOwner {
private var handle: OpaquePointer?
public init(bytes: [UInt8]) throws {
var handle: OpaquePointer?
try checkError(signal_sender_key_record_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
let handle: OpaquePointer? = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_sender_key_record_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class SessionRecord: ClonableHandleOwner {
internal override class func destroyNativeHandle(_ handle: OpaquePointer) {
@ -9,9 +10,12 @@ public class SessionRecord: ClonableHandleOwner {
return signal_session_record_clone(&newHandle, currentHandle)
}
public init(bytes: [UInt8]) throws {
var handle: OpaquePointer?
try checkError(signal_session_record_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
let handle: OpaquePointer? = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_session_record_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}

View File

@ -1,4 +1,5 @@
import SignalFfi
import Foundation
public class SignedPreKeyRecord: ClonableHandleOwner {
internal override class func destroyNativeHandle(_ handle: OpaquePointer) {
@ -9,21 +10,27 @@ public class SignedPreKeyRecord: ClonableHandleOwner {
return signal_signed_pre_key_record_clone(&newHandle, currentHandle)
}
public init(bytes: [UInt8]) throws {
var handle: OpaquePointer?
try checkError(signal_signed_pre_key_record_deserialize(&handle, bytes, bytes.count))
public init<Bytes: ContiguousBytes>(bytes: Bytes) throws {
let handle: OpaquePointer? = try bytes.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_signed_pre_key_record_deserialize(&result, $0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}
public init(id: UInt32,
timestamp: UInt64,
privateKey: PrivateKey,
signature: [UInt8]) throws {
public init<Bytes: ContiguousBytes>(id: UInt32,
timestamp: UInt64,
privateKey: PrivateKey,
signature: Bytes) throws {
let publicKey = try privateKey.publicKey()
var handle: OpaquePointer?
try checkError(signal_signed_pre_key_record_new(&handle, id, timestamp,
publicKey.nativeHandle, privateKey.nativeHandle,
signature, signature.count))
let handle: OpaquePointer? = try signature.withUnsafeBytes {
var result: OpaquePointer?
try checkError(signal_signed_pre_key_record_new(&result, id, timestamp,
publicKey.nativeHandle, privateKey.nativeHandle,
$0.baseAddress?.assumingMemoryBound(to: UInt8.self), $0.count))
return result
}
super.init(owned: handle!)
}

View File

@ -2,8 +2,7 @@ import XCTest
import SignalProtocol
class PublicAPITests: XCTestCase {
func testHkdf() {
func testHkdfSimple() {
let ikm: [UInt8] = [
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
@ -23,8 +22,29 @@ class PublicAPITests: XCTestCase {
XCTAssertThrowsError(try hkdf(outputLength: okm.count,
version: 19,
inputKeyMaterial: ikm,
salt: salt,
info: info))
salt: salt,
info: info))
}
func testHkdfUsingRFCExample() {
// https://tools.ietf.org/html/rfc5869 A.2
let ikm: [UInt8] = Array(0...0x4f)
let salt: [UInt8] = Array(0x60...0xaf)
let info: [UInt8] = Array(0xb0...0xff)
let okm: [UInt8] = [0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
0x1d, 0x87]
let version = UInt32(3)
let derived = try! hkdf(outputLength: okm.count,
version: version,
inputKeyMaterial: ikm,
salt: salt,
info: info)
XCTAssertEqual(derived, okm)
}
func testAddress() {
@ -297,12 +317,13 @@ class PublicAPITests: XCTestCase {
static var allTests: [(String, (PublicAPITests) -> () throws -> Void)] {
return [
("testAddreses", testAddress),
("testFingerprint", testFingerprint),
("testPkOperations", testPkOperations),
("testHkdf", testHkdf),
("testGroupCipher", testGroupCipher),
("testSessionCipher", testSessionCipher),
("testAddreses", testAddress),
("testFingerprint", testFingerprint),
("testPkOperations", testPkOperations),
("testHkdfSimple", testHkdfSimple),
("testHkdfUsingRFCExample", testHkdfUsingRFCExample),
("testGroupCipher", testGroupCipher),
("testSessionCipher", testSessionCipher),
]
}
}