@moonbase.sh/licensing 3.0.0 → 3.1.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 +241 -8
- package/README.md +12 -2
- package/dist/index.cjs +107 -20
- package/dist/index.d.cts +73 -20
- package/dist/index.d.ts +73 -20
- package/dist/index.js +105 -20
- package/fingerprint-vectors.json +204 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -33,11 +33,30 @@ declare const FINGERPRINT_VERSION = 2;
|
|
|
33
33
|
declare const MAX_VALUE_LENGTH = 128;
|
|
34
34
|
/**
|
|
35
35
|
* What the material was built from. `identity` is the real hardware fingerprint;
|
|
36
|
-
* `deviceName` is the opt-in, deliberately weaker host-name fallback
|
|
37
|
-
*
|
|
36
|
+
* `deviceName` is the opt-in, deliberately weaker host-name fallback; `scoped` is
|
|
37
|
+
* an id that is stable only within one app scope (iOS/Android, where the platform
|
|
38
|
+
* exposes nothing an unrelated app can read). Each is stamped distinctly so a
|
|
39
|
+
* server can tell them apart.
|
|
38
40
|
*/
|
|
39
|
-
type DeviceIdSource = 'identity' | 'deviceName';
|
|
40
|
-
|
|
41
|
+
type DeviceIdSource = 'identity' | 'deviceName' | 'scoped';
|
|
42
|
+
/** The tags this version defines, as a detached frozen map (see {@link IDENTIFYING_PARAM_NAMES}). */
|
|
43
|
+
declare const DEVICE_ID_SOURCE_TAGS: Readonly<Record<DeviceIdSource, string>>;
|
|
44
|
+
type PlatformTag = 'mac' | 'ios' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
|
|
45
|
+
/**
|
|
46
|
+
* The source a successful identity read earns on this platform.
|
|
47
|
+
*
|
|
48
|
+
* Scoped platforms expose no identifier an unrelated app can read, so anything
|
|
49
|
+
* built from their parameters is scoped to the app and must be stamped `mbd2s_`.
|
|
50
|
+
* Stamping it `mbd2_` would tell a server the id is a hardware fingerprint
|
|
51
|
+
* comparable across every app on the device, which is exactly what it is not:
|
|
52
|
+
* the server would then be entitled to correlate ids the spec forbids
|
|
53
|
+
* correlating, and a diagnostic would offer remedies that cannot apply.
|
|
54
|
+
*
|
|
55
|
+
* Derived from the platform rather than chosen by the caller, so a host that
|
|
56
|
+
* bridges `identifierForVendor` or `androidId` through a custom
|
|
57
|
+
* {@link DeviceIdentityReader} cannot accidentally mislabel it.
|
|
58
|
+
*/
|
|
59
|
+
declare function identitySource(platform: PlatformTag): DeviceIdSource;
|
|
41
60
|
type FingerprintParam = readonly [name: string, value: string];
|
|
42
61
|
interface DeviceIdentity {
|
|
43
62
|
/** Ordered identity params. Empty values are dropped by the material builder. */
|
|
@@ -53,7 +72,10 @@ interface DeviceIdentityReader {
|
|
|
53
72
|
interface DeviceIdStamp {
|
|
54
73
|
/** Fingerprint spec version that produced the digest. */
|
|
55
74
|
version: number;
|
|
56
|
-
source:
|
|
75
|
+
/** The literal source tag: `''`, `'n'`, `'s'`, or one a newer SDK introduced. */
|
|
76
|
+
sourceTag: string;
|
|
77
|
+
/** What {@link sourceTag} means, or `null` when this SDK does not define that tag. */
|
|
78
|
+
source: DeviceIdSource | null;
|
|
57
79
|
/** The 64-char lowercase-hex SHA-256. */
|
|
58
80
|
digest: string;
|
|
59
81
|
}
|
|
@@ -69,8 +91,23 @@ interface DeviceIdStamp {
|
|
|
69
91
|
* decodings disagree about is discarded either way.
|
|
70
92
|
*/
|
|
71
93
|
declare function canonicalizeValue(value: string): string;
|
|
72
|
-
/**
|
|
73
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Map a Node.js `process.platform` value onto a canonical platform tag, or pass
|
|
96
|
+
* through a value that is already one.
|
|
97
|
+
*
|
|
98
|
+
* The passthrough is what makes the mobile platforms reachable at all. Node does
|
|
99
|
+
* not run on iOS, so `process.platform` is never `'ios'` and no amount of mapping
|
|
100
|
+
* can produce that tag — yet a host embedding Node can bridge
|
|
101
|
+
* `identifierForVendor` from platform API. Without this, such a host has no way
|
|
102
|
+
* to say which platform it is on: the value would fall through to `'unknown'`,
|
|
103
|
+
* and the material would carry `platform=unknown`, so the digest would not match
|
|
104
|
+
* what a conforming iOS SDK computes on the same device, and the id would be
|
|
105
|
+
* stamped `mbd2_` rather than `mbd2s_`. Both wrong, and both silent.
|
|
106
|
+
*
|
|
107
|
+
* `'android'` and `'linux'` belong to both vocabularies and map to themselves, so
|
|
108
|
+
* the two spellings cannot disagree.
|
|
109
|
+
*/
|
|
110
|
+
declare function platformTag(platform?: NodeJS.Platform | PlatformTag): PlatformTag;
|
|
74
111
|
/**
|
|
75
112
|
* The identifying parameter names, as a detached frozen list.
|
|
76
113
|
*
|
|
@@ -107,9 +144,12 @@ declare function stampDeviceId(digest: string, source?: DeviceIdSource): string;
|
|
|
107
144
|
declare function fingerprintDeviceId(material: string, source?: DeviceIdSource): string;
|
|
108
145
|
/**
|
|
109
146
|
* Split a stamped device id into its parts, or `null` if it is not a Moonbase
|
|
110
|
-
* stamp (a legacy id, or one from a custom resolver). Lets a validator tell
|
|
147
|
+
* stamp at all (a legacy id, or one from a custom resolver). Lets a validator tell
|
|
111
148
|
* "this license belongs to another machine" apart from "this license was bound
|
|
112
149
|
* by an older fingerprint version".
|
|
150
|
+
*
|
|
151
|
+
* An id whose *tag* is unrecognised still parses, with `source` null: it came from
|
|
152
|
+
* a newer SDK, and reporting it as unparseable would be worse than saying so.
|
|
113
153
|
*/
|
|
114
154
|
declare function parseDeviceIdStamp(deviceId: string): DeviceIdStamp | null;
|
|
115
155
|
/** Extract `IOPlatformUUID` from `ioreg` output: hyphens stripped, uppercased (spec: macOS `ioPlatformUuid`). */
|
|
@@ -136,7 +176,7 @@ declare function selectMachineId(...sources: string[]): string;
|
|
|
136
176
|
*/
|
|
137
177
|
declare function parseSmbiosParams(smbiosData: Buffer): FingerprintParam[];
|
|
138
178
|
/** The real, platform-dispatching identity reader used by {@link MoonbaseDeviceIdResolver}. */
|
|
139
|
-
declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform): DeviceIdentityReader;
|
|
179
|
+
declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform | PlatformTag): DeviceIdentityReader;
|
|
140
180
|
|
|
141
181
|
interface IDeviceIdResolver {
|
|
142
182
|
resolveDeviceName: () => Promise<string>;
|
|
@@ -181,8 +221,16 @@ interface IMigratingDeviceIdResolver extends IDeviceIdResolver {
|
|
|
181
221
|
interface MoonbaseDeviceIdResolverOptions {
|
|
182
222
|
/** Overrides the identity source. Primarily for testing. */
|
|
183
223
|
reader?: DeviceIdentityReader;
|
|
184
|
-
/**
|
|
185
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Overrides the detected platform. Accepts a Node.js `process.platform` value
|
|
226
|
+
* or a canonical {@link PlatformTag}.
|
|
227
|
+
*
|
|
228
|
+
* The tag spelling exists for hosts that bridge a platform Node does not run
|
|
229
|
+
* on: pass `'ios'` together with a {@link reader} that supplies
|
|
230
|
+
* `identifierForVendor`. Without it the platform would resolve to `'unknown'`,
|
|
231
|
+
* which changes the material and so the device id.
|
|
232
|
+
*/
|
|
233
|
+
platform?: NodeJS.Platform | PlatformTag;
|
|
186
234
|
/**
|
|
187
235
|
* What to do when no hardware identity is readable. `'none'` (the default)
|
|
188
236
|
* throws {@link InsufficientDeviceIdentityError}; `'deviceName'` falls back to
|
|
@@ -190,6 +238,10 @@ interface MoonbaseDeviceIdResolverOptions {
|
|
|
190
238
|
*
|
|
191
239
|
* The fallback is opt-in because a host name is user-renameable, frequently
|
|
192
240
|
* duplicated across imaged machines, and regenerated on every container start.
|
|
241
|
+
*
|
|
242
|
+
* It is **ignored on iOS and Android**, which throw regardless: there the host
|
|
243
|
+
* name is identical on every device, so the fallback would give a whole install
|
|
244
|
+
* base one id rather than merely a weak one.
|
|
193
245
|
*/
|
|
194
246
|
fallback?: 'none' | 'deviceName';
|
|
195
247
|
}
|
|
@@ -575,14 +627,15 @@ declare class MoonbaseError extends Error {
|
|
|
575
627
|
* either would hand a whole class of machines the *same* device id, and a license
|
|
576
628
|
* bound to it would validate on all of them.
|
|
577
629
|
*
|
|
578
|
-
* Reachable on platforms with no
|
|
579
|
-
* anything unknown
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
630
|
+
* Reachable on platforms with no identity parameters this package can read (BSD
|
|
631
|
+
* and anything unknown, which the spec leaves undefined, plus Android, whose
|
|
632
|
+
* `androidId` needs Android-framework API a Node.js process cannot call); when
|
|
633
|
+
* every source fails — a sandboxed process that cannot spawn `ioreg`, a container
|
|
634
|
+
* with no DMI, a blocked PowerShell; and on machines whose per-device identifiers
|
|
635
|
+
* are simply absent, such as a Linux install with no `machine-id` or a VM whose
|
|
636
|
+
* SMBIOS carries an unset UUID and a blank baseboard serial. Enable the host-name
|
|
637
|
+
* fallback (`new MoonbaseDeviceIdResolver({ fallback: 'deviceName' })`) to accept
|
|
638
|
+
* a deliberately weaker id on those machines.
|
|
586
639
|
*/
|
|
587
640
|
declare class InsufficientDeviceIdentityError extends MoonbaseError {
|
|
588
641
|
readonly platform: string;
|
|
@@ -631,4 +684,4 @@ declare class MoonbaseLicensing {
|
|
|
631
684
|
readRawLicense(license: Buffer): Promise<License>;
|
|
632
685
|
}
|
|
633
686
|
|
|
634
|
-
export { ActivationMethod, type ActivationRequestResponse, type DeviceIdDescription, type DeviceIdSource, type DeviceIdStamp, type DeviceIdentity, type DeviceIdentityReader, type DeviceToken, ErrorType, FINGERPRINT_PREFIX, FINGERPRINT_VERSION, FileLicenseStore, type FingerprintParam, IDENTIFYING_PARAM_NAMES, type IDescribableDeviceIdResolver, type IDeviceIdResolver, type ILicenseClient, type ILicenseStore, type ILicenseValidator, type IMigratingDeviceIdResolver, InMemoryLicenseStore, InsufficientDeviceIdentityError, LegacyDeviceIdResolver, type License, LicenseClient, LicenseValidator, MAX_VALUE_LENGTH, type Metadata, MigratingDeviceIdResolver, type MoonbaseConfiguration, MoonbaseDeviceIdResolver, type MoonbaseDeviceIdResolverOptions, MoonbaseError, MoonbaseLicensing, type Platform, type PlatformTag, type Product, type User, buildFingerprintMaterial, canonicalizeParams, canonicalizeValue, defaultDeviceIdentityReader, fingerprintDeviceId, fingerprintDigest, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
|
|
687
|
+
export { ActivationMethod, type ActivationRequestResponse, DEVICE_ID_SOURCE_TAGS, type DeviceIdDescription, type DeviceIdSource, type DeviceIdStamp, type DeviceIdentity, type DeviceIdentityReader, type DeviceToken, ErrorType, FINGERPRINT_PREFIX, FINGERPRINT_VERSION, FileLicenseStore, type FingerprintParam, IDENTIFYING_PARAM_NAMES, type IDescribableDeviceIdResolver, type IDeviceIdResolver, type ILicenseClient, type ILicenseStore, type ILicenseValidator, type IMigratingDeviceIdResolver, InMemoryLicenseStore, InsufficientDeviceIdentityError, LegacyDeviceIdResolver, type License, LicenseClient, LicenseValidator, MAX_VALUE_LENGTH, type Metadata, MigratingDeviceIdResolver, type MoonbaseConfiguration, MoonbaseDeviceIdResolver, type MoonbaseDeviceIdResolverOptions, MoonbaseError, MoonbaseLicensing, type Platform, type PlatformTag, type Product, type User, buildFingerprintMaterial, canonicalizeParams, canonicalizeValue, defaultDeviceIdentityReader, fingerprintDeviceId, fingerprintDigest, identitySource, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
|
package/dist/index.d.ts
CHANGED
|
@@ -33,11 +33,30 @@ declare const FINGERPRINT_VERSION = 2;
|
|
|
33
33
|
declare const MAX_VALUE_LENGTH = 128;
|
|
34
34
|
/**
|
|
35
35
|
* What the material was built from. `identity` is the real hardware fingerprint;
|
|
36
|
-
* `deviceName` is the opt-in, deliberately weaker host-name fallback
|
|
37
|
-
*
|
|
36
|
+
* `deviceName` is the opt-in, deliberately weaker host-name fallback; `scoped` is
|
|
37
|
+
* an id that is stable only within one app scope (iOS/Android, where the platform
|
|
38
|
+
* exposes nothing an unrelated app can read). Each is stamped distinctly so a
|
|
39
|
+
* server can tell them apart.
|
|
38
40
|
*/
|
|
39
|
-
type DeviceIdSource = 'identity' | 'deviceName';
|
|
40
|
-
|
|
41
|
+
type DeviceIdSource = 'identity' | 'deviceName' | 'scoped';
|
|
42
|
+
/** The tags this version defines, as a detached frozen map (see {@link IDENTIFYING_PARAM_NAMES}). */
|
|
43
|
+
declare const DEVICE_ID_SOURCE_TAGS: Readonly<Record<DeviceIdSource, string>>;
|
|
44
|
+
type PlatformTag = 'mac' | 'ios' | 'windows' | 'android' | 'linux' | 'bsd' | 'unknown';
|
|
45
|
+
/**
|
|
46
|
+
* The source a successful identity read earns on this platform.
|
|
47
|
+
*
|
|
48
|
+
* Scoped platforms expose no identifier an unrelated app can read, so anything
|
|
49
|
+
* built from their parameters is scoped to the app and must be stamped `mbd2s_`.
|
|
50
|
+
* Stamping it `mbd2_` would tell a server the id is a hardware fingerprint
|
|
51
|
+
* comparable across every app on the device, which is exactly what it is not:
|
|
52
|
+
* the server would then be entitled to correlate ids the spec forbids
|
|
53
|
+
* correlating, and a diagnostic would offer remedies that cannot apply.
|
|
54
|
+
*
|
|
55
|
+
* Derived from the platform rather than chosen by the caller, so a host that
|
|
56
|
+
* bridges `identifierForVendor` or `androidId` through a custom
|
|
57
|
+
* {@link DeviceIdentityReader} cannot accidentally mislabel it.
|
|
58
|
+
*/
|
|
59
|
+
declare function identitySource(platform: PlatformTag): DeviceIdSource;
|
|
41
60
|
type FingerprintParam = readonly [name: string, value: string];
|
|
42
61
|
interface DeviceIdentity {
|
|
43
62
|
/** Ordered identity params. Empty values are dropped by the material builder. */
|
|
@@ -53,7 +72,10 @@ interface DeviceIdentityReader {
|
|
|
53
72
|
interface DeviceIdStamp {
|
|
54
73
|
/** Fingerprint spec version that produced the digest. */
|
|
55
74
|
version: number;
|
|
56
|
-
source:
|
|
75
|
+
/** The literal source tag: `''`, `'n'`, `'s'`, or one a newer SDK introduced. */
|
|
76
|
+
sourceTag: string;
|
|
77
|
+
/** What {@link sourceTag} means, or `null` when this SDK does not define that tag. */
|
|
78
|
+
source: DeviceIdSource | null;
|
|
57
79
|
/** The 64-char lowercase-hex SHA-256. */
|
|
58
80
|
digest: string;
|
|
59
81
|
}
|
|
@@ -69,8 +91,23 @@ interface DeviceIdStamp {
|
|
|
69
91
|
* decodings disagree about is discarded either way.
|
|
70
92
|
*/
|
|
71
93
|
declare function canonicalizeValue(value: string): string;
|
|
72
|
-
/**
|
|
73
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Map a Node.js `process.platform` value onto a canonical platform tag, or pass
|
|
96
|
+
* through a value that is already one.
|
|
97
|
+
*
|
|
98
|
+
* The passthrough is what makes the mobile platforms reachable at all. Node does
|
|
99
|
+
* not run on iOS, so `process.platform` is never `'ios'` and no amount of mapping
|
|
100
|
+
* can produce that tag — yet a host embedding Node can bridge
|
|
101
|
+
* `identifierForVendor` from platform API. Without this, such a host has no way
|
|
102
|
+
* to say which platform it is on: the value would fall through to `'unknown'`,
|
|
103
|
+
* and the material would carry `platform=unknown`, so the digest would not match
|
|
104
|
+
* what a conforming iOS SDK computes on the same device, and the id would be
|
|
105
|
+
* stamped `mbd2_` rather than `mbd2s_`. Both wrong, and both silent.
|
|
106
|
+
*
|
|
107
|
+
* `'android'` and `'linux'` belong to both vocabularies and map to themselves, so
|
|
108
|
+
* the two spellings cannot disagree.
|
|
109
|
+
*/
|
|
110
|
+
declare function platformTag(platform?: NodeJS.Platform | PlatformTag): PlatformTag;
|
|
74
111
|
/**
|
|
75
112
|
* The identifying parameter names, as a detached frozen list.
|
|
76
113
|
*
|
|
@@ -107,9 +144,12 @@ declare function stampDeviceId(digest: string, source?: DeviceIdSource): string;
|
|
|
107
144
|
declare function fingerprintDeviceId(material: string, source?: DeviceIdSource): string;
|
|
108
145
|
/**
|
|
109
146
|
* Split a stamped device id into its parts, or `null` if it is not a Moonbase
|
|
110
|
-
* stamp (a legacy id, or one from a custom resolver). Lets a validator tell
|
|
147
|
+
* stamp at all (a legacy id, or one from a custom resolver). Lets a validator tell
|
|
111
148
|
* "this license belongs to another machine" apart from "this license was bound
|
|
112
149
|
* by an older fingerprint version".
|
|
150
|
+
*
|
|
151
|
+
* An id whose *tag* is unrecognised still parses, with `source` null: it came from
|
|
152
|
+
* a newer SDK, and reporting it as unparseable would be worse than saying so.
|
|
113
153
|
*/
|
|
114
154
|
declare function parseDeviceIdStamp(deviceId: string): DeviceIdStamp | null;
|
|
115
155
|
/** Extract `IOPlatformUUID` from `ioreg` output: hyphens stripped, uppercased (spec: macOS `ioPlatformUuid`). */
|
|
@@ -136,7 +176,7 @@ declare function selectMachineId(...sources: string[]): string;
|
|
|
136
176
|
*/
|
|
137
177
|
declare function parseSmbiosParams(smbiosData: Buffer): FingerprintParam[];
|
|
138
178
|
/** The real, platform-dispatching identity reader used by {@link MoonbaseDeviceIdResolver}. */
|
|
139
|
-
declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform): DeviceIdentityReader;
|
|
179
|
+
declare function defaultDeviceIdentityReader(platform?: NodeJS.Platform | PlatformTag): DeviceIdentityReader;
|
|
140
180
|
|
|
141
181
|
interface IDeviceIdResolver {
|
|
142
182
|
resolveDeviceName: () => Promise<string>;
|
|
@@ -181,8 +221,16 @@ interface IMigratingDeviceIdResolver extends IDeviceIdResolver {
|
|
|
181
221
|
interface MoonbaseDeviceIdResolverOptions {
|
|
182
222
|
/** Overrides the identity source. Primarily for testing. */
|
|
183
223
|
reader?: DeviceIdentityReader;
|
|
184
|
-
/**
|
|
185
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Overrides the detected platform. Accepts a Node.js `process.platform` value
|
|
226
|
+
* or a canonical {@link PlatformTag}.
|
|
227
|
+
*
|
|
228
|
+
* The tag spelling exists for hosts that bridge a platform Node does not run
|
|
229
|
+
* on: pass `'ios'` together with a {@link reader} that supplies
|
|
230
|
+
* `identifierForVendor`. Without it the platform would resolve to `'unknown'`,
|
|
231
|
+
* which changes the material and so the device id.
|
|
232
|
+
*/
|
|
233
|
+
platform?: NodeJS.Platform | PlatformTag;
|
|
186
234
|
/**
|
|
187
235
|
* What to do when no hardware identity is readable. `'none'` (the default)
|
|
188
236
|
* throws {@link InsufficientDeviceIdentityError}; `'deviceName'` falls back to
|
|
@@ -190,6 +238,10 @@ interface MoonbaseDeviceIdResolverOptions {
|
|
|
190
238
|
*
|
|
191
239
|
* The fallback is opt-in because a host name is user-renameable, frequently
|
|
192
240
|
* duplicated across imaged machines, and regenerated on every container start.
|
|
241
|
+
*
|
|
242
|
+
* It is **ignored on iOS and Android**, which throw regardless: there the host
|
|
243
|
+
* name is identical on every device, so the fallback would give a whole install
|
|
244
|
+
* base one id rather than merely a weak one.
|
|
193
245
|
*/
|
|
194
246
|
fallback?: 'none' | 'deviceName';
|
|
195
247
|
}
|
|
@@ -575,14 +627,15 @@ declare class MoonbaseError extends Error {
|
|
|
575
627
|
* either would hand a whole class of machines the *same* device id, and a license
|
|
576
628
|
* bound to it would validate on all of them.
|
|
577
629
|
*
|
|
578
|
-
* Reachable on platforms with no
|
|
579
|
-
* anything unknown
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
630
|
+
* Reachable on platforms with no identity parameters this package can read (BSD
|
|
631
|
+
* and anything unknown, which the spec leaves undefined, plus Android, whose
|
|
632
|
+
* `androidId` needs Android-framework API a Node.js process cannot call); when
|
|
633
|
+
* every source fails — a sandboxed process that cannot spawn `ioreg`, a container
|
|
634
|
+
* with no DMI, a blocked PowerShell; and on machines whose per-device identifiers
|
|
635
|
+
* are simply absent, such as a Linux install with no `machine-id` or a VM whose
|
|
636
|
+
* SMBIOS carries an unset UUID and a blank baseboard serial. Enable the host-name
|
|
637
|
+
* fallback (`new MoonbaseDeviceIdResolver({ fallback: 'deviceName' })`) to accept
|
|
638
|
+
* a deliberately weaker id on those machines.
|
|
586
639
|
*/
|
|
587
640
|
declare class InsufficientDeviceIdentityError extends MoonbaseError {
|
|
588
641
|
readonly platform: string;
|
|
@@ -631,4 +684,4 @@ declare class MoonbaseLicensing {
|
|
|
631
684
|
readRawLicense(license: Buffer): Promise<License>;
|
|
632
685
|
}
|
|
633
686
|
|
|
634
|
-
export { ActivationMethod, type ActivationRequestResponse, type DeviceIdDescription, type DeviceIdSource, type DeviceIdStamp, type DeviceIdentity, type DeviceIdentityReader, type DeviceToken, ErrorType, FINGERPRINT_PREFIX, FINGERPRINT_VERSION, FileLicenseStore, type FingerprintParam, IDENTIFYING_PARAM_NAMES, type IDescribableDeviceIdResolver, type IDeviceIdResolver, type ILicenseClient, type ILicenseStore, type ILicenseValidator, type IMigratingDeviceIdResolver, InMemoryLicenseStore, InsufficientDeviceIdentityError, LegacyDeviceIdResolver, type License, LicenseClient, LicenseValidator, MAX_VALUE_LENGTH, type Metadata, MigratingDeviceIdResolver, type MoonbaseConfiguration, MoonbaseDeviceIdResolver, type MoonbaseDeviceIdResolverOptions, MoonbaseError, MoonbaseLicensing, type Platform, type PlatformTag, type Product, type User, buildFingerprintMaterial, canonicalizeParams, canonicalizeValue, defaultDeviceIdentityReader, fingerprintDeviceId, fingerprintDigest, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
|
|
687
|
+
export { ActivationMethod, type ActivationRequestResponse, DEVICE_ID_SOURCE_TAGS, type DeviceIdDescription, type DeviceIdSource, type DeviceIdStamp, type DeviceIdentity, type DeviceIdentityReader, type DeviceToken, ErrorType, FINGERPRINT_PREFIX, FINGERPRINT_VERSION, FileLicenseStore, type FingerprintParam, IDENTIFYING_PARAM_NAMES, type IDescribableDeviceIdResolver, type IDeviceIdResolver, type ILicenseClient, type ILicenseStore, type ILicenseValidator, type IMigratingDeviceIdResolver, InMemoryLicenseStore, InsufficientDeviceIdentityError, LegacyDeviceIdResolver, type License, LicenseClient, LicenseValidator, MAX_VALUE_LENGTH, type Metadata, MigratingDeviceIdResolver, type MoonbaseConfiguration, MoonbaseDeviceIdResolver, type MoonbaseDeviceIdResolverOptions, MoonbaseError, MoonbaseLicensing, type Platform, type PlatformTag, type Product, type User, buildFingerprintMaterial, canonicalizeParams, canonicalizeValue, defaultDeviceIdentityReader, fingerprintDeviceId, fingerprintDigest, identitySource, parseDeviceIdStamp, parseIoregPlatformUuid, parseSmbiosParams, platformTag, selectMachineId, stampDeviceId };
|
package/dist/index.js
CHANGED
|
@@ -222,7 +222,20 @@ var FINGERPRINT_VERSION = 2;
|
|
|
222
222
|
var MAX_VALUE_LENGTH = 128;
|
|
223
223
|
var PRINTABLE_ASCII_MIN = 32;
|
|
224
224
|
var PRINTABLE_ASCII_MAX = 126;
|
|
225
|
-
var
|
|
225
|
+
var SOURCE_TAGS = {
|
|
226
|
+
identity: "",
|
|
227
|
+
deviceName: "n",
|
|
228
|
+
scoped: "s"
|
|
229
|
+
};
|
|
230
|
+
var DEVICE_ID_SOURCE_TAGS = Object.freeze({ ...SOURCE_TAGS });
|
|
231
|
+
var SOURCE_BY_TAG = new Map(
|
|
232
|
+
Object.entries(SOURCE_TAGS).map(([source, tag]) => [tag, source])
|
|
233
|
+
);
|
|
234
|
+
var SCOPED_PLATFORMS = /* @__PURE__ */ new Set(["ios", "android"]);
|
|
235
|
+
function identitySource(platform) {
|
|
236
|
+
return SCOPED_PLATFORMS.has(platform) ? "scoped" : "identity";
|
|
237
|
+
}
|
|
238
|
+
var STAMP_PATTERN = /^mbd(\d+)([a-z]*)_([0-9a-f]{64})$/;
|
|
226
239
|
function canonicalizeValue(value) {
|
|
227
240
|
var _a;
|
|
228
241
|
let printable = "";
|
|
@@ -233,6 +246,12 @@ function canonicalizeValue(value) {
|
|
|
233
246
|
}
|
|
234
247
|
return printable.slice(0, MAX_VALUE_LENGTH).replace(/^ +| +$/g, "");
|
|
235
248
|
}
|
|
249
|
+
var PLATFORM_TAGS = /* @__PURE__ */ new Set(
|
|
250
|
+
["mac", "ios", "windows", "android", "linux", "bsd", "unknown"]
|
|
251
|
+
);
|
|
252
|
+
function isPlatformTag(value) {
|
|
253
|
+
return PLATFORM_TAGS.has(value);
|
|
254
|
+
}
|
|
236
255
|
function platformTag(platform = process.platform) {
|
|
237
256
|
switch (platform) {
|
|
238
257
|
case "darwin":
|
|
@@ -248,7 +267,7 @@ function platformTag(platform = process.platform) {
|
|
|
248
267
|
case "netbsd":
|
|
249
268
|
return "bsd";
|
|
250
269
|
default:
|
|
251
|
-
return "unknown";
|
|
270
|
+
return isPlatformTag(platform) ? platform : "unknown";
|
|
252
271
|
}
|
|
253
272
|
}
|
|
254
273
|
var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -256,6 +275,8 @@ var IDENTIFYING_PARAMS = /* @__PURE__ */ new Set([
|
|
|
256
275
|
"machineId",
|
|
257
276
|
"systemUuid",
|
|
258
277
|
"baseboardSerialNumber",
|
|
278
|
+
"identifierForVendor",
|
|
279
|
+
"androidId",
|
|
259
280
|
"deviceName"
|
|
260
281
|
]);
|
|
261
282
|
var IDENTIFYING_PARAM_NAMES = Object.freeze([...IDENTIFYING_PARAMS]);
|
|
@@ -280,9 +301,35 @@ var NOT_PROGRAMMED_VALUES = /* @__PURE__ */ new Set([
|
|
|
280
301
|
// deployed from such an image reads it, so it is the opposite of an identifier.
|
|
281
302
|
"uninitialized"
|
|
282
303
|
]);
|
|
304
|
+
var IDENTITY_CONSTRAINTS = {
|
|
305
|
+
androidId: {
|
|
306
|
+
// Reading the *static field* `Settings.Secure.ANDROID_ID` instead of calling
|
|
307
|
+
// `getString` with it yields the key name "android_id", identical on every
|
|
308
|
+
// device, so a single activation would unlock a whole Android install base.
|
|
309
|
+
// That string is not hex, so this stops it reaching the material.
|
|
310
|
+
//
|
|
311
|
+
// The bound is 1,16 and not 16 because AOSP before 8.0 generated the value
|
|
312
|
+
// with `Long.toHexString`, which drops leading zeros — a strict 16 would
|
|
313
|
+
// reject legitimate ids on roughly one in sixteen pre-Oreo devices.
|
|
314
|
+
format: /^[0-9a-f]{1,16}$/,
|
|
315
|
+
// A real ANDROID_ID shared by a large batch of 2010-era devices whose
|
|
316
|
+
// `ro.serialno` was unset, seeding the generator identically on every unit.
|
|
317
|
+
// Valid hex, so the format rule cannot catch it.
|
|
318
|
+
rejected: /* @__PURE__ */ new Set(["9774d56d682e549c"])
|
|
319
|
+
}
|
|
320
|
+
};
|
|
283
321
|
function isNotProgrammed(value) {
|
|
284
322
|
return NOT_PROGRAMMED_VALUES.has(value.toLowerCase()) || /^0+$/.test(value) || /^f+$/i.test(value);
|
|
285
323
|
}
|
|
324
|
+
function isUsableIdentity(name, value) {
|
|
325
|
+
var _a, _b, _c;
|
|
326
|
+
if (isNotProgrammed(value))
|
|
327
|
+
return false;
|
|
328
|
+
const constraint = IDENTITY_CONSTRAINTS[name];
|
|
329
|
+
if (!constraint)
|
|
330
|
+
return true;
|
|
331
|
+
return ((_b = (_a = constraint.format) == null ? void 0 : _a.test(value)) != null ? _b : true) && !((_c = constraint.rejected) == null ? void 0 : _c.has(value.toLowerCase()));
|
|
332
|
+
}
|
|
286
333
|
function canonicalizeParams(params) {
|
|
287
334
|
const kept = [];
|
|
288
335
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -290,7 +337,7 @@ function canonicalizeParams(params) {
|
|
|
290
337
|
const value = canonicalizeValue(rawValue);
|
|
291
338
|
if (value.length === 0)
|
|
292
339
|
continue;
|
|
293
|
-
if (IDENTIFYING_PARAMS.has(name) &&
|
|
340
|
+
if (IDENTIFYING_PARAMS.has(name) && !isUsableIdentity(name, value))
|
|
294
341
|
continue;
|
|
295
342
|
if (seen.has(name))
|
|
296
343
|
throw new Error(`Duplicate fingerprint parameter name: ${name}`);
|
|
@@ -303,6 +350,12 @@ function buildFingerprintMaterial(platform, params) {
|
|
|
303
350
|
const kept = canonicalizeParams(params);
|
|
304
351
|
if (kept.length === 0)
|
|
305
352
|
throw new InsufficientDeviceIdentityError(platform, "no identity parameter could be read");
|
|
353
|
+
if (SCOPED_PLATFORMS.has(platform) && kept.some(([name]) => name === "deviceName")) {
|
|
354
|
+
throw new InsufficientDeviceIdentityError(
|
|
355
|
+
platform,
|
|
356
|
+
"the host-name fallback is not available on this platform, where the host name is the same on every device"
|
|
357
|
+
);
|
|
358
|
+
}
|
|
306
359
|
if (!kept.some(([name]) => IDENTIFYING_PARAMS.has(name))) {
|
|
307
360
|
throw new InsufficientDeviceIdentityError(
|
|
308
361
|
platform,
|
|
@@ -318,18 +371,20 @@ function fingerprintDigest(material) {
|
|
|
318
371
|
return createHash("sha256").update(material, "utf8").digest("hex");
|
|
319
372
|
}
|
|
320
373
|
function stampDeviceId(digest, source = "identity") {
|
|
321
|
-
return `mbd${FINGERPRINT_VERSION}${source
|
|
374
|
+
return `mbd${FINGERPRINT_VERSION}${SOURCE_TAGS[source]}_${digest}`;
|
|
322
375
|
}
|
|
323
376
|
function fingerprintDeviceId(material, source = "identity") {
|
|
324
377
|
return stampDeviceId(fingerprintDigest(material), source);
|
|
325
378
|
}
|
|
326
379
|
function parseDeviceIdStamp(deviceId) {
|
|
380
|
+
var _a;
|
|
327
381
|
const match = STAMP_PATTERN.exec(deviceId);
|
|
328
382
|
if (!match)
|
|
329
383
|
return null;
|
|
330
384
|
return {
|
|
331
385
|
version: Number(match[1]),
|
|
332
|
-
|
|
386
|
+
sourceTag: match[2],
|
|
387
|
+
source: (_a = SOURCE_BY_TAG.get(match[2])) != null ? _a : null,
|
|
333
388
|
digest: match[3]
|
|
334
389
|
};
|
|
335
390
|
}
|
|
@@ -458,18 +513,23 @@ function readWindowsIdentity() {
|
|
|
458
513
|
return { params: parseSmbiosParams(readWindowsSmbios()), deviceName: os.hostname() };
|
|
459
514
|
}
|
|
460
515
|
function defaultDeviceIdentityReader(platform = process.platform) {
|
|
516
|
+
const tag = platformTag(platform);
|
|
461
517
|
return {
|
|
462
518
|
read() {
|
|
463
|
-
switch (
|
|
464
|
-
case "
|
|
519
|
+
switch (tag) {
|
|
520
|
+
case "mac":
|
|
465
521
|
return readMacIdentity();
|
|
466
522
|
case "linux":
|
|
467
523
|
return readLinuxIdentity();
|
|
468
|
-
case "
|
|
524
|
+
case "windows":
|
|
469
525
|
return readWindowsIdentity();
|
|
470
|
-
//
|
|
471
|
-
// has no stable hardware source for them, so they resolve to an
|
|
526
|
+
// Everything else yields no identity params here, so it resolves to an
|
|
472
527
|
// insufficient-identity error unless the deviceName fallback is enabled.
|
|
528
|
+
// For bsd and unknown that matches the spec, which defines none. Android
|
|
529
|
+
// is different: the spec *does* define `androidId`, but it comes from
|
|
530
|
+
// `Settings.Secure.getString`, which is Android-framework API that a
|
|
531
|
+
// Node.js process cannot reach. Collecting it is the C++/.NET SDKs' job.
|
|
532
|
+
// A host that can bridge it should supply its own DeviceIdentityReader.
|
|
473
533
|
default:
|
|
474
534
|
return { params: [], deviceName: os.hostname() };
|
|
475
535
|
}
|
|
@@ -521,7 +581,7 @@ var MoonbaseDeviceIdResolver = class {
|
|
|
521
581
|
computeDescription() {
|
|
522
582
|
const { params, deviceName } = this.readIdentity();
|
|
523
583
|
try {
|
|
524
|
-
return this.describe(params,
|
|
584
|
+
return this.describe(params, identitySource(this.platform));
|
|
525
585
|
} catch (err) {
|
|
526
586
|
if (this.fallback !== "deviceName" || !(err instanceof InsufficientDeviceIdentityError))
|
|
527
587
|
throw err;
|
|
@@ -768,25 +828,48 @@ var LicenseValidator = class {
|
|
|
768
828
|
};
|
|
769
829
|
function deviceMismatchError(expected, bound) {
|
|
770
830
|
const detail = "This license is not for this device";
|
|
771
|
-
const
|
|
831
|
+
const stampNote = describeStampDifference(expected, bound);
|
|
772
832
|
return new MoonbaseError(
|
|
773
833
|
"License is for another device",
|
|
774
|
-
|
|
834
|
+
stampNote ? `${detail}. ${stampNote}` : detail,
|
|
775
835
|
"LicenseDeviceMismatch" /* LicenseDeviceMismatch */
|
|
776
836
|
);
|
|
777
837
|
}
|
|
778
|
-
function
|
|
838
|
+
function describeStampDifference(expected, bound) {
|
|
779
839
|
const expectedStamp = parseDeviceIdStamp(expected);
|
|
780
840
|
if (!expectedStamp)
|
|
781
841
|
return null;
|
|
782
842
|
const boundStamp = parseDeviceIdStamp(bound);
|
|
783
|
-
if (boundStamp
|
|
784
|
-
return
|
|
785
|
-
if (boundStamp
|
|
786
|
-
return
|
|
843
|
+
if (!boundStamp || boundStamp.version !== expectedStamp.version)
|
|
844
|
+
return describeVersionDifference(expectedStamp, boundStamp);
|
|
845
|
+
if (boundStamp.sourceTag !== expectedStamp.sourceTag)
|
|
846
|
+
return describeSourceDifference(expectedStamp, boundStamp);
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
function describeVersionDifference(expected, bound) {
|
|
850
|
+
if (bound && bound.version > expected.version) {
|
|
851
|
+
return `The binding was created by device fingerprint v${bound.version}, which is newer than the v${expected.version} this SDK computes \u2014 update the SDK rather than re-activating, which would rebind the device to the older algorithm.`;
|
|
852
|
+
}
|
|
853
|
+
const boundVersion = bound ? `device fingerprint v${bound.version}` : "an SDK predating versioned device fingerprints";
|
|
854
|
+
return `The binding was created by ${boundVersion}, while this SDK computes v${expected.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.`;
|
|
855
|
+
}
|
|
856
|
+
function describeSourceDifference(expected, bound) {
|
|
857
|
+
if (bound.source === "scoped") {
|
|
858
|
+
return "The binding uses an app-scoped device identity, which cannot be compared with the id this SDK computes \u2014 not even on the same device. Re-activate here to bind this build.";
|
|
859
|
+
}
|
|
860
|
+
if (expected.source === "scoped") {
|
|
861
|
+
return "This SDK computes an app-scoped device identity, which cannot be compared with the one the binding carries \u2014 not even on the same device. Re-activate here to bind this app.";
|
|
862
|
+
}
|
|
863
|
+
if (bound.source === null) {
|
|
864
|
+
return `The binding carries the device identity tag "${bound.sourceTag}", which this SDK does not recognise \u2014 it was created by a newer Moonbase SDK, so update rather than re-activating.`;
|
|
865
|
+
}
|
|
866
|
+
if (expected.source === null) {
|
|
867
|
+
return `The id this SDK computes carries the device identity tag "${expected.sourceTag}", which the spec does not define \u2014 it came from a custom device id resolver, so check that resolver rather than the binding.`;
|
|
868
|
+
}
|
|
869
|
+
if (bound.source === "deviceName") {
|
|
870
|
+
return "The binding was created from the host-name fallback, while this SDK reads hardware identity, so this may instead be the same machine bound while no hardware identity could be read \u2014 re-activate to find out.";
|
|
787
871
|
}
|
|
788
|
-
|
|
789
|
-
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.`;
|
|
872
|
+
return "The binding was created from hardware identity, while this SDK has fallen back to the host name \u2014 check why hardware identity cannot be read here rather than re-activating, which would rebind the device to the weaker id.";
|
|
790
873
|
}
|
|
791
874
|
|
|
792
875
|
// src/index.ts
|
|
@@ -833,6 +916,7 @@ var MoonbaseLicensing = class {
|
|
|
833
916
|
};
|
|
834
917
|
export {
|
|
835
918
|
ActivationMethod,
|
|
919
|
+
DEVICE_ID_SOURCE_TAGS,
|
|
836
920
|
ErrorType,
|
|
837
921
|
FINGERPRINT_PREFIX,
|
|
838
922
|
FINGERPRINT_VERSION,
|
|
@@ -854,6 +938,7 @@ export {
|
|
|
854
938
|
defaultDeviceIdentityReader,
|
|
855
939
|
fingerprintDeviceId,
|
|
856
940
|
fingerprintDigest,
|
|
941
|
+
identitySource,
|
|
857
942
|
parseDeviceIdStamp,
|
|
858
943
|
parseIoregPlatformUuid,
|
|
859
944
|
parseSmbiosParams,
|