etwmc / Personal-HomeKit-HAP

This project will provide source code to build a HomeKit support accessories.
MIT License
236 stars 85 forks source link

Randomly control/notify failed (could not authenticate the message) #62

Closed benwwchen closed 7 years ago

benwwchen commented 8 years ago

Update 2: After removing all the libmosquitto related code, it still logs Stop Connect and at the same time authentication error on the phone appears. Still cannot find real causes.

Update: It seems that the issue was caused by my mistaken use of libmosquitto instead of Personal-HomeKit-HAP.

I cross-compile it for my openwrt(mipsel, little-endian) router with bswap header and declaration commented out(or it'll give compilation errors), as well as change the line in PHKNetworkIP.cpp to return !e.c[0]; according to #53 .

It seems to work fine. No issues with pairing. However, when I control it (with the Elgato Eve app), it will fail to renew some characteristics every several times on the phone with this authentication error, but the setValue() callback function is called: img_7970

This happens more frequently on the power bool characteristic than other ones, though it can update the value only on the server side correctly most of the times.

Here is the log of me turning on and off several times with some of them failed: randomerrorlog.txt

(Every time authentication error occurs the log will log:

Return len 0 for socket X
Message:
Stop Connect: X

where X is around 10)

benwwchen commented 7 years ago

Here is my initAccessorySet():

void initAccessorySet() {
    currentDeviceType = deviceType_bridge;

    printf("Initial Accessory\n");
    accSet = &AccessorySet::getInstance();

    // add light accessory
    Accessory *lightAcc = new Accessory();
    addInfoServiceToAccessory(lightAcc, "WS2812B", "Ben", "LED Strip", "12345678", &lightIdentify);
    accSet->addAccessory(lightAcc);

    Service *lightService = new Service(serviceType_lightBulb);
    lightAcc->addService(lightService);

    stringCharacteristics *lightServiceName = new stringCharacteristics(charType_serviceName, premission_read, 0);
    lightServiceName->setValue("Light");
    lightAcc->addCharacteristics(lightService, lightServiceName);

    boolCharacteristics *powerState = new boolCharacteristics(charType_on, premission_read|premission_write|premission_notify);
    powerState->setValue("false");
    powerState->valueChangeFunctionCall = &changeLightPower;
    lightAcc->addCharacteristics(lightService, powerState);

    intCharacteristics *brightnessState = new intCharacteristics(charType_brightness, premission_read|premission_write|premission_notify, 0, 100, 1, unit_percentage);
    brightnessState->setValue("50");
    brightnessState->valueChangeFunctionCall = &changeLightIntensity;
    lightAcc->addCharacteristics(lightService, brightnessState);

    intCharacteristics *hueState = new intCharacteristics(charType_hue, premission_read|premission_write|premission_notify, 0, 360, 1, unit_arcDegree);
    hueState->setValue("240");
    hueState->valueChangeFunctionCall = &changeLightHue;
    lightAcc->addCharacteristics(lightService, hueState);

    intCharacteristics *saturationState = new intCharacteristics(charType_saturation, premission_read|premission_write|premission_notify, 0, 100, 1, unit_percentage);
    saturationState->setValue("0");
    saturationState->valueChangeFunctionCall = &changeLightSaturation;
    lightAcc->addCharacteristics(lightService, saturationState);

    // add temperature & humidity sensor accessory
    Accessory *sensorAcc = new Accessory();
    addInfoServiceToAccessory(sensorAcc, "DHT11", "Ben", "Temperature and Humidity Sensor", "12345678", &sensorIdentify);
    accSet->addAccessory(sensorAcc);

    // add services
    Service *temperatureSensorService = new Service(serviceType_temperatureSensor);
    sensorAcc->addService(temperatureSensorService);
    Service *humiditySensorService = new Service(serviceType_humiditySensor);
    sensorAcc->addService(humiditySensorService);

    stringCharacteristics *temperatureSensorServiceName = new stringCharacteristics(charType_serviceName, premission_read, 0);
    temperatureSensorServiceName->setValue("Temperature Sensor");
    sensorAcc->addCharacteristics(temperatureSensorService, temperatureSensorServiceName);
    stringCharacteristics *humiditySensorServiceName = new stringCharacteristics(charType_serviceName, premission_read, 0);
    humiditySensorServiceName->setValue("Humidity Sensor");
    sensorAcc->addCharacteristics(humiditySensorService, humiditySensorServiceName);

    // add characteristics
    floatCharacteristics *temperature = new floatCharacteristics(charType_currentTemperature, premission_read|premission_notify, 0, 100, 0.1, unit_celsius);
    temperature->setValue("0");
    sensorAcc->addCharacteristics(temperatureSensorService, temperature);

    floatCharacteristics *humidity = new floatCharacteristics(charType_currentHumidity, premission_read|premission_notify, 0, 100, 1, unit_percentage);
    humidity->setValue("0");
    sensorAcc->addCharacteristics(humiditySensorService, humidity);

    // pass the temperature and humidity characteristics to the handler
    passSensorCharacteristics(temperature, humidity);

};

Update: I also found that the floatCharacteristics(the temperature and the humidity above) can't be set to any value other than 0, or it'll log Stop Connect and could not pair or be offline one the phone if it's set to other value after pairing when the value is 0.

I changed them to intCharacteristics, then pairing can be done and the value updates successfully. But I still get Stop Connect logs along with authentication errors frequently.

benwwchen commented 7 years ago

After updating to iOS 10, the Home app shows "No Response" on all accessories. Tapping on the sensors will show the value but the light can't be controlled from the Home app and can only controlled through third party apps. The authentication problem remains. img_8098 img_8099

ghost commented 7 years ago

I think I got the same problem here

etwmc commented 7 years ago

Have you guys tried the new push, it was suppose to solve it

ghost commented 7 years ago

Hi

Which push do you mention, the last commit I have here is :

and I can see the problem.

It seems the socket connection close suddenly but I am not sure at 100%

Cheers Marco

etwmc commented 7 years ago

Can you check the new version I just push? I just realize two lines of the code got rollback before the commit in Sep.

ghost commented 7 years ago

Thanks a lot! I will test it and come back to you

benwwchen commented 7 years ago

@etwmc Thanks for helping.

I just tried the latest version without adding my own accessories in the code.

It still cannot work correctly: It's initially on with 50% brightness, then I turn it off, which works. Then I turn it on and it shows "No Response" and cannot be turn on.

Here is the log: log.txt

And I also tried changing the brightness, it'll succeed the first time, then it'll be off with "no response" the second time I change it. Then it'll succeed the third time, and be off with "no response" the fourth time, etc.

I tried it both on my Mac and the mipsel OpenWrt router with the Home app on iOS 10.1, iPhone 6. The results were the same on both.

nick-dijkshoorn commented 7 years ago

I also had issues with floatCharacteristics. A value < 10 was correctly formatted in the message. For example "0.5". For a value >= 10, the value was "10.". The decimal was omitted, which was causing issues.

I force 1 decimal by replacing a line in PHKAccessory.cpp

snprintf(tempStr, 4, "%f", value); with snprintf(tempStr, 5, "%.1f", value);

beckmx commented 7 years ago

Hello @nick-dijkshoorn I tried adding the changes but it seems it stops randomly when requesting information, it does sync correctly but it looks it doesn't respond on time.

I tested on OpenWRT

etwmc commented 7 years ago

Try to uncomment line 199 in PHKNetworkIP.cpp, I played with it for a minute , and I don't see any complaint. There's a period of HAP behave weird (iOS 8), and I mostly use beta on personal devices, and I have to stop the advance on the sender counter to stop the notification, since I have no access to MFi, I assume it's a engineering decision they made. I guess it's a bug and they fixed it alone the way. (Sorry for replying so late. I'm focusing on job hunting so I won't got deported by April Fool's Day 2017. )

beckmx commented 7 years ago

Hello @etwmc I try uncommenting line 199, it seems to be wait a little longer to respond but at the end it looks it stops the syncing process and stops responding, what app did you use to see the traffic between the emulator and the phone? do you recommend one? and btw I believe this new behavior was introduced in iOS10, before that I remember it was working just fine

etwmc commented 7 years ago
  1. Can you get the device log on the process homed around the time on sync? I use Wireshark, but after the pairing handshake basically everything is encrypted, so maybe read the log in the program?
  2. What platform are you using, espically the endian?
  3. Have you try unpaid and pair again? I did a fresh pairing at the time, it might not affect anything but it doesn't cause any harm to try.
  4. The problem is before iOS 10, at the time no one use notify so no one notice, but Apple heavily use of multi query fetch and push notification in the Home app, so the problem become trivial.
beckmx commented 7 years ago

Hello @etwmc , I followed your instructions, I found a detail, when I compile your code against my macOS Sierra the home app correctly syncs and adds the device, I see only a little problem with one of the characteristics (the brightness characteristic), but still it receives information from the iOS "Home app".

When I use a big-endian processor I notice the syncing process also tries to sync but exactly when it tries to perform a PUT action it says it couldn't decrypt the data, here is my log:

Not Big endian (macos sierra): Path: /accessories Ask for accessories info Reply: HTTP/1.1 200 OK Content-Type: application/hap+json Content-Length: 743

{"accessories":[]} Length: 51 send: 1 send: 1 send: 2 Length: 205 Request: PUT /characteristics HTTP/1.1 Host: House\032Light._hap._tcp.local Content-Length: 78 Content-Type: application/hap+json

{"characteristics":[....]} PacketLen: 223 , MessageLen: 205 Verify successfully!

Big-endian (small processor): Path: /accessories Ask for accessories info Reply: HTTP/1.1 200 OK Content-Type: application/hap+json Content-Length: 743

{"accessories":[]} Length: 51 send: 1 send: 1 send: 2 Length: 205 Request: ?�q\ʲA,�$�S�f����U����j���Ze�m�=���(�*�d������������U;��nr���n����cكآ� ��U}�Z�}�q��C��?��c�N���UӠn�{���+�>�rȔ�� �%�wa�Qm��\}��V ƛ��^�3T������g�b�b�F����-n�m#�[�c PacketLen: 223 , MessageLen: 200 Passed-in data is no-verified!

etwmc commented 7 years ago

Try to put ! on the return function at line 94 in PHKNetworkIP.cpp for the big endian I start to think I don't need the endian check anymore, so probably would just reply a constant in the next push

beckmx commented 7 years ago

I made this modification:

if (is_big_endian()){ return !_socket; } return _socket;

At the end of the setupSocketV4 but it seems it doen't recognize the bonjour service now in big endian of course

2016-11-17 15:21 GMT-06:00 etwmc notifications@github.com:

Try to put ! on the return function at line 94 in PHKNetworkIP.cpp for the big endian I start to think I don't need the endian check anymore, so probably would just reply a constant in the next push

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/etwmc/Personal-HomeKit-HAP/issues/62#issuecomment-261373098, or mute the thread https://github.com/notifications/unsubscribe-auth/AAb3GcdkSlcjDi8_sHL8h_jzxqI91LZjks5q_MVVgaJpZM4J0w3V .

beckmx commented 7 years ago

@etwmc @benwwchen I believe I found the problem with this issue, on line 502 of the PHKAccessory.cpp file you can safely add:

statusCode=200;

because the next while never gets fired due to a previous 400 statusCode, and with that, the code runs smoothly and gets detected without any errors, my concern now is to make it run on big-endian.

Greetings

etwmc commented 7 years ago

Actually, what I mean is this int is_big_endian(void) { union { uint32_t i; char c[4]; } e = { 0x01000000 };

printf("Endian: %d \n", (int)e.c[0]);

return !e.c[0];

} Maybe the line number is different

beckmx commented 7 years ago

Hello @etwmc I believe I fixed one of the issues related to the big endian, some systems do have the byteswap.h header, and some do not, so this header help us (I believe you tried to guess which systems correctly implement it) to avoid worrying about the big endian stuff, I found that if you replace this part:

//#include <byteswap.h>
static inline unsigned short bswap_16(unsigned short x) {
return (x>>8) | (x<<8);
}

static inline unsigned int bswap_32(unsigned int x) {
    return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16));
}
static inline unsigned long long bswap_64(unsigned long long x) {
    return x;//(((unsigned long long)bswap_32(x&0xffffffffull))<<32) |
//    (bswap_32(x>>32));
}

