156 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C
		
	
	
	
| /*********************************************************************************
 | |
|  *  MIT License
 | |
|  *  
 | |
|  *  Copyright (c) 2020 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 <nvs.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;  // slot is allocated with Controller data
 | |
|   boolean admin;            // Controller has admin privileges
 | |
|   uint8_t ID[36];           // Pairing ID
 | |
|   uint8_t LTPK[32];         // Long Term Ed2519 Public Key
 | |
| };
 | |
| 
 | |
| /////////////////////////////////////////////////
 | |
| // 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=8095;                     // max number of bytes in HTTP message buffer
 | |
|   static const int MAX_CONTROLLERS=16;                // maximum number of paired controllers (HAP requires at least 16)
 | |
|   
 | |
|   static TLV<kTLVType,10> tlv8;                       // TLV8 structure (HAP Section 14.1) with space for 10 TLV records of type kTLVType (HAP Table 5-6)
 | |
|   static nvs_handle hapNVS;                           // handle for non-volatile-storage of HAP data
 | |
|   static nvs_handle wifiNVS;                          // handle for non-volatile-storage of WiFi data
 | |
|   static nvs_handle srpNVS;                           // handle for non-volatile-storage of SRP data
 | |
|   static uint8_t httpBuf[MAX_HTTP+1];                 // buffer to store HTTP messages (+1 to leave room for storing an extra 'overflow' character)
 | |
|   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 Controller controllers[MAX_CONTROLLERS];     // 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=NULL;         // handle to client
 | |
|   Controller *cPair;              // 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)
 | |
| 
 | |
|   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();                                           // 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);     // prints 'n' bytes of *buf as HEX, one byte per row.  For diagnostics/debugging only
 | |
|   static void hexPrintRow(uint8_t *buf, int n);        // prints 'n' bytes of *buf as HEX, all on one row
 | |
|   static void charPrintRow(uint8_t *buf, int n);       // prints 'n' bytes of *buf as CHAR, all on one row
 | |
|   
 | |
|   static Controller *findController(uint8_t *id);                                      // returns pointer to controller with mathching ID (or NULL if no match)
 | |
|   static Controller *getFreeController();                                              // return pointer to next free controller slot (or NULL if no free slots)
 | |
|   static Controller *addController(uint8_t *id, uint8_t *ltpk, boolean admin);         // stores data for new Controller with specified data.  Returns pointer to Controller slot on success, else NULL
 | |
|   static int nAdminControllers();                                                      // returns number of admin Controllers stored
 | |
|   static void removeControllers();                                                     // removes all Controllers (sets allocated flags to false for all slots)
 | |
|   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();                                                      // prints IDs of all allocated (paired) Controller
 | |
|   static void callServiceLoops();                                                      // call the loop() method for any Service with that over-rode the default method
 | |
|   static void checkPushButtons();                                                      // checks for PushButton presses and calls button() method of attached Services when found
 | |
|   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;
 |