feat(rust): add Provider trait + refactor C ABI to route through Provider

SelfHostedProvider implements Provider trait for offline license verification
This commit is contained in:
2026-05-18 22:17:05 +08:00
parent 8b90a71077
commit fbce298f2b
3 changed files with 163 additions and 70 deletions
+71 -70
View File
@@ -1,23 +1,27 @@
// CraftLabs 授权核心库 — Rust 实现
// 导出 C ABI 接口,与 native/include/craftlabs_auth.h 一致
// 对齐 docs/平台架构思路.md §3.1
use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;
mod activate;
mod heartbeat;
mod license;
mod security;
mod trait_provider;
mod error;
mod security;
mod session;
pub mod crypto;
pub mod device;
pub mod provider_selfhosted;
use trait_provider::{Provider, ActivateResponse, HeartbeatResponse, LicenseStatus};
use provider_selfhosted::SelfHostedProvider;
pub struct CraftContext {
dummy: i32,
pub provider: Option<Box<dyn Provider>>,
pub initialized: bool,
}
impl CraftContext {
pub fn new() -> Self {
CraftContext { provider: None, initialized: false }
}
}
#[repr(C)]
@@ -36,41 +40,43 @@ pub struct LicenseInfo {
}
unsafe fn c_str_to_string(ptr: *const c_char) -> String {
if ptr.is_null() {
String::new()
} else {
CStr::from_ptr(ptr).to_string_lossy().into_owned()
}
if ptr.is_null() { String::new() } else { CStr::from_ptr(ptr).to_string_lossy().into_owned() }
}
static OK_MSG: &[u8] = b"ok\0";
static FAIL_MSG: &[u8] = b"failure\0";
fn ok_result() -> CraftResult {
CraftResult {
success: 1,
message: OK_MSG.as_ptr() as *const c_char,
}
fn ok_result() -> CraftResult { CraftResult { success: 1, message: OK_MSG.as_ptr() as *const c_char } }
fn fail_result() -> CraftResult {
static FAIL: &[u8] = b"failure\0";
CraftResult { success: 0, message: FAIL.as_ptr() as *const c_char }
}
fn fail_result() -> CraftResult {
CraftResult {
success: 0,
message: FAIL_MSG.as_ptr() as *const c_char,
}
fn parse_provider(config: &str) -> String {
serde_json::from_str::<serde_json::Value>(config)
.ok()
.and_then(|v| v.get("provider").and_then(|p| p.as_str().map(|s| s.to_string())))
.unwrap_or_else(|| "selfhosted".to_string())
}
#[no_mangle]
pub extern "C" fn craft_initialize(config_json: *const c_char) -> *mut CraftContext {
let _config = unsafe { c_str_to_string(config_json) };
let config_str = unsafe { c_str_to_string(config_json) };
#[cfg(feature = "security-hardening")]
{
security::anti_debug::anti_debug_check();
let _ = security::integrity::integrity_check();
let mut ctx = Box::new(CraftContext::new());
let mut provider: Box<dyn Provider> = match parse_provider(&config_str).as_str() {
"selfhosted" => Box::new(SelfHostedProvider::new()),
_ => Box::new(SelfHostedProvider::new()),
};
match provider.initialize(&ctx, &config_str) {
Ok(()) => {
ctx.provider = Some(provider);
ctx.initialized = true;
}
Err(_) => {
ctx.initialized = false;
}
}
let ctx = Box::new(CraftContext::new());
Box::into_raw(ctx)
}
@@ -78,77 +84,72 @@ pub extern "C" fn craft_initialize(config_json: *const c_char) -> *mut CraftCont
pub extern "C" fn craft_activate(
handle: *mut CraftContext,
license_key: *const c_char,
config_json: *const c_char,
_config_json: *const c_char,
) -> CraftResult {
if handle.is_null() {
return fail_result();
}
if handle.is_null() { return fail_result(); }
let ctx = unsafe { &*handle };
let key = unsafe { c_str_to_string(license_key) };
let config = unsafe { c_str_to_string(config_json) };
activate::core_activate(&key, &config)
ctx.provider.as_ref()
.and_then(|p| p.activate(ctx, &key).ok())
.map_or_else(fail_result, |_| ok_result())
}
#[no_mangle]
pub extern "C" fn craft_check_license(handle: *mut CraftContext) -> CraftResult {
if handle.is_null() {
return fail_result();
}
license::check_license(unsafe { &*handle })
if handle.is_null() { return fail_result(); }
let ctx = unsafe { &*handle };
ctx.provider.as_ref()
.and_then(|p| p.check_license(ctx).ok())
.map_or_else(fail_result, |s| if s.licensed { ok_result() } else { fail_result() })
}
#[no_mangle]
pub extern "C" fn craft_get_license_info(handle: *mut CraftContext) -> *mut LicenseInfo {
if handle.is_null() {
return ptr::null_mut();
}
license::get_license_info(unsafe { &*handle })
if handle.is_null() { return ptr::null_mut(); }
let ctx = unsafe { &*handle };
ctx.provider.as_ref()
.map(|p| p.get_license_info(ctx))
.map_or(ptr::null_mut(), |info| Box::into_raw(Box::new(info)))
}
#[no_mangle]
pub extern "C" fn craft_free_license_info(info: *mut LicenseInfo) {
if !info.is_null() {
unsafe {
drop(Box::from_raw(info));
}
}
if !info.is_null() { unsafe { drop(Box::from_raw(info)); } }
}
#[no_mangle]
pub extern "C" fn craft_has_feature(
handle: *mut CraftContext,
feature_name: *const c_char,
) -> i32 {
if handle.is_null() {
return 0;
}
pub extern "C" fn craft_has_feature(handle: *mut CraftContext, feature_name: *const c_char) -> i32 {
if handle.is_null() { return 0; }
let ctx = unsafe { &*handle };
let name = unsafe { c_str_to_string(feature_name) };
if license::has_feature(unsafe { &*handle }, &name) {
1
} else {
0
}
ctx.provider.as_ref().map_or(0, |p| if p.has_feature(ctx, &name) { 1 } else { 0 })
}
#[no_mangle]
pub extern "C" fn craft_release(handle: *mut CraftContext) -> CraftResult {
if handle.is_null() {
return fail_result();
if handle.is_null() { return fail_result(); }
let ctx = unsafe { &mut *handle };
if let Some(ref mut p) = ctx.provider {
let _ = p.release();
}
license::release_license(unsafe { &mut *handle })
ok_result()
}
#[no_mangle]
pub extern "C" fn craft_heartbeat(handle: *mut CraftContext) -> CraftResult {
if handle.is_null() {
return fail_result();
}
heartbeat::do_heartbeat(unsafe { &*handle })
if handle.is_null() { return fail_result(); }
let ctx = unsafe { &*handle };
ctx.provider.as_ref()
.and_then(|p| p.heartbeat(ctx).ok())
.map_or_else(fail_result, |_| ok_result())
}
#[no_mangle]
pub extern "C" fn craft_destroy(handle: *mut CraftContext) {
if !handle.is_null() {
unsafe {
let ctx = &mut *handle;
if let Some(ref mut p) = ctx.provider { p.close(); }
drop(Box::from_raw(handle));
}
}