For this section:

#ifdef bswap_64
#include <byteswap.h>
#else
static inline unsigned long long bswap_64(unsigned long long x) {
    return x;
}
#endif

The compiler will correctly guess what swap_64() function to use, another replacement is to replace this part in everywhere in the same file: if (!is_big_endian()) numberOfMsgSend = bswap_64(numberOfMsgSend);

For this one:

numberOfMsgSend = bswap_64(numberOfMsgSend);

Because the compiler is using the correct method for each system.

However, I found a little problem, although now everything runs Ok on macOS (sierra btw), I am having a little bit of trouble sending this response with smaller linux systems:

Reply: HTTP/1.1 204 OK
Content-Type: application/hap+json
Content-Length: 0

Somehow, the socket thinks there is nothing to send and kills the connection :(, I will keep fixing these little things, but if you have any idea on this new issue, it would be great if you could help me

etwmc commented 7 years ago

Are you sure the connection is not just failed the endian all together? The problem doesn't show till the first message, which at that point is usually setting notify property (well, in the Home app. In other app other message would also trigger die->not the message itself)

beckmx commented 7 years ago

Well, I know for sure the problem with decrypting the message in big endian was solved, because it shows the message correctly in plain text, the only problem now is with all the 200 OK messages, they get built correctly but they don't send, I noticed you put a little message in the PHKNetworkIP.cpp which says "fix me" related to the length of the buffer, what I have seen is that it responds correctly to all other requests except the 200 OK.

On Sat, Nov 19, 2016 at 03:52 etwmc notifications@github.com wrote:

Are you sure the connection is not just failed the endian all together? The problem doesn't show till the first message, which at that point is usually setting notify property (well, in the Home app. In other app other message would also trigger die->not the message itself)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/etwmc/Personal-HomeKit-HAP/issues/62#issuecomment-261704336, or mute the thread https://github.com/notifications/unsubscribe-auth/AAb3GbTw8lxULu0V_9pjSr52FQf07K_xks5q_sb2gaJpZM4J0w3V .

etwmc commented 7 years ago

The fix me comment is just directly copied from the example code form the poly library. I did not actually write it, I think. Is the 204 come from push notification, or respond of a request? Because both of them would at least give you 18 bytes (2 for the msg Len header, 16 from the hash of the message), and the socket should send the 18 bytes even if the respond is null (like no HTTP header and such) So at worst case scenario you would got drop by iOS for improper encryption (corrupted message), not TTL expired. Can you just send me your PHKNetworkIP.cpp? Because if my AR71xx OpenWRT doesn't got any endian modifier (like a flag in CPU) in it, I think big endian is already working without any change beside reversing the endian detector.

beckmx commented 7 years ago

Yeah sure, give 4 hours, I am stuck in a wedding

On Sat, Nov 19, 2016 at 18:46 etwmc notifications@github.com wrote:

The fix me comment is just directly copied from the example code form the poly library. I did not actually write it, I think. Is the 204 come from push notification, or respond of a request? Because both of them would at least give you 18 bytes (2 for the msg Len header, 16 from the hash of the message), and the socket should send the 18 bytes even if the respond is null (like no HTTP header and such) So at worst case scenario you would got drop by iOS for improper encryption (corrupted message). Can you just send me your PHKNetworkIP.cpp? Because if my AR71xx OpenWRT doesn't got any endian modifier (like a flag in CPU) in it, I think big endian is already working without endian change.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/etwmc/Personal-HomeKit-HAP/issues/62#issuecomment-261750217, or mute the thread https://github.com/notifications/unsubscribe-auth/AAb3GRj613EUU6R0eb-YCLxex0RtJVYHks5q_5hWgaJpZM4J0w3V .

beckmx commented 7 years ago

@etwmc here is the file, I was adding some additional lines to "hard-detect" 200 OK responses: (just comment the okMessage stuff)

//
//  PHKNetworkIP.cpp
//  Workbench
//
//  Created by Wai Man Chan on 4/8/14.
//
//

#include "PHKNetworkIP.h"
#define PHKNetworkServiceType "_hap._tcp"

#include <iostream>
#include <fstream>
#include <errno.h>
#include <strings.h>

#ifdef bswap_64
#include <byteswap.h>
#else
static inline unsigned long long bswap_64(unsigned long long x) {
    return x;
}
#endif

//Security
extern "C" {
    //ChaCha20-Poly1035
#include "poly1305-opt-master/poly1305.h"
#include "Chacha20/chacha20_simple.h"
    //HKDF-SHA512
#include "rfc6234-master/hkdf.h"
    //ED25519
#include "ed25519-donna/ed25519.h"
    //Curve25519
#include "curve25519/curve25519-donna.h"
    //SRP
#include "srp/srp.h"
}

//Store controller public key
#include "PHKControllerRecord.h"

//Handle Data
#include "PHKAccessory.h"

#include "Configuration.h"

#if MCU
#else
#include <pthread.h>
#endif

using namespace std;

#define portNumber 0

#if MCU
#else
connectionInfo connection[numberOfClient];
#endif

const unsigned char modulusStr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

const unsigned char curveBasePoint[] = { 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

const unsigned char generator[] = {0x05};
char tempStr[3073];

const unsigned char accessorySecretKey[32] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74};

int _socket_v4, _socket_v6;
DNSServiceRef netServiceV4, netServiceV6;

deviceType currentDeviceType = deviceType_other;

int currentConfigurationNum = 1;

int is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } e = { 0x01000000 };

    return e.c[0];
}

