chore: remove ffi from double ratchet

This commit is contained in:
kaichaosun 2026-04-10 14:30:53 +08:00
parent c0e07c765e
commit b6a6a2b76e
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
10 changed files with 1 additions and 299 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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"

View File

@ -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")

View File

@ -1,5 +0,0 @@
use double_ratchets::ffi;
fn main() -> std::io::Result<()> {
ffi::generate_headers()
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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);
}

View File

@ -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;
@ -11,5 +10,5 @@ pub mod types;
pub use keypair::InstallationKeyPair; pub use keypair::InstallationKeyPair;
pub use state::{Header, RatchetState, SkippedKey}; pub use state::{Header, RatchetState, SkippedKey};
pub use storage::{ pub use storage::{
RatchetSession, SessionError, restore_ratchet_state, to_ratchet_record, to_skipped_key_records, restore_ratchet_state, to_ratchet_record, to_skipped_key_records, RatchetSession, SessionError,
}; };