@carchalabs/seal-electron 0.5.0 → 0.6.1
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/LICENSE +9 -18
- package/README.md +2 -2
- package/dist/fingerprint.js +0 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +95 -20
- package/package.json +6 -6
- package/dist/fingerprint.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/src/fingerprint.ts +0 -103
- package/src/index.ts +0 -623
package/LICENSE
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
Carcha Seal SDK
|
|
2
|
+
Copyright (c) 2026 Bugra Omer Aydemir (Carcha Labs). All rights reserved.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
This software is proprietary. You may use, copy and distribute it only as section 5 (the SDK license) of the
|
|
5
|
+
Carcha Seal Vendor Terms allows: https://seal.carchalabs.com/terms/. You accept those terms by creating a Carcha
|
|
6
|
+
Seal account or by using this software.
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
8
|
+
Open-source components included in this software are licensed under their own licenses, reproduced in
|
|
9
|
+
THIRD-PARTY-NOTICES.txt where a package contains any.
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
11
|
+
Earlier versions keep the license they were published with: versions published before 7 November 2026 are under
|
|
12
|
+
the MIT License; versions published on 7 November 2026 are under the Carcha Seal SDK License 1.0.
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ if (state.status === 'unlicensed') await kit.activate(userEnteredKey);
|
|
|
21
21
|
if (Seal.hasFeature(state, 'export-pdf')) exportPdf();
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`check()` returns one of `valid`, `grace`, `invalid`, `unlicensed` or `clock_suspect`, with `offline` and `storageProtected` flags. A refusal from the server (suspended, expired, device removed, update required) is kept while offline and cleared by the next successful check-in. `notices` carries what the server asks your app to act on, such as `sdk_update_recommended` or `app_update_recommended` with a `minimum` version and a `requiredFrom` date; the SDK shows nothing itself. `activate(key, machineName)` sends the device name you pass
|
|
24
|
+
`check()` returns one of `valid`, `grace`, `invalid`, `unlicensed` or `clock_suspect`, with `offline` and `storageProtected` flags. A refusal from the server (suspended, expired, device removed, update required) is kept while offline and cleared by the next successful check-in. `notices` carries what the server asks your app to act on, such as `sdk_update_recommended` or `app_update_recommended` with a `minimum` version and a `requiredFrom` date; the SDK shows nothing itself. `activate(key, machineName)` sends the device name you pass, and none when you pass nothing (a computer's host name often holds its user's name). `deactivate()` releases the seat.
|
|
25
25
|
|
|
26
26
|
Code can be protected the same way (Envelope): encrypt a CommonJS module or a folder of them with `seal-encrypt --feature pro dist/premium dist/premium`, and load it with `await kit.requireEncrypted('pro', path.join(__dirname, 'premium/index.js.lke'))`. Without the feature in the license the code cannot be read; a licensed user can still read it from memory.
|
|
27
27
|
|
|
@@ -33,4 +33,4 @@ The SDK sends the license key, a per-product SHA-256 hash of the operating syste
|
|
|
33
33
|
|
|
34
34
|
**Hardware components.** When `@carchalabs/seal-hwid` is installed, the SDK also sends one keyed hash per hardware part (motherboard, disk, network card, OS install, CPU and graphics models) and whether the computer is a virtual machine. The license server then recognises the computer after a new disk, network card or operating system install, and refuses a copied disk or system image on other hardware. Raw serial numbers never leave the computer. Without the package, or on a platform it has no module for, nothing extra is sent. `hardware: false` turns it off. Bundlers must leave the package to Node (mark it external); electron-builder unpacks its native module from the asar archive.
|
|
35
35
|
|
|
36
|
-
Integration guide (activate once, check at start, put feature code behind `decrypt`, ship the native module, harden the app): [seal.carchalabs.com/docs/#integration](https://seal.carchalabs.com/docs/#integration). Full reference: [seal.carchalabs.com/docs](https://seal.carchalabs.com/docs/#sdk). Requires Node 22.13 or later.
|
|
36
|
+
Integration guide (activate once, check at start, put feature code behind `decrypt`, ship the native module, harden the app): [seal.carchalabs.com/docs/#integration](https://seal.carchalabs.com/docs/#integration). Full reference: [seal.carchalabs.com/docs](https://seal.carchalabs.com/docs/#sdk). Requires Node 22.13 or later. Proprietary: use under the [Carcha Seal Vendor Terms](https://seal.carchalabs.com/terms/) ([LICENSE](LICENSE)).
|
package/dist/fingerprint.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export interface SecureStore {
|
|
|
45
45
|
isProtected?(): boolean;
|
|
46
46
|
}
|
|
47
47
|
export interface SealOptions {
|
|
48
|
+
/** The license server: https://, or http:// on this computer only (localhost, 127.x.x.x, [::1]). */
|
|
48
49
|
serverUrl: string;
|
|
49
50
|
product: string;
|
|
50
51
|
publicKey: string | string[];
|
|
@@ -86,7 +87,10 @@ export declare class Seal {
|
|
|
86
87
|
fingerprint(): Promise<string>;
|
|
87
88
|
/** Read once per instance: hardware does not change while the app runs. Never throws. */
|
|
88
89
|
private hardware;
|
|
90
|
+
/** The saved record; null when there is none, or it cannot be read or is not a record (as no license). */
|
|
89
91
|
private load;
|
|
92
|
+
/** Replaces license.dat whole: written to license.dat.tmp, then renamed over it, so a crash or full disk mid-write
|
|
93
|
+
* leaves the previous record. */
|
|
90
94
|
private save;
|
|
91
95
|
private clear;
|
|
92
96
|
private post;
|
|
@@ -109,8 +113,11 @@ export declare class Seal {
|
|
|
109
113
|
*/
|
|
110
114
|
private verify;
|
|
111
115
|
private stateFromLease;
|
|
116
|
+
/** machineName: the name to show the vendor for this device. Nothing is sent when the app gives none (host names often hold a person's name). */
|
|
112
117
|
activate(licenseKey: string, machineName?: string): Promise<LicenseState>;
|
|
113
118
|
check(): Promise<LicenseState>;
|
|
119
|
+
/** The state from the saved record when the server gave no usable answer; a usable lease moves lastSeen forward. */
|
|
120
|
+
private offlineCheck;
|
|
114
121
|
/**
|
|
115
122
|
* Decrypts data you encrypted for a feature (encryptForFeature in @carchalabs/seal-core, with the
|
|
116
123
|
* feature's content key from the console). Works offline: it uses the saved lease, which must be valid
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,12 @@ const MAX_COMPONENTS = 32;
|
|
|
41
41
|
* re-activate), so the key is kept and the next successful online check restores the license on its own. */
|
|
42
42
|
const FORGET_KEY = new Set(['invalid_key', 'license_revoked']);
|
|
43
43
|
const CLOCK_TOLERANCE_SECONDS = 24 * 3600;
|
|
44
|
+
/**
|
|
45
|
+
* A check-in answered 2xx whose lease fails for one of these reasons did not come from this app's license server for
|
|
46
|
+
* this device (a TLS-inspecting proxy's or captive portal's page, say): it is treated as no answer, and the saved lease
|
|
47
|
+
* is kept. A lease that fails only on time (not_yet_valid, expired, revalidation_required) is the server's, and is used.
|
|
48
|
+
*/
|
|
49
|
+
const NOT_A_LEASE_FOR_THIS_DEVICE = new Set(['malformed', 'unsupported_algorithm', 'wrong_type', 'unknown_key', 'bad_signature', 'wrong_issuer', 'wrong_product', 'wrong_device']);
|
|
44
50
|
const DEFAULT_GRACE_DAYS = 7;
|
|
45
51
|
const CLOCK_MESSAGE = 'The date and time on this computer look wrong. Correct them, then connect to the internet.';
|
|
46
52
|
class LicenseError extends Error {
|
|
@@ -54,6 +60,26 @@ class LicenseError extends Error {
|
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
exports.LicenseError = LicenseError;
|
|
63
|
+
/**
|
|
64
|
+
* Refuses a server address that would send requests in plain text (OWASP ASVS 4.1.2, 12.2.1): every request
|
|
65
|
+
* carries the license key and the device's fingerprint. http:// is accepted for this computer only, where a server
|
|
66
|
+
* under development runs; `localhost` and the loopback addresses do not leave the machine.
|
|
67
|
+
*/
|
|
68
|
+
function checkServerUrl(serverUrl) {
|
|
69
|
+
let url;
|
|
70
|
+
try {
|
|
71
|
+
url = new URL(serverUrl);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new LicenseError('invalid_server_url', 'serverUrl is not a valid URL.');
|
|
75
|
+
}
|
|
76
|
+
if (url.protocol === 'https:')
|
|
77
|
+
return;
|
|
78
|
+
const loopback = url.hostname === 'localhost' || url.hostname === '[::1]' || /^127(\.\d{1,3}){3}$/.test(url.hostname);
|
|
79
|
+
if (url.protocol === 'http:' && loopback)
|
|
80
|
+
return;
|
|
81
|
+
throw new LicenseError('insecure_server_url', 'serverUrl must start with https:// (http:// only for localhost).');
|
|
82
|
+
}
|
|
57
83
|
const REASON_TEXT = {
|
|
58
84
|
malformed: 'The saved license is damaged. Please activate again.',
|
|
59
85
|
bad_signature: 'The saved license was modified. Please activate again.',
|
|
@@ -68,6 +94,27 @@ const REASON_TEXT = {
|
|
|
68
94
|
revalidation_required: 'Connect to the internet to confirm your license.',
|
|
69
95
|
};
|
|
70
96
|
const toIso = (seconds) => new Date(seconds * 1000).toISOString();
|
|
97
|
+
/** A saved record of the shape save() writes, keeping only its known fields; null for anything else. */
|
|
98
|
+
function parseStored(value) {
|
|
99
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
100
|
+
return null;
|
|
101
|
+
const v = value;
|
|
102
|
+
if (typeof v.licenseKey !== 'string')
|
|
103
|
+
return null;
|
|
104
|
+
const stored = { licenseKey: v.licenseKey };
|
|
105
|
+
if (typeof v.token === 'string')
|
|
106
|
+
stored.token = v.token;
|
|
107
|
+
if (typeof v.lastSeen === 'number' && Number.isFinite(v.lastSeen))
|
|
108
|
+
stored.lastSeen = v.lastSeen;
|
|
109
|
+
const r = v.refused;
|
|
110
|
+
if (r && typeof r.code === 'string' && typeof r.message === 'string' && typeof r.at === 'number')
|
|
111
|
+
stored.refused = { code: r.code, message: r.message, at: r.at };
|
|
112
|
+
if (Array.isArray(v.notices))
|
|
113
|
+
stored.notices = v.notices.filter((n) => !!n && typeof n.code === 'string');
|
|
114
|
+
if (v.offline === true)
|
|
115
|
+
stored.offline = true;
|
|
116
|
+
return stored;
|
|
117
|
+
}
|
|
71
118
|
function noticesFrom(response) {
|
|
72
119
|
const list = Array.isArray(response?.notices) ? response.notices.filter((n) => n && typeof n.code === 'string') : [];
|
|
73
120
|
return list.length ? { notices: list } : {};
|
|
@@ -83,6 +130,7 @@ class Seal {
|
|
|
83
130
|
deviceKeyCache;
|
|
84
131
|
constructor(opts) {
|
|
85
132
|
this.opts = opts;
|
|
133
|
+
checkServerUrl(opts.serverUrl);
|
|
86
134
|
this.file = (0, node_path_1.join)(opts.storageDir, 'license.dat');
|
|
87
135
|
this.keys = (0, seal_core_1.keySet)(Array.isArray(opts.publicKey) ? opts.publicKey : [opts.publicKey]);
|
|
88
136
|
}
|
|
@@ -132,20 +180,31 @@ class Seal {
|
|
|
132
180
|
})();
|
|
133
181
|
return this.hardwareCache.then((report) => report && report.components.length ? { components: report.components.slice(0, MAX_COMPONENTS), vm: report.vm === true } : {});
|
|
134
182
|
}
|
|
183
|
+
/** The saved record; null when there is none, or it cannot be read or is not a record (as no license). */
|
|
135
184
|
async load() {
|
|
136
185
|
try {
|
|
137
186
|
const buf = await (0, promises_1.readFile)(this.file);
|
|
138
187
|
const text = this.opts.secureStore ? await this.opts.secureStore.decrypt(buf) : buf.toString('utf8');
|
|
139
|
-
return JSON.parse(text);
|
|
188
|
+
return parseStored(JSON.parse(text));
|
|
140
189
|
}
|
|
141
190
|
catch {
|
|
142
191
|
return null;
|
|
143
192
|
}
|
|
144
193
|
}
|
|
194
|
+
/** Replaces license.dat whole: written to license.dat.tmp, then renamed over it, so a crash or full disk mid-write
|
|
195
|
+
* leaves the previous record. */
|
|
145
196
|
async save(stored) {
|
|
146
197
|
await (0, promises_1.mkdir)(this.opts.storageDir, { recursive: true });
|
|
147
198
|
const text = JSON.stringify(stored);
|
|
148
|
-
|
|
199
|
+
const temp = this.file + '.tmp';
|
|
200
|
+
try {
|
|
201
|
+
await (0, promises_1.writeFile)(temp, this.opts.secureStore ? await this.opts.secureStore.encrypt(text) : text, { mode: 0o600, flush: true });
|
|
202
|
+
await (0, promises_1.rename)(temp, this.file);
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
await (0, promises_1.rm)(temp, { force: true }).catch(() => { });
|
|
206
|
+
throw e;
|
|
207
|
+
}
|
|
149
208
|
}
|
|
150
209
|
async clear() {
|
|
151
210
|
await (0, promises_1.rm)(this.file, { force: true });
|
|
@@ -157,6 +216,9 @@ class Seal {
|
|
|
157
216
|
method: 'POST',
|
|
158
217
|
headers: { 'Content-Type': 'application/json' },
|
|
159
218
|
body: JSON.stringify(body),
|
|
219
|
+
// A redirect is refused, not followed: it could take the request, license key included, to another
|
|
220
|
+
// address or to http:// (ASVS 4.1.2). The server answers its API at the configured address.
|
|
221
|
+
redirect: 'error',
|
|
160
222
|
signal: AbortSignal.timeout(this.opts.requestTimeoutMs ?? 8000),
|
|
161
223
|
});
|
|
162
224
|
}
|
|
@@ -271,14 +333,15 @@ class Seal {
|
|
|
271
333
|
}
|
|
272
334
|
return this.emptyState('invalid', offline, { reason: strict.reason, message: REASON_TEXT[strict.reason], keyHint });
|
|
273
335
|
}
|
|
274
|
-
|
|
336
|
+
/** machineName: the name to show the vendor for this device. Nothing is sent when the app gives none (host names often hold a person's name). */
|
|
337
|
+
async activate(licenseKey, machineName) {
|
|
275
338
|
const fingerprint = await this.fingerprint();
|
|
276
339
|
const key = licenseKey.trim().toUpperCase();
|
|
277
340
|
const response = await this.post('/v1/activate', {
|
|
278
341
|
licenseKey: key,
|
|
279
342
|
product: this.opts.product,
|
|
280
343
|
fingerprint,
|
|
281
|
-
machineName: machineName
|
|
344
|
+
machineName: machineName?.slice(0, 80),
|
|
282
345
|
platform: (0, node_os_1.platform)(),
|
|
283
346
|
appVersion: this.opts.appVersion,
|
|
284
347
|
sdkVersion: exports.SDK_VERSION,
|
|
@@ -313,8 +376,9 @@ class Seal {
|
|
|
313
376
|
}
|
|
314
377
|
return state;
|
|
315
378
|
}
|
|
379
|
+
let response;
|
|
316
380
|
try {
|
|
317
|
-
|
|
381
|
+
response = await this.post('/v1/validate', {
|
|
318
382
|
licenseKey: stored.licenseKey,
|
|
319
383
|
product: this.opts.product,
|
|
320
384
|
fingerprint: this.fingerprintCache,
|
|
@@ -323,30 +387,42 @@ class Seal {
|
|
|
323
387
|
...(await this.hardware()),
|
|
324
388
|
...this.deviceKey(),
|
|
325
389
|
});
|
|
326
|
-
const fresh = { licenseKey: stored.licenseKey, token: response.token, lastSeen: this.nowSec(), ...noticesFrom(response) };
|
|
327
|
-
await this.save(fresh);
|
|
328
|
-
return this.stateFromLease(fresh, false);
|
|
329
390
|
}
|
|
330
391
|
catch (e) {
|
|
331
|
-
const keyHint = `${stored.licenseKey.slice(0, 7)}…${stored.licenseKey.slice(-4)}`;
|
|
332
392
|
if (e instanceof LicenseError && DEFINITIVE.has(e.code)) {
|
|
333
393
|
// Persist the refusal: drop the signed lease so going offline cannot bring the license back.
|
|
334
394
|
if (FORGET_KEY.has(e.code))
|
|
335
395
|
await this.clear();
|
|
336
396
|
else
|
|
337
397
|
await this.save({ licenseKey: stored.licenseKey, lastSeen: stored.lastSeen, refused: { code: e.code, message: e.message, at: this.nowSec() } });
|
|
398
|
+
const keyHint = `${stored.licenseKey.slice(0, 7)}…${stored.licenseKey.slice(-4)}`;
|
|
338
399
|
return this.emptyState('invalid', false, { reason: e.code, message: e.message, keyHint });
|
|
339
400
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
const state = this.stateFromLease(
|
|
345
|
-
if (state.status === '
|
|
346
|
-
await this.save(
|
|
401
|
+
return this.offlineCheck(stored);
|
|
402
|
+
}
|
|
403
|
+
if (typeof response?.token === 'string') {
|
|
404
|
+
const fresh = { licenseKey: stored.licenseKey, token: response.token, lastSeen: this.nowSec(), ...noticesFrom(response) };
|
|
405
|
+
const state = this.stateFromLease(fresh, false);
|
|
406
|
+
if (!(state.status === 'invalid' && NOT_A_LEASE_FOR_THIS_DEVICE.has(state.reason ?? ''))) {
|
|
407
|
+
await this.save(fresh);
|
|
408
|
+
return state;
|
|
347
409
|
}
|
|
348
|
-
return state;
|
|
349
410
|
}
|
|
411
|
+
// A 2xx without a lease from this server for this device: as no answer.
|
|
412
|
+
return this.offlineCheck(stored);
|
|
413
|
+
}
|
|
414
|
+
/** The state from the saved record when the server gave no usable answer; a usable lease moves lastSeen forward. */
|
|
415
|
+
async offlineCheck(stored) {
|
|
416
|
+
if (!stored.token) {
|
|
417
|
+
const keyHint = `${stored.licenseKey.slice(0, 7)}…${stored.licenseKey.slice(-4)}`;
|
|
418
|
+
const refused = stored.refused ?? { code: 'not_activated', message: 'This device is not activated for this license' };
|
|
419
|
+
return this.emptyState('invalid', true, { reason: refused.code, message: refused.message, keyHint });
|
|
420
|
+
}
|
|
421
|
+
const state = this.stateFromLease({ ...stored, token: stored.token }, true);
|
|
422
|
+
if (state.status === 'valid' || state.status === 'grace') {
|
|
423
|
+
await this.save({ ...stored, lastSeen: Math.max(stored.lastSeen ?? 0, this.nowSec()) });
|
|
424
|
+
}
|
|
425
|
+
return state;
|
|
350
426
|
}
|
|
351
427
|
/**
|
|
352
428
|
* Decrypts data you encrypted for a feature (encryptForFeature in @carchalabs/seal-core, with the
|
|
@@ -442,13 +518,13 @@ class Seal {
|
|
|
442
518
|
* POST /v1/offline/activate (or its offline activation page). It holds what activate() would send:
|
|
443
519
|
* the license key, this device's fingerprint and hashed hardware parts, never raw serial numbers.
|
|
444
520
|
*/
|
|
445
|
-
async offlineRequest(licenseKey, machineName
|
|
521
|
+
async offlineRequest(licenseKey, machineName) {
|
|
446
522
|
const fingerprint = await this.fingerprint();
|
|
447
523
|
const activation = {
|
|
448
524
|
licenseKey: licenseKey.trim().toUpperCase(),
|
|
449
525
|
product: this.opts.product,
|
|
450
526
|
fingerprint,
|
|
451
|
-
machineName: machineName
|
|
527
|
+
machineName: machineName?.slice(0, 80),
|
|
452
528
|
platform: (0, node_os_1.platform)(),
|
|
453
529
|
appVersion: this.opts.appVersion,
|
|
454
530
|
sdkVersion: exports.SDK_VERSION,
|
|
@@ -521,4 +597,3 @@ class Seal {
|
|
|
521
597
|
}
|
|
522
598
|
exports.Seal = Seal;
|
|
523
599
|
exports.LicenseKit = Seal;
|
|
524
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carchalabs/seal-electron",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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/",
|
|
7
7
|
"repository": { "type": "git", "url": "git+https://github.com/carcha-labs/carcha-seal-sdk.git", "directory": "packages/sdk-electron" },
|
|
8
|
-
"bugs": { "
|
|
8
|
+
"bugs": { "email": "bugra@carchalabs.com" },
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
|
-
"files": ["dist", "
|
|
11
|
+
"files": ["dist", "!dist/**/*.test.*", "!dist/**/*.map"],
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsc -p tsconfig.json",
|
|
14
14
|
"prepublishOnly": "npm run build",
|
|
15
15
|
"test": "node --no-warnings=ExperimentalWarning --test dist/*.test.js"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@carchalabs/seal-core": "0.
|
|
18
|
+
"@carchalabs/seal-core": "0.5.1"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@carchalabs/seal-hwid": "0.
|
|
21
|
+
"@carchalabs/seal-hwid": "0.4.1"
|
|
22
22
|
},
|
|
23
23
|
"engines": { "node": ">=22.13" },
|
|
24
24
|
"publishConfig": { "access": "public" },
|
|
25
|
-
"license": "
|
|
25
|
+
"license": "SEE LICENSE IN LICENSE"
|
|
26
26
|
}
|
package/dist/fingerprint.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../src/fingerprint.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/src/fingerprint.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { cpus, hostname, networkInterfaces, platform } from 'node:os';
|
|
4
|
-
import { win32 } from 'node:path';
|
|
5
|
-
import { promisify } from 'node:util';
|
|
6
|
-
|
|
7
|
-
const execFileAsync = promisify(execFile);
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The OS tools are run by absolute path, never looked up through PATH: an app started from a terminal inherits the
|
|
11
|
-
* user's PATH, and a fake `ioreg` or `reg` placed first in it could report another computer's id.
|
|
12
|
-
*/
|
|
13
|
-
export const IOREG = '/usr/sbin/ioreg';
|
|
14
|
-
export const REG_EXE = [
|
|
15
|
-
'C:\\Windows\\System32\\reg.exe',
|
|
16
|
-
win32.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'reg.exe'),
|
|
17
|
-
].filter((file, index, all) => all.indexOf(file) === index);
|
|
18
|
-
export const LINUX_ID_FILES = ['/etc/machine-id', '/var/lib/dbus/machine-id'];
|
|
19
|
-
|
|
20
|
-
export interface MachineIdDeps {
|
|
21
|
-
platform?: NodeJS.Platform;
|
|
22
|
-
/** Attempts to read the OS id before giving up (default 3). */
|
|
23
|
-
attempts?: number;
|
|
24
|
-
/** Pause between attempts (default 250 ms, doubled each time). */
|
|
25
|
-
delayMs?: number;
|
|
26
|
-
/**
|
|
27
|
-
* Use hostname + CPU + MAC when the OS id cannot be read. Off by default: that value differs from the OS id, so the
|
|
28
|
-
* same computer would look like a new device and take another seat.
|
|
29
|
-
*/
|
|
30
|
-
allowFallback?: boolean;
|
|
31
|
-
run?: (file: string, args: string[], options: { timeout: number; windowsHide: boolean }) => Promise<{ stdout: string }>;
|
|
32
|
-
readText?: (file: string) => Promise<string>;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** The OS id could not be read; licensing on this computer cannot continue until it can. */
|
|
36
|
-
export class MachineIdUnavailableError extends Error {
|
|
37
|
-
readonly code = 'machine_id_unavailable';
|
|
38
|
-
constructor() {
|
|
39
|
-
super('This computer\'s id could not be read. Try again; if it keeps failing, contact the app\'s support.');
|
|
40
|
-
this.name = 'MachineIdUnavailableError';
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Reads a stable, OS-provided machine id, retrying a few times. Throws MachineIdUnavailableError unless allowFallback. */
|
|
45
|
-
export async function readMachineId(deps: MachineIdDeps = {}): Promise<string> {
|
|
46
|
-
const attempts = Math.max(1, deps.attempts ?? 3);
|
|
47
|
-
let delay = deps.delayMs ?? 250;
|
|
48
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
49
|
-
const id = await readOsMachineId(deps);
|
|
50
|
-
if (id) return id;
|
|
51
|
-
if (attempt < attempts) {
|
|
52
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
53
|
-
delay *= 2;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (deps.allowFallback) return `fallback:${fallbackTraits()}`;
|
|
57
|
-
throw new MachineIdUnavailableError();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** One attempt; null when the OS id is not available. */
|
|
61
|
-
async function readOsMachineId(deps: MachineIdDeps): Promise<string | null> {
|
|
62
|
-
const os = deps.platform ?? platform();
|
|
63
|
-
const run = deps.run ?? ((file, args, options) => execFileAsync(file, args, options));
|
|
64
|
-
const readText = deps.readText ?? ((file) => readFile(file, 'utf8'));
|
|
65
|
-
try {
|
|
66
|
-
if (os === 'darwin') {
|
|
67
|
-
const { stdout } = await run(IOREG, ['-rd1', '-c', 'IOPlatformExpertDevice'], { timeout: 3000, windowsHide: true });
|
|
68
|
-
const m = stdout.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
69
|
-
if (m) return `mac:${m[1]}`;
|
|
70
|
-
} else if (os === 'win32') {
|
|
71
|
-
for (const reg of REG_EXE) {
|
|
72
|
-
try {
|
|
73
|
-
// /reg:64 reads the 64-bit registry view, so a 32-bit build sees the same MachineGuid.
|
|
74
|
-
const { stdout } = await run(reg, ['query', 'HKLM\\SOFTWARE\\Microsoft\\Cryptography', '/v', 'MachineGuid', '/reg:64'], {
|
|
75
|
-
timeout: 3000,
|
|
76
|
-
windowsHide: true,
|
|
77
|
-
});
|
|
78
|
-
const m = stdout.match(/MachineGuid\s+REG_SZ\s+([\w-]+)/i);
|
|
79
|
-
if (m) return `win:${m[1]}`;
|
|
80
|
-
} catch {
|
|
81
|
-
/* try the next location */
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
for (const f of LINUX_ID_FILES) {
|
|
86
|
-
const id = (await readText(f).catch(() => '')).trim();
|
|
87
|
-
if (id) return `linux:${id}`;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch {
|
|
91
|
-
/* fall through */
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function fallbackTraits(): string {
|
|
97
|
-
const macs = Object.values(networkInterfaces())
|
|
98
|
-
.flat()
|
|
99
|
-
.filter((i) => i && !i.internal && i.mac && i.mac !== '00:00:00:00:00:00')
|
|
100
|
-
.map((i) => i!.mac)
|
|
101
|
-
.sort();
|
|
102
|
-
return [hostname(), cpus()[0]?.model ?? '', macs[0] ?? ''].join('|');
|
|
103
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,623 +0,0 @@
|
|
|
1
|
-
import { KeyObject } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { createRequire } from 'node:module';
|
|
5
|
-
import { hostname, platform } from 'node:os';
|
|
6
|
-
import { dirname, join, resolve } from 'node:path';
|
|
7
|
-
import { compileFunction } from 'node:vm';
|
|
8
|
-
import { decodeLease, decryptFeature, hashFingerprint, keySet, LeaseClaims, LeaseFailure, LeaseVerifyOptions, LeaseVerifyResult, verifyLease } from '@carchalabs/seal-core';
|
|
9
|
-
import { MachineIdUnavailableError, readMachineId } from './fingerprint';
|
|
10
|
-
|
|
11
|
-
export { MachineIdUnavailableError, readMachineId };
|
|
12
|
-
|
|
13
|
-
/** This SDK's version, sent to the server so vendors can see (and require) which SDK each device runs. */
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
15
|
-
export const SDK_VERSION: string = require('../package.json').version;
|
|
16
|
-
|
|
17
|
-
export type LicenseStatus = 'valid' | 'grace' | 'invalid' | 'unlicensed' | 'clock_suspect';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Something the server asks the app to act on: sdk_update_recommended (update the Carcha Seal SDK in the app) or
|
|
21
|
-
* app_update_recommended (the vendor's minimum app version), each with minimum and requiredFrom.
|
|
22
|
-
*/
|
|
23
|
-
export interface LicenseNotice {
|
|
24
|
-
code: string;
|
|
25
|
-
minimum?: string;
|
|
26
|
-
/** ISO date from which an older SDK is refused; null when the update is only recommended. */
|
|
27
|
-
requiredFrom?: string | null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface LicenseState {
|
|
31
|
-
status: LicenseStatus;
|
|
32
|
-
offline: boolean;
|
|
33
|
-
storageProtected: boolean;
|
|
34
|
-
reason?: string;
|
|
35
|
-
message?: string;
|
|
36
|
-
customer?: string;
|
|
37
|
-
features: string[];
|
|
38
|
-
expiresAt: string | null;
|
|
39
|
-
maintenanceUntil?: string | null;
|
|
40
|
-
revalidateBy?: string;
|
|
41
|
-
keyHint?: string;
|
|
42
|
-
/** From the last successful check-in; kept while offline. The app decides how to show them. */
|
|
43
|
-
notices?: LicenseNotice[];
|
|
44
|
-
/** True when the license came from an offline license file (activateOffline); renew it with a new file. */
|
|
45
|
-
offlineActivated?: boolean;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Hashed hardware components (see @carchalabs/seal-hwid); the server matches a device by them. */
|
|
49
|
-
export interface HardwareReport {
|
|
50
|
-
components: { kind: string; hash: string }[];
|
|
51
|
-
/** True when the device runs as a virtual machine. */
|
|
52
|
-
vm: boolean;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface SecureStore {
|
|
56
|
-
encrypt(plain: string): Buffer | Promise<Buffer>;
|
|
57
|
-
decrypt(cipher: Buffer): string | Promise<string>;
|
|
58
|
-
isProtected?(): boolean;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface SealOptions {
|
|
62
|
-
serverUrl: string;
|
|
63
|
-
product: string;
|
|
64
|
-
publicKey: string | string[];
|
|
65
|
-
storageDir: string;
|
|
66
|
-
appVersion?: string;
|
|
67
|
-
graceDays?: number;
|
|
68
|
-
secureStore?: SecureStore;
|
|
69
|
-
requestTimeoutMs?: number;
|
|
70
|
-
fetchImpl?: typeof fetch;
|
|
71
|
-
machineId?: () => Promise<string>;
|
|
72
|
-
/** Use hostname + CPU + MAC when the OS machine id cannot be read (default false; see readMachineId). */
|
|
73
|
-
allowFallbackFingerprint?: boolean;
|
|
74
|
-
/**
|
|
75
|
-
* Hardware components sent with each request, so the device keeps its seat after a new disk, network
|
|
76
|
-
* card or OS install. Default: read with @carchalabs/seal-hwid when it is installed; nothing is
|
|
77
|
-
* sent when it is not (or cannot load on this platform). false: never send them. A function: your own.
|
|
78
|
-
*/
|
|
79
|
-
hardware?: false | (() => HardwareReport | null | Promise<HardwareReport | null>);
|
|
80
|
-
now?: () => number;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface Stored {
|
|
84
|
-
licenseKey: string;
|
|
85
|
-
/** Signed lease. Absent after the server refused this device: nothing is left to verify offline. */
|
|
86
|
-
token?: string;
|
|
87
|
-
lastSeen?: number;
|
|
88
|
-
/** The server's last definitive refusal, kept so an offline check reports it instead of an old lease. */
|
|
89
|
-
refused?: { code: string; message: string; at: number };
|
|
90
|
-
notices?: LicenseNotice[];
|
|
91
|
-
/** Activated from an offline license file: check() never calls the server; a new file renews it. */
|
|
92
|
-
offline?: boolean;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
type Leased = Stored & { token: string };
|
|
96
|
-
|
|
97
|
-
/** File formats of offline activation, shared with the license server. */
|
|
98
|
-
const OFFLINE_REQUEST_FORMAT = 'licensekit-offline-request';
|
|
99
|
-
const OFFLINE_LICENSE_FORMAT = 'licensekit-offline-license';
|
|
100
|
-
const OFFLINE_DEACTIVATION_FORMAT = 'licensekit-offline-deactivation';
|
|
101
|
-
|
|
102
|
-
const DEFINITIVE = new Set(['invalid_key', 'not_activated', 'license_suspended', 'license_revoked', 'license_expired', 'sdk_update_required', 'app_update_required', 'vm_not_allowed']);
|
|
103
|
-
/** The native hardware module. A non-literal require: bundlers leave it to Node, which must load the .node file from disk. */
|
|
104
|
-
const HWID_PACKAGE = '@carchalabs/seal-hwid';
|
|
105
|
-
/** The native module's lease verifier (@carchalabs/seal-hwid 0.2+): its keys are compiled in. */
|
|
106
|
-
interface NativeLease {
|
|
107
|
-
verifyLease(token: string, check: { product: string; fingerprintHash: string; now: number; graceSeconds?: number }): { ok: boolean; reason?: string };
|
|
108
|
-
leaseKeyIds(): string[];
|
|
109
|
-
/** This machine's X25519 public key from its hardware (base64url), or null. */
|
|
110
|
-
devicePublicKey?(key: string): string | null;
|
|
111
|
-
/** @carchalabs/seal-hwid 0.2+ with feature keys. */
|
|
112
|
-
decryptFeature?(
|
|
113
|
-
token: string,
|
|
114
|
-
check: { product: string; fingerprintHash: string; now: number; graceSeconds?: number },
|
|
115
|
-
licenseKey: string,
|
|
116
|
-
feature: string,
|
|
117
|
-
data: Buffer,
|
|
118
|
-
): { ok: boolean; data?: Buffer; reason?: string };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Loads the native module once per process; null when it is not installed or has no build for this platform. */
|
|
122
|
-
let hwidModule: any;
|
|
123
|
-
function loadHwid(): any {
|
|
124
|
-
if (hwidModule === undefined) {
|
|
125
|
-
try {
|
|
126
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
127
|
-
hwidModule = require(HWID_PACKAGE);
|
|
128
|
-
} catch {
|
|
129
|
-
hwidModule = null;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return hwidModule;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** The server accepts at most this many components. */
|
|
136
|
-
const MAX_COMPONENTS = 32;
|
|
137
|
-
/** The license itself is gone: forget the key too. The other refusals can be undone on the server (resume, renew,
|
|
138
|
-
* re-activate), so the key is kept and the next successful online check restores the license on its own. */
|
|
139
|
-
const FORGET_KEY = new Set(['invalid_key', 'license_revoked']);
|
|
140
|
-
const CLOCK_TOLERANCE_SECONDS = 24 * 3600;
|
|
141
|
-
const DEFAULT_GRACE_DAYS = 7;
|
|
142
|
-
const CLOCK_MESSAGE = 'The date and time on this computer look wrong. Correct them, then connect to the internet.';
|
|
143
|
-
|
|
144
|
-
export class LicenseError extends Error {
|
|
145
|
-
constructor(
|
|
146
|
-
public readonly code: string,
|
|
147
|
-
message: string,
|
|
148
|
-
public readonly status?: number,
|
|
149
|
-
) {
|
|
150
|
-
super(message);
|
|
151
|
-
this.name = 'LicenseError';
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const REASON_TEXT: Record<LeaseFailure, string> = {
|
|
156
|
-
malformed: 'The saved license is damaged. Please activate again.',
|
|
157
|
-
bad_signature: 'The saved license was modified. Please activate again.',
|
|
158
|
-
unsupported_algorithm: 'Please update the app.',
|
|
159
|
-
wrong_type: 'Please update the app.',
|
|
160
|
-
unknown_key: 'Please update the app.',
|
|
161
|
-
wrong_issuer: 'This license was issued by a different license server.',
|
|
162
|
-
not_yet_valid: CLOCK_MESSAGE,
|
|
163
|
-
wrong_product: 'This license is for a different product.',
|
|
164
|
-
wrong_device: 'This license is activated on another device.',
|
|
165
|
-
expired: 'Your license has expired.',
|
|
166
|
-
revalidation_required: 'Connect to the internet to confirm your license.',
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const toIso = (seconds: number) => new Date(seconds * 1000).toISOString();
|
|
170
|
-
|
|
171
|
-
function noticesFrom(response: any): { notices?: LicenseNotice[] } {
|
|
172
|
-
const list = Array.isArray(response?.notices) ? response.notices.filter((n: any) => n && typeof n.code === 'string') : [];
|
|
173
|
-
return list.length ? { notices: list } : {};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export class Seal {
|
|
177
|
-
private readonly file: string;
|
|
178
|
-
private readonly keys: Map<string, KeyObject>;
|
|
179
|
-
private fingerprintCache?: string;
|
|
180
|
-
private hardwareCache?: Promise<HardwareReport | null>;
|
|
181
|
-
/** The native lease verifier, when it trusts every key this app was given (undefined = not looked up yet). */
|
|
182
|
-
private nativeLease?: NativeLease | null;
|
|
183
|
-
private deviceKeyCache?: string | null;
|
|
184
|
-
|
|
185
|
-
constructor(private readonly opts: SealOptions) {
|
|
186
|
-
this.file = join(opts.storageDir, 'license.dat');
|
|
187
|
-
this.keys = keySet(Array.isArray(opts.publicKey) ? opts.publicKey : [opts.publicKey]);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private get fetch() {
|
|
191
|
-
return this.opts.fetchImpl ?? fetch;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private nowSec() {
|
|
195
|
-
return Math.floor((this.opts.now?.() ?? Date.now()) / 1000);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private get storageProtected(): boolean {
|
|
199
|
-
const store = this.opts.secureStore;
|
|
200
|
-
return store !== undefined && (store.isProtected?.() ?? true);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async fingerprint(): Promise<string> {
|
|
204
|
-
if (!this.fingerprintCache) {
|
|
205
|
-
let raw: string;
|
|
206
|
-
try {
|
|
207
|
-
raw = await (this.opts.machineId ?? (() => readMachineId({ allowFallback: this.opts.allowFallbackFingerprint })))();
|
|
208
|
-
} catch (e) {
|
|
209
|
-
if (e instanceof MachineIdUnavailableError) throw new LicenseError(e.code, e.message);
|
|
210
|
-
throw e;
|
|
211
|
-
}
|
|
212
|
-
this.fingerprintCache = hashFingerprint(raw, this.opts.product);
|
|
213
|
-
}
|
|
214
|
-
return this.fingerprintCache;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** Read once per instance: hardware does not change while the app runs. Never throws. */
|
|
218
|
-
private hardware(): Promise<{ components?: HardwareReport['components']; vm?: boolean }> {
|
|
219
|
-
this.hardwareCache ??= (async () => {
|
|
220
|
-
const source = this.opts.hardware;
|
|
221
|
-
if (source === false) return null;
|
|
222
|
-
try {
|
|
223
|
-
if (source) return await source();
|
|
224
|
-
const hwid = loadHwid();
|
|
225
|
-
if (!hwid) return null;
|
|
226
|
-
const { components, vm } = hwid.collect(`licensekit:${this.opts.product}`);
|
|
227
|
-
return { components, vm };
|
|
228
|
-
} catch {
|
|
229
|
-
return null; // not installed, or no module for this platform: send nothing, as before
|
|
230
|
-
}
|
|
231
|
-
})();
|
|
232
|
-
return this.hardwareCache.then((report) =>
|
|
233
|
-
report && report.components.length ? { components: report.components.slice(0, MAX_COMPONENTS), vm: report.vm === true } : {},
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private async load(): Promise<Stored | null> {
|
|
238
|
-
try {
|
|
239
|
-
const buf = await readFile(this.file);
|
|
240
|
-
const text = this.opts.secureStore ? await this.opts.secureStore.decrypt(buf) : buf.toString('utf8');
|
|
241
|
-
return JSON.parse(text);
|
|
242
|
-
} catch {
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private async save(stored: Stored) {
|
|
248
|
-
await mkdir(this.opts.storageDir, { recursive: true });
|
|
249
|
-
const text = JSON.stringify(stored);
|
|
250
|
-
await writeFile(this.file, this.opts.secureStore ? await this.opts.secureStore.encrypt(text) : text, { mode: 0o600 });
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
private async clear() {
|
|
254
|
-
await rm(this.file, { force: true });
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
private async post(path: string, body: Record<string, unknown>): Promise<any> {
|
|
258
|
-
let res: Response;
|
|
259
|
-
try {
|
|
260
|
-
res = await this.fetch(this.opts.serverUrl.replace(/\/+$/, '') + path, {
|
|
261
|
-
method: 'POST',
|
|
262
|
-
headers: { 'Content-Type': 'application/json' },
|
|
263
|
-
body: JSON.stringify(body),
|
|
264
|
-
signal: AbortSignal.timeout(this.opts.requestTimeoutMs ?? 8000),
|
|
265
|
-
});
|
|
266
|
-
} catch {
|
|
267
|
-
throw new LicenseError('network', 'Could not reach the license server.');
|
|
268
|
-
}
|
|
269
|
-
const json: any = await res.json().catch(() => ({}));
|
|
270
|
-
if (!res.ok) throw new LicenseError(json.error ?? 'server_error', json.message ?? `License server error (${res.status})`, res.status);
|
|
271
|
-
return json;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private emptyState(status: LicenseStatus, offline: boolean, extra: Partial<LicenseState> = {}): LicenseState {
|
|
275
|
-
return { status, offline, storageProtected: this.storageProtected, features: [], expiresAt: null, ...extra };
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* The device's public key, sent with activation and check-ins so the server wraps feature keys for
|
|
280
|
-
* this machine only. Only when the native module verifies for this app (it then also decrypts, with
|
|
281
|
-
* the matching secret it derives itself) and hardware is not turned off. Never throws.
|
|
282
|
-
*/
|
|
283
|
-
private deviceKey(): { devicePublicKey?: string } {
|
|
284
|
-
if (this.deviceKeyCache === undefined) {
|
|
285
|
-
let key: string | null = null;
|
|
286
|
-
try {
|
|
287
|
-
const native = this.opts.hardware === false ? null : this.native();
|
|
288
|
-
if (native && typeof native.devicePublicKey === 'function' && typeof native.decryptFeature === 'function') {
|
|
289
|
-
key = native.devicePublicKey(`licensekit:${this.opts.product}`);
|
|
290
|
-
}
|
|
291
|
-
} catch {
|
|
292
|
-
key = null;
|
|
293
|
-
}
|
|
294
|
-
this.deviceKeyCache = key;
|
|
295
|
-
}
|
|
296
|
-
return this.deviceKeyCache ? { devicePublicKey: this.deviceKeyCache } : {};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* The native verifier, used only when its compiled-in keys include every key this app was given:
|
|
301
|
-
* an app that trusts other keys (a self-hosted server) is checked by the JavaScript verifier alone.
|
|
302
|
-
*/
|
|
303
|
-
private native(): NativeLease | null {
|
|
304
|
-
if (this.nativeLease === undefined) {
|
|
305
|
-
const hwid = loadHwid();
|
|
306
|
-
let native: NativeLease | null = null;
|
|
307
|
-
if (hwid && typeof hwid.verifyLease === 'function' && typeof hwid.leaseKeyIds === 'function') {
|
|
308
|
-
const trusted = new Set<string>(hwid.leaseKeyIds());
|
|
309
|
-
if ([...this.keys.keys()].every((kid) => trusted.has(kid))) native = hwid;
|
|
310
|
-
}
|
|
311
|
-
this.nativeLease = native;
|
|
312
|
-
}
|
|
313
|
-
return this.nativeLease;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Verifies a lease twice, in JavaScript and in the native module, when the native verifier applies.
|
|
318
|
-
* Both follow the same rules, so a different answer means one of them was tampered with: the lease
|
|
319
|
-
* is then treated as modified.
|
|
320
|
-
*/
|
|
321
|
-
private verify(token: string, options: LeaseVerifyOptions): LeaseVerifyResult {
|
|
322
|
-
const result = verifyLease(token, options);
|
|
323
|
-
const native = this.native();
|
|
324
|
-
if (!native) return result;
|
|
325
|
-
const check = native.verifyLease(token, {
|
|
326
|
-
product: options.product,
|
|
327
|
-
fingerprintHash: options.fingerprintHash,
|
|
328
|
-
now: options.now ?? Math.floor(Date.now() / 1000),
|
|
329
|
-
...(options.graceSeconds !== undefined ? { graceSeconds: options.graceSeconds } : {}),
|
|
330
|
-
});
|
|
331
|
-
const agree = check.ok === result.ok && (result.ok || check.reason === result.reason);
|
|
332
|
-
return agree ? result : { ok: false, reason: 'bad_signature' };
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
private stateFromLease(stored: Leased, offline: boolean): LicenseState {
|
|
336
|
-
const fingerprintHash = this.fingerprintCache!;
|
|
337
|
-
const now = this.nowSec();
|
|
338
|
-
const keyHint = `${stored.licenseKey.slice(0, 7)}…${stored.licenseKey.slice(-4)}`;
|
|
339
|
-
if (offline && stored.lastSeen !== undefined && now < stored.lastSeen - CLOCK_TOLERANCE_SECONDS) {
|
|
340
|
-
return this.emptyState('clock_suspect', offline, { reason: 'clock_rollback', message: CLOCK_MESSAGE, keyHint });
|
|
341
|
-
}
|
|
342
|
-
const verifyOptions = { keys: this.keys, product: this.opts.product, fingerprintHash, now };
|
|
343
|
-
const strict = this.verify(stored.token, verifyOptions);
|
|
344
|
-
const fromClaims = (claims: LeaseClaims) => ({
|
|
345
|
-
customer: claims.cus,
|
|
346
|
-
features: claims.feat,
|
|
347
|
-
expiresAt: claims.exp === undefined ? null : toIso(claims.exp),
|
|
348
|
-
maintenanceUntil: claims.mnt === undefined ? null : toIso(claims.mnt),
|
|
349
|
-
storageProtected: this.storageProtected,
|
|
350
|
-
keyHint,
|
|
351
|
-
...(stored.notices ? { notices: stored.notices } : {}),
|
|
352
|
-
...(stored.offline ? { offlineActivated: true } : {}),
|
|
353
|
-
});
|
|
354
|
-
if (strict.ok) {
|
|
355
|
-
return { status: 'valid', offline, ...fromClaims(strict.claims), revalidateBy: toIso(strict.claims.rev) };
|
|
356
|
-
}
|
|
357
|
-
if (strict.reason === 'not_yet_valid') {
|
|
358
|
-
return this.emptyState('clock_suspect', offline, { reason: 'lease_from_future', message: CLOCK_MESSAGE, keyHint });
|
|
359
|
-
}
|
|
360
|
-
if (strict.reason === 'revalidation_required' && strict.claims) {
|
|
361
|
-
const grace = strict.claims.grc ?? (this.opts.graceDays ?? DEFAULT_GRACE_DAYS) * 86400;
|
|
362
|
-
const lenient = this.verify(stored.token, { ...verifyOptions, graceSeconds: grace });
|
|
363
|
-
if (lenient.ok) {
|
|
364
|
-
return {
|
|
365
|
-
status: 'grace',
|
|
366
|
-
offline,
|
|
367
|
-
reason: 'offline_grace',
|
|
368
|
-
message: stored.offline
|
|
369
|
-
? 'This offline license is past its renewal date. Load a new offline license file soon to keep using the app.'
|
|
370
|
-
: 'Working offline. Connect to the internet soon to keep using the app.',
|
|
371
|
-
...fromClaims(lenient.claims),
|
|
372
|
-
revalidateBy: toIso(Math.min(lenient.claims.rev + grace, lenient.claims.exp ?? Infinity)),
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return this.emptyState('invalid', offline, { reason: strict.reason, message: REASON_TEXT[strict.reason], keyHint });
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
async activate(licenseKey: string, machineName = hostname()): Promise<LicenseState> {
|
|
380
|
-
const fingerprint = await this.fingerprint();
|
|
381
|
-
const key = licenseKey.trim().toUpperCase();
|
|
382
|
-
const response = await this.post('/v1/activate', {
|
|
383
|
-
licenseKey: key,
|
|
384
|
-
product: this.opts.product,
|
|
385
|
-
fingerprint,
|
|
386
|
-
machineName: machineName.slice(0, 80),
|
|
387
|
-
platform: platform(),
|
|
388
|
-
appVersion: this.opts.appVersion,
|
|
389
|
-
sdkVersion: SDK_VERSION,
|
|
390
|
-
...(await this.hardware()),
|
|
391
|
-
...this.deviceKey(),
|
|
392
|
-
});
|
|
393
|
-
const stored: Leased = { licenseKey: key, token: response.token, lastSeen: this.nowSec(), ...noticesFrom(response) };
|
|
394
|
-
const state = this.stateFromLease(stored, false);
|
|
395
|
-
if (state.status !== 'valid') throw new LicenseError(state.status === 'clock_suspect' ? 'clock_suspect' : state.reason ?? 'invalid', state.message ?? 'Server returned an unusable license');
|
|
396
|
-
await this.save(stored);
|
|
397
|
-
return state;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
async check(): Promise<LicenseState> {
|
|
401
|
-
try {
|
|
402
|
-
await this.fingerprint();
|
|
403
|
-
} catch (e) {
|
|
404
|
-
// Nothing is changed on disk: the next check tries to read the id again.
|
|
405
|
-
if (e instanceof LicenseError && e.code === 'machine_id_unavailable') return this.emptyState('invalid', false, { reason: e.code, message: e.message });
|
|
406
|
-
throw e;
|
|
407
|
-
}
|
|
408
|
-
const stored = await this.load();
|
|
409
|
-
if (!stored) return this.emptyState('unlicensed', false);
|
|
410
|
-
if (stored.offline && stored.token) {
|
|
411
|
-
// Activated from an offline license file: the machine is not expected to reach the server.
|
|
412
|
-
const state = this.stateFromLease({ ...stored, token: stored.token }, true);
|
|
413
|
-
if (state.status === 'valid' || state.status === 'grace') {
|
|
414
|
-
await this.save({ ...stored, lastSeen: Math.max(stored.lastSeen ?? 0, this.nowSec()) });
|
|
415
|
-
}
|
|
416
|
-
return state;
|
|
417
|
-
}
|
|
418
|
-
try {
|
|
419
|
-
const response = await this.post('/v1/validate', {
|
|
420
|
-
licenseKey: stored.licenseKey,
|
|
421
|
-
product: this.opts.product,
|
|
422
|
-
fingerprint: this.fingerprintCache,
|
|
423
|
-
appVersion: this.opts.appVersion,
|
|
424
|
-
sdkVersion: SDK_VERSION,
|
|
425
|
-
...(await this.hardware()),
|
|
426
|
-
...this.deviceKey(),
|
|
427
|
-
});
|
|
428
|
-
const fresh: Leased = { licenseKey: stored.licenseKey, token: response.token, lastSeen: this.nowSec(), ...noticesFrom(response) };
|
|
429
|
-
await this.save(fresh);
|
|
430
|
-
return this.stateFromLease(fresh, false);
|
|
431
|
-
} catch (e) {
|
|
432
|
-
const keyHint = `${stored.licenseKey.slice(0, 7)}…${stored.licenseKey.slice(-4)}`;
|
|
433
|
-
if (e instanceof LicenseError && DEFINITIVE.has(e.code)) {
|
|
434
|
-
// Persist the refusal: drop the signed lease so going offline cannot bring the license back.
|
|
435
|
-
if (FORGET_KEY.has(e.code)) await this.clear();
|
|
436
|
-
else await this.save({ licenseKey: stored.licenseKey, lastSeen: stored.lastSeen, refused: { code: e.code, message: e.message, at: this.nowSec() } });
|
|
437
|
-
return this.emptyState('invalid', false, { reason: e.code, message: e.message, keyHint });
|
|
438
|
-
}
|
|
439
|
-
if (!stored.token) {
|
|
440
|
-
const refused = stored.refused ?? { code: 'not_activated', message: 'This device is not activated for this license' };
|
|
441
|
-
return this.emptyState('invalid', true, { reason: refused.code, message: refused.message, keyHint });
|
|
442
|
-
}
|
|
443
|
-
const state = this.stateFromLease({ ...stored, token: stored.token }, true);
|
|
444
|
-
if (state.status === 'valid' || state.status === 'grace') {
|
|
445
|
-
await this.save({ ...stored, lastSeen: Math.max(stored.lastSeen ?? 0, this.nowSec()) });
|
|
446
|
-
}
|
|
447
|
-
return state;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Decrypts data you encrypted for a feature (encryptForFeature in @carchalabs/seal-core, with the
|
|
453
|
-
* feature's content key from the console). Works offline: it uses the saved lease, which must be valid
|
|
454
|
-
* (or in its offline grace period), grant the feature and carry its key, and the saved license key.
|
|
455
|
-
* The native module does the work when it trusts this app's keys, so the content key never reaches
|
|
456
|
-
* JavaScript; without it the JavaScript implementation is used. Throws a LicenseError:
|
|
457
|
-
* `unlicensed`, a lease failure reason, `clock_suspect`, `feature_not_licensed` or `decrypt_failed`.
|
|
458
|
-
*/
|
|
459
|
-
async decrypt(feature: string, data: Uint8Array): Promise<Buffer> {
|
|
460
|
-
return (await this.opener(feature))(data);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Envelope: loads a CommonJS module you encrypted for a feature (seal-encrypt, one file or a
|
|
465
|
-
* folder) and returns its exports, like require(). The code is decrypted with the lease's feature key
|
|
466
|
-
* (see decrypt) and compiled from memory; it never exists as a plain file. Relative requires inside
|
|
467
|
-
* it load encrypted neighbours (`./x` finds `x.lke`, `x.js.lke` or `x/index.js.lke`), anything else
|
|
468
|
-
* goes to Node as usual. Loaded once per process, like require(). Throws the same LicenseErrors as
|
|
469
|
-
* decrypt. A licensed user can still read the code from memory: this keeps an app with the license
|
|
470
|
-
* check removed from having the feature, it does not hide the code from its licensed users.
|
|
471
|
-
*/
|
|
472
|
-
async requireEncrypted(feature: string, file: string): Promise<any> {
|
|
473
|
-
const open = await this.opener(feature);
|
|
474
|
-
return this.loadEncrypted(resolve(file), feature, open);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
private readonly encryptedModules = new Map<string, { exports: any; filename: string; id: string; loaded: boolean }>();
|
|
478
|
-
|
|
479
|
-
private loadEncrypted(file: string, feature: string, open: (data: Uint8Array) => Buffer): any {
|
|
480
|
-
const cacheKey = `${feature}\0${file}`;
|
|
481
|
-
const cached = this.encryptedModules.get(cacheKey);
|
|
482
|
-
if (cached) return cached.exports;
|
|
483
|
-
const source = open(readFileSync(file)).toString('utf8');
|
|
484
|
-
const filename = file.replace(/\.lke$/, '');
|
|
485
|
-
const mod = { exports: {} as any, filename, id: filename, loaded: false };
|
|
486
|
-
// Registered before it runs, so a require cycle gets the partial exports, as in Node.
|
|
487
|
-
this.encryptedModules.set(cacheKey, mod);
|
|
488
|
-
const nodeRequire = createRequire(filename);
|
|
489
|
-
const localRequire = ((request: string) => {
|
|
490
|
-
if (request.startsWith('./') || request.startsWith('../')) {
|
|
491
|
-
const base = resolve(dirname(filename), request);
|
|
492
|
-
const encrypted = [`${base}.lke`, `${base}.js.lke`, join(base, 'index.js.lke')].find((candidate) => existsSync(candidate));
|
|
493
|
-
if (encrypted) return this.loadEncrypted(encrypted, feature, open);
|
|
494
|
-
}
|
|
495
|
-
return nodeRequire(request);
|
|
496
|
-
}) as NodeJS.Require;
|
|
497
|
-
Object.assign(localRequire, { resolve: nodeRequire.resolve, cache: nodeRequire.cache, main: nodeRequire.main, extensions: nodeRequire.extensions });
|
|
498
|
-
try {
|
|
499
|
-
const run = compileFunction(source, ['exports', 'require', 'module', '__filename', '__dirname'], { filename });
|
|
500
|
-
run.call(mod.exports, mod.exports, localRequire, mod, filename, dirname(filename));
|
|
501
|
-
} catch (e) {
|
|
502
|
-
this.encryptedModules.delete(cacheKey);
|
|
503
|
-
throw e;
|
|
504
|
-
}
|
|
505
|
-
mod.loaded = true;
|
|
506
|
-
return mod.exports;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Checks the saved lease once for a feature and returns a synchronous decrypter bound to it: decrypt
|
|
511
|
-
* uses it once, requireEncrypted for a module and the encrypted modules it requires.
|
|
512
|
-
*/
|
|
513
|
-
private async opener(feature: string): Promise<(data: Uint8Array) => Buffer> {
|
|
514
|
-
const fingerprintHash = await this.fingerprint();
|
|
515
|
-
const stored = await this.load();
|
|
516
|
-
if (!stored?.token) throw new LicenseError('unlicensed', 'This device has no license.');
|
|
517
|
-
const leased: Leased = { ...stored, token: stored.token };
|
|
518
|
-
const state = this.stateFromLease(leased, true);
|
|
519
|
-
if (state.status !== 'valid' && state.status !== 'grace') {
|
|
520
|
-
throw new LicenseError(state.status === 'clock_suspect' ? 'clock_suspect' : (state.reason ?? 'invalid'), state.message ?? 'The license is not valid.');
|
|
521
|
-
}
|
|
522
|
-
const claims = decodeLease(leased.token)?.claims;
|
|
523
|
-
const graceSeconds = state.status === 'grace' ? (claims?.grc ?? (this.opts.graceDays ?? DEFAULT_GRACE_DAYS) * 86400) : 0;
|
|
524
|
-
const check = { product: this.opts.product, fingerprintHash, now: this.nowSec(), ...(graceSeconds ? { graceSeconds } : {}) };
|
|
525
|
-
const native = this.native();
|
|
526
|
-
return (data: Uint8Array) => {
|
|
527
|
-
const result: { ok: boolean; data?: Buffer; reason?: string } =
|
|
528
|
-
native && typeof native.decryptFeature === 'function'
|
|
529
|
-
? native.decryptFeature(leased.token, check, leased.licenseKey, feature, Buffer.from(data))
|
|
530
|
-
: decryptFeature(leased.token, { keys: this.keys, ...check }, leased.licenseKey, feature, Buffer.from(data));
|
|
531
|
-
if (result.ok && result.data) return Buffer.from(result.data);
|
|
532
|
-
if (result.reason === 'not_licensed') throw new LicenseError('feature_not_licensed', `This license does not include "${feature}".`);
|
|
533
|
-
if (result.reason === 'decrypt_failed') throw new LicenseError('decrypt_failed', 'The data could not be decrypted with this license.');
|
|
534
|
-
throw new LicenseError(result.reason ?? 'invalid', REASON_TEXT[result.reason as LeaseFailure] ?? 'The license is not valid.');
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Offline activation, step 1 on a machine that cannot reach the server: the request file (JSON text)
|
|
540
|
-
* the user takes to a connected device. The license server turns it into a license file at
|
|
541
|
-
* POST /v1/offline/activate (or its offline activation page). It holds what activate() would send:
|
|
542
|
-
* the license key, this device's fingerprint and hashed hardware parts, never raw serial numbers.
|
|
543
|
-
*/
|
|
544
|
-
async offlineRequest(licenseKey: string, machineName = hostname()): Promise<string> {
|
|
545
|
-
const fingerprint = await this.fingerprint();
|
|
546
|
-
const activation = {
|
|
547
|
-
licenseKey: licenseKey.trim().toUpperCase(),
|
|
548
|
-
product: this.opts.product,
|
|
549
|
-
fingerprint,
|
|
550
|
-
machineName: machineName.slice(0, 80),
|
|
551
|
-
platform: platform(),
|
|
552
|
-
appVersion: this.opts.appVersion,
|
|
553
|
-
sdkVersion: SDK_VERSION,
|
|
554
|
-
...(await this.hardware()),
|
|
555
|
-
...this.deviceKey(),
|
|
556
|
-
};
|
|
557
|
-
return JSON.stringify({ format: OFFLINE_REQUEST_FORMAT, version: 1, activation }, null, 2);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Offline activation, step 2: loads the license file the server issued for this machine's request.
|
|
562
|
-
* It is verified like an online lease (signature, product, this device) and then saved; from then on
|
|
563
|
-
* check() works without the server until the lease's renewal date plus the offline grace. Throws a
|
|
564
|
-
* LicenseError: `invalid_file`, `wrong_product`, or a lease failure reason such as `wrong_device`.
|
|
565
|
-
*/
|
|
566
|
-
async activateOffline(licenseFile: string): Promise<LicenseState> {
|
|
567
|
-
await this.fingerprint();
|
|
568
|
-
let file: any;
|
|
569
|
-
try {
|
|
570
|
-
file = JSON.parse(licenseFile);
|
|
571
|
-
} catch {
|
|
572
|
-
throw new LicenseError('invalid_file', 'This is not a Carcha Seal offline license file.');
|
|
573
|
-
}
|
|
574
|
-
if (!file || file.format !== OFFLINE_LICENSE_FORMAT || file.version !== 1 || typeof file.token !== 'string' || typeof file.licenseKey !== 'string') {
|
|
575
|
-
throw new LicenseError('invalid_file', 'This is not a Carcha Seal offline license file.');
|
|
576
|
-
}
|
|
577
|
-
if (file.product !== this.opts.product) throw new LicenseError('wrong_product', 'This offline license file is for another product.');
|
|
578
|
-
const stored: Leased = { licenseKey: file.licenseKey.trim().toUpperCase(), token: file.token, lastSeen: this.nowSec(), offline: true };
|
|
579
|
-
const state = this.stateFromLease(stored, true);
|
|
580
|
-
if (state.status !== 'valid') {
|
|
581
|
-
throw new LicenseError(state.status === 'clock_suspect' ? 'clock_suspect' : state.reason ?? 'invalid', state.message ?? 'This offline license file cannot be used on this computer.');
|
|
582
|
-
}
|
|
583
|
-
await this.save(stored);
|
|
584
|
-
return state;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Gives an offline machine's seat back: removes the license from this device and returns the
|
|
589
|
-
* deactivation file (JSON text) for the vendor, who frees the seat by uploading it in the console.
|
|
590
|
-
* Throws `unlicensed` when there is no license to remove.
|
|
591
|
-
*/
|
|
592
|
-
async deactivateOffline(): Promise<string> {
|
|
593
|
-
const stored = await this.load();
|
|
594
|
-
if (!stored?.token) throw new LicenseError('unlicensed', 'This device has no license.');
|
|
595
|
-
await this.clear();
|
|
596
|
-
return JSON.stringify({ format: OFFLINE_DEACTIVATION_FORMAT, version: 1, product: this.opts.product, token: stored.token }, null, 2);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
async deactivate(): Promise<void> {
|
|
600
|
-
const stored = await this.load();
|
|
601
|
-
if (!stored) return;
|
|
602
|
-
try {
|
|
603
|
-
await this.post('/v1/deactivate', {
|
|
604
|
-
licenseKey: stored.licenseKey,
|
|
605
|
-
product: this.opts.product,
|
|
606
|
-
fingerprint: await this.fingerprint(),
|
|
607
|
-
...(await this.hardware()),
|
|
608
|
-
});
|
|
609
|
-
} catch (e) {
|
|
610
|
-
if (!(e instanceof LicenseError && (e.code === 'not_activated' || e.code === 'invalid_key'))) throw e;
|
|
611
|
-
}
|
|
612
|
-
await this.clear();
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
static hasFeature(state: LicenseState, feature: string): boolean {
|
|
616
|
-
return (state.status === 'valid' || state.status === 'grace') && state.features.includes(feature);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/** The SDK class under its earlier name (LicenseKit). Same class; use Seal in new code. */
|
|
621
|
-
export { Seal as LicenseKit };
|
|
622
|
-
/** @deprecated The options under the class's earlier name; use SealOptions. */
|
|
623
|
-
export type LicenseKitOptions = SealOptions;
|