定价统一:专业版包年 ¥99 / 永久 ¥199(与 OpsXShell 一致);授权真实链路验证

- /pricing 与落地页:¥299 终身→¥199 永久、¥69→¥99、个人版更名专业版(3/5 台设备);
  补 Apipost 离线版式购买叙事(一机一码/当场发证/无需注册/年 2 次免费换绑)
- 授权服务器(licence.rlwj.cn):opsxapi 永久计划 299→199;注册 opsxdb 产品与
  trial/yearly/perpetual 计划——三产品公开 API 均为 0/99/199
- 真实链路验证:管理端签发 opsxapi 在线 LIC-(activate/verify 全通,Ed25519 签名下发)+
  离线 LICOFF-(绑本机 device_id);新增 4 项 Rust 单测用真实 fixture 跑通
  (离线验签/换设备拒绝/篡改拒绝/在线响应签名)
This commit is contained in:
logan committed 2026-10-16 03:36:43 +08:00
1 parent c304b9f251
commit 0f8e1f75da
7 files changed
+57 -1

No files matched your search

+51
View File
@@ -432,3 +432,54 @@ pub fn deactivate(app: &AppHandle) -> Result<LicenseStatus, String> {
let _ = fs::remove_file(&path);
read_status(app)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
/// 真实服务器签发的离线证(licence.rlwj.cn,product=opsxapi)
const REAL_OFFLINE: &str = include_str!("../../../tests/fixtures/licoff-opsxapi.txt");
const REAL_DEVICE: &str = include_str!("../../../tests/fixtures/device-id.txt");
#[test]
fn offline_serial_validates_for_bound_device() {
let key = REAL_OFFLINE.trim();
assert!(is_offline_license_key(key));
let claims = verify_offline_serial(key, REAL_DEVICE.trim())
.expect("真实离线证应通过本产品+设备校验");
assert_eq!(claims.product_code, "opsxapi");
assert!(claims.exp > claims.iat);
}
#[test]
fn offline_serial_rejects_other_device() {
assert!(verify_offline_serial(REAL_OFFLINE.trim(), "00000000-0000-0000-0000-000000000000").is_err());
}
#[test]
fn offline_serial_rejects_tampered_payload() {
// 篡改 base64 载荷中段一个字符(保持格式合法,签名必失效)
let key = REAL_OFFLINE.trim().to_string();
let (payload, sig) = key.strip_prefix("LICOFF-1.").unwrap().rsplit_once('.').unwrap();
let mut chars: Vec<char> = payload.chars().collect();
let mid = chars.len() / 2;
chars[mid] = if chars[mid] == 'A' { 'B' } else { 'A' };
let tampered = format!("LICOFF-1.{}.{}", chars.into_iter().collect::<String>(), sig);
assert_ne!(key, tampered);
assert!(verify_offline_serial(&tampered, REAL_DEVICE.trim()).is_err());
}
/// 真实服务器 /v1/license/verify 响应的 Ed25519 签名(hex)与签名原文
#[test]
fn online_response_sig_verifies_with_embedded_key() {
// 1) 原文直接验(证明公钥同源)
let msg = include_str!("../../../tests/fixtures/verify-sig-msg.txt");
let sig_hex = include_str!("../../../tests/fixtures/verify-sig.txt").trim();
let sig = Signature::from_slice(&hex::decode(sig_hex).unwrap()).unwrap();
offline_verifying_key().unwrap().verify(msg.as_bytes(), &sig).expect("服务器签名应通过内嵌公钥校验");
// 2) 完整响应 JSON 走 verify_response_sig(证明客户端重组串与服务器一致)
let payload: Value = serde_json::from_str(include_str!("../../../tests/fixtures/verify-response.json")).unwrap();
assert!(verify_response_sig(&payload).is_ok());
}
}