@moonbase.sh/licensing 2.0.0 → 3.0.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/FINGERPRINT_SPEC.md +414 -0
- package/README.md +133 -6
- package/dist/index.cjs +512 -66
- package/dist/index.d.cts +314 -14
- package/dist/index.d.ts +314 -14
- package/dist/index.js +496 -67
- package/fingerprint-vectors.json +744 -0
- package/package.json +8 -3
package/dist/index.cjs
CHANGED
|
@@ -31,20 +31,44 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ActivationMethod: () => ActivationMethod,
|
|
34
|
-
DefaultDeviceIdResolver: () => DefaultDeviceIdResolver,
|
|
35
34
|
ErrorType: () => ErrorType,
|
|
35
|
+
FINGERPRINT_PREFIX: () => FINGERPRINT_PREFIX,
|
|
36
|
+
FINGERPRINT_VERSION: () => FINGERPRINT_VERSION,
|
|
36
37
|
FileLicenseStore: () => FileLicenseStore,
|
|
38
|
+
IDENTIFYING_PARAM_NAMES: () => IDENTIFYING_PARAM_NAMES,
|
|
37
39
|
InMemoryLicenseStore: () => InMemoryLicenseStore,
|
|
40
|
+
InsufficientDeviceIdentityError: () => InsufficientDeviceIdentityError,
|
|
41
|
+
LegacyDeviceIdResolver: () => LegacyDeviceIdResolver,
|
|
38
42
|
LicenseClient: () => LicenseClient,
|
|
39
43
|
LicenseValidator: () => LicenseValidator,
|
|
44
|
+
MAX_VALUE_LENGTH: () => MAX_VALUE_LENGTH,
|
|
45
|
+
MigratingDeviceIdResolver: () => MigratingDeviceIdResolver,
|
|
46
|
+
MoonbaseDeviceIdResolver: () => MoonbaseDeviceIdResolver,
|
|
40
47
|
MoonbaseError: () => MoonbaseError,
|
|
41
|
-
MoonbaseLicensing: () => MoonbaseLicensing
|
|
48
|
+
MoonbaseLicensing: () => MoonbaseLicensing,
|
|
49
|
+
buildFingerprintMaterial: () => buildFingerprintMaterial,
|
|
50
|
+
canonicalizeParams: () => canonicalizeParams,
|
|
51
|
+
canonicalizeValue: () => canonicalizeValue,
|
|
52
|
+
defaultDeviceIdentityReader: () => defaultDeviceIdentityReader,
|
|
53
|
+
fingerprintDeviceId: () => fingerprintDeviceId,
|
|
54
|
+
fingerprintDigest: () => fingerprintDigest,
|
|
55
|
+
parseDeviceIdStamp: () => parseDeviceIdStamp,
|
|
56
|
+
parseIoregPlatformUuid: () => parseIoregPlatformUuid,
|
|
57
|
+
parseSmbiosParams: () => parseSmbiosParams,
|
|
58
|
+
platformTag: () => platformTag,
|
|
59
|
+
selectMachineId: () => selectMachineId,
|
|
60
|
+
stampDeviceId: () => stampDeviceId
|
|
42
61
|
});
|
|
43
62
|
module.exports = __toCommonJS(index_exports);
|
|
63
|
+
var import_node_buffer2 = require("buffer");
|
|
64
|
+
var import_node_process3 = __toESM(require("process"), 1);
|
|
44
65
|
|
|
45
66
|
// src/client.ts
|
|
46
67
|
var import_cross_fetch = __toESM(require("cross-fetch"), 1);
|
|
47
68
|
|
|
69
|
+
// src/schemas.ts
|
|
70
|
+
var import_zod = require("zod");
|
|
71
|
+
|
|
48
72
|
// src/types.ts
|
|
49
73
|
var ActivationMethod = /* @__PURE__ */ ((ActivationMethod2) => {
|
|
50
74
|
ActivationMethod2["Online"] = "Online";
|
|
@@ -53,7 +77,6 @@ var ActivationMethod = /* @__PURE__ */ ((ActivationMethod2) => {
|
|
|
53
77
|
})(ActivationMethod || {});
|
|
54
78
|
|
|
55
79
|
// src/schemas.ts
|
|
56
|
-
var import_zod = require("zod");
|
|
57
80
|
var activationRequestResponseSchema = import_zod.z.object({
|
|
58
81
|
id: import_zod.z.string(),
|
|
59
82
|
request: import_zod.z.string(),
|
|
@@ -107,7 +130,7 @@ var LicenseClient = class {
|
|
|
107
130
|
deviceName: await this.deviceIdResolver.resolveDeviceName(),
|
|
108
131
|
deviceSignature: await this.deviceIdResolver.resolveDeviceId()
|
|
109
132
|
};
|
|
110
|
-
const response = await (0, import_cross_fetch.default)(this.configuration.endpoint
|
|
133
|
+
const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/activations/${this.configuration.productId}/request${this.buildQueryString()}`, {
|
|
111
134
|
...defaultFetchOptions,
|
|
112
135
|
method: "POST",
|
|
113
136
|
body: JSON.stringify(content)
|
|
@@ -129,7 +152,8 @@ var LicenseClient = class {
|
|
|
129
152
|
}
|
|
130
153
|
async getRequestedActivation(request) {
|
|
131
154
|
const response = await (0, import_cross_fetch.default)(request.request, defaultFetchOptions);
|
|
132
|
-
if (response.status === 204 || response.status === 404)
|
|
155
|
+
if (response.status === 204 || response.status === 404)
|
|
156
|
+
return null;
|
|
133
157
|
return await this.handleLicenseResponse(response);
|
|
134
158
|
}
|
|
135
159
|
async requestTrial() {
|
|
@@ -137,7 +161,7 @@ var LicenseClient = class {
|
|
|
137
161
|
deviceName: await this.deviceIdResolver.resolveDeviceName(),
|
|
138
162
|
deviceSignature: await this.deviceIdResolver.resolveDeviceId()
|
|
139
163
|
};
|
|
140
|
-
const response = await (0, import_cross_fetch.default)(this.configuration.endpoint
|
|
164
|
+
const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/trials/${this.configuration.productId}/request${this.buildQueryString()}`, {
|
|
141
165
|
...defaultFetchOptions,
|
|
142
166
|
method: "POST",
|
|
143
167
|
headers: {
|
|
@@ -149,7 +173,7 @@ var LicenseClient = class {
|
|
|
149
173
|
return await this.handleLicenseResponse(response);
|
|
150
174
|
}
|
|
151
175
|
async validateLicense(license) {
|
|
152
|
-
const response = await (0, import_cross_fetch.default)(this.configuration.endpoint
|
|
176
|
+
const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/validate${this.buildQueryString()}`, {
|
|
153
177
|
...defaultFetchOptions,
|
|
154
178
|
method: "POST",
|
|
155
179
|
headers: {
|
|
@@ -162,11 +186,12 @@ var LicenseClient = class {
|
|
|
162
186
|
}
|
|
163
187
|
async validateRawLicense(rawLicense) {
|
|
164
188
|
const license = await this.licenseValidator.validateLicense(rawLicense.toString("utf8"));
|
|
165
|
-
if (license.activationMethod === "Offline" /* Offline */)
|
|
189
|
+
if (license.activationMethod === "Offline" /* Offline */)
|
|
190
|
+
return license;
|
|
166
191
|
return await this.validateLicense(license);
|
|
167
192
|
}
|
|
168
193
|
async revokeLicense(license) {
|
|
169
|
-
const response = await (0, import_cross_fetch.default)(this.configuration.endpoint
|
|
194
|
+
const response = await (0, import_cross_fetch.default)(`${this.configuration.endpoint}/api/client/licenses/${this.configuration.productId}/revoke?format=JWT`, {
|
|
170
195
|
...defaultFetchOptions,
|
|
171
196
|
method: "POST",
|
|
172
197
|
headers: {
|
|
@@ -188,11 +213,14 @@ var LicenseClient = class {
|
|
|
188
213
|
buildQueryString() {
|
|
189
214
|
const parts = ["format=JWT"];
|
|
190
215
|
const { platform, appVersion, metadata } = this.configuration;
|
|
191
|
-
if (platform)
|
|
192
|
-
|
|
216
|
+
if (platform)
|
|
217
|
+
parts.push(`platform=${encodeURIComponent(platform)}`);
|
|
218
|
+
if (appVersion)
|
|
219
|
+
parts.push(`appVersion=${encodeURIComponent(appVersion)}`);
|
|
193
220
|
if (metadata) {
|
|
194
221
|
for (const [key, value] of Object.entries(metadata)) {
|
|
195
|
-
if (value === null || value === void 0 || value === "")
|
|
222
|
+
if (value === null || value === void 0 || value === "")
|
|
223
|
+
continue;
|
|
196
224
|
parts.push(`meta[${encodeURIComponent(key)}]=${encodeURIComponent(value)}`);
|
|
197
225
|
}
|
|
198
226
|
}
|
|
@@ -201,32 +229,426 @@ var LicenseClient = class {
|
|
|
201
229
|
};
|
|
202
230
|
|
|
203
231
|
// src/deviceIdResolver.ts
|
|
232
|
+
var import_node_child_process2 = __toESM(require("child_process"), 1);
|
|
233
|
+
var import_node_crypto2 = require("crypto");
|
|
234
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
235
|
+
var import_node_process2 = __toESM(require("process"), 1);
|
|
204
236
|
var import_systeminformation = __toESM(require("systeminformation"), 1);
|
|
205
|
-
|
|
237
|
+
|
|
238
|
+
// src/errors.ts
|
|
239
|
+
var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
|
|
240
|
+
ErrorType2["None"] = "None";
|
|
241
|
+
ErrorType2["ApiError"] = "ApiError";
|
|
242
|
+
ErrorType2["NoEligibleLicense"] = "NoEligibleLicense";
|
|
243
|
+
ErrorType2["LicenseInvalid"] = "LicenseInvalid";
|
|
244
|
+
ErrorType2["LicenseRevoked"] = "LicenseRevoked";
|
|
245
|
+
ErrorType2["LicenseActivationRevoked"] = "LicenseActivationRevoked";
|
|
246
|
+
ErrorType2["LicenseExpired"] = "LicenseExpired";
|
|
247
|
+
ErrorType2["LicenseDeviceMismatch"] = "LicenseDeviceMismatch";
|
|
248
|
+
ErrorType2["DeviceIdentityUnavailable"] = "DeviceIdentityUnavailable";
|
|
249
|
+
return ErrorType2;
|
|
250
|
+
})(ErrorType || {});
|
|
251
|
+
var MoonbaseError = class extends Error {
|
|
252
|
+
constructor(title, detail, type, inner) {
|
|
253
|
+
super();
|
|
254
|
+
this.title = title;
|
|
255
|
+
this.detail = detail;
|
|
256
|
+
this.type = type;
|
|
257
|
+
this.inner = inner;
|
|
258
|
+
this.name = "MoonbaseError";
|
|
259
|
+
this.message = detail != null ? detail : title;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var InsufficientDeviceIdentityError = class extends MoonbaseError {
|
|
263
|
+
constructor(platform, reason = "no identity parameter could be read") {
|
|
264
|
+
super(
|
|
265
|
+
"No device identity",
|
|
266
|
+
`Could not identify this device (platform: ${platform}): ${reason}`,
|
|
267
|
+
"DeviceIdentityUnavailable" /* DeviceIdentityUnavailable */
|
|
268
|
+
);
|
|
269
|
+
this.platform = platform;
|
|
270
|
+
this.name = "InsufficientDeviceIdentityError";
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// src/fingerprint.ts
|
|
275
|
+
var import_node_buffer = require("buffer");
|
|
206
276
|
var import_node_child_process = __toESM(require("child_process"), 1);
|
|
207
277
|
var import_node_crypto = require("crypto");
|
|
208
|
-
var
|
|
278
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
279
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
280
|
+
var import_node_process = __toESM(require("process"), 1);
|
|
281
|
+
var FINGERPRINT_PREFIX = "moonbase:fingerprint:v2";
|
|
282
|
+
var FINGERPRINT_VERSION = 2;
|
|
283
|
+
var MAX_VALUE_LENGTH = 128;
|
|
284
|
+
var PRINTABLE_ASCII_MIN = 32;
|
|
285
|
+
var PRINTABLE_ASCII_MAX = 126;
|
|
286
|
+
var STAMP_PATTERN = /^mbd(\d+)(n?)_([0-9a-f]{64})$/;
|
|
287
|
+
function canonicalizeValue(value) {
|
|
288
|
+
var _a;
|
|
289
|
+
let printable = "";
|
|
290
|
+
for (const char of value.normalize("NFC")) {
|
|
291
|
+
const code = (_a = char.codePointAt(0)) != null ? _a : 0;
|
|
292
|
+
if (code >= PRINTABLE_ASCII_MIN && code <= PRINTABLE_ASCII_MAX)
|
|
293
|
+
printable += char;
|
|
294
|
+
}
|
|
295
|
+
return printable.slice(0, MAX_VALUE_LENGTH).replace(/^ +| +$/g, "");
|
|
296
|
+
}
|
|
297
|
+
function platformTag(platform = import_node_process.default.platform) {
|
|
298
|
+
switch (platform) {
|
|
299
|
+
case "darwin":
|
|
300
|
+
return "mac";
|
|
301
|
+
case "win32":
|
|
302
|
+
return "windows";
|
|
303
|
+
case "android":
|
|
304
|
+
return "android";
|
|
305
|
+
case "linux":
|
|
306
|
+
return "linux";
|
|
307
|
+
case "freebsd":
|
|
308
|
+
case "openbsd":
|
|
309
|
+
case "netbsd":
|
|
310
|
+
return "bsd";
|
|
311
|
+
default:
|
|
312
|
+
return "unknown";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
|
|
316
|
+
"ioPlatformUuid",
|
|
317
|
+
"machineId",
|
|
318
|
+
"systemUuid",
|
|
319
|
+
"baseboardSerialNumber",
|
|
320
|
+
"deviceName"
|
|
321
|
+
]);
|
|
322
|
+
var IDENTIFYING_PARAM_NAMES = Object.freeze([...IDENTIFYING_PARAMS]);
|
|
323
|
+
var NOT_PROGRAMMED_VALUES = /* @__PURE__ */ new Set([
|
|
324
|
+
"to be filled by o.e.m.",
|
|
325
|
+
// The same filler without the dots, which plenty of firmware writes instead.
|
|
326
|
+
"to be filled by oem",
|
|
327
|
+
"default string",
|
|
328
|
+
"system serial number",
|
|
329
|
+
"base board serial number",
|
|
330
|
+
"chassis serial number",
|
|
331
|
+
"not specified",
|
|
332
|
+
"not applicable",
|
|
333
|
+
"not available",
|
|
334
|
+
"none",
|
|
335
|
+
"unknown",
|
|
336
|
+
"invalid",
|
|
337
|
+
"n/a",
|
|
338
|
+
"0123456789",
|
|
339
|
+
// `machine-id(5)`: the literal marker systemd writes to say "no id yet",
|
|
340
|
+
// e.g. in an initrd or a golden image awaiting first boot. Every machine
|
|
341
|
+
// deployed from such an image reads it, so it is the opposite of an identifier.
|
|
342
|
+
"uninitialized"
|
|
343
|
+
]);
|
|
344
|
+
function isNotProgrammed(value) {
|
|
345
|
+
return NOT_PROGRAMMED_VALUES.has(value.toLowerCase()) || /^0+$/.test(value) || /^f+$/i.test(value);
|
|
346
|
+
}
|
|
347
|
+
function canonicalizeParams(params) {
|
|
348
|
+
const kept = [];
|
|
349
|
+
const seen = /* @__PURE__ */ new Set();
|
|
350
|
+
for (const [name, rawValue] of params) {
|
|
351
|
+
const value = canonicalizeValue(rawValue);
|
|
352
|
+
if (value.length === 0)
|
|
353
|
+
continue;
|
|
354
|
+
if (IDENTIFYING_PARAMS.has(name) && isNotProgrammed(value))
|
|
355
|
+
continue;
|
|
356
|
+
if (seen.has(name))
|
|
357
|
+
throw new Error(`Duplicate fingerprint parameter name: ${name}`);
|
|
358
|
+
seen.add(name);
|
|
359
|
+
kept.push([name, value]);
|
|
360
|
+
}
|
|
361
|
+
return kept;
|
|
362
|
+
}
|
|
363
|
+
function buildFingerprintMaterial(platform, params) {
|
|
364
|
+
const kept = canonicalizeParams(params);
|
|
365
|
+
if (kept.length === 0)
|
|
366
|
+
throw new InsufficientDeviceIdentityError(platform, "no identity parameter could be read");
|
|
367
|
+
if (!kept.some(([name]) => IDENTIFYING_PARAMS.has(name))) {
|
|
368
|
+
throw new InsufficientDeviceIdentityError(
|
|
369
|
+
platform,
|
|
370
|
+
`only model-level parameters could be read (${kept.map(([name]) => name).join(", ")}), none of which identify this individual machine`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
const lines = [FINGERPRINT_PREFIX, `platform=${platform}`];
|
|
374
|
+
for (const [name, value] of kept)
|
|
375
|
+
lines.push(`${name}=${value}`);
|
|
376
|
+
return lines.join("\n");
|
|
377
|
+
}
|
|
378
|
+
function fingerprintDigest(material) {
|
|
379
|
+
return (0, import_node_crypto.createHash)("sha256").update(material, "utf8").digest("hex");
|
|
380
|
+
}
|
|
381
|
+
function stampDeviceId(digest, source = "identity") {
|
|
382
|
+
return `mbd${FINGERPRINT_VERSION}${source === "deviceName" ? "n" : ""}_${digest}`;
|
|
383
|
+
}
|
|
384
|
+
function fingerprintDeviceId(material, source = "identity") {
|
|
385
|
+
return stampDeviceId(fingerprintDigest(material), source);
|
|
386
|
+
}
|
|
387
|
+
function parseDeviceIdStamp(deviceId) {
|
|
388
|
+
const match = STAMP_PATTERN.exec(deviceId);
|
|
389
|
+
if (!match)
|
|
390
|
+
return null;
|
|
391
|
+
return {
|
|
392
|
+
version: Number(match[1]),
|
|
393
|
+
source: match[2] === "n" ? "deviceName" : "identity",
|
|
394
|
+
digest: match[3]
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function commandOutput(command) {
|
|
398
|
+
try {
|
|
399
|
+
return import_node_child_process.default.execSync(command, { encoding: "utf8" });
|
|
400
|
+
} catch (e) {
|
|
401
|
+
return "";
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function readSysFile(path2) {
|
|
405
|
+
try {
|
|
406
|
+
return import_node_fs.default.readFileSync(path2, "utf8");
|
|
407
|
+
} catch (e) {
|
|
408
|
+
return "";
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function parseIoregPlatformUuid(ioregOutput) {
|
|
412
|
+
const match = ioregOutput.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
413
|
+
return match ? match[1].replaceAll("-", "").toUpperCase() : "";
|
|
414
|
+
}
|
|
415
|
+
function readMacIdentity() {
|
|
416
|
+
const uuid = parseIoregPlatformUuid(commandOutput("ioreg -rd1 -c IOPlatformExpertDevice"));
|
|
417
|
+
const params = [["ioPlatformUuid", uuid]];
|
|
418
|
+
return { params, deviceName: import_node_os.default.hostname().replace(/\.local$/i, "") };
|
|
419
|
+
}
|
|
420
|
+
function selectMachineId(...sources) {
|
|
421
|
+
var _a;
|
|
422
|
+
return (_a = sources.map(canonicalizeValue).find((value) => /^[0-9a-f]{32}$/.test(value) && !isNotProgrammed(value))) != null ? _a : "";
|
|
423
|
+
}
|
|
424
|
+
function readMachineId() {
|
|
425
|
+
return selectMachineId(
|
|
426
|
+
readSysFile("/etc/machine-id"),
|
|
427
|
+
readSysFile("/var/lib/dbus/machine-id")
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
function readLinuxIdentity() {
|
|
431
|
+
const params = [
|
|
432
|
+
["machineId", readMachineId()],
|
|
433
|
+
["sysVendor", readSysFile("/sys/class/dmi/id/sys_vendor")],
|
|
434
|
+
["productName", readSysFile("/sys/class/dmi/id/product_name")],
|
|
435
|
+
["boardVendor", readSysFile("/sys/class/dmi/id/board_vendor")],
|
|
436
|
+
["boardName", readSysFile("/sys/class/dmi/id/board_name")]
|
|
437
|
+
];
|
|
438
|
+
return { params, deviceName: import_node_os.default.hostname() };
|
|
439
|
+
}
|
|
440
|
+
function parseSmbiosStructures(data) {
|
|
441
|
+
const structures = [];
|
|
442
|
+
let offset = 0;
|
|
443
|
+
while (offset + 4 <= data.length) {
|
|
444
|
+
const type = data[offset];
|
|
445
|
+
const length = data[offset + 1];
|
|
446
|
+
if (length < 4 || offset + length > data.length)
|
|
447
|
+
break;
|
|
448
|
+
const formatted = data.subarray(offset, offset + length);
|
|
449
|
+
const strings = [];
|
|
450
|
+
let p = offset + length;
|
|
451
|
+
if (p + 1 < data.length && data[p] === 0 && data[p + 1] === 0) {
|
|
452
|
+
p += 2;
|
|
453
|
+
} else {
|
|
454
|
+
while (p < data.length) {
|
|
455
|
+
let end = p;
|
|
456
|
+
while (end < data.length && data[end] !== 0)
|
|
457
|
+
end++;
|
|
458
|
+
strings.push(data.toString("latin1", p, end));
|
|
459
|
+
p = end + 1;
|
|
460
|
+
if (p < data.length && data[p] === 0) {
|
|
461
|
+
p += 1;
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
structures.push({ type, formatted, strings });
|
|
467
|
+
if (type === 127)
|
|
468
|
+
break;
|
|
469
|
+
offset = p;
|
|
470
|
+
}
|
|
471
|
+
return structures;
|
|
472
|
+
}
|
|
473
|
+
function resolveSmbiosString(structure, fieldOffset) {
|
|
474
|
+
if (fieldOffset >= structure.formatted.length)
|
|
475
|
+
return "";
|
|
476
|
+
const index = structure.formatted[fieldOffset];
|
|
477
|
+
if (index === 0 || index > structure.strings.length)
|
|
478
|
+
return "";
|
|
479
|
+
return structure.strings[index - 1];
|
|
480
|
+
}
|
|
481
|
+
function formatSmbiosUuid(structure, fieldOffset) {
|
|
482
|
+
if (fieldOffset + 16 > structure.formatted.length)
|
|
483
|
+
return "";
|
|
484
|
+
const bytes = structure.formatted.subarray(fieldOffset, fieldOffset + 16);
|
|
485
|
+
if (bytes.every((byte) => byte === 0) || bytes.every((byte) => byte === 255))
|
|
486
|
+
return "";
|
|
487
|
+
let hex = "";
|
|
488
|
+
for (const byte of bytes)
|
|
489
|
+
hex += byte.toString(16).padStart(2, "0").toUpperCase();
|
|
490
|
+
return hex;
|
|
491
|
+
}
|
|
492
|
+
function parseSmbiosParams(smbiosData) {
|
|
493
|
+
const structures = parseSmbiosStructures(smbiosData);
|
|
494
|
+
const params = [];
|
|
495
|
+
const system = structures.find((structure) => structure.type === 1);
|
|
496
|
+
if (system) {
|
|
497
|
+
params.push(["systemManufacturer", resolveSmbiosString(system, 4)]);
|
|
498
|
+
params.push(["systemProductName", resolveSmbiosString(system, 5)]);
|
|
499
|
+
params.push(["systemUuid", formatSmbiosUuid(system, 8)]);
|
|
500
|
+
}
|
|
501
|
+
const baseboard = structures.find((structure) => structure.type === 2);
|
|
502
|
+
if (baseboard) {
|
|
503
|
+
params.push(["baseboardManufacturer", resolveSmbiosString(baseboard, 4)]);
|
|
504
|
+
params.push(["baseboardProduct", resolveSmbiosString(baseboard, 5)]);
|
|
505
|
+
params.push(["baseboardSerialNumber", resolveSmbiosString(baseboard, 7)]);
|
|
506
|
+
}
|
|
507
|
+
return params;
|
|
508
|
+
}
|
|
509
|
+
function readWindowsSmbios() {
|
|
510
|
+
const script = "[Convert]::ToBase64String((Get-CimInstance -Namespace root/wmi -ClassName MSSmBios_RawSMBiosTables).SMBiosData)";
|
|
511
|
+
const output = commandOutput(`powershell -NoProfile -NonInteractive -Command "${script}"`);
|
|
512
|
+
try {
|
|
513
|
+
return import_node_buffer.Buffer.from(output.trim(), "base64");
|
|
514
|
+
} catch (e) {
|
|
515
|
+
return import_node_buffer.Buffer.alloc(0);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function readWindowsIdentity() {
|
|
519
|
+
return { params: parseSmbiosParams(readWindowsSmbios()), deviceName: import_node_os.default.hostname() };
|
|
520
|
+
}
|
|
521
|
+
function defaultDeviceIdentityReader(platform = import_node_process.default.platform) {
|
|
522
|
+
return {
|
|
523
|
+
read() {
|
|
524
|
+
switch (platform) {
|
|
525
|
+
case "darwin":
|
|
526
|
+
return readMacIdentity();
|
|
527
|
+
case "linux":
|
|
528
|
+
return readLinuxIdentity();
|
|
529
|
+
case "win32":
|
|
530
|
+
return readWindowsIdentity();
|
|
531
|
+
// android, bsd and anything else define no identity params: the spec
|
|
532
|
+
// has no stable hardware source for them, so they resolve to an
|
|
533
|
+
// insufficient-identity error unless the deviceName fallback is enabled.
|
|
534
|
+
default:
|
|
535
|
+
return { params: [], deviceName: import_node_os.default.hostname() };
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/deviceIdResolver.ts
|
|
542
|
+
var MoonbaseDeviceIdResolver = class {
|
|
543
|
+
constructor(options) {
|
|
544
|
+
var _a, _b, _c;
|
|
545
|
+
const platform = (_a = options == null ? void 0 : options.platform) != null ? _a : import_node_process2.default.platform;
|
|
546
|
+
this.platform = platformTag(platform);
|
|
547
|
+
this.reader = (_b = options == null ? void 0 : options.reader) != null ? _b : defaultDeviceIdentityReader(platform);
|
|
548
|
+
this.fallback = (_c = options == null ? void 0 : options.fallback) != null ? _c : "none";
|
|
549
|
+
}
|
|
550
|
+
async resolveDeviceName() {
|
|
551
|
+
return this.readIdentity().deviceName;
|
|
552
|
+
}
|
|
553
|
+
async resolveDeviceId() {
|
|
554
|
+
return this.compute().deviceId;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* A fresh copy each call. The device binding is what activation sends and what
|
|
558
|
+
* validation compares, so handing out the resolver's own object would let a
|
|
559
|
+
* consumer that edits a diagnostic — or logs it through something that
|
|
560
|
+
* normalizes in place — silently change the id every later call returns.
|
|
561
|
+
*/
|
|
562
|
+
async describeDevice() {
|
|
563
|
+
const described = this.compute();
|
|
564
|
+
return { ...described, paramNames: [...described.paramNames] };
|
|
565
|
+
}
|
|
566
|
+
compute() {
|
|
567
|
+
var _a;
|
|
568
|
+
(_a = this.described) != null ? _a : this.described = this.computeDescription();
|
|
569
|
+
return this.described;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Read identity at most once. Both halves of an activation request ask for it —
|
|
573
|
+
* the name and then the id — and a read can mean spawning `ioreg` or
|
|
574
|
+
* PowerShell, so reading per call would double the cost of every request and
|
|
575
|
+
* let the name and the id come from two different reads of the machine.
|
|
576
|
+
*/
|
|
577
|
+
readIdentity() {
|
|
578
|
+
var _a;
|
|
579
|
+
(_a = this.identity) != null ? _a : this.identity = this.reader.read();
|
|
580
|
+
return this.identity;
|
|
581
|
+
}
|
|
582
|
+
computeDescription() {
|
|
583
|
+
const { params, deviceName } = this.readIdentity();
|
|
584
|
+
try {
|
|
585
|
+
return this.describe(params, "identity");
|
|
586
|
+
} catch (err) {
|
|
587
|
+
if (this.fallback !== "deviceName" || !(err instanceof InsufficientDeviceIdentityError))
|
|
588
|
+
throw err;
|
|
589
|
+
return this.describe([["deviceName", deviceName]], "deviceName");
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
describe(params, source) {
|
|
593
|
+
return {
|
|
594
|
+
deviceId: fingerprintDeviceId(buildFingerprintMaterial(this.platform, params), source),
|
|
595
|
+
version: FINGERPRINT_VERSION,
|
|
596
|
+
platform: this.platform,
|
|
597
|
+
source,
|
|
598
|
+
paramNames: canonicalizeParams(params).map(([name]) => name)
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
var MigratingDeviceIdResolver = class {
|
|
603
|
+
constructor(current, ...previous) {
|
|
604
|
+
this.current = current;
|
|
605
|
+
this.previous = previous;
|
|
606
|
+
const describe = current.describeDevice;
|
|
607
|
+
if (typeof describe === "function")
|
|
608
|
+
this.describeDevice = () => describe.call(current);
|
|
609
|
+
}
|
|
610
|
+
async resolveDeviceName() {
|
|
611
|
+
return this.current.resolveDeviceName();
|
|
612
|
+
}
|
|
613
|
+
async resolveDeviceId() {
|
|
614
|
+
return this.current.resolveDeviceId();
|
|
615
|
+
}
|
|
616
|
+
async acceptsDeviceId(deviceId) {
|
|
617
|
+
if (!this.previousIds) {
|
|
618
|
+
this.previousIds = Promise.all(this.previous.map(async (resolver) => {
|
|
619
|
+
try {
|
|
620
|
+
return await resolver.resolveDeviceId();
|
|
621
|
+
} catch (e) {
|
|
622
|
+
return "";
|
|
623
|
+
}
|
|
624
|
+
}));
|
|
625
|
+
}
|
|
626
|
+
return (await this.previousIds).some((previous) => previous.length > 0 && previous === deviceId);
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
var LegacyDeviceIdResolver = class {
|
|
209
630
|
async resolveDeviceName() {
|
|
210
|
-
switch (
|
|
631
|
+
switch (import_node_process2.default.platform) {
|
|
211
632
|
case "darwin":
|
|
212
633
|
try {
|
|
213
|
-
return
|
|
634
|
+
return import_node_child_process2.default.execSync("scutil --get ComputerName").toString().trim();
|
|
214
635
|
} catch (e) {
|
|
215
|
-
return
|
|
636
|
+
return import_node_os2.default.hostname();
|
|
216
637
|
}
|
|
217
638
|
case "win32":
|
|
218
|
-
return
|
|
219
|
-
case "linux":
|
|
220
|
-
const prettyname =
|
|
221
|
-
return prettyname ||
|
|
639
|
+
return import_node_process2.default.env.COMPUTERNAME || import_node_os2.default.hostname();
|
|
640
|
+
case "linux": {
|
|
641
|
+
const prettyname = import_node_child_process2.default.execSync("hostnamectl --pretty").toString().trim();
|
|
642
|
+
return prettyname || import_node_os2.default.hostname();
|
|
643
|
+
}
|
|
222
644
|
default:
|
|
223
|
-
return
|
|
645
|
+
return import_node_os2.default.hostname();
|
|
224
646
|
}
|
|
225
647
|
}
|
|
226
648
|
async resolveDeviceId() {
|
|
227
649
|
const [cpu, hw] = await Promise.all([import_systeminformation.default.cpu(), import_systeminformation.default.system()]);
|
|
228
650
|
const parts = [
|
|
229
|
-
|
|
651
|
+
import_node_os2.default.hostname(),
|
|
230
652
|
hw.manufacturer,
|
|
231
653
|
hw.model,
|
|
232
654
|
hw.serial,
|
|
@@ -238,40 +660,15 @@ var DefaultDeviceIdResolver = class {
|
|
|
238
660
|
cpu.family,
|
|
239
661
|
cpu.model
|
|
240
662
|
];
|
|
241
|
-
const hash = (0,
|
|
663
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(parts.join("")).digest();
|
|
242
664
|
return hash.toString("base64").replaceAll("=", "");
|
|
243
665
|
}
|
|
244
666
|
};
|
|
245
667
|
|
|
246
668
|
// src/store.ts
|
|
247
|
-
var
|
|
669
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
248
670
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
249
671
|
var import_node_path = __toESM(require("path"), 1);
|
|
250
|
-
|
|
251
|
-
// src/errors.ts
|
|
252
|
-
var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
|
|
253
|
-
ErrorType2["None"] = "None";
|
|
254
|
-
ErrorType2["ApiError"] = "ApiError";
|
|
255
|
-
ErrorType2["NoEligibleLicense"] = "NoEligibleLicense";
|
|
256
|
-
ErrorType2["LicenseInvalid"] = "LicenseInvalid";
|
|
257
|
-
ErrorType2["LicenseRevoked"] = "LicenseRevoked";
|
|
258
|
-
ErrorType2["LicenseActivationRevoked"] = "LicenseActivationRevoked";
|
|
259
|
-
ErrorType2["LicenseExpired"] = "LicenseExpired";
|
|
260
|
-
return ErrorType2;
|
|
261
|
-
})(ErrorType || {});
|
|
262
|
-
var MoonbaseError = class extends Error {
|
|
263
|
-
constructor(title, detail, type, inner) {
|
|
264
|
-
super();
|
|
265
|
-
this.title = title;
|
|
266
|
-
this.detail = detail;
|
|
267
|
-
this.type = type;
|
|
268
|
-
this.inner = inner;
|
|
269
|
-
this.name = "MoonbaseError";
|
|
270
|
-
this.message = detail != null ? detail : title;
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
// src/store.ts
|
|
275
672
|
var InMemoryLicenseStore = class {
|
|
276
673
|
async loadLocalLicense() {
|
|
277
674
|
var _a;
|
|
@@ -289,7 +686,8 @@ var FileLicenseStore = class {
|
|
|
289
686
|
this.options = options;
|
|
290
687
|
}
|
|
291
688
|
async loadLocalLicense() {
|
|
292
|
-
if (!
|
|
689
|
+
if (!import_node_fs2.default.existsSync(this.path))
|
|
690
|
+
return null;
|
|
293
691
|
try {
|
|
294
692
|
const file = await import_promises.default.readFile(this.path, { encoding: "utf-8" });
|
|
295
693
|
const license = licenseSchema.parse(JSON.parse(file));
|
|
@@ -302,7 +700,8 @@ var FileLicenseStore = class {
|
|
|
302
700
|
await import_promises.default.writeFile(this.path, JSON.stringify(license), { encoding: "utf-8" });
|
|
303
701
|
}
|
|
304
702
|
async deleteLocalLicense() {
|
|
305
|
-
if (!
|
|
703
|
+
if (!import_node_fs2.default.existsSync(this.path))
|
|
704
|
+
return;
|
|
306
705
|
await import_promises.default.rm(this.path);
|
|
307
706
|
}
|
|
308
707
|
get path() {
|
|
@@ -312,8 +711,8 @@ var FileLicenseStore = class {
|
|
|
312
711
|
};
|
|
313
712
|
|
|
314
713
|
// src/validator.ts
|
|
315
|
-
var import_zod2 = require("zod");
|
|
316
714
|
var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
|
|
715
|
+
var import_zod2 = require("zod");
|
|
317
716
|
var licenseClaimsSchema = import_zod2.z.object({
|
|
318
717
|
["l:id" /* LicenseId */]: import_zod2.z.string(),
|
|
319
718
|
["trial" /* Trial */]: import_zod2.z.string().or(import_zod2.z.boolean()).transform((v) => v === true || v === "true").pipe(import_zod2.z.boolean()),
|
|
@@ -350,15 +749,20 @@ var LicenseValidator = class {
|
|
|
350
749
|
);
|
|
351
750
|
}
|
|
352
751
|
const expectedSignature = await this.deviceIdResolver.resolveDeviceId();
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
"This license is not for this device",
|
|
357
|
-
"LicenseExpired" /* LicenseExpired */
|
|
358
|
-
);
|
|
359
|
-
}
|
|
752
|
+
const boundSignature = claims["sig" /* ComputerSignature */];
|
|
753
|
+
if (expectedSignature !== boundSignature && !await this.acceptsHistoricalDeviceId(boundSignature))
|
|
754
|
+
throw deviceMismatchError(expectedSignature, boundSignature);
|
|
360
755
|
return license;
|
|
361
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* Give a {@link IMigratingDeviceIdResolver} the chance to vouch for a device id
|
|
759
|
+
* this machine used to have. Only consulted after the fast path fails, so
|
|
760
|
+
* apps that have not opted into a migration pay nothing.
|
|
761
|
+
*/
|
|
762
|
+
async acceptsHistoricalDeviceId(boundSignature) {
|
|
763
|
+
const accepts = this.deviceIdResolver.acceptsDeviceId;
|
|
764
|
+
return typeof accepts === "function" && await accepts.call(this.deviceIdResolver, boundSignature);
|
|
765
|
+
}
|
|
362
766
|
parseLicenseToken(token) {
|
|
363
767
|
return new Promise((resolve, reject) => {
|
|
364
768
|
import_jsonwebtoken.default.verify(token, this.configuration.publicKey, {
|
|
@@ -423,11 +827,34 @@ var LicenseValidator = class {
|
|
|
423
827
|
});
|
|
424
828
|
}
|
|
425
829
|
};
|
|
830
|
+
function deviceMismatchError(expected, bound) {
|
|
831
|
+
const detail = "This license is not for this device";
|
|
832
|
+
const versionNote = describeVersionDifference(expected, bound);
|
|
833
|
+
return new MoonbaseError(
|
|
834
|
+
"License is for another device",
|
|
835
|
+
versionNote ? `${detail}. ${versionNote}` : detail,
|
|
836
|
+
"LicenseDeviceMismatch" /* LicenseDeviceMismatch */
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
function describeVersionDifference(expected, bound) {
|
|
840
|
+
const expectedStamp = parseDeviceIdStamp(expected);
|
|
841
|
+
if (!expectedStamp)
|
|
842
|
+
return null;
|
|
843
|
+
const boundStamp = parseDeviceIdStamp(bound);
|
|
844
|
+
if (boundStamp && boundStamp.version === expectedStamp.version)
|
|
845
|
+
return null;
|
|
846
|
+
if (boundStamp && boundStamp.version > expectedStamp.version) {
|
|
847
|
+
return `The binding was created by device fingerprint v${boundStamp.version}, which is newer than the v${expectedStamp.version} this SDK computes \u2014 update the SDK rather than re-activating, which would rebind the device to the older algorithm.`;
|
|
848
|
+
}
|
|
849
|
+
const boundVersion = boundStamp ? `device fingerprint v${boundStamp.version}` : "an SDK predating versioned device fingerprints";
|
|
850
|
+
return `The binding was created by ${boundVersion}, while this SDK computes v${expectedStamp.version}, so this may instead be the same machine bound under the older algorithm \u2014 re-activate to find out, or configure a MigratingDeviceIdResolver to keep accepting the previous id.`;
|
|
851
|
+
}
|
|
426
852
|
|
|
427
853
|
// src/index.ts
|
|
428
854
|
function detectPlatform() {
|
|
429
|
-
if (typeof
|
|
430
|
-
|
|
855
|
+
if (typeof import_node_process3.default === "undefined")
|
|
856
|
+
return void 0;
|
|
857
|
+
switch (import_node_process3.default.platform) {
|
|
431
858
|
case "darwin":
|
|
432
859
|
return "Mac";
|
|
433
860
|
case "win32":
|
|
@@ -447,7 +874,7 @@ var MoonbaseLicensing = class {
|
|
|
447
874
|
platform: configuration.platform === void 0 ? detectPlatform() : configuration.platform
|
|
448
875
|
};
|
|
449
876
|
this.store = (_a = configuration.licenseStore) != null ? _a : new InMemoryLicenseStore();
|
|
450
|
-
this.deviceIdResolver = (_b = configuration.
|
|
877
|
+
this.deviceIdResolver = (_b = configuration.deviceIdResolver) != null ? _b : new MoonbaseDeviceIdResolver();
|
|
451
878
|
this.validator = new LicenseValidator(this.configuration, this.deviceIdResolver);
|
|
452
879
|
this.client = new LicenseClient(this.configuration, this.deviceIdResolver, this.validator);
|
|
453
880
|
}
|
|
@@ -459,7 +886,7 @@ var MoonbaseLicensing = class {
|
|
|
459
886
|
format: "JWT"
|
|
460
887
|
};
|
|
461
888
|
const json = JSON.stringify(token);
|
|
462
|
-
return Buffer.from(Buffer.from(json).toString("base64"));
|
|
889
|
+
return import_node_buffer2.Buffer.from(import_node_buffer2.Buffer.from(json).toString("base64"));
|
|
463
890
|
}
|
|
464
891
|
async readRawLicense(license) {
|
|
465
892
|
return await this.client.validateRawLicense(license);
|
|
@@ -468,12 +895,31 @@ var MoonbaseLicensing = class {
|
|
|
468
895
|
// Annotate the CommonJS export names for ESM import in node:
|
|
469
896
|
0 && (module.exports = {
|
|
470
897
|
ActivationMethod,
|
|
471
|
-
DefaultDeviceIdResolver,
|
|
472
898
|
ErrorType,
|
|
899
|
+
FINGERPRINT_PREFIX,
|
|
900
|
+
FINGERPRINT_VERSION,
|
|
473
901
|
FileLicenseStore,
|
|
902
|
+
IDENTIFYING_PARAM_NAMES,
|
|
474
903
|
InMemoryLicenseStore,
|
|
904
|
+
InsufficientDeviceIdentityError,
|
|
905
|
+
LegacyDeviceIdResolver,
|
|
475
906
|
LicenseClient,
|
|
476
907
|
LicenseValidator,
|
|
908
|
+
MAX_VALUE_LENGTH,
|
|
909
|
+
MigratingDeviceIdResolver,
|
|
910
|
+
MoonbaseDeviceIdResolver,
|
|
477
911
|
MoonbaseError,
|
|
478
|
-
MoonbaseLicensing
|
|
912
|
+
MoonbaseLicensing,
|
|
913
|
+
buildFingerprintMaterial,
|
|
914
|
+
canonicalizeParams,
|
|
915
|
+
canonicalizeValue,
|
|
916
|
+
defaultDeviceIdentityReader,
|
|
917
|
+
fingerprintDeviceId,
|
|
918
|
+
fingerprintDigest,
|
|
919
|
+
parseDeviceIdStamp,
|
|
920
|
+
parseIoregPlatformUuid,
|
|
921
|
+
parseSmbiosParams,
|
|
922
|
+
platformTag,
|
|
923
|
+
selectMachineId,
|
|
924
|
+
stampDeviceId
|
|
479
925
|
});
|