Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './netmd';
export * from './netmd-ekb';
export * from './netmd-interface';
export * from './netmd-commands';
export * from './encrypt-generator';
Expand Down
4 changes: 2 additions & 2 deletions src/netmd-commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NetMD, DevicesIds } from './netmd';
import { NetMDInterface, Encoding, Channels, TrackFlag, DiscFlag, MDTrack, MDSession, EKBOpenSource, Wireformat } from './netmd-interface';
import { NetMDInterface, Encoding, Channels, TrackFlag, DiscFlag, MDTrack, MDSession, Wireformat } from './netmd-interface';
import {
timeToFrames,
sanitizeHalfWidthTitle,
Expand Down Expand Up @@ -461,7 +461,7 @@ export async function download(
) {
await prepareDownload(mdIface);

const session = new MDSession(mdIface, new EKBOpenSource());
const session = new MDSession(mdIface);
await session.init();
const [trk, uuid, ccid] = await session.downloadTrack(track, progressCallback);

Expand Down
88 changes: 88 additions & 0 deletions src/netmd-ekb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export interface EKBObject {
getLeafId(): string;
getRootKey(): Uint8Array;
getEKBID(): number;
getEKBDataForLeafId(): [Uint8Array[], number, Uint8Array];
};

export class EKBOpenSource {
static getLeafId() {
return undefined;
}

getRootKey() {
// prettier-ignore
return new Uint8Array([
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21
])
}

getEKBID() {
return 0x26422642;
}

getEKBDataForLeafId(): [Uint8Array[], number, Uint8Array] {
// prettier-ignore
return [
[
new Uint8Array([0x25, 0x45, 0x06, 0x4d, 0xea, 0xca, 0x14, 0xf9, 0x96, 0xbd, 0xc8, 0xa4, 0x06, 0xc2, 0x2b, 0x81]),
new Uint8Array([0xfb, 0x60, 0xbd, 0xdd, 0x0d, 0xbc, 0xab, 0x84, 0x8a, 0x00, 0x5e, 0x03, 0x19, 0x4d, 0x3e, 0xda]),
],
9,
new Uint8Array([
0x8f, 0x2b, 0xc3, 0x52, 0xe8, 0x6c, 0x5e, 0xd3, 0x06, 0xdc, 0xae, 0x18,
0xd2, 0xf3, 0x8c, 0x7f, 0x89, 0xb5, 0xe1, 0x85, 0x55, 0xa1, 0x05, 0xea
])
];
}
}

// EKB generated by Sir68k.
export class EKBFFOpenSource {
static getLeafId() {
return 'ffffffff';
}

getRootKey() {
// prettier-ignore
return new Uint8Array([
// 'WMDPWMDPMiniDisc'
0x57, 0x4d, 0x44, 0x50, 0x57, 0x4d, 0x44, 0x50,
0x4d, 0x69, 0x6e, 0x69, 0x44, 0x69, 0x73, 0x63
])
}

getEKBID() {
return 0x13371337;
}

getEKBDataForLeafId(): [Uint8Array[], number, Uint8Array] {
// prettier-ignore
return [
[
new Uint8Array([0xb1, 0xd4, 0xaf, 0xfa, 0x80, 0xa0, 0xc9, 0x03, 0xc2, 0x58, 0x4b, 0x1b, 0x44, 0xaf, 0xc4, 0xa6]),
],
9,

new Uint8Array([
0x6c, 0x2b, 0xc2, 0x8c, 0x45, 0x2b, 0x54, 0xf1, 0xc3, 0x59, 0x72, 0x3b,
0xe3, 0x19, 0x1f, 0x55, 0x17, 0x25, 0x64, 0x0e, 0x65, 0x8c, 0x81, 0x0b
])
];
}
}

// Custom EKB compatibility map
export const CustomEKBObjects = [
{ ekb: EKBFFOpenSource, deviceIds: [0x0081, 0x0084] } // SoC: CXD1873, CXD2677
];

export async function getEKBObject(leafId: string, deviceId: number) {
for (const { ekb, deviceIds } of CustomEKBObjects) {
if (deviceIds.indexOf(deviceId) >= 0 && ekb.getLeafId() === leafId) {
return new ekb();
}
}
return new EKBOpenSource(); // original EKB
}
42 changes: 8 additions & 34 deletions src/netmd-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Crypto from '@originjs/crypto-js-wasm';
import { NetMDNotImplemented, NetMDRejected, Status } from './netmd-shared-objects';
import { HiMDFactoryInterface, NetMDFactoryInterface, RH1FactoryInterface } from './factory/netmd-factory-interface';
import { getDescriptiveDeviceCode } from './factory';
import { getEKBObject } from './netmd-ekb';

enum Action {
play = 0x75,
Expand Down Expand Up @@ -933,35 +934,6 @@ const discforwire: { [k: number]: number } = {
[Wireformat.lp4]: DiscFormat.lp4,
};

export class EKBOpenSource {
getRootKey() {
// prettier-ignore
return new Uint8Array([
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21
])
}

getEKBID() {
return 0x26422642;
}

getEKBDataForLeafId(): [Uint8Array[], number, Uint8Array] {
// prettier-ignore
return [
[
new Uint8Array([0x25, 0x45, 0x06, 0x4d, 0xea, 0xca, 0x14, 0xf9, 0x96, 0xbd, 0xc8, 0xa4, 0x06, 0xc2, 0x2b, 0x81]),
new Uint8Array([0xfb, 0x60, 0xbd, 0xdd, 0x0d, 0xbc, 0xab, 0x84, 0x8a, 0x00, 0x5e, 0x03, 0x19, 0x4d, 0x3e, 0xda]),
],
9,
new Uint8Array([
0x8f, 0x2b, 0xc3, 0x52, 0xe8, 0x6c, 0x5e, 0xd3, 0x06, 0xdc, 0xae, 0x18,
0xd2, 0xf3, 0x8c, 0x7f, 0x89, 0xb5, 0xe1, 0x85, 0x55, 0xa1, 0x05, 0xea
])
];
}
}

export class MDTrack {
constructor(
public title: string,
Expand Down Expand Up @@ -1117,21 +1089,23 @@ export class MDTrack {
}

export class MDSession {
constructor(private md: NetMDInterface, private ekbobject: EKBOpenSource, private hexSessionKey?: string) {}
constructor(private md: NetMDInterface, private hexSessionKey?: string) {}

async init() {
await this.md.enterSecureSession();
await this.md.getLeafID(); // Panasonic compatibility
const [chain, depth, sig] = this.ekbobject.getEKBDataForLeafId();
await this.md.sendKeyData(JSBI.BigInt(this.ekbobject.getEKBID()), chain, depth, sig);
const leafId = await this.md.getLeafID(); // Panasonic compatibility
const deviceId = this.md.netMd.getProduct();
const ekbobject = await getEKBObject(leafId, deviceId);
const [chain, depth, sig] = ekbobject.getEKBDataForLeafId();
await this.md.sendKeyData(JSBI.BigInt(ekbobject.getEKBID()), chain, depth, sig);
let hostnonce = new Uint8Array(
Array(8)
.fill(0)
.map(_ => Math.round(Math.random() * 255))
);
let devnonce = await this.md.sessionKeyExchange(hostnonce);
let nonce = concatUint8Arrays(hostnonce, devnonce);
this.hexSessionKey = retailmac(this.ekbobject.getRootKey(), nonce);
this.hexSessionKey = retailmac(ekbobject.getRootKey(), nonce);
}

async downloadTrack(trk: MDTrack, progressCallback?: (progress: { writtenBytes: number; totalBytes: number }) => void, discFormat = discforwire[trk.getDataFormat()]) {
Expand Down