0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-20 03:52:17 +02:00

Initial incomplete implementation of Swift binding

This commit is contained in:
Jack Lloyd 2020-08-27 16:10:16 -04:00
parent fdf3e954e0
commit bf09bd9895
26 changed files with 1506 additions and 0 deletions

18
Package.swift Normal file
View File

@ -0,0 +1,18 @@
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "SwiftSignal",
products: [
.library(
name: "SwiftSignal",
targets: ["SwiftSignal"]
)
],
dependencies: [],
targets: [
.systemLibrary(name: "SignalFfi"),
.target(name: "SwiftSignal", dependencies: ["SignalFfi"]),
.testTarget(name: "SwiftSignalTests", dependencies: ["SwiftSignal"])
]
)

View File

@ -0,0 +1,4 @@
module SignalFfi {
umbrella header "swift_signal.h"
link "signal_ffi"
}

View File

@ -0,0 +1,2 @@
#include <signal_ffi.h>

View File

@ -0,0 +1,48 @@
import SignalFfi
import Foundation
class ProtocolAddress {
private var handle: OpaquePointer?;
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
init(name: String, device_id: UInt32) throws {
try CheckError(signal_address_new(&handle,
name,
device_id));
}
deinit {
signal_address_destroy(handle);
}
func getName() throws -> String {
return try invokeFnReturningString(fn: { (b) in signal_address_get_name(handle, b) });
}
func getDeviceId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_address_get_device_id(handle, i) });
}
}
extension ProtocolAddress: Hashable {
static func == (lhs: ProtocolAddress, rhs: ProtocolAddress) -> Bool {
let lhsDeviceId = try! lhs.getDeviceId();
let rhsDeviceId = try! rhs.getDeviceId();
if lhsDeviceId != rhsDeviceId {
return false;
}
let lhsSenderName = try! lhs.getName();
let rhsSenderName = try! rhs.getName();
return lhsSenderName == rhsSenderName;
}
func hash(into hasher: inout Hasher) {
hasher.combine(try! getName())
hasher.combine(try! getDeviceId())
}
}

View File

@ -0,0 +1,12 @@
class InMemorySenderKeyStore : SenderKeyStore {
private var map : [SenderKeyName : SenderKeyRecord] = [:];
func saveSenderKey(name: SenderKeyName, record: SenderKeyRecord, ctx: UnsafeMutableRawPointer?) throws {
map[name] = record;
}
func loadSenderKey(name: SenderKeyName, ctx: UnsafeMutableRawPointer?) throws -> SenderKeyRecord? {
return map[name];
}
}

View File

@ -0,0 +1,36 @@
import SignalFfi
import Foundation
enum Direction {
case Sending
case Receiving
}
protocol IdentityKeyStore {
func getIdentityKeyPair(ctx: UnsafeMutableRawPointer?) throws -> IdentityKeyPair;
func getLocalRegistrationId(ctx: UnsafeMutableRawPointer?) throws -> UInt32;
mutating func saveIdentity(address: ProtocolAddress, identity: IdentityKey, ctx: UnsafeMutableRawPointer?) throws -> Bool;
func isTrustedIdentity(address: ProtocolAddress, identity: IdentityKey, direction: Direction, ctx: UnsafeMutableRawPointer?) throws -> Bool;
func getIdentity(address: ProtocolAddress, ctx: UnsafeMutableRawPointer?) throws -> Optional<IdentityKey>;
}
protocol PreKeyStore {
func getPreKey(prekeyId: UInt32, ctx: UnsafeMutableRawPointer?) throws -> PreKeyRecord;
mutating func savePreKey(prekeyId: UInt32, record: PreKeyRecord, ctx: UnsafeMutableRawPointer?) throws;
func removePreKey(prekeyId: UInt32, ctx: UnsafeMutableRawPointer?) throws;
}
protocol SignedPreKeyStore {
func getSignedPreKey(signedPrekeyId: UInt32, ctx: UnsafeMutableRawPointer?) throws -> SignedPreKeyRecord;
mutating func saveSignedPreKey(signedPrekeyId: UInt32, record: SignedPreKeyRecord, ctx: UnsafeMutableRawPointer?) throws;
}
protocol SessionStore {
func loadSession(address: ProtocolAddress, ctx: UnsafeMutableRawPointer?) throws -> Optional<SessionRecord>;
mutating func storeSession(address: ProtocolAddress, record: SessionRecord, ctx: UnsafeMutableRawPointer?) throws;
}
protocol SenderKeyStore {
mutating func saveSenderKey(name: SenderKeyName, record: SenderKeyRecord, ctx: UnsafeMutableRawPointer?) throws;
func loadSenderKey(name: SenderKeyName, ctx: UnsafeMutableRawPointer?) throws -> Optional<SenderKeyRecord>;
}

View File

@ -0,0 +1,90 @@
import SignalFfi
import Foundation
public enum SignalError : Error {
case invalid_state(String)
case internal_error(String)
case null_parameter(String)
case invalid_argument(String)
case invalid_type(String)
case invalid_utf8_string(String)
case insufficient_output_size(String)
case protobuf_error(String)
case invalid_ciphertext(String)
case legacy_ciphertext_version(String)
case unknown_ciphertext_version(String)
case unrecognized_message_version(String)
case invalid_message(String)
case invalid_key(String)
case invalid_signature(String)
case fingerprint_identifier_mismatch(String)
case fingerprint_version_mismatch(String)
case untrusted_identity(String)
case invalid_key_identifier(String)
case session_not_found(String)
case duplicated_message(String)
case callback_error(String)
case unknown(UInt32, String)
}
func CheckError(_ error: OpaquePointer?) throws {
if error != nil {
let err_type = signal_error_get_type(error);
let err_str = try invokeFnReturningString(fn: { (b) in signal_error_get_message(error, b) });
signal_error_free(error);
// FIXME: Swift is willing to import the SIGNAL_ERROR_CODE_xxx
// values, and we can compare them to each other but cannot
// get their integer values without reflection - is there
// some other way?
switch err_type {
case 2:
throw SignalError.invalid_state(err_str)
case 3:
throw SignalError.internal_error(err_str)
case 4:
throw SignalError.null_parameter(err_str)
case 5:
throw SignalError.invalid_argument(err_str)
case 6:
throw SignalError.invalid_type(err_str)
case 7:
throw SignalError.invalid_utf8_string(err_str)
case 8:
throw SignalError.insufficient_output_size(err_str)
case 10:
throw SignalError.protobuf_error(err_str)
case 20:
throw SignalError.invalid_ciphertext(err_str)
case 21:
throw SignalError.legacy_ciphertext_version(err_str)
case 22:
throw SignalError.unknown_ciphertext_version(err_str)
case 23:
throw SignalError.unrecognized_message_version(err_str)
case 30:
throw SignalError.invalid_message(err_str)
case 40:
throw SignalError.invalid_key(err_str)
case 41:
throw SignalError.invalid_signature(err_str)
case 50:
throw SignalError.fingerprint_identifier_mismatch(err_str)
case 51:
throw SignalError.fingerprint_version_mismatch(err_str)
case 60:
throw SignalError.untrusted_identity(err_str)
case 70:
throw SignalError.invalid_key_identifier(err_str)
case 80:
throw SignalError.session_not_found(err_str)
case 90:
throw SignalError.duplicated_message(err_str)
case 100:
throw SignalError.callback_error(err_str)
default:
throw SignalError.unknown(err_type, err_str)
};
}
}

