networkwm-js 0.1.0 → 0.1.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.
Files changed (73) hide show
  1. package/README.MD +3 -2
  2. package/dist/bytemanip.d.ts.map +1 -1
  3. package/dist/bytemanip.js +4 -0
  4. package/dist/cli/decrypt-mp3.d.ts +3 -0
  5. package/dist/cli/decrypt-mp3.d.ts.map +1 -0
  6. package/dist/cli/decrypt-mp3.js +59 -0
  7. package/dist/cli/decrypt-oma.d.ts +2 -0
  8. package/dist/cli/decrypt-oma.d.ts.map +1 -0
  9. package/dist/cli/decrypt-oma.js +25 -0
  10. package/dist/cli/sign-device.d.ts +2 -0
  11. package/dist/cli/sign-device.d.ts.map +1 -0
  12. package/dist/cli/sign-device.js +30 -0
  13. package/dist/cli/table-file-info.d.ts +2 -0
  14. package/dist/cli/table-file-info.d.ts.map +1 -0
  15. package/dist/cli/table-file-info.js +46 -0
  16. package/dist/cli/tagged-oma-info.d.ts +2 -0
  17. package/dist/cli/tagged-oma-info.d.ts.map +1 -0
  18. package/dist/cli/tagged-oma-info.js +192 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +50 -0
  22. package/dist/codecs.d.ts +13 -0
  23. package/dist/codecs.d.ts.map +1 -0
  24. package/dist/codecs.js +40 -0
  25. package/dist/database-abstraction.d.ts +11 -4
  26. package/dist/database-abstraction.d.ts.map +1 -1
  27. package/dist/database-abstraction.js +84 -27
  28. package/dist/databases.d.ts +10 -1
  29. package/dist/databases.d.ts.map +1 -1
  30. package/dist/databases.js +2 -1
  31. package/dist/derive-mp3-key.d.ts +12 -0
  32. package/dist/derive-mp3-key.d.ts.map +1 -0
  33. package/dist/derive-mp3-key.js +109 -0
  34. package/dist/devices.d.ts +9 -2
  35. package/dist/devices.d.ts.map +1 -1
  36. package/dist/devices.js +70 -2
  37. package/dist/encryption.d.ts +2 -2
  38. package/dist/encryption.d.ts.map +1 -1
  39. package/dist/encryption.js +25 -12
  40. package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts +2 -0
  41. package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts.map +1 -1
  42. package/dist/filesystem/usb-mass-storage-webusb-filesystem.js +8 -1
  43. package/dist/helpers.d.ts +6 -2
  44. package/dist/helpers.d.ts.map +1 -1
  45. package/dist/helpers.js +9 -8
  46. package/dist/id3.d.ts +7 -0
  47. package/dist/id3.d.ts.map +1 -1
  48. package/dist/id3.js +25 -1
  49. package/dist/init-data.d.ts +25 -14
  50. package/dist/init-data.d.ts.map +1 -1
  51. package/dist/init-data.js +23 -15
  52. package/dist/initialization.d.ts +3 -2
  53. package/dist/initialization.d.ts.map +1 -1
  54. package/dist/initialization.js +14 -4
  55. package/dist/mp3.d.ts +9 -0
  56. package/dist/mp3.d.ts.map +1 -0
  57. package/dist/mp3.js +137 -0
  58. package/dist/tables.d.ts +1 -0
  59. package/dist/tables.d.ts.map +1 -1
  60. package/dist/tables.js +3 -0
  61. package/dist/tagged-oma.d.ts +3 -2
  62. package/dist/tagged-oma.d.ts.map +1 -1
  63. package/dist/tagged-oma.js +72 -33
  64. package/dist/test.d.ts +2 -0
  65. package/dist/test.d.ts.map +1 -0
  66. package/dist/test.js +70 -0
  67. package/dist/utils.d.ts +6 -2
  68. package/dist/utils.d.ts.map +1 -1
  69. package/dist/utils.js +35 -12
  70. package/package.json +8 -4
  71. package/dist/functions.d.ts +0 -12
  72. package/dist/functions.d.ts.map +0 -1
  73. package/dist/functions.js +0 -73
@@ -1,25 +1,54 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DatabaseAbstraction = void 0;
4
- const himd_js_1 = require("himd-js");
5
4
  const databases_1 = require("./databases");
6
5
  const sort_1 = require("./sort");
7
6
  const initialization_1 = require("./initialization");
8
7
  const filesystem_1 = require("./filesystem");
9
8
  const tagged_oma_1 = require("./tagged-oma");
10
9
  const helpers_1 = require("./helpers");
