mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-03-27 14:43:06 +00:00
* feat: ffi * feat: ffi interface and header generation * feat: nim ffi example * chore: doc * fix: encrypt state clean * chore: zeroize when drop
145 lines
4.4 KiB
Nim
145 lines
4.4 KiB
Nim
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")
|