1use std::fs::File;
55use std::io::Write;
56
57use base64::prelude::*;
58use serde::{Deserialize, Serialize};
59
60#[derive(Serialize, Deserialize, Debug)]
62#[allow(non_snake_case)]
63struct RSAKeyValue {
64 Modulus: String,
65 Exponent: String,
66}
67
68#[derive(Serialize, Deserialize, Debug)]
70#[allow(non_snake_case)]
71pub struct Customer {
72 pub Id: u64,
73 pub Name: String,
74 pub Email: String,
75 pub CompanyName: String,
76 pub Created: u64,
77}
78
79#[derive(Serialize, Deserialize, Debug)]
81#[allow(non_snake_case)]
82pub struct ActivationData {
83 pub Mid: String,
84 pub IP: String,
85 pub Time: u64,
86}
87
88#[derive(Serialize, Deserialize, Debug)]
90#[allow(non_snake_case)]
91pub struct DataObject {
92 pub Id: u64,
93 pub Name: String,
94 pub StringValue: String,
95 pub IntValue: u64,
96}
97
98#[derive(Debug)]
100#[allow(non_snake_case)]
101pub struct KeyActivateArguments {
102 pub ProductId: u64,
103 pub Key: String,
104 pub MachineCode: String,
105 pub FriendlyName: String,
106 pub FieldsToReturn: u8,
107 pub SignMethod: u8,
108 pub FloatingTimeInterval: u64,
109 pub MaxOverdraft: u64,
110 pub Metadata: bool,
111 pub OSInfo: String,
112 pub ModelVersion: u8,
113 pub v: u8,
114}
115
116impl Default for KeyActivateArguments {
117 fn default() -> Self {
118 KeyActivateArguments {
119 ProductId: 0,
120 Key: "".to_string(),
121 MachineCode: "".to_string(),
122 FriendlyName: "".to_string(),
123 FieldsToReturn: 0,
124 SignMethod: 1,
125 FloatingTimeInterval: 0,
126 MaxOverdraft: 0,
127 Metadata: false,
128 OSInfo: "".to_string(),
129 ModelVersion: 1,
130 v: 1,
131 }
132 }
133}
134
135#[derive(Serialize, Deserialize, Debug)]
136#[allow(non_snake_case)]
137struct SerdeLicenseKey {
138 ProductId: u64,
139 Id: Option<u64>,
140 Key: Option<String>,
141 Created: u64,
142 Expires: u64,
143 Period: u64,
144 F1: bool,
145 F2: bool,
146 F3: bool,
147 F4: bool,
148 F5: bool,
149 F6: bool,
150 F7: bool,
151 F8: bool,
152 Notes: Option<String>,
153 Block: bool,
154 GlobalId: Option<u64>,
155 Customer: Option<Customer>,
156 ActivatedMachines: Vec<ActivationData>,
157 TrialActivation: bool,
158 MaxNoOfMachines: Option<u64>,
159 AllowedMachines: Option<String>,
160 DataObjects: Vec<DataObject>,
161 SignDate: u64,
162}
163
164#[derive(Serialize, Deserialize, Debug)]
166#[allow(non_snake_case)]
167pub struct LicenseKey {
168 pub ProductId: u64,
169 pub Id: Option<u64>,
170 pub Key: Option<String>,
171 pub Created: u64,
172 pub Expires: u64,
173 pub Period: u64,
174 pub F1: bool,
175 pub F2: bool,
176 pub F3: bool,
177 pub F4: bool,
178 pub F5: bool,
179 pub F6: bool,
180 pub F7: bool,
181 pub F8: bool,
182 pub Notes: Option<String>,
183 pub Block: bool,
184 pub GlobalId: Option<u64>,
185 pub Customer: Option<Customer>,
186 pub ActivatedMachines: Vec<ActivationData>,
187 pub TrialActivation: bool,
188 pub MaxNoOfMachines: Option<u64>,
189 pub AllowedMachines: Vec<String>,
190 pub DataObjects: Vec<DataObject>,
191 pub SignDate: u64,
192
193 license_key_bytes: Vec<u8>,
194 signature_bytes: Vec<u8>,
195}
196
197#[derive(Serialize, Deserialize, Debug)]
199#[allow(non_snake_case)]
200pub struct ActivateResponse {
201 result: i8,
202 message: String,
203 licenseKey: String,
204 signature: Option<String>,
205}
206
207impl LicenseKey {
208 pub fn from_response_str(s: &str) -> anyhow::Result<LicenseKey> {
216 let activate_response: ActivateResponse = serde_json::from_str(s)?;
217
218 let license_key = activate_response.licenseKey;
219 let signature = activate_response.signature;
220
221 let license_key_bytes = BASE64_STANDARD.decode(license_key)?;
222 let signature_bytes = BASE64_STANDARD.decode(signature.unwrap())?;
223
224 let license_key_string = String::from_utf8(license_key_bytes.clone())?;
225 let serde_lk: SerdeLicenseKey = serde_json::from_str(&license_key_string)?;
226
227 Ok(LicenseKey {
228 ProductId: serde_lk.ProductId,
229 Id: serde_lk.Id,
230 Key: serde_lk.Key,
231 Created: serde_lk.Created,
232 Expires: serde_lk.Expires,
233 Period: serde_lk.Period,
234 F1: serde_lk.F1,
235 F2: serde_lk.F2,
236 F3: serde_lk.F3,
237 F4: serde_lk.F4,
238 F5: serde_lk.F5,
239 F6: serde_lk.F6,
240 F7: serde_lk.F7,
241 F8: serde_lk.F8,
242 Notes: serde_lk.Notes,
243 Block: serde_lk.Block,
244 GlobalId: serde_lk.GlobalId,
245 Customer: serde_lk.Customer,
246 ActivatedMachines: serde_lk.ActivatedMachines,
247 TrialActivation: serde_lk.TrialActivation,
248 MaxNoOfMachines: serde_lk.MaxNoOfMachines,
249 AllowedMachines: serde_lk
250 .AllowedMachines
251 .map(|s| s.split('\n').map(|x| x.to_string()).collect())
252 .unwrap_or_default(),
253 DataObjects: serde_lk.DataObjects,
254 SignDate: serde_lk.SignDate,
255
256 license_key_bytes,
257 signature_bytes,
258 })
259 }
260
261 pub fn has_valid_signature(&self, public_key: &str) -> anyhow::Result<bool> {
272 let public_key: RSAKeyValue = serde_xml_rs::from_str(public_key)?;
273
274 let modulus_bytes = BASE64_STANDARD.decode(&public_key.Modulus)?;
275 let exponent_bytes = BASE64_STANDARD.decode(&public_key.Exponent)?;
276
277 let modulus = openssl::bn::BigNum::from_slice(&modulus_bytes)?;
278 let exponent = openssl::bn::BigNum::from_slice(&exponent_bytes)?;
279
280 let keypair = openssl::rsa::Rsa::from_public_components(modulus, exponent)?;
281 let keypair = openssl::pkey::PKey::from_rsa(keypair)?;
282
283 let mut verifier = openssl::sign::Verifier::new(openssl::hash::MessageDigest::sha256(), &keypair)?;
284
285 verifier.update(&self.license_key_bytes)?;
286 let valid = verifier.verify(&self.signature_bytes)?;
287 Ok(valid)
288 }
289}
290
291pub fn key_activate(token: &str, args: KeyActivateArguments) -> anyhow::Result<LicenseKey> {
300 let client = reqwest::blocking::Client::new();
302 let params = [
303 ("token", token),
304 ("ProductId", &args.ProductId.to_string()),
305 ("Key", &args.Key),
306 ("MachineCode", &args.MachineCode),
307 ("FriendlyName", &args.FriendlyName),
308 ("FieldsToReturn", &args.FieldsToReturn.to_string()),
309 ("SignMethod", &args.SignMethod.to_string()),
310 ("FloatingTimeInterval", &args.FloatingTimeInterval.to_string()),
311 ("MaxOverdraft", &args.MaxOverdraft.to_string()),
312 ("Metadata", &args.Metadata.to_string()),
313 ("OSInfo", &args.OSInfo),
314 ("ModelVersion", &args.ModelVersion.to_string()),
315 ("v", &args.v.to_string()),
316 ("Sign", "true"),
317 ];
318
319 let res = client
320 .post("https://app.cryptolens.io/api/key/Activate")
321 .form(¶ms)
322 .send()?;
323 let s = res.text()?;
324
325 let response: serde_json::Value = serde_json::from_str(&s)?;
327 if response["result"] != 0 {
328 return Err(anyhow::anyhow!(
329 "Error Info: result: {}, message: {}",
330 response["result"],
331 response["message"]
332 ));
333 }
334
335 LicenseKey::from_response_str(&s)
337}
338
339pub fn save_license_key_to_file(license_key: &LicenseKey, path: &str) -> anyhow::Result<()> {
348 let mut file = File::create(path)?;
349 let activate_response = ActivateResponse {
350 licenseKey: BASE64_STANDARD.encode(&license_key.license_key_bytes),
351 signature: Some(BASE64_STANDARD.encode(&license_key.signature_bytes)),
352 result: 0,
353 message: "".to_string(),
354 };
355 let s = serde_json::to_string(&activate_response)?;
356 file.write_all(s.as_bytes())?;
357 Ok(())
358}
359
360pub fn load_license_key_from_file(path: &str) -> anyhow::Result<LicenseKey> {
371 let activate_response_str = std::fs::read_to_string(path)?;
372 let license_key = LicenseKey::from_response_str(&activate_response_str)?;
373
374 Ok(license_key)
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_key_activate() {
383 let public_key = "<RSAKeyValue><Modulus>khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
385
386 let product_id = "3646";
387 let key = "MPDWY-PQAOW-FKSCH-SGAAU";
388 let token = "WyI0NjUiLCJBWTBGTlQwZm9WV0FyVnZzMEV1Mm9LOHJmRDZ1SjF0Vk52WTU0VzB2Il0=";
389
390 let license_key = key_activate(
391 token,
392 KeyActivateArguments {
393 ProductId: product_id.parse().unwrap(),
394 Key: key.to_string(),
395 MachineCode: "289jf2afs3".to_string(),
396 ..Default::default()
397 },
398 )
399 .unwrap();
400
401 match license_key.has_valid_signature(public_key) {
402 Ok(valid) => assert!(valid, "Signature should be valid"),
403 Err(e) => panic!("Error: {}", e),
404 }
405 }
406
407 #[test]
408 fn test_save_license_key_to_file() {
409 let license_key = LicenseKey {
410 ProductId: 3646,
411 Id: Some(1),
412 Key: Some("XXXXX-XXXXX-XXXXX-XXXXX".to_string()),
413 Created: 1614556800,
414 Expires: 1614556800,
415 Period: 0,
416 F1: false,
417 F2: false,
418 F3: false,
419 F4: false,
420 F5: false,
421 F6: false,
422 F7: false,
423 F8: false,
424 Notes: None,
425 Block: false,
426 GlobalId: None,
427 Customer: None,
428 ActivatedMachines: vec![],
429 TrialActivation: false,
430 MaxNoOfMachines: None,
431 AllowedMachines: vec![],
432 DataObjects: vec![],
433 SignDate: 1614556800,
434 license_key_bytes: vec![1, 2, 3, 4, 5],
435 signature_bytes: vec![1, 2, 3, 4, 5],
436 };
437
438 let path = "test_license_key";
439 save_license_key_to_file(&license_key, path).unwrap();
440 assert!(std::path::Path::new(path).exists());
441
442 std::fs::remove_file(path).unwrap();
444 }
445
446 #[test]
447 fn test_load_license_key_from_file() {
448 let path = "fixtures/test_license_key";
449 let public_key = "<RSAKeyValue><Modulus>khbyu3/vAEBHi339fTuo2nUaQgSTBj0jvpt5xnLTTF35FLkGI+5Z3wiKfnvQiCLf+5s4r8JB/Uic/i6/iNjPMILlFeE0N6XZ+2pkgwRkfMOcx6eoewypTPUoPpzuAINJxJRpHym3V6ZJZ1UfYvzRcQBD/lBeAYrvhpCwukQMkGushKsOS6U+d+2C9ZNeP+U+uwuv/xu8YBCBAgGb8YdNojcGzM4SbCtwvJ0fuOfmCWZvUoiumfE4x7rAhp1pa9OEbUe0a5HL+1v7+JLBgkNZ7Z2biiHaM6za7GjHCXU8rojatEQER+MpgDuQV3ZPx8RKRdiJgPnz9ApBHFYDHLDzDw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
450
451 let license_key = load_license_key_from_file(path).unwrap();
452 assert!(license_key.ProductId == 3646);
453 assert!(license_key.Key == Some("MPDWY-PQAOW-FKSCH-SGAAU".to_string()));
454
455 assert!(license_key.has_valid_signature(public_key).unwrap());
456 }
457}