View File

@ -0,0 +1,77 @@
import SignalFfi
import Foundation
class DisplayableFingerprint {
let formatted: String
internal init(formatted: String) {
self.formatted = formatted;
}
}
class ScannableFingerprint {
let encoding: [UInt8];
internal init(encoding: [UInt8]) {
self.encoding = encoding;
}
func compareWith(other: ScannableFingerprint) throws -> Bool {
var result : UInt8 = 0;
try CheckError(signal_fingerprint_compare(&result, encoding, encoding.count,
other.encoding, other.encoding.count));
if result == 1 {
return true;
} else {
return false;
}
}
}
extension ScannableFingerprint: Equatable {
static func == (lhs: ScannableFingerprint, rhs: ScannableFingerprint) -> Bool {
return try! lhs.compareWith(other: rhs);
}
}
class Fingerprint {
let scannable : ScannableFingerprint;
let displayable: DisplayableFingerprint;
internal init(displayable: DisplayableFingerprint, scannable: ScannableFingerprint) {
self.displayable = displayable;
self.scannable = scannable;
}
}
class NumericFingerprintGenerator {
private let iterations: Int;
init(iterations: Int) {
self.iterations = iterations;
}
func createFor(version: Int,
local_identifier: [UInt8],
local_key: PublicKey,
remote_identifier: [UInt8],
remote_key: PublicKey) throws -> Fingerprint {
var obj : OpaquePointer?;
try CheckError(signal_fingerprint_new(&obj, UInt32(iterations), UInt32(version),
local_identifier, local_identifier.count,
local_key.nativeHandle(),
remote_identifier, remote_identifier.count,
remote_key.nativeHandle()));
let fprint_str = try invokeFnReturningString(fn: { (b) in signal_fingerprint_display_string(obj, b) });
let displayable = DisplayableFingerprint(formatted: fprint_str);
let scannable_bits = try invokeFnReturningArray(fn: { (b,bl) in signal_fingerprint_scannable_encoding(obj, b, bl) });
let scannable = ScannableFingerprint(encoding: scannable_bits);
try CheckError(signal_fingerprint_destroy(obj));
return Fingerprint(displayable: displayable, scannable: scannable);
}
}

View File

@ -0,0 +1,57 @@
import SignalFfi
import Foundation
class IdentityKey {
private let key : PublicKey;
init(pk: PublicKey) {
key = pk;
}
init(bytes: [UInt8]) throws {
key = try PublicKey(bytes);
}
func serialize() throws -> [UInt8] {
return try key.serialize();
}
func publicKey() -> PublicKey {
return key;
}
}
class IdentityKeyPair {
private let pubkey : PublicKey;
private let privkey : PrivateKey;
init() throws {
privkey = try PrivateKey(); // generates new key
pubkey = try privkey.getPublicKey();
}
init(bytes: [UInt8]) throws {
var pubkey_ptr : OpaquePointer?;
var privkey_ptr : OpaquePointer?;
try CheckError(signal_identitykeypair_deserialize(&pubkey_ptr, &privkey_ptr, bytes, bytes.count));
pubkey = PublicKey(raw_ptr: pubkey_ptr);
privkey = PrivateKey(raw_ptr: privkey_ptr);
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_identitykeypair_serialize(b,bl,pubkey.nativeHandle(), privkey.nativeHandle()) });
}
func publicKey() -> PublicKey {
return pubkey;
}
func privateKey() -> PrivateKey {
return privkey;
}
func identityKey() -> IdentityKey {
return IdentityKey(pk: publicKey());
}
}

View File

@ -0,0 +1,26 @@
import SignalFfi
import Foundation
func hkdf(output_length: UInt32,
version: UInt32,
input_key_material: [UInt8],
salt: [UInt8],
info: [UInt8]) throws -> Array<UInt8> {
var output = Array(repeating: UInt8(0x00), count: Int(output_length));
let error = signal_hkdf_derive(&output,
Int(output_length),
Int32(version),
input_key_material,
input_key_material.count,
info,
info.count,
salt,
salt.count);
try CheckError(error);
return output;
}

View File

@ -0,0 +1,43 @@
import SignalFfi
import Foundation
class PrivateKey {
private var handle: OpaquePointer?;
init(_ bytes: [UInt8]) throws {
try CheckError(signal_privatekey_deserialize(&handle, bytes, bytes.count));
}
internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr;
}
// Some more explicit syntax would be nice for this (eg PublicKey.generate() ??)
init() throws {
try CheckError(signal_privatekey_generate(&handle));
}
deinit {
signal_privatekey_destroy(handle);
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_serialize(handle,b,bl) });
}
func generateSignature(message: [UInt8]) throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_sign(b,bl,handle,message,message.count) });
}
func keyAgreement(other_key: PublicKey) throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_privatekey_agree(b,bl,handle,other_key.nativeHandle()) });
}
func getPublicKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_privatekey_get_public_key(k, handle) });
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
}

View File

