diff --git a/inc/ec_base.h b/inc/ec_base.h index 74dde8f..9236e14 100644 --- a/inc/ec_base.h +++ b/inc/ec_base.h @@ -196,33 +196,97 @@ typedef enum { ec_session_type_recfg, } ec_session_type_t; +/** + * @brief The consistent parameters used for all connections between a Configurator and all Enrollees/Agents + */ typedef struct { const EC_GROUP *group; - const EVP_MD *hashfcn; - BIGNUM *x, *y, *prime; - BIGNUM *m, *n, *l; - EC_POINT *M, *N; - BN_CTX *bnctx; - EC_KEY *initiator_proto_key; - EC_KEY *responder_proto_key; - EC_POINT *responder_proto_pt; - EC_POINT *responder_connector; - int group_num; - int digestlen; - int noncelen; + const EVP_MD *hash_fcn; + BIGNUM *prime; + BN_CTX *bn_ctx; + int digest_len; + int nonce_len; int nid; - bool mutual; - unsigned char initiator_keyhash[SHA512_DIGEST_LENGTH]; - unsigned char responder_keyhash[SHA512_DIGEST_LENGTH]; - unsigned char initiator_nonce[SHA512_DIGEST_LENGTH/2]; - unsigned char responder_nonce[SHA512_DIGEST_LENGTH/2]; + + +} ec_persistent_context_t; + +/** + * @brief The parameters used only during the creation of a connection between a Configurator and a specific Enrollee/Agent + */ +typedef struct { + + /** + * Initiator, Responder, Enrollee, and Configurator Nonces. + * Multiple nonces need to be accounted for at one time. + * These are heap allocated but freed after the auth/cfg process is complete. + */ + uint8_t *i_nonce, *r_nonce, *e_nonce, *c_nonce; + + /** + * The protocol key pairs for the initiator and responder. These are are exchanged/generated during the auth/cfg process. + * This must be freed after the auth/cfg process is complete. + */ + EC_POINT *public_init_proto_key, *public_resp_proto_key; + BIGNUM *priv_init_proto_key, *priv_resp_proto_key; + + /** + * The generated intermediate keys. These are used multiple times during the auth/cfg process. + * These are heap allocated but freed after the auth/cfg process is complete. + */ + uint8_t *k1, *k2, *ke, *bk; + + /** + * The EC x coordinate values for the M, N, and (optional) L points. + * Once generated, these are used multiple times during the auth/cfg process. + */ + BIGNUM *m, *n, *l; + + /** + * A random point on the curve for reconfiguration. The same point is used throughout reconfiguration. + * Used from the configurator/controller end as short term memory of which enrollee's it's seen before. + * Used by the enrollee to provide that short term memory to the controller. + * This must be freed after the reconfiguration process is complete. + */ + EC_POINT *E_Id; + + uint8_t transaction_id; + + /** + * Only needs to be known during the auth process to decide wether or not to generate the L key. + */ + bool is_mutual_auth; + +} ec_ephemeral_context_t; + +/** + * @brief The parameters used for a specific connection between a Configurator and a specific Enrollee/Agent + */ +typedef struct { + /* + The protocol key of the Enrollee is used as Network Access key (netAccessKey) later in the DPP Configuration and DPP Introduction protocol + */ + EC_KEY *net_access_key; + + /* TODO: + Add (if needed): + - C-connector + - c-sign-key + - privacy-protection-key (ppk) + + */ + + ec_ephemeral_context_t eph_ctx; +} ec_connection_context_t; + + + +/* +REMOVED: + -k1, k2, ke. These are only generated once per device per authentication session (and it should be that way) + unsigned char responder_nonce[SHA512_DIGEST_LENGTH/2]; unsigned char enrollee_nonce[SHA512_DIGEST_LENGTH/2]; - unsigned char k1[SHA512_DIGEST_LENGTH]; - unsigned char k2[SHA512_DIGEST_LENGTH]; - unsigned char ke[SHA512_DIGEST_LENGTH]; - unsigned char rauth[SHA512_DIGEST_LENGTH]; - unsigned char iauth[SHA512_DIGEST_LENGTH]; -} ec_params_t; +*/ typedef struct { @@ -232,9 +296,18 @@ typedef struct { mac_address_t mac_addr; ec_session_type_t type; - // Updated data - EC_KEY *initiator_boot_key; - EC_KEY *responder_boot_key; + /* + Initiator/Configurator bootstrapping key. (ALWAYS REQUIRED on controller, OPTIONAL on enrollee) + - If this is the Controller, then this key is stored on the Controller. + - If this is the Enrollee, then this key is required for "mutual authentication" and must be recieved via an out-of-band mechanism from the controller. + */ + const EC_KEY *initiator_boot_key; + /* + Responder/Enrollee bootstrapping key. (REQUIRED) + - If this is the Controller, then this key was recieved out-of-band from the Enrollee in the DPP URI + - If this is the Enrollee, then this key is stored locally. + */ + const EC_KEY *responder_boot_key; } ec_data_t; #ifdef __cplusplus diff --git a/inc/ec_configurator.h b/inc/ec_configurator.h new file mode 100644 index 0000000..412d938 --- /dev/null +++ b/inc/ec_configurator.h @@ -0,0 +1,191 @@ +#ifndef EC_CONFIGURATOR_H +#define EC_CONFIGURATOR_H + +#include +#include +#include + +#include "em_base.h" +#include "ec_crypto.h" + + +/** + * @brief Sends a chirp notification + * + * @param chirp_tlv The chirp TLV to send + * @param len The length of the chirp TLV + * @return bool true if successful, false otherwise + */ +using send_chirp_func = std::function; + +/** + * @brief Sends a proxied encapsulated DPP message + * + * @param encap_dpp_tlv The 1905 Encap DPP TLV to include in the message + * @param encap_dpp_len The length of the 1905 Encap DPP TLV + * @param chirp_tlv The chirp value to include in the message. If NULL, the message will not include a chirp value + * @param chirp_len The length of the chirp value + * @return bool true if successful, false otherwise + */ +using send_encap_dpp_func = std::function; + +/** +* @brief Set the CCE IEs in the beacon and probe response frames +* +* @param bool Whether to enable or disable the inclusion of CCE IEs in the beacon and probe response frames +* @return bool true if successful, false otherwise +*/ +using toggle_cce_func = std::function; + +class ec_configurator_t { +public: + /** + * @brief Construct an EC configurator + * + * @param send_chirp_notification The function to send a chirp notification + * @param send_prox_encap_dpp_msg The function to send a proxied encapsulated DPP message + */ + // TODO: Add send_action_frame and send_gas_frame functions + ec_configurator_t(std::string mac_addr, send_chirp_func send_chirp_notification, send_encap_dpp_func send_prox_encap_dpp_msg); + ~ec_configurator_t(); // Destructor + + /** + * @brief Set the CCE IEs in the beacon and probe response frames + * + * @param bool Whether to enable or disable the inclusion of CCE IEs in the beacon and probe response frames + * @return bool true if successful, false otherwise + */ + toggle_cce_func m_toggle_cce; + + /** + * @brief Start the EC configurator onboarding + * + * @param ec_data The data to use for onboarding (Parsed DPP URI Data) + * @return bool true if successful, false otherwise + */ + bool start(ec_data_t* ec_data); + + /** + * @brief Handles a presence announcement 802.11 frame, performing the necessary actions + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + virtual bool handle_presence_announcement(uint8_t *buff, unsigned int len) { + return 0; // Optional to implement + } + + /** + * @brief Handles an authentication request 802.11 frame, performing the necessary actions + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + virtual bool handle_auth_response(uint8_t *buff, unsigned int len) { + return true; // Optional to implement + } + + /** + * @brief Handles an configuration request 802.11+GAS frame, performing the necessary actions + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + virtual bool handle_cfg_request(uint8_t *buff, unsigned int len) { + return true; // Optional to implement + } + + /** + * @brief Handles an configuration result 802.11+GAS frame, performing the necessary actions + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + virtual bool handle_cfg_result(uint8_t *buff, unsigned int len) { + return true; // Optional to implement + } + + /** + * @brief Handle a chirp notification msg tlv and direct to the correct place (802.11 or 1905) + * + * @param chirp_tlv The chirp TLV to parse and handle + * @param tlv_len The length of the chirp TLV + * @return bool true if successful, false otherwise + */ + virtual bool process_chirp_notification(em_dpp_chirp_value_t* chirp_tlv, uint16_t tlv_len) = 0; + + /** + * @brief Handle a proxied encapsulated DPP message TLVs (including chirp value) and direct to the correct place (802.11 or 1905) + * + * @param encap_tlv The 1905 Encap DPP TLV to parse and handle + * @param encap_tlv_len The length of the 1905 Encap DPP TLV + * @param chirp_tlv The DPP Chirp Value TLV to parse and handle (NULL if not present) + * @param chirp_tlv_len The length of the DPP Chirp Value TLV (0 if not present) + * @return bool true if successful, false otherwise + */ + virtual bool process_proxy_encap_dpp_msg(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, em_dpp_chirp_value_t *chirp_tlv, uint16_t chirp_tlv_len) = 0; + + inline std::string get_mac_addr() { return m_mac_addr; }; + + // Disable copy construction and assignment + // Requires use of references or pointers when working with instances of this class + ec_configurator_t(const ec_configurator_t&) = delete; + ec_configurator_t& operator=(const ec_configurator_t&) = delete; + +protected: + ec_persistent_context_t m_p_ctx; + + ec_data_t m_boot_data; + + send_chirp_func m_send_chirp_notification; + + send_encap_dpp_func m_send_prox_encap_dpp_msg; + + std::string m_mac_addr; + + // The connections to the Enrollees/Agents + std::map m_connections; + + inline ec_connection_context_t* get_conn_ctx(const std::string& mac) { + if (m_connections.find(mac) == m_connections.end()) { + return NULL; + } + return &m_connections[mac]; + } + + inline ec_ephemeral_context_t* get_eph_ctx(const std::string& mac) { + static ec_ephemeral_context_t empty_ctx; // Static fallback for error cases + auto conn_ctx = get_conn_ctx(mac); + if (!conn_ctx) { + printf("%s:%d: Connection context not found for enrollee MAC %s\n", __func__, __LINE__, mac.c_str()); + return NULL; // Return reference to static empty context + } + return &conn_ctx->eph_ctx; + } + + inline void clear_conn_eph_ctx(const std::string& mac) { + auto conn = m_connections.find(mac); + if (conn == m_connections.end()) return; + auto &eph_ctx = conn->second.eph_ctx; + ec_crypto::free_ephemeral_context(&eph_ctx, m_p_ctx.nonce_len, m_p_ctx.digest_len); + } + +}; + +#endif // EC_CONFIGURATOR_H \ No newline at end of file diff --git a/inc/ec_crypto.h b/inc/ec_crypto.h new file mode 100644 index 0000000..8a8a748 --- /dev/null +++ b/inc/ec_crypto.h @@ -0,0 +1,258 @@ +#ifndef EC_CRYPTO_H +#define EC_CRYPTO_H + +#include "em_base.h" +#include "ec_base.h" + +#include + +class ec_crypto { +public: + + static int hkdf(const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, + uint8_t *salt, int saltlen, uint8_t *info, int infolen, + uint8_t *okm, int okmlen); + + + /** + * @brief Compute the hash of the provided key with an optional prefix + * + * @param key The key to hash + * @param digest The buffer to store the hash + * @param prefix The optional prefix to add to the key before hashing (NULL by default) + * @return int The length of the hash + */ + static uint8_t* compute_key_hash(const EC_KEY *key, const char *prefix = NULL); + + + static int compute_ke(ec_persistent_context_t& p_ctx, ec_ephemeral_context_t e_ctx, uint8_t *ke_buffer); + + /** + * @brief Abstracted HKDF computation that handles both simple and complex inputs + * + * This function provides a unified interface for HKDF computations, handling both + * simple single-input operations and more complex operations with multiple inputs. + * It properly formats BIGNUMs with appropriate padding based on prime length. + * + * @param key_out Buffer to store the output key (must be pre-allocated) + * @param key_out_len Length of the output key + * @param info_str Information string for HKDF + * @param bn_inputs Array of BIGNUMs to use as IKM + * @param bn_count Number of BIGNUMs in the array + * @param raw_salt Raw salt buffer (can be NULL) + * @param raw_salt_len Length of raw salt buffer + * + * @return Length of the output key on success, 0 on failure + */ + static int compute_hkdf_key(ec_persistent_context_t& p_ctx, uint8_t *key_out, int key_out_len, const char *info_str, + const BIGNUM **x_val_inputs, int x_val_count, + uint8_t *raw_salt, int raw_salt_len); + + /** + * Calculates L = ((b_R + p_R) modulo q) * B_I then gets the x-coordinate of the result + * + * @param group The EC_GROUP representing the elliptic curve + * @param bR Private Responder Bootstrapping Key + * @param pR Private Responder Protocol Key + * @param BI Public Initiator Bootstrapping Key + * @return EC_POINT* The calculated L point X value, or NULL on failure. Caller must free with BN_free() + */ + static BIGNUM* calculate_Lx(ec_persistent_context_t& p_ctx, const BIGNUM* bR, const BIGNUM* pR, const EC_POINT* BI); + + + static inline BIGNUM* get_ec_x(ec_persistent_context_t& p_ctx, const EC_POINT *point) { + BIGNUM *x = BN_new(); + if (EC_POINT_get_affine_coordinates_GFp(p_ctx.group, point, + x, NULL, p_ctx.bn_ctx) == 0) { + printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); + return NULL; + } + return x; + } + + /** + * @brief Encode an EC point into a protocol key buffer + * + * @param p_ctx The persistent context containing the EC group + * @param point The EC point to encode + * @return uint8_t* The encoded protocol key buffer, or NULL on failure. Caller must free with free() + * + */ + static inline uint8_t* encode_proto_key(ec_persistent_context_t& p_ctx, const EC_POINT *point) { + + BIGNUM *x = BN_new(); + BIGNUM *y = BN_new(); + + if (EC_POINT_get_affine_coordinates_GFp(p_ctx.group, point, + x, y, p_ctx.bn_ctx) == 0) { + printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); + BN_free(x); + BN_free(y); + return NULL; + } + + int prime_len = BN_num_bytes(p_ctx.prime); + + uint8_t* protocol_key_buff = (uint8_t *)calloc(2*prime_len, 1); + if (protocol_key_buff == NULL) { + printf("%s:%d unable to allocate memory\n", __func__, __LINE__); + BN_free(x); + BN_free(y); + return NULL; + } + BN_bn2bin((const BIGNUM *)x, &protocol_key_buff[prime_len - BN_num_bytes(x)]); + BN_bn2bin((const BIGNUM *)y, &protocol_key_buff[2*prime_len - BN_num_bytes(y)]); + + BN_free(x); + BN_free(y); + + return protocol_key_buff; + } + + /** + * @brief Decode a protocol key buffer into an EC point + * + * @param p_ctx The persistent context containing the EC group + * @param protocol_key_buff The encoded protocol key buffer + * @return EC_POINT* The decoded EC point, or NULL on failure. Caller must free with EC_POINT_free() + */ + static inline EC_POINT* decode_proto_key(ec_persistent_context_t& p_ctx, const uint8_t* protocol_key_buff) { + if (protocol_key_buff == NULL) { + printf("%s:%d null protocol key buffer\n", __func__, __LINE__); + return NULL; + } + + int prime_len = BN_num_bytes(p_ctx.prime); + BIGNUM *x = BN_bin2bn(protocol_key_buff, prime_len, NULL); + BIGNUM *y = BN_bin2bn(protocol_key_buff + prime_len, prime_len, NULL); + EC_POINT *point = EC_POINT_new(p_ctx.group); + + if (x == NULL || y == NULL) { + printf("%s:%d unable to convert buffer to BIGNUMs\n", __func__, __LINE__); + goto err; + } + + if (point == NULL) { + printf("%s:%d unable to create EC_POINT\n", __func__, __LINE__); + goto err; + } + + if (EC_POINT_set_affine_coordinates_GFp(p_ctx.group, point, x, y, p_ctx.bn_ctx) == 0) { + printf("%s:%d unable to set coordinates for the point\n", __func__, __LINE__); + goto err; + } + + // Verify the point is on the curve + if (EC_POINT_is_on_curve(p_ctx.group, point, p_ctx.bn_ctx) == 0) { + printf("%s:%d point is not on the curve\n", __func__, __LINE__); + goto err; + } + + BN_free(x); + BN_free(y); + + return point; + + err: + if (x) BN_free(x); + if (y) BN_free(y); + if (point) EC_POINT_free(point); + return NULL; + } + /** + * @brief Compute the shared secret X coordinate for an EC key pair + * (for example M.x, N.x) + * + * @param priv The private key (for example p_I, p_R, or b_R) + * @param pub The public key (for example P_I, B_R, or P_R) + * @return BIGNUM* The X coordinate of the shared secret, or NULL on failure. Caller must free with BN_free() + */ + static inline BIGNUM* compute_ec_ss_x(ec_persistent_context_t& p_ctx, const BIGNUM* priv, const EC_POINT* pub) { + // TODO: May have to adjust to get the group from the public key + EC_POINT *ss = EC_POINT_new(p_ctx.group); + if (EC_POINT_mul(p_ctx.group, ss, NULL, pub, priv, p_ctx.bn_ctx) == 0) { + printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); + return NULL; + } + return get_ec_x(p_ctx, ss); + } + + /** + * @brief Generate a protocol key pair for the given EC curve + * + * @param p_ctx The persistent context containing the EC curve + * @return std::pair Both the private and public keys, or NULL on failure + * + * @warning The caller must free the BIGNUM and EC_POINT with BN_free() and EC_POINT_free() respectively + */ + static inline std::pair generate_proto_keypair(ec_persistent_context_t& p_ctx) { + EC_KEY* proto_key = EC_KEY_new_by_curve_name(p_ctx.nid); + if (proto_key == NULL) { + printf("%s:%d Could not create protocol key\n", __func__, __LINE__); + return std::pair(NULL, NULL); + } + + if (EC_KEY_generate_key(proto_key) == 0) { + printf("%s:%d Could not generate protocol key\n", __func__, __LINE__); + return std::pair(NULL, NULL); + } + + const EC_POINT* proto_pub = EC_KEY_get0_public_key(proto_key); + if (proto_pub == NULL) { + printf("%s:%d Could not get protocol public key\n", __func__, __LINE__); + return std::pair(NULL, NULL); + } + + const BIGNUM* proto_priv = EC_KEY_get0_private_key(proto_key); + if (proto_priv == NULL) { + printf("%s:%d Could not get protocol private key\n", __func__, __LINE__); + return std::pair(NULL, NULL); + } + + return std::pair(proto_priv, proto_pub); + } + + + + static void print_bignum (BIGNUM *bn); + static void print_ec_point (const EC_GROUP *group, BN_CTX *bnctx, EC_POINT *point); + + static inline void rand_zero_free(uint8_t *buff, size_t len) { + if (buff == NULL) return; + RAND_bytes(buff, len); + memset(buff, 0, len); + free(buff); + }; + + /** + * @brief Free an ephemeral context by randomizing, zeroing, and freeing all memory + * + * @param ctx The ephemeral context to free + * @param nonce_len The length of the nonces in the context + * @param digest_len The length of the digests/keys in the context + */ + static inline void free_ephemeral_context(ec_ephemeral_context_t* ctx, int nonce_len, int digest_len) { + + if (ctx->public_init_proto_key) EC_POINT_free(ctx->public_init_proto_key); + if (ctx->public_resp_proto_key) EC_POINT_free(ctx->public_resp_proto_key); + if (ctx->priv_init_proto_key) BN_free(ctx->priv_init_proto_key); + if (ctx->priv_resp_proto_key) BN_free(ctx->priv_resp_proto_key); + if (ctx->E_Id) EC_POINT_free(ctx->E_Id); + if (ctx->m) BN_free(ctx->m); + if (ctx->n) BN_free(ctx->n); + if (ctx->l) BN_free(ctx->l); + if (ctx->i_nonce) rand_zero_free(ctx->i_nonce, nonce_len); + if (ctx->r_nonce) rand_zero_free(ctx->r_nonce, nonce_len); + if (ctx->e_nonce) rand_zero_free(ctx->e_nonce, nonce_len); + if (ctx->c_nonce) rand_zero_free(ctx->c_nonce, nonce_len); + if (ctx->k1) rand_zero_free(ctx->k1, digest_len); + if (ctx->k2) rand_zero_free(ctx->k2, digest_len); + if (ctx->ke) rand_zero_free(ctx->ke, digest_len); + if (ctx->bk) rand_zero_free(ctx->bk, digest_len); + + rand_zero_free((uint8_t *)ctx, sizeof(ec_ephemeral_context_t)); + } + +}; + +#endif // EC_CRYPTO_H \ No newline at end of file diff --git a/inc/ec_ctrl_configurator.h b/inc/ec_ctrl_configurator.h new file mode 100644 index 0000000..d031dc1 --- /dev/null +++ b/inc/ec_ctrl_configurator.h @@ -0,0 +1,42 @@ +#ifndef EC_CTRL_CONFIGURATOR_H +#define EC_CTRL_CONFIGURATOR_H + +#include "ec_configurator.h" + +class ec_ctrl_configurator_t : public ec_configurator_t { +public: + ec_ctrl_configurator_t(std::string mac_addr, send_chirp_func send_chirp_notification, send_encap_dpp_func send_prox_encap_dpp_msg) : + ec_configurator_t(mac_addr, send_chirp_notification, send_prox_encap_dpp_msg) {}; + // No MAC address needed for controller configurator + + /** + * @brief Handle a chirp notification msg tlv and direct to 1905 agent + * + * @param chirp_tlv The chirp TLV to parse and handle + * @param tlv_len The length of the chirp TLV + * @return bool true if successful, false otherwise + */ + bool process_chirp_notification(em_dpp_chirp_value_t* chirp_tlv, uint16_t tlv_len) override; + + /** + * @brief Handle a proxied encapsulated DPP message TLVs (including chirp value) and direct to 1905 agent + * + * @param encap_tlv The 1905 Encap DPP TLV to parse and handle + * @param encap_tlv_len The length of the 1905 Encap DPP TLV + * @param chirp_tlv The DPP Chirp Value TLV to parse and handle (NULL if not present) + * @param chirp_tlv_len The length of the DPP Chirp Value TLV (0 if not present) + * @return bool true if successful, false otherwise + */ + bool process_proxy_encap_dpp_msg(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, em_dpp_chirp_value_t *chirp_tlv, uint16_t chirp_tlv_len) override; + +private: + // Private member variables can be added here + + std::pair create_auth_request(std::string enrollee_mac); + std::pair create_recfg_auth_request(); + std::pair create_auth_confirm(); + std::pair create_recfg_auth_confirm(std::string enrollee_mac); + std::pair create_config_response(); +}; + +#endif // EC_CTRL_CONFIGURATOR_H \ No newline at end of file diff --git a/inc/ec_enrollee.h b/inc/ec_enrollee.h new file mode 100644 index 0000000..cf3165f --- /dev/null +++ b/inc/ec_enrollee.h @@ -0,0 +1,104 @@ +#ifndef EC_ENROLLEE_H +#define EC_ENROLLEE_H + +#include "em_base.h" + +#include +#include + +class ec_enrollee_t { +public: + // TODO: Add Send Action Frame, Send GAS Frame + /** + * @brief The EasyConnect Enrollee + * + * Broadcasts 802.11 presence announcements, handles 802.11 frames from Proxy Agents and sends 802.11 responses to Proxy Agents. + * + * @param mac_addr The MAC address of the device + * + * @note The default state of an enrollee is non-onboarding. All non-controller devices are started as (non-onboarding) enrollees + * until they are told that they are on the network at which point they can be upgraded to a proxy agent. + */ + ec_enrollee_t(std::string mac_addr); + + // Destructor + ~ec_enrollee_t(); + + /** + * @brief Start the EC enrollee onboarding + * + * @param do_reconfig Whether to reconfigure/reauth the enrollee + * @return bool true if successful, false otherwise + */ + bool start(bool do_reconfig); + + /** + * @brief Handle an authentication request 802.11 frame, performing the necessary actions and responding with an authentication response via 802.11 + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + */ + bool handle_auth_request(uint8_t *buff, unsigned int len); + + /** + * @brief Handle an authentication confirmation 802.11 frame, performing the necessary actions + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + */ + bool handle_auth_confirm(uint8_t *buff, unsigned int len); + + /** + * @brief Handle a configuration request 802.11+GAS frame, performing the necessary actions and responding with a configuration result via 802.11 + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + */ + bool handle_config_response(uint8_t *buff, unsigned int len); + + inline std::string get_mac_addr() { return m_mac_addr; }; + + // Disable copy construction and assignment + // Requires use of references or pointers when working with instances of this class + ec_enrollee_t(const ec_enrollee_t&) = delete; + ec_enrollee_t& operator=(const ec_enrollee_t&) = delete; + +private: + + // TODO: Send Action Frame + + // TODO: Send GAS Frame + + /** + * @brief Called when recieving a Authentication Request, + * this function checks that the "Responder" (self) is capable of + * supporting the role indicated by the Initiator's capabilities. + * + * @param caps The capabilities of the Initiator + * @return bool true if the Responder supports the Initiator's capabilities, false otherwise + */ + bool check_supports_init_caps(ec_dpp_capabilities_t caps); + + std::pair create_presence_announcement(); + std::pair create_recfg_presence_announcement(); + std::pair create_auth_response(ec_status_code_t dpp_status); + std::pair create_recfg_auth_response(ec_status_code_t dpp_status); + std::pair create_config_request(); + std::pair create_config_result(); + + ec_persistent_context_t m_p_ctx; + + // Randomized and cleared at the end of the authentication/configuration process + ec_ephemeral_context_t m_eph_ctx; + + ec_data_t m_boot_data; + + std::string m_mac_addr; + + +}; + +#endif // EC_ENROLLEE_H \ No newline at end of file diff --git a/inc/ec_manager.h b/inc/ec_manager.h new file mode 100644 index 0000000..8862c71 --- /dev/null +++ b/inc/ec_manager.h @@ -0,0 +1,138 @@ +#ifndef EC_MANAGER_H +#define EC_MANAGER_H + +#include "ec_configurator.h" +#include "ec_pa_configurator.h" +#include "ec_enrollee.h" + +#include +#include + +class ec_manager_t { +public: + // TODO: Add send_action_frame and send_gas_frame functions + /** + * @brief The Manager (unified dispatcher of sorts) for the EasyConnect Configurator or Enrollee + * + * All non-controller devices are started as (non-onboarding) enrollees until they are told that they are on the network + * at which point they can be upgraded to a proxy agent. + * + * @param mac_addr The MAC address of the device + * @param send_chirp The function to send a chirp notification via 1905 + * @param send_encap_dpp The function to send a proxied encapsulated DPP message via 1905 + * @param m_is_controller Whether the node holding this manager is a controller or not + * + * @note Some method calls are only valid for the controller, proxy agent, or the enrollee, and will return fail if called on the wrong object. + * If the EasyMesh code is correctly implemented this should not be an issue. + * + */ + ec_manager_t(std::string mac_addr, send_chirp_func send_chirp, send_encap_dpp_func send_encap_dpp, bool m_is_controller); + ~ec_manager_t(); + + /** + * @brief Handles DPP action frames directed at this nodes EC manager + * + * @param frame The frame recieved to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + */ + bool handle_recv_ec_action_frame(ec_frame_t* frame, size_t len); + + /** + * @brief Start the EC configurator onboarding + * + * @param data The data to use for onboarding (Parsed DPP URI Data) + * @return bool true if successful, false otherwise + */ + inline bool cfg_start(ec_data_t* data) { + if (!m_is_controller || m_configurator == nullptr) { + return -1; + } + return m_configurator->start(data); + } + + /** + * @brief Start the EC enrollee onboarding + * + * @param do_reconfig Whether to reconfigure/reauth the enrollee + * @return bool true if successful, false otherwise + */ + inline bool enrollee_start(bool do_reconfig) { + if (m_is_controller || m_enrollee == nullptr) { + return -1; + } + return m_enrollee->start(do_reconfig); + } + + /** + * @brief Toggle the presence of CCE (Configurator Connectivity Element) in Proxy Agent beacon and probe response IEs + * + * @param enable Whether to enable or disable CCE presence + * @return bool true if successful, false otherwise + */ + inline bool pa_cfg_toggle_cce(bool enable) { + if (!m_is_controller || m_configurator == nullptr) { + return -1; + } + auto pa_cfg = dynamic_cast(m_configurator.get()); + if (!pa_cfg) { + // Only a proxy agent configurator can toggle CCE + return -1; + } + return pa_cfg->m_toggle_cce(enable); + } + + /** + * @brief Upgrade an enrollee to an onboarded proxy agent. + * Called once m1/m2 exchange verifies the enrollee agent is on the network. + * + * @param toggle_cce The function to call to toggle CCE presence + * @return bool true if successful, false otherwise + */ + bool upgrade_to_onboarded_proxy_agent(toggle_cce_func toggle_cce); + + /** + * @brief Handle a chirp notification TLV and direct to the correct place (802.11 or 1905) + * + * @param chirp_tlv The chirp TLV to parse and handle + * @param tlv_len The length of the chirp TLV + * @return bool true if successful, false otherwise + */ + inline bool process_chirp_notification(em_dpp_chirp_value_t* chirp_tlv, uint16_t tlv_len) { + if (!m_configurator) { + return -1; + } + return m_configurator->process_chirp_notification(chirp_tlv, tlv_len); + } + + /** + * @brief Handle a proxied encapsulated DPP message TLVs (including chirp value) and direct to the correct place (802.11 or 1905) + * + * @param encap_tlv The 1905 Encap DPP TLV to parse and handle + * @param encap_tlv_len The length of the 1905 Encap DPP TLV + * @param chirp_tlv The DPP Chirp Value TLV to parse and handle (NULL if not present) + * @param chirp_tlv_len The length of the DPP Chirp Value TLV (0 if not present) + * @return bool true if successful, false otherwise + */ + inline bool process_proxy_encap_dpp_msg(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, em_dpp_chirp_value_t *chirp_tlv, uint16_t chirp_tlv_len) { + if (!m_configurator) { + return -1; + } + return m_configurator->process_proxy_encap_dpp_msg(encap_tlv, encap_tlv_len, chirp_tlv, chirp_tlv_len); + } + + + +private: + std::unique_ptr m_configurator; + std::unique_ptr m_enrollee; + bool m_is_controller; + + // Used to store the function pointers to instantiate objects again + send_chirp_func m_stored_chirp_fn; + send_encap_dpp_func m_stored_encap_dpp_fn; + toggle_cce_func m_stored_toggle_cce_fn; + std::string m_stored_mac_addr; +}; + +#endif // EC_MANAGER_H \ No newline at end of file diff --git a/inc/ec_pa_configurator.h b/inc/ec_pa_configurator.h new file mode 100644 index 0000000..6c54353 --- /dev/null +++ b/inc/ec_pa_configurator.h @@ -0,0 +1,108 @@ +#ifndef EC_PA_CONFIGURATOR_H +#define EC_PA_CONFIGURATOR_H + +#include "ec_configurator.h" + +#include +#include + +class ec_pa_configurator_t : public ec_configurator_t { +public: + /** + * @brief The Proxy Agent side of the EasyConnect Configurator. + * + * Handles the 802.11 frames from the Enrollee and forwards to the Controller. + * Handles the 1905 frames from the Controller and forwards to the Enrollee. + * + * @param mac_addr The MAC address of the device + * @param send_chirp_notification The function to send a chirp notification via 1905 + * @param send_prox_encap_dpp_msg The function to send a proxied encapsulated DPP message via 1905 + */ + ec_pa_configurator_t(std::string mac_addr, send_chirp_func send_chirp_notification, send_encap_dpp_func send_prox_encap_dpp_msg) : + ec_configurator_t(mac_addr, send_chirp_notification, send_prox_encap_dpp_msg) {}; + + + /** + * @brief Handles a presence announcement 802.11 frame, performing the necessary actions and possibly passing to 1905 + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + bool handle_presence_announcement(uint8_t *buff, unsigned int len) override; + + /** + * @brief Handles an authentication request 802.11 frame, performing the necessary actions and possibly passing to 1905 + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + bool handle_auth_response(uint8_t *buff, unsigned int len) override; + + /** + * @brief Handles an configuration request 802.11+GAS frame, performing the necessary actions and possibly passing to 1905 + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + bool handle_cfg_request(uint8_t *buff, unsigned int len) override; + + /** + * @brief Handles an configuration result 802.11+GAS frame, performing the necessary actions and possibly passing to 1905 + * + * @param buff The frame to handle + * @param len The length of the frame + * @return bool true if successful, false otherwise + * + * @note Optional to implement because the controller+configurator does not handle 802.11, + * but the proxy agent + configurator does. + */ + bool handle_cfg_result(uint8_t *buff, unsigned int len) override; + + /** + * @brief Handle a chirp notification TLV and direct to the correct place (802.11 or 1905) + * + * @param chirp_tlv The chirp TLV to parse and handle + * @param tlv_len The length of the chirp TLV + * @return bool true if successful, false otherwise + */ + bool process_chirp_notification(em_dpp_chirp_value_t* chirp_tlv, uint16_t tlv_len) override; + + /** + * @brief Handle a proxied encapsulated DPP message TLVs (including chirp value) and direct to the correct place (802.11 or 1905) + * + * @param encap_tlv The 1905 Encap DPP TLV to parse and handle + * @param encap_tlv_len The length of the 1905 Encap DPP TLV + * @param chirp_tlv The DPP Chirp Value TLV to parse and handle (NULL if not present) + * @param chirp_tlv_len The length of the DPP Chirp Value TLV (0 if not present) + * @return bool true if successful, false otherwise + */ + bool process_proxy_encap_dpp_msg(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, em_dpp_chirp_value_t *chirp_tlv, uint16_t chirp_tlv_len) override; + +private: + // Private member variables go here + /* + * Map from Chirp Hash to DPP Authentication Request + */ + std::map> m_chirp_hash_frame_map; + /* + * Vector of all cached DPP Reconfiguration Authentication Requests. + * Hash does not matter since it is compared against the Controllers C-sign key + */ + std::vector> m_stored_recfg_auth_frames; +protected: + // Protected member variables and methods go here +}; + +#endif // EC_PA_CONFIGURATOR_H \ No newline at end of file diff --git a/inc/ec_session.h b/inc/ec_session.h deleted file mode 100644 index dee71f1..0000000 --- a/inc/ec_session.h +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2023 Comcast Cable Communications Management, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef EC_SESSION_H -#define EC_SESSION_H - -#include "em_base.h" -#include "ec_base.h" - -#include -#include -#include -#include -#include - -#define EC_FRAME_BASE_SIZE (offsetof(ec_frame_t, attributes)) - -class ec_session_t { - mac_address_t m_enrollee_mac; - unsigned char m_cfgrtr_ver; - unsigned char m_enrollee_ver; - ec_params_t m_params; - wifi_activation_status_t m_activation_status; - ec_data_t m_data; - - /** - * @brief Send a chirp notification to the peer - * - * @param chirp_tlv The chirp TLV to send - * @param len The length of the chirp TLV - * @return int 0 if successful, -1 otherwise - */ - std::function m_send_chirp_notification; - - /** - * @brief Send a proxied encapsulated DPP message - * - * @param encap_dpp_tlv The 1905 Encap DPP TLV to include in the message - * @param encap_dpp_len The length of the 1905 Encap DPP TLV - * @param chirp_tlv The chirp value to include in the message. If NULL, the message will not include a chirp value - * @param chirp_len The length of the chirp value - * @return int 0 if successful, -1 otherwise - */ - std::function m_send_prox_encap_dpp_msg; - - /** - * @brief Compute the hash of the provided key with an optional prefix - * - * @param key The key to hash - * @param digest The buffer to store the hash - * @param prefix The optional prefix to add to the key before hashing (NULL by default) - * @return int The length of the hash - */ - int compute_key_hash(EC_KEY *key, uint8_t *digest, const char *prefix = NULL); - - /** - * @brief Handle a presence announcement frame - * - * @param buff The frame to handle - * @param len The length of the frame - * @return int 0 if successful, -1 otherwise - */ - int handle_pres_ann(uint8_t *buff, unsigned int len); - - - int compute_intermediate_key(bool is_first); - - /** - * @brief Add a wrapped data attribute to a frame - * - * @param frame The frame to use as AAD. Can be NULL if no AAD is needed - * @param frame_attribs The attributes to add the wrapped data attribute to and to use as AAD - * @param non_wrapped_len The length of the non-wrapped attributes (`frame_attribs`, In/Out) - * @param use_aad Whether to use AAD in the encryption - * @param key The key to use for encryption - * @param create_wrap_attribs A function to create the attributes to wrap and their length. Memory is handled by function (see note) - * @return uint8_t* The new frame attributes with the wrapped data attribute added - * - * @note The `create_wrap_attribs` function will allocate heap-memory which is freed inside the `add_wrapped_data_attr` function. - * **The caller should not use statically allocated memory in `create_wrap_attribs` or free the memory returned by `create_wrap_attribs`.** - */ - uint8_t* add_wrapped_data_attr(ec_frame_t *frame, uint8_t* frame_attribs, uint16_t* non_wrapped_len, - bool use_aad, uint8_t* key, std::function()> create_wrap_attribs); - - int hkdf(const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, - uint8_t *salt, int saltlen, uint8_t *info, int infolen, - uint8_t *okm, int okmlen); - -public: - int init_session(ec_data_t* ec_data); - - /** - * @brief Create an authentication request `ec_frame_t` with the necessary attributes - * - * @return std::pair The buffer containing the `ec_frame_t` and the length of the frame - */ - std::pair create_auth_request(); - - /** - * @brief Handle a chirp notification TLV and output the authentication request frame (if necessary) - * - * @param chirp_tlv The chirp TLV to parse and handle - * @param out_frame The buffer to store the output frame (NULL if no frame is needed) - * @return int 0 if successful, -1 otherwise - */ - int handle_chirp_notification(em_dpp_chirp_value_t* chirp_tlv, uint8_t **out_frame); - - /** - * @brief Handle a proxied encapsulated DPP TLV and output the correct frame to send (if necessary) - * - * @param encap_tlv The 1905 Encap DPP TLV to parse and handle - * @param out_frame The buffer to store the output frame (NULL if no frame is needed) - * @return int 0 if successful, -1 otherwise - */ - int handle_proxy_encap_dpp_tlv(em_encap_dpp_t *encap_tlv, uint8_t **out_frame); - - /** - * @brief Create an authentication response frame in a pre-allocated buffer - * - * @param buff The buffer to store the frame - * @return int The length of the frame - */ - int create_auth_rsp(uint8_t *buff); - - /** - * @brief Create an authentication confirmation frame in a pre-allocated buffer - * - * @param buff The buffer to store the frame - * @return int The length of the frame - */ - int create_auth_cnf(uint8_t *buff); - - /** - * @brief Create a presence announcement frame in a pre-allocated buffer - * - * @param buff The buffer to store the frame - * @return int The length of the frame - */ - int create_pres_ann(uint8_t *buff); - - /** - * @brief Handles DPP action frames directed at this nodes ec_session - * - * @param frame The frame recieved to handle - * @param len The length of the frame - * @return int 0 if successful, -1 otherwise - */ - int handle_recv_ec_action_frame(ec_frame_t* frame, size_t len); - - /** - * @brief Construct an EC session - * - * @param send_chirp_notification The function to send a chirp notification - * @param send_prox_encap_dpp_msg The function to send a proxied encapsulated DPP message - */ - ec_session_t( std::function send_chirp_notification, - std::function send_prox_encap_dpp_msg); - ~ec_session_t(); -}; - -#endif diff --git a/inc/ec_util.h b/inc/ec_util.h index 8adc659..f748de7 100644 --- a/inc/ec_util.h +++ b/inc/ec_util.h @@ -18,9 +18,14 @@ #include "em_base.h" #include "ec_base.h" +#include +#include #include #include +#include + +#define EC_FRAME_BASE_SIZE (offsetof(ec_frame_t, attributes)) namespace easyconnect { @@ -42,7 +47,7 @@ namespace easyconnect { {DPP_STATUS_NEW_KEY_NEEDED, "New Key Needed: The Enrollee needs to generate a new Protocol key."} }; -} +}; class ec_util { public: @@ -106,6 +111,38 @@ class ec_util { return add_attrib(buff, buff_len, id, sizeof(uint16_t), (uint8_t *)&val); } + /** + * @brief Heap allocate an EC frame with the default WFA parameters + type + * + * @param type The frame type + * @return ec_frame_t* The heap allocated frame, NULL if failed + * + * @warning The frame must be freed by the caller + */ + static inline ec_frame_t* alloc_frame(ec_frame_type_t type) { + uint8_t* buff = (uint8_t*) calloc(EC_FRAME_BASE_SIZE, 1); + if (buff == NULL) { + printf("%s:%d unable to allocate memory\n", __func__, __LINE__); + return NULL; + } + ec_frame_t *frame = (ec_frame_t *)buff; + init_frame(frame); + frame->frame_type = type; + return frame; + } + + /** + * @brief Copy (overrride) attributes to a frame + * + * @param frame The frame to copy the attributes to + * @param attrs The attributes to copy + * @param attrs_len The length of the attributes + * @return ec_frame_t* The frame with the copied attributes (returned due to realloc) + * + * @warning The frame must be freed by the caller + */ + static ec_frame_t* copy_attrs_to_frame(ec_frame_t *frame, uint8_t *attrs, uint16_t attrs_len); + /** * @brief Validate an EC frame based on the WFA parameters * @@ -125,6 +162,46 @@ class ec_util { return validate_frame(frame) && frame->frame_type == type; } + /** + * @brief Parse a DPP Chirp TLV + * + * @param buff [in] The buffer containing the chirp TLV + * @param chirp_tlv_len [in] The length of the chirp TLV + * @param mac [out] The MAC address to store in the chirp TLV + * @param hash [out] The hash to store in the chirp TLV + * @param hash_len [out] The length of the hash + * @return bool true if successful, false otherwise + */ + static bool parse_dpp_chirp_tlv(em_dpp_chirp_value_t* chirp_tlv, uint16_t chirp_tlv_len, mac_addr_t *mac, uint8_t **hash, uint8_t *hash_len); + + /** + * @brief Parse an Encap DPP TLV + * + * @param encap_tlv [in] The buffer containing the Encap DPP TLV + * @param encap_tlv_len [in] The length of the Encap DPP TLV + * @param dest_mac [out] The destination MAC address (0 if not present) + * @param frame_type [out] The frame type + * @param encap_frame [out] The encapsulated frame + * @param encap_frame_len [out] The length of the encapsulated frame + * @return bool true if successful, false otherwise + */ + static bool parse_encap_dpp_tlv(em_encap_dpp_t* encap_tlv, uint16_t encap_tlv_len, mac_addr_t *dest_mac, uint8_t *frame_type, uint8_t** encap_frame, uint8_t *encap_frame_len); + + /** + * @brief Creates and allocates an Encap DPP TLV + * + * @param dpp_frame_indicator [in] The DPP frame indicator (0 = DPP Public Action frame, 1 = GAS Frame) + * @param content_type [in] The content type + * @param dest_mac [in] The destination MAC address (0 if not present) + * @param frame_type [in] The frame type + * @param encap_frame [in] The encapsulated frame + * @param encap_frame_len [in] The length of the encapsulated frame + * @return em_encap_dpp_t* The heap allocated Encap DPP TLV, NULL if failed + */ + static em_encap_dpp_t * create_encap_dpp_tlv(bool dpp_frame_indicator, uint8_t content_type, + mac_addr_t *dest_mac, uint8_t frame_type, uint8_t *encap_frame, uint8_t encap_frame_len); + + /** * @brief Converts a frequency to a WFA channel attribute format (opclass + channel) * @@ -135,14 +212,65 @@ class ec_util { */ static uint16_t freq_to_channel_attr(unsigned int freq); - static void print_bignum (BIGNUM *bn); - static void print_ec_point (const EC_GROUP *group, BN_CTX *bnctx, EC_POINT *point); - + + /** + * @brief Get the size of an EC attribute + * + * @param data_len The length of the data in the attribute + * @return size_t The size of the attribute + */ static inline size_t get_ec_attr_size(size_t data_len) { return offsetof(ec_attribute_t, data) + data_len; }; + /** + * @brief Get the string representation of a status code + * + * @param status The status code to convert + * @return std::string The string representation of the status code + */ static inline std::string status_code_to_string(ec_status_code_t status) { return easyconnect::status_code_map.at(status); }; + + /** + * @brief Add a wrapped data attribute to a frame + * + * @param frame The frame to use as AAD. Can be NULL if no AAD is needed + * @param frame_attribs The attributes to add the wrapped data attribute to and to use as AAD + * @param non_wrapped_len The length of the non-wrapped attributes (`frame_attribs`, In/Out) + * @param use_aad Whether to use AAD in the encryption + * @param key The key to use for encryption + * @param create_wrap_attribs A function to create the attributes to wrap and their length. Memory is handled by function (see note) + * @return uint8_t* The new frame attributes with the wrapped data attribute added + * + * @note The `create_wrap_attribs` function will allocate heap-memory which is freed inside the `add_wrapped_data_attr` function. + * **The caller should not use statically allocated memory in `create_wrap_attribs` or free the memory returned by `create_wrap_attribs`.** + */ + static uint8_t* add_wrapped_data_attr(ec_frame_t *frame, uint8_t* frame_attribs, uint16_t* non_wrapped_len, + bool use_aad, uint8_t* key, std::function()> create_wrap_attribs); + + + /** + * @brief Unwrap a wrapped data attribute + * + * @param wrapped_attrib The wrapped attribute to unwrap (retreieved using `get_attribute`) + * @param frame The frame to use as AAD. Can be NULL if no AAD is needed + * @param uses_aad Whether the wrapped attribute uses AAD + * @param key The key to use for decryption + * @return std::pair A heap allocated buffer of unwrapped attributes on success which can then be fetched via `get_attribute`, + * along with the length of that buffer. The buffer is NULL and the size is 0 on failure. + * + * @warning The caller is responsible for freeing the memory returned by this function + */ + static std::pair unwrap_wrapped_attrib(ec_attribute_t* wrapped_attrib, ec_frame_t *frame, bool uses_aad, uint8_t* key); + + /** + * @brief Convert a hash to a hex string + * + * @param hash The hash to convert + * @param hash_len The length of the hash + * @return std::string The hex string representation of the hash + */ + static std::string hash_to_hex_string(const uint8_t *hash, size_t hash_len); }; \ No newline at end of file diff --git a/inc/em.h b/inc/em.h index 8db2ff7..d08b9d9 100644 --- a/inc/em.h +++ b/inc/em.h @@ -32,7 +32,6 @@ #include "em_policy_cfg.h" #include "dm_easy_mesh.h" #include "em_sm.h" -#include "ec_session.h" class em_mgr_t; diff --git a/inc/em_base.h b/inc/em_base.h index adf7529..b19ace3 100644 --- a/inc/em_base.h +++ b/inc/em_base.h @@ -611,10 +611,15 @@ typedef struct { unsigned char reserved : 1; unsigned char dpp_frame_indicator : 1; unsigned char content_type : 5; - mac_address_t dest_mac_addr; - unsigned char frame_type; - unsigned short encap_frame_len; - unsigned char encap_frame[0]; +/* + Contains: + - dest_mac_addr (6 bytes, if enrollee_mac_addr_present) + - frame_type (1 byte) + - encap_frame_len (2 bytes) + - encap_frame (encap_frame_len bytes) +*/ + unsigned char data[0]; + }__attribute__((__packed__)) em_encap_dpp_t; typedef struct { diff --git a/inc/em_provisioning.h b/inc/em_provisioning.h index 74d2a0b..b682bba 100644 --- a/inc/em_provisioning.h +++ b/inc/em_provisioning.h @@ -20,7 +20,7 @@ #define EM_PROVISIONING_H #include "em_base.h" -#include "ec_session.h" +#include "ec_manager.h" #include class em_cmd_t; @@ -38,8 +38,6 @@ class em_provisioning_t { int handle_dpp_chirp_notif(uint8_t *buff, unsigned int len); int handle_proxy_encap_dpp(uint8_t *buff, unsigned int len); - int send_chirp_notif_msg(em_dpp_chirp_value_t *chirp, size_t chirp_len); - int send_prox_encap_dpp_msg(em_encap_dpp_t* encap_dpp_tlv, size_t encap_dpp_len, em_dpp_chirp_value_t *chirp, size_t chirp_len); // states void handle_state_prov_none(); void handle_state_prov(); @@ -61,12 +59,16 @@ class em_provisioning_t { virtual int send_cmd(em_cmd_type_t type, em_service_type_t svc, uint8_t *buff, unsigned int len) = 0; virtual em_cmd_t *get_current_cmd() = 0; +protected: + int send_chirp_notif_msg(em_dpp_chirp_value_t *chirp, size_t chirp_len); + int send_prox_encap_dpp_msg(em_encap_dpp_t* encap_dpp_tlv, size_t encap_dpp_len, em_dpp_chirp_value_t *chirp, size_t chirp_len); + public: void process_msg(uint8_t *data, unsigned int len); void process_agent_state(); void process_ctrl_state(); - std::unique_ptr m_ec_session; + std::unique_ptr m_ec_manager; em_provisioning_t(); virtual ~em_provisioning_t(); diff --git a/inc/util.h b/inc/util.h index bc02d23..597ae81 100644 --- a/inc/util.h +++ b/inc/util.h @@ -58,6 +58,21 @@ void add_milliseconds(struct timespec *ts, long milliseconds); char *get_date_time_rfc3399(char *buff, unsigned int len); void print_hex_dump(unsigned int length, uint8_t *buffer, easymesh_dbg_type_t module=EM_STDOUT); +/** + * Converts a MAC address to its string representation + * + * @param mac The MAC address as an array of 6 bytes + * @param delim The delimiter between bytes (default: ":") + * @return The MAC address as a string in the format "XX:XX:XX:XX:XX:XX" + */ +inline std::string mac_to_string(const uint8_t mac[6], const std::string& delim = ":") { + char mac_str[18]; // Max size: 6 bytes * 2 hex chars + 5 delimiters + null terminator + snprintf(mac_str, sizeof(mac_str), "%02x%s%02x%s%02x%s%02x%s%02x%s%02x", + mac[0], delim.c_str(), mac[1], delim.c_str(), mac[2], delim.c_str(), + mac[3], delim.c_str(), mac[4], delim.c_str(), mac[5]); + return std::string(mac_str); +} + /** * em_chan_to_freq - Convert channel info to frequency * @param op_class: Operating class diff --git a/src/agent/em_agent.cpp b/src/agent/em_agent.cpp index 3aa8183..33b6513 100644 --- a/src/agent/em_agent.cpp +++ b/src/agent/em_agent.cpp @@ -338,8 +338,7 @@ void em_agent_t::handle_recv_wfa_action_frame(em_bus_event_t *evt) switch (oui_type) { case DPP_OUI_TYPE: - // TODO: Implement DPP - dest_radio_node->m_ec_session->handle_recv_ec_action_frame(ec_frame, full_action_frame_len); + dest_radio_node->m_ec_manager->handle_recv_ec_action_frame(ec_frame, full_action_frame_len); break; default: break; diff --git a/src/em/em.cpp b/src/em/em.cpp index 7ebfe3b..7428e91 100644 --- a/src/em/em.cpp +++ b/src/em/em.cpp @@ -49,7 +49,7 @@ #include "em.h" #include "em_cmd.h" #include "em_cmd_exec.h" - +#include "util.h" void em_t::orch_execute(em_cmd_t *pcmd) { @@ -95,8 +95,9 @@ void em_t::orch_execute(em_cmd_t *pcmd) if (dpp_info->ec_freqs[i] == 0) break; printf("\t\tFreq: %d\n", dpp_info->ec_freqs[i]); } - - m_ec_session->init_session(dpp_info); + if (!m_ec_manager->cfg_start(dpp_info)){ + printf("Failed to start DPP\n"); + } break; } @@ -1049,6 +1050,21 @@ em_t::em_t(em_interface_t *ruid, em_freq_band_t band, dm_easy_mesh_t *dm, em_mgr RAND_bytes(get_crypto_info()->r_nonce, sizeof(em_nonce_t)); m_data_model = dm; m_mgr = mgr; + + /* + //TODO: Placeholder Lambda function for toggle cce + [this](bool enable) { + printf("Toggle CCE: %s\n", enable ? "true" : "false"); + return 0; // Added return value + }, + */ + std::string mac_address = util::mac_to_string(get_peer_mac()); + m_ec_manager = std::unique_ptr(new ec_manager_t( + mac_address, //TODO: Revisit + std::bind(&em_t::send_chirp_notif_msg, this, std::placeholders::_1, std::placeholders::_2), + std::bind(&em_t::send_prox_encap_dpp_msg, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + get_service_type() == em_service_type_ctrl + )); } em_t::~em_t() diff --git a/src/em/prov/easyconnect/ec_configurator.cpp b/src/em/prov/easyconnect/ec_configurator.cpp new file mode 100644 index 0000000..6bde9f8 --- /dev/null +++ b/src/em/prov/easyconnect/ec_configurator.cpp @@ -0,0 +1,85 @@ +#include "ec_configurator.h" + +ec_configurator_t::ec_configurator_t(std::string mac_addr, send_chirp_func send_chirp_notification, send_encap_dpp_func send_prox_encap_dpp_msg) + : m_mac_addr(mac_addr), m_send_chirp_notification(send_chirp_notification), m_send_prox_encap_dpp_msg(send_prox_encap_dpp_msg) +{ +} + +ec_configurator_t::~ec_configurator_t() +{ +} + +// TODO: Maybe move to controller +bool ec_configurator_t::start(ec_data_t *ec_data) +{ + memset(&m_boot_data, 0, sizeof(ec_data_t)); + memcpy(&m_boot_data, ec_data, sizeof(ec_data_t)); + + const EC_POINT *init_pub_key, *resp_pub_key = NULL; + const BIGNUM *proto_priv; + + resp_pub_key = EC_KEY_get0_public_key(m_boot_data.responder_boot_key); + if (resp_pub_key == NULL) { + printf("%s:%d Could not get responder bootstrap public key\n", __func__, __LINE__); + return false; + } + m_p_ctx.group = EC_KEY_get0_group(m_boot_data.responder_boot_key); + + + m_p_ctx.prime = BN_new(); + m_p_ctx.bn_ctx = BN_CTX_new(); + + if (!m_p_ctx.prime || !m_p_ctx.bn_ctx) { + printf("%s:%d Some BN NULL\n", __func__, __LINE__); + BN_free(m_p_ctx.prime); + BN_CTX_free(m_p_ctx.bn_ctx); + return false; + } + + m_p_ctx.nid = EC_GROUP_get_curve_name(m_p_ctx.group); + + //printf("%s:%d nid: %d\n", __func__, __LINE__, m_p_ctx.nid); + switch (m_p_ctx.nid) { + case NID_X9_62_prime256v1: + m_p_ctx.digest_len = 32; + m_p_ctx.hash_fcn = EVP_sha256(); + break; + case NID_secp384r1: + m_p_ctx.digest_len = 48; + m_p_ctx.hash_fcn = EVP_sha384(); + break; + case NID_secp521r1: + m_p_ctx.digest_len = 64; + m_p_ctx.hash_fcn = EVP_sha512(); + break; + case NID_X9_62_prime192v1: + m_p_ctx.digest_len = 32; + m_p_ctx.hash_fcn = EVP_sha256(); + break; + case NID_secp224r1: + m_p_ctx.digest_len = 32; + m_p_ctx.hash_fcn = EVP_sha256(); + break; + default: + printf("%s:%d nid:%d not handled\n", __func__, __LINE__, m_p_ctx.nid); + return false; + } + + m_p_ctx.nonce_len = m_p_ctx.digest_len*4; + + // Fetch prime + if (EC_GROUP_get_curve_GFp(m_p_ctx.group, m_p_ctx.prime, NULL, NULL, m_p_ctx.bn_ctx) == 0) { + printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); + return false; + } + + printf("Successfully started configurator with params:\n"); + printf("\tMAC: %s\n", m_mac_addr.c_str()); + printf("\tNID: %d\n", m_p_ctx.nid); + printf("\tDigest Length: %d\n", m_p_ctx.digest_len); + printf("\tNonce Length: %d\n", m_p_ctx.nonce_len); + printf("\tPrime (Length: %d):\n", BN_num_bytes(m_p_ctx.prime)); + ec_crypto::print_bignum(m_p_ctx.prime); + + return true; +} diff --git a/src/em/prov/easyconnect/ec_crypto.cpp b/src/em/prov/easyconnect/ec_crypto.cpp index ac8be45..4042ea3 100644 --- a/src/em/prov/easyconnect/ec_crypto.cpp +++ b/src/em/prov/easyconnect/ec_crypto.cpp @@ -1,40 +1,10 @@ -#include "ec_session.h" +#include "ec_crypto.h" #include "em_crypto.h" #include "util.h" -int ec_session_t::compute_intermediate_key(bool is_first) -{ - unsigned int primelen, offset, keylen; - - - BIGNUM *x = is_first ? m_params.m : m_params.n; - const char *info = is_first ? "first intermediate key" : "second intermediate key"; - - // The key to store - uint8_t *key = is_first ? m_params.k1 : m_params.k2; - - primelen = BN_num_bytes(m_params.prime); - - uint8_t m[2048]; - memset(m, 0, primelen); - - offset = primelen - BN_num_bytes(x); - - BN_bn2bin(x, m + offset); - if ((keylen = hkdf(m_params.hashfcn, 0, m, primelen, NULL, 0, - (uint8_t *)info, strlen(info), - key, m_params.digestlen)) == 0) { - printf("%s:%d: Failed in hashing\n", __func__, __LINE__); - return -1; - } - - printf("Key:\n"); - util::print_hex_dump(m_params.digestlen, key); - - return 0; -} - -int ec_session_t::compute_key_hash(EC_KEY *key, uint8_t *digest, const char *prefix) +// TODO: "might" need to switch over to using hash_len instead of SHA256_DIGEST_LENGTH +// although SHA_256 might be standardized for some operations +uint8_t* ec_crypto::compute_key_hash(const EC_KEY *key, const char *prefix) { BIO *bio; uint8_t *asn1; @@ -44,7 +14,7 @@ int ec_session_t::compute_key_hash(EC_KEY *key, uint8_t *digest, const char *pre // Setup the BIO for key conversion if ((bio = BIO_new(BIO_s_mem())) == NULL) { - return -1; + return NULL; } // Convert key to DER format @@ -59,32 +29,193 @@ int ec_session_t::compute_key_hash(EC_KEY *key, uint8_t *digest, const char *pre len[1] = asn1len; // Call platform_SHA256 with our two elements + uint8_t *digest = (uint8_t *)calloc(SHA256_DIGEST_LENGTH, 1); uint8_t result = em_crypto_t::platform_SHA256(2, addr, len, digest); BIO_free(bio); if (result == 0) { - return -1; + free(digest); + return NULL; + } + + return digest; +} + +/** + * @brief Abstracted HKDF computation that handles both simple and complex inputs + * + * This function provides a unified interface for HKDF computations, handling both + * simple single-input operations and more complex operations with multiple inputs. + * It properly formats BIGNUMs with appropriate padding based on prime length. + * + * @param key_out Buffer to store the output key (must be pre-allocated) + * @param key_out_len Length of the output key + * @param info_str Information string for HKDF + * @param x_val_inputs Array of BIGNUMs to use as IKM (Concatenated if > 1) + * @param x_val_count Number of BIGNUMs in the array + * @param raw_salt Raw salt buffer (can be NULL) + * @param raw_salt_len Length of raw salt buffer + * + * @return Length of the output key on success, 0 on failure + */ +int ec_crypto::compute_hkdf_key(ec_persistent_context_t& p_ctx, uint8_t *key_out, int key_out_len, const char *info_str, + const BIGNUM **x_val_inputs, int x_val_count, + uint8_t *raw_salt, int raw_salt_len) +{ + unsigned int primelen = 0; + uint8_t *bn_buffer = NULL; + uint8_t *ikm = NULL; + int ikm_len = 0; + int result = 0; + + // Calculate prime length for padding and format BIGNUMs + primelen = BN_num_bytes(p_ctx.prime); + bn_buffer = (uint8_t *)malloc(primelen * x_val_count); + if (bn_buffer == NULL) { + perror("malloc"); + return 0; + } + memset(bn_buffer, 0, primelen * x_val_count); + + // Format each X Val BIGNUM with proper padding + for (int i = 0; i < x_val_count; i++) { + if (x_val_inputs[i] != NULL) { + unsigned int offset = primelen - BN_num_bytes(x_val_inputs[i]); + BN_bn2bin(x_val_inputs[i], bn_buffer + (i * primelen) + offset); + } + } + + // Use formatted BIGNUMs as IKM + ikm = bn_buffer; + ikm_len = primelen * x_val_count; + + // Call the hkdf function + result = hkdf(p_ctx.hash_fcn, 0, ikm, ikm_len, raw_salt, raw_salt_len, + (uint8_t *)info_str, strlen(info_str), + key_out, key_out_len); + + // Free allocated memory + if (bn_buffer != NULL) { + free(bn_buffer); + } + + return result; +} + + +BIGNUM* ec_crypto::calculate_Lx(ec_persistent_context_t& p_ctx, const BIGNUM* bR, const BIGNUM* pR, const EC_POINT* BI) +{ + EC_POINT* L = NULL; + BIGNUM *sum, *order, *L_x = NULL; + int success = 0; + + // Get the order of the curve (q) + if (!(order = BN_new()) || !EC_GROUP_get_order(p_ctx.group, order, p_ctx.bn_ctx)) + goto cleanup; + + // Calculate (bR + pR) mod q + if (!(sum = BN_new()) || !BN_mod_add(sum, bR, pR, order, p_ctx.bn_ctx)) + goto cleanup; + + // Create result point L + if (!(L = EC_POINT_new(p_ctx.group))) + goto cleanup; + + // Calculate L = sum * BI (point multiplication) + if (!EC_POINT_mul(p_ctx.group, L, NULL, BI, sum, p_ctx.bn_ctx)) + goto cleanup; + + if (EC_POINT_get_affine_coordinates_GFp(p_ctx.group, L, L_x, NULL, p_ctx.bn_ctx) == 0) + success = 1; + +cleanup: + if (sum) BN_free(sum); + if (order) BN_free(order); + + if (!success && L_x) { + BN_free(L_x); + L_x = NULL; + } + + return L_x; +} + +// TODO: Might remove, might keep, unsure +/** + * @brief Compute ke using nonces and coordinate values + * + * @param ke_buffer Buffer to store ke (must be pre-allocated) + * @param I_nonce Initiator nonce + * @param I_nonce_len Length of initiator nonce + * @param R_nonce Responder nonce + * @param R_nonce_len Length of responder nonce + * @param include_L Whether to include L.x for mutual authentication + * @return Length of ke on success, 0 on failure + */ +int ec_crypto::compute_ke(ec_persistent_context_t& p_ctx, ec_ephemeral_context_t e_ctx, uint8_t *ke_buffer) +{ + // Create concatenated nonces buffer (Initiator Nonce | Responder Nonce) + int total_nonce_len = p_ctx.nonce_len * 2; + uint8_t *nonces = (uint8_t *)calloc(total_nonce_len, 1); + if (nonces == NULL) { + printf("%s:%d: Failed to allocate memory for nonces\n", __func__, __LINE__); + return 0; + } + + // Copy nonces + memcpy(nonces, e_ctx.i_nonce, p_ctx.nonce_len); + memcpy(nonces + p_ctx.nonce_len, e_ctx.r_nonce, p_ctx.nonce_len); + + // Set up BIGNUM array of X values (M, N, and possibly L if mutual auth) + int x_count = e_ctx.is_mutual_auth ? 3 : 2; + const BIGNUM **x_val_array = (const BIGNUM **)calloc(x_count, sizeof(BIGNUM *)); + if (x_val_array == NULL) { + free(nonces); + printf("%s:%d: Failed to allocate memory for X values\n", __func__, __LINE__); + return 0; } - return SHA256_DIGEST_LENGTH; + x_val_array[0] = e_ctx.m; // M.x + x_val_array[1] = e_ctx.n; // N.x + if (e_ctx.is_mutual_auth) { + x_val_array[2] = e_ctx.l; // L.x if doing mutual auth + } + + // Compute the key + int result = compute_hkdf_key( + p_ctx, + ke_buffer, + p_ctx.digest_len, + "DPP Key", + x_val_array, // Concatenated X Vals for IKM + x_count, + nonces, // Concatenated Nonces as salt + total_nonce_len + ); + + // Free allocated memory + free(nonces); + free(x_val_array); + + return result; } -int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, +int ec_crypto::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, uint8_t *salt, int saltlen, uint8_t *info, int infolen, uint8_t *okm, int okmlen) { uint8_t *prk, *tweak, ctr, *digest; int len; - unsigned int digestlen, prklen, tweaklen; + unsigned int digest_len, prklen, tweaklen; #if OPENSSL_VERSION_NUMBER < 0x10100000L HMAC_CTX ctx; #else HMAC_CTX *ctx = HMAC_CTX_new(); #endif - digestlen = prklen = EVP_MD_size(h); - if ((digest = (uint8_t *)malloc(digestlen)) == NULL) { + digest_len = prklen = EVP_MD_size(h); + if ((digest = (uint8_t *)malloc(digest_len)) == NULL) { perror("malloc"); return 0; } @@ -98,7 +229,7 @@ int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, /* * if !skip then do HKDF-extract */ - if ((prk = (uint8_t *)malloc(digestlen)) == NULL) { + if ((prk = (uint8_t *)malloc(digest_len)) == NULL) { free(digest); perror("malloc"); return 0; @@ -107,13 +238,13 @@ int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, * if there's no salt then use all zeros */ if (!salt || (saltlen == 0)) { - if ((tweak = (uint8_t *)malloc(digestlen)) == NULL) { + if ((tweak = (uint8_t *)malloc(digest_len)) == NULL) { free(digest); free(prk); perror("malloc"); return 0; } - memset(tweak, 0, digestlen); + memset(tweak, 0, digest_len); tweaklen = saltlen; } else { tweak = salt; @@ -127,8 +258,8 @@ int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, prk = ikm; prklen = ikmlen; } - memset(digest, 0, digestlen); - digestlen = 0; + memset(digest, 0, digest_len); + digest_len = 0; ctr = 0; len = 0; while (len < okmlen) { @@ -140,10 +271,10 @@ int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, ctr++; #if OPENSSL_VERSION_NUMBER < 0x10100000L HMAC_Init_ex(&ctx, prk, prklen, h, NULL); - HMAC_Update(&ctx, digest, digestlen); + HMAC_Update(&ctx, digest, digest_len); #else HMAC_Init_ex(ctx, prk, prklen, h, NULL); - HMAC_Update(ctx, digest, digestlen); + HMAC_Update(ctx, digest, digest_len); #endif if (info && (infolen != 0)) { #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -154,22 +285,22 @@ int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, } #if OPENSSL_VERSION_NUMBER < 0x10100000L HMAC_Update(&ctx, &ctr, sizeof(uint8_t)); - HMAC_Final(&ctx, digest, &digestlen); + HMAC_Final(&ctx, digest, &digest_len); #else HMAC_Update(ctx, &ctr, sizeof(uint8_t)); - HMAC_Final(ctx, digest, &digestlen); + HMAC_Final(ctx, digest, &digest_len); #endif - if ((len + digestlen) > okmlen) { + if ((len + digest_len) > okmlen) { memcpy(okm + len, digest, okmlen - len); } else { - memcpy(okm + len, digest, digestlen); + memcpy(okm + len, digest, digest_len); } #if OPENSSL_VERSION_NUMBER < 0x10100000L HMAC_CTX_cleanup(&ctx); #else HMAC_CTX_free(ctx); #endif - len += digestlen; + len += digest_len; } if (!skip) { free(prk); @@ -183,3 +314,50 @@ int ec_session_t::hkdf (const EVP_MD *h, int skip, uint8_t *ikm, int ikmlen, return okmlen; } + +void ec_crypto::print_bignum (BIGNUM *bn) +{ + unsigned char *buf; + int len; + + len = BN_num_bytes(bn); + if ((buf = (unsigned char *)malloc(len)) == NULL) { + printf("Could not print bignum\n"); + return; + } + BN_bn2bin(bn, buf); + util::print_hex_dump(len, buf); + free(buf); +} + +void ec_crypto::print_ec_point (const EC_GROUP *group, BN_CTX *bnctx, EC_POINT *point) +{ + BIGNUM *x = NULL, *y = NULL; + + if ((x = BN_new()) == NULL) { + printf("%s:%d:Could not print ec_point\n", __func__, __LINE__); + return; + } + + if ((y = BN_new()) == NULL) { + BN_free(x); + printf("%s:%d:Could not print ec_point\n", __func__, __LINE__); + return; + } + + if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) == 0) { + BN_free(y); + BN_free(x); + printf("%s:%d:Could not print ec_point\n", __func__, __LINE__); + return; + + } + + printf("POINT.x:\n"); + print_bignum(x); + printf("POINT.y:\n"); + print_bignum(y); + + BN_free(y); + BN_free(x); +} \ No newline at end of file diff --git a/src/em/prov/easyconnect/ec_ctrl_configurator.cpp b/src/em/prov/easyconnect/ec_ctrl_configurator.cpp new file mode 100644 index 0000000..bf8f510 --- /dev/null +++ b/src/em/prov/easyconnect/ec_ctrl_configurator.cpp @@ -0,0 +1,372 @@ +#include "ec_ctrl_configurator.h" + +#include "ec_base.h" +#include "ec_util.h" +#include "util.h" + +bool ec_ctrl_configurator_t::process_chirp_notification(em_dpp_chirp_value_t *chirp_tlv, uint16_t tlv_len) +{ + + mac_addr_t mac = {0}; + uint8_t hash[255] = {0}; // Max hash length to avoid dynamic allocation + uint8_t hash_len = 0; + + if (ec_util::parse_dpp_chirp_tlv(chirp_tlv, tlv_len, &mac, (uint8_t**)&hash, &hash_len) < 0) { + printf("%s:%d: Failed to parse DPP Chirp TLV\n", __func__, __LINE__); + return false; + } + + // Validate hash + // Compute the hash of the responder boot key + uint8_t *resp_boot_key_chirp_hash = ec_crypto::compute_key_hash(m_boot_data.responder_boot_key, "chirp"); + if (resp_boot_key_chirp_hash == NULL) { + printf("%s:%d unable to compute \"chirp\" responder bootstrapping key hash\n", __func__, __LINE__); + return false; + } + + if (memcmp(hash, resp_boot_key_chirp_hash, hash_len) != 0) { + // Hashes don't match, don't initiate DPP authentication + printf("%s:%d: Chirp notification hash and DPP URI hash did not match! Stopping DPP!\n", __func__, __LINE__); + free(resp_boot_key_chirp_hash); + return false; + } + + free(resp_boot_key_chirp_hash); + std::string mac_str = util::mac_to_string(mac); + if (m_connections.find(mac_str) == m_connections.end()) { + // New connection context + ec_connection_context_t conn_ctx; + m_connections[mac_str] = conn_ctx; + } + auto [auth_frame, auth_frame_len] = create_auth_request(mac_str); + if (auth_frame == NULL || auth_frame_len == 0) { + printf("%s:%d: Failed to create authentication request frame\n", __func__, __LINE__); + return false; + } + + // Create Auth Request Encap TLV: EasyMesh 5.3.4 + em_encap_dpp_t* encap_dpp_tlv = ec_util::create_encap_dpp_tlv(0, 0, &mac, 0, auth_frame, auth_frame_len); + if (encap_dpp_tlv == NULL) { + printf("%s:%d: Failed to create Encap DPP TLV\n", __func__, __LINE__); + return false; + } + + free(auth_frame); + + // Create Auth Request Chirp TLV: EasyMesh 5.3.4 + size_t data_size = sizeof(mac_addr_t) + hash_len + sizeof(uint8_t); + em_dpp_chirp_value_t* chirp = (em_dpp_chirp_value_t*)calloc(sizeof(em_dpp_chirp_value_t) + data_size, 1); + if (chirp == NULL) { + printf("%s:%d: Failed to allocate memory for chirp TLV\n", __func__, __LINE__); + free(encap_dpp_tlv); + return false; + } + chirp->mac_present = 1; + chirp->hash_valid = 1; + + uint8_t* tmp = chirp->data; + memcpy(tmp, mac, sizeof(mac_addr_t)); + tmp += sizeof(mac_addr_t); + + *tmp = hash_len; + tmp++; + + memcpy(tmp, hash, hash_len); + + // Send the encapsulated DPP message (with Encap TLV and Chirp TLV) + this->m_send_prox_encap_dpp_msg(encap_dpp_tlv, sizeof(em_encap_dpp_t) + auth_frame_len, chirp, sizeof(em_dpp_chirp_value_t) + data_size); + + free(encap_dpp_tlv); + free(chirp); + + return true; +} + +bool ec_ctrl_configurator_t::process_proxy_encap_dpp_msg(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, em_dpp_chirp_value_t *chirp_tlv, uint16_t chirp_tlv_len) +{ + if (encap_tlv == NULL || encap_tlv_len == 0) { + printf("%s:%d: Encap DPP TLV is empty\n", __func__, __LINE__); + return false; + } + + + mac_addr_t dest_mac = {0}; + uint8_t frame_type = 0; + uint8_t* encap_frame = NULL; + uint8_t encap_frame_len = 0; + + if (ec_util::parse_encap_dpp_tlv(encap_tlv, encap_tlv_len, &dest_mac, &frame_type, &encap_frame, &encap_frame_len) < 0) { + printf("%s:%d: Failed to parse Encap DPP TLV\n", __func__, __LINE__); + return false; + } + + mac_addr_t chirp_mac = {0}; + uint8_t chirp_hash[255] = {0}; // Max hash length to avoid dynamic allocation + uint8_t chirp_hash_len = 0; + + ec_frame_type_t ec_frame_type = (ec_frame_type_t)frame_type; + switch (ec_frame_type) { + case ec_frame_type_recfg_announcement: { + auto [recfg_auth_frame, recfg_auth_frame_len] = create_recfg_auth_request(); + if (recfg_auth_frame == NULL || recfg_auth_frame_len == 0) { + printf("%s:%d: Failed to create reconfiguration authentication request frame\n", __func__, __LINE__); + return false; + } + em_encap_dpp_t* encap_dpp_tlv = ec_util::create_encap_dpp_tlv(0, 0, &dest_mac, ec_frame_type_recfg_auth_req, recfg_auth_frame, recfg_auth_frame_len); + if (encap_dpp_tlv == NULL) { + printf("%s:%d: Failed to create Encap DPP TLV\n", __func__, __LINE__); + free(recfg_auth_frame); + return false; + } + free(recfg_auth_frame); + // Send the encapsulated ReCfg Auth Request message (with Encap TLV) + // TODO: SEND TO ALL AGENTS + this->m_send_prox_encap_dpp_msg(encap_dpp_tlv, sizeof(em_encap_dpp_t) + recfg_auth_frame_len, NULL, 0); + free(encap_dpp_tlv); + break; + } + case ec_frame_type_auth_rsp: { + break; + } + case ec_frame_type_recfg_auth_rsp: { + + break; + } + case ec_frame_type_auth_cnf: + case ec_frame_type_recfg_auth_cnf: { + break; + } + + default: + printf("%s:%d: Encap DPP frame type (%d) not handled\n", __func__, __LINE__, ec_frame_type); + break; + } + // Parse out dest STA mac address and hash value then validate against the hash in the + // ec_session dpp uri info public key. + // Then construct an Auth request frame and send back in an Encap message + return true; +} + +std::pair ec_ctrl_configurator_t::create_auth_request(std::string enrollee_mac) +{ + + EC_KEY *responder_boot_key, *initiator_boot_key; + + ec_dpp_capabilities_t caps = {{ + .enrollee = 0, + .configurator = 1 + }}; + + printf("%s:%d Enter\n", __func__, __LINE__); + + ec_frame_t *frame = ec_util::alloc_frame(ec_frame_type_auth_req); + if (frame == NULL) { + printf("%s:%d failed to allocate memory for frame\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + auto e_ctx = get_eph_ctx(enrollee_mac); + if (!e_ctx) return std::make_pair(NULL, 0); + // Start EasyConnect 6.3.2 + + // Generate initiator nonce + RAND_bytes(e_ctx->i_nonce, m_p_ctx.nonce_len); + + // Generate initiator protocol key pair (p_i/P_I) + auto [priv_init_proto_key, pub_init_proto_key] = ec_crypto::generate_proto_keypair(m_p_ctx); + if (priv_init_proto_key == NULL || pub_init_proto_key == NULL) { + printf("%s:%d failed to generate initiator protocol key pair\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + e_ctx->priv_init_proto_key = (BIGNUM*)priv_init_proto_key; + e_ctx->public_init_proto_key = (EC_POINT*)pub_init_proto_key; + + // Compute the M.x + const EC_POINT *pub_resp_boot_key = EC_KEY_get0_public_key(m_boot_data.responder_boot_key); + if (pub_resp_boot_key == NULL) { + printf("%s:%d failed to get responder bootstrapping public key\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + e_ctx->m = ec_crypto::compute_ec_ss_x(m_p_ctx, e_ctx->priv_init_proto_key, pub_resp_boot_key); + const BIGNUM *bn_inputs[1] = { e_ctx->m }; + // Compute the "first intermediate key" (k1) + if (ec_crypto::compute_hkdf_key(m_p_ctx, e_ctx->k1, m_p_ctx.digest_len, "first intermediate key", bn_inputs, 1, NULL, 0) == 0) { + printf("%s:%d: Failed to compute k1\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + printf("Key K_1:\n"); + util::print_hex_dump(m_p_ctx.digest_len, e_ctx->k1); + + + + uint8_t* attribs = NULL; + uint16_t attrib_len = 0; + + // Responder Bootstrapping Key Hash: SHA-256(B_R) + uint8_t* responder_keyhash = ec_crypto::compute_key_hash(m_boot_data.responder_boot_key); + if (responder_keyhash == NULL) { + printf("%s:%d failed to compute responder bootstrapping key hash\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_resp_bootstrap_key_hash, SHA256_DIGEST_LENGTH, responder_keyhash); + free(responder_keyhash); + + // Initiator Bootstrapping Key Hash: SHA-256(B_I) + uint8_t* initiator_keyhash = ec_crypto::compute_key_hash(m_boot_data.initiator_boot_key); + if (initiator_keyhash == NULL) { + printf("%s:%d failed to compute initiator bootstrapping key hash\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_init_bootstrap_key_hash, SHA256_DIGEST_LENGTH, initiator_keyhash); + free(initiator_keyhash); + + // Public Initiator Protocol Key: P_I + uint8_t* protocol_key_buff = ec_crypto::encode_proto_key(m_p_ctx, e_ctx->public_init_proto_key); + if (protocol_key_buff == NULL) { + printf("%s:%d failed to encode initiator protocol key\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_init_proto_key, 2*BN_num_bytes(m_p_ctx.prime), protocol_key_buff); + free(protocol_key_buff); + + // Protocol Version + // if (m_cfgrtr_ver > 1) { + // attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_proto_version, m_cfgrtr_ver); + // } + + // Channel Attribute (optional) + //TODO: REVISIT THIS + if (m_boot_data.ec_freqs[0] != 0){ + int base_freq = m_boot_data.ec_freqs[0]; + uint16_t chann_attr = ec_util::freq_to_channel_attr(base_freq); + attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_channel, sizeof(uint16_t), (uint8_t *)&chann_attr); + } + + + // Wrapped Data (with Initiator Nonce and Initiator Capabilities) + // EasyMesh 8.2.2 Table 36 + attribs = ec_util::add_wrapped_data_attr(frame, attribs, &attrib_len, true, e_ctx->k1, [&](){ + uint8_t* wrap_attribs = NULL; + uint16_t wrapped_len = 0; + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_init_nonce, m_p_ctx.nonce_len, e_ctx->i_nonce); + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_init_caps, caps.byte); + return std::make_pair(wrap_attribs, wrapped_len); + }); + + // Add attributes to the frame + if (!(frame = ec_util::copy_attrs_to_frame(frame, attribs, attrib_len))) { + printf("%s:%d unable to copy attributes to frame\n", __func__, __LINE__); + free(attribs); + return std::make_pair(NULL, 0); + } + + free(attribs); + + return std::make_pair((uint8_t*)frame, EC_FRAME_BASE_SIZE + attrib_len); + +} + +std::pair ec_ctrl_configurator_t::create_recfg_auth_request() +{ + return std::pair(); +} + +std::pair ec_ctrl_configurator_t::create_auth_confirm() +{ + ec_frame_t *frame = ec_util::alloc_frame(ec_frame_type_auth_cnf); + if (frame == NULL) { + printf("%s:%d failed to allocate memory for frame\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + uint8_t* attribs = NULL; + uint16_t attrib_len = 0; + + // TODO: Move DPP status outside + ec_status_code_t dpp_status = DPP_STATUS_OK; // TODO + + // uint8_t* key = (dpp_status == DPP_STATUS_OK ? m_params.k2 : m_params.ke); + + // attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_dpp_status, (uint8_t)dpp_status); + // attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_resp_bootstrap_key_hash, sizeof(m_params.responder_keyhash), m_params.responder_keyhash); + // // Conditional (Only included for mutual authentication) + // if (m_params.mutual) { + // attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_init_bootstrap_key_hash, sizeof(m_params.initiator_keyhash), m_params.initiator_keyhash); + // } + + // attribs = ec_util::add_wrapped_data_attr(frame, attribs, &attrib_len, true, key, [&](){ + // uint8_t* wrap_attribs = NULL; + // uint16_t wrapped_len = 0; + // if (dpp_status == DPP_STATUS_OK) { + // wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_init_auth_tag, sizeof(m_params.iauth), m_params.iauth); + // } else { + // wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_resp_nonce, m_params.nonce_len, m_params.responder_nonce); + // } + // return std::make_pair(wrap_attribs, wrapped_len); + // }); + + if (!(frame = ec_util::copy_attrs_to_frame(frame, attribs, attrib_len))) { + printf("%s:%d unable to copy attributes to frame\n", __func__, __LINE__); + free(attribs); + return std::make_pair(NULL, 0); + } + free(attribs); + + return std::make_pair((uint8_t*)frame, EC_FRAME_BASE_SIZE + attrib_len); +} + +std::pair ec_ctrl_configurator_t::create_recfg_auth_confirm(std::string enrollee_mac) +{ + ec_frame_t *frame = ec_util::alloc_frame(ec_frame_type_recfg_auth_cnf); + if (frame == NULL) { + printf("%s:%d failed to allocate memory for frame\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + uint8_t* attribs = NULL; + uint16_t attrib_len = 0; + + // TODO: Move DPP status outside + ec_status_code_t dpp_status = DPP_STATUS_OK; // TODO + + // TODO: Add transaction ID outside this function + uint8_t trans_id = 0; + ec_dpp_reconfig_flags_t reconfig_flags = { + .connector_key = 1, // DONT REUSE + }; + + auto e_ctx = get_eph_ctx(enrollee_mac); + if (!e_ctx) return std::make_pair(NULL, 0); + + attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_dpp_status, (uint8_t)dpp_status); + + attribs = ec_util::add_wrapped_data_attr(frame, attribs, &attrib_len, false, e_ctx->ke, [&](){ + uint8_t* wrap_attribs = NULL; + uint16_t wrapped_len = 0; + + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_trans_id, trans_id); + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_proto_version, (uint8_t)m_boot_data.version); + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_config_nonce, m_p_ctx.nonce_len, e_ctx->i_nonce); + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_enrollee_nonce, m_p_ctx.nonce_len, e_ctx->e_nonce); + wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_reconfig_flags, sizeof(reconfig_flags), (uint8_t*)&reconfig_flags); + + return std::make_pair(wrap_attribs, wrapped_len); + }); + + if (!(frame = ec_util::copy_attrs_to_frame(frame, attribs, attrib_len))) { + printf("%s:%d unable to copy attributes to frame\n", __func__, __LINE__); + free(attribs); + return std::make_pair(NULL, 0); + } + free(attribs); + + return std::make_pair((uint8_t*)frame, EC_FRAME_BASE_SIZE + attrib_len); +} + +std::pair ec_ctrl_configurator_t::create_config_response() +{ + return std::pair(); +} diff --git a/src/em/prov/easyconnect/ec_enrollee.cpp b/src/em/prov/easyconnect/ec_enrollee.cpp new file mode 100644 index 0000000..39490ea --- /dev/null +++ b/src/em/prov/easyconnect/ec_enrollee.cpp @@ -0,0 +1,234 @@ +#include "ec_enrollee.h" + +#include "ec_util.h" +#include "ec_crypto.h" +#include "util.h" + +ec_enrollee_t::ec_enrollee_t(std::string mac_addr) : m_mac_addr(mac_addr) +{ +} + +ec_enrollee_t::~ec_enrollee_t() +{ +} + +bool ec_enrollee_t::start(bool do_reconfig) +{ + return true; +} + +bool ec_enrollee_t::handle_auth_request(uint8_t *buff, unsigned int len) +{ + ec_frame_t *frame = (ec_frame_t *)buff; + + size_t attrs_len = len - EC_FRAME_BASE_SIZE; + + ec_attribute_t *B_r_hash_attr = ec_util::get_attrib(frame->attributes, attrs_len, ec_attrib_id_resp_bootstrap_key_hash); + if (!B_r_hash_attr) return false; + + uint8_t* responder_keyhash = ec_crypto::compute_key_hash(m_boot_data.responder_boot_key); + + if (memcmp(B_r_hash_attr->data, responder_keyhash, B_r_hash_attr->length) != 0) { + printf("%s:%d Responder key hash mismatch\n", __func__, __LINE__); + return false; + } + free(responder_keyhash); + + ec_attribute_t *B_i_hash_attr = ec_util::get_attrib(frame->attributes, attrs_len, ec_attrib_id_init_bootstrap_key_hash); + if (!B_i_hash_attr) return false; + + if (m_boot_data.initiator_boot_key != NULL){ + // Initiator bootstrapping key is present on enrollee, mutual authentication is possible + uint8_t* initiator_keyhash = ec_crypto::compute_key_hash(m_boot_data.initiator_boot_key); + if (initiator_keyhash != NULL) { + if (memcmp(B_i_hash_attr->data, initiator_keyhash, B_i_hash_attr->length) == 0) { + printf("%s:%d Initiator key hash matched, mutual authentication can now occur\n", __func__, __LINE__); + // Hashes match, mutual authentication can occur + m_eph_ctx.is_mutual_auth = true; + /* + Specifically, the Responder shall request mutual authentication when the hash of the Responder + bootstrapping key in the authentication request indexes an entry in the bootstrapping table corresponding to a + bidirectional bootstrapping method, for example, PKEX or BTLE. + */ + } + free(initiator_keyhash); + } + } + + ec_attribute_t *channel_attr = ec_util::get_attrib(frame->attributes, attrs_len, ec_attrib_id_channel); + if (channel_attr && channel_attr->length == sizeof(uint16_t)) { + /* + the Responder determines whether it can use the requested channel for the +following exchanges. If so, it sends the DPP Authentication Response frame on that channel. If not, it discards the DPP +Authentication Request frame without replying to it. + */ + uint16_t op_chan = *(uint16_t*)channel_attr->data; + printf("%s:%d Channel attribute: %d\n", __func__, __LINE__, op_chan); + + uint8_t op_class = (uint8_t)(op_chan >> 8); + uint8_t channel = (uint8_t)(op_chan & 0x00ff); + printf("%s:%d op_class: %d channel %d\n", __func__, __LINE__, op_class, channel); + //TODO: Check One-Wifi for channel selection if possible + // Maybe just attempt to send it on the channel + } + + ec_attribute_t *pub_init_proto_key_attr = ec_util::get_attrib(frame->attributes, attrs_len, ec_attrib_id_init_proto_key); + if (!pub_init_proto_key_attr) { + printf("%s:%d No public initiator protocol key attribute found\n", __func__, __LINE__); + return false; + } + if (pub_init_proto_key_attr->length != BN_num_bytes(m_p_ctx.prime) * 2){ + printf("%s:%d Invalid public initiator protocol key length\n", __func__, __LINE__); + return false; + } + if (m_eph_ctx.public_init_proto_key) { + EC_POINT_free(m_eph_ctx.public_init_proto_key); + } + + m_eph_ctx.public_init_proto_key = ec_crypto::decode_proto_key(m_p_ctx, pub_init_proto_key_attr->data); + + // START Crypto in EasyConnect 6.3.3 + // Compute the M.x + const BIGNUM *priv_resp_boot_key = EC_KEY_get0_private_key(m_boot_data.responder_boot_key); + if (priv_resp_boot_key == NULL) { + printf("%s:%d failed to get responder bootstrapping private key\n", __func__, __LINE__); + return false; + } + m_eph_ctx.m = ec_crypto::compute_ec_ss_x(m_p_ctx, priv_resp_boot_key, m_eph_ctx.public_init_proto_key); + const BIGNUM *bn_inputs[1] = { m_eph_ctx.m }; + // Compute the "first intermediate key" (k1) + if (ec_crypto::compute_hkdf_key(m_p_ctx, m_eph_ctx.k1, m_p_ctx.digest_len, "first intermediate key", bn_inputs, 1, NULL, 0) == 0) { + printf("%s:%d: Failed to compute k1\n", __func__, __LINE__); + return false; + } + + printf("Key K_1:\n"); + util::print_hex_dump(m_p_ctx.digest_len, m_eph_ctx.k1); + + ec_attribute_t *wrapped_data_attr = ec_util::get_attrib(frame->attributes, attrs_len, ec_attrib_id_wrapped_data); + if (!wrapped_data_attr) { + printf("%s:%d No wrapped data attribute found\n", __func__, __LINE__); + return false; + } + + // Attempt to unwrap the wrapped data with generated k1 (from sent keys) + auto [wrapped_data, wrapped_len] = ec_util::unwrap_wrapped_attrib(wrapped_data_attr, frame, false, m_eph_ctx.k1); + if (wrapped_data == NULL || wrapped_len == 0) { + printf("%s:%d failed to unwrap wrapped data\n", __func__, __LINE__); + // "Abondon the exchange" + return false; + } + + ec_attribute_t *init_caps_attr = ec_util::get_attrib(wrapped_data, wrapped_len, ec_attrib_id_init_caps); + if (!init_caps_attr) { + printf("%s:%d No initiator capabilities attribute found\n", __func__, __LINE__); + return false; + } + ec_dpp_capabilities_t init_caps = { + .byte = init_caps_attr->data[0] + }; + + if (!check_supports_init_caps(init_caps)) { + printf("%s:%d Initiator capabilities not supported\n", __func__, __LINE__); + + auto [resp_frame, resp_len] = create_auth_response(DPP_STATUS_NOT_COMPATIBLE); + if (resp_frame == NULL || resp_len == 0) { + printf("%s:%d failed to create response frame\n", __func__, __LINE__); + return false; + } +/* +it shall respond with a DPP Authentication +Response frame indicating failure by adding the DPP Status field set to STATUS_NOT_COMPATIBLE, a hash of its +public bootstrapping key, a hash of the Initiator’s public bootstrapping key if it is doing mutual authentication, Protocol +Version attribute if it was sent in the DPP Authentication Request frame and is version 2 or higher, and Wrapped Data +element consisting of the Initiator’s nonce and the Responder’s desired capabilities wrapped with k1: +*/ + return false; + } + + //TODO/NOTE: Unknown: If need more time to process, respond `STATUS_RESPONSE_PENDING` (EasyConnect 6.3.3) + // If the Responder needs more time to respond, e.g., to complete bootstrapping of the Initiator’s bootstrapping key + + //The Responder first selects capabilities that support the Initiator—for example, + // if the Initiator states it is a Configurator, then the Responder takes on the Enrollee role. + auto [resp_frame, resp_len] = create_auth_response(DPP_STATUS_OK); + if (resp_frame == NULL || resp_len == 0) { + printf("%s:%d failed to create response frame\n", __func__, __LINE__); + return false; + } + // TODO: Send the response frame +} + +bool ec_enrollee_t::handle_auth_confirm(uint8_t *buff, unsigned int len) +{ + return true; +} + +bool ec_enrollee_t::handle_config_response(uint8_t *buff, unsigned int len) +{ + return true; +} + +bool ec_enrollee_t::check_supports_init_caps(ec_dpp_capabilities_t caps) +{ + // Currently just returning true for all capabilities + return true; +} + +std::pair ec_enrollee_t::create_presence_announcement() +{ + printf("%s:%d Enter\n", __func__, __LINE__); + + ec_frame_t *frame = ec_util::alloc_frame(ec_frame_type_presence_announcement); + if (frame == NULL) { + printf("%s:%d failed to allocate memory for frame\n", __func__, __LINE__); + return std::make_pair(NULL, 0); + } + + // Compute the hash of the responder boot key + uint8_t *resp_boot_key_chirp_hash = ec_crypto::compute_key_hash(m_boot_data.responder_boot_key, "chirp"); + if (resp_boot_key_chirp_hash == NULL) { + printf("%s:%d unable to compute \"chirp\" responder bootstrapping key hash\n", __func__, __LINE__); + return std::pair(NULL, 0); + } + + uint8_t* attribs = NULL; + uint16_t attrib_len = 0; + + attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_resp_bootstrap_key_hash, SHA256_DIGEST_LENGTH, resp_boot_key_chirp_hash); + free(resp_boot_key_chirp_hash); + + if (!(frame = ec_util::copy_attrs_to_frame(frame, attribs, attrib_len))) { + printf("%s:%d unable to copy attributes to frame\n", __func__, __LINE__); + free(attribs); + return std::make_pair(NULL, 0); + } + free(attribs); + + return std::pair(); +} + +std::pair ec_enrollee_t::create_recfg_presence_announcement() +{ + return std::pair(); +} + +std::pair ec_enrollee_t::create_auth_response(ec_status_code_t dpp_status) +{ + return std::pair(); +} + +std::pair ec_enrollee_t::create_recfg_auth_response(ec_status_code_t dpp_status) +{ + return std::pair(); +} + +std::pair ec_enrollee_t::create_config_request() +{ + return std::pair(); +} + +std::pair ec_enrollee_t::create_config_result() +{ + return std::pair(); +} diff --git a/src/em/prov/easyconnect/ec_manager.cpp b/src/em/prov/easyconnect/ec_manager.cpp new file mode 100644 index 0000000..25450da --- /dev/null +++ b/src/em/prov/easyconnect/ec_manager.cpp @@ -0,0 +1,68 @@ +#include "ec_manager.h" +#include "ec_ctrl_configurator.h" + +#include "ec_util.h" + +#include + +ec_manager_t::ec_manager_t(std::string mac_addr, send_chirp_func send_chirp, send_encap_dpp_func send_encap_dpp, bool m_is_controller) + : m_is_controller(m_is_controller), m_stored_chirp_fn(send_chirp), m_stored_encap_dpp_fn(send_encap_dpp), m_stored_mac_addr(mac_addr){ + if (m_is_controller) { + m_configurator = std::unique_ptr(new ec_ctrl_configurator_t(mac_addr, send_chirp, send_encap_dpp)); + } else { + m_enrollee = std::unique_ptr(new ec_enrollee_t(mac_addr)); + } +} + +ec_manager_t::~ec_manager_t() +{ +} + +bool ec_manager_t::handle_recv_ec_action_frame(ec_frame_t *frame, size_t len) +{ + if (!ec_util::validate_frame(frame)) { + printf("%s:%d: frame validation failed\n", __func__, __LINE__); + return false; + } + switch (frame->frame_type) { + case ec_frame_type_presence_announcement: + return m_configurator->handle_presence_announcement((uint8_t *)frame, len); + case ec_frame_type_auth_req: + return m_enrollee->handle_auth_request((uint8_t *)frame, len); + case ec_frame_type_auth_rsp: + return m_configurator->handle_auth_response((uint8_t *)frame, len); + case ec_frame_type_auth_cnf: + return m_enrollee->handle_auth_confirm((uint8_t *)frame, len); + + default: + printf("%s:%d: frame type (%d) not handled\n", __func__, __LINE__, frame->frame_type); + break; + } + return true; +} + +bool ec_manager_t::upgrade_to_onboarded_proxy_agent(toggle_cce_func toggle_cce) +{ + if (m_is_controller) { + // Only an enrollee agent can be upgraded to a proxy agent + return false; + } + + // If a configurator is already defined (i.e. the enrollee agent is already upgraded) or somehow it's a controller + // return an error + if (m_configurator || dynamic_cast(m_configurator.get()) == nullptr) { + return false; + } + if (!m_enrollee) { + // Can't upgrade an enrollee if it's not defined + return false; + } + std::string enrollee_mac = m_enrollee->get_mac_addr(); + // Free the enrollee object + m_enrollee.reset(); + + // Create a new proxy agent configurator + m_configurator = std::unique_ptr(new ec_pa_configurator_t(enrollee_mac, m_stored_chirp_fn, m_stored_encap_dpp_fn)); + m_configurator->m_toggle_cce = toggle_cce; + return true; +} diff --git a/src/em/prov/easyconnect/ec_pa_configurator.cpp b/src/em/prov/easyconnect/ec_pa_configurator.cpp new file mode 100644 index 0000000..1139c5f --- /dev/null +++ b/src/em/prov/easyconnect/ec_pa_configurator.cpp @@ -0,0 +1,101 @@ +#include "ec_pa_configurator.h" + +#include "ec_util.h" + +bool ec_pa_configurator_t::handle_presence_announcement(uint8_t *buff, unsigned int len) +{ + ec_frame_t *frame = (ec_frame_t *)buff; + + if (ec_util::validate_frame(frame, ec_frame_type_presence_announcement) == false) { + printf("%s:%d: frame validation failed\n", __func__, __LINE__); + return -1; + } + + ec_attribute_t *attrib = ec_util::get_attrib(frame->attributes, len-EC_FRAME_BASE_SIZE, ec_attrib_id_resp_bootstrap_key_hash); + if (!attrib) { + return -1; + } + + return true; +} + +bool ec_pa_configurator_t::handle_auth_response(uint8_t *buff, unsigned int len) +{ + return true; +} + +bool ec_pa_configurator_t::handle_cfg_request(uint8_t *buff, unsigned int len) +{ + return true; +} + +bool ec_pa_configurator_t::handle_cfg_result(uint8_t *buff, unsigned int len) +{ + return true; +} + +bool ec_pa_configurator_t::process_chirp_notification(em_dpp_chirp_value_t *chirp_tlv, uint16_t tlv_len) +{ + return true; +} + +bool ec_pa_configurator_t::process_proxy_encap_dpp_msg(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, em_dpp_chirp_value_t *chirp_tlv, uint16_t chirp_tlv_len) +{ + if (encap_tlv == NULL || encap_tlv_len == 0) { + printf("%s:%d: Encap DPP TLV is empty\n", __func__, __LINE__); + return -1; + } + + + mac_addr_t dest_mac = {0}; + uint8_t frame_type = 0; + uint8_t* encap_frame = NULL; + uint8_t encap_frame_len = 0; + + if (ec_util::parse_encap_dpp_tlv(encap_tlv, encap_tlv_len, &dest_mac, &frame_type, &encap_frame, &encap_frame_len) < 0) { + printf("%s:%d: Failed to parse Encap DPP TLV\n", __func__, __LINE__); + return -1; + } + + mac_addr_t chirp_mac = {0}; + uint8_t chirp_hash[255] = {0}; // Max hash length to avoid dynamic allocation + uint8_t chirp_hash_len = 0; + + ec_frame_type_t ec_frame_type = (ec_frame_type_t)frame_type; + switch (ec_frame_type) { + case ec_frame_type_auth_req: { + if (chirp_tlv == NULL || chirp_tlv_len == 0) { + printf("%s:%d: Chirp TLV is empty\n", __func__, __LINE__); + return -1; + } + if (ec_util::parse_dpp_chirp_tlv(chirp_tlv, chirp_tlv_len, &chirp_mac, (uint8_t**)&chirp_hash, &chirp_hash_len) < 0) { + printf("%s:%d: Failed to parse DPP Chirp TLV\n", __func__, __LINE__); + return -1; + } + std::string chirp_hash_str = ec_util::hash_to_hex_string(chirp_hash, chirp_hash_len); + printf("%s:%d: Chirp TLV Hash: %s\n", __func__, __LINE__, chirp_hash_str.c_str()); + + // Store the encap frame keyed by the chirp hash in the map + std::vector encap_frame_vec(encap_frame, encap_frame + encap_frame_len); + m_chirp_hash_frame_map[chirp_hash_str] = encap_frame_vec; + break; + } + case ec_frame_type_recfg_auth_req: { + printf("%s:%d: Encap DPP frame type (%d) not handled\n", __func__, __LINE__, ec_frame_type); + std::vector encap_frame_vec(encap_frame, encap_frame + encap_frame_len); + // Will be compared against incoming presence announcement hash and mac-addr + m_stored_recfg_auth_frames.push_back(encap_frame_vec); + break; + } + case ec_frame_type_auth_cnf: + case ec_frame_type_recfg_auth_cnf: { + break; + } + default: + printf("%s:%d: Encap DPP frame type (%d) not handled\n", __func__, __LINE__, ec_frame_type); + break; + } + // Parse out dest STA mac address and hash value then validate against the hash in the + // ec_session dpp uri info public key. + // Then construct an Auth request frame and send back in an Encap message +} diff --git a/src/em/prov/easyconnect/ec_session.cpp b/src/em/prov/easyconnect/ec_session.cpp deleted file mode 100644 index 4ff40c3..0000000 --- a/src/em/prov/easyconnect/ec_session.cpp +++ /dev/null @@ -1,529 +0,0 @@ -/** - * Copyright 2025 Comcast Cable Communications Management, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include "ec_base.h" -#include "ec_session.h" -#include "ec_util.h" -#include "em.h" -#include "aes_siv.h" - -std::pair ec_session_t::create_auth_request() -{ - - EC_KEY *responder_boot_key, *initiator_boot_key; - - ec_dpp_capabilities_t caps = {{ - .enrollee = 0, - .configurator = 1 - }}; - - printf("%s:%d Enter\n", __func__, __LINE__); - - uint8_t* buff = (uint8_t*) calloc(EC_FRAME_BASE_SIZE, 1); - - ec_frame_t *frame = (ec_frame_t *)buff; - frame->frame_type = ec_frame_type_auth_req; - - if (init_session(NULL) != 0) { - m_activation_status = ActStatus_Failed; - printf("%s:%d Failed to initialize session parameters\n", __func__, __LINE__); - return std::make_pair(NULL, 0); - } - - if (compute_intermediate_key(true) != 0) { - m_activation_status = ActStatus_Failed; - printf("%s:%d failed to generate key\n", __func__, __LINE__); - return std::make_pair(NULL, 0); - } - - uint8_t* attribs = NULL; - uint16_t attrib_len = 0; - - // Responder Bootstrapping Key Hash - if (compute_key_hash(m_data.responder_boot_key, m_params.responder_keyhash) < 1) { - m_activation_status = ActStatus_Failed; - printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); - return std::make_pair(NULL, 0); - } - - attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_resp_bootstrap_key_hash, SHA256_DIGEST_LENGTH, m_params.responder_keyhash); - - // Initiator Bootstrapping Key Hash - if (compute_key_hash(m_data.initiator_boot_key, m_params.initiator_keyhash) < 1) { - m_activation_status = ActStatus_Failed; - printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); - return std::make_pair(NULL, 0); - } - - attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_init_bootstrap_key_hash, SHA256_DIGEST_LENGTH, m_params.initiator_keyhash); - - // Initiator Protocol Key - uint8_t protocol_key_buff[1024]; - BN_bn2bin((const BIGNUM *)m_params.x, - &protocol_key_buff[BN_num_bytes(m_params.prime) - BN_num_bytes(m_params.x)]); - BN_bn2bin((const BIGNUM *)m_params.y, - &protocol_key_buff[2*BN_num_bytes(m_params.prime) - BN_num_bytes(m_params.x)]); - - attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_init_proto_key, 2*BN_num_bytes(m_params.prime), protocol_key_buff); - - // Protocol Version - if (m_cfgrtr_ver > 1) { - attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_proto_version, m_cfgrtr_ver); - } - - // Channel Attribute (optional) - //TODO: REVISIT THIS - if (m_data.ec_freqs[0] != 0){ - int base_freq = m_data.ec_freqs[0]; - uint16_t chann_attr = ec_util::freq_to_channel_attr(base_freq); - attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_channel, sizeof(uint16_t), (uint8_t *)&chann_attr); - } - - - // Wrapped Data (with Initiator Nonce and Initiator Capabilities) - // EasyMesh 8.2.2 Table 36 - attribs = add_wrapped_data_attr(frame, attribs, &attrib_len, true, m_params.k1, [&](){ - uint8_t* wrap_attribs = NULL; - uint16_t wrapped_len = 0; - wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_init_nonce, m_params.noncelen, m_params.initiator_nonce); - wrap_attribs = ec_util::add_attrib(wrap_attribs, &wrapped_len, ec_attrib_id_init_caps, caps.byte); - return std::make_pair(wrap_attribs, wrapped_len); - }); - - // Add attributes to the frame - uint16_t new_len = EC_FRAME_BASE_SIZE + attrib_len; - buff = (uint8_t*) realloc(buff, new_len); - if (buff == NULL) { - m_activation_status = ActStatus_Failed; - printf("%s:%d unable to realloc memory\n", __func__, __LINE__); - return std::make_pair(NULL, 0); - } - frame = (ec_frame_t *)buff; - memcpy(frame->attributes, attribs, attrib_len); - - free(attribs); - - return std::make_pair(buff, new_len); - -} - -int ec_session_t::create_auth_rsp(uint8_t *buff) -{ - return -1; -} - -int ec_session_t::create_auth_cnf(uint8_t *buff) -{ - return -1; -} - -int ec_session_t::create_pres_ann(uint8_t *buff) -{ - - ec_frame_t *frame = (ec_frame_t *)buff; - frame->frame_type = ec_frame_type_presence_announcement; - - // Compute the hash of the responder boot key - uint8_t resp_boot_key_chirp_hash[SHA512_DIGEST_LENGTH]; - if (compute_key_hash(m_data.responder_boot_key, resp_boot_key_chirp_hash, "chirp") < 1) { - m_activation_status = ActStatus_Failed; - printf("%s:%d unable to compute \"chirp\" responder bootstrapping key hash\n", __func__, __LINE__); - return -1; - } - - uint8_t* attribs = frame->attributes; - uint16_t attrib_len = 0; - - attribs = ec_util::add_attrib(attribs, &attrib_len, ec_attrib_id_resp_bootstrap_key_hash, SHA256_DIGEST_LENGTH, resp_boot_key_chirp_hash); - attrib_len += ec_util::get_ec_attr_size(SHA256_DIGEST_LENGTH); - - return attrib_len; -} - -int ec_session_t::handle_pres_ann(uint8_t *buff, unsigned int len) -{ - ec_frame_t *frame = (ec_frame_t *)buff; - - if (ec_util::validate_frame(frame, ec_frame_type_presence_announcement) == false) { - printf("%s:%d: frame validation failed\n", __func__, __LINE__); - return -1; - } - - ec_attribute_t *attrib = ec_util::get_attrib(frame->attributes, len-EC_FRAME_BASE_SIZE, ec_attrib_id_resp_bootstrap_key_hash); - if (!attrib) { - return -1; - } - - // TODO: Come back to this - memcpy(m_params.responder_keyhash, attrib->data, attrib->length); - - return 0; -} - - - -int ec_session_t::init_session(ec_data_t* ec_data) -{ - const EC_POINT *ipt, *rpt = NULL; - const BIGNUM *proto_priv; - - if (ec_data != NULL) { - memset(&m_data, 0, sizeof(ec_data_t)); - memcpy(&m_data, ec_data, sizeof(ec_data_t)); - } - - if (m_data.type == ec_session_type_cfg) { - // Set in DPP URI - - rpt = EC_KEY_get0_public_key(m_data.responder_boot_key); - if (rpt == NULL) { - printf("%s:%d Could not get responder bootstrap public key\n", __func__, __LINE__); - return -1; - } - - } else if (m_data.type == ec_session_type_recfg) { - - m_params.group = EC_KEY_get0_group(m_data.initiator_boot_key); - m_params.responder_connector = EC_POINT_new(m_params.group); - } - - - m_params.x = BN_new(); - m_params.y = BN_new(); - m_params.m = BN_new(); - m_params.n = BN_new(); - m_params.prime = BN_new(); - m_params.bnctx = BN_CTX_new(); - - if (!m_params.x || !m_params.y || !m_params.m || !m_params.n || - !m_params.prime || !m_params.bnctx) { - printf("%s:%d Some BN NULL\n", __func__, __LINE__); - BN_free(m_params.x); - BN_free(m_params.y); - BN_free(m_params.m); - BN_free(m_params.n); - BN_free(m_params.prime); - BN_CTX_free(m_params.bnctx); - return -1; - } - - m_params.responder_proto_pt = EC_POINT_new(m_params.group); - m_params.nid = EC_GROUP_get_curve_name(m_params.group); - - //printf("%s:%d nid: %d\n", __func__, __LINE__, m_params.nid); - switch (m_params.nid) { - case NID_X9_62_prime256v1: - m_params.group_num = 19; - m_params.digestlen = 32; - m_params.hashfcn = EVP_sha256(); - break; - case NID_secp384r1: - m_params.group_num = 20; - m_params.digestlen = 48; - m_params.hashfcn = EVP_sha384(); - break; - case NID_secp521r1: - m_params.group_num = 21; - m_params.digestlen = 64; - m_params.hashfcn = EVP_sha512(); - break; - case NID_X9_62_prime192v1: - m_params.group_num = 25; - m_params.digestlen = 32; - m_params.hashfcn = EVP_sha256(); - break; - case NID_secp224r1: - m_params.group_num = 26; - m_params.digestlen = 32; - m_params.hashfcn = EVP_sha256(); - break; - default: - printf("%s:%d nid:%d not handled\n", __func__, __LINE__, m_params.nid); - return -1; - } - - m_params.noncelen = m_params.digestlen/2; - - //printf("%s:%d group_num:%d digestlen:%d\n", __func__, __LINE__, m_params.group_num, m_params.digestlen); - if (m_params.initiator_proto_key != NULL){ - EC_KEY_free(m_params.initiator_proto_key); - m_params.initiator_proto_key = NULL; - } - m_params.initiator_proto_key = EC_KEY_new_by_curve_name(m_params.nid); - if (m_params.initiator_proto_key == NULL) { - printf("%s:%d Could not create protocol key\n", __func__, __LINE__); - return -1; - } - - if (EC_KEY_generate_key(m_params.initiator_proto_key) == 0) { - printf("%s:%d Could not generate protocol key\n", __func__, __LINE__); - return -1; - } - - ipt = EC_KEY_get0_public_key(m_params.initiator_proto_key); - if (ipt == NULL) { - printf("%s:%d Could not get initiator protocol public key\n", __func__, __LINE__); - return -1; - } - - proto_priv = EC_KEY_get0_private_key(m_params.initiator_proto_key); - if (proto_priv == NULL) { - printf("%s:%d Could not get initiator protocol private key\n", __func__, __LINE__); - return -1; - } - - if ((m_params.N = EC_POINT_new(m_params.group)) == NULL) { - printf("%s:%d unable to create bignums to initiate DPP!\n", __func__, __LINE__); - return -1; - } - - - if ((m_params.M = EC_POINT_new(m_params.group)) == NULL) { - printf("%s:%d unable to create bignums to initiate DPP!\n", __func__, __LINE__); - return -1; - } - - - if (EC_POINT_get_affine_coordinates_GFp(m_params.group, ipt, m_params.x, - m_params.y, m_params.bnctx) == 0) { - printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); - return -1; - } - - if (m_data.type == ec_session_type_cfg) { - - if (EC_POINT_mul(m_params.group, m_params.M, NULL, rpt, proto_priv, m_params.bnctx) == 0) { - printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); - return -1; - } - - - printf("Point M:\n"); - ec_util::print_ec_point(m_params.group, m_params.bnctx, m_params.M); - - if (EC_POINT_get_affine_coordinates_GFp(m_params.group, m_params.M, - m_params.m, NULL, m_params.bnctx) == 0) { - printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); - return -1; - - } - } - - RAND_bytes(m_params.initiator_nonce, m_params.noncelen); - if (EC_GROUP_get_curve_GFp(m_params.group, m_params.prime, NULL, NULL, - m_params.bnctx) == 0) { - printf("%s:%d unable to get x, y of the curve\n", __func__, __LINE__); - return -1; - } - - - return 0; - -} - -int ec_session_t::handle_chirp_notification(em_dpp_chirp_value_t *chirp_tlv, uint8_t **out_frame) -{ - // TODO: Currently only handling controller side - - // Parse TLV - bool mac_addr_present = chirp_tlv->mac_present; - bool hash_valid = chirp_tlv->hash_valid; - - uint8_t *data_ptr = chirp_tlv->data; - mac_addr_t mac = {0}; - if (mac_addr_present) { - memcpy(mac, data_ptr, sizeof(mac_addr_t)); - data_ptr += sizeof(mac_addr_t); - } - - if (!hash_valid) { - // Clear (Re)configuration state, agent side - return 0; - } - - uint8_t hash[255] = {0}; // Max hash length to avoid dynamic allocation - uint8_t hash_len = 0; - - hash_len = *data_ptr; - data_ptr++; - memcpy(hash, data_ptr, hash_len); - - // Validate hash - // Compute the hash of the responder boot key - uint8_t resp_boot_key_chirp_hash[SHA512_DIGEST_LENGTH]; - if (compute_key_hash(m_data.responder_boot_key, resp_boot_key_chirp_hash, "chirp") < 1) { - m_activation_status = ActStatus_Failed; - printf("%s:%d unable to compute \"chirp\" responder bootstrapping key hash\n", __func__, __LINE__); - return -1; - } - - if (memcmp(hash, resp_boot_key_chirp_hash, hash_len) != 0) { - // Hashes don't match, don't initiate DPP authentication - *out_frame = NULL; - printf("%s:%d: Chirp notification hash and DPP URI hash did not match! Stopping DPP!\n", __func__, __LINE__); - return -1; - } - - auto [auth_frame, auth_frame_len] = create_auth_request(); - if (auth_frame == NULL || auth_frame_len == 0) { - printf("%s:%d: Failed to create authentication request frame\n", __func__, __LINE__); - return -1; - } - - // Create Auth Request Encap TLV: EasyMesh 5.3.4 - em_encap_dpp_t* encap_dpp_tlv = (em_encap_dpp_t*)calloc(sizeof(em_encap_dpp_t) + auth_frame_len , 1); - if (encap_dpp_tlv == NULL) { - printf("%s:%d: Failed to allocate memory for Encap DPP TLV\n", __func__, __LINE__); - return -1; - } - encap_dpp_tlv->dpp_frame_indicator = 0; - encap_dpp_tlv->frame_type = 0; // DPP Authentication Request Frame - encap_dpp_tlv->enrollee_mac_addr_present = 1; - - memcpy(encap_dpp_tlv->dest_mac_addr, mac, sizeof(mac_addr_t)); - encap_dpp_tlv->encap_frame_len = auth_frame_len; - memcpy(encap_dpp_tlv->encap_frame, auth_frame, auth_frame_len); - - free(auth_frame); - - // Create Auth Request Chirp TLV: EasyMesh 5.3.4 - size_t data_size = sizeof(mac_addr_t) + hash_len + sizeof(uint8_t); - em_dpp_chirp_value_t* chirp = (em_dpp_chirp_value_t*)calloc(sizeof(em_dpp_chirp_value_t) + data_size, 1); - if (chirp == NULL) { - printf("%s:%d: Failed to allocate memory for chirp TLV\n", __func__, __LINE__); - free(encap_dpp_tlv); - return -1; - } - chirp->mac_present = 1; - chirp->hash_valid = 1; - - uint8_t* tmp = chirp->data; - memcpy(tmp, mac, sizeof(mac_addr_t)); - tmp += sizeof(mac_addr_t); - - *tmp = hash_len; - tmp++; - - memcpy(tmp, hash, hash_len); - - // Send the encapsulated DPP message (with Encap TLV and Chirp TLV) - this->m_send_prox_encap_dpp_msg(encap_dpp_tlv, sizeof(em_encap_dpp_t) + auth_frame_len, chirp, sizeof(em_dpp_chirp_value_t) + data_size); - - free(encap_dpp_tlv); - free(chirp); - - return 0; - -} - -int ec_session_t::handle_proxy_encap_dpp_tlv(em_encap_dpp_t *encap_tlv, uint8_t **out_frame) { - -} - -uint8_t* ec_session_t::add_wrapped_data_attr(ec_frame_t *frame, uint8_t* frame_attribs, uint16_t* non_wrapped_len, bool use_aad, uint8_t* key, std::function()> create_wrap_attribs) -{ - siv_ctx ctx; - - // Initialize AES-SIV context - switch(m_params.digestlen) { - case SHA256_DIGEST_LENGTH: - siv_init(&ctx, key, SIV_256); - break; - case SHA384_DIGEST_LENGTH: - siv_init(&ctx, key, SIV_384); - break; - case SHA512_DIGEST_LENGTH: - siv_init(&ctx, key, SIV_512); - break; - default: - printf("%s:%d Unknown digest length\n", __func__, __LINE__); - return NULL; - } - - // Use the provided function to create wrap_attribs and wrapped_len - auto [wrap_attribs, wrapped_len] = create_wrap_attribs(); - - // Encapsulate the attributes in a wrapped data attribute - uint16_t wrapped_attrib_len = wrapped_len + AES_BLOCK_SIZE; - ec_attribute_t *wrapped_attrib = (ec_attribute_t *)calloc(sizeof(ec_attribute_t) + wrapped_attrib_len, 1); - wrapped_attrib->attr_id = ec_attrib_id_wrapped_data; - wrapped_attrib->length = wrapped_attrib_len; - memset(wrapped_attrib->data, 0, wrapped_attrib_len); - - /** - * Encrypt attributes using SIV mode with two additional authenticated data (AAD) inputs: - * 1. The frame structure and 2. Non-wrapped attributes (per EasyMesh 6.3.1.4) - * The synthetic IV/tag is stored in the first AES_BLOCK_SIZE bytes of wrapped_attrib->data - */ - if (use_aad) { - if (frame == NULL || frame_attribs == NULL || non_wrapped_len == NULL) { - printf("%s:%d: AAD input is NULL, AAD encryption failed!\n", __func__, __LINE__); - return NULL; - } - siv_encrypt(&ctx, wrap_attribs, &wrapped_attrib->data[AES_BLOCK_SIZE], wrapped_len, wrapped_attrib->data, 2, - frame, sizeof(ec_frame_t), - frame_attribs, *non_wrapped_len); - } else { - siv_encrypt(&ctx, wrap_attribs, &wrapped_attrib->data[AES_BLOCK_SIZE], wrapped_len, wrapped_attrib->data, 0); - } - - // Add the wrapped data attribute to the frame - uint8_t* ret_frame_attribs = ec_util::add_attrib(frame_attribs, non_wrapped_len, ec_attrib_id_wrapped_data, wrapped_attrib_len, (uint8_t *)wrapped_attrib); - - - free(wrap_attribs); - - return ret_frame_attribs; -} - -int ec_session_t::handle_recv_ec_action_frame(ec_frame_t *frame, size_t len) -{ - if (!ec_util::validate_frame(frame)) { - printf("%s:%d: frame validation failed\n", __func__, __LINE__); - return -1; - } - switch (frame->frame_type) { - case ec_frame_type_presence_announcement: - return handle_pres_ann((uint8_t *)frame, len); - default: - printf("%s:%d: frame type (%d) not handled\n", __func__, __LINE__, frame->frame_type); - break; - } - return 0; -} - -ec_session_t::ec_session_t(std::function send_chirp_notification, - std::function send_prox_encap_dpp_msg) - : m_send_chirp_notification(send_chirp_notification), m_send_prox_encap_dpp_msg(send_prox_encap_dpp_msg) -{ - // Initialize member variables - m_cfgrtr_ver = 0; - m_enrollee_ver = 0; - m_activation_status = ActStatus_Idle; - memset(&m_enrollee_mac, 0, sizeof(mac_address_t)); - memset(&m_params, 0, sizeof(ec_params_t)); - memset(&m_data, 0, sizeof(ec_data_t)); -} - -ec_session_t::~ec_session_t() -{ - // Clean up any allocated resources if necessary -} - diff --git a/src/em/prov/easyconnect/ec_util.cpp b/src/em/prov/easyconnect/ec_util.cpp index c9a3087..143d675 100644 --- a/src/em/prov/easyconnect/ec_util.cpp +++ b/src/em/prov/easyconnect/ec_util.cpp @@ -1,7 +1,11 @@ #include +#include +#include #include "ec_util.h" #include "util.h" +#include "aes_siv.h" +#include "em_crypto.h" void ec_util::init_frame(ec_frame_t *frame) { @@ -89,49 +93,244 @@ bool ec_util::validate_frame(const ec_frame_t *frame) return true; } -void ec_util::print_bignum (BIGNUM *bn) + + +uint8_t* ec_util::add_wrapped_data_attr(ec_frame_t *frame, uint8_t* frame_attribs, uint16_t* non_wrapped_len, bool use_aad, uint8_t* key, std::function()> create_wrap_attribs) { - unsigned char *buf; - int len; + siv_ctx ctx; + + // Initialize AES-SIV context +// TODO: Come back to + // switch(params.digestlen) { + // case SHA256_DIGEST_LENGTH: + // siv_init(&ctx, key, SIV_256); + // break; + // case SHA384_DIGEST_LENGTH: + // siv_init(&ctx, key, SIV_384); + // break; + // case SHA512_DIGEST_LENGTH: + // siv_init(&ctx, key, SIV_512); + // break; + // default: + // printf("%s:%d Unknown digest length\n", __func__, __LINE__); + // return NULL; + // } + + // Use the provided function to create wrap_attribs and wrapped_len + auto [wrap_attribs, wrapped_len] = create_wrap_attribs(); + + // Encapsulate the attributes in a wrapped data attribute + uint16_t wrapped_attrib_len = wrapped_len + AES_BLOCK_SIZE; + ec_attribute_t *wrapped_attrib = (ec_attribute_t *)calloc(sizeof(ec_attribute_t) + wrapped_attrib_len, 1); + wrapped_attrib->attr_id = ec_attrib_id_wrapped_data; + wrapped_attrib->length = wrapped_attrib_len; + memset(wrapped_attrib->data, 0, wrapped_attrib_len); - len = BN_num_bytes(bn); - if ((buf = (unsigned char *)malloc(len)) == NULL) { - printf("Could not print bignum\n"); - return; + /** + * Encrypt attributes using SIV mode with two additional authenticated data (AAD) inputs: + * 1. The frame structure and 2. Non-wrapped attributes (per EasyMesh 6.3.1.4) + * The synthetic IV/tag is stored in the first AES_BLOCK_SIZE bytes of wrapped_attrib->data + */ + if (use_aad) { + if (frame == NULL || frame_attribs == NULL || non_wrapped_len == NULL) { + printf("%s:%d: AAD input is NULL, AAD encryption failed!\n", __func__, __LINE__); + return NULL; + } + siv_encrypt(&ctx, wrap_attribs, &wrapped_attrib->data[AES_BLOCK_SIZE], wrapped_len, wrapped_attrib->data, 2, + frame, sizeof(ec_frame_t), + frame_attribs, *non_wrapped_len); + } else { + siv_encrypt(&ctx, wrap_attribs, &wrapped_attrib->data[AES_BLOCK_SIZE], wrapped_len, wrapped_attrib->data, 0); } - BN_bn2bin(bn, buf); - util::print_hex_dump(len, buf); - free(buf); + + // Add the wrapped data attribute to the frame + uint8_t* ret_frame_attribs = ec_util::add_attrib(frame_attribs, non_wrapped_len, ec_attrib_id_wrapped_data, wrapped_attrib_len, (uint8_t *)wrapped_attrib); + + + free(wrap_attribs); + + return ret_frame_attribs; } -void ec_util::print_ec_point (const EC_GROUP *group, BN_CTX *bnctx, EC_POINT *point) +std::pair ec_util::unwrap_wrapped_attrib(ec_attribute_t* wrapped_attrib, ec_frame_t *frame, bool uses_aad, uint8_t* key) { - BIGNUM *x = NULL, *y = NULL; + siv_ctx ctx; + + // Initialize AES-SIV context + // switch(m_params.digestlen) { + // case SHA256_DIGEST_LENGTH: + // siv_init(&ctx, key, SIV_256); + // break; + // case SHA384_DIGEST_LENGTH: + // siv_init(&ctx, key, SIV_384); + // break; + // case SHA512_DIGEST_LENGTH: + // siv_init(&ctx, key, SIV_512); + // break; + // default: + // printf("%s:%d Unknown digest length\n", __func__, __LINE__); + // return std::pair(NULL, 0); + // } + + uint8_t* wrapped_ciphertext = wrapped_attrib->data + AES_BLOCK_SIZE; + size_t wrapped_len = wrapped_attrib->length - AES_BLOCK_SIZE; - if ((x = BN_new()) == NULL) { - printf("%s:%d:Could not print ec_point\n", __func__, __LINE__); - return; + uint8_t* unwrap_attribs = (uint8_t*)calloc(wrapped_len, 1); + int result = -1; + if (uses_aad) { + if (frame == NULL) { + printf("%s:%d: AAD input is NULL, AAD decryption failed!\n", __func__, __LINE__); + return std::pair(NULL, 0); + } + result = siv_decrypt(&ctx, wrapped_ciphertext, unwrap_attribs, wrapped_len, wrapped_attrib->data, 2, + frame, sizeof(ec_frame_t), + frame->attributes, ((uint8_t*)wrapped_attrib) - frame->attributes); // Non-wrapped attributes + } else { + result = siv_decrypt(&ctx, wrapped_ciphertext, unwrap_attribs, wrapped_len, wrapped_attrib->data, 0); } - if ((y = BN_new()) == NULL) { - BN_free(x); - printf("%s:%d:Could not print ec_point\n", __func__, __LINE__); - return; + if (result < 0) { + printf("%s:%d: Failed to decrypt and authenticate wrapped data\n", __func__, __LINE__); + free(unwrap_attribs); + return std::pair(NULL, 0); } - if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) == 0) { - BN_free(y); - BN_free(x); - printf("%s:%d:Could not print ec_point\n", __func__, __LINE__); - return; + return std::pair(unwrap_attribs, wrapped_len); +} +bool ec_util::parse_dpp_chirp_tlv(em_dpp_chirp_value_t* chirp_tlv, uint16_t chirp_tlv_len, mac_addr_t *mac, uint8_t **hash, uint8_t *hash_len) +{ + if (chirp_tlv == NULL || chirp_tlv_len == 0) { + fprintf(stderr, "Invalid input\n"); + return false; } - printf("POINT.x:\n"); - print_bignum(x); - printf("POINT.y:\n"); - print_bignum(y); + uint16_t data_len = chirp_tlv_len - sizeof(em_dpp_chirp_value_t); + // Parse TLV + bool mac_addr_present = chirp_tlv->mac_present; + bool hash_valid = chirp_tlv->hash_valid; + + uint8_t *data_ptr = chirp_tlv->data; + if (mac_addr_present && data_len >= sizeof(mac_addr_t)) { + memcpy(*mac, data_ptr, sizeof(mac_addr_t)); + data_ptr += sizeof(mac_addr_t); + data_len -= sizeof(mac_addr_t); + } + + if (!hash_valid || data_len <= 0) { + // Clear (Re)configuration state, agent side + return true; + } + + *hash_len = *data_ptr; + data_ptr++; + if (data_len < *hash_len) { + fprintf(stderr, "Invalid chirp tlv\n"); + return NULL; + } + memcpy(*hash, data_ptr, *hash_len); + + return true; +} + +bool ec_util::parse_encap_dpp_tlv(em_encap_dpp_t *encap_tlv, uint16_t encap_tlv_len, mac_addr_t *dest_mac, uint8_t *frame_type, uint8_t **encap_frame, uint8_t *encap_frame_len) +{ + if (encap_tlv == NULL || encap_tlv_len == 0) { + fprintf(stderr, "Invalid input\n"); + return false; + } + + uint16_t data_len = encap_tlv_len - sizeof(em_encap_dpp_t); + // Parse TLV + bool mac_addr_present = encap_tlv->enrollee_mac_addr_present; + + // Copy mac address if present + uint8_t *data_ptr = encap_tlv->data; + if (mac_addr_present && data_len >= sizeof(mac_addr_t)) { + memcpy(*dest_mac, data_ptr, sizeof(mac_addr_t)); + data_ptr += sizeof(mac_addr_t); + data_len -= sizeof(mac_addr_t); + } else { + memset(*dest_mac, 0, sizeof(mac_addr_t)); + } + + if (data_len < sizeof(uint8_t) + sizeof(uint16_t)) { + fprintf(stderr, "Invalid encap tlv\n"); + return false; + } + + // Get frame type + *frame_type = *data_ptr; + data_ptr++; + + // Get frame length + *encap_frame_len = htons(*((uint16_t *)data_ptr)); + data_ptr += sizeof(uint16_t); + + if (data_len < *encap_frame_len) { + fprintf(stderr, "Invalid encap tlv\n"); + return false; + } + + // Copy frame + memcpy(*encap_frame, data_ptr, *encap_frame_len); + + return true; +} + +em_encap_dpp_t * ec_util::create_encap_dpp_tlv(bool dpp_frame_indicator, uint8_t content_type, mac_addr_t *dest_mac, uint8_t frame_type, uint8_t *encap_frame, uint8_t encap_frame_len) +{ + size_t data_size = sizeof(em_encap_dpp_t) + sizeof(uint8_t) + sizeof(uint16_t) + encap_frame_len; + if (dest_mac != NULL) { + data_size += sizeof(mac_addr_t); + } + em_encap_dpp_t *encap_tlv = NULL; + + if ((encap_tlv = (em_encap_dpp_t *)calloc(data_size, 1)) == NULL){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + (encap_tlv)->dpp_frame_indicator = dpp_frame_indicator; + (encap_tlv)->content_type = content_type; + (encap_tlv)->enrollee_mac_addr_present = (dest_mac != NULL) ? 1 : 0; + + uint8_t *data_ptr = (encap_tlv)->data; + if (dest_mac != NULL) { + memcpy(data_ptr, dest_mac, sizeof(mac_addr_t)); + data_ptr += sizeof(mac_addr_t); + } + + *data_ptr = frame_type; + data_ptr++; + + *((uint16_t *)data_ptr) = htons(encap_frame_len); + data_ptr += sizeof(uint16_t); + + memcpy(data_ptr, encap_frame, encap_frame_len); + + return encap_tlv; +} + +ec_frame_t *ec_util::copy_attrs_to_frame(ec_frame_t *frame, uint8_t *attrs, uint16_t attrs_len) +{ + uint16_t new_len = EC_FRAME_BASE_SIZE + attrs_len; + ec_frame_t* new_frame = (ec_frame_t *) realloc((uint8_t*)frame, new_len); + if (new_frame == NULL) { + printf("%s:%d unable to realloc memory\n", __func__, __LINE__); + return NULL; + } + memcpy(new_frame->attributes, attrs, attrs_len); + + return new_frame; + +} + +std::string ec_util::hash_to_hex_string(const uint8_t *hash, size_t hash_len) { + char output[hash_len * 2 + 1]; + for (size_t i = 0; i < hash_len; i++) { + sprintf(output + (i * 2), "%02x", hash[i]); + } + output[hash_len * 2] = '\0'; // Null-terminate the string + return std::string(output); +} - BN_free(y); - BN_free(x); -} \ No newline at end of file diff --git a/src/em/prov/em_provisioning.cpp b/src/em/prov/em_provisioning.cpp index e4fccd0..f00be81 100644 --- a/src/em/prov/em_provisioning.cpp +++ b/src/em/prov/em_provisioning.cpp @@ -495,13 +495,9 @@ int em_provisioning_t::handle_dpp_chirp_notif(uint8_t *buff, unsigned int len) em_dpp_chirp_value_t* chirp_tlv = (em_dpp_chirp_value_t*)tlv->value; uint8_t* out_frame = NULL; - if (m_ec_session->handle_chirp_notification(chirp_tlv, &out_frame) != 0){ + if (m_ec_manager->process_chirp_notification(chirp_tlv, htons(tlv->len)) != 0){ //TODO: Fail } - - // Create 1905 Encap DPP Message with a 1905 Encap DPP TLV (out frame) and chirp TLV - - } tlv_len -= (sizeof(em_tlv_t) + htons(tlv->len)); @@ -519,45 +515,35 @@ int em_provisioning_t::handle_proxy_encap_dpp(uint8_t *buff, unsigned int len) tlv = (em_tlv_t *)(buff + sizeof(em_raw_hdr_t) + sizeof(em_cmdu_t)); tlv_len = len - (sizeof(em_raw_hdr_t) + sizeof(em_cmdu_t)); + uint16_t encap_tlv_len, chirp_tlv_len = 0; + em_encap_dpp_t* encap_tlv = NULL; + em_dpp_chirp_value_t* chirp_tlv = NULL; + while ((tlv->type != em_tlv_type_eom) && (len > 0)) { if (tlv->type == em_tlv_type_1905_encap_dpp) { // Parse out dest STA mac address and hash value then validate against the hash in the // ec_session dpp uri info public key. // Then construct an Auth request frame and send back in an Encap message - em_encap_dpp_t* chirp_tlv = (em_encap_dpp_t*)tlv->value; - - uint8_t* out_frame = NULL; - if (m_ec_session->handle_proxy_encap_dpp_tlv(chirp_tlv, &out_frame) != 0){ - //TODO: Fail - } - // TODO: Send out_frame in an Encap message - - + encap_tlv = (em_encap_dpp_t*)tlv->value; + encap_tlv_len = htons(tlv->len); } - - // Can be 0 or 1 + // Optional: Can be 0 or 1 if (tlv->type == em_tlv_type_dpp_chirp_value) { - // Parse out dest STA mac address and hash value then validate against the hash in the - // ec_session dpp uri info public key. - // Then construct an Auth request frame and send back in an Encap message - em_dpp_chirp_value_t* chirp_tlv = (em_dpp_chirp_value_t*)tlv->value; - - uint8_t* out_frame = NULL; - if (m_ec_session->handle_chirp_notification(chirp_tlv, &out_frame) != 0){ - //TODO: Fail - } - - // Create 1905 Encap DPP Message with a 1905 Encap DPP TLV (out frame) and chirp TLV - - + chirp_tlv = (em_dpp_chirp_value_t*)tlv->value; + chirp_tlv_len = htons(tlv->len); } tlv_len -= (sizeof(em_tlv_t) + htons(tlv->len)); tlv = (em_tlv_t *)((uint8_t *)tlv + sizeof(em_tlv_t) + htons(tlv->len)); } + if (m_ec_manager->process_proxy_encap_dpp_msg(encap_tlv, encap_tlv_len, chirp_tlv, chirp_tlv_len) != 0){ + //TODO: Fail + return -1; + } + return 0; } @@ -650,10 +636,6 @@ void em_provisioning_t::process_ctrl_state() em_provisioning_t::em_provisioning_t() { - m_ec_session = std::unique_ptr(new ec_session_t( - std::bind(&em_provisioning_t::send_chirp_notif_msg, this, std::placeholders::_1, std::placeholders::_2), - std::bind(&em_provisioning_t::send_prox_encap_dpp_msg, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4) - )); } em_provisioning_t::~em_provisioning_t()