provisioning: Added documentation for SRP6a based Security Scheme 2

pull/7436/merge
Aditya Patwardhan 2022-05-27 20:35:52 +05:30 zatwierdzone przez BOT
rodzic 7fa47173b2
commit 0b88785286
6 zmienionych plików z 227 dodań i 9 usunięć

Wyświetl plik

@ -8,14 +8,135 @@ Protocol Communication (protocomm) component manages secure sessions and provide
Following features are available for provisioning :
* Communication security at application level -
* protocomm_security0 (no security)
* protocomm_security1 (curve25519 key exchange + AES-CTR encryption)
* protocomm_security1 (Curve25519 key exchange + AES-CTR encryption/decryption)
* protocomm_security2 (SRP6a-based key exchange + AES-GCM encryption/decryption)
* Proof-of-possession (support with protocomm_security1 only)
* Salt and Verifier (support with protocomm_security2 only)
Protocomm internally uses protobuf (protocol buffers) for secure session establishment. Though users can implement their own security (even without using protobuf). One can even use protocomm without any security layer.
Protocomm provides framework for various transports - WiFi (SoftAP+HTTPD), BLE, console - in which case the handler invocation is automatically taken care of on the device side (see Transport Examples below for code snippets).
Note that the client still needs to establish session (only for protocomm_security1) by performing the two way handshake. See :doc:`provisioning` for more details about the secure handshake logic.
Note that the client still needs to establish session (for protocomm_security1 and protocomm_security2) by performing the two way handshake. See :doc:`provisioning` for more details about the secure handshake logic.
Transport Example (SoftAP + HTTP) with Security 2
-------------------------------------------------
For sample usage, see :component_file:`wifi_provisioning/src/scheme_softap.c`
.. highlight:: c
::
/* Endpoint handler to be registered with protocomm.
* This simply echoes back the received data. */
esp_err_t echo_req_handler (uint32_t session_id,
const uint8_t *inbuf, ssize_t inlen,
uint8_t **outbuf, ssize_t *outlen,
void *priv_data)
{
/* Session ID may be used for persistence */
printf("Session ID : %d", session_id);
/* Echo back the received data */
*outlen = inlen; /* Output data length updated */
*outbuf = malloc(inlen); /* This will be deallocated outside */
memcpy(*outbuf, inbuf, inlen);
/* Private data that was passed at the time of endpoint creation */
uint32_t *priv = (uint32_t *) priv_data;
if (priv) {
printf("Private data : %d", *priv);
}
return ESP_OK;
}
static const char sec2_salt[] = {0xf7, 0x5f, 0xe2, 0xbe, 0xba, 0x7c, 0x81, 0xcd};
static const char sec2_verifier[] = {0xbf, 0x86, 0xce, 0x63, 0x8a, 0xbb, 0x7e, 0x2f, 0x38, 0xa8, 0x19, 0x1b, 0x35,
0xc9, 0xe3, 0xbe, 0xc3, 0x2b, 0x45, 0xee, 0x10, 0x74, 0x22, 0x1a, 0x95, 0xbe, 0x62, 0xf7, 0x0c, 0x65, 0x83, 0x50,
0x08, 0xef, 0xaf, 0xa5, 0x94, 0x4b, 0xcb, 0xe1, 0xce, 0x59, 0x2a, 0xe8, 0x7b, 0x27, 0xc8, 0x72, 0x26, 0x71, 0xde,
0xb2, 0xf2, 0x80, 0x02, 0xdd, 0x11, 0xf0, 0x38, 0x0e, 0x95, 0x25, 0x00, 0xcf, 0xb3, 0x3f, 0xf0, 0x73, 0x2a, 0x25,
0x03, 0xe8, 0x51, 0x72, 0xef, 0x6d, 0x3e, 0x14, 0xb9, 0x2e, 0x9f, 0x2a, 0x90, 0x9e, 0x26, 0xb6, 0x3e, 0xc7, 0xe4,
0x9f, 0xe3, 0x20, 0xce, 0x28, 0x7c, 0xbf, 0x89, 0x50, 0xc9, 0xb6, 0xec, 0xdd, 0x81, 0x18, 0xf1, 0x1a, 0xd9, 0x7a,
0x21, 0x99, 0xf1, 0xee, 0x71, 0x2f, 0xcc, 0x93, 0x16, 0x34, 0x0c, 0x79, 0x46, 0x23, 0xe4, 0x32, 0xec, 0x2d, 0x9e,
0x18, 0xa6, 0xb9, 0xbb, 0x0a, 0xcf, 0xc4, 0xa8, 0x32, 0xc0, 0x1c, 0x32, 0xa3, 0x97, 0x66, 0xf8, 0x30, 0xb2, 0xda,
0xf9, 0x8d, 0xc3, 0x72, 0x72, 0x5f, 0xe5, 0xee, 0xc3, 0x5c, 0x24, 0xc8, 0xdd, 0x54, 0x49, 0xfc, 0x12, 0x91, 0x81,
0x9c, 0xc3, 0xac, 0x64, 0x5e, 0xd6, 0x41, 0x88, 0x2f, 0x23, 0x66, 0xc8, 0xac, 0xb0, 0x35, 0x0b, 0xf6, 0x9c, 0x88,
0x6f, 0xac, 0xe1, 0xf4, 0xca, 0xc9, 0x07, 0x04, 0x11, 0xda, 0x90, 0x42, 0xa9, 0xf1, 0x97, 0x3d, 0x94, 0x65, 0xe4,
0xfb, 0x52, 0x22, 0x3b, 0x7a, 0x7b, 0x9e, 0xe9, 0xee, 0x1c, 0x44, 0xd0, 0x73, 0x72, 0x2a, 0xca, 0x85, 0x19, 0x4a,
0x60, 0xce, 0x0a, 0xc8, 0x7d, 0x57, 0xa4, 0xf8, 0x77, 0x22, 0xc1, 0xa5, 0xfa, 0xfb, 0x7b, 0x91, 0x3b, 0xfe, 0x87,
0x5f, 0xfe, 0x05, 0xd2, 0xd6, 0xd3, 0x74, 0xe5, 0x2e, 0x68, 0x79, 0x34, 0x70, 0x40, 0x12, 0xa8, 0xe1, 0xb4, 0x6c,
0xaa, 0x46, 0x73, 0xcd, 0x8d, 0x17, 0x72, 0x67, 0x32, 0x42, 0xdc, 0x10, 0xd3, 0x71, 0x7e, 0x8b, 0x00, 0x46, 0x9b,
0x0a, 0xe9, 0xb4, 0x0f, 0xeb, 0x70, 0x52, 0xdd, 0x0a, 0x1c, 0x7e, 0x2e, 0xb0, 0x61, 0xa6, 0xe1, 0xa3, 0x34, 0x4b,
0x2a, 0x3c, 0xc4, 0x5d, 0x42, 0x05, 0x58, 0x25, 0xd3, 0xca, 0x96, 0x5c, 0xb9, 0x52, 0xf9, 0xe9, 0x80, 0x75, 0x3d,
0xc8, 0x9f, 0xc7, 0xb2, 0xaa, 0x95, 0x2e, 0x76, 0xb3, 0xe1, 0x48, 0xc1, 0x0a, 0xa1, 0x0a, 0xe8, 0xaf, 0x41, 0x28,
0xd2, 0x16, 0xe1, 0xa6, 0xd0, 0x73, 0x51, 0x73, 0x79, 0x98, 0xd9, 0xb9, 0x00, 0x50, 0xa2, 0x4d, 0x99, 0x18, 0x90,
0x70, 0x27, 0xe7, 0x8d, 0x56, 0x45, 0x34, 0x1f, 0xb9, 0x30, 0xda, 0xec, 0x4a, 0x08, 0x27, 0x9f, 0xfa, 0x59, 0x2e,
0x36, 0x77, 0x00, 0xe2, 0xb6, 0xeb, 0xd1, 0x56, 0x50, 0x8e};
/* Example function for launching a protocomm instance over HTTP */
protocomm_t *start_pc()
{
protocomm_t *pc = protocomm_new();
/* Config for protocomm_httpd_start() */
protocomm_httpd_config_t pc_config = {
.data = {
.config = PROTOCOMM_HTTPD_DEFAULT_CONFIG()
}
};
/* Start protocomm server on top of HTTP */
protocomm_httpd_start(pc, &pc_config);
/* Create Security2 params object from salt and verifier. It must be valid
* throughout the scope of protocomm endpoint. This need not be static,
* ie. could be dynamically allocated and freed at the time of endpoint
* removal */
const static protocomm_security2_params_t sec2_params = {
.salt = (const uint8_t *) salt,
.salt_len = sizeof(salt),
.verifier = (const uint8_t *) verifier,
.verifier_len = sizeof(verifier),
};
/* Set security for communication at application level. Just like for
* request handlers, setting security creates an endpoint and registers
* the handler provided by protocomm_security1. One can similarly use
* protocomm_security0. Only one type of security can be set for a
* protocomm instance at a time. */
protocomm_set_security(pc, "security_endpoint", &protocomm_security2, &sec2_params);
/* Private data passed to the endpoint must be valid throughout the scope
* of protocomm endpoint. This need not be static, ie. could be dynamically
* allocated and freed at the time of endpoint removal */
static uint32_t priv_data = 1234;
/* Add a new endpoint for the protocomm instance, identified by a unique name
* and register a handler function along with private data to be passed at the
* time of handler execution. Multiple endpoints can be added as long as they
* are identified by unique names */
protocomm_add_endpoint(pc, "echo_req_endpoint",
echo_req_handler, (void *) &priv_data);
return pc;
}
/* Example function for stopping a protocomm instance */
void stop_pc(protocomm_t *pc)
{
/* Remove endpoint identified by it's unique name */
protocomm_remove_endpoint(pc, "echo_req_endpoint");
/* Remove security endpoint identified by it's name */
protocomm_unset_security(pc, "security_endpoint");
/* Stop HTTP server */
protocomm_httpd_stop(pc);
/* Delete (deallocate) the protocomm instance */
protocomm_delete(pc);
}
Transport Example (SoftAP + HTTP) with Security 1
-------------------------------------------------
@ -65,11 +186,11 @@ For sample usage, see :component_file:`wifi_provisioning/src/scheme_softap.c`
/* Start protocomm server on top of HTTP */
protocomm_httpd_start(pc, &pc_config);
/* Create Proof of Possession object from pop_string. It must be valid
/* Create security1 params object from pop_string. It must be valid
* throughout the scope of protocomm endpoint. This need not be static,
* ie. could be dynamically allocated and freed at the time of endpoint
* removal */
const static protocomm_security_pop_t pop_obj = {
const static protocomm_security1_params_t sec1_params = {
.data = (const uint8_t *) strdup(pop_string),
.len = strlen(pop_string)
};
@ -79,7 +200,7 @@ For sample usage, see :component_file:`wifi_provisioning/src/scheme_softap.c`
* the handler provided by protocomm_security1. One can similarly use
* protocomm_security0. Only one type of security can be set for a
* protocomm instance at a time. */
protocomm_set_security(pc, "security_endpoint", &protocomm_security1, &pop_obj);
protocomm_set_security(pc, "security_endpoint", &protocomm_security1, &sec1_params);
/* Private data passed to the endpoint must be valid throughout the scope
* of protocomm endpoint. This need not be static, ie. could be dynamically

Wyświetl plik

@ -99,14 +99,18 @@ Application creates a protocomm instance which is mapped to a specific transport
Security Schemes
>>>>>>>>>>>>>>>>
At present unified provisioning supports two security schemes:
1. Security0 - No security (No encryption)
2. Security1 - Curve25519 based key exchange, shared key derivation and AES256-CTR mode encryption of the data. It supports two modes :
At present, unified provisioning supports the following security schemes:
1. Security0 - No security (No encryption)
2. Security1 - Curve25519-based key exchange, shared key derivation and AES256-CTR mode encryption of the data. It supports two modes :
a. Authorized - Proof of Possession (PoP) string used to authorize session and derive shared key
b. No Auth (Null PoP) - Shared key derived through key exchange only
3. Security2 - SRP6a-based shared key derivation and AES256-GCM mode encryption of the data.
Security1 scheme details are shown in the below sequence diagram
Security1 Scheme
>>>>>>>>>>>>>>>>
Security1 scheme details are shown in the below sequence diagram -
.. seqdiag::
:caption: Security1
@ -140,6 +144,71 @@ Security1 scheme details are shown in the below sequence diagram
CLIENT -> CLIENT [label = "Verify Device", rightnote = "check (cli_pubkey == aes_ctr_dec(dev_verify...)"];
}
.. note:: We shall soon migrate to ``Security2 scheme`` as the default scheme in our examples as it provides enhanced security. This change shall be done once we have our phone apps (Android/iOS) upgraded to handle ``Security2 scheme``.
Security2 Scheme
>>>>>>>>>>>>>>>>
Security2 scheme is based on the Secure Remote Password (SRP6a) protocol - `RFC 5054 <https://datatracker.ietf.org/doc/html/rfc5054>`_.
The protocol requires the Salt and Verifier to be generated beforehand with help of the identifying username ``I`` and the plaintext password ``p``. The Salt and Verifier are then stored on {IDF_TARGET_NAME}.
- The password ``p`` and username ``I`` are to be provided to the Phone App (Provisioning entity) by suitable means for example QR code sticker.
Security2 scheme details are shown in the below sequence diagram -
.. seqdiag::
:caption: Security2
:align: center
seqdiag security2 {
activation = none;
node_width = 80;
node_height = 60;
edge_length = 550;
span_height = 5;
default_shape = roundedbox;
default_fontsize = 12;
CLIENT [label = "Client\n(PhoneApp)"];
DEVICE [label = "Device\n(ESP)"];
=== Security 2 ===
CLIENT -> CLIENT [label = "Generate\nKey Pair", rightnote = "a (cli_privkey) = 256 bit random value,
A (cli_pubkey) = g^a.
g - generator, N - large safe prime,
All arithmetic operations are performed in ring of integers modulo N,
thus all occurrences like y^z should be read as y^z modulo N."];
CLIENT -> DEVICE [label = "SessionCmd0(cli_pubkey A, username I)"];
DEVICE -> DEVICE [label = "Obtain\n Salt and Verifier", leftnote = "Obtain salt and verifier stored on esp
Salt s = 256 bit random value,
Verifier v = g^x where x = H(s | I | p)"];
DEVICE -> DEVICE [label = "Generate\nKey Pair", leftnote = "b (dev_privkey) = 256 bit random value
B(dev_pubkey) = k*v + g^b where k = H(N, g)"];
DEVICE -> DEVICE [label = "Shared Key", leftnote = "Shared Key K = H(S) where,
S = (A * v^u) ^ b
u = H(A, B)"];
DEVICE -> CLIENT [label = "SessionResp0(dev_pubkey B, dev_rand)"];
CLIENT -> CLIENT [label = "Shared Key", rightnote = "shared_key(K) = H(S) where,
S = (B - k*v) ^ (a + ux),
u = H(A, B),
k = H(N, g),
v = g^x,
x = H(s | I | p).
"];
CLIENT -> CLIENT [label = "Verification\nToken", rightnote = "client_proof M = H[H(N) XOR H(g) | H(I) | s | A | B | K]"];
CLIENT -> DEVICE [label = "SessionCmd1(client_proof M1)"];
DEVICE -> DEVICE [label = "Verify Client", leftnote = "device generates M1 = H[H(N) XOR H(g) | H(I) | s | A | B | K]
device verifies this M1 with the M1 obtained from Client"];
DEVICE -> DEVICE [label = "Verification\nToken", leftnote = "
Device generate device_proof M2 = H(A, M, K)"];
DEVICE -> DEVICE [label = "Initialization\nVector", leftnote = "dev_rand = gen_16byte_random()
This random number is to be used for AES-GCM operation
for encryption and decryption of data using the shared secret"];
DEVICE -> CLIENT [label = "SessionResp1(device_proof M2, dev_rand)"];
CLIENT -> CLIENT [label = "Verify Device", rightnote = "Client calculates device proof M2 as M2 = H(A, M, K)
client verifies this M2 with M2 obtained from device"];
}
Sample Code
>>>>>>>>>>>
Please refer to :doc:`protocomm` and :doc:`wifi_provisioning` for API guides and code snippets on example usage.

Wyświetl plik

@ -11,6 +11,7 @@ ESP-IDF 5.0 Migration Guides
freertos
peripherals
protocols
provisioning
removed-components
storage
system

Wyświetl plik

@ -0,0 +1,25 @@
Migrate Provisioning to ESP-IDF 5.0
===================================
Protocomm
---------
The :cpp:func:`protocomm_set_security` API now takes a parameter ``sec_params`` as input instead of ``pop`` (deprecated).
This parameter should contain the structure (containing the security parameters) as required by the protocol version used.
For example when using security version 2, the ``sec_params`` parameter should contain the pointer to the structure of type :cpp:type:`protocomm_security2_params_t`.
Wi-Fi Provisioning
------------------
The :cpp:func:`wifi_prov_mgr_start_provisioning` API now takes a parameter ``wifi_prov_sec_params`` as input instead of ``pop``.
This parameter should contain the structure (containing the security parameters) as required by the protocol version used.
For example when using security version 2, the ``wifi_prov_sec_params`` parameter should contain the pointer to the structure of type :cpp:type:`wifi_prov_security2_params_t`.
ESP Local Control
-----------------
The `pop` field in :cpp:type:`esp_local_ctrl_proto_sec_cfg_t` is now deprecated, use ``sec_params`` field instead of ``pop``.
This field should contain the structure (containing the security parameters) as required by the protocol version used.
For example when using security version 2, the ``sec_params`` field should contain pointer to the structure of type :cpp:type:`esp_local_ctrl_security2_params_t`.

Wyświetl plik

@ -11,6 +11,7 @@ ESP-IDF 5.0 迁移指南
freertos
peripherals
protocols
provisioning
removed-components
storage
system

Wyświetl plik

@ -0,0 +1 @@
.. include:: ../../en/migration-guides/provisioning.rst