hap-nodejs 1.1.1-beta.1 → 1.1.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/bonjour-hap.d.ts +50 -53
- package/README.md +16 -19
- package/dist/accessories/AirConditioner_accessory.js +28 -30
- package/dist/accessories/AirConditioner_accessory.js.map +1 -1
- package/dist/accessories/AppleTVRemote_accessory.js +69 -81
- package/dist/accessories/AppleTVRemote_accessory.js.map +1 -1
- package/dist/accessories/Camera_accessory.js +141 -136
- package/dist/accessories/Camera_accessory.js.map +1 -1
- package/dist/accessories/Fan_accessory.js +18 -22
- package/dist/accessories/Fan_accessory.js.map +1 -1
- package/dist/accessories/GarageDoorOpener_accessory.js +33 -35
- package/dist/accessories/GarageDoorOpener_accessory.js.map +1 -1
- package/dist/accessories/Light-AdaptiveLighting_accessory.js +42 -44
- package/dist/accessories/Light-AdaptiveLighting_accessory.js.map +1 -1
- package/dist/accessories/Light_accessory.js +32 -34
- package/dist/accessories/Light_accessory.js.map +1 -1
- package/dist/accessories/Lock_accessory.js +25 -26
- package/dist/accessories/Lock_accessory.js.map +1 -1
- package/dist/accessories/MotionSensor_accessory.js +13 -16
- package/dist/accessories/MotionSensor_accessory.js.map +1 -1
- package/dist/accessories/Outlet_accessory.js +20 -22
- package/dist/accessories/Outlet_accessory.js.map +1 -1
- package/dist/accessories/SmartSpeaker_accessory.js +18 -20
- package/dist/accessories/SmartSpeaker_accessory.js.map +1 -1
- package/dist/accessories/Sprinkler_accessory.js +34 -37
- package/dist/accessories/Sprinkler_accessory.js.map +1 -1
- package/dist/accessories/TV_accessory.js +43 -45
- package/dist/accessories/TV_accessory.js.map +1 -1
- package/dist/accessories/TemperatureSensor_accessory.js +12 -15
- package/dist/accessories/TemperatureSensor_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.d.ts +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.d.ts.map +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.js +9 -12
- package/dist/accessories/Wi-FiRouter_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.d.ts +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.d.ts.map +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.js +11 -14
- package/dist/accessories/Wi-FiSatellite_accessory.js.map +1 -1
- package/dist/accessories/gstreamer-audioProducer.d.ts +3 -3
- package/dist/accessories/gstreamer-audioProducer.d.ts.map +1 -1
- package/dist/accessories/gstreamer-audioProducer.js +37 -38
- package/dist/accessories/gstreamer-audioProducer.js.map +1 -1
- package/dist/accessories/types.d.ts +63 -63
- package/dist/accessories/types.d.ts.map +1 -1
- package/dist/accessories/types.js +83 -87
- package/dist/accessories/types.js.map +1 -1
- package/dist/index.d.ts +26 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -34
- package/dist/index.js.map +1 -1
- package/dist/internal-types.d.ts +1 -1
- package/dist/internal-types.d.ts.map +1 -1
- package/dist/internal-types.js +19 -21
- package/dist/internal-types.js.map +1 -1
- package/dist/lib/Accessory.d.ts +39 -38
- package/dist/lib/Accessory.d.ts.map +1 -1
- package/dist/lib/Accessory.js +314 -292
- package/dist/lib/Accessory.js.map +1 -1
- package/dist/lib/Advertiser.d.ts +7 -7
- package/dist/lib/Advertiser.d.ts.map +1 -1
- package/dist/lib/Advertiser.js +128 -136
- package/dist/lib/Advertiser.js.map +1 -1
- package/dist/lib/Bridge.d.ts +1 -1
- package/dist/lib/Bridge.d.ts.map +1 -1
- package/dist/lib/Bridge.js +2 -6
- package/dist/lib/Bridge.js.map +1 -1
- package/dist/lib/Characteristic.d.ts +40 -41
- package/dist/lib/Characteristic.d.ts.map +1 -1
- package/dist/lib/Characteristic.js +204 -208
- package/dist/lib/Characteristic.js.map +1 -1
- package/dist/lib/HAPServer.d.ts +31 -30
- package/dist/lib/HAPServer.d.ts.map +1 -1
- package/dist/lib/HAPServer.js +220 -229
- package/dist/lib/HAPServer.js.map +1 -1
- package/dist/lib/Service.d.ts +22 -22
- package/dist/lib/Service.d.ts.map +1 -1
- package/dist/lib/Service.js +63 -67
- package/dist/lib/Service.js.map +1 -1
- package/dist/lib/camera/RTPProxy.d.ts +2 -1
- package/dist/lib/camera/RTPProxy.d.ts.map +1 -1
- package/dist/lib/camera/RTPProxy.js +28 -44
- package/dist/lib/camera/RTPProxy.js.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.d.ts +34 -33
- package/dist/lib/camera/RTPStreamManagement.d.ts.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.js +179 -151
- package/dist/lib/camera/RTPStreamManagement.js.map +1 -1
- package/dist/lib/camera/RecordingManagement.d.ts +19 -17
- package/dist/lib/camera/RecordingManagement.d.ts.map +1 -1
- package/dist/lib/camera/RecordingManagement.js +160 -151
- package/dist/lib/camera/RecordingManagement.js.map +1 -1
- package/dist/lib/camera/index.d.ts +3 -3
- package/dist/lib/camera/index.d.ts.map +1 -1
- package/dist/lib/camera/index.js +3 -6
- package/dist/lib/camera/index.js.map +1 -1
- package/dist/lib/controller/AdaptiveLightingController.d.ts +33 -33
- package/dist/lib/controller/AdaptiveLightingController.d.ts.map +1 -1
- package/dist/lib/controller/AdaptiveLightingController.js +152 -144
- package/dist/lib/controller/AdaptiveLightingController.js.map +1 -1
- package/dist/lib/controller/CameraController.d.ts +22 -20
- package/dist/lib/controller/CameraController.d.ts.map +1 -1
- package/dist/lib/controller/CameraController.js +74 -78
- package/dist/lib/controller/CameraController.js.map +1 -1
- package/dist/lib/controller/Controller.d.ts +4 -4
- package/dist/lib/controller/Controller.d.ts.map +1 -1
- package/dist/lib/controller/Controller.js +5 -8
- package/dist/lib/controller/Controller.js.map +1 -1
- package/dist/lib/controller/DoorbellController.d.ts +5 -4
- package/dist/lib/controller/DoorbellController.d.ts.map +1 -1
- package/dist/lib/controller/DoorbellController.js +9 -13
- package/dist/lib/controller/DoorbellController.js.map +1 -1
- package/dist/lib/controller/RemoteController.d.ts +39 -37
- package/dist/lib/controller/RemoteController.d.ts.map +1 -1
- package/dist/lib/controller/RemoteController.js +208 -197
- package/dist/lib/controller/RemoteController.js.map +1 -1
- package/dist/lib/controller/index.d.ts +5 -5
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/index.js +5 -8
- package/dist/lib/controller/index.js.map +1 -1
- package/dist/lib/datastream/DataStreamManagement.d.ts +2 -2
- package/dist/lib/datastream/DataStreamManagement.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamManagement.js +39 -39
- package/dist/lib/datastream/DataStreamManagement.js.map +1 -1
- package/dist/lib/datastream/DataStreamParser.d.ts +1 -0
- package/dist/lib/datastream/DataStreamParser.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamParser.js +57 -77
- package/dist/lib/datastream/DataStreamParser.js.map +1 -1
- package/dist/lib/datastream/DataStreamServer.d.ts +23 -22
- package/dist/lib/datastream/DataStreamServer.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamServer.js +154 -164
- package/dist/lib/datastream/DataStreamServer.js.map +1 -1
- package/dist/lib/datastream/index.d.ts +3 -3
- package/dist/lib/datastream/index.d.ts.map +1 -1
- package/dist/lib/datastream/index.js +3 -6
- package/dist/lib/datastream/index.js.map +1 -1
- package/dist/lib/dbus/align.d.ts +2 -0
- package/dist/lib/dbus/align.d.ts.map +1 -0
- package/dist/lib/dbus/align.js +12 -0
- package/dist/lib/dbus/align.js.map +1 -0
- package/dist/lib/dbus/bus.d.ts +38 -0
- package/dist/lib/dbus/bus.d.ts.map +1 -0
- package/dist/lib/dbus/bus.js +222 -0
- package/dist/lib/dbus/bus.js.map +1 -0
- package/dist/lib/dbus/constants.d.ts +43 -0
- package/dist/lib/dbus/constants.d.ts.map +1 -0
- package/dist/lib/dbus/constants.js +53 -0
- package/dist/lib/dbus/constants.js.map +1 -0
- package/dist/lib/dbus/dbus-buffer.d.ts +30 -0
- package/dist/lib/dbus/dbus-buffer.d.ts.map +1 -0
- package/dist/lib/dbus/dbus-buffer.js +175 -0
- package/dist/lib/dbus/dbus-buffer.js.map +1 -0
- package/dist/lib/dbus/handshake.d.ts +2 -0
- package/dist/lib/dbus/handshake.d.ts.map +1 -0
- package/dist/lib/dbus/handshake.js +130 -0
- package/dist/lib/dbus/handshake.js.map +1 -0
- package/dist/lib/dbus/index.d.ts +3 -0
- package/dist/lib/dbus/index.d.ts.map +1 -0
- package/dist/lib/dbus/index.js +123 -0
- package/dist/lib/dbus/index.js.map +1 -0
- package/dist/lib/dbus/introspect.d.ts +30 -0
- package/dist/lib/dbus/introspect.d.ts.map +1 -0
- package/dist/lib/dbus/introspect.js +208 -0
- package/dist/lib/dbus/introspect.js.map +1 -0
- package/dist/lib/dbus/marshall.d.ts +2 -0
- package/dist/lib/dbus/marshall.d.ts.map +1 -0
- package/dist/lib/dbus/marshall.js +97 -0
- package/dist/lib/dbus/marshall.js.map +1 -0
- package/dist/lib/dbus/marshallers.d.ts +10 -0
- package/dist/lib/dbus/marshallers.d.ts.map +1 -0
- package/dist/lib/dbus/marshallers.js +329 -0
- package/dist/lib/dbus/marshallers.js.map +1 -0
- package/dist/lib/dbus/message.d.ts +4 -0
- package/dist/lib/dbus/message.d.ts.map +1 -0
- package/dist/lib/dbus/message.js +116 -0
- package/dist/lib/dbus/message.js.map +1 -0
- package/dist/lib/dbus/put.d.ts +21 -0
- package/dist/lib/dbus/put.d.ts.map +1 -0
- package/dist/lib/dbus/put.js +120 -0
- package/dist/lib/dbus/put.js.map +1 -0
- package/dist/lib/dbus/readline.d.ts +2 -0
- package/dist/lib/dbus/readline.d.ts.map +1 -0
- package/dist/lib/dbus/readline.js +27 -0
- package/dist/lib/dbus/readline.js.map +1 -0
- package/dist/lib/dbus/signature.d.ts +2 -0
- package/dist/lib/dbus/signature.d.ts.map +1 -0
- package/dist/lib/dbus/signature.js +58 -0
- package/dist/lib/dbus/signature.js.map +1 -0
- package/dist/lib/dbus/stdifaces.d.ts +3 -0
- package/dist/lib/dbus/stdifaces.d.ts.map +1 -0
- package/dist/lib/dbus/stdifaces.js +206 -0
- package/dist/lib/dbus/stdifaces.js.map +1 -0
- package/dist/lib/definitions/CharacteristicDefinitions.d.ts +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.d.ts.map +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.js +958 -1204
- package/dist/lib/definitions/CharacteristicDefinitions.js.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.d.ts +1 -1
- package/dist/lib/definitions/ServiceDefinitions.d.ts.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.js +620 -695
- package/dist/lib/definitions/ServiceDefinitions.js.map +1 -1
- package/dist/lib/definitions/generate-definitions.d.ts +3 -3
- package/dist/lib/definitions/generate-definitions.d.ts.map +1 -1
- package/dist/lib/definitions/generate-definitions.js +246 -253
- package/dist/lib/definitions/generate-definitions.js.map +1 -1
- package/dist/lib/definitions/generator-configuration.d.ts +1 -1
- package/dist/lib/definitions/generator-configuration.d.ts.map +1 -1
- package/dist/lib/definitions/generator-configuration.js +160 -165
- package/dist/lib/definitions/generator-configuration.js.map +1 -1
- package/dist/lib/definitions/index.d.ts +2 -2
- package/dist/lib/definitions/index.d.ts.map +1 -1
- package/dist/lib/definitions/index.js +2 -5
- package/dist/lib/definitions/index.js.map +1 -1
- package/dist/lib/model/AccessoryInfo.d.ts +4 -3
- package/dist/lib/model/AccessoryInfo.d.ts.map +1 -1
- package/dist/lib/model/AccessoryInfo.js +50 -53
- package/dist/lib/model/AccessoryInfo.js.map +1 -1
- package/dist/lib/model/ControllerStorage.d.ts +3 -3
- package/dist/lib/model/ControllerStorage.d.ts.map +1 -1
- package/dist/lib/model/ControllerStorage.js +17 -22
- package/dist/lib/model/ControllerStorage.js.map +1 -1
- package/dist/lib/model/HAPStorage.d.ts +2 -2
- package/dist/lib/model/HAPStorage.d.ts.map +1 -1
- package/dist/lib/model/HAPStorage.js +4 -11
- package/dist/lib/model/HAPStorage.js.map +1 -1
- package/dist/lib/model/IdentifierCache.d.ts +1 -1
- package/dist/lib/model/IdentifierCache.d.ts.map +1 -1
- package/dist/lib/model/IdentifierCache.js +19 -27
- package/dist/lib/model/IdentifierCache.js.map +1 -1
- package/dist/lib/tv/AccessControlManagement.d.ts +9 -9
- package/dist/lib/tv/AccessControlManagement.d.ts.map +1 -1
- package/dist/lib/tv/AccessControlManagement.js +27 -29
- package/dist/lib/tv/AccessControlManagement.js.map +1 -1
- package/dist/lib/util/checkName.d.ts +2 -2
- package/dist/lib/util/checkName.d.ts.map +1 -1
- package/dist/lib/util/checkName.js +6 -9
- package/dist/lib/util/checkName.js.map +1 -1
- package/dist/lib/util/clone.d.ts.map +1 -1
- package/dist/lib/util/clone.js +1 -5
- package/dist/lib/util/clone.js.map +1 -1
- package/dist/lib/util/color-utils.d.ts +1 -1
- package/dist/lib/util/color-utils.d.ts.map +1 -1
- package/dist/lib/util/color-utils.js +4 -9
- package/dist/lib/util/color-utils.js.map +1 -1
- package/dist/lib/util/eventedhttp.d.ts +23 -22
- package/dist/lib/util/eventedhttp.d.ts.map +1 -1
- package/dist/lib/util/eventedhttp.js +109 -116
- package/dist/lib/util/eventedhttp.js.map +1 -1
- package/dist/lib/util/hapCrypto.d.ts +3 -2
- package/dist/lib/util/hapCrypto.d.ts.map +1 -1
- package/dist/lib/util/hapCrypto.js +31 -40
- package/dist/lib/util/hapCrypto.js.map +1 -1
- package/dist/lib/util/hapStatusError.d.ts +1 -1
- package/dist/lib/util/hapStatusError.d.ts.map +1 -1
- package/dist/lib/util/hapStatusError.js +4 -8
- package/dist/lib/util/hapStatusError.js.map +1 -1
- package/dist/lib/util/net-utils.d.ts +1 -1
- package/dist/lib/util/net-utils.js +17 -23
- package/dist/lib/util/net-utils.js.map +1 -1
- package/dist/lib/util/once.d.ts.map +1 -1
- package/dist/lib/util/once.js +2 -6
- package/dist/lib/util/once.js.map +1 -1
- package/dist/lib/util/promise-utils.d.ts +1 -1
- package/dist/lib/util/promise-utils.d.ts.map +1 -1
- package/dist/lib/util/promise-utils.js +3 -9
- package/dist/lib/util/promise-utils.js.map +1 -1
- package/dist/lib/util/request-util.d.ts +3 -2
- package/dist/lib/util/request-util.d.ts.map +1 -1
- package/dist/lib/util/request-util.js +11 -19
- package/dist/lib/util/request-util.js.map +1 -1
- package/dist/lib/util/time.d.ts +1 -0
- package/dist/lib/util/time.d.ts.map +1 -1
- package/dist/lib/util/time.js +6 -11
- package/dist/lib/util/time.js.map +1 -1
- package/dist/lib/util/tlv.d.ts +1 -0
- package/dist/lib/util/tlv.d.ts.map +1 -1
- package/dist/lib/util/tlv.js +28 -43
- package/dist/lib/util/tlv.js.map +1 -1
- package/dist/lib/util/uuid.d.ts +1 -0
- package/dist/lib/util/uuid.d.ts.map +1 -1
- package/dist/lib/util/uuid.js +26 -38
- package/dist/lib/util/uuid.js.map +1 -1
- package/dist/types.d.ts +24 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -5
- package/dist/types.js.map +1 -1
- package/package.json +51 -46
- package/@types/simple-plist.d.ts +0 -4
- package/dist/lib/gen/HomeKit.d.ts +0 -7
- package/dist/lib/gen/HomeKit.d.ts.map +0 -1
- package/dist/lib/gen/HomeKit.js +0 -8
- package/dist/lib/gen/HomeKit.js.map +0 -1
package/dist/lib/HAPServer.js
CHANGED
|
@@ -1,44 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const hapCrypto = tslib_1.__importStar(require("./util/hapCrypto"));
|
|
15
|
-
const once_1 = require("./util/once");
|
|
16
|
-
const tlv = tslib_1.__importStar(require("./util/tlv"));
|
|
17
|
-
const debug = (0, debug_1.default)("HAP-NodeJS:HAPServer");
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { EventEmitter } from 'node:events';
|
|
4
|
+
import { URL } from 'node:url';
|
|
5
|
+
import createDebug from 'debug';
|
|
6
|
+
import { SRP, SrpServer } from 'fast-srp-hap';
|
|
7
|
+
import tweetnacl from 'tweetnacl';
|
|
8
|
+
import { consideredTrue } from '../internal-types.js';
|
|
9
|
+
import { EventedHTTPServer, HAPEncryption } from './util/eventedhttp.js';
|
|
10
|
+
import { chacha20_poly1305_decryptAndVerify, chacha20_poly1305_encryptAndSeal, generateCurve25519KeyPair, generateCurve25519SharedSecKey, HKDF, } from './util/hapCrypto.js';
|
|
11
|
+
import { once } from './util/once.js';
|
|
12
|
+
import { decode, encode } from './util/tlv.js';
|
|
13
|
+
const debug = createDebug('HAP-NodeJS:HAPServer');
|
|
18
14
|
/**
|
|
19
15
|
* TLV error codes for the `TLVValues.ERROR_CODE` field.
|
|
20
16
|
*
|
|
21
17
|
* @group HAP Accessory Server
|
|
22
18
|
*/
|
|
23
|
-
|
|
19
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
20
|
+
export var TLVErrorCode;
|
|
24
21
|
(function (TLVErrorCode) {
|
|
25
|
-
// noinspection JSUnusedGlobalSymbols
|
|
26
22
|
TLVErrorCode[TLVErrorCode["UNKNOWN"] = 1] = "UNKNOWN";
|
|
27
23
|
TLVErrorCode[TLVErrorCode["INVALID_REQUEST"] = 2] = "INVALID_REQUEST";
|
|
28
|
-
// eslint-disable-next-line
|
|
24
|
+
// eslint-disable-next-line ts/no-duplicate-enum-values
|
|
29
25
|
TLVErrorCode[TLVErrorCode["AUTHENTICATION"] = 2] = "AUTHENTICATION";
|
|
30
26
|
TLVErrorCode[TLVErrorCode["BACKOFF"] = 3] = "BACKOFF";
|
|
31
27
|
TLVErrorCode[TLVErrorCode["MAX_PEERS"] = 4] = "MAX_PEERS";
|
|
32
28
|
TLVErrorCode[TLVErrorCode["MAX_TRIES"] = 5] = "MAX_TRIES";
|
|
33
29
|
TLVErrorCode[TLVErrorCode["UNAVAILABLE"] = 6] = "UNAVAILABLE";
|
|
34
|
-
TLVErrorCode[TLVErrorCode["BUSY"] = 7] = "BUSY";
|
|
35
|
-
})(TLVErrorCode || (
|
|
30
|
+
TLVErrorCode[TLVErrorCode["BUSY"] = 7] = "BUSY";
|
|
31
|
+
})(TLVErrorCode || (TLVErrorCode = {}));
|
|
36
32
|
/**
|
|
37
33
|
* @group HAP Accessory Server
|
|
38
34
|
*/
|
|
39
|
-
|
|
35
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
36
|
+
export var HAPStatus;
|
|
40
37
|
(function (HAPStatus) {
|
|
41
|
-
// noinspection JSUnusedGlobalSymbols
|
|
42
38
|
/**
|
|
43
39
|
* Success of the request.
|
|
44
40
|
*/
|
|
@@ -91,19 +87,19 @@ var HAPStatus;
|
|
|
91
87
|
* Operation not allowed in the current state.
|
|
92
88
|
*/
|
|
93
89
|
HAPStatus[HAPStatus["NOT_ALLOWED_IN_CURRENT_STATE"] = -70412] = "NOT_ALLOWED_IN_CURRENT_STATE";
|
|
94
|
-
// when adding new status codes, remember to update bounds in
|
|
95
|
-
})(HAPStatus || (
|
|
90
|
+
// when adding new status codes, remember to update bounds in isKnownHAPStatusError below
|
|
91
|
+
})(HAPStatus || (HAPStatus = {}));
|
|
96
92
|
/**
|
|
97
93
|
* Determines if the given status code is a known {@link HAPStatus} error code.
|
|
98
94
|
*
|
|
99
95
|
* @group HAP Accessory Server
|
|
100
96
|
*/
|
|
101
|
-
function
|
|
97
|
+
export function isKnownHAPStatusError(status) {
|
|
102
98
|
return (
|
|
103
99
|
// Lower bound (most negative error code)
|
|
104
|
-
status >= -70412 /* HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE */
|
|
100
|
+
status >= -70412 /* HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE */
|
|
105
101
|
// Upper bound (negative error code closest to zero)
|
|
106
|
-
status <= -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */);
|
|
102
|
+
&& status <= -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */);
|
|
107
103
|
}
|
|
108
104
|
/**
|
|
109
105
|
* Those status codes are the one listed as appropriate for the HAP spec!
|
|
@@ -115,9 +111,9 @@ function IsKnownHAPStatusError(status) {
|
|
|
115
111
|
*
|
|
116
112
|
* @group HAP Accessory Server
|
|
117
113
|
*/
|
|
118
|
-
|
|
114
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
115
|
+
export var HAPHTTPCode;
|
|
119
116
|
(function (HAPHTTPCode) {
|
|
120
|
-
// noinspection JSUnusedGlobalSymbols
|
|
121
117
|
HAPHTTPCode[HAPHTTPCode["OK"] = 200] = "OK";
|
|
122
118
|
HAPHTTPCode[HAPHTTPCode["NO_CONTENT"] = 204] = "NO_CONTENT";
|
|
123
119
|
HAPHTTPCode[HAPHTTPCode["MULTI_STATUS"] = 207] = "MULTI_STATUS";
|
|
@@ -128,27 +124,28 @@ var HAPHTTPCode;
|
|
|
128
124
|
// server error
|
|
129
125
|
HAPHTTPCode[HAPHTTPCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
|
130
126
|
HAPHTTPCode[HAPHTTPCode["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
|
|
131
|
-
})(HAPHTTPCode || (
|
|
127
|
+
})(HAPHTTPCode || (HAPHTTPCode = {}));
|
|
132
128
|
/**
|
|
133
129
|
* When in a request is made to the pairing endpoints, and mime type is 'application/pairing+tlv8'
|
|
134
130
|
* one should use the below status codes.
|
|
135
131
|
*
|
|
136
132
|
* @group HAP Accessory Server
|
|
137
133
|
*/
|
|
138
|
-
|
|
134
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
135
|
+
export var HAPPairingHTTPCode;
|
|
139
136
|
(function (HAPPairingHTTPCode) {
|
|
140
|
-
// noinspection JSUnusedGlobalSymbols
|
|
141
137
|
HAPPairingHTTPCode[HAPPairingHTTPCode["OK"] = 200] = "OK";
|
|
142
138
|
HAPPairingHTTPCode[HAPPairingHTTPCode["BAD_REQUEST"] = 400] = "BAD_REQUEST";
|
|
143
139
|
HAPPairingHTTPCode[HAPPairingHTTPCode["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
|
|
144
140
|
HAPPairingHTTPCode[HAPPairingHTTPCode["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
|
|
145
141
|
HAPPairingHTTPCode[HAPPairingHTTPCode["CONNECTION_AUTHORIZATION_REQUIRED"] = 470] = "CONNECTION_AUTHORIZATION_REQUIRED";
|
|
146
142
|
HAPPairingHTTPCode[HAPPairingHTTPCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
|
147
|
-
})(HAPPairingHTTPCode || (
|
|
143
|
+
})(HAPPairingHTTPCode || (HAPPairingHTTPCode = {}));
|
|
148
144
|
/**
|
|
149
145
|
* @group HAP Accessory Server
|
|
150
146
|
*/
|
|
151
|
-
|
|
147
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
148
|
+
export var HAPServerEventTypes;
|
|
152
149
|
(function (HAPServerEventTypes) {
|
|
153
150
|
/**
|
|
154
151
|
* Emitted when the server is fully set up and ready to receive connections.
|
|
@@ -191,7 +188,7 @@ var HAPServerEventTypes;
|
|
|
191
188
|
HAPServerEventTypes["SET_CHARACTERISTICS"] = "set-characteristics";
|
|
192
189
|
HAPServerEventTypes["REQUEST_RESOURCE"] = "request-resource";
|
|
193
190
|
HAPServerEventTypes["CONNECTION_CLOSED"] = "connection-closed";
|
|
194
|
-
})(HAPServerEventTypes || (
|
|
191
|
+
})(HAPServerEventTypes || (HAPServerEventTypes = {}));
|
|
195
192
|
/**
|
|
196
193
|
* The actual HAP server that iOS devices talk to.
|
|
197
194
|
*
|
|
@@ -213,8 +210,8 @@ var HAPServerEventTypes;
|
|
|
213
210
|
*
|
|
214
211
|
* @group HAP Accessory Server
|
|
215
212
|
*/
|
|
216
|
-
// eslint-disable-next-line
|
|
217
|
-
class HAPServer extends
|
|
213
|
+
// eslint-disable-next-line ts/no-unsafe-declaration-merging
|
|
214
|
+
export class HAPServer extends EventEmitter {
|
|
218
215
|
accessoryInfo;
|
|
219
216
|
httpServer;
|
|
220
217
|
unsuccessfulPairAttempts = 0; // after 100 unsuccessful attempts the server won't accept any further attempts. Will currently be reset on a reboot
|
|
@@ -224,13 +221,13 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
224
221
|
this.accessoryInfo = accessoryInfo;
|
|
225
222
|
this.allowInsecureRequest = false;
|
|
226
223
|
// internal server that does all the actual communication
|
|
227
|
-
this.httpServer = new
|
|
224
|
+
this.httpServer = new EventedHTTPServer();
|
|
228
225
|
this.httpServer.on("listening" /* EventedHTTPServerEvent.LISTENING */, this.onListening.bind(this));
|
|
229
226
|
this.httpServer.on("request" /* EventedHTTPServerEvent.REQUEST */, this.handleRequestOnHAPConnection.bind(this));
|
|
230
227
|
this.httpServer.on("connection-closed" /* EventedHTTPServerEvent.CONNECTION_CLOSED */, this.handleConnectionClosed.bind(this));
|
|
231
228
|
}
|
|
232
229
|
listen(port = 0, host) {
|
|
233
|
-
if (host ===
|
|
230
|
+
if (host === '::') {
|
|
234
231
|
// this will work around "EAFNOSUPPORT: address family not supported" errors
|
|
235
232
|
// on systems where IPv6 is not supported/enabled, we just use the node default then by supplying undefined
|
|
236
233
|
host = undefined;
|
|
@@ -253,14 +250,14 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
253
250
|
* @param value - The newly set value of the characteristic.
|
|
254
251
|
* @param originator - If specified, the connection will not get an event message.
|
|
255
252
|
* @param immediateDelivery - The HAP spec requires some characteristics to be delivery immediately.
|
|
256
|
-
*
|
|
253
|
+
* Namely, for the {@link Characteristic.ButtonEvent} and the {@link Characteristic.ProgrammableSwitchEvent} characteristics.
|
|
257
254
|
*/
|
|
258
255
|
sendEventNotifications(aid, iid, value, originator, immediateDelivery) {
|
|
259
256
|
try {
|
|
260
257
|
this.httpServer.broadcastEvent(aid, iid, value, originator, immediateDelivery);
|
|
261
258
|
}
|
|
262
259
|
catch (error) {
|
|
263
|
-
console.warn(
|
|
260
|
+
console.warn(`[${this.accessoryInfo.username}] Error when sending event notifications: ${error.message}`);
|
|
264
261
|
}
|
|
265
262
|
}
|
|
266
263
|
onListening(port, hostname) {
|
|
@@ -268,15 +265,15 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
268
265
|
}
|
|
269
266
|
// Called when an HTTP request was detected.
|
|
270
267
|
handleRequestOnHAPConnection(connection, request, response) {
|
|
271
|
-
debug(
|
|
268
|
+
debug('[%s] HAP Request: %s %s', this.accessoryInfo.username, request.method, request.url);
|
|
272
269
|
const buffers = [];
|
|
273
|
-
request.on(
|
|
274
|
-
request.on(
|
|
275
|
-
const url = new
|
|
270
|
+
request.on('data', data => buffers.push(data));
|
|
271
|
+
request.on('end', () => {
|
|
272
|
+
const url = new URL(request.url, 'http://hap-nodejs.local'); // parse the url (query strings etc)
|
|
276
273
|
const handler = this.getHandler(url);
|
|
277
274
|
if (!handler) {
|
|
278
|
-
debug(
|
|
279
|
-
response.writeHead(404 /* HAPHTTPCode.NOT_FOUND */, {
|
|
275
|
+
debug('[%s] WARNING: Handler for %s not implemented', this.accessoryInfo.username, request.url);
|
|
276
|
+
response.writeHead(404 /* HAPHTTPCode.NOT_FOUND */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
280
277
|
response.end(JSON.stringify({ status: -70409 /* HAPStatus.RESOURCE_DOES_NOT_EXIST */ }));
|
|
281
278
|
}
|
|
282
279
|
else {
|
|
@@ -285,8 +282,8 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
285
282
|
handler(connection, url, request, data, response);
|
|
286
283
|
}
|
|
287
284
|
catch (error) {
|
|
288
|
-
debug(
|
|
289
|
-
response.writeHead(500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, {
|
|
285
|
+
debug('[%s] Error executing route handler: %s', this.accessoryInfo.username, error.stack);
|
|
286
|
+
response.writeHead(500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
290
287
|
response.end(JSON.stringify({ status: -70403 /* HAPStatus.RESOURCE_BUSY */ })); // resource busy try again, does somehow fit?
|
|
291
288
|
}
|
|
292
289
|
}
|
|
@@ -297,21 +294,21 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
297
294
|
}
|
|
298
295
|
getHandler(url) {
|
|
299
296
|
switch (url.pathname.toLowerCase()) {
|
|
300
|
-
case
|
|
297
|
+
case '/identify':
|
|
301
298
|
return this.handleIdentifyRequest.bind(this);
|
|
302
|
-
case
|
|
299
|
+
case '/pair-setup':
|
|
303
300
|
return this.handlePairSetup.bind(this);
|
|
304
|
-
case
|
|
301
|
+
case '/pair-verify':
|
|
305
302
|
return this.handlePairVerify.bind(this);
|
|
306
|
-
case
|
|
303
|
+
case '/pairings':
|
|
307
304
|
return this.handlePairings.bind(this);
|
|
308
|
-
case
|
|
305
|
+
case '/accessories':
|
|
309
306
|
return this.handleAccessories.bind(this);
|
|
310
|
-
case
|
|
307
|
+
case '/characteristics':
|
|
311
308
|
return this.handleCharacteristics.bind(this);
|
|
312
|
-
case
|
|
309
|
+
case '/prepare':
|
|
313
310
|
return this.handlePrepareWrite.bind(this);
|
|
314
|
-
case
|
|
311
|
+
case '/resource':
|
|
315
312
|
return this.handleResource.bind(this);
|
|
316
313
|
default:
|
|
317
314
|
return undefined;
|
|
@@ -323,19 +320,19 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
323
320
|
handleIdentifyRequest(connection, url, request, data, response) {
|
|
324
321
|
// POST body is empty
|
|
325
322
|
if (this.accessoryInfo.paired() && !this.allowInsecureRequest) {
|
|
326
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
323
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
327
324
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
328
325
|
return;
|
|
329
326
|
}
|
|
330
|
-
this.emit("identify" /* HAPServerEventTypes.IDENTIFY */,
|
|
327
|
+
this.emit("identify" /* HAPServerEventTypes.IDENTIFY */, once((err) => {
|
|
331
328
|
if (!err) {
|
|
332
|
-
debug(
|
|
329
|
+
debug('[%s] Identification success', this.accessoryInfo.username);
|
|
333
330
|
response.writeHead(204 /* HAPHTTPCode.NO_CONTENT */);
|
|
334
331
|
response.end();
|
|
335
332
|
}
|
|
336
333
|
else {
|
|
337
|
-
debug(
|
|
338
|
-
response.writeHead(500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, {
|
|
334
|
+
debug('[%s] Identification error: %s', this.accessoryInfo.username, err.message);
|
|
335
|
+
response.writeHead(500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
339
336
|
response.end(JSON.stringify({ status: -70403 /* HAPStatus.RESOURCE_BUSY */ }));
|
|
340
337
|
}
|
|
341
338
|
}));
|
|
@@ -343,17 +340,17 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
343
340
|
handlePairSetup(connection, url, request, data, response) {
|
|
344
341
|
// Can only be directly paired with one iOS device
|
|
345
342
|
if (!this.allowInsecureRequest && this.accessoryInfo.paired()) {
|
|
346
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
347
|
-
response.end(
|
|
343
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
344
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, 6 /* TLVErrorCode.UNAVAILABLE */));
|
|
348
345
|
return;
|
|
349
346
|
}
|
|
350
347
|
if (this.unsuccessfulPairAttempts > 100) {
|
|
351
|
-
debug(
|
|
352
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
353
|
-
response.end(
|
|
348
|
+
debug('[%s] Reached maximum amount of unsuccessful pair attempts!', this.accessoryInfo.username);
|
|
349
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
350
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, 5 /* TLVErrorCode.MAX_TRIES */));
|
|
354
351
|
return;
|
|
355
352
|
}
|
|
356
|
-
const tlvData =
|
|
353
|
+
const tlvData = decode(data);
|
|
357
354
|
const sequence = tlvData[6 /* TLVValues.SEQUENCE_NUM */][0]; // value is single byte with sequence number
|
|
358
355
|
if (sequence === 1 /* PairingStates.M1 */) {
|
|
359
356
|
this.handlePairSetupM1(connection, request, response);
|
|
@@ -366,33 +363,31 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
366
363
|
}
|
|
367
364
|
else {
|
|
368
365
|
// Invalid state/sequence number
|
|
369
|
-
response.writeHead(400 /* HAPPairingHTTPCode.BAD_REQUEST */, {
|
|
370
|
-
response.end(
|
|
371
|
-
return;
|
|
366
|
+
response.writeHead(400 /* HAPPairingHTTPCode.BAD_REQUEST */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
367
|
+
response.end(encode(6 /* TLVValues.STATE */, sequence + 1, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
372
368
|
}
|
|
373
369
|
}
|
|
374
370
|
handlePairSetupM1(connection, request, response) {
|
|
375
|
-
debug(
|
|
376
|
-
const salt =
|
|
377
|
-
const srpParams =
|
|
378
|
-
|
|
371
|
+
debug('[%s] Pair step 1/5', this.accessoryInfo.username);
|
|
372
|
+
const salt = randomBytes(16);
|
|
373
|
+
const srpParams = SRP.params.hap;
|
|
374
|
+
SRP.genKey(32).then((key) => {
|
|
379
375
|
// create a new SRP server
|
|
380
|
-
const srpServer = new
|
|
376
|
+
const srpServer = new SrpServer(srpParams, salt, Buffer.from('Pair-Setup'), Buffer.from(this.accessoryInfo.pincode), key);
|
|
381
377
|
const srpB = srpServer.computeB();
|
|
382
378
|
// attach it to the current TCP session
|
|
383
379
|
connection.srpServer = srpServer;
|
|
384
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
385
|
-
response.end(
|
|
380
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
381
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 2 /* PairingStates.M2 */, 2 /* TLVValues.SALT */, salt, 3 /* TLVValues.PUBLIC_KEY */, srpB));
|
|
386
382
|
connection._pairSetupState = 2 /* PairingStates.M2 */;
|
|
387
|
-
}).catch(error => {
|
|
388
|
-
debug(
|
|
389
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
390
|
-
response.end(
|
|
391
|
-
return;
|
|
383
|
+
}).catch((error) => {
|
|
384
|
+
debug('[%s] Error occurred when generating srp key: %s', this.accessoryInfo.username, error.message);
|
|
385
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
386
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
392
387
|
});
|
|
393
388
|
}
|
|
394
389
|
handlePairSetupM3(connection, request, response, tlvData) {
|
|
395
|
-
debug(
|
|
390
|
+
debug('[%s] Pair step 2/5', this.accessoryInfo.username);
|
|
396
391
|
const A = tlvData[3 /* TLVValues.PUBLIC_KEY */]; // "A is a public key that exists only for a single login session."
|
|
397
392
|
const M1 = tlvData[4 /* TLVValues.PASSWORD_PROOF */]; // "M1 is the proof that you actually know your own password."
|
|
398
393
|
// pull the SRP server we created in stepOne out of the current session
|
|
@@ -404,20 +399,20 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
404
399
|
catch (err) {
|
|
405
400
|
// most likely the client supplied an incorrect pincode.
|
|
406
401
|
this.unsuccessfulPairAttempts++;
|
|
407
|
-
debug(
|
|
408
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
409
|
-
response.end(
|
|
402
|
+
debug('[%s] Error while checking pincode: %s', this.accessoryInfo.username, err.message);
|
|
403
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
404
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 4 /* PairingStates.M4 */, 7 /* TLVValues.ERROR_CODE */, 2 /* TLVErrorCode.AUTHENTICATION */));
|
|
410
405
|
connection._pairSetupState = undefined;
|
|
411
406
|
return;
|
|
412
407
|
}
|
|
413
408
|
// "M2 is the proof that the server actually knows your password."
|
|
414
409
|
const M2 = srpServer.computeM2();
|
|
415
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
416
|
-
response.end(
|
|
410
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
411
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 4 /* PairingStates.M4 */, 4 /* TLVValues.PASSWORD_PROOF */, M2));
|
|
417
412
|
connection._pairSetupState = 4 /* PairingStates.M4 */;
|
|
418
413
|
}
|
|
419
414
|
handlePairSetupM5(connection, request, response, tlvData) {
|
|
420
|
-
debug(
|
|
415
|
+
debug('[%s] Pair step 3/5', this.accessoryInfo.username);
|
|
421
416
|
// pull the SRP server we created in stepOne out of the current session
|
|
422
417
|
const srpServer = connection.srpServer;
|
|
423
418
|
const encryptedData = tlvData[5 /* TLVValues.ENCRYPTED_DATA */];
|
|
@@ -426,22 +421,22 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
426
421
|
encryptedData.copy(messageData, 0, 0, encryptedData.length - 16);
|
|
427
422
|
encryptedData.copy(authTagData, 0, encryptedData.length - 16, encryptedData.length);
|
|
428
423
|
const S_private = srpServer.computeK();
|
|
429
|
-
const encSalt = Buffer.from(
|
|
430
|
-
const encInfo = Buffer.from(
|
|
431
|
-
const outputKey =
|
|
424
|
+
const encSalt = Buffer.from('Pair-Setup-Encrypt-Salt');
|
|
425
|
+
const encInfo = Buffer.from('Pair-Setup-Encrypt-Info');
|
|
426
|
+
const outputKey = HKDF('sha512', encSalt, S_private, encInfo, 32);
|
|
432
427
|
let plaintext;
|
|
433
428
|
try {
|
|
434
|
-
plaintext =
|
|
429
|
+
plaintext = chacha20_poly1305_decryptAndVerify(outputKey, Buffer.from('PS-Msg05'), null, messageData, authTagData);
|
|
435
430
|
}
|
|
436
431
|
catch (error) {
|
|
437
|
-
debug(
|
|
438
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
439
|
-
response.end(
|
|
432
|
+
debug('[%s] Error while decrypting and verifying M5 subTlv: %s', this.accessoryInfo.username);
|
|
433
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
434
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 4 /* PairingStates.M4 */, 7 /* TLVValues.ERROR_CODE */, 2 /* TLVErrorCode.AUTHENTICATION */));
|
|
440
435
|
connection._pairSetupState = undefined;
|
|
441
436
|
return;
|
|
442
437
|
}
|
|
443
438
|
// decode the client payload and pass it on to the next step
|
|
444
|
-
const M5Packet =
|
|
439
|
+
const M5Packet = decode(plaintext);
|
|
445
440
|
const clientUsername = M5Packet[1 /* TLVValues.USERNAME */];
|
|
446
441
|
const clientLTPK = M5Packet[3 /* TLVValues.PUBLIC_KEY */];
|
|
447
442
|
const clientProof = M5Packet[10 /* TLVValues.PROOF */];
|
|
@@ -449,16 +444,16 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
449
444
|
}
|
|
450
445
|
// M5-2
|
|
451
446
|
handlePairSetupM5_2(connection, request, response, clientUsername, clientLTPK, clientProof, hkdfEncKey) {
|
|
452
|
-
debug(
|
|
447
|
+
debug('[%s] Pair step 4/5', this.accessoryInfo.username);
|
|
453
448
|
const S_private = connection.srpServer.computeK();
|
|
454
|
-
const controllerSalt = Buffer.from(
|
|
455
|
-
const controllerInfo = Buffer.from(
|
|
456
|
-
const outputKey =
|
|
449
|
+
const controllerSalt = Buffer.from('Pair-Setup-Controller-Sign-Salt');
|
|
450
|
+
const controllerInfo = Buffer.from('Pair-Setup-Controller-Sign-Info');
|
|
451
|
+
const outputKey = HKDF('sha512', controllerSalt, S_private, controllerInfo, 32);
|
|
457
452
|
const completeData = Buffer.concat([outputKey, clientUsername, clientLTPK]);
|
|
458
|
-
if (!
|
|
459
|
-
debug(
|
|
460
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
461
|
-
response.end(
|
|
453
|
+
if (!tweetnacl.sign.detached.verify(completeData, clientProof, clientLTPK)) {
|
|
454
|
+
debug('[%s] Invalid signature', this.accessoryInfo.username);
|
|
455
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
456
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 6 /* PairingStates.M6 */, 7 /* TLVValues.ERROR_CODE */, 2 /* TLVErrorCode.AUTHENTICATION */));
|
|
462
457
|
connection._pairSetupState = undefined;
|
|
463
458
|
return;
|
|
464
459
|
}
|
|
@@ -466,35 +461,35 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
466
461
|
}
|
|
467
462
|
// M5 - F + M6
|
|
468
463
|
handlePairSetupM5_3(connection, request, response, clientUsername, clientLTPK, hkdfEncKey) {
|
|
469
|
-
debug(
|
|
464
|
+
debug('[%s] Pair step 5/5', this.accessoryInfo.username);
|
|
470
465
|
const S_private = connection.srpServer.computeK();
|
|
471
|
-
const accessorySalt = Buffer.from(
|
|
472
|
-
const accessoryInfo = Buffer.from(
|
|
473
|
-
const outputKey =
|
|
466
|
+
const accessorySalt = Buffer.from('Pair-Setup-Accessory-Sign-Salt');
|
|
467
|
+
const accessoryInfo = Buffer.from('Pair-Setup-Accessory-Sign-Info');
|
|
468
|
+
const outputKey = HKDF('sha512', accessorySalt, S_private, accessoryInfo, 32);
|
|
474
469
|
const serverLTPK = this.accessoryInfo.signPk;
|
|
475
470
|
const usernameData = Buffer.from(this.accessoryInfo.username);
|
|
476
471
|
const material = Buffer.concat([outputKey, usernameData, serverLTPK]);
|
|
477
472
|
const privateKey = Buffer.from(this.accessoryInfo.signSk);
|
|
478
|
-
const serverProof =
|
|
479
|
-
const message =
|
|
480
|
-
const encrypted =
|
|
473
|
+
const serverProof = tweetnacl.sign.detached(material, privateKey);
|
|
474
|
+
const message = encode(1 /* TLVValues.USERNAME */, usernameData, 3 /* TLVValues.PUBLIC_KEY */, serverLTPK, 10 /* TLVValues.PROOF */, serverProof);
|
|
475
|
+
const encrypted = chacha20_poly1305_encryptAndSeal(hkdfEncKey, Buffer.from('PS-Msg06'), null, message);
|
|
481
476
|
// finally, notify listeners that we have been paired with a client
|
|
482
|
-
this.emit("pair" /* HAPServerEventTypes.PAIR */, clientUsername.toString(), clientLTPK,
|
|
477
|
+
this.emit("pair" /* HAPServerEventTypes.PAIR */, clientUsername.toString(), clientLTPK, once((err) => {
|
|
483
478
|
if (err) {
|
|
484
|
-
debug(
|
|
485
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
486
|
-
response.end(
|
|
479
|
+
debug('[%s] Error adding pairing info: %s', this.accessoryInfo.username, err.message);
|
|
480
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
481
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 6 /* PairingStates.M6 */, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
487
482
|
connection._pairSetupState = undefined;
|
|
488
483
|
return;
|
|
489
484
|
}
|
|
490
485
|
// send final pairing response to client
|
|
491
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
492
|
-
response.end(
|
|
486
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
487
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 6 /* PairingStates.M6 */, 5 /* TLVValues.ENCRYPTED_DATA */, Buffer.concat([encrypted.ciphertext, encrypted.authTag])));
|
|
493
488
|
connection._pairSetupState = undefined;
|
|
494
489
|
}));
|
|
495
490
|
}
|
|
496
491
|
handlePairVerify(connection, url, request, data, response) {
|
|
497
|
-
const tlvData =
|
|
492
|
+
const tlvData = decode(data);
|
|
498
493
|
const sequence = tlvData[6 /* TLVValues.SEQUENCE_NUM */][0]; // value is single byte with sequence number
|
|
499
494
|
if (sequence === 1 /* PairingStates.M1 */) {
|
|
500
495
|
this.handlePairVerifyM1(connection, request, response, tlvData);
|
|
@@ -504,36 +499,35 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
504
499
|
}
|
|
505
500
|
else {
|
|
506
501
|
// Invalid state/sequence number
|
|
507
|
-
response.writeHead(400 /* HAPPairingHTTPCode.BAD_REQUEST */, {
|
|
508
|
-
response.end(
|
|
509
|
-
return;
|
|
502
|
+
response.writeHead(400 /* HAPPairingHTTPCode.BAD_REQUEST */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
503
|
+
response.end(encode(6 /* TLVValues.STATE */, sequence + 1, 7 /* TLVValues.ERROR_CODE */, 1 /* TLVErrorCode.UNKNOWN */));
|
|
510
504
|
}
|
|
511
505
|
}
|
|
512
506
|
handlePairVerifyM1(connection, request, response, tlvData) {
|
|
513
|
-
debug(
|
|
507
|
+
debug('[%s] Pair verify step 1/2', this.accessoryInfo.username);
|
|
514
508
|
const clientPublicKey = tlvData[3 /* TLVValues.PUBLIC_KEY */]; // Buffer
|
|
515
509
|
// generate new encryption keys for this session
|
|
516
|
-
const keyPair =
|
|
510
|
+
const keyPair = generateCurve25519KeyPair();
|
|
517
511
|
const secretKey = Buffer.from(keyPair.secretKey);
|
|
518
512
|
const publicKey = Buffer.from(keyPair.publicKey);
|
|
519
|
-
const sharedSec = Buffer.from(
|
|
513
|
+
const sharedSec = Buffer.from(generateCurve25519SharedSecKey(secretKey, clientPublicKey));
|
|
520
514
|
const usernameData = Buffer.from(this.accessoryInfo.username);
|
|
521
515
|
const material = Buffer.concat([publicKey, usernameData, clientPublicKey]);
|
|
522
516
|
const privateKey = Buffer.from(this.accessoryInfo.signSk);
|
|
523
|
-
const serverProof =
|
|
524
|
-
const encSalt = Buffer.from(
|
|
525
|
-
const encInfo = Buffer.from(
|
|
526
|
-
const outputKey =
|
|
527
|
-
connection.encryption = new
|
|
517
|
+
const serverProof = tweetnacl.sign.detached(material, privateKey);
|
|
518
|
+
const encSalt = Buffer.from('Pair-Verify-Encrypt-Salt');
|
|
519
|
+
const encInfo = Buffer.from('Pair-Verify-Encrypt-Info');
|
|
520
|
+
const outputKey = HKDF('sha512', encSalt, sharedSec, encInfo, 32).subarray(0, 32);
|
|
521
|
+
connection.encryption = new HAPEncryption(clientPublicKey, secretKey, publicKey, sharedSec, outputKey);
|
|
528
522
|
// compose the response data in TLV format
|
|
529
|
-
const message =
|
|
530
|
-
const encrypted =
|
|
531
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
532
|
-
response.end(
|
|
523
|
+
const message = encode(1 /* TLVValues.USERNAME */, usernameData, 10 /* TLVValues.PROOF */, serverProof);
|
|
524
|
+
const encrypted = chacha20_poly1305_encryptAndSeal(outputKey, Buffer.from('PV-Msg02'), null, message);
|
|
525
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
526
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 2 /* PairingStates.M2 */, 5 /* TLVValues.ENCRYPTED_DATA */, Buffer.concat([encrypted.ciphertext, encrypted.authTag]), 3 /* TLVValues.PUBLIC_KEY */, publicKey));
|
|
533
527
|
connection._pairVerifyState = 2 /* PairingStates.M2 */;
|
|
534
528
|
}
|
|
535
529
|
handlePairVerifyM3(connection, request, response, objects) {
|
|
536
|
-
debug(
|
|
530
|
+
debug('[%s] Pair verify step 2/2', this.accessoryInfo.username);
|
|
537
531
|
const encryptedData = objects[5 /* TLVValues.ENCRYPTED_DATA */];
|
|
538
532
|
const messageData = Buffer.alloc(encryptedData.length - 16);
|
|
539
533
|
const authTagData = Buffer.alloc(16);
|
|
@@ -543,16 +537,16 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
543
537
|
const enc = connection.encryption;
|
|
544
538
|
let plaintext;
|
|
545
539
|
try {
|
|
546
|
-
plaintext =
|
|
540
|
+
plaintext = chacha20_poly1305_decryptAndVerify(enc.hkdfPairEncryptionKey, Buffer.from('PV-Msg03'), null, messageData, authTagData);
|
|
547
541
|
}
|
|
548
542
|
catch (error) {
|
|
549
|
-
debug(
|
|
550
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
551
|
-
response.end(
|
|
543
|
+
debug('[%s] M3: Failed to decrypt and/or verify', this.accessoryInfo.username);
|
|
544
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
545
|
+
response.end(encode(6 /* TLVValues.STATE */, 4 /* PairingStates.M4 */, 7 /* TLVValues.ERROR_CODE */, 2 /* TLVErrorCode.AUTHENTICATION */));
|
|
552
546
|
connection._pairVerifyState = undefined;
|
|
553
547
|
return;
|
|
554
548
|
}
|
|
555
|
-
const decoded =
|
|
549
|
+
const decoded = decode(plaintext);
|
|
556
550
|
const clientUsername = decoded[1 /* TLVValues.USERNAME */];
|
|
557
551
|
const proof = decoded[10 /* TLVValues.PROOF */];
|
|
558
552
|
const material = Buffer.concat([enc.clientPublicKey, clientUsername, enc.publicKey]);
|
|
@@ -561,30 +555,30 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
561
555
|
// if we're not actually paired, then there's nothing to verify - this client thinks it's paired with us, but we
|
|
562
556
|
// disagree. Respond with invalid request (seems to match HomeKit Accessory Simulator behavior)
|
|
563
557
|
if (!clientPublicKey) {
|
|
564
|
-
debug(
|
|
565
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
566
|
-
response.end(
|
|
558
|
+
debug('[%s] Client %s attempting to verify, but we are not paired; rejecting client', this.accessoryInfo.username, clientUsername);
|
|
559
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
560
|
+
response.end(encode(6 /* TLVValues.STATE */, 4 /* PairingStates.M4 */, 7 /* TLVValues.ERROR_CODE */, 2 /* TLVErrorCode.AUTHENTICATION */));
|
|
567
561
|
connection._pairVerifyState = undefined;
|
|
568
562
|
return;
|
|
569
563
|
}
|
|
570
|
-
if (!
|
|
571
|
-
debug(
|
|
572
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
573
|
-
response.end(
|
|
564
|
+
if (!tweetnacl.sign.detached.verify(material, proof, clientPublicKey)) {
|
|
565
|
+
debug('[%s] Client %s provided an invalid signature', this.accessoryInfo.username, clientUsername);
|
|
566
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
567
|
+
response.end(encode(6 /* TLVValues.STATE */, 4 /* PairingStates.M4 */, 7 /* TLVValues.ERROR_CODE */, 2 /* TLVErrorCode.AUTHENTICATION */));
|
|
574
568
|
connection._pairVerifyState = undefined;
|
|
575
569
|
return;
|
|
576
570
|
}
|
|
577
|
-
debug(
|
|
578
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
579
|
-
response.end(
|
|
571
|
+
debug('[%s] Client %s verification complete', this.accessoryInfo.username, clientUsername);
|
|
572
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
573
|
+
response.end(encode(6 /* TLVValues.SEQUENCE_NUM */, 4 /* PairingStates.M4 */));
|
|
580
574
|
// now that the client has been verified, we must "upgrade" our pseudo-HTTP connection to include
|
|
581
575
|
// TCP-level encryption. We'll do this by adding some more encryption vars to the session, and using them
|
|
582
576
|
// in future calls to onEncrypt, onDecrypt.
|
|
583
|
-
const encSalt = Buffer.from(
|
|
584
|
-
const infoRead = Buffer.from(
|
|
585
|
-
const infoWrite = Buffer.from(
|
|
586
|
-
enc.accessoryToControllerKey =
|
|
587
|
-
enc.controllerToAccessoryKey =
|
|
577
|
+
const encSalt = Buffer.from('Control-Salt');
|
|
578
|
+
const infoRead = Buffer.from('Control-Read-Encryption-Key');
|
|
579
|
+
const infoWrite = Buffer.from('Control-Write-Encryption-Key');
|
|
580
|
+
enc.accessoryToControllerKey = HKDF('sha512', encSalt, enc.sharedSecret, infoRead, 32);
|
|
581
|
+
enc.controllerToAccessoryKey = HKDF('sha512', encSalt, enc.sharedSecret, infoWrite, 32);
|
|
588
582
|
// Our connection is now completely setup. We now want to subscribe this connection to special
|
|
589
583
|
connection.connectionAuthenticated(clientUsername.toString());
|
|
590
584
|
connection._pairVerifyState = undefined;
|
|
@@ -592,11 +586,11 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
592
586
|
handlePairings(connection, url, request, data, response) {
|
|
593
587
|
// Only accept /pairing request if there is a secure session
|
|
594
588
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
595
|
-
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, {
|
|
589
|
+
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
596
590
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
597
591
|
return;
|
|
598
592
|
}
|
|
599
|
-
const objects =
|
|
593
|
+
const objects = decode(data);
|
|
600
594
|
const method = objects[0 /* TLVValues.METHOD */][0]; // value is single byte with request type
|
|
601
595
|
const state = objects[6 /* TLVValues.STATE */][0];
|
|
602
596
|
if (state !== 1 /* PairingStates.M1 */) {
|
|
@@ -606,41 +600,40 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
606
600
|
const identifier = objects[1 /* TLVValues.IDENTIFIER */].toString();
|
|
607
601
|
const publicKey = objects[3 /* TLVValues.PUBLIC_KEY */];
|
|
608
602
|
const permissions = objects[11 /* TLVValues.PERMISSIONS */][0];
|
|
609
|
-
this.emit("add-pairing" /* HAPServerEventTypes.ADD_PAIRING */, connection, identifier, publicKey, permissions,
|
|
603
|
+
this.emit("add-pairing" /* HAPServerEventTypes.ADD_PAIRING */, connection, identifier, publicKey, permissions, once((error) => {
|
|
610
604
|
if (error > 0) {
|
|
611
|
-
debug(
|
|
612
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
613
|
-
response.end(
|
|
605
|
+
debug('[%s] Pairings: failed ADD_PAIRING with code %d', this.accessoryInfo.username, error);
|
|
606
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
607
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, error));
|
|
614
608
|
return;
|
|
615
609
|
}
|
|
616
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
617
|
-
response.end(
|
|
618
|
-
debug(
|
|
610
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
611
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */));
|
|
612
|
+
debug('[%s] Pairings: successfully executed ADD_PAIRING', this.accessoryInfo.username);
|
|
619
613
|
}));
|
|
620
614
|
}
|
|
621
615
|
else if (method === 4 /* PairMethods.REMOVE_PAIRING */) {
|
|
622
616
|
const identifier = objects[1 /* TLVValues.IDENTIFIER */].toString();
|
|
623
|
-
this.emit("remove-pairing" /* HAPServerEventTypes.REMOVE_PAIRING */, connection, identifier,
|
|
617
|
+
this.emit("remove-pairing" /* HAPServerEventTypes.REMOVE_PAIRING */, connection, identifier, once((error) => {
|
|
624
618
|
if (error > 0) {
|
|
625
|
-
debug(
|
|
626
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
627
|
-
response.end(
|
|
619
|
+
debug('[%s] Pairings: failed REMOVE_PAIRING with code %d', this.accessoryInfo.username, error);
|
|
620
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
621
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, error));
|
|
628
622
|
return;
|
|
629
623
|
}
|
|
630
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
631
|
-
response.end(
|
|
632
|
-
debug(
|
|
624
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
625
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */));
|
|
626
|
+
debug('[%s] Pairings: successfully executed REMOVE_PAIRING', this.accessoryInfo.username);
|
|
633
627
|
}));
|
|
634
628
|
}
|
|
635
629
|
else if (method === 5 /* PairMethods.LIST_PAIRINGS */) {
|
|
636
|
-
this.emit("list-pairings" /* HAPServerEventTypes.LIST_PAIRINGS */, connection,
|
|
630
|
+
this.emit("list-pairings" /* HAPServerEventTypes.LIST_PAIRINGS */, connection, once((error, data) => {
|
|
637
631
|
if (error > 0) {
|
|
638
|
-
debug(
|
|
639
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
640
|
-
response.end(
|
|
632
|
+
debug('[%s] Pairings: failed LIST_PAIRINGS with code %d', this.accessoryInfo.username, error);
|
|
633
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': 'application/pairing+tlv8' });
|
|
634
|
+
response.end(encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, 7 /* TLVValues.ERROR_CODE */, error));
|
|
641
635
|
return;
|
|
642
636
|
}
|
|
643
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
644
637
|
const tlvList = [];
|
|
645
638
|
data.forEach((value, index) => {
|
|
646
639
|
if (index > 0) {
|
|
@@ -648,63 +641,63 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
648
641
|
}
|
|
649
642
|
tlvList.push(1 /* TLVValues.IDENTIFIER */, value.username, 3 /* TLVValues.PUBLIC_KEY */, value.publicKey, 11 /* TLVValues.PERMISSIONS */, value.permission);
|
|
650
643
|
});
|
|
651
|
-
const list =
|
|
652
|
-
response.writeHead(200 /* HAPPairingHTTPCode.OK */, {
|
|
644
|
+
const list = encode(6 /* TLVValues.STATE */, 2 /* PairingStates.M2 */, ...tlvList);
|
|
645
|
+
response.writeHead(200 /* HAPPairingHTTPCode.OK */, { 'Content-Type': "application/pairing+tlv8" /* HAPMimeTypes.PAIRING_TLV8 */ });
|
|
653
646
|
response.end(list);
|
|
654
|
-
debug(
|
|
647
|
+
debug('[%s] Pairings: successfully executed LIST_PAIRINGS', this.accessoryInfo.username);
|
|
655
648
|
}));
|
|
656
649
|
}
|
|
657
650
|
}
|
|
658
651
|
handleAccessories(connection, url, request, data, response) {
|
|
659
652
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
660
|
-
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, {
|
|
653
|
+
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
661
654
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
662
655
|
return;
|
|
663
656
|
}
|
|
664
657
|
// call out to listeners to retrieve the latest accessories JSON
|
|
665
|
-
this.emit("accessories" /* HAPServerEventTypes.ACCESSORIES */, connection,
|
|
658
|
+
this.emit("accessories" /* HAPServerEventTypes.ACCESSORIES */, connection, once((error, result) => {
|
|
666
659
|
if (error) {
|
|
667
|
-
response.writeHead(error.httpCode, {
|
|
660
|
+
response.writeHead(error.httpCode, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
668
661
|
response.end(JSON.stringify({ status: error.status }));
|
|
669
662
|
}
|
|
670
663
|
else {
|
|
671
|
-
response.writeHead(200 /* HAPHTTPCode.OK */, {
|
|
664
|
+
response.writeHead(200 /* HAPHTTPCode.OK */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
672
665
|
response.end(JSON.stringify(result));
|
|
673
666
|
}
|
|
674
667
|
}));
|
|
675
668
|
}
|
|
676
669
|
handleCharacteristics(connection, url, request, data, response) {
|
|
677
670
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
678
|
-
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, {
|
|
671
|
+
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
679
672
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
680
673
|
return;
|
|
681
674
|
}
|
|
682
|
-
if (request.method ===
|
|
675
|
+
if (request.method === 'GET') {
|
|
683
676
|
const searchParams = url.searchParams;
|
|
684
|
-
const idParam = searchParams.get(
|
|
677
|
+
const idParam = searchParams.get('id');
|
|
685
678
|
if (!idParam) {
|
|
686
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
679
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
687
680
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
688
681
|
return;
|
|
689
682
|
}
|
|
690
683
|
const ids = [];
|
|
691
|
-
for (const entry of idParam.split(
|
|
692
|
-
const split = entry.split(
|
|
684
|
+
for (const entry of idParam.split(',')) { // ["1.9","2.14"]
|
|
685
|
+
const split = entry.split('.'); // ["1","9"]
|
|
693
686
|
ids.push({
|
|
694
|
-
aid: parseInt(split[0], 10), // accessory id
|
|
695
|
-
iid: parseInt(split[1], 10), // (characteristic) instance id
|
|
687
|
+
aid: Number.parseInt(split[0], 10), // accessory id
|
|
688
|
+
iid: Number.parseInt(split[1], 10), // (characteristic) instance id
|
|
696
689
|
});
|
|
697
690
|
}
|
|
698
691
|
const readRequest = {
|
|
699
|
-
ids
|
|
700
|
-
includeMeta:
|
|
701
|
-
includePerms:
|
|
702
|
-
includeType:
|
|
703
|
-
includeEvent:
|
|
692
|
+
ids,
|
|
693
|
+
includeMeta: consideredTrue(searchParams.get('meta')),
|
|
694
|
+
includePerms: consideredTrue(searchParams.get('perms')),
|
|
695
|
+
includeType: consideredTrue(searchParams.get('type')),
|
|
696
|
+
includeEvent: consideredTrue(searchParams.get('ev')),
|
|
704
697
|
};
|
|
705
|
-
this.emit("get-characteristics" /* HAPServerEventTypes.GET_CHARACTERISTICS */, connection, readRequest,
|
|
698
|
+
this.emit("get-characteristics" /* HAPServerEventTypes.GET_CHARACTERISTICS */, connection, readRequest, once((error, readResponse) => {
|
|
706
699
|
if (error) {
|
|
707
|
-
response.writeHead(error.httpCode, {
|
|
700
|
+
response.writeHead(error.httpCode, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
708
701
|
response.end(JSON.stringify({ status: error.status }));
|
|
709
702
|
return;
|
|
710
703
|
}
|
|
@@ -724,27 +717,27 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
724
717
|
}
|
|
725
718
|
}
|
|
726
719
|
// 207 "multi-status" is returned when an error occurs reading a characteristic. otherwise 200 is returned
|
|
727
|
-
response.writeHead(errorOccurred ? 207 /* HAPHTTPCode.MULTI_STATUS */ : 200 /* HAPHTTPCode.OK */, {
|
|
728
|
-
response.end(JSON.stringify({ characteristics
|
|
720
|
+
response.writeHead(errorOccurred ? 207 /* HAPHTTPCode.MULTI_STATUS */ : 200 /* HAPHTTPCode.OK */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
721
|
+
response.end(JSON.stringify({ characteristics }));
|
|
729
722
|
}));
|
|
730
723
|
}
|
|
731
|
-
else if (request.method ===
|
|
724
|
+
else if (request.method === 'PUT') {
|
|
732
725
|
if (!connection.isAuthenticated()) {
|
|
733
726
|
if (!request.headers || (request.headers && request.headers.authorization !== this.accessoryInfo.pincode)) {
|
|
734
|
-
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, {
|
|
727
|
+
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
735
728
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
736
729
|
return;
|
|
737
730
|
}
|
|
738
731
|
}
|
|
739
732
|
if (data.length === 0) {
|
|
740
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
733
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
741
734
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
742
735
|
return;
|
|
743
736
|
}
|
|
744
|
-
const writeRequest = JSON.parse(data.toString(
|
|
745
|
-
this.emit("set-characteristics" /* HAPServerEventTypes.SET_CHARACTERISTICS */, connection, writeRequest,
|
|
737
|
+
const writeRequest = JSON.parse(data.toString('utf8'));
|
|
738
|
+
this.emit("set-characteristics" /* HAPServerEventTypes.SET_CHARACTERISTICS */, connection, writeRequest, once((error, writeResponse) => {
|
|
746
739
|
if (error) {
|
|
747
|
-
response.writeHead(error.httpCode, {
|
|
740
|
+
response.writeHead(error.httpCode, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
748
741
|
response.end(JSON.stringify({ status: error.status }));
|
|
749
742
|
return;
|
|
750
743
|
}
|
|
@@ -759,8 +752,8 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
759
752
|
}
|
|
760
753
|
if (multiStatus) {
|
|
761
754
|
// 207 is "multi-status" since HomeKit may be setting multiple things and any one can fail independently
|
|
762
|
-
response.writeHead(207 /* HAPHTTPCode.MULTI_STATUS */, {
|
|
763
|
-
response.end(JSON.stringify({ characteristics
|
|
755
|
+
response.writeHead(207 /* HAPHTTPCode.MULTI_STATUS */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
756
|
+
response.end(JSON.stringify({ characteristics }));
|
|
764
757
|
}
|
|
765
758
|
else {
|
|
766
759
|
// if everything went fine send 204 no content response
|
|
@@ -770,80 +763,78 @@ class HAPServer extends events_1.EventEmitter {
|
|
|
770
763
|
}));
|
|
771
764
|
}
|
|
772
765
|
else {
|
|
773
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
766
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ }); // method not allowed
|
|
774
767
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
775
768
|
}
|
|
776
769
|
}
|
|
777
770
|
handlePrepareWrite(connection, url, request, data, response) {
|
|
778
771
|
if (!this.allowInsecureRequest && !connection.isAuthenticated()) {
|
|
779
|
-
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, {
|
|
772
|
+
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
780
773
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
781
774
|
return;
|
|
782
775
|
}
|
|
783
|
-
if (request.method ===
|
|
776
|
+
if (request.method === 'PUT') {
|
|
784
777
|
if (data.length === 0) {
|
|
785
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
778
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
786
779
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
787
780
|
return;
|
|
788
781
|
}
|
|
789
782
|
const prepareRequest = JSON.parse(data.toString());
|
|
790
783
|
if (prepareRequest.pid && prepareRequest.ttl) {
|
|
791
|
-
debug(
|
|
784
|
+
debug('[%s] Received prepare write request with pid %d and ttl %d', this.accessoryInfo.username, prepareRequest.pid, prepareRequest.ttl);
|
|
792
785
|
if (connection.timedWriteTimeout) { // clear any currently existing timeouts
|
|
793
786
|
clearTimeout(connection.timedWriteTimeout);
|
|
794
787
|
}
|
|
795
788
|
connection.timedWritePid = prepareRequest.pid;
|
|
796
789
|
connection.timedWriteTimeout = setTimeout(() => {
|
|
797
|
-
debug(
|
|
790
|
+
debug('[%s] Timed write request timed out for pid %d', this.accessoryInfo.username, prepareRequest.pid);
|
|
798
791
|
connection.timedWritePid = undefined;
|
|
799
792
|
connection.timedWriteTimeout = undefined;
|
|
800
793
|
}, prepareRequest.ttl);
|
|
801
|
-
response.writeHead(200 /* HAPHTTPCode.OK */, {
|
|
794
|
+
response.writeHead(200 /* HAPHTTPCode.OK */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
802
795
|
response.end(JSON.stringify({ status: 0 /* HAPStatus.SUCCESS */ }));
|
|
803
|
-
return;
|
|
804
796
|
}
|
|
805
797
|
else {
|
|
806
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
798
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
807
799
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
808
800
|
}
|
|
809
801
|
}
|
|
810
802
|
else {
|
|
811
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
803
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
812
804
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
813
805
|
}
|
|
814
806
|
}
|
|
815
807
|
handleResource(connection, url, request, data, response) {
|
|
816
808
|
if (!connection.isAuthenticated()) {
|
|
817
809
|
if (!(this.allowInsecureRequest && request.headers && request.headers.authorization === this.accessoryInfo.pincode)) {
|
|
818
|
-
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, {
|
|
810
|
+
response.writeHead(470 /* HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
819
811
|
response.end(JSON.stringify({ status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ }));
|
|
820
812
|
return;
|
|
821
813
|
}
|
|
822
814
|
}
|
|
823
|
-
if (request.method ===
|
|
815
|
+
if (request.method === 'POST') {
|
|
824
816
|
if (data.length === 0) {
|
|
825
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
817
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
826
818
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
827
819
|
return;
|
|
828
820
|
}
|
|
829
821
|
const resourceRequest = JSON.parse(data.toString());
|
|
830
822
|
// call out to listeners to retrieve the resource, snapshot only right now
|
|
831
|
-
this.emit("request-resource" /* HAPServerEventTypes.REQUEST_RESOURCE */, resourceRequest,
|
|
823
|
+
this.emit("request-resource" /* HAPServerEventTypes.REQUEST_RESOURCE */, resourceRequest, once((error, resource) => {
|
|
832
824
|
if (error) {
|
|
833
|
-
response.writeHead(error.httpCode, {
|
|
825
|
+
response.writeHead(error.httpCode, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ });
|
|
834
826
|
response.end(JSON.stringify({ status: error.status }));
|
|
835
827
|
}
|
|
836
828
|
else {
|
|
837
|
-
response.writeHead(200 /* HAPHTTPCode.OK */, {
|
|
829
|
+
response.writeHead(200 /* HAPHTTPCode.OK */, { 'Content-Type': "image/jpeg" /* HAPMimeTypes.IMAGE_JPEG */ });
|
|
838
830
|
response.end(resource);
|
|
839
831
|
}
|
|
840
832
|
}));
|
|
841
833
|
}
|
|
842
834
|
else {
|
|
843
|
-
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, {
|
|
835
|
+
response.writeHead(400 /* HAPHTTPCode.BAD_REQUEST */, { 'Content-Type': "application/hap+json" /* HAPMimeTypes.HAP_JSON */ }); // method not allowed
|
|
844
836
|
response.end(JSON.stringify({ status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ }));
|
|
845
837
|
}
|
|
846
838
|
}
|
|
847
839
|
}
|
|
848
|
-
exports.HAPServer = HAPServer;
|
|
849
840
|
//# sourceMappingURL=HAPServer.js.map
|