mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
feat(rust): split core library into activate/license/heartbeat modules with build.rs and C ABI tests
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
// Activation logic — communicates with BitAnswer cloud or self-hosted backend
|
||||
|
||||
use crate::CraftResult;
|
||||
|
||||
/// Core activation logic — communicates with BitAnswer cloud or self-hosted backend
|
||||
pub fn core_activate(_license_key: &str, _config_json: &str) -> CraftResult {
|
||||
// TODO: actual network communication based on config provider
|
||||
crate::ok_result()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Heartbeat logic — periodic license validation
|
||||
|
||||
use crate::{CraftContext, CraftResult};
|
||||
|
||||
pub fn do_heartbeat(_ctx: &CraftContext) -> CraftResult {
|
||||
crate::ok_result()
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// 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;
|
||||
|
||||
pub struct CraftContext {
|
||||
dummy: i32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CraftResult {
|
||||
pub success: i32,
|
||||
pub message: *const c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LicenseInfo {
|
||||
pub is_licensed: i32,
|
||||
pub expiration_date: i64,
|
||||
pub feature_names: *const *const c_char,
|
||||
pub feature_values: *const i32,
|
||||
pub feature_count: i32,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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 fail_result() -> CraftResult {
|
||||
CraftResult {
|
||||
success: 0,
|
||||
message: FAIL_MSG.as_ptr() as *const c_char,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn craft_initialize(config_json: *const c_char) -> *mut CraftContext {
|
||||
let _config = unsafe { c_str_to_string(config_json) };
|
||||
|
||||
#[cfg(feature = "security-hardening")]
|
||||
{
|
||||
security::anti_debug::anti_debug_check();
|
||||
let _ = security::integrity::integrity_check();
|
||||
}
|
||||
|
||||
let ctx = Box::new(CraftContext::new());
|
||||
Box::into_raw(ctx)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn craft_activate(
|
||||
handle: *mut CraftContext,
|
||||
license_key: *const c_char,
|
||||
config_json: *const c_char,
|
||||
) -> CraftResult {
|
||||
if handle.is_null() {
|
||||
return fail_result();
|
||||
}
|
||||
let key = unsafe { c_str_to_string(license_key) };
|
||||
let config = unsafe { c_str_to_string(config_json) };
|
||||
activate::core_activate(&key, &config)
|
||||
}
|
||||
|
||||
#[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 })
|
||||
}
|
||||
|
||||
#[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 })
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn craft_free_license_info(info: *mut LicenseInfo) {
|
||||
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;
|
||||
}
|
||||
let name = unsafe { c_str_to_string(feature_name) };
|
||||
if license::has_feature(unsafe { &*handle }, &name) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn craft_release(handle: *mut CraftContext) -> CraftResult {
|
||||
if handle.is_null() {
|
||||
return fail_result();
|
||||
}
|
||||
license::release_license(unsafe { &mut *handle })
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn craft_heartbeat(handle: *mut CraftContext) -> CraftResult {
|
||||
if handle.is_null() {
|
||||
return fail_result();
|
||||
}
|
||||
heartbeat::do_heartbeat(unsafe { &*handle })
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn craft_destroy(handle: *mut CraftContext) {
|
||||
if !handle.is_null() {
|
||||
unsafe {
|
||||
drop(Box::from_raw(handle));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// License state management
|
||||
|
||||
use crate::{CraftContext, CraftResult, LicenseInfo};
|
||||
use std::ptr;
|
||||
|
||||
/// License state machine
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LicenseState {
|
||||
Uninitialized,
|
||||
Active,
|
||||
Expired,
|
||||
Released,
|
||||
}
|
||||
|
||||
impl CraftContext {
|
||||
pub fn new() -> Self {
|
||||
CraftContext { dummy: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_license(_ctx: &CraftContext) -> CraftResult {
|
||||
crate::ok_result()
|
||||
}
|
||||
|
||||
pub fn get_license_info(_ctx: &CraftContext) -> *mut LicenseInfo {
|
||||
let info = Box::new(LicenseInfo {
|
||||
is_licensed: 1,
|
||||
expiration_date: 0,
|
||||
feature_names: ptr::null(),
|
||||
feature_values: ptr::null(),
|
||||
feature_count: 0,
|
||||
});
|
||||
Box::into_raw(info)
|
||||
}
|
||||
|
||||
pub fn has_feature(_ctx: &CraftContext, _feature_name: &str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn release_license(_ctx: &mut CraftContext) -> CraftResult {
|
||||
crate::ok_result()
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn is_debugger_present() -> bool {
|
||||
if let Ok(status) = std::fs::read_to_string("/proc/self/status") {
|
||||
for line in status.lines() {
|
||||
if line.starts_with("TracerPid:") {
|
||||
if let Some(pid_str) = line.split_whitespace().nth(1) {
|
||||
if let Ok(pid) = pid_str.parse::<i32>() {
|
||||
return pid != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn is_debugger_present() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn is_debugger_present() -> bool {
|
||||
unsafe { windows_sys::Win32::System::Diagnostics::Debug::IsDebuggerPresent() != 0 }
|
||||
}
|
||||
|
||||
pub fn anti_debug_check() {
|
||||
if is_debugger_present() {
|
||||
std::process::abort();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const EXPECTED_HASH: &str = env!("BUILD_SRC_HASH");
|
||||
|
||||
pub fn verify_integrity() -> bool {
|
||||
let lib_path = match get_own_library_path() {
|
||||
Some(path) => path,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let runtime_hash = match compute_file_sha256(&lib_path) {
|
||||
Some(hash) => hash,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
tracing_mode_check(runtime_hash)
|
||||
}
|
||||
|
||||
fn get_own_library_path() -> Option<PathBuf> {
|
||||
std::env::current_exe().ok()
|
||||
}
|
||||
|
||||
fn compute_file_sha256(path: &PathBuf) -> Option<String> {
|
||||
let data = fs::read(path).ok()?;
|
||||
let hash = Sha256::digest(&data);
|
||||
Some(format!("{:x}", hash))
|
||||
}
|
||||
|
||||
fn tracing_mode_check(_runtime_hash: String) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn integrity_check() -> Result<(), &'static str> {
|
||||
if verify_integrity() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Integrity check failed")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod anti_debug;
|
||||
pub mod integrity;
|
||||
pub mod string_encrypt;
|
||||
@@ -0,0 +1,26 @@
|
||||
use obfstr::obfstr;
|
||||
|
||||
#[inline]
|
||||
pub fn error_activate_failed() -> String {
|
||||
obfstr!("Activation failed: invalid license key").to_string()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn error_handle_null() -> String {
|
||||
obfstr!("Error: null handle").to_string()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn error_network_timeout() -> String {
|
||||
obfstr!("Network timeout contacting license server").to_string()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn api_activate_path() -> String {
|
||||
obfstr!("/api/v1/activate").to_string()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn api_heartbeat_path() -> String {
|
||||
obfstr!("/api/v1/heartbeat").to_string()
|
||||
}
|
||||
Reference in New Issue
Block a user