HomeSpan/src/HAP.h

164 lines
9.7 KiB
C

/*********************************************************************************
* 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 <WiFi.h>
#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<kTLVType,11> 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<Controller, Mallocator<Controller>> 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;