@ -0,0 +1,117 @@
import SignalFfi
import Foundation
/*
SignalFfiError *signal_process_prekey_bundle(PreKeyBundle *bundle,
const ProtocolAddress *protocol_address,
FfiSessionStoreStruct *session_store,
FfiIdentityKeyStoreStruct *identity_key_store,
void *ctx);
SignalFfiError *signal_encrypt_message(const unsigned char **result,
size_t *result_len,
const unsigned char *ptext,
size_t ptext_len,
const ProtocolAddress *protocol_address,
FfiSessionStoreStruct *session_store,
FfiIdentityKeyStoreStruct *identity_key_store,
void *ctx);
SignalFfiError *signal_decrypt_message(const unsigned char **result,
size_t *result_len,
const SignalMessage *message,
const ProtocolAddress *protocol_address,
FfiSessionStoreStruct *session_store,
FfiIdentityKeyStoreStruct *identity_key_store,
void *ctx);
SignalFfiError *signal_decrypt_pre_key_message(const unsigned char **result,
size_t *result_len,
const PreKeySignalMessage *message,
const ProtocolAddress *protocol_address,
FfiSessionStoreStruct *session_store,
FfiIdentityKeyStoreStruct *identity_key_store,
FfiPreKeyStoreStruct *prekey_store,
FfiSignedPreKeyStoreStruct *signed_prekey_store,
void *ctx);
*/
func SignalEncrypt(message: [UInt8],
address: ProtocolAddress,
session_store: SessionStore,
identity_store: IdentityKeyStore,
ctx: UnsafeMutableRawPointer?) throws -> [UInt8] {
var ffi_session = try createFfiSessionStoreStruct(session_store);
var ffi_identity = try createFfiIdentityKeyStoreStruct(identity_store);
return try invokeFnReturningArray(fn: { (b,bl) in signal_encrypt_message(b,bl,message,message.count,
address.nativeHandle(),
&ffi_session, &ffi_identity, ctx) });
}
func SignalDecrypt(message: SignalMessage,
address: ProtocolAddress,
session_store: SessionStore,
identity_store: IdentityKeyStore,
ctx: UnsafeMutableRawPointer?) throws -> [UInt8] {
var ffi_session = try createFfiSessionStoreStruct(session_store);
var ffi_identity = try createFfiIdentityKeyStoreStruct(identity_store);
return try invokeFnReturningArray(fn: { (b,bl) in signal_decrypt_message(b,bl,message.nativeHandle(),
address.nativeHandle(),
&ffi_session, &ffi_identity, ctx) });
}
func SignalDecryptPreKey(message: PreKeySignalMessage,
address: ProtocolAddress,
session_store: SessionStore,
identity_store: IdentityKeyStore,
pre_key_store: PreKeyStore,
signed_pre_key_store: SignedPreKeyStore,
ctx: UnsafeMutableRawPointer?) throws -> [UInt8] {
var ffi_session = try createFfiSessionStoreStruct(session_store);
var ffi_identity = try createFfiIdentityKeyStoreStruct(identity_store);
var ffi_pk = try createFfiPreKeyStoreStruct(pre_key_store);
var ffi_spk = try createFfiSignedPreKeyStoreStruct(signed_pre_key_store);
return try invokeFnReturningArray(fn: { (b,bl) in signal_decrypt_pre_key_message(b,bl,message.nativeHandle(),
address.nativeHandle(),
&ffi_session, &ffi_identity,
&ffi_pk, &ffi_spk, ctx) })
}
func ProcessPreKeyBundle(bundle: PreKeyBundle,
address: ProtocolAddress,
session_store: SessionStore,
identity_store: IdentityKeyStore,
ctx: UnsafeMutableRawPointer?) throws {
var ffi_session = try createFfiSessionStoreStruct(session_store);
var ffi_identity = try createFfiIdentityKeyStoreStruct(identity_store);
try CheckError(signal_process_prekey_bundle(bundle.nativeHandle(),
address.nativeHandle(),
&ffi_session, &ffi_identity, ctx));
}
func GroupEncrypt(group_id: SenderKeyName,
message: [UInt8],
store: SenderKeyStore,
ctx: UnsafeMutableRawPointer?) throws -> [UInt8] {
var ffi = try createFfiSenderKeyStoreStruct(store);
return try invokeFnReturningArray(fn: { (b,bl) in signal_group_encrypt_message(b,bl,group_id.nativeHandle(), message, message.count, &ffi, ctx) });
}
func GroupDecrypt(group_id: SenderKeyName,
message: [UInt8],
store: SenderKeyStore,
ctx: UnsafeMutableRawPointer?) throws -> [UInt8] {
var ffi = try createFfiSenderKeyStoreStruct(store);
return try invokeFnReturningArray(fn: { (b,bl) in signal_group_decrypt_message(b,bl,group_id.nativeHandle(), message, message.count, &ffi, ctx) });
}
func ProcessSenderKeyDistributionMessage(sender_name: SenderKeyName,
msg: SenderKeyDistributionMessage,
store: SenderKeyStore,
ctx: UnsafeMutableRawPointer?) throws {
var ffi = try createFfiSenderKeyStoreStruct(store);
try CheckError(signal_process_sender_key_distribution_message(sender_name.nativeHandle(),
msg.nativeHandle(),
&ffi, ctx));
}

View File

@ -0,0 +1,55 @@
import SignalFfi
import Foundation
class PublicKey {
private var handle: OpaquePointer?;
init(_ bytes: [UInt8]) throws {
try CheckError(signal_publickey_deserialize(&handle, bytes, bytes.count));
}
internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr;
}
deinit {
signal_publickey_destroy(handle);
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_publickey_serialize(handle,b,bl) });
}
func verifySignature(message: [UInt8], signature: [UInt8]) throws -> Bool {
var result : UInt8 = 0;
try CheckError(signal_publickey_verify(handle, &result, message, message.count, signature, signature.count));
if result == 1 {
return true;
} else {
return false;
}
}
func compareWith(other_key: PublicKey) throws -> Int32 {
var result : Int32 = 0;
try CheckError(signal_publickey_compare(&result, handle, other_key.handle));
return result;
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
}
extension PublicKey: Equatable {
static func == (lhs: PublicKey, rhs: PublicKey) -> Bool {
return try! lhs.compareWith(other_key: rhs) == 0;
}
}
extension PublicKey: Comparable {
static func < (lhs: PublicKey, rhs: PublicKey) -> Bool {
return try! lhs.compareWith(other_key: rhs) < 0;
}
}

View File

