Skip to content

Commit

Permalink
Move setup ID related functions
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelthomas2774 committed Nov 2, 2020
1 parent 0903a20 commit 60a52f7
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 58 deletions.
53 changes: 5 additions & 48 deletions src/lib/Accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
CameraRTPStreamManagement,
} from "./gen/HomeKit";
import { ControllerStorage } from "./model/ControllerStorage";
import { generateSetupId, generateSetupUri } from './util/setupid';

const debug = createDebug('HAP-NodeJS:Accessory');
const MAX_ACCESSORIES = 149; // Maximum number of bridged accessories per bridge.
Expand Down Expand Up @@ -690,39 +691,6 @@ export class Accessory extends EventEmitter<Events> {
});
}

setupURI = (pincode?: string) => {
if (this._setupURI && !pincode) {
return this._setupURI;
}

var buffer = Buffer.alloc(8);
var setupCode = parseInt((pincode || (this._accessoryInfo && this._accessoryInfo.pincode))!.replace(/-/g, ''), 10);

var value_low = setupCode;
var value_high = this._accessoryInfo && this._accessoryInfo.category >> 1;

value_low |= 1 << 28; // Supports IP;

buffer.writeUInt32BE(value_low, 4);

if (this._accessoryInfo && this._accessoryInfo.category & 1) {
buffer[4] = buffer[4] | 1 << 7;
}

buffer.writeUInt32BE(value_high!, 0);

var encodedPayload = (buffer.readUInt32BE(4) + (buffer.readUInt32BE(0) * Math.pow(2, 32))).toString(36).toUpperCase();

if (encodedPayload.length != 9) {
for (var i = 0; i <= 9 - encodedPayload.length; i++) {
encodedPayload = "0" + encodedPayload;
}
}

this._setupURI = "X-HM://" + encodedPayload + this._setupID;
return this._setupURI;
}

/**
* This method is called right before the accessory is published. It should be used to check for common
* mistakes in Accessory structured, which may lead to HomeKit rejecting the accessory when pairing.
Expand Down Expand Up @@ -902,7 +870,7 @@ export class Accessory extends EventEmitter<Events> {
if (info.setupID) {
this._setupID = info.setupID;
} else if (this._accessoryInfo.setupID === undefined || this._accessoryInfo.setupID === "") {
this._setupID = this._generateSetupID();
this._setupID = generateSetupId();
} else {
this._setupID = this._accessoryInfo.setupID;
}
Expand Down Expand Up @@ -1056,7 +1024,9 @@ export class Accessory extends EventEmitter<Events> {
/** Called when starting the pair setup process after a setup code has been generated */
_handlePairSetupStarted = (setupcode: string, session: Session) => {
if (this.listenerCount(AccessoryEventTypes.PAIR_SETUP_STARTED)) {
this.emit(AccessoryEventTypes.PAIR_SETUP_STARTED, setupcode, this.setupURI(setupcode), session);
if (!this._setupID) this._setupID = generateSetupId();
const setupuri = generateSetupUri(setupcode, this._setupID, this.category);
this.emit(AccessoryEventTypes.PAIR_SETUP_STARTED, setupcode, setupuri, session);
} else if (!this._accessoryInfo!.pincode) {
// If we're using random setup codes and there's nothing listening for the setup code print it to the console
console.log('[%s] Received pair request from %s', this.displayName, session._connection._clientSocket.remoteAddress, setupcode);
Expand Down Expand Up @@ -1630,19 +1600,6 @@ export class Accessory extends EventEmitter<Events> {
});
}

_generateSetupID = () => {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const bytes = crypto.randomBytes(4);
let setupID = '';

for (var i = 0; i < 4; i++) {
var index = bytes.readUInt8(i) % 26;
setupID += chars.charAt(index);
}

return setupID;
}

// serialization and deserialization functions, mainly designed for homebridge to create a json copy to store on disk
public static serialize(accessory: Accessory): SerializedAccessory {
const json: SerializedAccessory = {
Expand Down
12 changes: 2 additions & 10 deletions src/lib/Advertiser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import bonjour, { BonjourHap, MulticastOptions, Service } from 'bonjour-hap';

import { Nullable } from '../types';
import { AccessoryInfo } from './model/AccessoryInfo';
import { generateSetupHash } from './util/setupid';

/**
* Advertiser uses mdns to broadcast the presence of an Accessory to the local network.
Expand All @@ -25,7 +26,7 @@ export class Advertiser {
constructor(public accessoryInfo: AccessoryInfo, mdnsConfig: MulticastOptions) {
this._bonjourService = bonjour(mdnsConfig);
this._advertisement = null;
this._setupHash = this._computeSetupHash();
this._setupHash = generateSetupHash(this.accessoryInfo.username, this.accessoryInfo.setupID).toString('base64');
}

startAdvertising = (port: number) => {
Expand Down Expand Up @@ -104,14 +105,5 @@ export class Advertiser {

this._bonjourService.destroy();
}

_computeSetupHash = () => {
var setupHashMaterial = this.accessoryInfo.setupID + this.accessoryInfo.username;
var hash = crypto.createHash('sha512');
hash.update(setupHashMaterial);
var setupHash = hash.digest().slice(0, 4).toString('base64');

return setupHash;
}
}

53 changes: 53 additions & 0 deletions src/lib/util/setupid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

import * as crypto from 'crypto';
import {Categories} from '../Accessory';

export function generateSetupId() {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const bytes = crypto.randomBytes(4);
let setupID = '';

for (var i = 0; i < 4; i++) {
var index = bytes.readUInt8(i) % 26;
setupID += chars.charAt(index);
}

return setupID;
}

export function generateSetupHash(deviceid: string, setupid: string) {
if (!setupid.match(/^[0-9A-Z]{4}$/)) throw new Error('Invalid setup ID');

return crypto.createHash('sha512')
.update(setupid + deviceid)
.digest().slice(0, 4);
}

export function generateSetupUri(setupcode: string | number, setupid: string, category = Categories.OTHER) {
if (typeof setupcode === 'string') setupcode = parseInt(setupcode.replace(/-/g, ''), 10);
const buffer = Buffer.alloc(8);

let value_low = setupcode;
const value_high = category >> 1;

value_low |= 1 << 28; // Supports IP;

buffer.writeUInt32BE(value_low, 4);

if (category & 1) {
buffer[4] = buffer[4] | 1 << 7;
}

buffer.writeUInt32BE(value_high!, 0);

let encodedPayload = (buffer.readUInt32BE(4) + (buffer.readUInt32BE(0) * Math.pow(2, 32)))
.toString(36).toUpperCase();

if (encodedPayload.length != 9) {
for (let i = 0; i <= 9 - encodedPayload.length; i++) {
encodedPayload = "0" + encodedPayload;
}
}

return "X-HM://" + encodedPayload + setupid;
}

0 comments on commit 60a52f7

Please sign in to comment.