//Network Setup
int setupSocketV4(unsigned int maximumConnection) {
    int _socket = socket(PF_INET, SOCK_STREAM, 0);
    sockaddr_in addr;   bzero(&addr, sizeof(addr));
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   addr.sin_family = PF_INET;    addr.sin_port = htons(portNumber);

    int optval = 1; socklen_t optlen = sizeof(optval);
    setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);

    bind(_socket, (const struct sockaddr *)&addr, sizeof(addr));
    listen(_socket, maximumConnection);
    return _socket;
}

int setupSocketV6(unsigned int maximumConnection) {
    int _socket = socket(PF_INET6, SOCK_STREAM, 0);
    sockaddr_in6 addr;   bzero(&addr, sizeof(addr));
    addr.sin6_addr = in6addr_any;   addr.sin6_family = PF_INET6;    addr.sin6_port = htons(portNumber);
    bind(_socket, (const struct sockaddr *)&addr, sizeof(addr));
    listen(_socket, maximumConnection);
    return _socket;
}

unsigned short getSocketPortNumberV4(int _socket) {
    sockaddr_in addr; socklen_t len = sizeof(addr);
    getsockname(_socket, (struct sockaddr *)&addr, &len);
    return ntohs(addr.sin_port);
}

unsigned short getSocketPortNumberV6(int _socket) {
    sockaddr_in6 addr; socklen_t len = sizeof(addr);
    getsockname(_socket, (struct sockaddr *)&addr, &len);
    return ntohs(addr.sin6_port);
}

void registerFail(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context ) {
#if HomeKitLog == 1
    printf("Service can't register\n");
#endif
    exit(0);
}

TXTRecordRef buildTXTRecord() {
    TXTRecordRef txtRecord;
    TXTRecordCreate(&txtRecord, 0, NULL);
    TXTRecordSetValue(&txtRecord, "pv", 3, "1.0");  //Version
    TXTRecordSetValue(&txtRecord, "id", 17, deviceIdentity);    //Device id
    char buf[3];
    sprintf(buf, "%d", currentConfigurationNum);
    TXTRecordSetValue(&txtRecord, "c#", 1, buf);    //Configuration Number
    TXTRecordSetValue(&txtRecord, "s#", 1, "4");    //Number of service
    if (hasController()) buf[0] = '0';
    else buf[0] = '1';
    TXTRecordSetValue(&txtRecord, "sf", 1, buf);    //Discoverable: 0 if has been paired
    TXTRecordSetValue(&txtRecord, "ff", 1, "0");    //1 for MFI product
    TXTRecordSetValue(&txtRecord, "md", strlen(deviceName), deviceName);    //Model Name
    int len = sprintf(buf, "%d", currentDeviceType);
    TXTRecordSetValue(&txtRecord, "ci", len, buf);    //1 for MFI product
    return txtRecord;
}

void updateConfiguration() {
    currentConfigurationNum++;
    TXTRecordRef txtRecord = buildTXTRecord();
    DNSServiceUpdateRecord(netServiceV4, NULL, 0, TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), 0);
    TXTRecordDeallocate(&txtRecord);
}

void PHKNetworkIP::setupSocket() {
    TXTRecordRef txtRecord = buildTXTRecord();
    _socket_v4 = setupSocketV4(5);
    DNSServiceRegister(&netServiceV4, 0, 0, deviceName, PHKNetworkServiceType, "", NULL, htons(getSocketPortNumberV4(_socket_v4)), TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), NULL, NULL);
    TXTRecordDeallocate(&txtRecord);
}

PHKNetworkIP::PHKNetworkIP() {
    SRP_initialize_library();
    srand((unsigned int)time(NULL));
    for (int i = 0; i < numberOfClient; i++) {
        connection[i].subSocket = -1;
    }
    setupSocket();
}

//Connection Logic, not finished
void broadcastMessage(void *sender, char *resultData, size_t resultLen) {

#if HomeKitLog == 1
    printf("Broadcast with sender\n");
#endif

    for (int i = 0; i < numberOfClient; i++) {
        int socketNumber = connection[i].subSocket;
        if (socketNumber >= 0 && connection[i].notify(sender)) {

            pthread_mutex_lock(&connection[i].mutex);

            chacha20_ctx chacha20;    bzero(&chacha20, sizeof(chacha20));

            char temp[64];  bzero(temp, 64); char temp2[64];  bzero(temp2, 64);

            char *reply = new char[resultLen+18];
            reply[0] = resultLen%256;
            reply[1] = (resultLen-(uint8_t)reply[0])/256;

            chacha20_setup(&chacha20, (const uint8_t *)connection[i].accessoryToControllerKey, 32, (uint8_t *)&connection[i].numberOfMsgSend);
            connection[i].numberOfMsgSend++;
            chacha20_encrypt(&chacha20, (const uint8_t*)temp, (uint8_t *)temp2, 64);
            chacha20_encrypt(&chacha20, (const uint8_t*)resultData, (uint8_t*)&reply[2], resultLen);

            //XXX FIXME
#if 1
            poly1305_context verifyContext; bzero(&verifyContext, sizeof(verifyContext));
            poly1305_init(&verifyContext, (const unsigned char*)temp2);
            {
                char waste[16];
                bzero(waste, 16);

                poly1305_update(&verifyContext, (const unsigned char *)reply, 2);
                poly1305_update(&verifyContext, (const unsigned char *)waste, 14);

                poly1305_update(&verifyContext, (const unsigned char *)&reply[2], resultLen);
                poly1305_update(&verifyContext, (const unsigned char *)waste, 16-resultLen%16);
                unsigned long long _len;
                _len = 2;
                poly1305_update(&verifyContext, (const unsigned char *)&_len, 8);
                _len = resultLen;
                poly1305_update(&verifyContext, (const unsigned char *)&_len, 8);
            }
            poly1305_finish(&verifyContext, (unsigned char*)&reply[resultLen+2]);
#else
            char verify[16];
            memset(verify, 0, 16);
            Poly1305_GenKey((const unsigned char *)temp2, (uint8_t *)reply, resultLen, Type_Data_With_Length, verify);
            memcpy((unsigned char*)&reply[resultLen+2], verify, 16);
#endif

            write(socketNumber, reply, resultLen+18);
            delete [] reply;
            pthread_mutex_unlock(&connection[i].mutex);
        }
    }
}
void *connectionLoop(void *threadInfo) {
    connectionInfo *info = (connectionInfo *)threadInfo;
    int subSocket = info->subSocket;    ssize_t len;
    if (subSocket >= 0) {
#if HomeKitLog == 1
        printf("Start Connect: %d\n", subSocket);
#endif

        do {
            len = read(subSocket, info->buffer, 4096);
#if HomeKitLog == 1
            printf("Return len %d for socket %d\n", len, subSocket);
#endif

#if HomeKitReplyHeaderLog == 1
            printf("Message: %s\n", info->buffer);
#endif

            PHKNetworkMessage msg(info->buffer);
            if (len > 0) {
                if (!strcmp(msg.directory, "pair-setup")){

                    /*
                     * The process of pair-setup
                     */

                    info->handlePairSeup();

                    updateConfiguration();
                }
                else if (!strcmp(msg.directory, "pair-verify")){
                    info->handlePairVerify();
                    //When pair-verify done, we handle Accessory Request
                    info->handleAccessoryRequest();
                }
                else if (!strcmp(msg.directory, "identify")){
                    close(subSocket);
                }
            }

        } while (len > 0);

        close(subSocket);
#if HomeKitLog == 1
        printf("Stop Connect: %d\n", subSocket);
#endif

        info->subSocket = -1;

    }
    return NULL;
}