@ -0,0 +1,67 @@
import SignalFfi
import Foundation
class SenderKeyName {
private var handle: OpaquePointer?;
init(group_name: String, sender_name: String, device_id: UInt32) throws {
try CheckError(signal_sender_key_name_new(&handle, group_name, sender_name, device_id));
}
init(group_name: String, sender: ProtocolAddress) throws {
try CheckError(signal_sender_key_name_new(&handle, group_name, sender.getName(), sender.getDeviceId()));
}
internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr;
}
deinit {
signal_sender_key_name_destroy(handle);
}
func getGroupId() throws -> String {
return try invokeFnReturningString(fn: { (b) in signal_sender_key_name_get_group_id(handle, b) });
}
func getSenderName() throws -> String {
return try invokeFnReturningString(fn: { (b) in signal_sender_key_name_get_sender_name(handle, b) });
}
func getSenderDeviceId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_sender_key_name_get_sender_device_id(handle, i) });
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
}
extension SenderKeyName: Hashable {
static func == (lhs: SenderKeyName, rhs: SenderKeyName) -> Bool {
let lhsDeviceId = try! lhs.getSenderDeviceId();
let rhsDeviceId = try! rhs.getSenderDeviceId();
if lhsDeviceId != rhsDeviceId {
return false;
}
let lhsName = try! lhs.getSenderName();
let rhsName = try! rhs.getSenderName();
if lhsName != rhsName {
return false;
}
let lhsGroupId = try! lhs.getGroupId();
let rhsGroupId = try! rhs.getGroupId();
return lhsGroupId == rhsGroupId;
}
func hash(into hasher: inout Hasher) {
hasher.combine(try! getGroupId())
hasher.combine(try! getSenderName())
hasher.combine(try! getSenderDeviceId())
}
}

View File

@ -0,0 +1,134 @@
import SignalFfi
import Foundation
func invokeFnReturningString(fn: (UnsafeMutablePointer<UnsafePointer<Int8>?>?) -> OpaquePointer?) throws -> String {
var output : UnsafePointer<Int8>? = nil;
try CheckError(fn(&output));
let result = String(cString: output!);
signal_free_string(output);
return result;
}
func invokeFnReturningArray(fn: (UnsafeMutablePointer<UnsafePointer<UInt8>?>?, UnsafeMutablePointer<Int>?) -> OpaquePointer?) throws -> [UInt8] {
var output : UnsafePointer<UInt8>? = nil;
var output_len = 0;
try CheckError(fn(&output, &output_len));
let result = Array(UnsafeBufferPointer(start: output, count: output_len));
signal_free_buffer(output, output_len);
return result;
}
func invokeFnReturningUInt32(fn: (UnsafeMutablePointer<UInt32>?) -> OpaquePointer?) throws -> UInt32 {
var output : UInt32 = 0;
try CheckError(fn(&output));
return output;
}
func invokeFnReturningUInt64(fn: (UnsafeMutablePointer<UInt64>?) -> OpaquePointer?) throws -> UInt64 {
var output : UInt64 = 0;
try CheckError(fn(&output));
return output;
}
func invokeFnReturningPublicKey(fn: (UnsafeMutablePointer<OpaquePointer?>?) -> OpaquePointer?) throws -> PublicKey {
var pk_handle : OpaquePointer?;
try CheckError(fn(&pk_handle));
return PublicKey(raw_ptr: pk_handle);
}
func invokeFnReturningPrivateKey(fn: (UnsafeMutablePointer<OpaquePointer?>?) -> OpaquePointer?) throws -> PrivateKey {
var pk_handle : OpaquePointer?;
try CheckError(fn(&pk_handle));
return PrivateKey(raw_ptr: pk_handle);
}
func invokeFnReturningOptionalPublicKey(fn: (UnsafeMutablePointer<OpaquePointer?>?) -> OpaquePointer?) throws -> Optional<PublicKey> {
var pk_handle : OpaquePointer?;
try CheckError(fn(&pk_handle));
if pk_handle == nil {
return Optional.none;
} else {
return Optional.some(PublicKey(raw_ptr: pk_handle));
}
}
func createFfiIdentityKeyStoreStruct(_ store: IdentityKeyStore) throws -> FfiIdentityKeyStoreStruct {
var ffi = FfiIdentityKeyStoreStruct()
ffi.ctx = unsafeBitCast(store, to: UnsafeMutableRawPointer.self);
throw SignalError.internal_error("not implemented")
}
func createFfiPreKeyStoreStruct(_ store: PreKeyStore) throws -> FfiPreKeyStoreStruct {
throw SignalError.internal_error("not implemented")
}
func createFfiSignedPreKeyStoreStruct(_ store: SignedPreKeyStore) throws -> FfiSignedPreKeyStoreStruct {
throw SignalError.internal_error("not implemented")
}
func createFfiSessionStoreStruct(_ store: SessionStore) throws -> FfiSessionStoreStruct {
throw SignalError.internal_error("not implemented")
}
func createFfiSenderKeyStoreStruct(_ store: SenderKeyStore) throws -> FfiSenderKeyStoreStruct {
print("createFfiSenderKeyStoreStruct");
class SenderKeyStoreWrapper {
var store: SenderKeyStore;
init(store: SenderKeyStore) {
self.store = store;
}
func saveSenderKey(name: SenderKeyName, record: SenderKeyRecord, ctx: UnsafeMutableRawPointer?) throws {
try store.saveSenderKey(name: name, record: record, ctx: ctx);
}
func loadSenderKey(name: SenderKeyName, ctx: UnsafeMutableRawPointer?) throws -> Optional<SenderKeyRecord> {
try store.loadSenderKey(name: name, ctx: ctx);
}
}
func ffiShimStoreSenderKey(store_ctx: UnsafeMutableRawPointer?,
sender_name: OpaquePointer?,
record: OpaquePointer?,
ctx: UnsafeMutableRawPointer?) -> Int32 {
do {
print("ffiShimStoreSenderKey");
var store = unsafeBitCast(store_ctx, to: SenderKeyStore.self);
let sender_name = SenderKeyName(raw_ptr: sender_name);
let record = SenderKeyRecord(raw_ptr: record);
try store.saveSenderKey(name: sender_name, record: record, ctx: ctx);
return 0;
}
catch {
return -1;
}
}
func ffiShimLoadSenderKey(store_ctx: UnsafeMutableRawPointer?,
recordp: UnsafeMutablePointer<OpaquePointer?>?,
sender_name: OpaquePointer?,
ctx: UnsafeMutableRawPointer?) -> Int32 {
do {
print("ffiShimLoadSenderKey");
let store = unsafeBitCast(store_ctx, to: SenderKeyStore.self);
let sender_name = SenderKeyName(raw_ptr: sender_name);
let record = try store.loadSenderKey(name: sender_name, ctx: ctx);
recordp!.pointee = record?.leakNativeHandle();
return 0;
}
catch {
return -1;
}
}
return FfiSenderKeyStoreStruct(
ctx: unsafeBitCast(store, to: UnsafeMutableRawPointer.self),
//ctx: Unmanaged.passUnretained(store).toOpaque(),
load_sender_key: ffiShimLoadSenderKey,
store_sender_key: ffiShimStoreSenderKey)
}

