devkanan 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- devkanan-0.2.2/PKG-INFO +157 -0
- devkanan-0.2.2/README.md +138 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan/__init__.py +1 -1
- devkanan-0.2.2/devkanan.egg-info/PKG-INFO +157 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/pyproject.toml +1 -1
- devkanan-0.2.0/PKG-INFO +0 -120
- devkanan-0.2.0/README.md +0 -101
- devkanan-0.2.0/devkanan.egg-info/PKG-INFO +0 -120
- {devkanan-0.2.0 → devkanan-0.2.2}/LICENSE +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan/helpers.py +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan/key.py +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan/license_key.py +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan.egg-info/SOURCES.txt +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan.egg-info/dependency_links.txt +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan.egg-info/requires.txt +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/devkanan.egg-info/top_level.txt +0 -0
- {devkanan-0.2.0 → devkanan-0.2.2}/setup.cfg +0 -0
devkanan-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: devkanan
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Cliente Python para DevKanan — validación local + API online de licencias firmadas RSA
|
|
5
|
+
Author-email: Vilchis <vilchislalo98@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://devkanan.dev
|
|
8
|
+
Keywords: license,licensing,rsa,drm,devkanan,kanan
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: cryptography>=41.0.0
|
|
17
|
+
Requires-Dist: requests>=2.28.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# devkanan
|
|
21
|
+
|
|
22
|
+
[](https://pypi.org/project/devkanan/)
|
|
23
|
+
[](https://pypi.org/project/devkanan/)
|
|
24
|
+
[](LICENSE)
|
|
25
|
+
|
|
26
|
+
Lightweight Python client to activate and validate RSA-signed licenses issued by the **[DevKanan](https://devkanan.dev)** platform or an on-premise **DevKananServer**.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install devkanan
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Online activation
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from devkanan import Key, Helpers
|
|
40
|
+
|
|
41
|
+
result, msg = Key.activate(
|
|
42
|
+
token=ACCESS_TOKEN,
|
|
43
|
+
rsa_pub_key=RSA_PUBLIC_KEY,
|
|
44
|
+
product_id=100000,
|
|
45
|
+
key="ABCD-EFGH-IJKL-MNOP",
|
|
46
|
+
machine_code=Helpers.GetMachineCode(v=2),
|
|
47
|
+
# floating_time_interval=300, # optional floating lease
|
|
48
|
+
server_url="https://devkanan.dev", # optional
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if result is None:
|
|
52
|
+
print(f"Activation failed: {msg}")
|
|
53
|
+
exit(1)
|
|
54
|
+
|
|
55
|
+
if result.is_on_right_machine(Helpers.GetMachineCode(v=2)) and result.has_not_expired():
|
|
56
|
+
print("License valid — app can start")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Offline validation (signed `.dat` file)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from devkanan import LicenseKey, Helpers
|
|
63
|
+
|
|
64
|
+
RSA_PUB_KEY = """-----BEGIN PUBLIC KEY-----
|
|
65
|
+
... your tenant's RSA public key ...
|
|
66
|
+
-----END PUBLIC KEY-----"""
|
|
67
|
+
|
|
68
|
+
with open("license.dat", encoding="utf-8-sig") as f:
|
|
69
|
+
lk = LicenseKey.load_from_string(RSA_PUB_KEY, f.read(), max_age_days=1)
|
|
70
|
+
|
|
71
|
+
if lk is None:
|
|
72
|
+
print("Invalid license (corrupt signature, expired or blocked)")
|
|
73
|
+
exit(1)
|
|
74
|
+
|
|
75
|
+
if not lk.is_on_right_machine(Helpers.GetMachineCode(v=2)):
|
|
76
|
+
print("This machine is not authorized")
|
|
77
|
+
exit(1)
|
|
78
|
+
|
|
79
|
+
# Features
|
|
80
|
+
if lk.F1:
|
|
81
|
+
enable_pro_features()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Usage credits
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from devkanan import Credits
|
|
88
|
+
|
|
89
|
+
# Consume credits when a premium feature is used
|
|
90
|
+
ok, balance, msg = Credits.record(
|
|
91
|
+
token=ACCESS_TOKEN, product_id=100000, key=LICENSE_KEY,
|
|
92
|
+
amount=10, feature="export_pdf",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Check current balance
|
|
96
|
+
enabled, balance, msg = Credits.balance(
|
|
97
|
+
token=ACCESS_TOKEN, product_id=100000, key=LICENSE_KEY,
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API
|
|
102
|
+
|
|
103
|
+
### `Helpers.GetMachineCode(v=2)`
|
|
104
|
+
|
|
105
|
+
Generates a unique machine code based on hardware properties. `v=2` (default) uses BIOS UUID via PowerShell on Windows or `/etc/machine-id` on Linux. Produces the same hash as the Cryptolens SDK.
|
|
106
|
+
|
|
107
|
+
### `LicenseKey.load_from_string(rsa_pub_key, content, max_age_days=0)`
|
|
108
|
+
|
|
109
|
+
Parses a `.dat`, verifies the RSA signature and freshness.
|
|
110
|
+
|
|
111
|
+
| Parameter | Type | Description |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `rsa_pub_key` | str | XML (.NET) or PEM (standard). Auto-detected. |
|
|
114
|
+
| `content` | str | `.dat` file contents |
|
|
115
|
+
| `max_age_days` | int | Max days since `signDate` before re-validation is required. 0 = no limit. |
|
|
116
|
+
|
|
117
|
+
Returns a `LicenseKey` object or `None` if any check fails.
|
|
118
|
+
|
|
119
|
+
### `LicenseKey` methods
|
|
120
|
+
|
|
121
|
+
- `is_on_right_machine(machine_code)` — verifies the machine is authorized
|
|
122
|
+
- `has_not_expired()` — verifies `expires > now`
|
|
123
|
+
- `has_feature(n)` — shortcut for `getattr(lk, f'f{n}')` (n = 1..8)
|
|
124
|
+
|
|
125
|
+
### `LicenseKey` attributes
|
|
126
|
+
|
|
127
|
+
| Attribute | Type |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `product_id`, `id`, `key`, `notes` | basic data |
|
|
130
|
+
| `created`, `expires`, `sign_date` | unix timestamps |
|
|
131
|
+
| `period`, `max_no_of_machines` | integers |
|
|
132
|
+
| `F1`..`F8`, `block`, `trial_activation` | flags |
|
|
133
|
+
| `activated_machines` | List[ActivatedMachine] |
|
|
134
|
+
| `data_objects` | List[DataObject] |
|
|
135
|
+
| `customer` | Customer or None |
|
|
136
|
+
| `offline_mode` | "FloatingLease" / "FloatingManual" / "Locked" |
|
|
137
|
+
|
|
138
|
+
## Comparison with Cryptolens SDK
|
|
139
|
+
|
|
140
|
+
| | Cryptolens SDK | devkanan |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| Online activation | ✅ | ✅ |
|
|
143
|
+
| Offline `.dat` / `.skm` validation | ✅ | ✅ |
|
|
144
|
+
| `Helpers.GetMachineCode` | ✅ | ✅ (same hash) |
|
|
145
|
+
| `LicenseKey.load_from_string` | ✅ | ✅ |
|
|
146
|
+
| Size | ~2 MB with deps | ~10 KB |
|
|
147
|
+
| Dependencies | `pycryptodome`, `requests` | `cryptography`, `requests` |
|
|
148
|
+
|
|
149
|
+
## Documentation
|
|
150
|
+
|
|
151
|
+
- [DevKanan docs](https://devkanan.dev/docs)
|
|
152
|
+
- [API reference](https://devkanan.dev/docs/en/api)
|
|
153
|
+
- [Offline mode guide](https://devkanan.dev/docs/en/offline)
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
[Apache 2.0](LICENSE). Partially based on Cryptolens Python SDK (also Apache 2.0).
|
devkanan-0.2.2/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# devkanan
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/devkanan/)
|
|
4
|
+
[](https://pypi.org/project/devkanan/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Lightweight Python client to activate and validate RSA-signed licenses issued by the **[DevKanan](https://devkanan.dev)** platform or an on-premise **DevKananServer**.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install devkanan
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Online activation
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from devkanan import Key, Helpers
|
|
21
|
+
|
|
22
|
+
result, msg = Key.activate(
|
|
23
|
+
token=ACCESS_TOKEN,
|
|
24
|
+
rsa_pub_key=RSA_PUBLIC_KEY,
|
|
25
|
+
product_id=100000,
|
|
26
|
+
key="ABCD-EFGH-IJKL-MNOP",
|
|
27
|
+
machine_code=Helpers.GetMachineCode(v=2),
|
|
28
|
+
# floating_time_interval=300, # optional floating lease
|
|
29
|
+
server_url="https://devkanan.dev", # optional
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if result is None:
|
|
33
|
+
print(f"Activation failed: {msg}")
|
|
34
|
+
exit(1)
|
|
35
|
+
|
|
36
|
+
if result.is_on_right_machine(Helpers.GetMachineCode(v=2)) and result.has_not_expired():
|
|
37
|
+
print("License valid — app can start")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Offline validation (signed `.dat` file)
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from devkanan import LicenseKey, Helpers
|
|
44
|
+
|
|
45
|
+
RSA_PUB_KEY = """-----BEGIN PUBLIC KEY-----
|
|
46
|
+
... your tenant's RSA public key ...
|
|
47
|
+
-----END PUBLIC KEY-----"""
|
|
48
|
+
|
|
49
|
+
with open("license.dat", encoding="utf-8-sig") as f:
|
|
50
|
+
lk = LicenseKey.load_from_string(RSA_PUB_KEY, f.read(), max_age_days=1)
|
|
51
|
+
|
|
52
|
+
if lk is None:
|
|
53
|
+
print("Invalid license (corrupt signature, expired or blocked)")
|
|
54
|
+
exit(1)
|
|
55
|
+
|
|
56
|
+
if not lk.is_on_right_machine(Helpers.GetMachineCode(v=2)):
|
|
57
|
+
print("This machine is not authorized")
|
|
58
|
+
exit(1)
|
|
59
|
+
|
|
60
|
+
# Features
|
|
61
|
+
if lk.F1:
|
|
62
|
+
enable_pro_features()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Usage credits
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from devkanan import Credits
|
|
69
|
+
|
|
70
|
+
# Consume credits when a premium feature is used
|
|
71
|
+
ok, balance, msg = Credits.record(
|
|
72
|
+
token=ACCESS_TOKEN, product_id=100000, key=LICENSE_KEY,
|
|
73
|
+
amount=10, feature="export_pdf",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Check current balance
|
|
77
|
+
enabled, balance, msg = Credits.balance(
|
|
78
|
+
token=ACCESS_TOKEN, product_id=100000, key=LICENSE_KEY,
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API
|
|
83
|
+
|
|
84
|
+
### `Helpers.GetMachineCode(v=2)`
|
|
85
|
+
|
|
86
|
+
Generates a unique machine code based on hardware properties. `v=2` (default) uses BIOS UUID via PowerShell on Windows or `/etc/machine-id` on Linux. Produces the same hash as the Cryptolens SDK.
|
|
87
|
+
|
|
88
|
+
### `LicenseKey.load_from_string(rsa_pub_key, content, max_age_days=0)`
|
|
89
|
+
|
|
90
|
+
Parses a `.dat`, verifies the RSA signature and freshness.
|
|
91
|
+
|
|
92
|
+
| Parameter | Type | Description |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `rsa_pub_key` | str | XML (.NET) or PEM (standard). Auto-detected. |
|
|
95
|
+
| `content` | str | `.dat` file contents |
|
|
96
|
+
| `max_age_days` | int | Max days since `signDate` before re-validation is required. 0 = no limit. |
|
|
97
|
+
|
|
98
|
+
Returns a `LicenseKey` object or `None` if any check fails.
|
|
99
|
+
|
|
100
|
+
### `LicenseKey` methods
|
|
101
|
+
|
|
102
|
+
- `is_on_right_machine(machine_code)` — verifies the machine is authorized
|
|
103
|
+
- `has_not_expired()` — verifies `expires > now`
|
|
104
|
+
- `has_feature(n)` — shortcut for `getattr(lk, f'f{n}')` (n = 1..8)
|
|
105
|
+
|
|
106
|
+
### `LicenseKey` attributes
|
|
107
|
+
|
|
108
|
+
| Attribute | Type |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `product_id`, `id`, `key`, `notes` | basic data |
|
|
111
|
+
| `created`, `expires`, `sign_date` | unix timestamps |
|
|
112
|
+
| `period`, `max_no_of_machines` | integers |
|
|
113
|
+
| `F1`..`F8`, `block`, `trial_activation` | flags |
|
|
114
|
+
| `activated_machines` | List[ActivatedMachine] |
|
|
115
|
+
| `data_objects` | List[DataObject] |
|
|
116
|
+
| `customer` | Customer or None |
|
|
117
|
+
| `offline_mode` | "FloatingLease" / "FloatingManual" / "Locked" |
|
|
118
|
+
|
|
119
|
+
## Comparison with Cryptolens SDK
|
|
120
|
+
|
|
121
|
+
| | Cryptolens SDK | devkanan |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| Online activation | ✅ | ✅ |
|
|
124
|
+
| Offline `.dat` / `.skm` validation | ✅ | ✅ |
|
|
125
|
+
| `Helpers.GetMachineCode` | ✅ | ✅ (same hash) |
|
|
126
|
+
| `LicenseKey.load_from_string` | ✅ | ✅ |
|
|
127
|
+
| Size | ~2 MB with deps | ~10 KB |
|
|
128
|
+
| Dependencies | `pycryptodome`, `requests` | `cryptography`, `requests` |
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
- [DevKanan docs](https://devkanan.dev/docs)
|
|
133
|
+
- [API reference](https://devkanan.dev/docs/en/api)
|
|
134
|
+
- [Offline mode guide](https://devkanan.dev/docs/en/offline)
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
[Apache 2.0](LICENSE). Partially based on Cryptolens Python SDK (also Apache 2.0).
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: devkanan
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Cliente Python para DevKanan — validación local + API online de licencias firmadas RSA
|
|
5
|
+
Author-email: Vilchis <vilchislalo98@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://devkanan.dev
|
|
8
|
+
Keywords: license,licensing,rsa,drm,devkanan,kanan
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: cryptography>=41.0.0
|
|
17
|
+
Requires-Dist: requests>=2.28.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# devkanan
|
|
21
|
+
|
|
22
|
+
[](https://pypi.org/project/devkanan/)
|
|
23
|
+
[](https://pypi.org/project/devkanan/)
|
|
24
|
+
[](LICENSE)
|
|
25
|
+
|
|
26
|
+
Lightweight Python client to activate and validate RSA-signed licenses issued by the **[DevKanan](https://devkanan.dev)** platform or an on-premise **DevKananServer**.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install devkanan
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Online activation
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from devkanan import Key, Helpers
|
|
40
|
+
|
|
41
|
+
result, msg = Key.activate(
|
|
42
|
+
token=ACCESS_TOKEN,
|
|
43
|
+
rsa_pub_key=RSA_PUBLIC_KEY,
|
|
44
|
+
product_id=100000,
|
|
45
|
+
key="ABCD-EFGH-IJKL-MNOP",
|
|
46
|
+
machine_code=Helpers.GetMachineCode(v=2),
|
|
47
|
+
# floating_time_interval=300, # optional floating lease
|
|
48
|
+
server_url="https://devkanan.dev", # optional
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if result is None:
|
|
52
|
+
print(f"Activation failed: {msg}")
|
|
53
|
+
exit(1)
|
|
54
|
+
|
|
55
|
+
if result.is_on_right_machine(Helpers.GetMachineCode(v=2)) and result.has_not_expired():
|
|
56
|
+
print("License valid — app can start")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Offline validation (signed `.dat` file)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from devkanan import LicenseKey, Helpers
|
|
63
|
+
|
|
64
|
+
RSA_PUB_KEY = """-----BEGIN PUBLIC KEY-----
|
|
65
|
+
... your tenant's RSA public key ...
|
|
66
|
+
-----END PUBLIC KEY-----"""
|
|
67
|
+
|
|
68
|
+
with open("license.dat", encoding="utf-8-sig") as f:
|
|
69
|
+
lk = LicenseKey.load_from_string(RSA_PUB_KEY, f.read(), max_age_days=1)
|
|
70
|
+
|
|
71
|
+
if lk is None:
|
|
72
|
+
print("Invalid license (corrupt signature, expired or blocked)")
|
|
73
|
+
exit(1)
|
|
74
|
+
|
|
75
|
+
if not lk.is_on_right_machine(Helpers.GetMachineCode(v=2)):
|
|
76
|
+
print("This machine is not authorized")
|
|
77
|
+
exit(1)
|
|
78
|
+
|
|
79
|
+
# Features
|
|
80
|
+
if lk.F1:
|
|
81
|
+
enable_pro_features()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Usage credits
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from devkanan import Credits
|
|
88
|
+
|
|
89
|
+
# Consume credits when a premium feature is used
|
|
90
|
+
ok, balance, msg = Credits.record(
|
|
91
|
+
token=ACCESS_TOKEN, product_id=100000, key=LICENSE_KEY,
|
|
92
|
+
amount=10, feature="export_pdf",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Check current balance
|
|
96
|
+
enabled, balance, msg = Credits.balance(
|
|
97
|
+
token=ACCESS_TOKEN, product_id=100000, key=LICENSE_KEY,
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API
|
|
102
|
+
|
|
103
|
+
### `Helpers.GetMachineCode(v=2)`
|
|
104
|
+
|
|
105
|
+
Generates a unique machine code based on hardware properties. `v=2` (default) uses BIOS UUID via PowerShell on Windows or `/etc/machine-id` on Linux. Produces the same hash as the Cryptolens SDK.
|
|
106
|
+
|
|
107
|
+
### `LicenseKey.load_from_string(rsa_pub_key, content, max_age_days=0)`
|
|
108
|
+
|
|
109
|
+
Parses a `.dat`, verifies the RSA signature and freshness.
|
|
110
|
+
|
|
111
|
+
| Parameter | Type | Description |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `rsa_pub_key` | str | XML (.NET) or PEM (standard). Auto-detected. |
|
|
114
|
+
| `content` | str | `.dat` file contents |
|
|
115
|
+
| `max_age_days` | int | Max days since `signDate` before re-validation is required. 0 = no limit. |
|
|
116
|
+
|
|
117
|
+
Returns a `LicenseKey` object or `None` if any check fails.
|
|
118
|
+
|
|
119
|
+
### `LicenseKey` methods
|
|
120
|
+
|
|
121
|
+
- `is_on_right_machine(machine_code)` — verifies the machine is authorized
|
|
122
|
+
- `has_not_expired()` — verifies `expires > now`
|
|
123
|
+
- `has_feature(n)` — shortcut for `getattr(lk, f'f{n}')` (n = 1..8)
|
|
124
|
+
|
|
125
|
+
### `LicenseKey` attributes
|
|
126
|
+
|
|
127
|
+
| Attribute | Type |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `product_id`, `id`, `key`, `notes` | basic data |
|
|
130
|
+
| `created`, `expires`, `sign_date` | unix timestamps |
|
|
131
|
+
| `period`, `max_no_of_machines` | integers |
|
|
132
|
+
| `F1`..`F8`, `block`, `trial_activation` | flags |
|
|
133
|
+
| `activated_machines` | List[ActivatedMachine] |
|
|
134
|
+
| `data_objects` | List[DataObject] |
|
|
135
|
+
| `customer` | Customer or None |
|
|
136
|
+
| `offline_mode` | "FloatingLease" / "FloatingManual" / "Locked" |
|
|
137
|
+
|
|
138
|
+
## Comparison with Cryptolens SDK
|
|
139
|
+
|
|
140
|
+
| | Cryptolens SDK | devkanan |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| Online activation | ✅ | ✅ |
|
|
143
|
+
| Offline `.dat` / `.skm` validation | ✅ | ✅ |
|
|
144
|
+
| `Helpers.GetMachineCode` | ✅ | ✅ (same hash) |
|
|
145
|
+
| `LicenseKey.load_from_string` | ✅ | ✅ |
|
|
146
|
+
| Size | ~2 MB with deps | ~10 KB |
|
|
147
|
+
| Dependencies | `pycryptodome`, `requests` | `cryptography`, `requests` |
|
|
148
|
+
|
|
149
|
+
## Documentation
|
|
150
|
+
|
|
151
|
+
- [DevKanan docs](https://devkanan.dev/docs)
|
|
152
|
+
- [API reference](https://devkanan.dev/docs/en/api)
|
|
153
|
+
- [Offline mode guide](https://devkanan.dev/docs/en/offline)
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
[Apache 2.0](LICENSE). Partially based on Cryptolens Python SDK (also Apache 2.0).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devkanan"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Cliente Python para DevKanan — validación local + API online de licencias firmadas RSA"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
devkanan-0.2.0/PKG-INFO
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: devkanan
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Cliente Python para DevKanan — validación local + API online de licencias firmadas RSA
|
|
5
|
-
Author-email: Vilchis <vilchislalo98@gmail.com>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://devkanan.dev
|
|
8
|
-
Keywords: license,licensing,rsa,drm,devkanan,kanan
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.8
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
Requires-Dist: cryptography>=41.0.0
|
|
17
|
-
Requires-Dist: requests>=2.28.0
|
|
18
|
-
Dynamic: license-file
|
|
19
|
-
|
|
20
|
-
# qustomlicensing
|
|
21
|
-
|
|
22
|
-
Cliente Python ligero para validar archivos de licencia `.dat` firmados con RSA, emitidos por **LicensingServer** SaaS o QustomLicenseServer.
|
|
23
|
-
|
|
24
|
-
API estilo Cryptolens — fácil migración si vienes de su SDK.
|
|
25
|
-
|
|
26
|
-
## Instalación
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
pip install qustomlicensing
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
O para desarrollo:
|
|
33
|
-
```bash
|
|
34
|
-
pip install -e .
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Uso básico
|
|
38
|
-
|
|
39
|
-
```python
|
|
40
|
-
from qustomlicensing import LicenseKey, Helpers
|
|
41
|
-
|
|
42
|
-
# Clave pública RSA del tenant (XML o PEM)
|
|
43
|
-
RSA_PUB_KEY = """-----BEGIN PUBLIC KEY-----
|
|
44
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
45
|
-
-----END PUBLIC KEY-----"""
|
|
46
|
-
|
|
47
|
-
# Obtener machine code de esta PC
|
|
48
|
-
machine_code = Helpers.GetMachineCode(v=2)
|
|
49
|
-
|
|
50
|
-
# Leer y validar .dat
|
|
51
|
-
with open("license.dat", "r", encoding="utf-8-sig") as f:
|
|
52
|
-
license_key = LicenseKey.load_from_string(RSA_PUB_KEY, f.read(), max_age_days=1)
|
|
53
|
-
|
|
54
|
-
if license_key is None:
|
|
55
|
-
print("Licencia invalida o firma corrupta")
|
|
56
|
-
exit(1)
|
|
57
|
-
|
|
58
|
-
# Verificar esta máquina
|
|
59
|
-
if not license_key.is_on_right_machine(machine_code):
|
|
60
|
-
print("Esta maquina no esta autorizada")
|
|
61
|
-
exit(1)
|
|
62
|
-
|
|
63
|
-
# Verificar features
|
|
64
|
-
if license_key.F1:
|
|
65
|
-
print("Feature Pro activado")
|
|
66
|
-
|
|
67
|
-
print(f"Licencia valida hasta {license_key.expires}")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## API
|
|
71
|
-
|
|
72
|
-
### `Helpers.GetMachineCode(v=2)`
|
|
73
|
-
Genera un machine code único basado en propiedades del hardware. `v=2` es el algoritmo actual (SHA-256 sobre identificadores).
|
|
74
|
-
|
|
75
|
-
### `LicenseKey.load_from_string(rsa_pub_key, content, max_age_days=0)`
|
|
76
|
-
Parsea un `.dat` JSON, verifica firma RSA y freshness.
|
|
77
|
-
|
|
78
|
-
| Parámetro | Tipo | Descripción |
|
|
79
|
-
|---|---|---|
|
|
80
|
-
| `rsa_pub_key` | str | XML (.NET) o PEM. Auto-detecta. |
|
|
81
|
-
| `content` | str | Contenido del archivo `.dat` |
|
|
82
|
-
| `max_age_days` | int | Máx. días desde `signDate`. 0 = ilimitado o usa el del `.dat`. |
|
|
83
|
-
|
|
84
|
-
Devuelve un objeto `LicenseKey` o `None` si la validación falla.
|
|
85
|
-
|
|
86
|
-
### Atributos de `LicenseKey`
|
|
87
|
-
| Atributo | Tipo |
|
|
88
|
-
|---|---|
|
|
89
|
-
| `product_id`, `id`, `key`, `notes` | datos básicos |
|
|
90
|
-
| `created`, `expires`, `sign_date` | datetimes UTC |
|
|
91
|
-
| `period`, `max_no_of_machines`, `max_offline_days` | enteros |
|
|
92
|
-
| `F1`..`F8`, `block`, `trial_activation` | flags |
|
|
93
|
-
| `activated_machines` | List[ActivatedMachine] |
|
|
94
|
-
| `data_objects` | List[DataObject] |
|
|
95
|
-
| `customer` | Customer o None |
|
|
96
|
-
| `offline_mode` | str (FloatingLease/FloatingManual/Locked) |
|
|
97
|
-
|
|
98
|
-
### `license_key.is_on_right_machine(machine_code)`
|
|
99
|
-
Verifica que el machine code esté en `activatedMachines`.
|
|
100
|
-
|
|
101
|
-
### `license_key.has_not_expired()`
|
|
102
|
-
Verifica que `expires > now`.
|
|
103
|
-
|
|
104
|
-
### `license_key.has_feature(n)`
|
|
105
|
-
Atajo para `getattr(license_key, f'F{n}')`.
|
|
106
|
-
|
|
107
|
-
## Comparación con SDK Cryptolens
|
|
108
|
-
|
|
109
|
-
| | Cryptolens SDK | qustomlicensing |
|
|
110
|
-
|---|---|---|
|
|
111
|
-
| Activate online | ✅ | ❌ (usa requests directamente) |
|
|
112
|
-
| Validar .skm/.dat offline | ✅ | ✅ |
|
|
113
|
-
| `Helpers.GetMachineCode` | ✅ | ✅ (compatible) |
|
|
114
|
-
| `LicenseKey.load_from_string` | ✅ | ✅ (compatible) |
|
|
115
|
-
| Tamaño | ~2 MB con deps | ~50 KB |
|
|
116
|
-
| Dependencias | `pycryptodome`, `requests` | solo `cryptography` |
|
|
117
|
-
|
|
118
|
-
## Licencia
|
|
119
|
-
|
|
120
|
-
Apache-2.0. Basado en código del SDK de Cryptolens (también Apache-2.0).
|
devkanan-0.2.0/README.md
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# qustomlicensing
|
|
2
|
-
|
|
3
|
-
Cliente Python ligero para validar archivos de licencia `.dat` firmados con RSA, emitidos por **LicensingServer** SaaS o QustomLicenseServer.
|
|
4
|
-
|
|
5
|
-
API estilo Cryptolens — fácil migración si vienes de su SDK.
|
|
6
|
-
|
|
7
|
-
## Instalación
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
pip install qustomlicensing
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
O para desarrollo:
|
|
14
|
-
```bash
|
|
15
|
-
pip install -e .
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Uso básico
|
|
19
|
-
|
|
20
|
-
```python
|
|
21
|
-
from qustomlicensing import LicenseKey, Helpers
|
|
22
|
-
|
|
23
|
-
# Clave pública RSA del tenant (XML o PEM)
|
|
24
|
-
RSA_PUB_KEY = """-----BEGIN PUBLIC KEY-----
|
|
25
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
26
|
-
-----END PUBLIC KEY-----"""
|
|
27
|
-
|
|
28
|
-
# Obtener machine code de esta PC
|
|
29
|
-
machine_code = Helpers.GetMachineCode(v=2)
|
|
30
|
-
|
|
31
|
-
# Leer y validar .dat
|
|
32
|
-
with open("license.dat", "r", encoding="utf-8-sig") as f:
|
|
33
|
-
license_key = LicenseKey.load_from_string(RSA_PUB_KEY, f.read(), max_age_days=1)
|
|
34
|
-
|
|
35
|
-
if license_key is None:
|
|
36
|
-
print("Licencia invalida o firma corrupta")
|
|
37
|
-
exit(1)
|
|
38
|
-
|
|
39
|
-
# Verificar esta máquina
|
|
40
|
-
if not license_key.is_on_right_machine(machine_code):
|
|
41
|
-
print("Esta maquina no esta autorizada")
|
|
42
|
-
exit(1)
|
|
43
|
-
|
|
44
|
-
# Verificar features
|
|
45
|
-
if license_key.F1:
|
|
46
|
-
print("Feature Pro activado")
|
|
47
|
-
|
|
48
|
-
print(f"Licencia valida hasta {license_key.expires}")
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## API
|
|
52
|
-
|
|
53
|
-
### `Helpers.GetMachineCode(v=2)`
|
|
54
|
-
Genera un machine code único basado en propiedades del hardware. `v=2` es el algoritmo actual (SHA-256 sobre identificadores).
|
|
55
|
-
|
|
56
|
-
### `LicenseKey.load_from_string(rsa_pub_key, content, max_age_days=0)`
|
|
57
|
-
Parsea un `.dat` JSON, verifica firma RSA y freshness.
|
|
58
|
-
|
|
59
|
-
| Parámetro | Tipo | Descripción |
|
|
60
|
-
|---|---|---|
|
|
61
|
-
| `rsa_pub_key` | str | XML (.NET) o PEM. Auto-detecta. |
|
|
62
|
-
| `content` | str | Contenido del archivo `.dat` |
|
|
63
|
-
| `max_age_days` | int | Máx. días desde `signDate`. 0 = ilimitado o usa el del `.dat`. |
|
|
64
|
-
|
|
65
|
-
Devuelve un objeto `LicenseKey` o `None` si la validación falla.
|
|
66
|
-
|
|
67
|
-
### Atributos de `LicenseKey`
|
|
68
|
-
| Atributo | Tipo |
|
|
69
|
-
|---|---|
|
|
70
|
-
| `product_id`, `id`, `key`, `notes` | datos básicos |
|
|
71
|
-
| `created`, `expires`, `sign_date` | datetimes UTC |
|
|
72
|
-
| `period`, `max_no_of_machines`, `max_offline_days` | enteros |
|
|
73
|
-
| `F1`..`F8`, `block`, `trial_activation` | flags |
|
|
74
|
-
| `activated_machines` | List[ActivatedMachine] |
|
|
75
|
-
| `data_objects` | List[DataObject] |
|
|
76
|
-
| `customer` | Customer o None |
|
|
77
|
-
| `offline_mode` | str (FloatingLease/FloatingManual/Locked) |
|
|
78
|
-
|
|
79
|
-
### `license_key.is_on_right_machine(machine_code)`
|
|
80
|
-
Verifica que el machine code esté en `activatedMachines`.
|
|
81
|
-
|
|
82
|
-
### `license_key.has_not_expired()`
|
|
83
|
-
Verifica que `expires > now`.
|
|
84
|
-
|
|
85
|
-
### `license_key.has_feature(n)`
|
|
86
|
-
Atajo para `getattr(license_key, f'F{n}')`.
|
|
87
|
-
|
|
88
|
-
## Comparación con SDK Cryptolens
|
|
89
|
-
|
|
90
|
-
| | Cryptolens SDK | qustomlicensing |
|
|
91
|
-
|---|---|---|
|
|
92
|
-
| Activate online | ✅ | ❌ (usa requests directamente) |
|
|
93
|
-
| Validar .skm/.dat offline | ✅ | ✅ |
|
|
94
|
-
| `Helpers.GetMachineCode` | ✅ | ✅ (compatible) |
|
|
95
|
-
| `LicenseKey.load_from_string` | ✅ | ✅ (compatible) |
|
|
96
|
-
| Tamaño | ~2 MB con deps | ~50 KB |
|
|
97
|
-
| Dependencias | `pycryptodome`, `requests` | solo `cryptography` |
|
|
98
|
-
|
|
99
|
-
## Licencia
|
|
100
|
-
|
|
101
|
-
Apache-2.0. Basado en código del SDK de Cryptolens (también Apache-2.0).
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: devkanan
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Cliente Python para DevKanan — validación local + API online de licencias firmadas RSA
|
|
5
|
-
Author-email: Vilchis <vilchislalo98@gmail.com>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://devkanan.dev
|
|
8
|
-
Keywords: license,licensing,rsa,drm,devkanan,kanan
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.8
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
License-File: LICENSE
|
|
16
|
-
Requires-Dist: cryptography>=41.0.0
|
|
17
|
-
Requires-Dist: requests>=2.28.0
|
|
18
|
-
Dynamic: license-file
|
|
19
|
-
|
|
20
|
-
# qustomlicensing
|
|
21
|
-
|
|
22
|
-
Cliente Python ligero para validar archivos de licencia `.dat` firmados con RSA, emitidos por **LicensingServer** SaaS o QustomLicenseServer.
|
|
23
|
-
|
|
24
|
-
API estilo Cryptolens — fácil migración si vienes de su SDK.
|
|
25
|
-
|
|
26
|
-
## Instalación
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
pip install qustomlicensing
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
O para desarrollo:
|
|
33
|
-
```bash
|
|
34
|
-
pip install -e .
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Uso básico
|
|
38
|
-
|
|
39
|
-
```python
|
|
40
|
-
from qustomlicensing import LicenseKey, Helpers
|
|
41
|
-
|
|
42
|
-
# Clave pública RSA del tenant (XML o PEM)
|
|
43
|
-
RSA_PUB_KEY = """-----BEGIN PUBLIC KEY-----
|
|
44
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
|
45
|
-
-----END PUBLIC KEY-----"""
|
|
46
|
-
|
|
47
|
-
# Obtener machine code de esta PC
|
|
48
|
-
machine_code = Helpers.GetMachineCode(v=2)
|
|
49
|
-
|
|
50
|
-
# Leer y validar .dat
|
|
51
|
-
with open("license.dat", "r", encoding="utf-8-sig") as f:
|
|
52
|
-
license_key = LicenseKey.load_from_string(RSA_PUB_KEY, f.read(), max_age_days=1)
|
|
53
|
-
|
|
54
|
-
if license_key is None:
|
|
55
|
-
print("Licencia invalida o firma corrupta")
|
|
56
|
-
exit(1)
|
|
57
|
-
|
|
58
|
-
# Verificar esta máquina
|
|
59
|
-
if not license_key.is_on_right_machine(machine_code):
|
|
60
|
-
print("Esta maquina no esta autorizada")
|
|
61
|
-
exit(1)
|
|
62
|
-
|
|
63
|
-
# Verificar features
|
|
64
|
-
if license_key.F1:
|
|
65
|
-
print("Feature Pro activado")
|
|
66
|
-
|
|
67
|
-
print(f"Licencia valida hasta {license_key.expires}")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## API
|
|
71
|
-
|
|
72
|
-
### `Helpers.GetMachineCode(v=2)`
|
|
73
|
-
Genera un machine code único basado en propiedades del hardware. `v=2` es el algoritmo actual (SHA-256 sobre identificadores).
|
|
74
|
-
|
|
75
|
-
### `LicenseKey.load_from_string(rsa_pub_key, content, max_age_days=0)`
|
|
76
|
-
Parsea un `.dat` JSON, verifica firma RSA y freshness.
|
|
77
|
-
|
|
78
|
-
| Parámetro | Tipo | Descripción |
|
|
79
|
-
|---|---|---|
|
|
80
|
-
| `rsa_pub_key` | str | XML (.NET) o PEM. Auto-detecta. |
|
|
81
|
-
| `content` | str | Contenido del archivo `.dat` |
|
|
82
|
-
| `max_age_days` | int | Máx. días desde `signDate`. 0 = ilimitado o usa el del `.dat`. |
|
|
83
|
-
|
|
84
|
-
Devuelve un objeto `LicenseKey` o `None` si la validación falla.
|
|
85
|
-
|
|
86
|
-
### Atributos de `LicenseKey`
|
|
87
|
-
| Atributo | Tipo |
|
|
88
|
-
|---|---|
|
|
89
|
-
| `product_id`, `id`, `key`, `notes` | datos básicos |
|
|
90
|
-
| `created`, `expires`, `sign_date` | datetimes UTC |
|
|
91
|
-
| `period`, `max_no_of_machines`, `max_offline_days` | enteros |
|
|
92
|
-
| `F1`..`F8`, `block`, `trial_activation` | flags |
|
|
93
|
-
| `activated_machines` | List[ActivatedMachine] |
|
|
94
|
-
| `data_objects` | List[DataObject] |
|
|
95
|
-
| `customer` | Customer o None |
|
|
96
|
-
| `offline_mode` | str (FloatingLease/FloatingManual/Locked) |
|
|
97
|
-
|
|
98
|
-
### `license_key.is_on_right_machine(machine_code)`
|
|
99
|
-
Verifica que el machine code esté en `activatedMachines`.
|
|
100
|
-
|
|
101
|
-
### `license_key.has_not_expired()`
|
|
102
|
-
Verifica que `expires > now`.
|
|
103
|
-
|
|
104
|
-
### `license_key.has_feature(n)`
|
|
105
|
-
Atajo para `getattr(license_key, f'F{n}')`.
|
|
106
|
-
|
|
107
|
-
## Comparación con SDK Cryptolens
|
|
108
|
-
|
|
109
|
-
| | Cryptolens SDK | qustomlicensing |
|
|
110
|
-
|---|---|---|
|
|
111
|
-
| Activate online | ✅ | ❌ (usa requests directamente) |
|
|
112
|
-
| Validar .skm/.dat offline | ✅ | ✅ |
|
|
113
|
-
| `Helpers.GetMachineCode` | ✅ | ✅ (compatible) |
|
|
114
|
-
| `LicenseKey.load_from_string` | ✅ | ✅ (compatible) |
|
|
115
|
-
| Tamaño | ~2 MB con deps | ~50 KB |
|
|
116
|
-
| Dependencias | `pycryptodome`, `requests` | solo `cryptography` |
|
|
117
|
-
|
|
118
|
-
## Licencia
|
|
119
|
-
|
|
120
|
-
Apache-2.0. Basado en código del SDK de Cryptolens (también Apache-2.0).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|