void PHKNetworkIP::handleConnection() const {
    int subSocket = accept(_socket_v4, 0, NULL);

    int index = -1;
    for (int i = 0; i < numberOfClient; i++) {
        if (connection[i].subSocket == -1) {
            index = i;
            connection[index].subSocket = subSocket;

            pthread_create(&connection[index].thread, NULL, connectionLoop, &connection[index]);
            break;
        }
    }

    if (index < 0) close(subSocket);

}

// 2ByteS(datalen) + data_buf + 16Bytes(Verfied Key)
// Type_Data_Without_Length
//Passed-in buf is OMIT len and 16Bytes verfied key
// Type_Data_With_Length
//Passed-in buf is len and data_buf
void connectionInfo::Poly1305_GenKey(const unsigned char * key, uint8_t * buf, uint16_t len, Poly1305Type_t type, char* verify)
{
    printf("Length: %d\n", buf[0]);
    if (key == NULL || buf == NULL || len < 2 || verify == NULL)
        return;

    poly1305_context verifyContext; bzero(&verifyContext, sizeof(verifyContext));
    poly1305_init(&verifyContext, key);

    char waste[16];
    bzero(waste, 16);

    if (type == Type_Data_With_Length) {
        poly1305_update(&verifyContext, (const unsigned char *)&buf[0], 1);
        poly1305_update(&verifyContext, (const unsigned char *)&buf[1], 1);
        poly1305_update(&verifyContext, (const unsigned char *)waste, 14);

        poly1305_update(&verifyContext, (const unsigned char *)&buf[2], len);
    }
    else {
        poly1305_update(&verifyContext, (const unsigned char *)buf, len);
    }

    if (len%16 > 0)
        poly1305_update(&verifyContext, (const unsigned char *)waste, 16-(len%16));
    unsigned char _len;
    if (type == Type_Data_With_Length) {
        _len = 2;
    }
    else {
        _len = 0;
    }

    poly1305_update(&verifyContext, (const unsigned char *)&_len, 1);
    poly1305_update(&verifyContext, (const unsigned char *)&waste, 7);
    _len = len;

    poly1305_update(&verifyContext, (const unsigned char *)&_len, 1);
    _len = len/256;
    poly1305_update(&verifyContext, (const unsigned char *)&_len, 1);

    poly1305_update(&verifyContext, (const unsigned char *)&waste, 6);

    poly1305_finish(&verifyContext, (unsigned char*)verify);
}

void connectionInfo::handlePairSeup() {
    PHKNetworkMessageDataRecord stateRecord;
    stateRecord.activate = true;
    stateRecord.data = new char[1];
    stateRecord.length = 1;
    stateRecord.index = 6;
    PairSetupState_t state = State_M1_SRPStartRequest;
    SRP *srp;
    srp = SRP_new(SRP6a_server_method());
    cstr *secretKey = NULL, *publicKey = NULL, *response = NULL;
    char sessionKey[64];
    char *responseBuffer = 0; int responseLen = 0;

    do {
        PHKNetworkMessage msg = PHKNetworkMessage(buffer);
        PHKNetworkResponse mResponse(200);

        state = (PairSetupState_t)(*msg.data.dataPtrForIndex(6));

        *stateRecord.data = (char)state+1;
        switch (state) {
            case State_M1_SRPStartRequest: {
#if HomeKitLog == 1
                printf("%s, %d: State_M1_SRPStartRequest\n", __func__, __LINE__);
#endif
                PHKNetworkMessageDataRecord saltRec;
                PHKNetworkMessageDataRecord publicKeyRec;
                unsigned char saltChar[16];

                for (int i = 0; i < 16; i++) {
                    saltChar[i] = rand();
                }

                SRP_RESULT result = SRP_set_username(srp, "Pair-Setup");
                int modulusSize = sizeof(modulusStr) / sizeof(modulusStr[0]);
                result = SRP_set_params(srp, (const unsigned char *)modulusStr, modulusSize, (const unsigned char *)generator, 1, saltChar, 16);
                result = SRP_set_auth_password(srp, devicePassword);
                result = SRP_gen_pub(srp, &publicKey);

                saltRec.index = 2;
                saltRec.activate = true;
                saltRec.length = 16;
                saltRec.data = new char[saltRec.length];
                bcopy(saltChar, saltRec.data, saltRec.length);
                publicKeyRec.index = 3;
                publicKeyRec.activate = true;
                publicKeyRec.length = publicKey->length;
                publicKeyRec.data = new char[publicKeyRec.length];
                bcopy(publicKey->data, publicKeyRec.data, publicKeyRec.length);

                mResponse.data.addRecord(stateRecord);
                mResponse.data.addRecord(publicKeyRec);
                mResponse.data.addRecord(saltRec);
            }
                break;
            case State_M3_SRPVerifyRequest: {
#if HomeKitLog == 1
                printf("%s, %d: State_M3_SRPVerifyRequest\n", __func__, __LINE__);
#endif
                const char *keyStr = 0;
                int keyLen = 0;
                const char *proofStr;
                int proofLen;
                keyStr = msg.data.dataPtrForIndex(3);
                keyLen = msg.data.lengthForIndex(3);
                char *temp = msg.data.dataPtrForIndex(4);
                if (temp != NULL) {
                    proofStr = temp;
                    proofLen = msg.data.lengthForIndex(4);
                }

                SRP_RESULT result = SRP_compute_key(srp, &secretKey, (const unsigned char*)keyStr, keyLen);
                result = SRP_verify(srp, (const unsigned char*)proofStr, proofLen);

                if (!SRP_OK(result)) {
                    PHKNetworkMessageDataRecord responseRecord;
                    responseRecord.activate = true;
                    responseRecord.data = new char[1];
                    responseRecord.data[0] = 2;
                    responseRecord.index = 7;
                    responseRecord.length = 1;
                    mResponse.data.addRecord(responseRecord);
#if HomeKitLog == 1
                    printf("Oops at M3\n");
#endif
                } else {
                    SRP_respond(srp, &response);
                    PHKNetworkMessageDataRecord responseRecord;
                    responseRecord.activate = true;
                    responseRecord.index = 4;
                    responseRecord.length = response->length;
                    responseRecord.data = new char[responseRecord.length];
                    bcopy(response->data, responseRecord.data, responseRecord.length);
                    mResponse.data.addRecord(responseRecord);
#if HomeKitLog == 1
                    printf("Password Correct\n");
#endif
                }

                const char salt[] = "Pair-Setup-Encrypt-Salt";
                const char info[] = "Pair-Setup-Encrypt-Info";
                int i = hkdf((const unsigned char*)salt, strlen(salt), (const unsigned char*)secretKey->data, secretKey->length, (const unsigned char*)info, strlen(info), (uint8_t*)sessionKey, 32);
                if (i != 0) return;
            }
                break;
            case State_M5_ExchangeRequest: {
#if HomeKitLog == 1
                printf("%s, %d: State_M5_ExchangeRequest\n", __func__, __LINE__);
#endif

                const char *encryptedPackage = NULL;int packageLen = 0;
                encryptedPackage = msg.data.dataPtrForIndex(5);
                packageLen = msg.data.lengthForIndex(5);
                char *encryptedData = new char[packageLen];
                bcopy(encryptedPackage, encryptedData, packageLen-16);
                char mac[16];
                bcopy(&encryptedPackage[packageLen-16], mac, 16);

                chacha20_ctx chacha20;    bzero(&chacha20, sizeof(chacha20));
                chacha20_setup(&chacha20, (const uint8_t *)sessionKey, 32, (uint8_t *)"PS-Msg05");

                //Ploy1305 key
                char temp[64];  bzero(temp, 64); char temp2[64];  bzero(temp2, 64);
                chacha20_encrypt(&chacha20, (const uint8_t*)temp, (uint8_t *)temp2, 64);

                char verify[16];  bzero(verify, 16);
                Poly1305_GenKey((const unsigned char*)temp2, (unsigned char *)encryptedData, packageLen - 16, Type_Data_Without_Length, verify);

                char *decryptedData = new char[packageLen-16];
                bzero(decryptedData, packageLen-16);
                chacha20_decrypt(&chacha20, (const uint8_t *)encryptedData, (uint8_t *)decryptedData, packageLen-16);

                if (bcmp(verify, mac, 16)) {
                    PHKNetworkMessageDataRecord responseRecord;
                    responseRecord.activate = true;
                    responseRecord.data = new char[1];
                    responseRecord.data[0] = 1;
                    responseRecord.index = 7;
                    responseRecord.length = 1;
                    mResponse.data.addRecord(responseRecord);

#if HomeKitLog == 1
                    for(int j = 0; j < packageLen-16; j++)
                        printf("%X ", decryptedData[j]);
                    printf("\n");

                    printf("verify: ");
                    for(int j = 0; j < 16; j++)
                        printf("%X ", verify[j]);
                    printf("\n");

                    printf("mac: ");
                    for(int j = 0; j < 16; j++)
                        printf("%X ", mac[j]);
                    printf("\n");

                    printf("Corrupt TLv8 at M5\n");
#endif
                } else {
                    /*
                     * HAK Pair Setup M6
                     */
                    PHKNetworkMessageData *subTLV8 = new PHKNetworkMessageData(decryptedData, packageLen-16);
                    char *controllerIdentifier = subTLV8->dataPtrForIndex(1);
                    char *controllerPublicKey = subTLV8->dataPtrForIndex(3);
                    char *controllerSignature = subTLV8->dataPtrForIndex(10);
                    char controllerHash[100];

                    PHKKeyRecord newRecord;
                    bcopy(controllerIdentifier, newRecord.controllerID, 36);
                    bcopy(controllerPublicKey, newRecord.publicKey, 32);
                    addControllerKey(newRecord);

                    const char salt[] = "Pair-Setup-Controller-Sign-Salt";
                    const char info[] = "Pair-Setup-Controller-Sign-Info";
                    int i = hkdf((const unsigned char*)salt, strlen(salt), (const unsigned char*)secretKey->data, secretKey->length, (const unsigned char*)info, strlen(info), (uint8_t*)controllerHash, 32);
                    if (i != 0) return;

                    bcopy(controllerIdentifier, &controllerHash[32], 36);
                    bcopy(controllerPublicKey, &controllerHash[68], 32);

                    int ed25519_err = ed25519_sign_open((const unsigned char*)controllerHash, 100, (const unsigned char*)controllerPublicKey, (const unsigned char*)controllerSignature);
                    delete subTLV8;

                    if (ed25519_err) return;
                    else {
                        PHKNetworkMessageData *returnTLV8 = new PHKNetworkMessageData();

                        {
                            PHKNetworkMessageDataRecord usernameRecord;
                            usernameRecord.activate = true; usernameRecord.index = 1;    usernameRecord.length = strlen(deviceIdentity); usernameRecord.data = new char[usernameRecord.length]; bcopy(deviceIdentity, usernameRecord.data, usernameRecord.length);
                            returnTLV8->addRecord(usernameRecord);
                        }

                        {
                            /*
                             * Generate Signature
                             */
                            const char salt[] = "Pair-Setup-Accessory-Sign-Salt";
                            const char info[] = "Pair-Setup-Accessory-Sign-Info";
                            uint8_t output[150];
                            hkdf((const unsigned char*)salt, strlen(salt), (const unsigned char*)secretKey->data, secretKey->length, (const unsigned char*)info, strlen(info), output, 32);

                            bcopy(deviceIdentity, &output[32], strlen(deviceIdentity));

                            char *signature = new char[64];
                            ed25519_secret_key edSecret;
                            bcopy(accessorySecretKey, edSecret, sizeof(edSecret));
                            ed25519_public_key edPubKey;
                            ed25519_publickey(edSecret, edPubKey);

                            bcopy(edPubKey, &output[32+strlen(deviceIdentity)], 32);
                            ed25519_sign(output, 64+strlen(deviceIdentity), (const unsigned char*)edSecret, (const unsigned char*)edPubKey, (unsigned char *)signature);
                            PHKNetworkMessageDataRecord signatureRecord;
                            signatureRecord.activate = true; signatureRecord.data = signature;   signatureRecord.index = 10;    signatureRecord.length = 64;
                            returnTLV8->addRecord(signatureRecord);

                            PHKNetworkMessageDataRecord publicKeyRecord;
                            publicKeyRecord.activate = true;
                            publicKeyRecord.index = 3;
                            publicKeyRecord.length = 32;
                            publicKeyRecord.data = new char[publicKeyRecord.length];
                            bcopy(edPubKey, publicKeyRecord.data, publicKeyRecord.length);
                            returnTLV8->addRecord(publicKeyRecord);
                        }

                        const char *tlv8Data;unsigned short tlv8Len;
                        returnTLV8->rawData(&tlv8Data, &tlv8Len);

                        PHKNetworkMessageDataRecord tlv8Record;
                        tlv8Record.data = new char[tlv8Len+16];tlv8Record.length = tlv8Len+16;
                        bzero(tlv8Record.data, tlv8Record.length);
                        {

                            chacha20_ctx ctx;   bzero(&ctx, sizeof(ctx));

                            chacha20_setup(&ctx, (const uint8_t *)sessionKey, 32, (uint8_t *)"PS-Msg06");
                            char buffer[64], key[64];   bzero(buffer, 64);
                            chacha20_encrypt(&ctx, (const uint8_t *)buffer, (uint8_t *)key, 64);
                            chacha20_encrypt(&ctx, (const uint8_t *)tlv8Data, (uint8_t *)tlv8Record.data, tlv8Len);

                            char verify[16];
                            memset(verify, 0, 16);
                            Poly1305_GenKey((const unsigned char *)key, (unsigned char*)tlv8Record.data, tlv8Len, Type_Data_Without_Length, verify);
                            memcpy((unsigned char *)&tlv8Record.data[tlv8Len], verify, 16);
                        }

                        tlv8Record.activate = true; tlv8Record.index = 5;
                        mResponse.data.addRecord(tlv8Record);

                        delete returnTLV8;
                    }

                }

                mResponse.data.addRecord(stateRecord);
                mResponse.getBinaryPtr(&responseBuffer, &responseLen);
                if (responseBuffer) {
                    write(subSocket, (const void *)responseBuffer, (size_t)responseLen);
                    delete [] responseBuffer;
                }

                delete []encryptedData;

                return;
            }
                break;
        }
        mResponse.data.addRecord(stateRecord);
        mResponse.getBinaryPtr(&responseBuffer, &responseLen);
        if (responseBuffer) {
#if HomeKitLog == 1
            printf("%s, %d, responseBuffer = %s, responseLen = %d\n", __func__, __LINE__, responseBuffer, responseLen);
#endif
            int len = write(subSocket, (const void *)responseBuffer, (size_t)responseLen);
            delete [] responseBuffer;
#if HomeKitLog == 1
            printf("Pair Setup Transfered length %d\n", len);
#endif
        } else {
#if HomeKitLog == 1
            printf("Why empty response\n");
#endif
        }

    } while (read(subSocket, (void *)buffer, 4096) > 0);
    SRP_free(srp);
}

