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:
Generated
+174
@@ -0,0 +1,174 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "craft-core"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"obfstr",
|
||||||
|
"sha2",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.186"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "obfstr"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["craft-core"]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = "symbols"
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "craft-core"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "CraftLabs 授权核心库 — Rust 实现,导出 craft_* C ABI。目标平台:Linux(主)> Windows(次)> macOS(最低)"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "staticlib"]
|
||||||
|
name = "craftlabs_auth_bitanswer"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
obfstr = "0.4"
|
||||||
|
sha2 = "0.10"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
windows-sys = { version = "0.52", features = ["Win32_System_Diagnostics_Debug"], optional = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
sha2 = "0.10"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["security-hardening"]
|
||||||
|
security-hardening = []
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let src_dir = PathBuf::from(&manifest_dir).join("src");
|
||||||
|
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hash_dir(&src_dir, &mut hasher);
|
||||||
|
let digest = hasher.finalize();
|
||||||
|
let hash_hex = format!("{:x}", digest);
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=BUILD_SRC_HASH={}", hash_hex);
|
||||||
|
|
||||||
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
fs::write(out_dir.join("build_hash.txt"), format!("{}\n", hash_hex)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_dir(dir: &PathBuf, hasher: &mut Sha256) {
|
||||||
|
if let Ok(entries) = fs::read_dir(dir) {
|
||||||
|
let mut paths: Vec<_> = entries.filter_map(|e| e.ok()).collect();
|
||||||
|
paths.sort_by_key(|e| e.file_name());
|
||||||
|
for entry in paths {
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
hash_dir(&path, hasher);
|
||||||
|
} else if path.extension().map_or(false, |ext| ext == "rs") {
|
||||||
|
if let Ok(content) = fs::read(&path) {
|
||||||
|
hasher.update(&content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
LIBRARY craftlabs_auth_bitanswer
|
||||||
|
EXPORTS
|
||||||
|
craft_initialize @1 NONAME
|
||||||
|
craft_activate @2 NONAME
|
||||||
|
craft_check_license @3 NONAME
|
||||||
|
craft_get_license_info @4 NONAME
|
||||||
|
craft_free_license_info @5 NONAME
|
||||||
|
craft_has_feature @6 NONAME
|
||||||
|
craft_release @7 NONAME
|
||||||
|
craft_heartbeat @8 NONAME
|
||||||
|
craft_destroy @9 NONAME
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
use std::ffi::CString;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
#[link(name = "craftlabs_auth_bitanswer")]
|
||||||
|
extern "C" {
|
||||||
|
fn craft_initialize(config_json: *const c_char) -> *mut std::ffi::c_void;
|
||||||
|
fn craft_activate(
|
||||||
|
handle: *mut std::ffi::c_void,
|
||||||
|
license_key: *const c_char,
|
||||||
|
config_json: *const c_char,
|
||||||
|
) -> CraftResult;
|
||||||
|
fn craft_check_license(handle: *mut std::ffi::c_void) -> CraftResult;
|
||||||
|
fn craft_get_license_info(handle: *mut std::ffi::c_void) -> *mut LicenseInfo;
|
||||||
|
fn craft_free_license_info(info: *mut LicenseInfo);
|
||||||
|
fn craft_has_feature(handle: *mut std::ffi::c_void, feature_name: *const c_char) -> i32;
|
||||||
|
fn craft_release(handle: *mut std::ffi::c_void) -> CraftResult;
|
||||||
|
fn craft_heartbeat(handle: *mut std::ffi::c_void) -> CraftResult;
|
||||||
|
fn craft_destroy(handle: *mut std::ffi::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct CraftResult {
|
||||||
|
success: i32,
|
||||||
|
message: *const c_char,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct LicenseInfo {
|
||||||
|
is_licensed: i32,
|
||||||
|
expiration_date: i64,
|
||||||
|
feature_names: *const *const c_char,
|
||||||
|
feature_values: *const i32,
|
||||||
|
feature_count: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_initialize_and_destroy() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_initialize_with_null_config() {
|
||||||
|
let handle = unsafe { craft_initialize(std::ptr::null()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_activate() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let key = CString::new("test-license-key").unwrap();
|
||||||
|
let cfg = CString::new("{}").unwrap();
|
||||||
|
let result = unsafe { craft_activate(handle, key.as_ptr(), cfg.as_ptr()) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_activate_null_handle() {
|
||||||
|
let key = CString::new("test-key").unwrap();
|
||||||
|
let cfg = CString::new("{}").unwrap();
|
||||||
|
let result = unsafe { craft_activate(std::ptr::null_mut(), key.as_ptr(), cfg.as_ptr()) };
|
||||||
|
assert_eq!(result.success, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_license() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let result = unsafe { craft_check_license(handle) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_license_null_handle() {
|
||||||
|
let result = unsafe { craft_check_license(std::ptr::null_mut()) };
|
||||||
|
assert_eq!(result.success, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_license_info() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let info = unsafe { craft_get_license_info(handle) };
|
||||||
|
assert!(!info.is_null());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!((*info).is_licensed, 1);
|
||||||
|
craft_free_license_info(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_license_info_null_handle() {
|
||||||
|
let info = unsafe { craft_get_license_info(std::ptr::null_mut()) };
|
||||||
|
assert!(info.is_null());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_has_feature() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let feature = CString::new("premium").unwrap();
|
||||||
|
let result = unsafe { craft_has_feature(handle, feature.as_ptr()) };
|
||||||
|
assert_eq!(result, 1);
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_has_feature_null_handle() {
|
||||||
|
let feature = CString::new("premium").unwrap();
|
||||||
|
let result = unsafe { craft_has_feature(std::ptr::null_mut(), feature.as_ptr()) };
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_release() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let result = unsafe { craft_release(handle) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_release_null_handle() {
|
||||||
|
let result = unsafe { craft_release(std::ptr::null_mut()) };
|
||||||
|
assert_eq!(result.success, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_heartbeat() {
|
||||||
|
let config = CString::new("{}").unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let result = unsafe { craft_heartbeat(handle) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_heartbeat_null_handle() {
|
||||||
|
let result = unsafe { craft_heartbeat(std::ptr::null_mut()) };
|
||||||
|
assert_eq!(result.success, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_full_lifecycle() {
|
||||||
|
let config = CString::new(r#"{"provider":"bitanswer"}"#).unwrap();
|
||||||
|
let handle = unsafe { craft_initialize(config.as_ptr()) };
|
||||||
|
assert!(!handle.is_null());
|
||||||
|
|
||||||
|
let key = CString::new("TEST-LICENSE-KEY").unwrap();
|
||||||
|
let cfg = CString::new("{}").unwrap();
|
||||||
|
let result = unsafe { craft_activate(handle, key.as_ptr(), cfg.as_ptr()) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
let result = unsafe { craft_check_license(handle) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
let info = unsafe { craft_get_license_info(handle) };
|
||||||
|
assert!(!info.is_null());
|
||||||
|
unsafe {
|
||||||
|
assert_eq!((*info).is_licensed, 1);
|
||||||
|
craft_free_license_info(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
let feature = CString::new("advanced").unwrap();
|
||||||
|
let has_feature = unsafe { craft_has_feature(handle, feature.as_ptr()) };
|
||||||
|
assert_eq!(has_feature, 1);
|
||||||
|
|
||||||
|
let result = unsafe { craft_heartbeat(handle) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
let result = unsafe { craft_release(handle) };
|
||||||
|
assert_eq!(result.success, 1);
|
||||||
|
|
||||||
|
unsafe { craft_destroy(handle) };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user