@carchalabs/seal-electron 0.6.2 → 0.7.0
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.
- package/README.md +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +42 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @carchalabs/seal-electron
|
|
2
2
|
|
|
3
|
-
The [Carcha Seal](https://seal.carchalabs.com)
|
|
3
|
+
The [Carcha Seal](https://seal.carchalabs.com) SDK. It runs in the Electron main process or in plain Node. It activates a license key on this device, saves the signed lease, and verifies it offline with your public keys.
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
6
|
const { Seal } = require('@carchalabs/seal-electron');
|
package/dist/index.d.ts
CHANGED
|
@@ -106,10 +106,16 @@ export declare class Seal {
|
|
|
106
106
|
private emptyState;
|
|
107
107
|
/**
|
|
108
108
|
* The device's public key, sent with activation and check-ins so the server wraps feature keys for
|
|
109
|
-
* this machine only.
|
|
110
|
-
*
|
|
109
|
+
* this machine only. Sent when the native module can also decrypt what is wrapped for it, with the
|
|
110
|
+
* matching secret it derives itself: through the compiled-in keys when they verify for this app, or,
|
|
111
|
+
* with seal-hwid 0.5+, through this app's own keys (every hosted vendor, a self-hosted server). Not
|
|
112
|
+
* with hardware turned off. Never throws.
|
|
111
113
|
*/
|
|
112
114
|
private deviceKey;
|
|
115
|
+
/** The native module's decryption with this app's own keys (seal-hwid 0.5+), or null: older module, none, or hardware off. */
|
|
116
|
+
private deviceDecrypter;
|
|
117
|
+
/** This app's public keys as SPKI PEM, for the native module. */
|
|
118
|
+
private keyPems;
|
|
113
119
|
/**
|
|
114
120
|
* The native verifier, used only when its compiled-in keys include every key this app was given:
|
|
115
121
|
* an app that trusts other keys (a self-hosted server) is checked by the JavaScript verifier alone.
|
|
@@ -133,8 +139,9 @@ export declare class Seal {
|
|
|
133
139
|
* Decrypts data you encrypted for a feature (encryptForFeature in @carchalabs/seal-core, with the
|
|
134
140
|
* feature's content key from the console). Works offline: it uses the saved lease, which must be valid
|
|
135
141
|
* (or in its offline grace period), grant the feature and carry its key, and the saved license key.
|
|
136
|
-
*
|
|
137
|
-
*
|
|
142
|
+
* When the lease is wrapped for this machine's device key, the native module does the work (with its
|
|
143
|
+
* compiled-in keys or, seal-hwid 0.5+, this app's keys), so the device secret and the content key never
|
|
144
|
+
* reach JavaScript; a lease wrapped for the license key alone is opened in JavaScript. Throws a LicenseError:
|
|
138
145
|
* `unlicensed`, a lease failure reason, `clock_suspect`, `feature_not_licensed` or `decrypt_failed`.
|
|
139
146
|
*/
|
|
140
147
|
decrypt(feature: string, data: Uint8Array): Promise<Buffer>;
|
package/dist/index.js
CHANGED
|
@@ -311,17 +311,20 @@ class Seal {
|
|
|
311
311
|
}
|
|
312
312
|
/**
|
|
313
313
|
* The device's public key, sent with activation and check-ins so the server wraps feature keys for
|
|
314
|
-
* this machine only.
|
|
315
|
-
*
|
|
314
|
+
* this machine only. Sent when the native module can also decrypt what is wrapped for it, with the
|
|
315
|
+
* matching secret it derives itself: through the compiled-in keys when they verify for this app, or,
|
|
316
|
+
* with seal-hwid 0.5+, through this app's own keys (every hosted vendor, a self-hosted server). Not
|
|
317
|
+
* with hardware turned off. Never throws.
|
|
316
318
|
*/
|
|
317
319
|
deviceKey() {
|
|
318
320
|
if (this.deviceKeyCache === undefined) {
|
|
319
321
|
let key = null;
|
|
320
322
|
try {
|
|
323
|
+
const hwid = this.opts.hardware === false ? null : loadHwid();
|
|
321
324
|
const native = this.opts.hardware === false ? null : this.native();
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
const decrypts = (native && typeof native.decryptFeature === 'function') || this.deviceDecrypter() !== null;
|
|
326
|
+
if (hwid && typeof hwid.devicePublicKey === 'function' && decrypts)
|
|
327
|
+
key = hwid.devicePublicKey(`licensekit:${this.opts.product}`);
|
|
325
328
|
}
|
|
326
329
|
catch {
|
|
327
330
|
key = null;
|
|
@@ -330,6 +333,17 @@ class Seal {
|
|
|
330
333
|
}
|
|
331
334
|
return this.deviceKeyCache ? { devicePublicKey: this.deviceKeyCache } : {};
|
|
332
335
|
}
|
|
336
|
+
/** The native module's decryption with this app's own keys (seal-hwid 0.5+), or null: older module, none, or hardware off. */
|
|
337
|
+
deviceDecrypter() {
|
|
338
|
+
if (this.opts.hardware === false)
|
|
339
|
+
return null;
|
|
340
|
+
const hwid = loadHwid();
|
|
341
|
+
return hwid && typeof hwid.decryptFeatureWithKeys === 'function' ? hwid : null;
|
|
342
|
+
}
|
|
343
|
+
/** This app's public keys as SPKI PEM, for the native module. */
|
|
344
|
+
keyPems() {
|
|
345
|
+
return [...this.keys.values()].map((key) => key.export({ type: 'spki', format: 'pem' }).toString());
|
|
346
|
+
}
|
|
333
347
|
/**
|
|
334
348
|
* The native verifier, used only when its compiled-in keys include every key this app was given:
|
|
335
349
|
* an app that trusts other keys (a self-hosted server) is checked by the JavaScript verifier alone.
|
|
@@ -510,8 +524,9 @@ class Seal {
|
|
|
510
524
|
* Decrypts data you encrypted for a feature (encryptForFeature in @carchalabs/seal-core, with the
|
|
511
525
|
* feature's content key from the console). Works offline: it uses the saved lease, which must be valid
|
|
512
526
|
* (or in its offline grace period), grant the feature and carry its key, and the saved license key.
|
|
513
|
-
*
|
|
514
|
-
*
|
|
527
|
+
* When the lease is wrapped for this machine's device key, the native module does the work (with its
|
|
528
|
+
* compiled-in keys or, seal-hwid 0.5+, this app's keys), so the device secret and the content key never
|
|
529
|
+
* reach JavaScript; a lease wrapped for the license key alone is opened in JavaScript. Throws a LicenseError:
|
|
515
530
|
* `unlicensed`, a lease failure reason, `clock_suspect`, `feature_not_licensed` or `decrypt_failed`.
|
|
516
531
|
*/
|
|
517
532
|
async decrypt(feature, data) {
|
|
@@ -581,10 +596,27 @@ class Seal {
|
|
|
581
596
|
const graceSeconds = state.status === 'grace' ? (claims?.grc ?? (this.opts.graceDays ?? DEFAULT_GRACE_DAYS) * 86400) : 0;
|
|
582
597
|
const check = { product: this.opts.product, fingerprintHash, now: this.nowSec(), ...(graceSeconds ? { graceSeconds } : {}) };
|
|
583
598
|
const native = this.native();
|
|
599
|
+
// A lease wrapped for this machine's device key (epk) opens only in the native module, which holds the device
|
|
600
|
+
// secret: through the compiled-in keys when they verify for this app, else through this app's keys (seal-hwid 0.5+).
|
|
601
|
+
const deviceBound = claims?.epk !== undefined;
|
|
602
|
+
const withKeys = deviceBound && !(native && typeof native.decryptFeature === 'function') ? this.deviceDecrypter() : null;
|
|
603
|
+
const pems = withKeys ? this.keyPems() : [];
|
|
584
604
|
return (data) => {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
605
|
+
let result;
|
|
606
|
+
if (native && typeof native.decryptFeature === 'function') {
|
|
607
|
+
result = native.decryptFeature(leased.token, check, leased.licenseKey, feature, Buffer.from(data));
|
|
608
|
+
}
|
|
609
|
+
else if (withKeys) {
|
|
610
|
+
try {
|
|
611
|
+
result = withKeys.decryptFeatureWithKeys(leased.token, check, pems, leased.licenseKey, feature, Buffer.from(data));
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
result = { ok: false, reason: 'decrypt_failed' };
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
result = (0, seal_core_1.decryptFeature)(leased.token, { keys: this.keys, ...check }, leased.licenseKey, feature, Buffer.from(data));
|
|
619
|
+
}
|
|
588
620
|
if (result.ok && result.data)
|
|
589
621
|
return Buffer.from(result.data);
|
|
590
622
|
if (result.reason === 'not_licensed')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carchalabs/seal-electron",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Carcha Seal (formerly LicenseKit) SDK for the Electron main process and Node: activation, offline verification, grace period",
|
|
5
5
|
"keywords": ["electron", "licensing", "license-key", "software-licensing", "offline-licensing", "node-locked", "ed25519", "jws", "carcha-seal"],
|
|
6
6
|
"homepage": "https://seal.carchalabs.com/docs/",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@carchalabs/seal-core": "0.5.1"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@carchalabs/seal-hwid": "0.
|
|
21
|
+
"@carchalabs/seal-hwid": "0.5.0"
|
|
22
22
|
},
|
|
23
23
|
"engines": { "node": ">=22.13" },
|
|
24
24
|
"publishConfig": { "access": "public" },
|