Skip to content

Commit b163c89

Browse files
committed
Initial import
0 parents  commit b163c89

File tree

5 files changed

+307
-0
lines changed

5 files changed

+307
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Cargo.lock
2+
/target
3+
**/*.rs.bk

Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "cryptolens"
3+
description = "Rust Interface for Cryptolens Web API"
4+
license-file = "LICENSE.md"
5+
homepage = "https://github.com/cryptolens/cryptolens-rust"
6+
documentation = "https://help.cryptolens.io/"
7+
repository = "https://github.com/cryptolens/cryptolens-rust"
8+
version = "0.0.1"
9+
authors = ["Cryptolens AB"]
10+
edition = "2018"
11+
12+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13+
14+
[dependencies]
15+
base64 = "0.11"
16+
openssl = "0.10"
17+
reqwest = "0.9"
18+
serde = { version = "1.0", features = ["derive"] }
19+
serde_json = "1.0"
20+
serde-xml-rs = "0.3"

LICENSE.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
The Cryptolens Client API for Rust is licensed under the 2-Clause BSD License
2+
given below. The client API consists of the files in the `src/`directory.
3+
4+
```
5+
Copyright (c) 2019, Cryptolens AB
6+
All rights reserved.
7+
8+
Redistribution and use in source and binary forms, with or without
9+
modification, are permitted provided that the following conditions are met:
10+
11+
* Redistributions of source code must retain the above copyright notice, this
12+
list of conditions and the following disclaimer.
13+
14+
* Redistributions in binary form must reproduce the above copyright notice,
15+
this list of conditions and the following disclaimer in the documentation
16+
and/or other materials provided with the distribution.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
```

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Cryptolens Client API for Rust
2+
3+
> This library is currently in a beta release.
4+
5+
This library simplifies access to the [Cryptolens Web API](https://cryptolens.io) from the
6+
Rust programming language.
7+
8+
Several examples are available in the `src/bin` directory. The following commands clone the
9+
repository and runs `example_activate`:
10+
11+
```
12+
$ git clone https://github.com/Cryptolens/cryptolens-rust.git
13+
$ cd cryptolens-rust/
14+
$ cargo run --bin example_activate
15+
```
16+
17+
As long as the library is at version `0.0.X` we are not following semantic versioning. Before
18+
moving to version `0.1.0` at least the following needs to be implemented:
19+
20+
* [ ] Parse server message when an activation fails, and return an appropriate error.
21+
* [ ] Add proper management of errors in third-party libraries.
22+
* [ ] Decide on how to deal with time, should we depend on e.g. the `chrono` crate?
23+
Or should we just expose the time as an integer and let the user deal with this
24+
as we do now?
25+
* [ ] Possibly change capitalization of names to make them more rust-like

src/lib.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
use std::result::Result;
2+
3+
use serde::{Deserialize, Serialize};
4+
//use serde_json::Result;
5+
6+
#[derive(Serialize, Deserialize)]
7+
#[allow(non_snake_case)]
8+
pub struct ActivateResponse {
9+
result: i64,
10+
message: Option<String>,
11+
licenseKey: Option<String>,
12+
signature: Option<String>,
13+
}
14+
15+
#[derive(Serialize, Deserialize)]
16+
#[allow(non_snake_case)]
17+
struct SerdeLicenseKey {
18+
ProductId: u64,
19+
Id: Option<u64>,
20+
Key: Option<String>,
21+
Created: u64,
22+
Expires: u64,
23+
Period: u64,
24+
F1: bool,
25+
F2: bool,
26+
F3: bool,
27+
F4: bool,
28+
F5: bool,
29+
F6: bool,
30+
F7: bool,
31+
F8: bool,
32+
Notes: Option<String>,
33+
Block: bool,
34+
GlobalId: Option<u64>,
35+
Customer: Option<Customer>,
36+
ActivatedMachines: Vec<ActivationData>,
37+
TrialActivation: bool,
38+
MaxNoOfMachines: Option<u64>,
39+
AllowedMachines: String,
40+
DataObjects: Vec<DataObject>,
41+
SignDate: u64,
42+
}
43+
44+
#[allow(non_snake_case)]
45+
pub struct LicenseKey {
46+
pub ProductId: u64,
47+
pub Id: Option<u64>,
48+
pub Key: Option<String>,
49+
pub Created: u64,
50+
pub Expires: u64,
51+
pub Period: u64,
52+
pub F1: bool,
53+
pub F2: bool,
54+
pub F3: bool,
55+
pub F4: bool,
56+
pub F5: bool,
57+
pub F6: bool,
58+
pub F7: bool,
59+
pub F8: bool,
60+
pub Notes: Option<String>,
61+
pub Block: bool,
62+
pub GlobalId: Option<u64>,
63+
pub Customer: Option<Customer>,
64+
pub ActivatedMachines: Vec<ActivationData>,
65+
pub TrialActivation: bool,
66+
pub MaxNoOfMachines: Option<u64>,
67+
pub AllowedMachines: Vec<String>,
68+
pub DataObjects: Vec<DataObject>,
69+
pub SignDate: u64,
70+
71+
license_key_bytes: Vec<u8>,
72+
signature_bytes: Vec<u8>,
73+
}
74+
75+
#[derive(Serialize, Deserialize)]
76+
#[allow(non_snake_case)]
77+
pub struct Customer {
78+
pub Id: u64,
79+
pub Name: String,
80+
pub Email: String,
81+
pub CompanyName: String,
82+
pub Created: u64,
83+
}
84+
85+
#[derive(Serialize, Deserialize)]
86+
#[allow(non_snake_case)]
87+
pub struct ActivationData {
88+
pub Mid: String,
89+
pub IP: String,
90+
pub Time: u64,
91+
}
92+
93+
#[derive(Serialize, Deserialize)]
94+
#[allow(non_snake_case)]
95+
pub struct DataObject {
96+
pub Id: u64,
97+
pub Name: String,
98+
pub StringValue: String,
99+
pub IntValue: u64,
100+
}
101+
102+
#[allow(non_snake_case)]
103+
pub struct KeyActivateArguments<'a, 'b> {
104+
pub ProductId: u64,
105+
pub Key: &'a str,
106+
pub MachineCode: &'b str,
107+
pub FieldsToReturn: u64,
108+
pub FloatingTimeInterval: u64,
109+
pub MaxOverdraft: u64,
110+
}
111+
112+
impl Default for KeyActivateArguments<'static, 'static> {
113+
fn default() -> Self {
114+
KeyActivateArguments {
115+
ProductId: 0,
116+
Key: "",
117+
MachineCode: "",
118+
FieldsToReturn: 0,
119+
FloatingTimeInterval: 0,
120+
MaxOverdraft: 0,
121+
}
122+
}
123+
}
124+
125+
impl LicenseKey {
126+
pub fn from_str(s: &str) -> Result<LicenseKey, ()> {
127+
let activate_response: ActivateResponse = serde_json::from_str(&s).map_err(|_| ())?;
128+
129+
// TODO: Check result and parse message in case there is an error
130+
131+
let license_key: &str = activate_response.licenseKey.as_ref().ok_or(())?;
132+
let signature: &str = activate_response.signature.as_ref().ok_or(())?;
133+
134+
let license_key_bytes = base64::decode(license_key).map_err(|_| ())?;
135+
let license_key_string = String::from_utf8(license_key_bytes.clone()).map_err(|_| ())?;
136+
137+
let signature_bytes = base64::decode(signature).map_err(|_| ())?;
138+
139+
let serde_lk: SerdeLicenseKey = serde_json::from_str(&license_key_string).map_err(|_| ())?;
140+
141+
Ok(LicenseKey {
142+
ProductId: serde_lk.ProductId,
143+
Id: serde_lk.Id,
144+
Key: serde_lk.Key,
145+
Created: serde_lk.Created,
146+
Expires: serde_lk.Expires,
147+
Period: serde_lk.Period,
148+
F1: serde_lk.F1,
149+
F2: serde_lk.F2,
150+
F3: serde_lk.F3,
151+
F4: serde_lk.F4,
152+
F5: serde_lk.F5,
153+
F6: serde_lk.F6,
154+
F7: serde_lk.F7,
155+
F8: serde_lk.F8,
156+
Notes: serde_lk.Notes,
157+
Block: serde_lk.Block,
158+
GlobalId: serde_lk.GlobalId,
159+
Customer: serde_lk.Customer,
160+
ActivatedMachines: serde_lk.ActivatedMachines,
161+
TrialActivation: serde_lk.TrialActivation,
162+
MaxNoOfMachines: serde_lk.MaxNoOfMachines,
163+
AllowedMachines: serde_lk.AllowedMachines.split('\n').map(|x| x.to_string()).collect(),
164+
DataObjects: serde_lk.DataObjects,
165+
SignDate: serde_lk.SignDate,
166+
167+
license_key_bytes: license_key_bytes,
168+
signature_bytes: signature_bytes,
169+
})
170+
}
171+
}
172+
173+
#[allow(non_snake_case)]
174+
pub fn KeyActivate(token: &str, args: KeyActivateArguments<'_, '_>) -> Result<LicenseKey, ()> {
175+
let product_id = args.ProductId.to_string();
176+
let fields_to_return = args.FieldsToReturn.to_string();
177+
let floating_time_interval = args.FloatingTimeInterval.to_string();
178+
let max_overdraft = args.MaxOverdraft.to_string();
179+
180+
let params = [
181+
("token", token),
182+
("ProductId", &product_id),
183+
("Key", args.Key),
184+
("MachineCode", args.MachineCode),
185+
("FieldsToReturn", &fields_to_return),
186+
("FloatingTimeInterval", &floating_time_interval),
187+
("MaxOverdraft", &max_overdraft),
188+
189+
("Sign", "true"),
190+
("SignMethod", "1"),
191+
("v", "1"),
192+
];
193+
194+
195+
let client = reqwest::Client::new();
196+
let mut res = client.post("https://app.cryptolens.io/api/key/Activate")
197+
.form(&params)
198+
.send()
199+
.map_err(|_| ())?;
200+
201+
let s = res.text().map_err(|_| ())?;
202+
203+
LicenseKey::from_str(&s)
204+
}
205+
206+
#[derive(Serialize, Deserialize)]
207+
#[allow(non_snake_case)]
208+
struct RSAKeyValue {
209+
Modulus: String,
210+
Exponent: String,
211+
}
212+
213+
impl LicenseKey {
214+
pub fn has_valid_signature(&self, public_key: &str) -> Result<bool, ()> {
215+
let public_key: RSAKeyValue = serde_xml_rs::from_str(public_key).map_err(|_| ())?;
216+
217+
let modulus_bytes = base64::decode(&public_key.Modulus).map_err(|_| ())?;
218+
let exponent_bytes = base64::decode(&public_key.Exponent).map_err(|_| ())?;
219+
220+
let modulus = openssl::bn::BigNum::from_slice(&modulus_bytes).map_err(|_| ())?;
221+
let exponent = openssl::bn::BigNum::from_slice(&exponent_bytes).map_err(|_| ())?;
222+
223+
let keypair = openssl::rsa::Rsa::from_public_components(modulus, exponent).map_err(|_| ())?;
224+
let keypair = openssl::pkey::PKey::from_rsa(keypair).map_err(|_| ())?;
225+
226+
let mut verifier = openssl::sign::Verifier::new(openssl::hash::MessageDigest::sha256(), &keypair).map_err(|_| ())?;
227+
228+
verifier.update(&self.license_key_bytes).map_err(|_| ())?;
229+
verifier.verify(&self.signature_bytes).map_err(|_| ())
230+
}
231+
}

0 commit comments

Comments
 (0)