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.
- package/README.MD +3 -2
- package/dist/bytemanip.d.ts.map +1 -1
- package/dist/bytemanip.js +4 -0
- package/dist/cli/decrypt-mp3.d.ts +3 -0
- package/dist/cli/decrypt-mp3.d.ts.map +1 -0
- package/dist/cli/decrypt-mp3.js +59 -0
- package/dist/cli/decrypt-oma.d.ts +2 -0
- package/dist/cli/decrypt-oma.d.ts.map +1 -0
- package/dist/cli/decrypt-oma.js +25 -0
- package/dist/cli/sign-device.d.ts +2 -0
- package/dist/cli/sign-device.d.ts.map +1 -0
- package/dist/cli/sign-device.js +30 -0
- package/dist/cli/table-file-info.d.ts +2 -0
- package/dist/cli/table-file-info.d.ts.map +1 -0
- package/dist/cli/table-file-info.js +46 -0
- package/dist/cli/tagged-oma-info.d.ts +2 -0
- package/dist/cli/tagged-oma-info.d.ts.map +1 -0
- package/dist/cli/tagged-oma-info.js +192 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +50 -0
- package/dist/codecs.d.ts +13 -0
- package/dist/codecs.d.ts.map +1 -0
- package/dist/codecs.js +40 -0
- package/dist/database-abstraction.d.ts +11 -4
- package/dist/database-abstraction.d.ts.map +1 -1
- package/dist/database-abstraction.js +84 -27
- package/dist/databases.d.ts +10 -1
- package/dist/databases.d.ts.map +1 -1
- package/dist/databases.js +2 -1
- package/dist/derive-mp3-key.d.ts +12 -0
- package/dist/derive-mp3-key.d.ts.map +1 -0
- package/dist/derive-mp3-key.js +109 -0
- package/dist/devices.d.ts +9 -2
- package/dist/devices.d.ts.map +1 -1
- package/dist/devices.js +70 -2
- package/dist/encryption.d.ts +2 -2
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +25 -12
- package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts +2 -0
- package/dist/filesystem/usb-mass-storage-webusb-filesystem.d.ts.map +1 -1
- package/dist/filesystem/usb-mass-storage-webusb-filesystem.js +8 -1
- package/dist/helpers.d.ts +6 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +9 -8
- package/dist/id3.d.ts +7 -0
- package/dist/id3.d.ts.map +1 -1
- package/dist/id3.js +25 -1
- package/dist/init-data.d.ts +25 -14
- package/dist/init-data.d.ts.map +1 -1
- package/dist/init-data.js +23 -15
- package/dist/initialization.d.ts +3 -2
- package/dist/initialization.d.ts.map +1 -1
- package/dist/initialization.js +14 -4
- package/dist/mp3.d.ts +9 -0
- package/dist/mp3.d.ts.map +1 -0
- package/dist/mp3.js +137 -0
- package/dist/tables.d.ts +1 -0
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +3 -0
- package/dist/tagged-oma.d.ts +3 -2
- package/dist/tagged-oma.d.ts.map +1 -1
- package/dist/tagged-oma.js +72 -33
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +70 -0
- package/dist/utils.d.ts +6 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +35 -12
- package/package.json +8 -4
- package/dist/functions.d.ts +0 -12
- package/dist/functions.d.ts.map +0 -1
- 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,
|
|
39
|
-
const codecKBPS = (0,
|
|
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,
|
|
59
|
-
const codecKBPS = (0,
|
|
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:
|
|
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
|
-
|
|
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 (
|
|
83
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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 (
|
|
236
|
-
|
|
237
|
-
|
|
292
|
+
if (handle) {
|
|
293
|
+
await (0, tagged_oma_1.updateMetadata)(handle, metadata);
|
|
294
|
+
}
|
|
238
295
|
}
|
|
239
296
|
}
|
|
240
297
|
exports.DatabaseAbstraction = DatabaseAbstraction;
|
package/dist/databases.d.ts
CHANGED
|
@@ -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>;
|
package/dist/databases.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databases.d.ts","sourceRoot":"","sources":["../src/databases.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
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
|
-
|
|
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
|
package/dist/devices.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAAA,
|
|
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
|
-
{
|
|
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;
|
package/dist/encryption.d.ts
CHANGED
|
@@ -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:
|
|
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;
|
package/dist/encryption.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/encryption.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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;
|
|
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
|
|
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(
|
|
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
|
-
|
|
11
|
+
definition: DeviceDefinition;
|
|
8
12
|
} | null>;
|
|
9
13
|
//# sourceMappingURL=helpers.d.ts.map
|
package/dist/helpers.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|