10
+ const mp3_1 = require("./mp3");
11
+ const codecs_1 = require("./codecs");
12
+ const bytemanip_1 = require("./bytemanip");
13
+ const DEVICE_ID_CACHE_FILE = "/OMGAUDIO/DevID.DAT";
11
14
  class DatabaseAbstraction {
12
- constructor(filesystem) {
15
+ constructor(filesystem, deviceInfo) {
13
16
  this.filesystem = filesystem;
17
+ this.deviceInfo = deviceInfo;
14
18
  this.lastTotalDuration = 0;
15
19
  this.allTracks = [];
16
20
  this.deletedTracks = [];
17
- this.database = new databases_1.DatabaseManager(filesystem);
21
+ this.database = new databases_1.DatabaseManager(filesystem, deviceInfo.databaseParameters);
18
22
  }
19
- static async create(filesystem) {
20
- const db = new DatabaseAbstraction(filesystem);
23
+ static async create(filesystem, deviceInfo) {
24
+ const db = new DatabaseAbstraction(filesystem, deviceInfo);
21
25
  await db.database.init();
22
26
  db._create();
27
+ // Get the MP3 device key (device ID)
28
+ let deviceId = null;
29
+ if (filesystem instanceof filesystem_1.UMSCNWJSFilesystem) {
30
+ deviceId = await filesystem.driver.getDiscID();
31
+ // Cache, if this file doesn't exist already
32
+ if ((await filesystem.getSize(DEVICE_ID_CACHE_FILE)) === null) {
33
+ // Not cached. Create it.
34
+ const handle = await filesystem.open(DEVICE_ID_CACHE_FILE, 'rw');
35
+ await handle.write(deviceId);
36
+ await handle.close();
37
+ }
38
+ }
39
+ else {
40
+ // Cannot retrieve directly.
41
+ // Check if there is a cache file.
42
+ const rawDeviceIDFile = await filesystem.open(DEVICE_ID_CACHE_FILE, 'ro');
43
+ if (rawDeviceIDFile) {
44
+ // Yes, it's been cached.
45
+ deviceId = await rawDeviceIDFile.read();
46
+ await rawDeviceIDFile.close();
47
+ }
48
+ }
49
+ if (deviceId) {
50
+ db.mp3DeviceKey = (0, bytemanip_1.getUint32)(deviceId.slice(-4));
51
+ }
23
52
  return db;
24
53
  }
25
54
  _create() {
@@ -35,8 +64,8 @@ class DatabaseAbstraction {
35
64
  const codecId = globalEntry.codecInfo[0];
36
65
  const codecParams = globalEntry.codecInfo.slice(1);
37
66
  const codecInfo = { codecId, codecInfo: codecParams };
38
- const codecName = (0, himd_js_1.getCodecName)(codecInfo);
39
- const codecKBPS = (0, himd_js_1.getKBPS)(codecInfo);
67
+ const codecName = (0, codecs_1.getCodecName)(codecInfo);
68
+ const codecKBPS = (0, codecs_1.getKBPS)(codecInfo);
40
69
  if (globalEntry.trackDuration === 0)
41
70
  this.deletedTracks.push(systemIndex + 1);
42
71
  return {
@@ -54,13 +83,13 @@ class DatabaseAbstraction {
54
83
  };
55
84
  });
56
85
  }
57
- addNewTrack(trackInfo, codecInfo) {
58
- const codecName = (0, himd_js_1.getCodecName)(codecInfo);
59
- const codecKBPS = (0, himd_js_1.getKBPS)(codecInfo);
86
+ addNewTrack(trackInfo, codecInfo, encryptionState) {
87
+ const codecName = (0, codecs_1.getCodecName)(codecInfo);
88
+ const codecKBPS = (0, codecs_1.getKBPS)(codecInfo);
60
89
  const newObject = {
61
90
  ...trackInfo,
62
91
  codecInfo: new Uint8Array([codecInfo.codecId, ...codecInfo.codecInfo.subarray(0, 3)]),
63
- encryptionState: new Uint8Array([0, 1]),
92
+ encryptionState: (0, bytemanip_1.writeUint16)(encryptionState),
64
93
  oneElementLength: 128,
65
94
  systemIndex: -1,
66
95
  codecName, codecKBPS
@@ -77,23 +106,24 @@ class DatabaseAbstraction {
77
106
  newObject.systemIndex = idx;
78
107
  return newObject.systemIndex;
79
108
  }
80
- async uploadTrack(trackInfo, codec, rawData, session, callback) {
109
+ reassignTrackNumber(trackInfo) {
110
+ let trackNumber = trackInfo.trackNumber;
81
111
  // If trackInfo.trackNumber == -1, it's the next one of this particular album
82
- if (trackInfo.trackNumber == -1) {
83
- trackInfo.trackNumber = this.allTracks
112
+ if (trackNumber == -1 || trackNumber === undefined) {
113
+ trackNumber = this.allTracks
84
114
  .filter(e => e.album === trackInfo.album && e.artist === trackInfo.artist)
85
115
  .reduce((prev, c) => Math.max(prev, c.trackNumber), -1) + 1;
86
116
  }
87
- // Step 1 - Create the encrypted OMA which will later be written to the device's storage
88
- const encryptedOMA = (0, tagged_oma_1.createTaggedEncryptedOMA)(rawData, trackInfo, codec);
89
- // Step 2 - write track to the database
90
- const globalTrackIndex = this.addNewTrack({
91
- ...trackInfo,
92
- trackDuration: encryptedOMA.duration,
93
- }, codec);
94
- // Step 3 - write track to the filesystem
117
+ return trackNumber;
118
+ }
119
+ async copyToFilesystem(data, globalTrackIndex, callback) {
120
+ // Make sure the audio store directory appropriate for this file exists
121
+ const audioStore = `/OMGAUDIO/10F${(globalTrackIndex >> 8).toString(16).padStart(2, '0')}`;
122
+ if (!(await this.filesystem.list("/OMGAUDIO")).some(e => e.name === audioStore)) {
123
+ // The audio store for this file doesn't yet exist - create it.
124
+ await this.filesystem.mkdir(audioStore);
125
+ }
95
126
  const fh = await this.database.filesystem.open((0, helpers_1.resolvePathFromGlobalIndex)(globalTrackIndex), 'rw');
96
- const data = encryptedOMA.data;
97
127
  let remaining = data.length;
98
128
  let i = 0;
99
129
  callback === null || callback === void 0 ? void 0 : callback(i, data.length);
@@ -105,6 +135,32 @@ class DatabaseAbstraction {
105
135
  callback === null || callback === void 0 ? void 0 : callback(i, data.length);
106
136
  }
107
137
  await fh.close();
138
+ }
139
+ async uploadMP3Track(trackInfo, rawData, callback) {
140
+ if (this.mp3DeviceKey === undefined)
141
+ throw new Error("Please load the device key first!");
142
+ const { codec, duration } = (0, mp3_1.generateMP3CodecField)(rawData);
143
+ const trackNumber = trackInfo.trackNumber = this.reassignTrackNumber(trackInfo);
144
+ const globalTrackIndex = this.addNewTrack({
145
+ ...trackInfo,
146
+ trackDuration: duration,
147
+ trackNumber,
148
+ }, codec, 0xFFFE);
149
+ const mp3Data = (0, mp3_1.createMP3OMAFile)(globalTrackIndex, trackInfo, rawData, this.mp3DeviceKey, codec);
150
+ await this.copyToFilesystem(mp3Data, globalTrackIndex, callback);
151
+ }
152
+ async uploadTrack(trackInfo, codec, rawData, session, callback) {
153
+ const trackNumber = this.reassignTrackNumber(trackInfo);
154
+ // Step 1 - Create the encrypted OMA which will later be written to the device's storage
155
+ const encryptedOMA = (0, tagged_oma_1.createTaggedEncryptedOMA)(rawData, trackInfo, codec);
156
+ // Step 2 - write track to the database
157
+ const globalTrackIndex = this.addNewTrack({
158
+ ...trackInfo,
159
+ trackDuration: encryptedOMA.duration,
160
+ trackNumber,
161
+ }, codec, 0x0001);
162
+ // Step 3 - write track to the filesystem
163
+ await this.copyToFilesystem(encryptedOMA.data, globalTrackIndex, callback);
108
164
  // Step 4 - write MAC
109
165
  session === null || session === void 0 ? void 0 : session.writeTrackMac(globalTrackIndex - 1, encryptedOMA.maclistValue);
110
166
  }
@@ -198,6 +254,7 @@ class DatabaseAbstraction {
198
254
  return (0, sort_1.complexSort)([[{ var: 'artist' }], [{ var: 'album' }], [{ var: 'trackNumber' }]], this.allTracks.filter(e => e.trackDuration > 0));
199
255
  }
200
256
  async eraseAll() {
257
+ var _a, _b;
201
258
  // Essentially reinitialize the filesystem.
202
259
  // Destroy all audio files
203
260
  const fs = this.database.filesystem;
@@ -219,7 +276,7 @@ class DatabaseAbstraction {
219
276
  if (fs instanceof filesystem_1.UMSCNWJSFilesystem) {
220
277
  await fs.fatfs.flushMetadataChanges();
221
278
  }
222
- await (0, initialization_1.initializeNW)(fs);
279
+ await (0, initialization_1.initializeNW)(fs, (_b = (_a = this.deviceInfo.databaseParameters) === null || _a === void 0 ? void 0 : _a.initLayers) !== null && _b !== void 0 ? _b : []);
223
280
  this.database = new databases_1.DatabaseManager(this.filesystem);
224
281
  await this.database.init();
225
282
  this._create();
@@ -232,9 +289,9 @@ class DatabaseAbstraction {
232
289
  // TODO: Is this necessary??
233
290
  // Update the metadata within the OMA file
234
291
  const handle = await this.database.filesystem.open((0, helpers_1.resolvePathFromGlobalIndex)(systemIndex), 'rw');
235
- if (!handle)
236
- return;
237
- await (0, tagged_oma_1.updateMetadata)(handle, metadata);
292
+ if (handle) {
293
+ await (0, tagged_oma_1.updateMetadata)(handle, metadata);
294
+ }
238
295
  }
239
296
  }
240
297
  exports.DatabaseAbstraction = DatabaseAbstraction;
@@ -1,5 +1,6 @@
1
1
  import { HiMDFilesystem, HiMDCodecName } from "himd-js";
2
2
  import { TableFile } from "./tables";
3
+ import { DatabaseParameters } from "./devices";
3
4
  export interface TreeFile {
4
5
  mapStartBounds: {
5
6
  firstTrackApplicableInTPLB: number;
@@ -32,8 +33,16 @@ export interface TrackMetadata {
32
33
  trackDuration: number;
33
34
  trackNumber: number;
34
35
  }
36
+ export interface InboundTrackMetadata {
37
+ album: string;
38
+ artist: string;
39
+ title: string;
40
+ genre: string;
41
+ trackNumber?: number;
42
+ }
35
43
  export declare class DatabaseManager {
36
44
  filesystem: HiMDFilesystem;
45
+ private databaseParameters?;
37
46
  tableFiles: {
38
47
  [fileName: string]: TableFile;
39
48
  };
@@ -44,7 +53,7 @@ export declare class DatabaseManager {
44
53
  [fileName: string]: GroupEntry[];
45
54
  };
46
55
  globalContentInfoFile: ContentEntry[];
47
- constructor(filesystem: HiMDFilesystem);
56
+ constructor(filesystem: HiMDFilesystem, databaseParameters?: DatabaseParameters | undefined);
48
57
  init(): Promise<void>;
49
58
  reserializeTables(): void;
50
59
  rewriteTables(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"databases.d.ts","sourceRoot":"","sources":["../src/databases.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,cAAc,EAAyB,aAAa,EAAE,MAAM,SAAS,CAAC;AAI1F,OAAO,EAAc,SAAS,EAAkB,MAAM,UAAU,CAAC;AA4DjE,MAAM,WAAW,QAAQ;IAAE,cAAc,EAAE;QAAE,0BAA0B,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAC;AAC3I,MAAM,WAAW,YAAY;IAAG,eAAe,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAA;CAAC;AACvK,MAAM,WAAW,UAAU;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAA;CAAC;AAChH,MAAM,WAAW,aAAa;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC;AACxI,qBAAa,eAAe;IAML,UAAU,EAAE,cAAc;IAL7C,UAAU,EAAE;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;KAAC,CAAK;IAChD,eAAe,EAAE;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAC,CAAM;IACrD,oBAAoB,EAAE;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAAA;KAAC,CAAM;IAC9D,qBAAqB,EAAE,YAAY,EAAE,CAAM;gBAExB,UAAU,EAAE,cAAc;IAEvC,IAAI;IAoFH,iBAAiB;IAsEX,aAAa;IAU1B,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM;;;;;;;;;IAkB/B,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAYvD,iBAAiB,IAAI;QAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,aAAa,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAC,EAAE;IAkC9L,kBAAkB,IAAI;QAAC,CAAC,MAAM,EAAE,MAAM,GAAG;YAAC,CAAC,KAAK,EAAE,MAAM,GAAG;gBAAC,KAAK,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAAC,QAAQ,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,aAAa,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,EAAE,CAAA;SAAC,CAAA;KAAC;CAwBnK"}
1
+ {"version":3,"file":"databases.d.ts","sourceRoot":"","sources":["../src/databases.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAyB,aAAa,EAAE,MAAM,SAAS,CAAC;AAI/E,OAAO,EAAc,SAAS,EAAkB,MAAM,UAAU,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AA2D/C,MAAM,WAAW,QAAQ;IAAE,cAAc,EAAE;QAAE,0BAA0B,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAC;AAC3I,MAAM,WAAW,YAAY;IAAG,eAAe,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAA;CAAC;AACvK,MAAM,WAAW,UAAU;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAA;CAAC;AAChH,MAAM,WAAW,aAAa;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC;AACxI,MAAM,WAAW,oBAAoB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE;AAC1H,qBAAa,eAAe;IAML,UAAU,EAAE,cAAc;IAAE,OAAO,CAAC,kBAAkB,CAAC;IAL1E,UAAU,EAAE;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;KAAC,CAAK;IAChD,eAAe,EAAE;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAC,CAAM;IACrD,oBAAoB,EAAE;QAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAAA;KAAC,CAAM;IAC9D,qBAAqB,EAAE,YAAY,EAAE,CAAM;gBAExB,UAAU,EAAE,cAAc,EAAU,kBAAkB,CAAC,gCAAoB;IAExF,IAAI;IAoFH,iBAAiB;IAsEX,aAAa;IAU1B,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM;;;;;;;;;IAkB/B,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAYvD,iBAAiB,IAAI;QAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,aAAa,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAC,EAAE;IAkC9L,kBAAkB,IAAI;QAAC,CAAC,MAAM,EAAE,MAAM,GAAG;YAAC,CAAC,KAAK,EAAE,MAAM,GAAG;gBAAC,KAAK,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAAC,QAAQ,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,aAAa,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,EAAE,CAAA;SAAC,CAAA;KAAC;CAwBnK"}
package/dist/databases.js CHANGED
@@ -60,8 +60,9 @@ function writePackedTags(tags, elementLength) {
60
60
  }
61
61
  ;
62
62
  class DatabaseManager {
63
- constructor(filesystem) {
63
+ constructor(filesystem, databaseParameters) {
64
64
  this.filesystem = filesystem;
65
+ this.databaseParameters = databaseParameters;
65
66
  this.tableFiles = {};
66
67
  this.parsedTreeFiles = {};
67
68
  this.parsedGroupInfoFiles = {};
@@ -0,0 +1,12 @@
1
+ export declare function deriveMP3ParametersFromOMA(mp3CodecInfo: Uint8Array): {
2
+ version: number;
3
+ layer: number;
4
+ bitrate: number;
5
+ sample: number;
6
+ chmod: number;
7
+ preemp: number;
8
+ flags: number;
9
+ };
10
+ export declare function deriveMP3TrackKey(rawFile: Uint8Array, callback?: (state: 'genFrames' | 'genKeys' | 'commonness', progress: number, outOf: number) => void): number;
11
+ export declare function decryptMP3(fullFile: Uint8Array, fileId: number, deviceKey?: number, callback?: (state: 'genFrames' | 'genKeys' | 'commonness' | 'decrypt', progress: number, of: number) => void): Uint8Array;
12
+ //# sourceMappingURL=derive-mp3-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-mp3-key.d.ts","sourceRoot":"","sources":["../src/derive-mp3-key.ts"],"names":[],"mappings":"AAUA,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,UAAU;;;;;;;;EAclE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CA6DlK;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,cAyBhM"}
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decryptMP3 = exports.deriveMP3TrackKey = exports.deriveMP3ParametersFromOMA = void 0;
4
+ const bytemanip_1 = require("./bytemanip");
5
+ const encryption_1 = require("./encryption");
6
+ const id3_1 = require("./id3");
7
+ const utils_1 = require("./utils");
8
+ const textEncoder = new TextEncoder();
9
+ const ENCRYPTION_HEADER_START = textEncoder.encode("ea3");
10
+ const FORMAT_HEADER_START = textEncoder.encode("EA3");
11
+ function deriveMP3ParametersFromOMA(mp3CodecInfo) {
12
+ if (mp3CodecInfo[0] !== 3)
13
+ throw new Error("Not an MP3 format tag");
14
+ const flags = mp3CodecInfo[1];
15
+ const cmb0 = mp3CodecInfo[2];
16
+ const cmb1 = mp3CodecInfo[3];
17
+ const version = (cmb0 >> 6) & 3;
18
+ const layer = (cmb0 >> 4) & 3;
19
+ const bitrate = cmb0 & 15;
20
+ const sample = (cmb1 >> 6) & 3;
21
+ const chmod = (cmb1 >> 4) & 3;
22
+ const preemp = (cmb1 >> 2) & 3;
23
+ return { version, layer, bitrate, sample, chmod, preemp, flags };
24
+ }
25
+ exports.deriveMP3ParametersFromOMA = deriveMP3ParametersFromOMA;
26
+ function deriveMP3TrackKey(rawFile, callback) {
27
+ // Make sure we're dealing with an MP3 OMA file
28
+ let offset = 0;
29
+ let headerStart = rawFile.subarray(offset, offset + 3);
30
+ // Assume first is the tag header
31
+ if (!(0, utils_1.arrayEq)(headerStart, ENCRYPTION_HEADER_START))
32
+ throw new Error("Expected metadata header");
33
+ const headerSizeRemaining = (0, id3_1.readSynchsafeInt32)(new DataView(rawFile.buffer), 6)[0];
34
+ offset = 10 + headerSizeRemaining;
35
+ headerStart = rawFile.subarray(offset, offset + 3);
36
+ const rawDataView = new DataView(rawFile.buffer);
37
+ if (!(0, utils_1.arrayEq)(headerStart, FORMAT_HEADER_START))
38
+ throw new Error("Expected format header");
39
+ const mp3CodecInfo = rawFile.subarray(offset + 32, offset + 32 + 5);
40
+ if (rawFile[offset + 6] !== 0xFF || rawFile[offset + 7] !== 0xFE)
41
+ throw new Error("OMA is not LeafID-XOR encoded!");
42
+ const { version, layer, bitrate, sample, chmod, preemp } = deriveMP3ParametersFromOMA(mp3CodecInfo);
43
+ const audioStartOffset = offset + 96;
44
+ const fourByteChunks = [];
45
+ for (let i = audioStartOffset; i < rawFile.length - 3; i += 4) {
46
+ fourByteChunks.push(rawDataView.getUint32(i) >>> 0);
47
+ }
48
+ function formHeader(variantIteration) {
49
+ const assumeCrc = !!(variantIteration & 64);
50
+ const assumePadding = !!(variantIteration & 32);
51
+ const assumePrivate = !!(variantIteration & 16);
52
+ const assumeOriginalMedia = !!(variantIteration & 8);
53
+ const assumeCopyright = !!(variantIteration & 4);
54
+ const assumeJointstereoExtinfo = variantIteration & 3;
55
+ const int = (e) => e ? 1 : 0;
56
+ const rootFrameHeader = new Uint8Array([
57
+ 0xFF,
58
+ (7 << 5) | (version << 3) | (layer << 1) | int(assumeCrc),
59
+ (bitrate << 4) | (sample << 2) | (int(assumePadding) << 1) | int(assumePrivate),
60
+ (chmod << 6) | (assumeJointstereoExtinfo << 4) | (int(assumeCopyright) << 3) | (int(assumeOriginalMedia) << 2) | (preemp)
61
+ ]);
62
+ return rootFrameHeader;
63
+ }
64
+ const firstXoredHeader = fourByteChunks[0];
65
+ const allFirstFrames = Array(128).fill(0).map((_, i) => (0, bytemanip_1.getUint32)(formHeader(i)));
66
+ callback === null || callback === void 0 ? void 0 : callback('genFrames', -1, -1);
67
+ const allKeys = allFirstFrames.map(r => (firstXoredHeader ^ r) >>> 0);
68
+ callback === null || callback === void 0 ? void 0 : callback('genKeys', -1, -1);
69
+ const commonness = [];
70
+ for (let keyI = 0; keyI < allKeys.length; keyI++) {
71
+ const key = allKeys[keyI];
72
+ // Zero should be dominant in the MP3 file => The most common key present as plaintext in the file will be valid
73
+ let z = 0;
74
+ for (let chunk of fourByteChunks) {
75
+ if (chunk === key)
76
+ ++z;
77
+ }
78
+ commonness.push(z);
79
+ callback === null || callback === void 0 ? void 0 : callback('commonness', keyI, allKeys.length);
80
+ }
81
+ // Find the most common key
82
+ const matchedKey = allKeys[commonness.indexOf(Math.max(...commonness))];
83
+ return matchedKey;
84
+ }
85
+ exports.deriveMP3TrackKey = deriveMP3TrackKey;
86
+ function decryptMP3(fullFile, fileId, deviceKey, callback) {
87
+ const trackKey = (deviceKey ? (0, encryption_1.getMP3EncryptionKey)(deviceKey, fileId) : deriveMP3TrackKey(fullFile, callback ? (s, p) => callback(s, p, 127) : undefined)) >>> 0;
88
+ // Make sure we're dealing with an MP3 OMA file
89
+ let offset = 0;
90
+ let headerStart = fullFile.subarray(offset, offset + 3);
91
+ // Assume first is the tag header
92
+ if (!(0, utils_1.arrayEq)(headerStart, ENCRYPTION_HEADER_START))
93
+ throw new Error("Expected metadata header");
94
+ const headerSizeRemaining = (0, id3_1.readSynchsafeInt32)(new DataView(fullFile.buffer), 6)[0];
95
+ offset = 10 + headerSizeRemaining;
96
+ // Reconstruct the ID3 header and decrypt the audio - it starts 96 bytes after the end of the ID3 header
97
+ const data = (0, utils_1.concatUint8Arrays)([fullFile.subarray(0, offset), fullFile.subarray(offset + 96, fullFile.length)]);
98
+ data.set(textEncoder.encode("ID3"), 0);
99
+ const dataView = new DataView(data.buffer);
100
+ for (offset; offset < data.length - 7; offset += 8) {
101
+ // 8-byte-long block processing on 4-byte-long key
102
+ // Ok Sony.
103
+ dataView.setUint32(offset, (dataView.getUint32(offset) ^ trackKey) >>> 0);
104
+ dataView.setUint32(offset + 4, (dataView.getUint32(offset + 4) ^ trackKey) >>> 0);
105
+ callback === null || callback === void 0 ? void 0 : callback('decrypt', offset, data.length);
106
+ }
107
+ return data;
108
+ }
109
+ exports.decryptMP3 = decryptMP3;
package/dist/devices.d.ts CHANGED
@@ -1,6 +1,13 @@
1
- export declare const DeviceIds: {
1
+ import type { LAYERS } from "./init-data";
2
+ export interface DatabaseParameters {
3
+ initLayers: (keyof typeof LAYERS)[];
4
+ }
5
+ export interface DeviceDefinition {
2
6
  vendorId: number;
3
7
  productId: number;
4
8
  name: string;
5
- }[];
9
+ databaseParameters?: DatabaseParameters;
10
+ }
11
+ export declare const DeviceIds: DeviceDefinition[];
12
+ export declare function findDevice(vid: number, pid: number): DeviceDefinition | null;
6
13
  //# sourceMappingURL=devices.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EAK3E,CAAC"}
1
+ {"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,CAAC,MAAM,OAAO,MAAM,CAAC,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CAC3C;AAED,eAAO,MAAM,SAAS,EAAE,gBAAgB,EAoEvC,CAAC;AAEF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAE5E"}
package/dist/devices.js CHANGED
@@ -1,9 +1,77 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DeviceIds = void 0;
3
+ exports.findDevice = exports.DeviceIds = void 0;
4
4
  exports.DeviceIds = [
5
5
  { vendorId: 0x054c, productId: 0x01ad, name: 'Sony NW-HD1 / NW-HD2' },
6
6
  { vendorId: 0x054c, productId: 0x0210, name: 'Sony NW-HD3' },
7
7
  { vendorId: 0x054c, productId: 0x0233, name: 'Sony NW-HD5' },
8
- { vendorId: 0x054c, productId: 0x0269, name: 'Sony NW-A3000' },
8
+ {
9
+ vendorId: 0x054c,
10
+ productId: 0x0269,
11
+ name: 'Sony NW-A3000',
12
+ databaseParameters: {
13
+ initLayers: ['needs_cid'],
14
+ }
15
+ },
16
+ {
17
+ vendorId: 0x054c,
18
+ productId: 0x0358,
19
+ name: 'Sony NW-E026F',
20
+ databaseParameters: {
21
+ initLayers: ['stick_gtrlst'],
22
+ }
23
+ },
24
+ {
25
+ vendorId: 0x054c,
26
+ productId: 0x03c6,
27
+ name: 'Sony NW-E043',
28
+ databaseParameters: {
29
+ initLayers: ['stick_gtrlst'],
30
+ }
31
+ },
32
+ {
33
+ vendorId: 0x054c,
34
+ productId: 0x02E4,
35
+ name: 'Sony NW-S203F',
36
+ databaseParameters: {
37
+ initLayers: ['stick_gtrlst'],
38
+ }
39
+ },
40
+ {
41
+ vendorId: 0x054c,
42
+ productId: 0x01FB,
43
+ name: 'Sony NW-E305',
44
+ databaseParameters: {
45
+ initLayers: ['stick_gtrlst'],
46
+ }
47
+ },
48
+ {
49
+ vendorId: 0x054c,
50
+ productId: 0x027c,
51
+ name: 'Sony NW-A608',
52
+ databaseParameters: {
53
+ initLayers: ['stick_gtrlst'],
54
+ }
55
+ },
56
+ {
57
+ vendorId: 0x054c,
58
+ productId: 0x082e,
59
+ name: 'Sony NWZ-W273S',
60
+ databaseParameters: {
61
+ initLayers: ['stick_gtrlst'],
62
+ }
63
+ },
64
+ {
65
+ vendorId: 0x054c,
66
+ productId: 0x0358,
67
+ name: 'Sony NW-E026f',
68
+ databaseParameters: {
69
+ initLayers: ['stick_gtrlst'],
70
+ }
71
+ },
9
72
  ];
73
+ function findDevice(vid, pid) {
74
+ var _a;
75
+ return (_a = exports.DeviceIds.find(e => e.vendorId === vid && e.productId === pid)) !== null && _a !== void 0 ? _a : null;
76
+ }
77
+ exports.findDevice = findDevice;
@@ -4,12 +4,12 @@ export declare const EKBROOTS: {
4
4
  [key: number]: Uint8Array;
5
5
  };
6
6
  export declare function importKeys(rawKeysContents: Uint8Array): void;
7
- export declare function getMP3EncryptionKey(discId: Uint8Array, trackNumber: number): Uint8Array;
7
+ export declare function getMP3EncryptionKey(discId: number, trackNumber: number): number;
8
8
  export declare function createTrackKeyForKeyring(ekbNum: number, verificationKey: Uint8Array, trackKey: Uint8Array): Uint8Array;
9
+ export declare function createTrackKeyFromKeyring(ekbNum: number, encryptedVerificationKey: Uint8Array, encryptedTrackKey: Uint8Array): Uint8Array;
9
10
  export declare function createMaclistValue(ekbNum: number, verificationKey: Uint8Array, contents: Uint8Array): Uint8Array;
10
11
  export declare function retailMac(message: Uint8Array, key: Uint8Array): Uint8Array;
11
12
  export declare function createIcvMac(icvAndHeader: Uint8Array, sessionKey: Uint8Array): Uint8Array;
12
- export declare function encryptTrackKey(trackKey: Uint8Array): Uint8Array;
13
13
  export declare function createTrackMac2(trackKeyWa: Crypto.lib.WordArray, trackEntry: Uint8Array): Uint8Array;
14
14
  export declare function createTrackMac(trackKey: Uint8Array, trackEntry: Uint8Array): Uint8Array;
15
15
  export declare function desDecrypt(data: Uint8Array, key: Uint8Array): Uint8Array;
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAK9C,wBAAsB,UAAU,kBAG/B;AAGD,eAAO,MAAM,QAAQ,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAExC,CAAC;AAEX,wBAAgB,UAAU,CAAC,eAAe,EAAE,UAAU,QAQrD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAG1E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,cAsBzG;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,cAenG;AAID,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,cAgB7D;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,cAM5E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,cAKnD;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,cAQvF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,cAG1E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,cAE3D"}
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAK9C,wBAAsB,UAAU,kBAG/B;AAGD,eAAO,MAAM,QAAQ,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAExC,CAAC;AAEX,wBAAgB,UAAU,CAAC,eAAe,EAAE,UAAU,QAQrD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAEtE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,cAsBzG;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,wBAAwB,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,cAsB5H;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,cAenG;AAID,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,cAgB7D;AAED,wBAAgB,YAAY,CAAC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,cAM5E;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,cAQvF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,cAG1E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,cAE3D"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.desDecrypt = exports.createTrackMac = exports.createTrackMac2 = exports.encryptTrackKey = exports.createIcvMac = exports.retailMac = exports.createMaclistValue = exports.createTrackKeyForKeyring = exports.getMP3EncryptionKey = exports.importKeys = exports.EKBROOTS = exports.initCrypto = void 0;
6
+ exports.desDecrypt = exports.createTrackMac = exports.createTrackMac2 = exports.createIcvMac = exports.retailMac = exports.createMaclistValue = exports.createTrackKeyFromKeyring = exports.createTrackKeyForKeyring = exports.getMP3EncryptionKey = exports.importKeys = exports.EKBROOTS = exports.initCrypto = void 0;
7
7
  const crypto_js_wasm_1 = __importDefault(require("@originjs/crypto-js-wasm"));
8
8
  const bytemanip_1 = require("./bytemanip");
9
9
  const errors_1 = require("./errors");
@@ -28,13 +28,12 @@ function importKeys(rawKeysContents) {
28
28
  }
29
29
  exports.importKeys = importKeys;
30
30
  function getMP3EncryptionKey(discId, trackNumber) {
31
- const key = (trackNumber * 0x6953b2ed + 0x6baab1) ^ (0, bytemanip_1.getUint32)(discId, 12);
32
- return (0, bytemanip_1.writeUint32)(key);
31
+ return ((trackNumber * 0x5296E435 + 0x2465) ^ discId) >>> 0;
33
32
  }
34
33
  exports.getMP3EncryptionKey = getMP3EncryptionKey;
35
34
  function createTrackKeyForKeyring(ekbNum, verificationKey, trackKey) {
36
35
  if (!(ekbNum in exports.EKBROOTS)) {
37
- throw new errors_1.NWJSError('Requested decription with an unknown EKB');
36
+ throw new errors_1.NWJSError('Requested decryption with an unknown EKB');
38
37
  }
39
38
  const rootKeyC = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[ekbNum]);
40
39
  const verificationKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
@@ -54,9 +53,30 @@ function createTrackKeyForKeyring(ekbNum, verificationKey, trackKey) {
54
53
  return (0, utils_1.wordArrayToByteArray)(decryptedTrackKey, 8);
55
54
  }
56
55
  exports.createTrackKeyForKeyring = createTrackKeyForKeyring;
56
+ function createTrackKeyFromKeyring(ekbNum, encryptedVerificationKey, encryptedTrackKey) {
57
+ if (!(ekbNum in exports.EKBROOTS)) {
58
+ throw new errors_1.NWJSError('Requested decryption with an unknown EKB');
59
+ }
60
+ const rootKeyC = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[ekbNum]);
61
+ const verificationKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
62
+ ciphertext: crypto_js_wasm_1.default.lib.WordArray.create(encryptedVerificationKey),
63
+ });
64
+ const trackKeyC = crypto_js_wasm_1.default.lib.WordArray.create(encryptedTrackKey);
65
+ // Step 1: get real verification key
66
+ const verificationKey = crypto_js_wasm_1.default.TripleDES.decrypt(verificationKeyC, rootKeyC, {
67
+ mode: crypto_js_wasm_1.default.mode.ECB,
68
+ });
69
+ // Step 2: get decrypted track key - so encrypt it
70
+ const decryptedTrackKey = crypto_js_wasm_1.default.TripleDES.encrypt(trackKeyC, verificationKey, {
71
+ mode: crypto_js_wasm_1.default.mode.ECB,
72
+ padding: NO_PADDING
73
+ }).ciphertext;
74
+ return (0, utils_1.wordArrayToByteArray)(decryptedTrackKey, 8);
75
+ }
76
+ exports.createTrackKeyFromKeyring = createTrackKeyFromKeyring;
57
77
  function createMaclistValue(ekbNum, verificationKey, contents) {
58
78
  if (!(ekbNum in exports.EKBROOTS)) {
59
- throw new errors_1.NWJSError('Requested decription with an unknown EKB');
79
+ throw new errors_1.NWJSError('Requested decryption with an unknown EKB');
60
80
  }
61
81
  const rootKeyC = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[ekbNum]);
62
82
  const verificationKeyC = crypto_js_wasm_1.default.lib.CipherParams.create({
@@ -96,13 +116,6 @@ function createIcvMac(icvAndHeader, sessionKey) {
96
116
  return (0, utils_1.wordArrayToByteArray)(result.ciphertext).subarray(-8);
97
117
  }
98
118
  exports.createIcvMac = createIcvMac;
99
- function encryptTrackKey(trackKey) {
100
- const trackKeyWa = crypto_js_wasm_1.default.lib.WordArray.create(trackKey);
101
- const keyWa = crypto_js_wasm_1.default.lib.WordArray.create(exports.EKBROOTS[0x00010012]);
102
- const encrypted = crypto_js_wasm_1.default.TripleDES.encrypt(trackKeyWa, keyWa, { mode: crypto_js_wasm_1.default.mode.ECB, padding: NO_PADDING });
103
- return (0, utils_1.wordArrayToByteArray)(encrypted.ciphertext);
104
- }
105
- exports.encryptTrackKey = encryptTrackKey;
106
119
  function createTrackMac2(trackKeyWa, trackEntry) {
107
120
  const trackEntryWa = crypto_js_wasm_1.default.lib.WordArray.create(trackEntry);
108
121
  const macKeySourceWa = crypto_js_wasm_1.default.lib.WordArray.create(new Uint8Array(8).fill(0));
@@ -1,8 +1,10 @@
1
1
  import { HiMDFile, HiMDFilesystem, SonyVendorUSMCDriver, UMSCHiMDFilesystem } from 'himd-js';
2
2
  import { WebUSBDevice } from 'usb';
3
3
  export declare class SonyVendorNWJSUSMCDriver extends SonyVendorUSMCDriver {
4
+ constructor(webUSB: WebUSBDevice);
4
5
  protected drmRead(param: number, length: number): Promise<Uint8Array>;
5
6
  protected drmWrite(param: number, data: Uint8Array): Promise<void>;
7
+ getDiscID(): Promise<Uint8Array>;
6
8
  writeHostLeafID(leafID: Uint8Array, hostNonce: Uint8Array): Promise<void>;
7
9
  getAuthenticationStage2Info(): Promise<{
8
10
  discId: Uint8Array;
@@ -1 +1 @@
1
- {"version":3,"file":"usb-mass-storage-webusb-filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem/usb-mass-storage-webusb-filesystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAI7F,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAGnC,qBAAa,wBAAyB,SAAQ,oBAAoB;cAC9C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;cAuBrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IA2BlD,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU;IASzD,2BAA2B;;;;;;IAoB3B,6BAA6B,CAAC,OAAO,EAAE,UAAU;IAyBjD,aAAa;;;;IAWb,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;CAuBhH;AAED,qBAAa,eAAe;IAgBZ,SAAS,CAAC,MAAM,EAAE,wBAAwB;IAAE,SAAS,CAAC,EAAE,EAAE,cAAc;IAfpF,SAAS,aAAuB;IAChC,UAAU,aAAoE;IAE9E,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,gBAAgB,CAAC,EAAE,UAAU,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,EAAE,UAAU,CAAC;gBAEC,MAAM,EAAE,wBAAwB,EAAY,EAAE,EAAE,cAAc;IAEvE,oBAAoB;IAkBpB,eAAe;IAYrB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU;CAG5D;AAED,qBAAa,kBAAmB,SAAQ,kBAAkB;IACtD,MAAM,EAAE,wBAAwB,CAAC;gBACrB,MAAM,EAAE,YAAY;cAKhB,MAAM,CAAC,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYrF"}
1
+ {"version":3,"file":"usb-mass-storage-webusb-filesystem.d.ts","sourceRoot":"","sources":["../../src/filesystem/usb-mass-storage-webusb-filesystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAI7F,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAKnC,qBAAa,wBAAyB,SAAQ,oBAAoB;gBAClD,MAAM,EAAE,YAAY;cAIhB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;cAuBrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IA2BlD,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAIhC,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU;IASzD,2BAA2B;;;;;;IAoB3B,6BAA6B,CAAC,OAAO,EAAE,UAAU;IAyBjD,aAAa;;;;IAWb,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;CAuBhH;AAED,qBAAa,eAAe;IAgBZ,SAAS,CAAC,MAAM,EAAE,wBAAwB;IAAE,SAAS,CAAC,EAAE,EAAE,cAAc;IAfpF,SAAS,aAAuB;IAChC,UAAU,aAAoE;IAE9E,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,gBAAgB,CAAC,EAAE,UAAU,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,EAAE,UAAU,CAAC;gBAEC,MAAM,EAAE,wBAAwB,EAAY,EAAE,EAAE,cAAc;IAEvE,oBAAoB;IAkBpB,eAAe;IAYrB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU;CAG5D;AAED,qBAAa,kBAAmB,SAAQ,kBAAkB;IACtD,MAAM,EAAE,wBAAwB,CAAC;gBACrB,MAAM,EAAE,YAAY;cAKhB,MAAM,CAAC,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYrF"}
@@ -6,7 +6,11 @@ const utils_1 = require("../utils");
6
6
  const bytemanip_1 = require("../bytemanip");
7
7
  const encryption_1 = require("../encryption");
8
8
  const nufatfs_1 = require("nufatfs");
9
+ const USB_MASS_STORAGE_CLASS = 8;
9
10
  class SonyVendorNWJSUSMCDriver extends himd_js_1.SonyVendorUSMCDriver {
11
+ constructor(webUSB) {
12
+ super(webUSB, webUSB.configuration.interfaces.find(e => e.alternate.interfaceClass === USB_MASS_STORAGE_CLASS).alternate.interfaceSubclass);
13
+ }
10
14
  async drmRead(param, length) {
11
15
  const command = new Uint8Array([
12
16
  0xa4,
@@ -54,6 +58,9 @@ class SonyVendorNWJSUSMCDriver extends himd_js_1.SonyVendorUSMCDriver {
54
58
  ]);
55
59
  await this.sendCommandOutGetResult(command, newData, command.length);
56
60
  }
61
+ async getDiscID() {
62
+ return (await this.drmRead(0x3f, 0x12)).slice(0, -2);
63
+ }
57
64
  async writeHostLeafID(leafID, hostNonce) {
58
65
  (0, utils_1.assert)(leafID.length === 8, 'Wrong length of leaf id');
59
66
  const finalBuffer = new Uint8Array(2 + 8 + 8);
@@ -171,7 +178,7 @@ exports.UMSCNWJSSession = UMSCNWJSSession;
171
178
  class UMSCNWJSFilesystem extends himd_js_1.UMSCHiMDFilesystem {
172
179
  constructor(webUSB) {
173
180
  super(webUSB);
174
- this.driver = new SonyVendorNWJSUSMCDriver(webUSB, 0x06);
181
+ this.driver = new SonyVendorNWJSUSMCDriver(webUSB);
175
182
  }
176
183
  async initFS(bypassCoherencyChecks) {
177
184
  await this.driver.inquiry();
package/dist/helpers.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import { WebUSBDevice } from "usb";
2
2
  import { UMSCNWJSFilesystem } from "./filesystem";
3
+ import { DeviceDefinition } from "./devices";
3
4
  export declare function resolvePathFromGlobalIndex(globalTrackIndex: number): string;
4
- export declare function createNWJSFS(webUsbDevice: WebUSBDevice, bypassCoherencyChecks: boolean): Promise<UMSCNWJSFilesystem>;
5
+ export declare function createNWJSFS(device: {
6
+ dev: WebUSBDevice;
7
+ definition: DeviceDefinition;
8
+ }): Promise<UMSCNWJSFilesystem>;
5
9
  export declare function openNewDeviceNode(): Promise<{
6
10
  dev: WebUSBDevice;
7
- name: string;
11
+ definition: DeviceDefinition;
8
12
  } | null>;
9
13
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkB,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAKlD,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,MAAM,UAElE;AAED,wBAAsB,YAAY,CAAC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,OAAO,+BAO5F;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA2B7F"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkB,MAAM,KAAK,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAa,MAAM,WAAW,CAAC;AAGxD,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,MAAM,UAElE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,gBAAgB,CAAA;CAAE,+BAO7F;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,gBAAgB,CAAA;CAAE,GAAG,IAAI,CAAC,CA2B7G"}