Skip to content

Commit 75f52fa

Browse files
committed
components/esp_matter_controller: support dcl attestation for commissioner
1 parent e2a83f8 commit 75f52fa

File tree

4 files changed

+211
-2
lines changed

4 files changed

+211
-2
lines changed

components/esp_matter_controller/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ set(MATTER_SDK_PATH ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip)
77
set(src_dirs_list )
88
set(include_dirs_list )
99
set(exclude_srcs_list )
10-
set(requires_list chip esp_matter esp_matter_console spiffs)
10+
set(requires_list chip esp_matter esp_matter_console spiffs esp_http_client)
1111
if (CONFIG_ESP_MATTER_CONTROLLER_ENABLE)
1212
list(APPEND src_dirs_list "${CMAKE_CURRENT_SOURCE_DIR}/core"
1313
"${CMAKE_CURRENT_SOURCE_DIR}/commands"

components/esp_matter_controller/Kconfig

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ menu "ESP Matter Controller"
3737
help
3838
Read the PAA root certificates from the spiffs partition
3939

40+
config DCL_ATTESTATION_TRUST_STORE
41+
bool "Attestation Trust Store - DCL"
42+
help
43+
Read the PAA root certificates from DCL
44+
4045
config CUSTOM_ATTESTATION_TRUST_STORE
4146
bool "Attestation Trust Store - Custom"
4247
help

components/esp_matter_controller/attestation_store/esp_matter_attestation_trust_store.cpp

+177-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include "esp_matter_controller_utils.h"
1516
#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
1617
#include <esp_check.h>
18+
#include <esp_crt_bundle.h>
19+
#include <esp_http_client.h>
1720
#include <esp_log.h>
1821
#include <esp_matter_attestation_trust_store.h>
1922
#include <esp_spiffs.h>
23+
#include <json_parser.h>
24+
#include <mbedtls/base64.h>
2025

2126
const char TAG[] = "spiffs_attestation";
2227

@@ -132,14 +137,185 @@ CHIP_ERROR spiffs_attestation_trust_store::GetProductAttestationAuthorityCert(co
132137
return CHIP_ERROR_INCORRECT_STATE;
133138
}
134139

140+
#if CONFIG_DCL_ATTESTATION_TRUST_STORE
141+
static void remove_backslash_n(char *str)
142+
{
143+
char *src = str, *dst = str;
144+
while (*src) {
145+
if (*src == '\\' && *(src + 1) == 'n' && *(src + 1) != '\0') {
146+
src += 2;
147+
} else {
148+
*dst++ = *src++;
149+
}
150+
}
151+
*dst = '\0';
152+
}
153+
154+
static esp_err_t convert_pem_to_der(const char *pem, uint8_t *der_buf, size_t *der_len)
155+
{
156+
ESP_RETURN_ON_FALSE(pem && strlen(pem) > 0, ESP_ERR_INVALID_ARG, TAG, "pem cannot be NULL");
157+
ESP_RETURN_ON_FALSE(der_buf && der_len, ESP_ERR_INVALID_ARG, TAG, "der_buf cannot be NULL");
158+
size_t pem_len = strlen(pem);
159+
size_t len = 0;
160+
const char *s1, *s2, *end = pem + pem_len;
161+
constexpr char *begin_mark = "-----BEGIN";
162+
constexpr char *end_mark = "-----END";
163+
s1 = (char *)strstr(pem, begin_mark);
164+
if (s1 == NULL) {
165+
return ESP_FAIL;
166+
}
167+
s2 = (char *)strstr(pem, end_mark);
168+
if (s2 == NULL) {
169+
return ESP_FAIL;
170+
}
171+
s1 += strlen(begin_mark);
172+
while (s1 < end && *s1 != '-') {
173+
s1++;
174+
}
175+
while (s1 < end && *s1 == '-') {
176+
s1++;
177+
}
178+
if (*s1 == '\r') {
179+
s1++;
180+
}
181+
if (*s1 == '\n') {
182+
s1++;
183+
}
184+
int ret = mbedtls_base64_decode(NULL, 0, &len, (const unsigned char *)s1, s2 - s1);
185+
if (ret == MBEDTLS_ERR_BASE64_INVALID_CHARACTER) {
186+
return ESP_FAIL;
187+
}
188+
if (len > *der_len) {
189+
return ESP_FAIL;
190+
}
191+
if ((ret = mbedtls_base64_decode(der_buf, len, &len, (const unsigned char *)s1, s2 - s1)) != 0) {
192+
return ESP_FAIL;
193+
}
194+
*der_len = len;
195+
return ESP_OK;
196+
}
197+
198+
CHIP_ERROR dcl_attestation_trust_store::GetProductAttestationAuthorityCert(const ByteSpan &skid,
199+
MutableByteSpan &outPaaDerBuffer) const
200+
{
201+
VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT);
202+
VerifyOrReturnError(outPaaDerBuffer.size() > 0 && outPaaDerBuffer.size() <= kMaxDERCertLength,
203+
CHIP_ERROR_INVALID_ARGUMENT);
204+
char url[200];
205+
int offset = 0;
206+
esp_err_t ret = ESP_OK;
207+
if (dcl_net_type == DCL_MAIN_NET) {
208+
offset += snprintf(url, sizeof(url), "%s", "https://on.dcl.csa-iot.org/dcl/pki/certificates?subjectKeyId=");
209+
} else {
210+
// DCL_TEST_NET
211+
offset +=
212+
snprintf(url, sizeof(url), "%s", "https://on.test-net.dcl.csa-iot.org/dcl/pki/certificates?subjectKeyId=");
213+
}
214+
215+
for (size_t i = 0; i < skid.size(); ++i) {
216+
if (i == skid.size() - 1) {
217+
offset += snprintf(url + offset, sizeof(url) - offset, "%02X", skid[i]);
218+
} else {
219+
offset += snprintf(url + offset, sizeof(url) - offset, "%02X%%3a", skid[i]);
220+
}
221+
}
222+
223+
ChipLogProgress(Controller, "DCL Attestation URL: %s", url);
224+
225+
esp_http_client_config_t config = {
226+
.url = url,
227+
.method = HTTP_METHOD_GET,
228+
.timeout_ms = 10000,
229+
.transport_type = HTTP_TRANSPORT_OVER_SSL,
230+
.buffer_size = 1024,
231+
.skip_cert_common_name_check = false,
232+
.crt_bundle_attach = esp_crt_bundle_attach,
233+
};
234+
esp_http_client_handle_t client = NULL;
235+
ScopedMemoryBufferWithSize<char> http_payload;
236+
int http_len, http_status_code;
237+
int certificates_count, certs_count, paa_str_len;
238+
jparse_ctx_t jctx;
239+
const size_t paa_pem_size = 1024;
240+
size_t paa_der_len = outPaaDerBuffer.size();
241+
242+
client = esp_http_client_init(&config);
243+
if (!client) {
244+
ESP_LOGE(TAG, "Failed to initialise HTTP Client.");
245+
return CHIP_ERROR_NO_MEMORY;
246+
}
247+
248+
char *paa_pem_buffer = (char *)malloc(paa_pem_size);
249+
ESP_GOTO_ON_FALSE(paa_pem_buffer, ESP_ERR_NO_MEM, cleanup, TAG, "Failed to alloc memory for paa_pem_buffer");
250+
ESP_GOTO_ON_ERROR(esp_http_client_set_header(client, "accept", "application/json"), cleanup, TAG,
251+
"Failed to set http header accept");
252+
ESP_GOTO_ON_ERROR(esp_http_client_open(client, 0), cleanup, TAG, "Failed to open http connection");
253+
254+
// Read Response
255+
http_len = esp_http_client_fetch_headers(client);
256+
http_status_code = esp_http_client_get_status_code(client);
257+
http_payload.Calloc(2400);
258+
ESP_GOTO_ON_FALSE(http_payload.Get(), ESP_ERR_NO_MEM, close, TAG, "Failed to alloc memory for http_payload");
259+
if (http_status_code == HttpStatus_Ok) {
260+
http_len = esp_http_client_read_response(client, http_payload.Get(), http_payload.AllocatedSize());
261+
http_payload[http_len] = '\0';
262+
} else {
263+
ESP_LOGE(TAG, "Status = %d. Invalid response for %s", http_status_code, url);
264+
ret = ESP_FAIL;
265+
goto close;
266+
}
267+
268+
// Parse the response payload
269+
ESP_GOTO_ON_FALSE(json_parse_start(&jctx, http_payload.Get(), http_len) == 0, ESP_FAIL, close, TAG,
270+
"Failed to parse the http response json on json_parse_start");
271+
if (json_obj_get_array(&jctx, "approvedCertificates", &certificates_count) == 0 && certificates_count == 1) {
272+
if (json_arr_get_object(&jctx, 0) == 0) {
273+
if (json_obj_get_array(&jctx, "certs", &certs_count) == 0 && certs_count == 1) {
274+
if (json_arr_get_object(&jctx, 0) == 0) {
275+
if (json_obj_get_strlen(&jctx, "pemCert", &paa_str_len) == 0 &&
276+
json_obj_get_string(&jctx, "pemCert", paa_pem_buffer, paa_pem_size) == 0) {
277+
paa_str_len = paa_str_len < paa_pem_size - 1 ? paa_str_len : paa_pem_size - 1;
278+
paa_pem_buffer[paa_str_len] = 0;
279+
remove_backslash_n(paa_pem_buffer);
280+
ret = convert_pem_to_der(paa_pem_buffer, outPaaDerBuffer.data(), &paa_der_len);
281+
if (ret == ESP_OK) {
282+
outPaaDerBuffer.reduce_size(paa_der_len);
283+
}
284+
}
285+
json_obj_leave_object(&jctx);
286+
}
287+
json_obj_leave_array(&jctx);
288+
} else {
289+
ret = ESP_FAIL;
290+
}
291+
json_obj_leave_object(&jctx);
292+
} else {
293+
ret = ESP_FAIL;
294+
}
295+
json_obj_leave_array(&jctx);
296+
} else {
297+
ret = ESP_FAIL;
298+
}
299+
json_parse_end(&jctx);
300+
301+
close:
302+
esp_http_client_close(client);
303+
cleanup:
304+
free(paa_pem_buffer);
305+
esp_http_client_cleanup(client);
306+
return ret == ESP_OK ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
307+
}
308+
#endif // CONFIG_DCL_ATTESTATION_TRUST_STORE
309+
135310
const AttestationTrustStore *get_attestation_trust_store()
136311
{
137-
// TODO: Fetch the PAA certificates from DCL
138312
#if CONFIG_TEST_ATTESTATION_TRUST_STORE
139313
return GetTestAttestationTrustStore();
140314
#elif CONFIG_SPIFFS_ATTESTATION_TRUST_STORE
141315
spiffs_attestation_trust_store::get_instance().init();
142316
return &spiffs_attestation_trust_store::get_instance();
317+
#elif CONFIG_DCL_ATTESTATION_TRUST_STORE
318+
return &dcl_attestation_trust_store::get_instance();
143319
#endif
144320
}
145321

