Skip to content
Snippets Groups Projects
Commit 81a5facb authored by Dan Walters's avatar Dan Walters
Browse files

refactor: modularize code a bit better

parent af23f2ea
Branches
Tags
No related merge requests found
#!/usr/bin/env node #!/usr/bin/env node
var Lock = require('./lock'); var Lock = require('./lib/lock');
var noble = require('noble'); var noble = require('noble');
var argv = require('yargs') var argv = require('yargs')
......
var Lock = require('./lock'); 'use strict';
var Lock = require('./lib/lock');
module.exports = { module.exports = {
Lock: Lock Lock: Lock
......
var Promise = require('bluebird'); 'use strict';
var crypto = require('crypto');
var debug = require('debug')('august');
var events = require('events');
var noble = require('noble');
var util = require('util');
var _ = require('underscore');
// promisification of noble var LockSession = require('./lock_session');
Promise.promisifyAll(require('noble/lib/characteristic').prototype);
Promise.promisifyAll(require('noble/lib/peripheral').prototype);
Promise.promisifyAll(require('noble/lib/service').prototype);
// relevant UUIDs - w/ this library, must be lowercase and without hyphens // relevant UUIDs - w/ this library, must be lowercase and without hyphens
const BLE_COMMAND_SERVICE = "bd4ac6100b4511e38ffd0800200c9a66"; const BLE_COMMAND_SERVICE = "bd4ac6100b4511e38ffd0800200c9a66";
...@@ -28,119 +19,6 @@ function LockCommand() { ...@@ -28,119 +19,6 @@ function LockCommand() {
return cmd; return cmd;
} }
// Calculates the security checksum of a command buffer.
function securityChecksum(buffer) {
return (0 - (buffer.readUInt32LE(0x00) + buffer.readUInt32LE(0x04) + buffer.readUInt32LE(0x08))) >>> 0;
}
///
// LockSession
function LockSession(writeCharacteristic, readCharacteristic, isSecure) {
if (!writeCharacteristic || !readCharacteristic) {
throw new Error('write and/or read characteristic not found');
}
this._writeCharacteristic = writeCharacteristic;
this._readCharacteristic = readCharacteristic;
this._isSecure = isSecure;
return this;
}
util.inherits(LockSession, events.EventEmitter);
LockSession.prototype.setKey = function(key) {
var cipherSuite, iv;
if (this._isSecure) {
cipherSuite = 'aes-128-ecb';
iv = '';
} else {
cipherSuite = 'aes-128-cbc';
iv = new Buffer(0x10);
iv.fill(0);
}
this._encryptCipher = crypto.createCipheriv(cipherSuite, key, iv);
this._encryptCipher.setAutoPadding(false);
this._decryptCipher = crypto.createDecipheriv(cipherSuite, key, iv);
this._decryptCipher.setAutoPadding(false);
};
LockSession.prototype.start = function() {
// decrypt all reads, modifying the buffer in place
this._readCharacteristic.on('read', function(data, isNotify) {
if (!data) {
throw new Error('read returned no data');
}
debug('read data: ' + data.toString('hex'));
if (this._decryptCipher) {
var cipherText = data.slice(0x00, 0x10);
var plainText = this._decryptCipher.update(cipherText);
plainText.copy(cipherText);
debug('decrypted data: ' + data.toString('hex'));
}
// the notification flag is not being set properly on OSX Yosemite, so just
// forcing it to true.
if (process.platform === 'darwin') {
isNotify = true;
}
if (isNotify) {
this.emit('notification', data);
}
}.bind(this));
// enable notifications on the read characterestic
debug('enabling notifications on ' + this._readCharacteristic);
return this._readCharacteristic.notifyAsync(true);
};
LockSession.prototype.execute = function(command) {
// write the security checksum if on the secure channel
if (this._isSecure) {
var checksum = securityChecksum(command);
command.writeUInt32LE(checksum, 0x0c);
}
debug((this._isSecure ? 'secure ' : '') + 'execute command: ' + command.toString('hex'));
// NOTE: the last two bytes are not encrypted
// general idea seems to be that if the last byte of the command indicates an offline key offset (is non-zero), the command is "secure" and encrypted with the offline key
if (this._encryptCipher) {
var plainText = command.slice(0x00, 0x10);
var cipherText = this._encryptCipher.update(plainText);
cipherText.copy(plainText);
debug('execute command (encrypted): ' + command.toString('hex'));
}
// register the notification event listener here, before issuing the write, as the
// response notification arrives before the write response.
var waitForNotification = new Promise(function(resolve) {
this.once('notification', resolve);
}.bind(this));
return this._writeCharacteristic.writeAsync(command, false).then(function() {
debug('write successful, waiting for notification...');
return waitForNotification;
}).then(function(data) {
// perform some basic validation before passing it on
if (this._isSecure) {
if (securityChecksum(data) !== data.readUInt32LE(0x0c)) {
throw new Error("security checksum mismatch");
}
} else {
if (data[0] !== 0xbb && data[0] !== 0xaa) {
throw new Error("unexpected magic in response");
}
}
return data;
}.bind(this));
};
/// ///
// Lock object. // Lock object.
......
'use strict';
var Promise = require('bluebird');
var crypto = require('crypto');
var debug = require('debug')('august');
var events = require('events');
var noble = require('noble');
var util = require('util');
var _ = require('underscore');
// promisification of noble
Promise.promisifyAll(require('noble/lib/characteristic').prototype);
Promise.promisifyAll(require('noble/lib/peripheral').prototype);
Promise.promisifyAll(require('noble/lib/service').prototype);
// Calculates the security checksum of a command buffer.
function securityChecksum(buffer) {
return (0 - (buffer.readUInt32LE(0x00) + buffer.readUInt32LE(0x04) + buffer.readUInt32LE(0x08))) >>> 0;
}
///
// LockSession
function LockSession(writeCharacteristic, readCharacteristic, isSecure) {
if (!writeCharacteristic || !readCharacteristic) {
throw new Error('write and/or read characteristic not found');
}
this._writeCharacteristic = writeCharacteristic;
this._readCharacteristic = readCharacteristic;
this._isSecure = isSecure;
return this;
}
util.inherits(LockSession, events.EventEmitter);
LockSession.prototype.setKey = function(key) {
var cipherSuite, iv;
if (this._isSecure) {
cipherSuite = 'aes-128-ecb';
iv = '';
} else {
cipherSuite = 'aes-128-cbc';
iv = new Buffer(0x10);
iv.fill(0);
}
this._encryptCipher = crypto.createCipheriv(cipherSuite, key, iv);
this._encryptCipher.setAutoPadding(false);
this._decryptCipher = crypto.createDecipheriv(cipherSuite, key, iv);
this._decryptCipher.setAutoPadding(false);
};
LockSession.prototype.start = function() {
// decrypt all reads, modifying the buffer in place
this._readCharacteristic.on('read', function(data, isNotify) {
if (!data) {
throw new Error('read returned no data');
}
debug('read data: ' + data.toString('hex'));
if (this._decryptCipher) {
var cipherText = data.slice(0x00, 0x10);
var plainText = this._decryptCipher.update(cipherText);
plainText.copy(cipherText);
debug('decrypted data: ' + data.toString('hex'));
}
// the notification flag is not being set properly on OSX Yosemite, so just
// forcing it to true.
if (process.platform === 'darwin') {
isNotify = true;
}
if (isNotify) {
this.emit('notification', data);
}
}.bind(this));
// enable notifications on the read characterestic
debug('enabling notifications on ' + this._readCharacteristic);
return this._readCharacteristic.notifyAsync(true);
};
LockSession.prototype.execute = function(command) {
// write the security checksum if on the secure channel
if (this._isSecure) {
var checksum = securityChecksum(command);
command.writeUInt32LE(checksum, 0x0c);
}
debug((this._isSecure ? 'secure ' : '') + 'execute command: ' + command.toString('hex'));
// NOTE: the last two bytes are not encrypted
// general idea seems to be that if the last byte of the command indicates an offline key offset (is non-zero), the command is "secure" and encrypted with the offline key
if (this._encryptCipher) {
var plainText = command.slice(0x00, 0x10);
var cipherText = this._encryptCipher.update(plainText);
cipherText.copy(plainText);
debug('execute command (encrypted): ' + command.toString('hex'));
}
// register the notification event listener here, before issuing the write, as the
// response notification arrives before the write response.
var waitForNotification = new Promise(function(resolve) {
this.once('notification', resolve);
}.bind(this));
return this._writeCharacteristic.writeAsync(command, false).then(function() {
debug('write successful, waiting for notification...');
return waitForNotification;
}).then(function(data) {
// perform some basic validation before passing it on
if (this._isSecure) {
if (securityChecksum(data) !== data.readUInt32LE(0x0c)) {
throw new Error("security checksum mismatch");
}
} else {
if (data[0] !== 0xbb && data[0] !== 0xaa) {
throw new Error("unexpected magic in response");
}
}
return data;
}.bind(this));
};
module.exports = LockSession;
'use strict';
// Decrypts a btsnoop_hci.log from an Android device (enabled in developer options.) // 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: // First use tshark to create a text log from a hci capture:
......
'use strict';
var crypto = require('crypto'); var crypto = require('crypto');
var fs = require('fs'); var fs = require('fs');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment