|
12 | 12 | // See the License for the specific language governing permissions and
|
13 | 13 | // limitations under the License.
|
14 | 14 |
|
| 15 | +#include "esp_matter_controller_utils.h" |
15 | 16 | #include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
|
16 | 17 | #include <esp_check.h>
|
| 18 | +#include <esp_crt_bundle.h> |
| 19 | +#include <esp_http_client.h> |
17 | 20 | #include <esp_log.h>
|
18 | 21 | #include <esp_matter_attestation_trust_store.h>
|
19 | 22 | #include <esp_spiffs.h>
|
| 23 | +#include <json_parser.h> |
| 24 | +#include <mbedtls/base64.h> |
20 | 25 |
|
21 | 26 | const char TAG[] = "spiffs_attestation";
|
22 | 27 |
|
@@ -132,14 +137,185 @@ CHIP_ERROR spiffs_attestation_trust_store::GetProductAttestationAuthorityCert(co
|
132 | 137 | return CHIP_ERROR_INCORRECT_STATE;
|
133 | 138 | }
|
134 | 139 |
|
| 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 | + |
135 | 310 | const AttestationTrustStore *get_attestation_trust_store()
|
136 | 311 | {
|
137 |
| - // TODO: Fetch the PAA certificates from DCL |
138 | 312 | #if CONFIG_TEST_ATTESTATION_TRUST_STORE
|
139 | 313 | return GetTestAttestationTrustStore();
|
140 | 314 | #elif CONFIG_SPIFFS_ATTESTATION_TRUST_STORE
|
141 | 315 | spiffs_attestation_trust_store::get_instance().init();
|
142 | 316 | return &spiffs_attestation_trust_store::get_instance();
|
| 317 | +#elif CONFIG_DCL_ATTESTATION_TRUST_STORE |
| 318 | + return &dcl_attestation_trust_store::get_instance(); |
143 | 319 | #endif
|
144 | 320 | }
|
145 | 321 |
|
|
0 commit comments