Skip to content

Commit

Permalink
Add starters for bindings/web5_c and bound/golang (#359)
Browse files Browse the repository at this point in the history
  • Loading branch information
KendallWeihe authored Sep 27, 2024
1 parent 4a5b8ff commit 005bd3c
Show file tree
Hide file tree
Showing 36 changed files with 1,262 additions and 33 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"bindings/web5_c",
"bindings/web5_uniffi",
"bindings/web5_uniffi_wrapper",
"bindings/web5_wasm",
Expand Down
6 changes: 5 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ test: setup
cargo test --workspace

lint: setup
cargo clippy --workspace
cargo clippy --workspace --exclude web5_c
cargo clippy --package web5_c -- -A clippy::not_unsafe_ptr_arg_deref
cargo fmt

bind: setup
just bind-kotlin

bindc: setup
cargo build --release --package web5_c

bind-kotlin: setup
mkdir -p bound/kt/src/main/resources
cargo build --release --package web5_uniffi --target aarch64-apple-darwin
Expand Down
1 change: 1 addition & 0 deletions bin/.go-1.23.1.pkg
1 change: 1 addition & 0 deletions bin/go
1 change: 1 addition & 0 deletions bin/gofmt
14 changes: 14 additions & 0 deletions bindings/web5_c/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "web5_c"
version = "0.1.0"
edition = "2021"
homepage.workspace = true
repository.workspace = true
license-file.workspace = true

[dependencies]
lazy_static = { workspace = true }
web5 = { path = "../../crates/web5" }

[lib]
crate-type = ["cdylib"]
34 changes: 34 additions & 0 deletions bindings/web5_c/src/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

#[no_mangle]
pub extern "C" fn free_string(s: *mut c_char) {
if !s.is_null() {
unsafe {
let _ = CString::from_raw(s);
}
}
}

pub fn free_bytes(ptr: *mut u8) {
if !ptr.is_null() {
unsafe {
let _ = Box::from_raw(ptr);
}
}
}

pub fn opt_cstr_to_string(c_str: *const c_char) -> Option<String> {
if c_str.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() })
}
}

pub fn opt_string_to_cstr(opt: Option<String>) -> *const c_char {
match opt {
Some(s) => CString::new(s).unwrap().into_raw(),
None => std::ptr::null(),
}
}
23 changes: 23 additions & 0 deletions bindings/web5_c/src/crypto/dsa/ed25519.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::{add_signer_to_registry, rust_signer_sign, CSigner};
use crate::crypto::jwk::CJwk;
use std::ptr;
use std::sync::Arc;
use web5::crypto::dsa::{ed25519::Ed25519Signer, Signer};
use web5::crypto::jwk::Jwk;

#[no_mangle]
pub extern "C" fn new_ed25519_signer(cjwk_ptr: *const CJwk) -> *mut CSigner {
if cjwk_ptr.is_null() {
return ptr::null_mut();
}

let jwk = unsafe { Jwk::from(&*cjwk_ptr) };
let signer: Arc<dyn Signer> = Arc::new(Ed25519Signer::new(jwk));

let signer_id = add_signer_to_registry(signer);

Box::into_raw(Box::new(CSigner {
signer_id,
sign: rust_signer_sign,
}))
}
83 changes: 83 additions & 0 deletions bindings/web5_c/src/crypto/dsa/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use lazy_static::lazy_static;
use std::{
collections::HashMap,
ptr,
sync::{
atomic::{AtomicI32, Ordering},
Arc, Mutex,
},
};
use web5::crypto::dsa::Signer;

pub mod ed25519;
pub mod poc;

static SIGNER_ID_COUNTER: AtomicI32 = AtomicI32::new(1);
lazy_static! {
static ref SIGNER_REGISTRY: Mutex<HashMap<i32, Arc<dyn Signer>>> = Mutex::new(HashMap::new());
}

pub fn add_signer_to_registry(signer: Arc<dyn Signer>) -> i32 {
let signer_id = SIGNER_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
SIGNER_REGISTRY.lock().unwrap().insert(signer_id, signer);
signer_id
}

#[repr(C)]
pub struct CSigner {
pub signer_id: i32,
pub sign: extern "C" fn(
signer_id: i32,
payload: *const u8,
payload_len: usize,
out_len: *mut usize,
) -> *mut u8,
}

#[no_mangle]
pub extern "C" fn alloc_csigner() -> *mut CSigner {
let signer = CSigner {
signer_id: 0,
sign: rust_signer_sign,
};
Box::into_raw(Box::new(signer))
}

#[no_mangle]
pub extern "C" fn free_csigner(signer: *mut CSigner) {
if !signer.is_null() {
unsafe {
let _ = Box::from_raw(signer);
}
}
}

pub extern "C" fn rust_signer_sign(
signer_id: i32,
payload: *const u8,
payload_len: usize,
out_len: *mut usize,
) -> *mut u8 {
let payload_slice = unsafe { std::slice::from_raw_parts(payload, payload_len) };

let registry = SIGNER_REGISTRY.lock().unwrap();
if let Some(signer) = registry.get(&signer_id) {
if let Ok(signature) = signer.sign(payload_slice) {
let signature_len = signature.len();
let signature_boxed = signature.into_boxed_slice();
unsafe { *out_len = signature_len };
return Box::into_raw(signature_boxed) as *mut u8;
}
}
ptr::null_mut()
}