void connectionInfo::handlePairVerify() {
    bool end = false;
    char state = State_Pair_Verify_M1;

    curved25519_key secretKey;
    curved25519_key publicKey;
    curved25519_key controllerPublicKey;
    curved25519_key sharedKey;

    uint8_t enKey[32];
#if HomeKitLog == 1
    printf("Start Pair Verify\n");
#endif

    do {
        PHKNetworkMessage msg(buffer);
        PHKNetworkResponse response = PHKNetworkResponse(200);
        bcopy(msg.data.dataPtrForIndex(6), &state, 1);
        switch (state) {
            case State_Pair_Verify_M1: {
#if HomeKitLog == 1
                printf("Pair Verify M1\n");
#endif
                bcopy(msg.data.dataPtrForIndex(3), controllerPublicKey, 32);
                for (short i = 0; i < sizeof(secretKey); i++) {
                    secretKey[i] = rand();
                }
                curve25519_donna((u8*)publicKey, (const u8 *)secretKey, (const u8 *)curveBasePoint);

                curve25519_donna(sharedKey, secretKey, controllerPublicKey);

                char *temp = new char[100];
                bcopy(publicKey, temp, 32);
                bcopy(deviceIdentity, &temp[32], strlen(deviceIdentity));
                bcopy(controllerPublicKey, &temp[32+strlen(deviceIdentity)], 32);

                PHKNetworkMessageDataRecord signRecord;
                signRecord.activate = true; signRecord.data = new char[64]; signRecord.index = 10;  signRecord.length = 64;

                ed25519_secret_key edSecret;
                bcopy(accessorySecretKey, edSecret, sizeof(edSecret));
                ed25519_public_key edPubKey;
                ed25519_publickey(edSecret, edPubKey);

                ed25519_sign((const unsigned char *)temp, 64+strlen(deviceIdentity), edSecret, edPubKey, (unsigned char *)signRecord.data);
                delete [] temp;

                PHKNetworkMessageDataRecord idRecord;
                idRecord.activate = true;
                idRecord.data = new char[17];
                bcopy(deviceIdentity, idRecord.data, 17);
                idRecord.index = 1;
                idRecord.length = (unsigned int)17;

                PHKNetworkMessageDataRecord pubKeyRecord;
                pubKeyRecord.activate = true;
                pubKeyRecord.data = new char[32];
                bcopy(publicKey, pubKeyRecord.data, 32);
                pubKeyRecord.index = 3;
                pubKeyRecord.length = 32;

                PHKNetworkMessageData data;
                response.data.addRecord(pubKeyRecord);
                data.addRecord(signRecord);
                data.addRecord(idRecord);

                unsigned char salt[] = "Pair-Verify-Encrypt-Salt";
                unsigned char info[] = "Pair-Verify-Encrypt-Info";

                int i = hkdf(salt, 24, sharedKey, 32, info, 24, enKey, 32);
                const char *plainMsg = 0;   unsigned short msgLen = 0;
                data.rawData(&plainMsg, &msgLen);

                char *encryptMsg = new char[msgLen+16];
                char *polyKey = new char[64];   bzero(polyKey, 64);

                char zero[64];  bzero(zero, 64);

                chacha20_ctx chacha;
                chacha20_setup(&chacha, enKey, 32, (uint8_t *)"PV-Msg02");
                chacha20_encrypt(&chacha, (uint8_t *)zero, (uint8_t *)polyKey, 64);
                chacha20_encrypt(&chacha, (uint8_t *)plainMsg, (uint8_t *)encryptMsg, msgLen);

                delete [] plainMsg;

                char verify[16];
                memset(verify, 0, 16);
                Poly1305_GenKey((const unsigned char *)polyKey, (uint8_t *)encryptMsg, msgLen, Type_Data_Without_Length, verify);
                memcpy((unsigned char *)&encryptMsg[msgLen], verify, 16);

                PHKNetworkMessageDataRecord encryptRecord;
                encryptRecord.activate = true;
                encryptRecord.index = 5;
                encryptRecord.length = msgLen+16;
                encryptRecord.data = new char[encryptRecord.length];
                bcopy(encryptMsg, encryptRecord.data, encryptRecord.length);
                response.data.addRecord(encryptRecord);

                delete [] encryptMsg;
                delete [] polyKey;
            }
                break;
            case State_Pair_Verify_M3: {
#if HomeKitLog == 1
                printf("Pair Verify M3\n");
#endif
                char *encryptedData = msg.data.dataPtrForIndex(5);
                short packageLen = msg.data.lengthForIndex(5);

                chacha20_ctx chacha20;    bzero(&chacha20, sizeof(chacha20));
                chacha20_setup(&chacha20, (const uint8_t *)enKey, 32, (uint8_t *)"PV-Msg03");

                //Ploy1305 key
                char temp[64];  bzero(temp, 64); char temp2[64];  bzero(temp2, 64);
                chacha20_encrypt(&chacha20, (const uint8_t*)temp, (uint8_t *)temp2, 64);

                char verify[16];    bzero(verify, 16);
                Poly1305_GenKey((const unsigned char *)temp2, (uint8_t *)encryptedData, packageLen - 16, Type_Data_Without_Length, verify);

                if (!bcmp(verify, &encryptedData[packageLen-16], 16)) {
                    char *decryptData = new char[packageLen-16];
                    chacha20_decrypt(&chacha20, (const uint8_t *)encryptedData, (uint8_t *)decryptData, packageLen-16);
                    PHKNetworkMessageData data = PHKNetworkMessageData(decryptData, packageLen-16);

                    PHKKeyRecord rec = getControllerKey(data.dataPtrForIndex(1));

                    char tempMsg[100];
                    bcopy(controllerPublicKey, tempMsg, 32);
                    bcopy(data.dataPtrForIndex(1), &tempMsg[32], 36);
                    bcopy(publicKey, &tempMsg[68], 32);

                    int err = ed25519_sign_open((const unsigned char *)tempMsg, 100, (const unsigned char *)rec.publicKey, (const unsigned char *)data.dataPtrForIndex(10));

                    char *repBuffer = 0;  int repLen = 0;
                    if (err == 0) {
                        end = true;

                        hkdf((uint8_t *)"Control-Salt", 12, sharedKey, 32, (uint8_t *)"Control-Read-Encryption-Key", 27, accessoryToControllerKey, 32);
                        hkdf((uint8_t *)"Control-Salt", 12, sharedKey, 32, (uint8_t *)"Control-Write-Encryption-Key", 28, controllerToAccessoryKey, 32);

#if HomeKitLog == 1
                        printf("Verify success\n");
#endif

                    } else {
                        PHKNetworkMessageDataRecord error;
                        error.activate = true;
                        error.data = new char[1];
                        error.data[0] = 2;
                        error.index = 7;
                        error.length = 1;
                        response.data.addRecord(error);
#if HomeKitLog == 1
                        printf("Verify failed\n");
#endif
                    }

                    delete [] decryptData;
                }

            }
        }

        PHKNetworkMessageDataRecord stage;
        stage.activate = true;
        stage.data = new char;
        stage.data[0] = (char)state+1;
        stage.index = 6;
        stage.length = 1;
        response.data.addRecord(stage);

        char *repBuffer = 0;  int repLen = 0;
        response.getBinaryPtr(&repBuffer, &repLen);
        if (repBuffer) {
            write(subSocket, repBuffer, repLen);
            delete [] repBuffer;
        }
    } while (!end && read(subSocket, buffer, 4096) > 0);

}