View File

@ -0,0 +1,79 @@
import SignalFfi
import Foundation
class PreKeySignalMessage {
private var handle: OpaquePointer?;
deinit {
signal_pre_key_signal_message_destroy(handle);
}
init(bytes: [UInt8]) throws {
try CheckError(signal_pre_key_signal_message_deserialize(&handle, bytes, bytes.count));
}
init(version: UInt8,
registration_id: UInt32,
pre_key_id: Optional<UInt32>,
signed_pre_key_id: UInt32,
base_key: PublicKey,
identity_key: PublicKey,
message: SignalMessage) throws {
// XXX why is var needed here? pointer arg is const so let should be ok
var pre_key_id = pre_key_id ?? 0xFFFFFFFF;
try CheckError(signal_pre_key_signal_message_new(&handle,
version,
registration_id,
&pre_key_id,
signed_pre_key_id,
base_key.nativeHandle(),
identity_key.nativeHandle(),
message.nativeHandle()));
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_pre_key_signal_message_serialize(handle,b,bl) });
}
func getVersion() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_signal_message_get_version(handle, i) });
}
func getRegistrationId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_signal_message_get_registration_id(handle, i) });
}
func getPreKeyId() throws -> Optional<UInt32> {
let id = try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_signal_message_get_pre_key_id(handle, i) });
if id == 0xFFFFFFFF {
return Optional.none;
} else {
return Optional.some(id);
}
}
func getSignedPreKeyId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_signal_message_get_signed_pre_key_id(handle, i) });
}
func getBaseKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_pre_key_signal_message_get_base_key(k, handle) });
}
func getIdentityKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_pre_key_signal_message_get_identity_key(k, handle) });
}
func getSignalMessage() throws -> SignalMessage {
var m : OpaquePointer?;
try CheckError(signal_pre_key_signal_message_get_signal_message(&m, handle));
return SignalMessage(raw_ptr: m);
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
}

View File

@ -0,0 +1,58 @@
import SignalFfi
import Foundation
class SenderKeyDistributionMessage {
private var handle: OpaquePointer?;
deinit {
signal_sender_key_distribution_message_destroy(handle);
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
init(name: SenderKeyName, store: SenderKeyStore, ctx: UnsafeMutableRawPointer?) throws {
var ffi = try createFfiSenderKeyStoreStruct(store);
try CheckError(signal_create_sender_key_distribution_message(&handle, name.nativeHandle(),
&ffi, ctx));
}
init(key_id: UInt32,
iteration: UInt32,
chain_key: [UInt8],
pk: PublicKey) throws {
try CheckError(signal_sender_key_distribution_message_new(&handle,
key_id,
iteration,
chain_key,
chain_key.count,
pk.nativeHandle()));
}
init(bytes: [UInt8]) throws {
try CheckError(signal_sender_key_distribution_message_deserialize(&handle, bytes, bytes.count));
}
func getSignatureKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_sender_key_distribution_message_get_signature_key(k, handle) });
}
func getId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_sender_key_distribution_message_get_id(handle, i) });
}
func getIteration() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_sender_key_distribution_message_get_iteration(handle, i) });
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_sender_key_distribution_message_serialize(handle,b,bl) });
}
func chain_key() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_sender_key_distribution_message_get_chain_key(handle,b,bl) });
}
}

View File

@ -0,0 +1,55 @@
import SignalFfi
import Foundation
class SenderKeyMessage {
private var handle: OpaquePointer?;
deinit {
signal_sender_key_message_destroy(handle);
}
init(key_id: UInt32,
iteration: UInt32,
chain_key: [UInt8],
ciphertext: [UInt8],
pk: PrivateKey) throws {
try CheckError(signal_sender_key_message_new(&handle,
key_id,
iteration,
ciphertext,
ciphertext.count,
pk.nativeHandle()));
}
init(bytes: [UInt8]) throws {
try CheckError(signal_sender_key_message_deserialize(&handle, bytes, bytes.count));
}
func getKeyId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_sender_key_message_get_key_id(handle, i) });
}
func getIteration() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_sender_key_message_get_iteration(handle, i) });
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_sender_key_message_serialize(handle,b,bl) });
}
func getCiphertext() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_sender_key_message_get_cipher_text(handle,b,bl) });
}
func verifySignature(key: PrivateKey) throws -> Bool {
var result : UInt32 = 0;
try CheckError(signal_sender_key_message_verify_signature(&result, handle, key.nativeHandle()))
if result == 1 {
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,84 @@
import SignalFfi
import Foundation
class SignalMessage {
private var handle: OpaquePointer?;
deinit {
signal_message_destroy(handle);
}
internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr;
}
init(bytes: [UInt8]) throws {
try CheckError(signal_message_deserialize(&handle, bytes, bytes.count));
}
init(version: UInt8,
mac_key: [UInt8],
sender_ratchet_key: PublicKey,
counter: UInt32,
previous_counter: UInt32,
ciphertext: [UInt8],
sender_identity_key: PublicKey,
receiver_identity_key: PublicKey) throws {
try CheckError(signal_message_new(&handle,
version,
mac_key,
mac_key.count,
sender_ratchet_key.nativeHandle(),
counter,
previous_counter,
ciphertext,
ciphertext.count,
sender_identity_key.nativeHandle(),
receiver_identity_key.nativeHandle()));
}
func getSenderRatchetKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_message_get_sender_ratchet_key(k, handle) });
}
func getBody() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_message_get_body(handle,b,bl) });
}
func getSerialized() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_message_get_serialized(handle,b,bl) });
}
func getMessageVersion() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_message_get_message_version(handle, i) });
}
func getCounter() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_message_get_counter(handle, i) });
}
func verifyMac(sender_identity_key: PublicKey,
receiver_identity_key: PublicKey,
mac_key: [UInt8]) throws -> Bool {
var result : UInt32 = 0;
try CheckError(signal_message_verify_mac(&result,
handle,
sender_identity_key.nativeHandle(),
receiver_identity_key.nativeHandle(),
mac_key,
mac_key.count));
if result == 0 {
return true;
} else {
return false;
}
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
}

View File

