mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-05-12 13:09:29 +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",
|
"hkdf",
|
||||||
"rand 0.9.3",
|
"rand 0.9.3",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"safer-ffi",
|
|
||||||
"serde",
|
"serde",
|
||||||
"storage",
|
"storage",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
|||||||
@ -6,10 +6,6 @@ edition = "2024"
|
|||||||
[lib]
|
[lib]
|
||||||
crate-type = ["rlib", "cdylib"]
|
crate-type = ["rlib", "cdylib"]
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "generate-headers"
|
|
||||||
required-features = ["headers"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
x25519-dalek = { version="2.0.1", features=["static_secrets"] }
|
x25519-dalek = { version="2.0.1", features=["static_secrets"] }
|
||||||
chacha20poly1305 = "0.10.1"
|
chacha20poly1305 = "0.10.1"
|
||||||
@ -18,14 +14,10 @@ rand = "0.9.3"
|
|||||||
hkdf = "0.12.4"
|
hkdf = "0.12.4"
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
blake2 = "0.10.6"
|
blake2 = "0.10.6"
|
||||||
safer-ffi = "0.1.13"
|
|
||||||
zeroize = "1.8.2"
|
zeroize = "1.8.2"
|
||||||
storage = { workspace = true }
|
storage = { workspace = true }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
|
||||||
[features]
|
|
||||||
headers = ["safer-ffi/headers"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sqlite = { package = "chat-sqlite", path = "../sqlite" }
|
sqlite = { package = "chat-sqlite", path = "../sqlite" }
|
||||||
tempfile = "3"
|
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 storage
|
||||||
cargo run --example storage_demo --features sqlcipher
|
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 aead;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod ffi;
|
|
||||||
pub mod hkdf;
|
pub mod hkdf;
|
||||||
pub mod keypair;
|
pub mod keypair;
|
||||||
pub mod reader;
|
pub mod reader;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user