void connectionInfo::handleAccessoryRequest() {
    connected = true;

    char *decryptData = new char[2048];

    int len;

#if HomeKitLog == 1
    printf("Successfully Connect\n");
#endif

    numberOfMsgRec = 0;
    numberOfMsgSend = 0;

    clearNotify();

    pthread_mutex_init(&mutex, NULL);

    do {
        bzero(buffer, 4096);
        len = read(subSocket, buffer, 4096);

        //FIXME make sure buffer len > (2 + msgLen + 16)??
        printf("connectionInfo::handleAccessoryRequest::0\n");
        printf("connectionInfo::okMessage%d\n", okMessage);
        if (len > 0) {
        if (okMessage>0)
            {
                okMessage=0;
            }
            printf("connectionInfo::handleAccessoryRequest::1\n");
            uint16_t msgLen = (uint8_t)buffer[1]*256+(uint8_t)*buffer;

            chacha20_ctx chacha20;    bzero(&chacha20, sizeof(chacha20));

            printf("send: %llx\n", numberOfMsgRec);
        numberOfMsgRec = bswap_64(numberOfMsgRec);
            printf("send: %llx\n", numberOfMsgRec);
            chacha20_setup(&chacha20, (const uint8_t *)controllerToAccessoryKey, 32, (uint8_t *)&numberOfMsgRec);
            numberOfMsgRec = bswap_64(numberOfMsgRec);
            numberOfMsgRec++;
            printf("send: %llx\n", numberOfMsgRec);

            char temp[64];  bzero(temp, 64); char temp2[64];  bzero(temp2, 64);
            chacha20_encrypt(&chacha20, (const uint8_t*)temp, (uint8_t *)temp2, 64);

            //Ploy1305 key
            char verify[16];    bzero(verify, 16);
            Poly1305_GenKey((const unsigned char *)temp2, (uint8_t *)buffer, msgLen, Type_Data_With_Length, verify);

            bzero(decryptData, 2048);
            chacha20_encrypt(&chacha20, (const uint8_t *)&buffer[2], (uint8_t *)decryptData, msgLen);

            printf("Request: %s\nPacketLen: %d\n, MessageLen: %d\n", decryptData, len, strlen(decryptData));

            if(len >= (2 + msgLen + 16)
               && memcmp((void *)verify, (void *)&buffer[2 + msgLen], 16) == 0) {
#if HomeKitLog == 1
                printf("Verify successfully!\n");
#endif
            }
            else {

#if HomeKitLog == 1
                printf("Passed-in data is no-verified!\n");
                for (int i = 0; i < 16; i++)
                    printf("%ud ", verify[i]);
                printf("\n");
                for (int i = 0; i < 16; i++)
                    printf("%ud ", buffer[2 + msgLen+i]);
                printf("\n");

                unsigned long long numberOfMsgRec_ = numberOfMsgRec-1;
                chacha20_setup(&chacha20, (const uint8_t *)controllerToAccessoryKey, 32, (uint8_t *)&numberOfMsgRec_);
                chacha20_encrypt(&chacha20, (const uint8_t*)temp, (uint8_t *)temp2, 64);
                Poly1305_GenKey((const unsigned char *)temp, (uint8_t *)buffer, msgLen, Type_Data_With_Length, verify);
                for (int i = 0; i < 16; i++)
                    printf("%ud ", verify[i]);
                printf("\n");

#endif
                continue;
            }

            pthread_mutex_lock(&mutex);

            //Output return
            char *resultData = 0; unsigned int resultLen = 0;
            handleAccessory(decryptData, msgLen, &resultData, &resultLen, this);

            //18 = 2(resultLen) + 16(poly1305 verify key)
            char *reply = new char[resultLen+18];
            reply[0] = resultLen%256;
            reply[1] = (resultLen-(uint8_t)reply[0])/256;

            bswap_64(numberOfMsgSend);
            chacha20_setup(&chacha20, (const uint8_t *)accessoryToControllerKey, 32, (uint8_t *)&numberOfMsgSend);
            bswap_64(numberOfMsgSend);
            numberOfMsgSend++;

            chacha20_encrypt(&chacha20, (const uint8_t*)temp, (uint8_t *)temp2, 64);
            chacha20_encrypt(&chacha20, (const uint8_t*)resultData, (uint8_t*)&reply[2], resultLen);

            Poly1305_GenKey((const unsigned char *)temp2, (uint8_t *)reply, resultLen, Type_Data_With_Length, verify);
            memcpy((unsigned char*)&reply[resultLen+2], verify, 16);

            write(subSocket, reply, resultLen+18);

            pthread_mutex_unlock(&mutex);

            delete [] reply;
            delete [] resultData;
        }
    } while (len > 0);

    pthread_mutex_destroy(&mutex);

    delete [] decryptData;
    connected = false;

}

//Object Logic
PHKNetworkIP::~PHKNetworkIP() {
    DNSServiceRefDeallocate(netServiceV4);
}

const char *copyLine(const char *rawData, char *destination) {
    int i;
    for (i = 0; rawData[i] != '\r' && rawData[i] != 0; i++) {
        destination[i] = rawData[i];
    }
    i++;
    if (rawData[i] == '\n')
        return &rawData[i+1];
    else return &rawData[i];
}

const char *skipTillChar(const char *ptr, const char target) {
    for (; (*ptr)!=0&&(*ptr)!=target; ptr++);  ptr++;
    return ptr;
}

PHKNetworkMessage::PHKNetworkMessage(const char *rawData) {
    strcpy(method, "POST");

    const char *ptr = rawData;
    for (int i = 0; (*ptr)!=0 && (*ptr)!=' '; ptr++, i++) {
        method[i] = (*ptr);
        method[i+1] = 0;
    } ptr+=2;

    //Copy message directory
    for (int i = 0; (*ptr)!=0 && (*ptr)!=' '; ptr++, i++) {
        directory[i] = (*ptr);
        directory[i+1] = 0;
    }

    ptr = skipTillChar(ptr, '\n');

    char buffer[1024];
    const char *dptr = ptr;
    for (int i = 0; i < 19; i++) {
        bzero(buffer, 1024);
        dptr = copyLine(dptr, buffer);
    }

    //Reject host
    if (strncmp(ptr, "Host", 4) == 0) {
        ptr = skipTillChar(ptr, '\n');
    }

    //Get the length of content
    //Skip to the content-type
    ptr = skipTillChar(ptr, ':'); ptr++;
    unsigned int dataSize = (unsigned int)strtol(ptr, (char **)&ptr, 10);

    //Get the type of content
    //Skip to the content-length
    ptr = skipTillChar(ptr, ':');  ptr+=2;
    for (int i = 0; (*ptr)!=0 && (*ptr)!='\r'; ptr++, i++) {
        type[i] = (*ptr);
        type[i+1] = 0;
    }
    ptr+=4;

    //Data
    data = PHKNetworkMessageData(ptr, dataSize);

}