components/esp_matter_controller/attestation_store/esp_matter_attestation_trust_store.h

+28
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,34 @@ class spiffs_attestation_trust_store : public AttestationTrustStore {
6464
spiffs_attestation_trust_store() {}
6565
};
6666

67+
#if CONFIG_DCL_ATTESTATION_TRUST_STORE
68+
class dcl_attestation_trust_store : public AttestationTrustStore {
69+
public:
70+
typedef enum {
71+
DCL_MAIN_NET,
72+
DCL_TEST_NET,
73+
} dcl_net_type_t;
74+
75+
dcl_attestation_trust_store(dcl_attestation_trust_store &other) = delete;
76+
void operator=(const dcl_attestation_trust_store &) = delete;
77+
78+
static dcl_attestation_trust_store &get_instance()
79+
{
80+
static dcl_attestation_trust_store instance;
81+
return instance;
82+
}
83+
84+
CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan &skid,
85+
MutableByteSpan &outPaaDerBuffer) const override;
86+
87+
void SetDCLNetType(dcl_net_type_t type) { dcl_net_type = type; }
88+
89+
private:
90+
dcl_net_type_t dcl_net_type = DCL_MAIN_NET;
91+
dcl_attestation_trust_store() {}
92+
};
93+
#endif // CONFIG_DCL_ATTESTATION_TRUST_STORE
94+
6795
const AttestationTrustStore *get_attestation_trust_store();
6896

6997
} // namespace Credentials

0 commit comments

Comments
 (0)