mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-05-12 04:59:27 +00:00
refactor: remove FFI interfaces from double ratchets (#84)
* chore: remove ffi from double ratchet * chore: update doc * chore: format
This commit is contained in:
parent
c0e07c765e
commit
94935c28fe
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -260,7 +260,6 @@ dependencies = [
|
||||
"hkdf",
|
||||
"rand 0.9.3",
|
||||
"rand_core 0.6.4",
|
||||
"safer-ffi",
|
||||
"serde",
|
||||
"storage",
|
||||
"tempfile",
|
||||
|
||||
@ -6,10 +6,6 @@ edition = "2024"
|
||||
[lib]
|
||||
crate-type = ["rlib", "cdylib"]
|
||||
|
||||
[[bin]]
|
||||
name = "generate-headers"
|
||||
required-features = ["headers"]
|
||||
|
||||
[dependencies]
|
||||
x25519-dalek = { version="2.0.1", features=["static_secrets"] }
|
||||
chacha20poly1305 = "0.10.1"
|
||||
@ -18,14 +14,10 @@ rand = "0.9.3"
|
||||
hkdf = "0.12.4"
|
||||
thiserror = "2"
|
||||
blake2 = "0.10.6"
|
||||
safer-ffi = "0.1.13"
|
||||
zeroize = "1.8.2"
|
||||
storage = { workspace = true }
|
||||
serde = "1.0"
|
||||
|
||||
[features]
|
||||
headers = ["safer-ffi/headers"]
|
||||
|
||||
[dev-dependencies]
|
||||
sqlite = { package = "chat-sqlite", path = "../sqlite" }
|
||||
tempfile = "3"
|
||||
|
||||
@ -23,12 +23,3 @@ cargo run --example double_ratchet_basic
|
||||
cargo run --example storage_demo --features storage
|
||||
cargo run --example storage_demo --features sqlcipher
|
||||
```
|
||||
|
||||
Run Nim FFI example,
|
||||
|
||||
```bash
|
||||
# In the root folder (libchat)
|
||||
cargo build --release
|
||||
# In ffi-nim-example folder
|
||||
nimble run
|
||||
```
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "kaichaosun"
|
||||
description = "A new awesome nimble package"
|
||||
license = "MIT"
|
||||
srcDir = "src"
|
||||
bin = @["ffi_nim_example"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 2.2.4"
|
||||
@ -1,144 +0,0 @@
|
||||
when defined(macosx):
|
||||
{.passL: "-Wl,-rpath,@executable_path/../../target/release".}
|
||||
when defined(linux):
|
||||
{.passL: "-Wl,-rpath,'$ORIGIN/../../target/release'".}
|
||||
|
||||
# Portable dynlib name with override capability (-d:RLN_LIB:"...")
|
||||
when defined(macosx):
|
||||
const DR_LIB* {.strdefine.} = "libdouble_ratchets.dylib"
|
||||
elif defined(linux):
|
||||
const DR_LIB* {.strdefine.} = "libdouble_ratchets.so"
|
||||
elif defined(windows):
|
||||
const DR_LIB* {.strdefine.} = "double_ratchets.dll"
|
||||
else:
|
||||
const DR_LIB* {.strdefine.} = "double_ratchets"
|
||||
|
||||
type FFIRatchetState* = object
|
||||
|
||||
type FFIEncryptResult* = object
|
||||
|
||||
type FFIInstallationKeyPair* = object
|
||||
|
||||
type CSize* = csize_t
|
||||
|
||||
type Vec_uint8* = object
|
||||
dataPtr*: ptr uint8
|
||||
len*: CSize
|
||||
cap*: CSize
|
||||
|
||||
type Array_uint8_32* {.bycopy.} = object
|
||||
idx*: array[32, uint8]
|
||||
|
||||
type CResult_Vec_uint8_Vec_uint8* {.bycopy.} = object ## <No documentation available>
|
||||
ok*: Vec_uint8
|
||||
err*: Vec_uint8
|
||||
|
||||
proc double_ratchet_init_receiver*(
|
||||
shared_secret: Array_uint8_32, keypair: ptr FFIInstallationKeyPair
|
||||
): ptr FFIRatchetState {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc double_ratchet_init_sender*(
|
||||
shared_secret: Array_uint8_32, remote_pub: Array_uint8_32
|
||||
): ptr FFIRatchetState {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc double_ratchet_encrypt_message*(
|
||||
state: ptr FFIRatchetState, plaintext: ptr Vec_uint8
|
||||
): ptr FFIEncryptResult {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc double_ratchet_descrypt_message*(
|
||||
state: ptr FFIRatchetState, encrypted: ptr FFIEncryptResult
|
||||
): CResult_Vec_uint8_Vec_uint8 {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc ratchet_state_destroy*(state: ptr FFIRatchetState) {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc encrypt_result_destroy*(result: ptr FFIEncryptResult) {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc installation_key_pair_generate*(): ptr FFIInstallationKeyPair {.
|
||||
importc, dynlib: DR_LIB
|
||||
.}
|
||||
|
||||
proc installation_key_pair_public*(
|
||||
keypair: ptr FFIInstallationKeyPair
|
||||
): Array_uint8_32 {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc installation_key_pair_destroy*(
|
||||
keypair: ptr FFIInstallationKeyPair
|
||||
) {.importc, dynlib: DR_LIB.}
|
||||
|
||||
proc ffi_c_string_free*(s: Vec_uint8) {.importc, cdecl, dynlib: DR_LIB.}
|
||||
|
||||
proc asString*(v: Vec_uint8): string =
|
||||
if v.dataPtr.isNil or v.len == 0:
|
||||
return ""
|
||||
result = newString(v.len.int)
|
||||
copyMem(addr result[0], v.dataPtr, v.len.int)
|
||||
|
||||
when isMainModule:
|
||||
echo("start run")
|
||||
|
||||
# === Shared secret (like X3DH) ===
|
||||
var sharedSecret: Array_uint8_32
|
||||
for i in 0 .. 31:
|
||||
sharedSecret.idx[i] = 42'u8
|
||||
|
||||
# === Bob generates DH keypair ===
|
||||
let bobKey = installation_key_pair_generate()
|
||||
let bobPub = installation_key_pair_public(bobKey)
|
||||
echo("bob public key:", bobPub)
|
||||
|
||||
# === Alice initializes as sender ===
|
||||
let alice = double_ratchet_init_sender(sharedSecret, bobPub)
|
||||
# # === Bob initializes as receiver ===
|
||||
let bob = double_ratchet_init_receiver(sharedSecret, bobKey)
|
||||
|
||||
# # === Alice sends message to Bob ===
|
||||
var msg1: array[3, uint8] = [11'u8, 12, 13]
|
||||
var msg1Vec = Vec_uint8(
|
||||
dataPtr: cast[ptr uint8](addr msg1[0]), len: CSize(msg1.len), cap: CSize(msg1.len)
|
||||
)
|
||||
|
||||
let enc1 = double_ratchet_encrypt_message(alice, addr msg1Vec)
|
||||
let dec1 = double_ratchet_descrypt_message(bob, enc1)
|
||||
|
||||
encrypt_result_destroy(enc1)
|
||||
|
||||
if dec1.err.dataPtr != nil:
|
||||
echo "Bob failed to decrypt: ", asString(dec1.err)
|
||||
ffi_c_string_free(dec1.err)
|
||||
ratchet_state_destroy(alice)
|
||||
ratchet_state_destroy(bob)
|
||||
installation_key_pair_destroy(bobKey)
|
||||
quit 1
|
||||
let res1 = dec1.ok
|
||||
var plaintext1: array[3, uint8]
|
||||
copyMem(addr plaintext1[0], res1.dataPtr, res1.len.int)
|
||||
echo "Bob received: ", plaintext1
|
||||
|
||||
# # === Bob replies (triggers DH ratchet) ===
|
||||
var msg2: array[3, uint8] = [1'u8, 2, 3]
|
||||
var msg2Vec = Vec_uint8(
|
||||
dataPtr: cast[ptr uint8](addr msg2[0]), len: CSize(msg1.len), cap: CSize(msg1.len)
|
||||
)
|
||||
let enc2 = double_ratchet_encrypt_message(bob, addr msg2Vec)
|
||||
let dec2 = double_ratchet_descrypt_message(alice, enc2)
|
||||
|
||||
encrypt_result_destroy(enc2)
|
||||
|
||||
if dec2.err.dataPtr != nil:
|
||||
echo "Alice failed to decrypt: ", asString(dec2.err)
|
||||
ffi_c_string_free(dec2.err)
|
||||
ratchet_state_destroy(alice)
|
||||
ratchet_state_destroy(bob)
|
||||
installation_key_pair_destroy(bobKey)
|
||||
quit 1
|
||||
let res2 = dec2.ok
|
||||
var plaintext2: array[3, uint8]
|
||||
copyMem(addr plaintext2[0], res2.dataPtr, res2.len.int)
|
||||
echo "Alice received: ", plaintext2
|
||||
|
||||
# # === Cleanup ===
|
||||
ratchet_state_destroy(alice)
|
||||
ratchet_state_destroy(bob)
|
||||
installation_key_pair_destroy(bobKey)
|
||||
|
||||
echo("==end==\n")
|
||||
@ -1,5 +0,0 @@
|
||||
use double_ratchets::ffi;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
ffi::generate_headers()
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
use safer_ffi::prelude::*;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use crate::{
|
||||
Header, RatchetState,
|
||||
ffi::{key::FFIInstallationKeyPair, utils::CResult},
|
||||
};
|
||||
|
||||
#[derive_ReprC]
|
||||
#[repr(opaque)]
|
||||
pub struct FFIRatchetState(pub(crate) RatchetState);
|
||||
|
||||
#[derive_ReprC]
|
||||
#[repr(opaque)]
|
||||
pub struct FFIEncryptResult {
|
||||
pub ciphertext: Vec<u8>,
|
||||
pub header: Header,
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn double_ratchet_init_sender(
|
||||
shared_secret: [u8; 32],
|
||||
remote_pub: [u8; 32],
|
||||
) -> repr_c::Box<FFIRatchetState> {
|
||||
let state = RatchetState::init_sender(shared_secret, PublicKey::from(remote_pub));
|
||||
Box::new(FFIRatchetState(state)).into()
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn double_ratchet_init_receiver(
|
||||
shared_secret: [u8; 32],
|
||||
keypair: &FFIInstallationKeyPair,
|
||||
) -> repr_c::Box<FFIRatchetState> {
|
||||
let state = RatchetState::init_receiver(shared_secret, keypair.0.clone());
|
||||
Box::new(FFIRatchetState(state)).into()
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn double_ratchet_encrypt_message(
|
||||
state: &mut FFIRatchetState,
|
||||
plaintext: &repr_c::Vec<u8>,
|
||||
) -> repr_c::Box<FFIEncryptResult> {
|
||||
let encrypted = state.0.encrypt_message(plaintext);
|
||||
let result = FFIEncryptResult {
|
||||
ciphertext: encrypted.0,
|
||||
header: encrypted.1,
|
||||
};
|
||||
Box::new(result).into()
|
||||
}
|
||||
|
||||
//TODO rename decrypt
|
||||
#[ffi_export]
|
||||
fn double_ratchet_descrypt_message(
|
||||
state: &mut FFIRatchetState,
|
||||
encrypted: &FFIEncryptResult,
|
||||
) -> CResult<repr_c::Vec<u8>, repr_c::String> {
|
||||
let decrypted = state
|
||||
.0
|
||||
.decrypt_message(&encrypted.ciphertext, encrypted.header.clone());
|
||||
|
||||
match decrypted {
|
||||
Ok(plaintext) => CResult {
|
||||
ok: Some(plaintext.into()),
|
||||
err: None,
|
||||
},
|
||||
Err(err) => CResult {
|
||||
ok: None,
|
||||
err: Some(err.to_string().into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn ratchet_state_destroy(state: repr_c::Box<FFIRatchetState>) {
|
||||
drop(state)
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn encrypt_result_destroy(result: repr_c::Box<FFIEncryptResult>) {
|
||||
drop(result)
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
use safer_ffi::prelude::*;
|
||||
|
||||
use crate::InstallationKeyPair;
|
||||
|
||||
#[derive_ReprC]
|
||||
#[repr(opaque)]
|
||||
pub struct FFIInstallationKeyPair(pub(crate) InstallationKeyPair);
|
||||
|
||||
#[ffi_export]
|
||||
fn installation_key_pair_generate() -> repr_c::Box<FFIInstallationKeyPair> {
|
||||
Box::new(FFIInstallationKeyPair(InstallationKeyPair::generate())).into()
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn installation_key_pair_public(keypair: &FFIInstallationKeyPair) -> [u8; 32] {
|
||||
keypair.0.public().clone().to_bytes()
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
fn installation_key_pair_destroy(keypair: repr_c::Box<FFIInstallationKeyPair>) {
|
||||
drop(keypair)
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
pub mod doubleratchet;
|
||||
pub mod key;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(feature = "headers")]
|
||||
pub fn generate_headers() -> std::io::Result<()> {
|
||||
safer_ffi::headers::builder()
|
||||
.to_file("double_ratchet.h")?
|
||||
.generate()
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
use safer_ffi::prelude::*;
|
||||
|
||||
#[derive_ReprC]
|
||||
#[repr(C)]
|
||||
pub struct CResult<T: ReprC, Err: ReprC> {
|
||||
pub ok: Option<T>,
|
||||
pub err: Option<Err>,
|
||||
}
|
||||
|
||||
#[ffi_export]
|
||||
pub fn ffi_c_string_free(s: repr_c::String) {
|
||||
drop(s);
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
pub mod aead;
|
||||
pub mod errors;
|
||||
pub mod ffi;
|
||||
pub mod hkdf;
|
||||
pub mod keypair;
|
||||
pub mod reader;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user