/********************************************************************************* * MIT License * * Copyright (c) 2020-2023 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * ********************************************************************************/ #pragma once #include #include "HomeSpan.h" #include "TLV.h" #include "HAPConstants.h" #include "HKDF.h" #include "SRP.h" ///////////////////////////////////////////////// // NONCE Structure (HAP used last 64 of 96 bits) struct Nonce { uint8_t x[12]; Nonce(); void zero(); uint8_t *get(); void inc(); }; ///////////////////////////////////////////////// // Paired Controller Structure for Permanently-Stored Data struct Controller { boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info) boolean admin; // Controller has admin privileges uint8_t ID[36]; // Pairing ID uint8_t LTPK[32]; // Long Term Ed2519 Public Key Controller(){} Controller(uint8_t *id, uint8_t *ltpk, boolean ad){ allocated=true; admin=ad; memcpy(ID,id,sizeof(ID)); memcpy(LTPK,ltpk,sizeof(LTPK)); } }; ///////////////////////////////////////////////// // Accessory Structure for Permanently-Stored Data struct Accessory { uint8_t ID[17]; // Pairing ID in form "XX:XX:XX:XX:XX:XX" uint8_t LTSK[64]; // secret key for Ed25519 signatures uint8_t LTPK[32]; // public key for Ed25519 signatures }; ///////////////////////////////////////////////// // HAPClient Structure // Reads and Writes from each HAP Client connection struct HAPClient { // common structures and data shared across all HAP Clients static const int MAX_HTTP=8096; // max number of bytes allowed for HTTP message static const int MAX_CONTROLLERS=16; // maximum number of paired controllers (HAP requires at least 16) static const int MAX_ACCESSORIES=150; // maximum number of allowed Accessories (HAP limit=150) static TLV tlv8; // TLV8 structure (HAP Section 14.1) with space for 11 TLV records of type kTLVType (HAP Table 5-6) static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data static nvs_handle srpNVS; // handle for non-volatile-storage of SRP data static HKDF hkdf; // generates (and stores) HKDF-SHA-512 32-byte keys derived from an inputKey of arbitrary length, a salt string, and an info string static pairState pairStatus; // tracks pair-setup status static SRP6A srp; // stores all SRP-6A keys used for Pair-Setup static Accessory accessory; // Accessory ID and Ed25519 public and secret keys- permanently stored static list> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored static int conNum; // connection number - used to keep track of per-connection EV notifications // individual structures and data defined for each Hap Client connection WiFiClient client; // handle to client Controller *cPair=NULL; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection) // These keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period uint8_t publicCurveKey[32]; // public key for Curve25519 encryption uint8_t sharedCurveKey[32]; // Pair-Verfied Shared Secret key derived from Accessory's epehmeral secretCurveKey and Controller's iosCurveKey uint8_t sessionKey[32]; // shared Session Key (derived with various HKDF calls) uint8_t iosCurveKey[32]; // Curve25519 public key for associated paired controller // CurveKey and CurveKey Nonces are created once each new session is verified in /pair-verify. Keys persist for as long as connection is open uint8_t a2cKey[32]; // AccessoryToControllerKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2) uint8_t c2aKey[32]; // ControllerToAccessoryKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2) Nonce a2cNonce; // encryption nonce (starts at zero at end of each Pair-Verify and increment every encryption - NOT DOCUMENTED) Nonce c2aNonce; // decryption nonce (starts at zero at end of each Pair-Verify and increment every encryption - NOT DOCUMENTED) // define member methods void processRequest(); // process HAP request int postPairSetupURL(); // POST /pair-setup (HAP Section 5.6) int postPairVerifyURL(); // POST /pair-verify (HAP Section 5.7) int getAccessoriesURL(); // GET /accessories (HAP Section 6.6) int postPairingsURL(); // POST /pairings (HAP Sections 5.10-5.12) int getCharacteristicsURL(char *urlBuf); // GET /characteristics (HAP Section 6.7.4) int putCharacteristicsURL(char *json); // PUT /characteristics (HAP Section 6.7.2) int putPrepareURL(char *json); // PUT /prepare (HAP Section 6.7.2.4) int getStatusURL(); // GET / status (an optional, non-HAP feature) void tlvRespond(); // respond to client with HTTP OK header and all defined TLV data records (those with length>0) void sendEncrypted(char *body, uint8_t *dataBuf, int dataLen); // send client complete ChaCha20-Poly1305 encrypted HTTP mesage comprising a null-terminated 'body' and 'dataBuf' with 'dataLen' bytes int receiveEncrypted(uint8_t *httpBuf, int messageSize); // decrypt HTTP request (HAP Section 6.5) int notFoundError(); // return 404 error int badRequestError(); // return 400 error int unauthorizedError(); // return 470 error // define static methods static void init(); // initialize HAP after start-up static void hexPrintColumn(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level static void hexPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level static void charPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match) static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any) static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements. static void printControllers(int minLogLevel=0); // prints IDs of all allocated (paired) Controller, subject to specified minimum log level static int listControllers(uint8_t *tlvBuf); // creates and prints a multi-TLV list of Controllers (HAP Section 5.12) static void saveControllers(); // saves Controller list in NVS static int nAdminControllers(); // returns number of admin Controller static void tearDown(uint8_t *id); // tears down connections using Controller with ID=id; tears down all connections if id=NULL static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8) static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4) static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client }; ///////////////////////////////////////////////// // Extern Variables extern HAPClient **hap;