diff --git a/tools/decode_capture.js b/tools/decode_capture.js index 391e5da6e6378e157268ed1febe4dbb3ab01e4ce..b9ff1ec98ee9764b6e823cc2bffb306bf8b6b5b9 100644 --- a/tools/decode_capture.js +++ b/tools/decode_capture.js @@ -1,12 +1,12 @@ -'use strict'; - // Decrypts a btsnoop_hci.log from an Android device (enabled in developer options.) // First use tshark to create a text log from a hci capture: // tshark -r btsnoop_hci.log -Y 'btatt.opcode == 0x12 || btatt.opcode == 0x1d' -Tfields -e frame.number -e btatt.opcode -e btatt.handle -e btatt.value >capture.log -// Then, assuming you already have a config file with your offline key, decrypt it: +// Then, assuming you already have set your offline key: // node tools/decode_capture.js capture.log >command.log +'use strict'; + var crypto = require('crypto'); var fs = require('fs'); @@ -20,6 +20,185 @@ function isSecurityChecksumValid(buf) { return cs === buf.readUInt32LE(0x0c); } +function isSimpleChecksumValid(buf) { + var cs = 0; + for (var i = 0; i < 0x12; i++) { + cs = (cs + buf[i]) & 0xff; + } + return cs === 0; +} + +var STATUS = { + 0: 'STM32_FIRMWARE', + 2: 'LOCK_STATE', + 3: 'CURRENT_ANGLE', + 5: 'BATTERY_LEVEL', + 9: 'LOCK_EVENTS_UNREAD', + 10: 'RTC', + 41: 'GIT_HASH' +}; + +var PARAMETERS = { + 0: 'BACKOFF_TIME_MS', + 1: 'ANIMATION_PERIOD_MS', + 2: 'BATTERY_TYPE', + 3: 'SERIAL_NUMBER_L', + 4: 'SERIAL_NUMBER_H', + 5: 'MANUF_INFO', + 6: 'MANUF_DATE', + 7: 'AUDIO_VOLUME', + 8: 'LED_BRIGHTNESS', + 9: 'CURRENT_KDT', + 10: 'CURRENT_KI', + 11: 'CURRENT_KP', + 12: 'CURRENT_ILIMIT', + 13: 'POS_KDT', + 14: 'POS_KI', + 15: 'POS_KP', + 16: 'POS_ILIMT', + 17: 'RSSI_THRESHOLD', + 18: 'RSSI_FILTER', + 19: 'ACC_THRESHOLD', + 20: 'KNOCK_THRESHOLD', + 21: 'KNOCK_FILTER', + 22: 'STALL_CURRENT_LIMIT', + 23: 'PWM_LIMIT', + 24: 'STALL_POSITION_CW', + 25: 'STALL_POSITION_CCW', + 26: 'TARGET_POSITION_CW', + 27: 'TARGET_POSITION_CCW', + 28: 'BATTERY_SOC_THRESHOLD', + 29: 'MIN_STALL_TIME_MS', + 30: 'BACKOFF_ANGLE', + 31: 'ANGLE_THRESHOLD', + 32: 'ORIENTATION', + 33: 'TEMPERATURE_WARNING', + 34: 'TEMPERATURE_ALARM', + 35: 'ABSOLUTE_CURRENT_LIMIT', + 36: 'MIN_BATTERY_VOTAGE_MV', + 37: 'LOCK_OP_TIMEOUT', + 38: 'BACKOFF_CURRENT_MA', + 39: 'CURRENT_MEASURE_INTERVAL_MS', + 40: 'RELOCK_SEC', + 41: 'UNLOCKED_TOL', + 42: 'LOCKED_TOL', + 43: 'MAX_STALL_CURRENT_ERROR', + 44: 'AUTOCAL_MS', + 45: 'MOTOR_POLARITY', + 46: 'AUTOCAL_ATTEMPTS', + 47: 'PWM_DZONE', + 48: 'NUM_RETRIES', + 49: 'RETRY_TIMER', + 50: 'HLIMIT_ANGLE', + 51: 'SIMULATED', + 52: 'ANGLE_TAU', + 53: 'ACC_VERBOSE', + 54: 'MOTION_VERBOSE', + 55: 'MIN_AWAKE_TIME_MS', + 56: 'AUDIO_ENABLED', + 57: 'BURNIN_TRG', + 58: 'BURNIN_CYCLES', + 59: 'BURNIN_DELAY', + 60: 'BURNIN_CYCLES_DONE', + 61: 'BURNIN_CYCLES_SUCCESS', + 62: 'BURNIN_FAIL_TOL', + 63: 'BURNIN_CAL_SUCCESS', + 64: 'BURNIN_ATTEMPTS', + 65: 'BURNIN_FAILED_ATTEMPTS', + 66: 'BURNIN_TIMESTAMP', + 67: 'BURNIN_NOSLEEP', + 68: 'BURNIN_AUTO_RESTART', + 69: 'BURNIN_INIT_DELAY', + 70: 'BURNIN_IN_PROGRESS', + 71: 'BURNIN_ERRORS', + 72: 'BAT_CRITICAL_TH', + 73: 'BAT_LOW_TH', + 74: 'BAT_MEDIUM_TH', + 75: 'BACKOFF_MIN_MS', + 76: 'BACKOFF_NUM_SAMPLES', + 77: 'AUDIO_INTER_DELAY', + 78: 'POSKP_BACKOFF' +}; + +// return a crude description of the command +function describe(command) { + switch (command[0]) { + case 0x01: return '-> SEC_LOCK_TO_MOBILE_KEY_EXCHANGE'; + case 0x02: return '<- SEC_LOCK_TO_MOBILE_KEY_EXCHANGE'; + case 0x03: return '-> SEC_INITIALIZATION_COMMAND'; + case 0x04: return '<- SEC_INITIALIZATION_COMMAND'; + case 0x05: return '-> DISCONNECT'; + case 0xaa: + case 0xbb: + case 0xee: + var isResponse = command[0] !== 0xee; + var prefix = isResponse ? '<- ' : '-> '; + var suffix = ''; + switch (command[1]) { + case 0x00: + if (isResponse) { + var index = command[2]; + switch (index) { + case 0: + suffix = JSON.stringify({ + timestamp: command.readUInt32LE(4), + opcode: command[8], + currentLockState: command[9], + rssi: command.slice(10, 15).toString('hex'), + error: command[15] + }); + break; + case 1: + suffix = JSON.stringify({ + currentAngularPosition: command.readUInt16LE(4), + targetAngularPosition: command.readUInt16LE(6), + coulombCounter: command.readUInt32LE(8), + currentSamples: [ + command.readUInt16LE(12), + command.readUInt16LE(14), + ] + }); + break; + case 2: + var samples = []; + for (var i = 0; i < 6; i++) { + samples.push(command.readUInt16LE(4 + i * 2)); + } + suffix = JSON.stringify({currentSamples: samples}); + break; + case 3: + suffix = JSON.stringify({ + currentSamples: [ + command.readUInt16LE(4), + command.readUInt16LE(6), + ], + batteryLevel: command.readUInt16LE(8) + }); + break; + } + } + return prefix + 'GET_LOCK_EVENTS' + suffix; + case 0x02: + var status = command.readUInt32LE(0x04); + if (isResponse) { + suffix = ' = 0x' + command.readUInt32LE(0x08).toString(16); + } + return prefix + 'LOCK_STATUS (0x' + status.toString(16) + ' ' + STATUS[status] + ')' + suffix; + case 0x04: + var parameter = command.readUInt32LE(0x04); + if (isResponse) { + suffix = ' = 0x' + command.readUInt32LE(0x08).toString(16); + } + return prefix + 'GET_PARAM (0x' + parameter.toString(16) + ' ' + PARAMETERS[parameter] + ')' + suffix; + case 0x0a: return prefix + 'UNLOCK'; + case 0x0b: return prefix + 'LOCK'; + } + break; + case 0x8b: return '<- DISCONNECT'; + } + return null; +} + function decode(frameNumber, opcode, handle, data) { var isSecure = (handle === 38 || handle === 41); var cipher = (opcode === 18) ? (isSecure ? txCipherSec : txCipher) : (isSecure ? rxCipherSec : rxCipher); @@ -34,9 +213,13 @@ function decode(frameNumber, opcode, handle, data) { if (!isSecurityChecksumValid(data)) { op = op + '*'; } + } else { + if (!isSimpleChecksumValid(data)) { + op = op + '*'; + } } - console.log([frameNumber, op, data.toString('hex')].join('\t')); + console.log([frameNumber, op, data.toString('hex'), describe(data)].join('\t')); if (isSecure) { switch (data[0]) { diff --git a/tools/decrypt_preferences.js b/tools/decrypt_preferences.js index f19cecded633221a3c6a1e6993f43ed2676fde50..33c11c231c9de3779828cd36b0d6879efbc186e6 100644 --- a/tools/decrypt_preferences.js +++ b/tools/decrypt_preferences.js @@ -9,7 +9,7 @@ var hexEncoded = /[0-9A-F]+(?=\<\/string\>)/.exec(prefs)[0]; var cipherText = new Buffer(hexEncoded, 'hex'); // decrypt -const key = new Buffer('August#@3417r\0\0\0', 'utf8'); +var key = new Buffer('August#@3417r\0\0\0', 'utf8'); var cipher = crypto.createDecipheriv('aes-128-ecb', key, ''); cipher.setAutoPadding(false); var plaintext = cipher.update(cipherText) + cipher.final();