@blackmagic-controller/core
Version:
An npm module for interfacing with the Blackmagic usb/bluetooth controllers
89 lines • 3.02 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.authenticate = authenticate;
const util_js_1 = require("./util.js");
/*
* This is based on the work from
* https://github.com/smunaut/blackmagic-misc/blob/master/bmd.py
*/
function generatePacket(featureReport, step, value) {
const packet = new Uint8Array(10);
const view = (0, util_js_1.uint8ArrayToDataView)(packet);
view.setUint8(0, featureReport);
view.setUint8(1, step);
view.setBigUint64(2, value, true);
return packet;
}
/**
* Perform the authentication handshake with the device
* @param device Device handle
* @param featureReport Report id to use for the authentication
* @returns Timeout until authentication is required again
*/
async function authenticate(device, featureReport) {
// Reset the state machine
await device.sendFeatureReport(generatePacket(featureReport, 0, 0n));
// Read the keyboard challenge (for keyboard to authenticate app)
const challenge = await device.getFeatureReport(featureReport, 10);
// Send our challenge (to authenticate the controller)
// We don't care ... so just send 0x0000000000000000
await device.sendFeatureReport(generatePacket(featureReport, 1, 0n));
// Read the keyboard response
// Again, we don't care, ignore the result
await device.getFeatureReport(featureReport, 10);
// Compute and send our response
const challengeView = (0, util_js_1.uint8ArrayToDataView)(challenge);
const response = bmd_kbd_auth(challengeView.getBigUint64(2, true));
await device.sendFeatureReport(generatePacket(featureReport, 3, response));
// Read the status
const status = await device.getFeatureReport(featureReport, 10);
const statusView = (0, util_js_1.uint8ArrayToDataView)(status);
if (status[1] !== 0x04)
throw new Error('Authentication failed');
// This is likely the timeout until authentication is required again
return statusView.getUint16(2, true);
}
function rol8(v) {
return ((v << 56n) | (v >> 8n)) & 0xffffffffffffffffn;
}
function rol8n(v, n) {
for (let i = 0; i < n; i++) {
v = rol8(v);
}
return v;
}
const AUTH_EVEN_TBL = [
0x3ae1206f97c10bc8n,
0x2a9ab32bebf244c6n,
0x20a6f8b8df9adf0an,
0xaf80ece52cfc1719n,
0xec2ee2f7414fd151n,
0xb055adfd73344a15n,
0xa63d2e3059001187n,
0x751bf623f42e0dden,
];
const AUTH_ODD_TBL = [
0x3e22b34f502e7fden,
0x24656b981875ab1cn,
0xa17f3456df7bf8c3n,
0x6df72e1941aef698n,
0x72226f011e66ab94n,
0x3831a3c606296b42n,
0xfd7ff81881332c89n,
0x61a3f6474ff236c6n,
];
const MASK = 0xa79a63f585d37bf0n;
function bmd_kbd_auth(challenge) {
const n = Number(challenge & 7n);
let v = rol8n(challenge, n);
let k;
if ((v & 1n) == (BigInt(0x78 >> n) & 1n)) {
k = AUTH_EVEN_TBL[n];
}
else {
v = v ^ rol8(v);
k = AUTH_ODD_TBL[n];
}
return v ^ (rol8(v) & MASK) ^ k;
}
//# sourceMappingURL=authenticate.js.map
;