void PHKNetworkMessage::getBinaryPtr(char **buffer, int *contentLength) {
    const char *_data; unsigned short dataSize;
    data.rawData(&_data, &dataSize);
    (*buffer) = new char[1024];
    (*contentLength) = snprintf((*buffer), 1024, "%s /%s HTTP/1.1\r\nContent-Length: %hu\r\nContent-Type: %s\r\n\r\n", method, directory, dataSize, type);
    for (int i = 0; i < dataSize; i++) {
        (*buffer)[*contentLength+i] = _data[i];
    }
    (*contentLength)+=dataSize;
    (*buffer)[*contentLength] = 0;
}

PHKNetworkMessageData & PHKNetworkMessageData::operator=(const PHKNetworkMessageData &data) {
    count = data.count;
    for (int i = 0; i < 10; i++) {
        if (data.records[i].length) {
            records[i] = data.records[i];
            records[i].data = new char[records[i].length];
            bcopy(data.records[i].data, records[i].data, data.records[i].length);
        }
    }
    return *this;
}

PHKNetworkMessageData::PHKNetworkMessageData(const char *rawData, unsigned short len) {
    unsigned short delta = 0;
    while (delta < len) {
        int index = recordIndex(rawData[delta+0]);
        if (index < 0) {
            records[count].index = (rawData)[delta+0];
            records[count].length = (unsigned char)(rawData)[delta+1];
            records[count].data = new char[records[count].length];
            records[count].activate = true;
            bcopy(&rawData[delta+2], records[count].data, records[count].length);
            delta += (records[count].length+2);
            count++;
        } else {
            int newLen = ((unsigned char*)(rawData))[delta+1];
            newLen += records[index].length;
            char *ptr = new char[newLen];
            bcopy(records[index].data, ptr, records[index].length);
            bcopy(&rawData[delta+2], &ptr[records[index].length], newLen-records[index].length);
            delete [] records[index].data;
            records[index].data = ptr;

            delta += (newLen-records[index].length+2);
            records[index].length = newLen;

        }
    }
}

void PHKNetworkMessageData::rawData(const char **dataPtr, unsigned short *len) {
    string buffer = "";
    for (int i = 0; i < 10; i++) {
        if (records[i].activate) {
            for (int j = 0; j != records[i].length;) {
                unsigned char len = records[i].length-j>255?255:records[i].length-j;
                string temp(&records[i].data[j], len);
                temp = (char)records[i].index+((char)len+temp);
                buffer += temp;
                j += (unsigned int)len;
            }
        }
    }
    *len = buffer.length();
    *dataPtr = new char[*len];
    bcopy(buffer.c_str(), (void *)*dataPtr, *len);
}

int PHKNetworkMessageData::recordIndex(unsigned char index) {
    for (int i = 0; i < count; i++) {
        if (records[i].activate&&records[i].index==index) return i;
    }
    return -1;
}

void PHKNetworkMessageData::addRecord(PHKNetworkMessageDataRecord& record) {
    records[count] = record;
    count++;
}

PHKNetworkResponse::PHKNetworkResponse(unsigned short _responseCode) {
    responseCode = _responseCode;
}

string PHKNetworkResponse::responseType() {
    switch (responseCode) {
        case 200:
            return "OK";
        default:
            return "";
    }
}

inline void int2str(int i, char *s) { sprintf(s,"%d",i); }

void PHKNetworkResponse::getBinaryPtr(char **buffer, int *contentLength) {
    char tempCstr[5];   int2str(responseCode, tempCstr);
    string temp = "HTTP/1.1 ";
    temp += tempCstr+(" "+responseType());
    temp += "\r\nContent-Type: application/pairing+tlv8\r\nContent-Length: ";
    const char *dataPtr;    unsigned short dataLen;
    data.rawData(&dataPtr, &dataLen);
    int2str(dataLen, tempCstr);
    temp += tempCstr;
    temp += "\r\n\r\n";
    *buffer = new char[temp.length()+dataLen];
    bcopy(temp.c_str(), *buffer, temp.length());
    bcopy(dataPtr, &((*buffer)[temp.length()]), dataLen);
    *contentLength = temp.length()+dataLen;
    delete [] dataPtr;
}

char *PHKNetworkMessageData::dataPtrForIndex(unsigned char index) {
    int _index = recordIndex(index);
    if (_index >= 0)
        return records[_index].data;
    return 0;
}

unsigned int PHKNetworkMessageData::lengthForIndex(unsigned char index) {
    int _index = recordIndex(index);
    if (_index >= 0)
        return records[_index].length;
    return 0;
}

PHKNetworkMessageDataRecord &PHKNetworkMessageDataRecord::operator=(const PHKNetworkMessageDataRecord& r) {
    index = r.index;
    activate = r.activate;
    length = r.length;
    if (data)
        delete [] data;
    data = new char[length];
    bcopy(r.data, data, length);
    return *this;
}

PHKNetworkMessageDataRecord::~PHKNetworkMessageDataRecord() {
    if (length) delete [] data;
}
beckmx commented 7 years ago

Hello @etwmc I believe I isolated the issue, but still don't know how it happens or how to solve it, in the PHKNetworkIP.cpp file inside the method connectionLoop() it seems every time the PHK responds as "OK 204" the iOS doesnt acknowledge the message and thus when the code reaches the part:

len = read(subSocket, info->buffer, 4096);

The buffer is empty and continues to close the socket, it just happens in openwrt, I tried to avoid closing the socket but it seems to lose connection at some point

etwmc commented 7 years ago

When the buffer is empty, it means it is close on the other end (hence iOS homed indicate problem) Are you sure the bswap code is right? in the reading decryption part, you have "numberOfMsgRec = bswap_64(numberOfMsgRec);" yet, in the writing encryption part, you have "bswap_64(numberOfMsgSend);", without no assignment Shouldn't bswap_64 keep the input value unchanged? It would explain a close on the other end, since the resulting sender nounce is incorrect, and homed would just assume the other end is corrupted somehow (as any security protocol would be)

beckmx commented 7 years ago

oh my!!, yeah it was that issue, I forgot to assign the value again, I was using two copies, one in linux for the openwrt compiler and the other for the mac compiler, yup, it is now working just fine, I luv it :D

2016-11-22 2:53 GMT-06:00 etwmc notifications@github.com:

When the buffer is empty, it means it is close on the other end (hence iOS homed indicate problem) Are you sure the bswap code is right? in the reading decryption part, you have "numberOfMsgRec = bswap_64(numberOfMsgRec);" yet, in the writing encryption part, you have "bswap_64(numberOfMsgSend);", without no assignment Shouldn't bswap_64 keep the input value unchanged? It would explain a close on the other end, since the resulting sender nounce is incorrect, and homed would just assume the other end is corrupted somehow (as any security protocol would be)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/etwmc/Personal-HomeKit-HAP/issues/62#issuecomment-262182202, or mute the thread https://github.com/notifications/unsubscribe-auth/AAb3Gbsd3gCD3pCadNPOWmSXjo1bCI13ks5rAq1ygaJpZM4J0w3V .

tstiman commented 7 years ago

Is there an example of setting up a ip camera?

etwmc commented 7 years ago
  1. Please, this is a separate thing, create a separate issue
  2. The current version of PHK does not support user-based characteristic, which is the feature how to set up the camera service (but the next version does. You can get it from the Wifi-Radar. I'm kinda busy to merge it back to PHK, since someone issue a merge request to support Windows)
  3. IP camera service require video service and audio, and HomeKit require the encoding on the accessory. Do you have any specific platform you set in mind? Because if you want to do household monitor on computer, Apple has an app for that. And if you want to replace the software on your IP cam, I can do a special version if you have a special chips in mind, and if we can get hardware multiple resolution video encoding to work. I have some idea on reducing the load from multiple client, but I can't just spent a week of work to end up with ffmpeg. It just make a bad product.
robot0518 commented 7 years ago

Hello @beckmx can you upload your code to solve this problem? I got the lastest version and ran it , the problem still existed

thanks.