@ -0,0 +1,97 @@
import SignalFfi
import Foundation
class PreKeyBundle {
private var handle: OpaquePointer?;
deinit {
signal_pre_key_bundle_destroy(handle);
}
internal func nativeHandle() -> OpaquePointer? {
return handle;
}
// with a prekey
init(registration_id: UInt32,
device_id: UInt32,
prekey_id: UInt32,
prekey: PublicKey,
signed_prekey_id: UInt32,
signed_prekey: PublicKey,
signed_prekey_signature: [UInt8],
identity_key: IdentityKey) throws {
// Why is this required??
var prekey_id = prekey_id;
try CheckError(signal_pre_key_bundle_new(&handle,
registration_id,
device_id,
&prekey_id,
prekey.nativeHandle(),
signed_prekey_id,
signed_prekey.nativeHandle(),
signed_prekey_signature,
signed_prekey_signature.count,
identity_key.publicKey().nativeHandle()));
}
// without a prekey
init(registration_id: UInt32,
device_id: UInt32,
signed_prekey_id: UInt32,
signed_prekey: PublicKey,
signed_prekey_signature: [UInt8],
identity_key: IdentityKey) throws {
try CheckError(signal_pre_key_bundle_new(&handle,
registration_id,
device_id,
nil,
nil,
signed_prekey_id,
signed_prekey.nativeHandle(),
signed_prekey_signature,
signed_prekey_signature.count,
identity_key.publicKey().nativeHandle()));
}
func getRegistrationId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_bundle_get_registration_id(handle, i) });
}
func getDeviceId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_bundle_get_device_id(handle, i) });
}
func getSignedPreKeyId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_bundle_get_signed_pre_key_id(handle, i) });
}
func getPreKeyId() throws -> Optional<UInt32> {
let prekey_id = try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_bundle_get_signed_pre_key_id(handle, i) });
if prekey_id == 0xFFFFFFFF {
return Optional.none;
} else {
return Optional.some(prekey_id);
}
}
func getPreKeyPublic() throws -> Optional<PublicKey> {
return try invokeFnReturningOptionalPublicKey(fn: { (k) in signal_pre_key_bundle_get_pre_key_public(k, handle) });
}
func getIdentityKey() throws -> IdentityKey {
let pk = try invokeFnReturningPublicKey(fn: { (k) in signal_pre_key_bundle_get_identity_key(k, handle) });
return IdentityKey(pk: pk);
}
func getSignedPreKeyPublic() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_pre_key_bundle_get_signed_pre_key_public(k, handle) });
}
func getSignedPreKeySignature() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_pre_key_bundle_get_signed_pre_key_signature(handle,b,bl) });
}
}

View File

@ -0,0 +1,36 @@
import SignalFfi
import Foundation
class PreKeyRecord {
private var handle: OpaquePointer?;
deinit {
signal_pre_key_record_destroy(handle);
}
init(bytes: [UInt8]) throws {
try CheckError(signal_pre_key_record_deserialize(&handle, bytes, bytes.count));
}
init(id: UInt32,
pub_key: PublicKey,
priv_key: PrivateKey) throws {
try CheckError(signal_pre_key_record_new(&handle, id, pub_key.nativeHandle(), priv_key.nativeHandle()));
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_pre_key_record_serialize(handle,b,bl) });
}
func getId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_pre_key_record_get_id(handle, i) });
}
func getPublicKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_pre_key_record_get_public_key(k, handle) });
}
func getPrivateKey() throws -> PrivateKey {
return try invokeFnReturningPrivateKey(fn: { (k) in signal_pre_key_record_get_private_key(k, handle) });
}
}

View File

@ -0,0 +1,32 @@
import SignalFfi
import Foundation
class SenderKeyRecord {
private var handle: OpaquePointer?;
deinit {
signal_sender_key_record_destroy(handle);
}
init(bytes: [UInt8]) throws {
try CheckError(signal_sender_key_record_deserialize(&handle, bytes, bytes.count));
}
internal init(raw_ptr: OpaquePointer?) {
handle = raw_ptr;
}
init() throws {
try CheckError(signal_sender_key_record_new_fresh(&handle));
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_sender_key_record_serialize(handle,b,bl) });
}
func leakNativeHandle() -> OpaquePointer? {
let save = handle;
handle = nil;
return save;
}
}

View File

@ -0,0 +1,18 @@
import SignalFfi
import Foundation
class SessionRecord {
private var handle: OpaquePointer?;
deinit {
signal_session_record_destroy(handle);
}
init(bytes: [UInt8]) throws {
try CheckError(signal_session_record_deserialize(&handle, bytes, bytes.count));
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_session_record_serialize(handle,b,bl) });
}
}

View File

@ -0,0 +1,38 @@
import SignalFfi
import Foundation
class SignedPreKeyRecord {
private var handle: OpaquePointer?;
deinit {
signal_signed_pre_key_record_destroy(handle);
}
init(bytes: [UInt8]) throws {
try CheckError(signal_signed_pre_key_record_deserialize(&handle, bytes, bytes.count));
}
func serialize() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_signed_pre_key_record_serialize(handle,b,bl) });
}
func getId() throws -> UInt32 {
return try invokeFnReturningUInt32(fn: { (i) in signal_signed_pre_key_record_get_id(handle, i) });
}
func getTimestamp() throws -> UInt64 {
return try invokeFnReturningUInt64(fn: { (i) in signal_signed_pre_key_record_get_timestamp(handle, i) });
}
func getPublicKey() throws -> PublicKey {
return try invokeFnReturningPublicKey(fn: { (k) in signal_signed_pre_key_record_get_public_key(k, handle) });
}
func getPrivateKey() throws -> PrivateKey {
return try invokeFnReturningPrivateKey(fn: { (k) in signal_signed_pre_key_record_get_private_key(k, handle) });
}
func getSignature() throws -> [UInt8] {
return try invokeFnReturningArray(fn: { (b,bl) in signal_signed_pre_key_record_get_signature(handle,b,bl) });
}
}

6
Tests/LinuxMain.swift Normal file
View File

@ -0,0 +1,6 @@
import XCTest
@testable import SwiftSignalTests
XCTMain([
testCase(SwiftSignalTests.allTests)
])

View File