#[no_mangle]
pub extern "C" fn call_sign(
signer: *const CSigner,
payload: *const u8,
payload_len: usize,
out_len: *mut usize,
) -> *mut u8 {
unsafe { ((*signer).sign)((*signer).signer_id, payload, payload_len, out_len) }
}
17 changes: 17 additions & 0 deletions bindings/web5_c/src/crypto/dsa/poc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use super::{call_sign, CSigner};
use crate::c::free_bytes;

#[no_mangle]
pub extern "C" fn poc_signer_from_foreign(signer: *const CSigner) {
if signer.is_null() {
return;
}

let signer = unsafe { &*signer };
let payload = b"Test message";

let mut out_len: usize = 0;
let signature = call_sign(signer, payload.as_ptr(), payload.len(), &mut out_len);

free_bytes(signature);
}
95 changes: 95 additions & 0 deletions bindings/web5_c/src/crypto/jwk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::c::{opt_cstr_to_string, opt_string_to_cstr};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr;
use web5::crypto::jwk::Jwk;

#[repr(C)]
pub struct CJwk {
pub alg: *const c_char,
pub kty: *const c_char,
pub crv: *const c_char,
pub d: *const c_char,
pub x: *const c_char,
pub y: *const c_char,
}

#[no_mangle]
pub extern "C" fn alloc_cjwk() -> *mut CJwk {
let jwk = CJwk {
alg: ptr::null(),
kty: ptr::null(),
crv: ptr::null(),
d: ptr::null(),
x: ptr::null(),
y: ptr::null(),
};
Box::into_raw(Box::new(jwk))
}

#[no_mangle]
pub extern "C" fn free_cjwk(jwk: *mut CJwk) {
if !jwk.is_null() {
unsafe {
if !(*jwk).alg.is_null() {
let _ = CString::from_raw((*jwk).alg as *mut c_char);
}
if !(*jwk).kty.is_null() {
let _ = CString::from_raw((*jwk).kty as *mut c_char);
}
if !(*jwk).crv.is_null() {
let _ = CString::from_raw((*jwk).crv as *mut c_char);
}
if !(*jwk).d.is_null() {
let _ = CString::from_raw((*jwk).d as *mut c_char);
}
if !(*jwk).x.is_null() {
let _ = CString::from_raw((*jwk).x as *mut c_char);
}
if !(*jwk).y.is_null() {
let _ = CString::from_raw((*jwk).y as *mut c_char);
}
let _ = Box::from_raw(jwk);
}
}
}

impl From<&CJwk> for Jwk {
fn from(jwk_c: &CJwk) -> Self {
Jwk {
alg: opt_cstr_to_string(jwk_c.alg),
kty: unsafe { CStr::from_ptr(jwk_c.kty).to_string_lossy().into_owned() },
crv: unsafe { CStr::from_ptr(jwk_c.crv).to_string_lossy().into_owned() },
d: opt_cstr_to_string(jwk_c.d),
x: unsafe { CStr::from_ptr(jwk_c.x).to_string_lossy().into_owned() },
y: opt_cstr_to_string(jwk_c.y),
}
}
}

impl From<Jwk> for CJwk {
fn from(jwk: Jwk) -> Self {
CJwk {
alg: opt_string_to_cstr(jwk.alg),
kty: CString::new(jwk.kty).unwrap().into_raw(),
crv: CString::new(jwk.crv).unwrap().into_raw(),
d: opt_string_to_cstr(jwk.d),
x: CString::new(jwk.x).unwrap().into_raw(),
y: opt_string_to_cstr(jwk.y),
}
}
}

#[no_mangle]
pub extern "C" fn jwk_compute_thumbprint(jwk_ptr: *const CJwk) -> *mut c_char {
if jwk_ptr.is_null() {
return ptr::null_mut();
}

let jwk = unsafe { Jwk::from(&*jwk_ptr) };

match jwk.compute_thumbprint() {
Ok(thumbprint) => CString::new(thumbprint).unwrap().into_raw(),
Err(_) => ptr::null_mut(),
}
}
19 changes: 19 additions & 0 deletions bindings/web5_c/src/crypto/key_managers/in_memory_key_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::{
add_key_manager_to_registry, rust_key_manager_get_signer, rust_key_manager_import_private_jwk,
CKeyManager,
};
use std::sync::Arc;
use web5::crypto::key_managers::{in_memory_key_manager::InMemoryKeyManager, KeyManager};

#[no_mangle]
pub extern "C" fn new_in_memory_key_manager() -> *mut CKeyManager {
let manager: Arc<dyn KeyManager> = Arc::new(InMemoryKeyManager::new());

let manager_id = add_key_manager_to_registry(manager);

Box::into_raw(Box::new(CKeyManager {
manager_id,
import_private_jwk: rust_key_manager_import_private_jwk,
get_signer: rust_key_manager_get_signer,
}))
}
Loading

0 comments on commit 005bd3c

Please sign in to comment.