@ -0,0 +1,217 @@
import XCTest
@testable import SwiftSignal
class SwiftSignalTests: XCTestCase {
func testHkdf() {
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,
];
let info : [UInt8] = [];
let salt : [UInt8] = [];
let okm : [UInt8] = [0x8d, 0xa4, 0xe7, 0x75];
let output_length = UInt32(okm.count);
let version = UInt32(3);
let derived = try! hkdf(output_length: output_length,
version: version,
input_key_material: ikm,
salt: salt,
info: info);
XCTAssertEqual(derived, okm);
XCTAssertThrowsError(try hkdf(output_length: output_length,
version: 19,
input_key_material: ikm,
salt: salt,
info: info));
}
func testAddress() {
let addr = try! ProtocolAddress(name: "addr1", device_id: 5);
XCTAssertEqual(try! addr.getName(), "addr1");
XCTAssertEqual(try! addr.getDeviceId(), 5);
}
func testPkOperations() {
let sk = try! PrivateKey();
let sk_bytes = try! sk.serialize();
let pk = try! sk.getPublicKey();
let sk_reloaded = try! PrivateKey(sk_bytes);
let pk_reloaded = try! sk_reloaded.getPublicKey();
XCTAssertEqual(pk, pk_reloaded);
XCTAssertEqual(try! pk.serialize(), try! pk_reloaded.serialize());
var message : [UInt8] = [1, 2, 3];
var signature = try! sk.generateSignature(message: message);
XCTAssertEqual(try! pk.verifySignature(message: message, signature: signature), true);
signature[5] ^= 1;
XCTAssertEqual(try! pk.verifySignature(message: message, signature: signature), false);
signature[5] ^= 1;
XCTAssertEqual(try! pk.verifySignature(message: message, signature: signature), true);
message[1] ^= 1;
XCTAssertEqual(try! pk.verifySignature(message: message, signature: signature), false);
message[1] ^= 1;
XCTAssertEqual(try! pk.verifySignature(message: message, signature: signature), true);
let sk2 = try! PrivateKey();
let shared_secret1 = try! sk.keyAgreement(other_key: sk2.getPublicKey());
let shared_secret2 = try! sk2.keyAgreement(other_key: sk.getPublicKey());
XCTAssertEqual(shared_secret1, shared_secret2);
}
func testFingerprint() {
let ALICE_IDENTITY : [UInt8] = [0x05, 0x06, 0x86, 0x3b, 0xc6, 0x6d, 0x02, 0xb4, 0x0d, 0x27, 0xb8, 0xd4, 0x9c, 0xa7, 0xc0, 0x9e, 0x92, 0x39, 0x23, 0x6f, 0x9d, 0x7d, 0x25, 0xd6, 0xfc, 0xca, 0x5c, 0xe1, 0x3c, 0x70, 0x64, 0xd8, 0x68];
let BOB_IDENTITY : [UInt8] = [0x05, 0xf7, 0x81, 0xb6, 0xfb, 0x32, 0xfe, 0xd9, 0xba, 0x1c, 0xf2, 0xde, 0x97, 0x8d, 0x4d, 0x5d, 0xa2, 0x8d, 0xc3, 0x40, 0x46, 0xae, 0x81, 0x44, 0x02, 0xb5, 0xc0, 0xdb, 0xd9, 0x6f, 0xda, 0x90, 0x7b];
let VERSION_1 = 1;
let DISPLAYABLE_FINGERPRINT_V1 = "300354477692869396892869876765458257569162576843440918079131";
let ALICE_SCANNABLE_FINGERPRINT_V1 : [UInt8] = [0x08, 0x01, 0x12, 0x22, 0x0a, 0x20, 0x1e, 0x30, 0x1a, 0x03, 0x53, 0xdc, 0xe3, 0xdb, 0xe7, 0x68, 0x4c, 0xb8, 0x33, 0x6e, 0x85, 0x13, 0x6c, 0xdc, 0x0e, 0xe9, 0x62, 0x19, 0x49, 0x4a, 0xda, 0x30, 0x5d, 0x62, 0xa7, 0xbd, 0x61, 0xdf, 0x1a, 0x22, 0x0a, 0x20, 0xd6, 0x2c, 0xbf, 0x73, 0xa1, 0x15, 0x92, 0x01, 0x5b, 0x6b, 0x9f, 0x16, 0x82, 0xac, 0x30, 0x6f, 0xea, 0x3a, 0xaf, 0x38, 0x85, 0xb8, 0x4d, 0x12, 0xbc, 0xa6, 0x31, 0xe9, 0xd4, 0xfb, 0x3a, 0x4d];
let BOB_SCANNABLE_FINGERPRINT_V1 : [UInt8] = [0x08, 0x01, 0x12, 0x22, 0x0a, 0x20, 0xd6, 0x2c, 0xbf, 0x73, 0xa1, 0x15, 0x92, 0x01, 0x5b, 0x6b, 0x9f, 0x16, 0x82, 0xac, 0x30, 0x6f, 0xea, 0x3a, 0xaf, 0x38, 0x85, 0xb8, 0x4d, 0x12, 0xbc, 0xa6, 0x31, 0xe9, 0xd4, 0xfb, 0x3a, 0x4d, 0x1a, 0x22, 0x0a, 0x20, 0x1e, 0x30, 0x1a, 0x03, 0x53, 0xdc, 0xe3, 0xdb, 0xe7, 0x68, 0x4c, 0xb8, 0x33, 0x6e, 0x85, 0x13, 0x6c, 0xdc, 0x0e, 0xe9, 0x62, 0x19, 0x49, 0x4a, 0xda, 0x30, 0x5d, 0x62, 0xa7, 0xbd, 0x61, 0xdf];
let VERSION_2 = 2;
let DISPLAYABLE_FINGERPRINT_V2 = DISPLAYABLE_FINGERPRINT_V1;
let ALICE_SCANNABLE_FINGERPRINT_V2: [UInt8] = [0x08, 0x02, 0x12, 0x22, 0x0a, 0x20, 0x1e, 0x30, 0x1a, 0x03, 0x53, 0xdc, 0xe3, 0xdb, 0xe7, 0x68, 0x4c, 0xb8, 0x33, 0x6e, 0x85, 0x13, 0x6c, 0xdc, 0x0e, 0xe9, 0x62, 0x19, 0x49, 0x4a, 0xda, 0x30, 0x5d, 0x62, 0xa7, 0xbd, 0x61, 0xdf, 0x1a, 0x22, 0x0a, 0x20, 0xd6, 0x2c, 0xbf, 0x73, 0xa1, 0x15, 0x92, 0x01, 0x5b, 0x6b, 0x9f, 0x16, 0x82, 0xac, 0x30, 0x6f, 0xea, 0x3a, 0xaf, 0x38, 0x85, 0xb8, 0x4d, 0x12, 0xbc, 0xa6, 0x31, 0xe9, 0xd4, 0xfb, 0x3a, 0x4d];
let BOB_SCANNABLE_FINGERPRINT_V2 : [UInt8] = [0x08, 0x02, 0x12, 0x22, 0x0a, 0x20, 0xd6, 0x2c, 0xbf, 0x73, 0xa1, 0x15, 0x92, 0x01, 0x5b, 0x6b, 0x9f, 0x16, 0x82, 0xac, 0x30, 0x6f, 0xea, 0x3a, 0xaf, 0x38, 0x85, 0xb8, 0x4d, 0x12, 0xbc, 0xa6, 0x31, 0xe9, 0xd4, 0xfb, 0x3a, 0x4d, 0x1a, 0x22, 0x0a, 0x20, 0x1e, 0x30, 0x1a, 0x03, 0x53, 0xdc, 0xe3, 0xdb, 0xe7, 0x68, 0x4c, 0xb8, 0x33, 0x6e, 0x85, 0x13, 0x6c, 0xdc, 0x0e, 0xe9, 0x62, 0x19, 0x49, 0x4a, 0xda, 0x30, 0x5d, 0x62, 0xa7, 0xbd, 0x61, 0xdf];
// testVectorsVersion1
let aliceStableId : [UInt8] = [UInt8]("+14152222222".utf8);
let bobStableId : [UInt8] = [UInt8]("+14153333333".utf8);
let aliceIdentityKey = try! PublicKey(ALICE_IDENTITY);
let bobIdentityKey = try! PublicKey(BOB_IDENTITY);
let generator = NumericFingerprintGenerator(iterations: 5200);
let aliceFingerprint = try! generator.createFor(version: VERSION_1,
local_identifier: aliceStableId,
local_key: aliceIdentityKey,
remote_identifier: bobStableId,
remote_key: bobIdentityKey);
let bobFingerprint = try! generator.createFor(version: VERSION_1,
local_identifier: bobStableId,
local_key: bobIdentityKey,
remote_identifier: aliceStableId,
remote_key: aliceIdentityKey);
XCTAssertEqual(aliceFingerprint.displayable.formatted, DISPLAYABLE_FINGERPRINT_V1);
XCTAssertEqual(bobFingerprint.displayable.formatted, DISPLAYABLE_FINGERPRINT_V1);
XCTAssertEqual(aliceFingerprint.scannable.encoding, ALICE_SCANNABLE_FINGERPRINT_V1);
XCTAssertEqual(bobFingerprint.scannable.encoding, BOB_SCANNABLE_FINGERPRINT_V1);
// testVectorsVersion2
let aliceFingerprint2 = try! generator.createFor(version: VERSION_2,
local_identifier: aliceStableId,
local_key: aliceIdentityKey,
remote_identifier: bobStableId,
remote_key: bobIdentityKey);
let bobFingerprint2 = try! generator.createFor(version: VERSION_2,
local_identifier: bobStableId,
local_key: bobIdentityKey,
remote_identifier: aliceStableId,
remote_key: aliceIdentityKey);
XCTAssertEqual(aliceFingerprint2.displayable.formatted, DISPLAYABLE_FINGERPRINT_V2);
XCTAssertEqual(bobFingerprint2.displayable.formatted, DISPLAYABLE_FINGERPRINT_V2);
XCTAssertEqual(aliceFingerprint2.scannable.encoding, ALICE_SCANNABLE_FINGERPRINT_V2);
XCTAssertEqual(bobFingerprint2.scannable.encoding, BOB_SCANNABLE_FINGERPRINT_V2);
// testMismatchingFingerprints
let mitmIdentityKey = try! PrivateKey().getPublicKey();
let aliceFingerprintM = try! generator.createFor(version: VERSION_1,
local_identifier: aliceStableId,
local_key: aliceIdentityKey,
remote_identifier: bobStableId,
remote_key: mitmIdentityKey);
let bobFingerprintM = try! generator.createFor(version: VERSION_1,
local_identifier: bobStableId,
local_key: bobIdentityKey,
remote_identifier: aliceStableId,
remote_key: aliceIdentityKey);
XCTAssertNotEqual(aliceFingerprintM.displayable.formatted,
bobFingerprintM.displayable.formatted);
XCTAssertEqual(try! bobFingerprintM.scannable.compareWith(other: aliceFingerprintM.scannable), false);
XCTAssertEqual(try! aliceFingerprintM.scannable.compareWith(other: bobFingerprintM.scannable), false);
XCTAssertEqual(aliceFingerprintM.displayable.formatted.count, 60);
// testMismatchingIdentifiers
let badBobStableId : [UInt8] = [UInt8]("+14153333334".utf8);
let aliceFingerprintI = try! generator.createFor(version: VERSION_1,
local_identifier: aliceStableId,
local_key: aliceIdentityKey,
remote_identifier: badBobStableId,
remote_key: bobIdentityKey);
let bobFingerprintI = try! generator.createFor(version: VERSION_1,
local_identifier: bobStableId,
local_key: bobIdentityKey,
remote_identifier: aliceStableId,
remote_key: aliceIdentityKey);
XCTAssertNotEqual(aliceFingerprintI.displayable.formatted,
bobFingerprintI.displayable.formatted);
XCTAssertEqual(try! bobFingerprintI.scannable.compareWith(other: aliceFingerprintI.scannable), false);
XCTAssertEqual(try! aliceFingerprintI.scannable.compareWith(other: bobFingerprintI.scannable), false);
}
func testGroupCipher() {
let sender = try! ProtocolAddress(name: "+14159999111", device_id: 4);
let group_id = try! SenderKeyName(group_name: "summer camp", sender: sender);
let a_store = InMemorySenderKeyStore();
let b_store = InMemorySenderKeyStore();
print("here");
let skdm = try! SenderKeyDistributionMessage(name: group_id, store: a_store, ctx: nil);
print("created skdm");
let skdm_bits = try! skdm.serialize();
let skdm_r = try! SenderKeyDistributionMessage(bytes: skdm_bits);
try! ProcessSenderKeyDistributionMessage(sender_name: group_id,
msg: skdm_r,
store: b_store,
ctx: nil);
let a_ctext = try! GroupEncrypt(group_id: group_id, message: [1,2,3], store: a_store, ctx: nil);
let b_ptext = try! GroupDecrypt(group_id: group_id, message: a_ctext, store: b_store, ctx: nil);
XCTAssertEqual(b_ptext, [1,2,3]);
}
static var allTests: [(String, (SwiftSignalTests) -> () throws -> Void)] {
return [
("testAddreses", testAddress),
("testFingerprint", testFingerprint),
("testPkOperations", testPkOperations),
("testHkdf", testHkdf),
("testGroupCipher", testGroupCipher),
]
}
}